123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- /*
- * openmpt123_waveout.hpp
- * ------------------------
- * Purpose: libopenmpt command line player
- * Notes : (currently none)
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #ifndef OPENMPT123_WAVEOUT_HPP
- #define OPENMPT123_WAVEOUT_HPP
- #include "openmpt123_config.hpp"
- #include "openmpt123.hpp"
- #if defined(WIN32)
- namespace openmpt123 {
- struct waveout_exception : public exception {
- waveout_exception() : exception( "waveout" ) { }
- };
- class waveout_stream_raii : public write_buffers_interface {
- private:
- HWAVEOUT waveout;
- std::size_t num_channels;
- std::size_t num_chunks;
- std::size_t frames_per_chunk;
- std::size_t bytes_per_chunk;
- std::vector<WAVEHDR> waveheaders;
- std::vector<std::vector<char> > wavebuffers;
- std::deque<char> byte_queue;
- public:
- waveout_stream_raii( commandlineflags & flags )
- : waveout(NULL)
- , num_channels(0)
- , num_chunks(0)
- , frames_per_chunk(0)
- , bytes_per_chunk(0)
- {
- if ( flags.buffer == default_high ) {
- flags.buffer = 150;
- } else if ( flags.buffer == default_low ) {
- flags.buffer = 50;
- }
- if ( flags.period == default_high ) {
- flags.period = 30;
- } else if ( flags.period == default_low ) {
- flags.period = 10;
- }
- flags.apply_default_buffer_sizes();
- WAVEFORMATEX wfx;
- ZeroMemory( &wfx, sizeof( wfx ) );
- wfx.wFormatTag = flags.use_float ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
- wfx.nChannels = static_cast<WORD>( flags.channels );
- wfx.nSamplesPerSec = flags.samplerate;
- wfx.wBitsPerSample = flags.use_float ? 32 : 16;
- wfx.nBlockAlign = ( wfx.wBitsPerSample / 8 ) * wfx.nChannels;
- wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
- wfx.cbSize = 0;
- std::istringstream device_string( flags.device );
- int device = -1;
- device_string >> device;
- waveOutOpen( &waveout, device == -1 ? WAVE_MAPPER : device, &wfx, 0, 0, CALLBACK_NULL );
- num_channels = flags.channels;
- std::size_t frames_per_buffer = flags.samplerate * flags.buffer / 1000;
- num_chunks = ( flags.buffer + flags.period - 1 ) / flags.period;
- if ( num_chunks < 2 ) {
- num_chunks = 2;
- }
- frames_per_chunk = ( frames_per_buffer + num_chunks - 1 ) / num_chunks;
- bytes_per_chunk = wfx.nBlockAlign * frames_per_chunk;
- waveheaders.resize( num_chunks );
- wavebuffers.resize( num_chunks );
- for ( std::size_t i = 0; i < num_chunks; ++i ) {
- wavebuffers[i].resize( bytes_per_chunk );
- waveheaders[i] = WAVEHDR();
- waveheaders[i].lpData = wavebuffers[i].data();
- waveheaders[i].dwBufferLength = static_cast<DWORD>( wavebuffers[i].size() );
- waveheaders[i].dwFlags = 0;
- waveOutPrepareHeader( waveout, &waveheaders[i], sizeof( WAVEHDR ) );
- }
- }
- ~waveout_stream_raii() {
- if ( waveout ) {
- write_or_wait( true );
- drain();
- waveOutReset( waveout );
- for ( std::size_t i = 0; i < num_chunks; ++i ) {
- waveheaders[i].dwBufferLength = static_cast<DWORD>( wavebuffers[i].size() );
- waveOutUnprepareHeader( waveout, &waveheaders[i], sizeof( WAVEHDR ) );
- }
- wavebuffers.clear();
- waveheaders.clear();
- frames_per_chunk = 0;
- num_chunks = 0;
- waveOutClose( waveout );
- waveout = NULL;
- }
- }
- private:
- void drain() {
- std::size_t empty_chunks = 0;
- while ( empty_chunks != num_chunks ) {
- empty_chunks = 0;
- for ( std::size_t chunk = 0; chunk < num_chunks; ++chunk ) {
- DWORD flags = waveheaders[chunk].dwFlags;
- if ( !(flags & WHDR_INQUEUE) || (flags & WHDR_DONE) ) {
- empty_chunks++;
- }
- }
- if ( empty_chunks != num_chunks ) {
- Sleep( 1 );
- }
- }
- }
- std::size_t wait_for_empty_chunk() {
- while ( true ) {
- for ( std::size_t chunk = 0; chunk < num_chunks; ++chunk ) {
- DWORD flags = waveheaders[chunk].dwFlags;
- if ( !(flags & WHDR_INQUEUE) || (flags & WHDR_DONE) ) {
- return chunk;
- }
- }
- Sleep( 1 );
- }
- }
- void write_chunk() {
- std::size_t chunk = wait_for_empty_chunk();
- std::size_t chunk_bytes = std::min( byte_queue.size(), bytes_per_chunk );
- waveheaders[chunk].dwBufferLength = static_cast<DWORD>( chunk_bytes );
- for ( std::size_t byte = 0; byte < chunk_bytes; ++byte ) {
- wavebuffers[chunk][byte] = byte_queue.front();
- byte_queue.pop_front();
- }
- waveOutWrite( waveout, &waveheaders[chunk], sizeof( WAVEHDR ) );
- }
- void write_or_wait( bool flush = false ) {
- while ( byte_queue.size() >= bytes_per_chunk ) {
- write_chunk();
- }
- if ( flush && !byte_queue.empty() ) {
- write_chunk();
- }
- }
- template < typename Tsample >
- void write_buffers( const std::vector<Tsample*> buffers, std::size_t frames ) {
- for ( std::size_t frame = 0; frame < frames; ++frame ) {
- for ( std::size_t channel = 0; channel < buffers.size(); ++channel ) {
- Tsample val = buffers[channel][frame];
- char buf[ sizeof( Tsample ) ];
- std::memcpy( buf, &val, sizeof( Tsample ) );
- std::copy( buf, buf + sizeof( Tsample ), std::back_inserter( byte_queue ) );
- }
- }
- write_or_wait();
- }
- public:
- void write( const std::vector<float*> buffers, std::size_t frames ) override {
- write_buffers( buffers, frames );
- }
- void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
- write_buffers( buffers, frames );
- }
- bool pause() override {
- waveOutPause( waveout );
- return true;
- }
- bool unpause() override {
- waveOutRestart( waveout );
- return true;
- }
- bool sleep( int ms ) override {
- Sleep( ms );
- return true;
- }
- };
- static std::string show_waveout_devices( std::ostream & /*log*/ ) {
- std::ostringstream devices;
- devices << " waveout:" << std::endl;
- for ( UINT i = 0; i < waveOutGetNumDevs(); ++i ) {
- devices << " " << i << ": ";
- WAVEOUTCAPSW caps;
- ZeroMemory( &caps, sizeof( caps ) );
- waveOutGetDevCapsW( i, &caps, sizeof( caps ) );
- devices << mpt::transcode<std::string>( mpt::common_encoding::utf8, caps.szPname );
- devices << std::endl;
- }
- return devices.str();
- }
- } // namespace openmpt123
- #endif // WIN32
- #endif // OPENMPT123_WAVEOUT_HPP
|