1
0

openmpt123_waveout.hpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /*
  2. * openmpt123_waveout.hpp
  3. * ------------------------
  4. * Purpose: libopenmpt command line player
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #ifndef OPENMPT123_WAVEOUT_HPP
  10. #define OPENMPT123_WAVEOUT_HPP
  11. #include "openmpt123_config.hpp"
  12. #include "openmpt123.hpp"
  13. #if defined(WIN32)
  14. namespace openmpt123 {
  15. struct waveout_exception : public exception {
  16. waveout_exception() : exception( "waveout" ) { }
  17. };
  18. class waveout_stream_raii : public write_buffers_interface {
  19. private:
  20. HWAVEOUT waveout;
  21. std::size_t num_channels;
  22. std::size_t num_chunks;
  23. std::size_t frames_per_chunk;
  24. std::size_t bytes_per_chunk;
  25. std::vector<WAVEHDR> waveheaders;
  26. std::vector<std::vector<char> > wavebuffers;
  27. std::deque<char> byte_queue;
  28. public:
  29. waveout_stream_raii( commandlineflags & flags )
  30. : waveout(NULL)
  31. , num_channels(0)
  32. , num_chunks(0)
  33. , frames_per_chunk(0)
  34. , bytes_per_chunk(0)
  35. {
  36. if ( flags.buffer == default_high ) {
  37. flags.buffer = 150;
  38. } else if ( flags.buffer == default_low ) {
  39. flags.buffer = 50;
  40. }
  41. if ( flags.period == default_high ) {
  42. flags.period = 30;
  43. } else if ( flags.period == default_low ) {
  44. flags.period = 10;
  45. }
  46. flags.apply_default_buffer_sizes();
  47. WAVEFORMATEX wfx;
  48. ZeroMemory( &wfx, sizeof( wfx ) );
  49. wfx.wFormatTag = flags.use_float ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
  50. wfx.nChannels = static_cast<WORD>( flags.channels );
  51. wfx.nSamplesPerSec = flags.samplerate;
  52. wfx.wBitsPerSample = flags.use_float ? 32 : 16;
  53. wfx.nBlockAlign = ( wfx.wBitsPerSample / 8 ) * wfx.nChannels;
  54. wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
  55. wfx.cbSize = 0;
  56. std::istringstream device_string( flags.device );
  57. int device = -1;
  58. device_string >> device;
  59. waveOutOpen( &waveout, device == -1 ? WAVE_MAPPER : device, &wfx, 0, 0, CALLBACK_NULL );
  60. num_channels = flags.channels;
  61. std::size_t frames_per_buffer = flags.samplerate * flags.buffer / 1000;
  62. num_chunks = ( flags.buffer + flags.period - 1 ) / flags.period;
  63. if ( num_chunks < 2 ) {
  64. num_chunks = 2;
  65. }
  66. frames_per_chunk = ( frames_per_buffer + num_chunks - 1 ) / num_chunks;
  67. bytes_per_chunk = wfx.nBlockAlign * frames_per_chunk;
  68. waveheaders.resize( num_chunks );
  69. wavebuffers.resize( num_chunks );
  70. for ( std::size_t i = 0; i < num_chunks; ++i ) {
  71. wavebuffers[i].resize( bytes_per_chunk );
  72. waveheaders[i] = WAVEHDR();
  73. waveheaders[i].lpData = wavebuffers[i].data();
  74. waveheaders[i].dwBufferLength = static_cast<DWORD>( wavebuffers[i].size() );
  75. waveheaders[i].dwFlags = 0;
  76. waveOutPrepareHeader( waveout, &waveheaders[i], sizeof( WAVEHDR ) );
  77. }
  78. }
  79. ~waveout_stream_raii() {
  80. if ( waveout ) {
  81. write_or_wait( true );
  82. drain();
  83. waveOutReset( waveout );
  84. for ( std::size_t i = 0; i < num_chunks; ++i ) {
  85. waveheaders[i].dwBufferLength = static_cast<DWORD>( wavebuffers[i].size() );
  86. waveOutUnprepareHeader( waveout, &waveheaders[i], sizeof( WAVEHDR ) );
  87. }
  88. wavebuffers.clear();
  89. waveheaders.clear();
  90. frames_per_chunk = 0;
  91. num_chunks = 0;
  92. waveOutClose( waveout );
  93. waveout = NULL;
  94. }
  95. }
  96. private:
  97. void drain() {
  98. std::size_t empty_chunks = 0;
  99. while ( empty_chunks != num_chunks ) {
  100. empty_chunks = 0;
  101. for ( std::size_t chunk = 0; chunk < num_chunks; ++chunk ) {
  102. DWORD flags = waveheaders[chunk].dwFlags;
  103. if ( !(flags & WHDR_INQUEUE) || (flags & WHDR_DONE) ) {
  104. empty_chunks++;
  105. }
  106. }
  107. if ( empty_chunks != num_chunks ) {
  108. Sleep( 1 );
  109. }
  110. }
  111. }
  112. std::size_t wait_for_empty_chunk() {
  113. while ( true ) {
  114. for ( std::size_t chunk = 0; chunk < num_chunks; ++chunk ) {
  115. DWORD flags = waveheaders[chunk].dwFlags;
  116. if ( !(flags & WHDR_INQUEUE) || (flags & WHDR_DONE) ) {
  117. return chunk;
  118. }
  119. }
  120. Sleep( 1 );
  121. }
  122. }
  123. void write_chunk() {
  124. std::size_t chunk = wait_for_empty_chunk();
  125. std::size_t chunk_bytes = std::min( byte_queue.size(), bytes_per_chunk );
  126. waveheaders[chunk].dwBufferLength = static_cast<DWORD>( chunk_bytes );
  127. for ( std::size_t byte = 0; byte < chunk_bytes; ++byte ) {
  128. wavebuffers[chunk][byte] = byte_queue.front();
  129. byte_queue.pop_front();
  130. }
  131. waveOutWrite( waveout, &waveheaders[chunk], sizeof( WAVEHDR ) );
  132. }
  133. void write_or_wait( bool flush = false ) {
  134. while ( byte_queue.size() >= bytes_per_chunk ) {
  135. write_chunk();
  136. }
  137. if ( flush && !byte_queue.empty() ) {
  138. write_chunk();
  139. }
  140. }
  141. template < typename Tsample >
  142. void write_buffers( const std::vector<Tsample*> buffers, std::size_t frames ) {
  143. for ( std::size_t frame = 0; frame < frames; ++frame ) {
  144. for ( std::size_t channel = 0; channel < buffers.size(); ++channel ) {
  145. Tsample val = buffers[channel][frame];
  146. char buf[ sizeof( Tsample ) ];
  147. std::memcpy( buf, &val, sizeof( Tsample ) );
  148. std::copy( buf, buf + sizeof( Tsample ), std::back_inserter( byte_queue ) );
  149. }
  150. }
  151. write_or_wait();
  152. }
  153. public:
  154. void write( const std::vector<float*> buffers, std::size_t frames ) override {
  155. write_buffers( buffers, frames );
  156. }
  157. void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
  158. write_buffers( buffers, frames );
  159. }
  160. bool pause() override {
  161. waveOutPause( waveout );
  162. return true;
  163. }
  164. bool unpause() override {
  165. waveOutRestart( waveout );
  166. return true;
  167. }
  168. bool sleep( int ms ) override {
  169. Sleep( ms );
  170. return true;
  171. }
  172. };
  173. static std::string show_waveout_devices( std::ostream & /*log*/ ) {
  174. std::ostringstream devices;
  175. devices << " waveout:" << std::endl;
  176. for ( UINT i = 0; i < waveOutGetNumDevs(); ++i ) {
  177. devices << " " << i << ": ";
  178. WAVEOUTCAPSW caps;
  179. ZeroMemory( &caps, sizeof( caps ) );
  180. waveOutGetDevCapsW( i, &caps, sizeof( caps ) );
  181. devices << mpt::transcode<std::string>( mpt::common_encoding::utf8, caps.szPname );
  182. devices << std::endl;
  183. }
  184. return devices.str();
  185. }
  186. } // namespace openmpt123
  187. #endif // WIN32
  188. #endif // OPENMPT123_WAVEOUT_HPP