X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Foal%2Fconsumer%2Foal_consumer.cpp;h=e0084ee3e384a8f4b68c340b713a5cf25efeced1;hb=52c5223f43ad44f8990608a65778e65b021aa4ff;hp=cd0130a5fedac3dbdd001b8eb5d809f58ad8e90b;hpb=7dd8bdd6999be5bf21f6963a7b4ed312806244e4;p=casparcg diff --git a/modules/oal/consumer/oal_consumer.cpp b/modules/oal/consumer/oal_consumer.cpp index cd0130a5f..e0084ee3e 100644 --- a/modules/oal/consumer/oal_consumer.cpp +++ b/modules/oal/consumer/oal_consumer.cpp @@ -1,139 +1,276 @@ -/* -* 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 "oal_consumer.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include - -#include -#include - -#include - -namespace caspar { - -struct oal_consumer : public core::frame_consumer, public sf::SoundStream -{ - safe_ptr graph_; - boost::timer perf_timer_; - - tbb::concurrent_bounded_queue>>> input_; - boost::circular_buffer>> container_; - tbb::atomic is_running_; - - core::video_format_desc format_desc_; - int preroll_count_; -public: - oal_consumer() - : graph_(diagnostics::create_graph(narrow(print()))) - , container_(16) - , preroll_count_(0) - { - if(core::consumer_buffer_depth() < 3) - BOOST_THROW_EXCEPTION(invalid_argument() << msg_info("audio-consumer does not support buffer-depth lower than 3.")); - - graph_->add_guide("tick-time", 0.5); - graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f)); - is_running_ = true; - input_.set_capacity(core::consumer_buffer_depth()-2); - } - - ~oal_consumer() - { - is_running_ = false; - input_.try_push(std::make_shared>>()); - input_.try_push(std::make_shared>>()); - Stop(); - CASPAR_LOG(info) << print() << L" Shutting down."; - } - - virtual void initialize(const core::video_format_desc& format_desc) - { - format_desc_ = format_desc; - sf::SoundStream::Initialize(2, 48000); - CASPAR_LOG(info) << print() << " Sucessfully initialized."; - } - - virtual bool send(const safe_ptr& frame) - { - if(preroll_count_ < input_.capacity()) - { - while(input_.try_push(std::make_shared>>(format_desc_.audio_samples_per_frame, 0))) - ++preroll_count_; - Play(); - } - - input_.push(std::make_shared>>(core::audio_32_to_16_sse(frame->audio_data()))); - - return true; - } - - virtual bool OnGetData(sf::SoundStream::Chunk& data) - { - std::shared_ptr>> audio_data; - input_.pop(audio_data); - - container_.push_back(std::move(*audio_data)); - data.Samples = container_.back().data(); - data.NbSamples = container_.back().size(); - - graph_->update_value("tick-time", perf_timer_.elapsed()*format_desc_.fps*0.5); - perf_timer_.restart(); - - return is_running_; - } - - virtual std::wstring print() const - { - return L"oal[" + format_desc_.name + L"]"; - } - - virtual const core::video_format_desc& get_video_format_desc() const - { - return format_desc_; - } -}; - -safe_ptr create_oal_consumer(const std::vector& params) -{ - if(params.size() < 1 || params[0] != L"AUDIO") - return core::frame_consumer::empty(); - - return make_safe(); -} - -safe_ptr create_oal_consumer() -{ - return make_safe(); -} - -} +/* +* Copyright (c) 2011 Sveriges Television AB +* +* This file is part of CasparCG (www.casparcg.com). +* +* 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 . +* +* Author: Robert Nagy, ronag89@gmail.com +*/ + +#include "oal_consumer.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +namespace caspar { namespace oal { + +typedef std::vector> audio_buffer_16; + +class device +{ + ALCdevice* device_; + ALCcontext* context_; + +public: + device() + : device_(0) + , context_(0) + { + device_ = alcOpenDevice(nullptr); + + if(!device_) + CASPAR_THROW_EXCEPTION(invalid_operation() << msg_info("Failed to initialize audio device.")); + + context_ = alcCreateContext(device_, nullptr); + + 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.")); + } + + ~device() + { + alcMakeContextCurrent(nullptr); + + if(context_) + alcDestroyContext(context_); + + if(device_) + alcCloseDevice(device_); + } + + ALCdevice* get() + { + return device_; + } +}; + +void init_device() +{ + static std::unique_ptr instance; + static boost::once_flag f = BOOST_ONCE_INIT; + + boost::call_once(f, []{instance.reset(new device());}); +} + +struct oal_consumer : public core::frame_consumer +{ + spl::shared_ptr graph_; + boost::timer perf_timer_; + int channel_index_; + + core::video_format_desc format_desc_; + + ALuint source_; + std::array buffers_; + + executor executor_; + +public: + oal_consumer() + : channel_index_(-1) + , source_(0) + , executor_(L"oal_consumer") + { + buffers_.assign(0); + + init_device(); + + 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_); + } + + ~oal_consumer() + { + executor_.begin_invoke([=] + { + if(source_) + { + alSourceStop(source_); + alDeleteSources(1, &source_); + } + + BOOST_FOREACH(auto& buffer, buffers_) + { + if(buffer) + alDeleteBuffers(1, &buffer); + }; + }); + } + + // frame consumer + + void initialize(const core::video_format_desc& format_desc, int channel_index) override + { + format_desc_ = format_desc; + channel_index_ = channel_index; + graph_->set_text(print()); + + executor_.begin_invoke([=] + { + alGenBuffers(static_cast(buffers_.size()), buffers_.data()); + alGenSources(1, &source_); + + for(std::size_t n = 0; n < buffers_.size(); ++n) + { + std::vector audio(format_desc_.audio_cadence[n % format_desc_.audio_cadence.size()], 0); + alBufferData(buffers_[n], AL_FORMAT_STEREO16, audio.data(), static_cast(audio.size()*sizeof(int16_t)), format_desc_.audio_sample_rate); + alSourceQueueBuffers(source_, 1, &buffers_[n]); + } + + alSourcei(source_, AL_LOOPING, AL_FALSE); + + alSourcePlay(source_); + }); + } + + boost::unique_future send(core::const_frame frame) override + { + // Will only block if the default executor queue capacity of 512 is + // exhausted, which should not happen + executor_.begin_invoke([=] + { + ALenum state; + alGetSourcei(source_, AL_SOURCE_STATE,&state); + if(state != AL_PLAYING) + { + for(int n = 0; n < buffers_.size()-1; ++n) + { + ALuint buffer = 0; + alSourceUnqueueBuffers(source_, 1, &buffer); + if(buffer) + { + std::vector audio(format_desc_.audio_cadence[n % format_desc_.audio_cadence.size()], 0); + alBufferData(buffer, AL_FORMAT_STEREO16, audio.data(), static_cast(audio.size()*sizeof(int16_t)), format_desc_.audio_sample_rate); + alSourceQueueBuffers(source_, 1, &buffer); + } + } + alSourcePlay(source_); + graph_->set_tag("late-frame"); + } + + auto audio = core::audio_32_to_16(frame.audio_data()); + + ALuint buffer = 0; + alSourceUnqueueBuffers(source_, 1, &buffer); + if(buffer) + { + alBufferData(buffer, AL_FORMAT_STEREO16, audio.data(), static_cast(audio.size()*sizeof(int16_t)), format_desc_.audio_sample_rate); + alSourceQueueBuffers(source_, 1, &buffer); + } + else + graph_->set_tag("dropped-frame"); + + graph_->set_value("tick-time", perf_timer_.elapsed()*format_desc_.fps*0.5); + perf_timer_.restart(); + }); + + return wrap_as_future(true); + } + + std::wstring print() const override + { + return L"oal[" + boost::lexical_cast(channel_index_) + L"|" + format_desc_.name + L"]"; + } + + std::wstring name() const override + { + return L"system-audio"; + } + + boost::property_tree::wptree info() const override + { + boost::property_tree::wptree info; + info.add(L"type", L"system-audio"); + return info; + } + + bool has_synchronization_clock() const override + { + return false; + } + + int buffer_depth() const override + { + return 3; + } + + int index() const override + { + return 500; + } + + void subscribe(const monitor::observable::observer_ptr& o) override + { + } + + void unsubscribe(const monitor::observable::observer_ptr& o) override + { + } +}; + +spl::shared_ptr create_consumer(const std::vector& params) +{ + if(params.size() < 1 || params[0] != L"AUDIO") + return core::frame_consumer::empty(); + + return spl::make_shared(); +} + +spl::shared_ptr create_consumer() +{ + return spl::make_shared(); +} + +}}