]> git.sesse.net Git - casparcg/blobdiff - modules/oal/consumer/oal_consumer.cpp
Created a consumer that provides sync to a channel based on the pace of another chann...
[casparcg] / modules / oal / consumer / oal_consumer.cpp
index 8099d5b0f714994d3bdf5b599fba2cef9d4acd83..286622f89076e5e031b0333a5fad82a3f3a2b022 100644 (file)
@@ -72,7 +72,7 @@ public:
 
                if(!context_)
                        CASPAR_THROW_EXCEPTION(invalid_operation() << msg_info("Failed to create audio context."));
-                       
+
                if(alcMakeContextCurrent(context_) == ALC_FALSE)
                        CASPAR_THROW_EXCEPTION(invalid_operation() << msg_info("Failed to activate audio context."));
        }
@@ -98,7 +98,7 @@ void init_device()
 {
        static std::unique_ptr<device> instance;
        static boost::once_flag f = BOOST_ONCE_INIT;
-       
+
        boost::call_once(f, []{instance.reset(new device());});
 }
 
@@ -110,25 +110,27 @@ struct oal_consumer : public core::frame_consumer
        boost::timer                                                                    perf_timer_;
        tbb::atomic<int64_t>                                                    presentation_age_;
        int                                                                                             channel_index_          = -1;
-       
+
        core::video_format_desc                                                 format_desc_;
        core::audio_channel_layout                                              out_channel_layout_;
        std::unique_ptr<core::audio_channel_remapper>   channel_remapper_;
 
        ALuint                                                                                  source_                         = 0;
        std::vector<ALuint>                                                             buffers_;
+       int                                                                                             latency_millis_;
 
        executor                                                                                executor_                       { L"oal_consumer" };
 
 public:
-       oal_consumer(const core::audio_channel_layout& out_channel_layout)
+       oal_consumer(const core::audio_channel_layout& out_channel_layout, int latency_millis)
                : out_channel_layout_(out_channel_layout)
+               , latency_millis_(latency_millis)
        {
                presentation_age_ = 0;
 
                init_device();
 
-               graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));   
+               graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
                graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
                graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
                diagnostics::register_graph(graph_);
