]> git.sesse.net Git - casparcg/blobdiff - core/mixer/audio/audio_mixer.cpp
2.0. audio: Audio pipeline is now in 32 bit.
[casparcg] / core / mixer / audio / audio_mixer.cpp
index 598842bd3477da470a74f6aea77dcd20bbec69d3..1c3a77364a8fe2bb725fb063d49233ea993f1c63 100644 (file)
 #include "audio_mixer.h"\r
 \r
 #include <core/mixer/write_frame.h>\r
-#include <core/producer/frame/audio_transform.h>\r
+#include <core/producer/frame/frame_transform.h>\r
+\r
+#include <tbb/parallel_for.h>\r
+\r
+#include <safeint.h>\r
+\r
+#include <stack>\r
+#include <deque>\r
 \r
 namespace caspar { namespace core {\r
+\r
+struct audio_item\r
+{\r
+       const void*                             tag;\r
+       frame_transform                 transform;\r
+       std::vector<int32_t>    audio_data;\r
+};\r
        \r
 struct audio_mixer::implementation\r
 {\r
-       std::vector<int16_t> audio_data_;\r
-       std::stack<core::audio_transform> transform_stack_;\r
-\r
-       std::map<int, core::audio_transform> prev_audio_transforms_;\r
-       std::map<int, core::audio_transform> next_audio_transforms_;\r
+       std::stack<core::frame_transform>                               transform_stack_;\r
+       std::map<const void*, core::frame_transform>    prev_frame_transforms_;\r
+       const core::video_format_desc                                   format_desc_;\r
+       std::vector<audio_item>                                                 items;\r
 \r
 public:\r
-       implementation()\r
+       implementation(const core::video_format_desc& format_desc)\r
+               : format_desc_(format_desc)\r
        {\r
-               transform_stack_.push(core::audio_transform());\r
+               transform_stack_.push(core::frame_transform());\r
        }\r
        \r
-       void begin(const core::basic_frame& frame)\r
+       void begin(core::basic_frame& frame)\r
        {\r
-               transform_stack_.push(transform_stack_.top()*frame.get_audio_transform());\r
+               transform_stack_.push(transform_stack_.top()*frame.get_frame_transform());\r
        }\r
 \r
        void visit(const core::write_frame& frame)\r
        {\r
-               if(!transform_stack_.top().get_has_audio())\r
+               // We only care about the last field.\r
+               if(format_desc_.field_mode == field_mode::upper && transform_stack_.top().field_mode == field_mode::upper)\r
                        return;\r
 \r
-               auto& audio_data = frame.audio_data();\r
-               auto tag = frame.tag(); // Get the identifier for the audio-stream.\r
-\r
-               if(audio_data_.empty())\r
-                       audio_data_.resize(audio_data.size(), 0);\r
-               \r
-               auto next = transform_stack_.top();\r
-               auto prev = next;\r
+               if(format_desc_.field_mode == field_mode::lower && transform_stack_.top().field_mode == field_mode::lower)\r
+                       return;\r
 \r
-               auto it = prev_audio_transforms_.find(tag);\r
-               if(it != prev_audio_transforms_.end())\r
-                       prev = it->second;\r
-                               \r
-               next_audio_transforms_[tag] = next; // Store all active tags, inactive tags will be removed in end_pass.\r
-               \r
-               \r
-               if(next.get_gain() < 0.001 && prev.get_gain() < 0.001)\r
+               // Skip empty audio.\r
+               if(transform_stack_.top().volume < 0.002 || frame.audio_data().empty())\r
                        return;\r
-               \r
-               static const int BASE = 1<<15;\r
 \r
-               auto next_gain = static_cast<int32_t>(next.get_gain()*BASE);\r
-               auto prev_gain = static_cast<int32_t>(prev.get_gain()*BASE);\r
-               \r
-               int n_samples = audio_data_.size();\r
-\r
-               tbb::parallel_for\r
-               (\r
-                       tbb::blocked_range<size_t>(0, audio_data.size()),\r
-                       [&](const tbb::blocked_range<size_t>& r)\r
-                       {\r
-                               for(size_t n = r.begin(); n < r.end(); ++n)\r
-                               {\r
-                                       int sample_gain = (prev_gain - (prev_gain * n)/n_samples) + (next_gain * n)/n_samples;\r
-                                       \r
-                                       int sample = (static_cast<int32_t>(audio_data[n])*sample_gain)/BASE;\r
-                                       \r
-                                       audio_data_[n] = static_cast<int16_t>((static_cast<int32_t>(audio_data_[n]) + sample) & 0xFFFF);\r
-                               }\r
-                       }\r
-               );\r
-       }\r
+               audio_item item;\r
+               item.tag                = frame.tag();\r
+               item.transform  = transform_stack_.top();\r
+               item.audio_data = std::vector<int32_t>(frame.audio_data().begin(), frame.audio_data().end());\r
 \r
+               items.push_back(item);          \r
+       }\r
 \r
-       void begin(const core::audio_transform& transform)\r
+       void begin(const core::frame_transform& transform)\r
        {\r
                transform_stack_.push(transform_stack_.top()*transform);\r
        }\r
@@ -103,19 +89,71 @@ public:
        {\r
                transform_stack_.pop();\r
        }\r
+       \r
+       std::vector<int32_t> mix()\r
+       {\r
+               auto result = std::vector<int32_t>(format_desc_.audio_samples_per_frame);\r
 \r
+               std::map<const void*, core::frame_transform> next_frame_transforms;\r
 \r
-       std::vector<short> mix()\r
-       {\r
-               prev_audio_transforms_ = std::move(next_audio_transforms_);     \r
-               return std::move(audio_data_);\r
+               BOOST_FOREACH(auto& item, items)\r
+               {                               \r
+                       const auto next = item.transform;\r
+                       auto prev = next;\r
+\r
+                       const auto it = prev_frame_transforms_.find(item.tag);\r
+                       if(it != prev_frame_transforms_.end())\r
+                               prev = it->second;\r
+                               \r
+                       next_frame_transforms[item.tag] = next; // Store all active tags, inactive tags will be removed at the end.\r
+                               \r
+                       if(next.volume < 0.001 && prev.volume < 0.001)\r
+                               continue;\r
+               \r
+                       static const int BASE = 1<<31;\r
+\r
+                       const auto next_volume = static_cast<int64_t>(next.volume*BASE);\r
+                       const auto prev_volume = static_cast<int64_t>(prev.volume*BASE);\r
+               \r
+                       const int n_samples = result.size();\r
+               \r
+                       const auto in_size = static_cast<size_t>(item.audio_data.size());\r
+                       CASPAR_VERIFY(in_size == 0 || in_size == result.size());\r
+\r
+                       if(in_size > result.size())\r
+                               continue;\r
+\r
+                       tbb::parallel_for\r
+                       (\r
+                               tbb::blocked_range<size_t>(0, item.audio_data.size()),\r
+                               [&](const tbb::blocked_range<size_t>& r)\r
+                               {\r
+                                       for(size_t n = r.begin(); n < r.end(); ++n)\r
+                                       {\r
+                                               const auto sample_volume = (prev_volume - (prev_volume * n)/n_samples) + (next_volume * n)/n_samples;\r
+                                               const auto sample = static_cast<int32_t>((static_cast<int64_t>(item.audio_data[n])*sample_volume)/BASE);\r
+                                               result[n] = result[n] + sample;\r
+                                       }\r
+                               }\r
+                       );\r
+               }\r
+\r
+               items.clear();\r
+               prev_frame_transforms_ = std::move(next_frame_transforms);      \r
+\r
+               return std::move(result);\r
        }\r
 };\r
 \r
-audio_mixer::audio_mixer() : impl_(new implementation()){}\r
-void audio_mixer::begin(const core::basic_frame& frame){impl_->begin(frame);}\r
+audio_mixer::audio_mixer(const core::video_format_desc& format_desc) : impl_(new implementation(format_desc)){}\r
+void audio_mixer::begin(core::basic_frame& frame){impl_->begin(frame);}\r
 void audio_mixer::visit(core::write_frame& frame){impl_->visit(frame);}\r
 void audio_mixer::end(){impl_->end();}\r
-std::vector<short> audio_mixer::mix(){return impl_->mix();}\r
+std::vector<int32_t> audio_mixer::mix(){return impl_->mix();}\r
+audio_mixer& audio_mixer::operator=(audio_mixer&& other)\r
+{\r
+       impl_ = std::move(other.impl_);\r
+       return *this;\r
+}\r
 \r
 }}
\ No newline at end of file