first init of 0.9
This commit is contained in:
237
source/audio/detail/audio_device.cpp
Normal file
237
source/audio/detail/audio_device.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
#include <nana/audio/detail/audio_device.hpp>
|
||||
#include <nana/system/platform.hpp>
|
||||
|
||||
#if defined(NANA_LINUX)
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
namespace nana{namespace audio
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
#if defined(NANA_WINDOWS)
|
||||
class wave_native
|
||||
{
|
||||
typedef MMRESULT (__stdcall *out_open_t)(LPHWAVEOUT, UINT_PTR, LPWAVEFORMATEX, DWORD_PTR, DWORD_PTR, DWORD);
|
||||
typedef MMRESULT (__stdcall *out_close_t)(HWAVEOUT);
|
||||
typedef MMRESULT (__stdcall *out_op_header_t)(HWAVEOUT, LPWAVEHDR, UINT);
|
||||
public:
|
||||
out_open_t out_open;
|
||||
out_close_t out_close;
|
||||
out_op_header_t out_write;
|
||||
out_op_header_t out_prepare;
|
||||
out_op_header_t out_unprepare;
|
||||
|
||||
wave_native()
|
||||
{
|
||||
HMODULE winmm = ::GetModuleHandleA("Winmm.DLL");
|
||||
if(0 == winmm)
|
||||
winmm = ::LoadLibraryA("Winmm.DLL");
|
||||
|
||||
out_open = reinterpret_cast<out_open_t>(::GetProcAddress(winmm, "waveOutOpen"));
|
||||
out_close = reinterpret_cast<out_close_t>(::GetProcAddress(winmm, "waveOutClose"));
|
||||
out_write = reinterpret_cast<out_op_header_t>(::GetProcAddress(winmm, "waveOutWrite"));
|
||||
out_prepare = reinterpret_cast<out_op_header_t>(::GetProcAddress(winmm, "waveOutPrepareHeader"));
|
||||
out_unprepare = reinterpret_cast<out_op_header_t>(::GetProcAddress(winmm, "waveOutUnprepareHeader"));
|
||||
}
|
||||
}wave_native_if;
|
||||
#endif
|
||||
//class audio_device
|
||||
audio_device::audio_device()
|
||||
#if defined(NANA_WINDOWS)
|
||||
: handle_(nullptr), buf_prep_(nullptr)
|
||||
#elif defined(NANA_LINUX)
|
||||
: handle_(nullptr), buf_prep_(nullptr)
|
||||
#endif
|
||||
{}
|
||||
|
||||
audio_device::~audio_device()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool audio_device::empty() const
|
||||
{
|
||||
return (nullptr == handle_);
|
||||
}
|
||||
|
||||
bool audio_device::open(std::size_t channels, std::size_t rate, std::size_t bits_per_sample)
|
||||
{
|
||||
#if defined(NANA_WINDOWS)
|
||||
close();
|
||||
|
||||
WAVEFORMATEX wfx;
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nChannels = static_cast<WORD>(channels);
|
||||
wfx.nSamplesPerSec = static_cast<DWORD>(rate);
|
||||
wfx.wBitsPerSample = static_cast<WORD>(bits_per_sample);
|
||||
|
||||
wfx.nBlockAlign = (wfx.wBitsPerSample >> 3 ) * wfx.nChannels;
|
||||
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
|
||||
wfx.cbSize = 0;
|
||||
|
||||
MMRESULT mmr = wave_native_if.out_open(&handle_, WAVE_MAPPER, &wfx, reinterpret_cast<DWORD_PTR>(&audio_device::_m_dev_callback), reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION);
|
||||
return (mmr == MMSYSERR_NOERROR);
|
||||
#elif defined(NANA_LINUX)
|
||||
if(nullptr == handle_)
|
||||
{
|
||||
if(::snd_pcm_open(&handle_, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, 0) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if(handle_)
|
||||
{
|
||||
channels_ = channels;
|
||||
rate_ = rate;
|
||||
bytes_per_sample_ = (bits_per_sample >> 3);
|
||||
bytes_per_frame_ = bytes_per_sample_ * channels;
|
||||
|
||||
snd_pcm_hw_params_t * params;
|
||||
if(snd_pcm_hw_params_malloc(¶ms) < 0)
|
||||
{
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(::snd_pcm_hw_params_any(handle_, params) < 0)
|
||||
{
|
||||
close();
|
||||
::snd_pcm_hw_params_free(params);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(::snd_pcm_hw_params_set_access(handle_, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
|
||||
{
|
||||
close();
|
||||
::snd_pcm_hw_params_free(params);
|
||||
return false;
|
||||
}
|
||||
|
||||
snd_pcm_format_t format = SND_PCM_FORMAT_U8;
|
||||
switch(bits_per_sample)
|
||||
{
|
||||
case 8:
|
||||
format = SND_PCM_FORMAT_U8; break;
|
||||
case 16:
|
||||
format = SND_PCM_FORMAT_S16_LE; break;
|
||||
case 32:
|
||||
format = SND_PCM_FORMAT_S32_LE; break;
|
||||
}
|
||||
|
||||
if(::snd_pcm_hw_params_set_format(handle_, params, format) < 0)
|
||||
{
|
||||
close();
|
||||
::snd_pcm_hw_params_free(params);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned tmp = rate;
|
||||
if(::snd_pcm_hw_params_set_rate_near(handle_, params, &tmp, 0) < 0)
|
||||
{
|
||||
close();
|
||||
::snd_pcm_hw_params_free(params);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(::snd_pcm_hw_params_set_channels(handle_, params, channels) < 0)
|
||||
{
|
||||
close();
|
||||
::snd_pcm_hw_params_free(params);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(::snd_pcm_hw_params(handle_, params) < 0)
|
||||
{
|
||||
close();
|
||||
::snd_pcm_hw_params_free(params);
|
||||
return false;
|
||||
}
|
||||
|
||||
::snd_pcm_hw_params_free(params);
|
||||
::snd_pcm_prepare(handle_);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void audio_device::close()
|
||||
{
|
||||
if(handle_)
|
||||
{
|
||||
#if defined(NANA_WINDOWS)
|
||||
wave_native_if.out_close(handle_);
|
||||
#elif defined(NANA_LINUX)
|
||||
::snd_pcm_close(handle_);
|
||||
#endif
|
||||
handle_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void audio_device::prepare(buffer_preparation & buf_prep)
|
||||
{
|
||||
buf_prep_ = & buf_prep;
|
||||
}
|
||||
|
||||
void audio_device::write(buffer_preparation::meta * m)
|
||||
{
|
||||
#if defined(NANA_WINDOWS)
|
||||
std::lock_guard<decltype(queue_lock_)> lock(queue_lock_);
|
||||
done_queue_.push_back(m);
|
||||
if(m->dwFlags & WHDR_PREPARED)
|
||||
wave_native_if.out_unprepare(handle_, m, sizeof(WAVEHDR));
|
||||
|
||||
wave_native_if.out_prepare(handle_, m, sizeof(WAVEHDR));
|
||||
wave_native_if.out_write(handle_, m, sizeof(WAVEHDR));
|
||||
#elif defined(NANA_LINUX)
|
||||
std::size_t frames = m->bufsize / bytes_per_frame_;
|
||||
std::size_t buffered = 0; //in bytes
|
||||
while(frames > 0)
|
||||
{
|
||||
int err = ::snd_pcm_writei(handle_, m->buf + buffered, frames);
|
||||
if(err > 0)
|
||||
{
|
||||
frames -= err;
|
||||
buffered += err * bytes_per_frame_;
|
||||
}
|
||||
else if(-EPIPE == err)
|
||||
::snd_pcm_prepare(handle_);
|
||||
}
|
||||
buf_prep_->revert(m);
|
||||
#endif
|
||||
}
|
||||
|
||||
void audio_device::wait_for_drain() const
|
||||
{
|
||||
#if defined(NANA_WINDOWS)
|
||||
while(buf_prep_->data_finished() == false)
|
||||
nana::system::sleep(200);
|
||||
#elif defined(NANA_LINUX)
|
||||
while(::snd_pcm_state(handle_) == SND_PCM_STATE_RUNNING)
|
||||
nana::system::sleep(200);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(NANA_WINDOWS)
|
||||
void __stdcall audio_device::_m_dev_callback(HWAVEOUT handle, UINT msg, audio_device * self, DWORD_PTR, DWORD_PTR)
|
||||
{
|
||||
if(WOM_DONE == msg)
|
||||
{
|
||||
buffer_preparation::meta * m;
|
||||
{
|
||||
std::lock_guard<decltype(queue_lock_)> lock(self->queue_lock_);
|
||||
m = self->done_queue_.front();
|
||||
self->done_queue_.erase(self->done_queue_.begin());
|
||||
}
|
||||
wave_native_if.out_unprepare(handle, m, sizeof(WAVEHDR));
|
||||
self->buf_prep_->revert(m);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//end class audio_device
|
||||
}//end namespace detail
|
||||
}//end namespace audio
|
||||
}//end namespace nana
|
||||
91
source/audio/detail/audio_stream.cpp
Normal file
91
source/audio/detail/audio_stream.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include <nana/audio/detail/audio_stream.hpp>
|
||||
#include <nana/charset.hpp>
|
||||
|
||||
namespace nana{ namespace audio
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
//class audio_stream
|
||||
bool audio_stream::open(const nana::string& file)
|
||||
{
|
||||
fs_.open(static_cast<std::string>(nana::charset(file)), std::ios::binary);
|
||||
if(fs_)
|
||||
{
|
||||
wave_spec::master_riff_chunk riff;
|
||||
fs_.read(reinterpret_cast<char*>(&riff), sizeof(riff));
|
||||
if(riff.ckID == *reinterpret_cast<const unsigned long*>("RIFF") && riff.waveID == *reinterpret_cast<const unsigned long*>("WAVE"))
|
||||
{
|
||||
fs_.read(reinterpret_cast<char*>(&ck_format_), sizeof(ck_format_));
|
||||
if(ck_format_.ckID == *reinterpret_cast<const unsigned long*>("fmt ") && ck_format_.wFormatTag == 1) //Only support PCM format
|
||||
{
|
||||
std::size_t cksize = _m_locate_chunck(*reinterpret_cast<const unsigned long*>("data"));
|
||||
if(cksize)
|
||||
{
|
||||
pcm_data_pos_ = static_cast<std::size_t>(fs_.tellg());
|
||||
pcm_data_size_ = cksize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void audio_stream::close()
|
||||
{
|
||||
fs_.close();
|
||||
}
|
||||
|
||||
bool audio_stream::empty() const
|
||||
{
|
||||
return (!fs_);
|
||||
}
|
||||
|
||||
const wave_spec::format_chunck & audio_stream::format() const
|
||||
{
|
||||
return ck_format_;
|
||||
}
|
||||
|
||||
std::size_t audio_stream::data_length() const
|
||||
{
|
||||
return data_size_;
|
||||
}
|
||||
|
||||
void audio_stream::locate()
|
||||
{
|
||||
fs_.clear();
|
||||
fs_.seekg(pcm_data_pos_, std::ios::beg);
|
||||
data_size_ = pcm_data_size_;
|
||||
}
|
||||
|
||||
std::size_t audio_stream::read(void * buf, std::size_t len)
|
||||
{
|
||||
fs_.read(reinterpret_cast<char*>(buf), static_cast<std::streamsize>(len <= data_size_ ? len : data_size_));
|
||||
std::size_t read_bytes = static_cast<std::size_t>(fs_.gcount());
|
||||
data_size_ -= read_bytes;
|
||||
return read_bytes;
|
||||
}
|
||||
|
||||
std::size_t audio_stream::_m_locate_chunck(unsigned ckID)
|
||||
{
|
||||
chunck ck;
|
||||
while(true)
|
||||
{
|
||||
fs_.read(reinterpret_cast<char*>(&ck), sizeof(ck));
|
||||
if(fs_.gcount() != sizeof(ck))
|
||||
break;
|
||||
|
||||
if(ck.ckID == ckID)
|
||||
return ck.cksize;
|
||||
if(ck.ckID == *reinterpret_cast<const unsigned long*>("data"))
|
||||
fs_.seekg(ck.cksize + (ck.cksize & 1 ? 1 : 0), std::ios::cur);
|
||||
else
|
||||
fs_.seekg(ck.cksize, std::ios::cur);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//end class audio_stream
|
||||
}//end namespace detail
|
||||
|
||||
}//end namespace audio
|
||||
}//end namespace nana
|
||||
161
source/audio/detail/buffer_preparation.cpp
Normal file
161
source/audio/detail/buffer_preparation.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include <nana/audio/detail/buffer_preparation.hpp>
|
||||
#include <cstring>
|
||||
|
||||
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<meta*>(rawbuf);
|
||||
#if defined(NANA_WINDOWS)
|
||||
memset(m, 0, sizeof(meta));
|
||||
m->dwBufferLength = static_cast<unsigned long>(block_size_);
|
||||
m->lpData = rawbuf + sizeof(meta);
|
||||
#elif defined(NANA_LINUX)
|
||||
m->bufsize = ck.nAvgBytesPerSec;
|
||||
m->buf = rawbuf + sizeof(meta);
|
||||
#endif
|
||||
prepared_.push_back(m);
|
||||
}
|
||||
|
||||
thr_ = std::move(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<char*>(metaptr);
|
||||
}
|
||||
|
||||
buffer_preparation::meta * buffer_preparation::read()
|
||||
{
|
||||
std::unique_lock<decltype(token_buffer_)> 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<decltype(token_prepared_)> lock(token_prepared_);
|
||||
bool if_signal = prepared_.empty();
|
||||
prepared_.push_back(m);
|
||||
if(if_signal)
|
||||
cond_prepared_.notify_one();
|
||||
}
|
||||
|
||||
bool buffer_preparation::data_finished() const
|
||||
{
|
||||
std::lock_guard<decltype(token_prepared_)> 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<decltype(token_prepared_)> 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(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<decltype(token_buffer_)> lock(token_buffer_);
|
||||
cond_buffer_.notify_one();
|
||||
running_ = false;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if defined(NANA_WINDOWS)
|
||||
m->dwBufferLength = static_cast<unsigned long>(buffered);
|
||||
#elif defined(NANA_LINUX)
|
||||
m->bufsize = buffered;
|
||||
#endif
|
||||
std::lock_guard<decltype(token_buffer_)> lock(token_buffer_);
|
||||
buffer_.push_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
|
||||
Reference in New Issue
Block a user