#include <common/memory/memshfl.h>\r
\r
#include <core/consumer/frame_consumer.h>\r
+#include <core/mixer/audio/audio_util.h>\r
\r
#include <tbb/concurrent_queue.h>\r
#include <tbb/cache_aligned_allocator.h>\r
#include <boost/circular_buffer.hpp>\r
#include <boost/timer.hpp>\r
#include <boost/property_tree/ptree.hpp>\r
+#include <boost/algorithm/string.hpp>\r
\r
namespace caspar { namespace decklink { \r
\r
default_latency\r
};\r
\r
- size_t device_index;\r
- bool embedded_audio;\r
- keyer_t keyer;\r
- latency_t latency;\r
- bool key_only;\r
- size_t base_buffer_depth;\r
+ size_t device_index;\r
+ bool embedded_audio;\r
+ core::channel_layout audio_layout;\r
+ keyer_t keyer;\r
+ latency_t latency;\r
+ bool key_only;\r
+ size_t base_buffer_depth;\r
\r
configuration()\r
: device_index(1)\r
, embedded_audio(false)\r
+ , audio_layout(core::default_channel_layout_repository().get_by_name(L"STEREO"))\r
, keyer(default_keyer)\r
, latency(default_latency)\r
, key_only(false)\r
{\r
return base_buffer_depth + (latency == low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);\r
}\r
+\r
+ int num_out_channels() const\r
+ {\r
+ if (audio_layout.num_channels <= 2)\r
+ return 2;\r
+ \r
+ if (audio_layout.num_channels <= 8)\r
+ return 8;\r
+\r
+ return 16;\r
+ }\r
};\r
\r
class decklink_frame : public IDeckLinkVideoFrame\r
\r
void enable_audio()\r
{\r
- if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped)))\r
+ if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, config_.num_out_channels(), bmdAudioOutputStreamTimestamped)))\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable audio output."));\r
\r
if(FAILED(output_->SetAudioCallback(this)))\r
{\r
graph_->set_tag("late-frame");\r
video_scheduled_ += format_desc_.duration;\r
- audio_scheduled_ += reinterpret_cast<decklink_frame*>(completed_frame)->audio_data().size()/format_desc_.audio_channels;\r
+ auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);\r
+ audio_scheduled_ += dframe->audio_data().size()/config_.num_out_channels();\r
//++video_scheduled_;\r
//audio_scheduled_ += format_desc_.audio_cadence[0];\r
//++audio_scheduled_;\r
start_playback(); \r
}\r
else\r
- schedule_next_audio(core::audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * format_desc_.audio_channels, 0)); \r
+ {\r
+ core::audio_buffer silent_audio(format_desc_.audio_cadence[preroll_count_ % format_desc_.audio_cadence.size()] * config_.num_out_channels(), 0);\r
+ auto view = core::make_multichannel_view<int32_t>(silent_audio.begin(), silent_audio.end(), config_.audio_layout, config_.num_out_channels());\r
+ schedule_next_audio(view);\r
+ }\r
}\r
else\r
{\r
while (audio_frame_buffer_.try_pop(frame))\r
{\r
send_completion_.try_completion();\r
- schedule_next_audio(frame->audio_data());\r
+ schedule_next_audio(frame->multichannel_view());\r
}\r
}\r
\r
unsigned long buffered;\r
output_->GetBufferedAudioSampleFrameCount(&buffered);\r
- graph_->set_value("buffered-audio", static_cast<double>(buffered)/(format_desc_.audio_cadence[0] * format_desc_.audio_channels * 2));\r
+ graph_->set_value("buffered-audio", static_cast<double>(buffered)/(format_desc_.audio_cadence[0] * config_.num_out_channels() * 2));\r
}\r
catch(...)\r
{\r
return S_OK;\r
}\r
\r
- template<typename T>\r
- void schedule_next_audio(const T& audio_data)\r
+ template<typename View>\r
+ void schedule_next_audio(const View& view)\r
{\r
- const int sample_frame_count = audio_data.size()/format_desc_.audio_channels;\r
+ const int sample_frame_count = view.num_samples();\r
+\r
+ if (core::needs_rearranging(\r
+ view, config_.audio_layout, config_.num_out_channels()))\r
+ {\r
+ std::vector<int32_t> resulting_audio_data;\r
+ resulting_audio_data.resize(\r
+ sample_frame_count * config_.num_out_channels());\r
+\r
+ auto dest_view = core::make_multichannel_view<int32_t>(\r
+ resulting_audio_data.begin(), \r
+ resulting_audio_data.end(),\r
+ config_.audio_layout,\r
+ config_.num_out_channels());\r
+\r
+ core::rearrange_or_rearrange_and_mix(\r
+ view, dest_view, core::default_mix_config_repository());\r
+\r
+ if (config_.audio_layout.num_channels == 1) // mono\r
+ boost::copy( // duplicate L to R\r
+ dest_view.channel(0),\r
+ dest_view.channel(1).begin());\r
\r
- audio_container_.push_back(std::vector<int32_t>(audio_data.begin(), audio_data.end()));\r
+ audio_container_.push_back(std::move(resulting_audio_data));\r
+ }\r
+ else\r
+ {\r
+ audio_container_.push_back(\r
+ std::vector<int32_t>(view.raw_begin(), view.raw_end()));\r
+ }\r
\r
- if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), sample_frame_count, audio_scheduled_, format_desc_.audio_sample_rate, nullptr)))\r
+ if(FAILED(output_->ScheduleAudioSamples(\r
+ audio_container_.back().data(),\r
+ sample_frame_count,\r
+ audio_scheduled_,\r
+ format_desc_.audio_sample_rate,\r
+ nullptr)))\r
CASPAR_LOG(error) << print() << L" Failed to schedule audio.";\r
\r
audio_scheduled_ += sample_frame_count;\r
\r
virtual boost::unique_future<bool> send(const safe_ptr<core::read_frame>& frame) override\r
{\r
- CASPAR_VERIFY(audio_cadence_.front() * format_desc_.audio_channels == static_cast<size_t>(frame->audio_data().size()));\r
+ CASPAR_VERIFY(audio_cadence_.front() * frame->num_channels() == static_cast<size_t>(frame->audio_data().size()));\r
boost::range::rotate(audio_cadence_, std::begin(audio_cadence_)+1);\r
\r
return context_->send(frame);\r
config.device_index = ptree.get(L"device", config.device_index);\r
config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);\r
config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);\r
+ config.audio_layout =\r
+ core::default_channel_layout_repository().get_by_name(\r
+ boost::to_upper_copy(ptree.get(L"channel-layout", L"STEREO")));\r
\r
return make_safe<decklink_consumer_proxy>(config);\r
}\r