@@ -137,7 +139,7 @@ public:
        ~oal_consumer()
        {
                executor_.invoke([=]
-               {               
+               {
                        if(source_)
                        {
                                alSourceStop(source_);
@@ -156,7 +158,7 @@ public:
 
        void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout& channel_layout, int channel_index) override
        {
-               format_desc_    = format_desc;          
+               format_desc_    = format_desc;
                channel_index_  = channel_index;
                if (out_channel_layout_ == core::audio_channel_layout::invalid())
                        out_channel_layout_ = channel_layout.num_channels == 2 ? channel_layout : *core::audio_channel_layout_repository::get_default()->get_layout(L"stereo");
@@ -167,7 +169,7 @@ public:
                graph_->set_text(print());
 
                executor_.begin_invoke([=]
-               {               
+               {
                        buffers_.resize(format_desc_.fps > 30 ? 8 : 4);
                        alGenBuffers(static_cast<ALsizei>(buffers_.size()), buffers_.data());
                        alGenSources(1, &source_);
@@ -178,10 +180,10 @@ public:
                                alBufferData(buffers_[n], AL_FORMAT_STEREO16, audio.data(), static_cast<ALsizei>(audio.size()*sizeof(int16_t)), format_desc_.audio_sample_rate);
                                alSourceQueueBuffers(source_, 1, &buffers_[n]);
                        }
-                       
+
                        alSourcei(source_, AL_LOOPING, AL_FALSE);
 
-                       alSourcePlay(source_);  
+                       alSourcePlay(source_);
                });
        }
 
@@ -196,13 +198,13 @@ public:
                // exhausted, which should not happen
                executor_.begin_invoke([=]
                {
-                       ALenum state; 
+                       ALenum state;
                        alGetSourcei(source_, AL_SOURCE_STATE,&state);
                        if(state != AL_PLAYING)
                        {
                                for(int n = 0; n < buffers_.size()-1; ++n)
-                               {                                       
-                                       ALuint buffer = 0;  
+                               {
+                                       ALuint buffer = 0;
                                        alSourceUnqueueBuffers(source_, 1, &buffer);
                                        if(buffer)
                                        {
@@ -211,13 +213,13 @@ public:
                                                alSourceQueueBuffers(source_, 1, &buffer);
                                        }
                                }
-                               alSourcePlay(source_);          
-                               graph_->set_tag("late-frame");  
+                               alSourcePlay(source_);
+                               graph_->set_tag(diagnostics::tag_severity::WARNING, "late-frame");
                        }
 
                        auto audio = core::audio_32_to_16(channel_remapper_->mix_and_rearrange(frame.audio_data()));
-                       
-                       ALuint buffer = 0;  
+
+                       ALuint buffer = 0;
                        alSourceUnqueueBuffers(source_, 1, &buffer);
                        if(buffer)
                        {
@@ -225,16 +227,16 @@ public:
                                alSourceQueueBuffers(source_, 1, &buffer);
                        }
                        else
-                               graph_->set_tag("dropped-frame");
+                               graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
 
-                       graph_->set_value("tick-time", perf_timer_.elapsed()*format_desc_.fps*0.5);             
+                       graph_->set_value("tick-time", perf_timer_.elapsed()*format_desc_.fps*0.5);
                        perf_timer_.restart();
-                       presentation_age_ = frame.get_age_millis() + delay_millis();
+                       presentation_age_ = frame.get_age_millis() + latency_millis();
                });
 
                return make_ready_future(true);
        }
-       
+
        std::wstring print() const override
        {
                return L"oal[" + boost::lexical_cast<std::wstring>(channel_index_) + L"|" + format_desc_.name + L"]";
@@ -251,24 +253,24 @@ public:
                info.add(L"type", L"system-audio");
                return info;
        }
-       
+
        bool has_synchronization_clock() const override
        {
                return false;
        }
 
-       int delay_millis() const
+       int latency_millis() const
        {
-               return 213;
+               return latency_millis_;
        }
-       
+
        int buffer_depth() const override
        {
-               int delay_in_frames = static_cast<int>(delay_millis() / (1000.0 / format_desc_.fps));
-               
+               int delay_in_frames = static_cast<int>(latency_millis() / (1000.0 / format_desc_.fps));
+
                return delay_in_frames;
        }
-               
+
        int index() const override
        {
                return 500;
@@ -283,50 +285,59 @@ public:
 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
 {
        sink.short_description(L"A system audio consumer.");
-       sink.syntax(L"AUDIO {CHANNEL_LAYOUT [channel_layout:string]}");
+       sink.syntax(L"AUDIO {CHANNEL_LAYOUT [channel_layout:string]} {LATENCY [latency_millis:int|200]}");
        sink.para()->text(L"Uses the system's default audio playback device.");
        sink.para()->text(L"Examples:");
        sink.example(L">> ADD 1 AUDIO");
        sink.example(L">> ADD 1 AUDIO CHANNEL_LAYOUT matrix", L"Uses the matrix channel layout");
+       sink.example(L">> ADD 1 AUDIO LATENCY 500", L"Specifies that the system-audio chain: openal => driver => sound card => speaker output is 500ms");
 }
 
-spl::shared_ptr<core::frame_consumer> create_consumer(const std::vector<std::wstring>& params, core::interaction_sink*)
+spl::shared_ptr<core::frame_consumer> create_consumer(
+               const std::vector<std::wstring>& params, core::interaction_sink*, std::vector<spl::shared_ptr<core::video_channel>> channels)
 {
        if(params.size() < 1 || !boost::iequals(params.at(0), L"AUDIO"))
                return core::frame_consumer::empty();
 
        auto channel_layout                     = core::audio_channel_layout::invalid();
        auto channel_layout_spec        = get_param(L"CHANNEL_LAYOUT", params);
-       
+
        if (!channel_layout_spec.empty())
        {
                auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(channel_layout_spec);
 
                if (!found_layout)
-                       CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"Channel layout " + channel_layout_spec + L" not found."));
+                       CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + channel_layout_spec + L" not found."));
 
                channel_layout = *found_layout;
        }
 
-       return spl::make_shared<oal_consumer>(channel_layout);
+       auto latency_millis                     = get_param(L"LATENCY", params, 200);
+
+       return spl::make_shared<oal_consumer>(channel_layout, latency_millis);
 }
 
-spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(const boost::property_tree::wptree& ptree, core::interaction_sink*)
+spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
+               const boost::property_tree::wptree& ptree, core::interaction_sink*, std::vector<spl::shared_ptr<core::video_channel>> channels)
 {
        auto channel_layout                     = core::audio_channel_layout::invalid();
        auto channel_layout_spec        = ptree.get_optional<std::wstring>(L"channel-layout");
 
        if (channel_layout_spec)
        {
+               CASPAR_SCOPED_CONTEXT_MSG("/channel-layout")
+
                auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(*channel_layout_spec);
 
                if (!found_layout)
-                       CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"Channel layout " + *channel_layout_spec + L" not found."));
+                       CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + *channel_layout_spec + L" not found."));
 
                channel_layout = *found_layout;
        }
 
-       return spl::make_shared<oal_consumer>(channel_layout);
+       auto latency_millis                     = ptree.get(L"latency", 200);
+
+       return spl::make_shared<oal_consumer>(channel_layout, latency_millis);
 }
 
 }}