openmpt123_pulseaudio.hpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /*
  2. * openmpt123_pulseaudio.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_PULSEAUDIO_HPP
  10. #define OPENMPT123_PULSEAUDIO_HPP
  11. #include "openmpt123_config.hpp"
  12. #include "openmpt123.hpp"
  13. #if defined(MPT_WITH_PULSEAUDIO)
  14. #include <pulse/pulseaudio.h>
  15. #include <pulse/simple.h>
  16. namespace openmpt123 {
  17. struct pulseaudio_exception : public exception {
  18. static std::string error_to_string( int error ) {
  19. try {
  20. if ( error == 0 ) {
  21. return std::string();
  22. }
  23. std::ostringstream e;
  24. const char * str = pa_strerror( error );
  25. if ( !str ) {
  26. e << "error=" << error;
  27. return e.str();
  28. }
  29. if ( std::strlen(str) == 0 ) {
  30. e << "error=" << error;
  31. return e.str();
  32. }
  33. e << str << " (error=" << error << ")";
  34. return e.str();
  35. } catch ( const std::bad_alloc & ) {
  36. return std::string();
  37. }
  38. }
  39. pulseaudio_exception( int error ) : exception( error_to_string( error ) ) { }
  40. };
  41. class pulseaudio_stream_raii : public write_buffers_interface {
  42. private:
  43. pa_simple * stream;
  44. std::size_t channels;
  45. std::size_t sampleSize;
  46. std::vector<float> sampleBufFloat;
  47. std::vector<std::int16_t> sampleBufInt;
  48. public:
  49. pulseaudio_stream_raii( commandlineflags & flags, std::ostream & /* log */ )
  50. : stream(NULL)
  51. , channels(flags.channels)
  52. , sampleSize(flags.use_float ? sizeof( float ) : sizeof( std::int16_t ))
  53. {
  54. int error = 0;
  55. pa_sample_spec ss;
  56. std::memset( &ss, 0, sizeof( pa_sample_spec ) );
  57. ss.format = ( flags.use_float ? PA_SAMPLE_FLOAT32 : PA_SAMPLE_S16NE );
  58. ss.rate = flags.samplerate;
  59. ss.channels = flags.channels;
  60. pa_buffer_attr ba;
  61. std::memset( &ba, 0, sizeof( pa_buffer_attr ) );
  62. bool use_ba = false;
  63. if ( flags.buffer != default_high && flags.buffer != default_low ) {
  64. use_ba = true;
  65. ba.maxlength = channels * sampleSize * ( flags.buffer * flags.samplerate / 1000 );
  66. } else {
  67. ba.maxlength = static_cast<std::uint32_t>(-1);
  68. }
  69. if ( flags.period != default_high && flags.period != default_low ) {
  70. use_ba = true;
  71. ba.minreq = channels * sampleSize * ( flags.period * flags.samplerate / 1000 );
  72. if ( ba.maxlength != static_cast<std::uint32_t>(-1) ) {
  73. ba.tlength = ba.maxlength - ba.minreq;
  74. ba.prebuf = ba.tlength;
  75. } else {
  76. ba.tlength = static_cast<std::uint32_t>(-1);
  77. ba.prebuf = static_cast<std::uint32_t>(-1);
  78. }
  79. } else {
  80. ba.minreq = static_cast<std::uint32_t>(-1);
  81. ba.tlength = static_cast<std::uint32_t>(-1);
  82. ba.prebuf = static_cast<std::uint32_t>(-1);
  83. }
  84. ba.fragsize = 0;
  85. flags.apply_default_buffer_sizes();
  86. sampleBufFloat.resize( channels * ( flags.ui_redraw_interval * flags.samplerate / 1000 ) );
  87. sampleBufInt.resize( channels * ( flags.ui_redraw_interval * flags.samplerate / 1000 ) );
  88. stream = pa_simple_new( NULL, "openmpt123", PA_STREAM_PLAYBACK, NULL, "openmpt123", &ss, NULL, ( use_ba ? &ba : NULL ), &error );
  89. if ( !stream ) {
  90. throw pulseaudio_exception( error );
  91. }
  92. }
  93. ~pulseaudio_stream_raii() {
  94. int error = 0;
  95. if ( stream ) {
  96. error = 0;
  97. if ( pa_simple_drain( stream, &error ) < 0 ) {
  98. // throw pulseaudio_exception( error );
  99. }
  100. pa_simple_free( stream );
  101. stream = NULL;
  102. }
  103. }
  104. private:
  105. template<typename Tsample>
  106. void write_frames( const Tsample * buffer, std::size_t frames ) {
  107. int error = 0;
  108. if ( pa_simple_write( stream, buffer, frames * channels * sampleSize, &error ) < 0 ) {
  109. throw pulseaudio_exception( error );
  110. }
  111. }
  112. public:
  113. void write( const std::vector<float*> buffers, std::size_t frames ) override {
  114. sampleBufFloat.clear();
  115. for ( std::size_t frame = 0; frame < frames; ++frame ) {
  116. for ( std::size_t channel = 0; channel < channels; ++channel ) {
  117. sampleBufFloat.push_back( buffers[channel][frame] );
  118. }
  119. }
  120. write_frames( sampleBufFloat.data(), frames );
  121. }
  122. void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
  123. sampleBufInt.clear();
  124. for ( std::size_t frame = 0; frame < frames; ++frame ) {
  125. for ( std::size_t channel = 0; channel < channels; ++channel ) {
  126. sampleBufInt.push_back( buffers[channel][frame] );
  127. }
  128. }
  129. write_frames( sampleBufInt.data(), frames );
  130. }
  131. bool unpause() override {
  132. return true;
  133. }
  134. bool pause() override {
  135. int error = 0;
  136. error = 0;
  137. if ( pa_simple_drain( stream, &error ) < 0 ) {
  138. throw pulseaudio_exception( error );
  139. }
  140. return true;
  141. }
  142. bool sleep( int ms ) override {
  143. pa_msleep( ms );
  144. return true;
  145. }
  146. };
  147. static std::string show_pulseaudio_devices( std::ostream & /* log */ ) {
  148. std::ostringstream devices;
  149. devices << " pulseaudio:" << std::endl;
  150. devices << " " << "0" << ": Default Device" << std::endl;
  151. return devices.str();
  152. }
  153. } // namespace openmpt123
  154. #endif // MPT_WITH_PULSEAUDIO
  155. #endif // OPENMPT123_PULSEAUDIO_HPP