#include #ifdef NANA_ENABLE_AUDIO #include namespace nana{ namespace audio { namespace detail { //class buffer_preparation buffer_preparation::buffer_preparation(audio_stream& as, std::size_t seconds) : running_(true), wait_for_buffer_(false), as_(as) { //Allocate the space buffer_.reserve(seconds); prepared_.reserve(seconds); const wave_spec::format_chunck & ck = as.format(); block_size_ = ck.nAvgBytesPerSec; for(std::size_t i = 0; i < seconds; ++i) { char * rawbuf = new char[sizeof(meta) + ck.nAvgBytesPerSec]; meta * m = reinterpret_cast(rawbuf); #if defined(NANA_WINDOWS) memset(m, 0, sizeof(meta)); m->dwBufferLength = static_cast(block_size_); m->lpData = rawbuf + sizeof(meta); #elif defined(__FreeBSD__) #elif defined(NANA_LINUX) m->bufsize = ck.nAvgBytesPerSec; m->buf = rawbuf + sizeof(meta); #endif prepared_.emplace_back(m); } thr_ = std::thread{[this](){this->_m_prepare_routine();}}; } buffer_preparation::~buffer_preparation() { running_ = false; cond_prepared_.notify_one(); cond_buffer_.notify_one(); if(thr_.joinable()) thr_.join(); for(auto metaptr : prepared_) delete [] reinterpret_cast(metaptr); } buffer_preparation::meta * buffer_preparation::read() { std::unique_lock lock(token_buffer_); //Wait for the buffer if(0 == buffer_.size()) { //Before waiting, checks the thread whether it is finished //it indicates the preparation is finished. if(false == running_) return nullptr; wait_for_buffer_ = true; cond_buffer_.wait(lock); //NO more buffers if(0 == buffer_.size()) return nullptr; } meta * m = buffer_.front(); buffer_.erase(buffer_.begin()); return m; } //Revert the meta that returned by read() void buffer_preparation::revert(meta * m) { std::lock_guard lock(token_prepared_); auto const if_signal = prepared_.empty(); prepared_.emplace_back(m); if(if_signal) cond_prepared_.notify_one(); } bool buffer_preparation::data_finished() const { std::lock_guard lock(token_prepared_); return ((prepared_.size() == prepared_.capacity()) && (running_ == false)); } void buffer_preparation::_m_prepare_routine() { const std::size_t fixed_bufsize = 1024; char buf[fixed_bufsize]; const std::size_t block_size = block_size_; while(running_) { meta * m = 0; { std::unique_lock lock(token_prepared_); if(prepared_.size() == 0) { cond_prepared_.wait(lock); if(false == running_) return; } m = prepared_.back(); prepared_.pop_back(); } std::size_t buffered = 0; while(buffered != block_size) { std::size_t want_bytes = block_size - buffered; if(want_bytes > fixed_bufsize) want_bytes = fixed_bufsize; std::size_t read_bytes = as_.read(buf, want_bytes); if(read_bytes) { #if defined(NANA_WINDOWS) memcpy(m->lpData + buffered, buf, read_bytes); #elif defined(__FreeBSD__) #elif defined(NANA_LINUX) memcpy(m->buf + buffered, buf, read_bytes); #endif buffered += read_bytes; } else if(0 == as_.data_length()) { if(buffered == 0) { //PCM data is drained std::lock_guard lock(token_buffer_); cond_buffer_.notify_one(); running_ = false; return; } break; } } #if defined(NANA_WINDOWS) m->dwBufferLength = static_cast(buffered); #elif defined(__FreeBSD__) #elif defined(NANA_LINUX) m->bufsize = buffered; #endif std::lock_guard lock(token_buffer_); buffer_.emplace_back(m); if(wait_for_buffer_) { cond_buffer_.notify_one(); wait_for_buffer_ = false; } if(0 == as_.data_length()) running_ = false; } } //end class buffer_preparation }//end namespace detail }//end namespace audio }//end namespace nana #endif //NANA_ENABLE_AUDIO