X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fmixer%2Faudio%2Faudio_mixer.cpp;h=dafa6e4dd0991c81a3310d407d4c61daceca954a;hb=ffcb75eabba1d77eab8d83447136261462e3e088;hp=4be55434ccd02b56e6714b4fb5da8d68dc3524a8;hpb=94e1a4ea8fbf536dea2924d4aa3a9d6d50bef77c;p=casparcg diff --git a/core/mixer/audio/audio_mixer.cpp b/core/mixer/audio/audio_mixer.cpp index 4be55434c..dafa6e4dd 100644 --- a/core/mixer/audio/audio_mixer.cpp +++ b/core/mixer/audio/audio_mixer.cpp @@ -1,89 +1,168 @@ +/* +* copyright (c) 2010 Sveriges Television AB +* +* This file is part of CasparCG. +* +* CasparCG is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* CasparCG is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with CasparCG. If not, see . +* +*/ #include "../../stdafx.h" #include "audio_mixer.h" -namespace caspar { namespace core { - -audio_transform::audio_transform() - : gain_(1.0){} +#include +#include -void audio_transform::set_gain(double value) -{ - tbb::spin_mutex::scoped_lock lock(mutex_); - gain_ = value; -} +#include -double audio_transform::get_gain() const -{ - tbb::spin_mutex::scoped_lock lock(mutex_); - return gain_; -} +#include -audio_transform& audio_transform::operator*=(const audio_transform &other) -{ - tbb::spin_mutex::scoped_lock lock(mutex_); - gain_ *= other.gain_; - return *this; -} +#include +#include -const audio_transform audio_transform::operator*(const audio_transform &other) const -{ - return audio_transform(*this) *= other; -} +namespace caspar { namespace core { +struct audio_item +{ + const void* tag; + frame_transform transform; + std::vector audio_data; +}; + struct audio_mixer::implementation { - std::vector audio_data_; - std::stack transform_stack_; + std::stack transform_stack_; + std::map prev_frame_transforms_; + const core::video_format_desc format_desc_; + std::vector items; public: - implementation() + implementation(const core::video_format_desc& format_desc) + : format_desc_(format_desc) { - transform_stack_.push(audio_transform()); + transform_stack_.push(core::frame_transform()); } - - void begin(const audio_transform& transform) + + void begin(core::basic_frame& frame) { - transform_stack_.push(transform_stack_.top()*transform); + transform_stack_.push(transform_stack_.top()*frame.get_frame_transform()); } - void process(const std::vector& audio_data) - { - if(audio_data_.empty()) - audio_data_.resize(audio_data.size(), 0); - - double gain = transform_stack_.top().get_gain(); - tbb::parallel_for - ( - tbb::blocked_range(0, audio_data.size()), - [&](const tbb::blocked_range& r) - { - for(size_t n = r.begin(); n < r.end(); ++n) - { - int sample = static_cast(audio_data[n]); - sample = (static_cast(gain*8192.0)*sample)/8192; - audio_data_[n] = static_cast((static_cast(audio_data_[n]) + sample) & 0xFFFF); - } - } - ); + void visit(const core::write_frame& frame) + { + // We only care about the last field. + if(format_desc_.field_mode == field_mode::upper && transform_stack_.top().field_mode == field_mode::upper) + return; + + if(format_desc_.field_mode == field_mode::lower && transform_stack_.top().field_mode == field_mode::lower) + return; + + // Skip empty audio. + if(transform_stack_.top().volume < 0.002 || frame.audio_data().empty()) + return; + + audio_item item; + item.tag = frame.tag(); + item.transform = transform_stack_.top(); + item.audio_data = std::vector(frame.audio_data().begin(), frame.audio_data().end()); + + items.push_back(item); } + void begin(const core::frame_transform& transform) + { + transform_stack_.push(transform_stack_.top()*transform); + } + void end() { transform_stack_.pop(); } - - std::vector begin_pass() + + audio_buffer mix() { - return std::move(audio_data_); + auto result = audio_buffer(format_desc_.audio_samples_per_frame, 0); + + std::map next_frame_transforms; + + BOOST_FOREACH(auto& item, items) + { + const auto next = item.transform; + auto prev = next; + + const auto it = prev_frame_transforms_.find(item.tag); + if(it != prev_frame_transforms_.end()) + prev = it->second; + + next_frame_transforms[item.tag] = next; // Store all active tags, inactive tags will be removed at the end. + + if(next.volume < 0.001 && prev.volume < 0.001) + continue; + + if(static_cast(item.audio_data.size()) != format_desc_.audio_samples_per_frame) + continue; + + CASPAR_ASSERT(format_desc_.audio_channels == 2); + + const float prev_volume = static_cast(prev.volume); + const float next_volume = static_cast(next.volume); + const float delta = 1.0f/static_cast(format_desc_.audio_samples_per_frame/2); + + tbb::parallel_for + ( + tbb::blocked_range(0, format_desc_.audio_samples_per_frame/2), + [&](const tbb::blocked_range& r) + { + for(size_t n = r.begin(); n < r.end(); ++n) + { + const float alpha = n * delta; + const float volume = prev_volume * (1.0f - alpha) + next_volume * alpha; + + auto sample_epi32 = _mm_loadl_epi64(reinterpret_cast<__m128i*>(&item.audio_data[n*2])); + + auto sample_ps = _mm_cvtepi32_ps(sample_epi32); + sample_ps = _mm_mul_ps(sample_ps, _mm_set1_ps(volume)); + + auto res_sample_epi32 = _mm_loadl_epi64(reinterpret_cast<__m128i*>(&result[n*2])); + auto res_sample_ps = _mm_cvtepi32_ps(res_sample_epi32); + + res_sample_ps = _mm_add_ps(sample_ps, res_sample_ps); + res_sample_epi32 = _mm_cvtps_epi32(res_sample_ps); + + _mm_storel_epi64(reinterpret_cast<__m128i*>(&result[n*2]), res_sample_epi32); + } + } + ); + } + + items.clear(); + prev_frame_transforms_ = std::move(next_frame_transforms); + + result.resize(format_desc_.audio_samples_per_frame); + return std::move(result); } }; -audio_mixer::audio_mixer() : impl_(new implementation()){} -void audio_mixer::begin(const audio_transform& transform){impl_->begin(transform);} -void audio_mixer::process(const std::vector& audio_data){impl_->process(audio_data);} +audio_mixer::audio_mixer(const core::video_format_desc& format_desc) : impl_(new implementation(format_desc)){} +void audio_mixer::begin(core::basic_frame& frame){impl_->begin(frame);} +void audio_mixer::visit(core::write_frame& frame){impl_->visit(frame);} void audio_mixer::end(){impl_->end();} -std::vector audio_mixer::begin_pass(){return impl_->begin_pass();} -void audio_mixer::end_pass(){} +audio_buffer audio_mixer::mix(){return impl_->mix();} +audio_mixer& audio_mixer::operator=(audio_mixer&& other) +{ + impl_ = std::move(other.impl_); + return *this; +} }} \ No newline at end of file