libopenmpt_example_cxx.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /*
  2. * libopenmpt_example_cxx.cpp
  3. * --------------------------
  4. * Purpose: libopenmpt C++ API example
  5. * Notes : PortAudio C++ is used for sound output.
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. /*
  10. * Usage: libopenmpt_example_cxx SOMEMODULE
  11. */
  12. #include <exception>
  13. #include <fstream>
  14. #include <iostream>
  15. #include <new>
  16. #include <stdexcept>
  17. #include <vector>
  18. #include <libopenmpt/libopenmpt.hpp>
  19. #if defined( __clang__ )
  20. #if ( ( __clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__ ) >= 40000 )
  21. #pragma clang diagnostic push
  22. #pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec"
  23. #endif
  24. #endif
  25. #include <portaudiocpp/PortAudioCpp.hxx>
  26. #if defined( __clang__ )
  27. #if ( ( __clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__ ) >= 40000 )
  28. #pragma clang diagnostic pop
  29. #endif
  30. #endif
  31. #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
  32. #if defined( __GNUC__ ) || ( defined( __clang__ ) && !defined( _MSC_VER ) )
  33. // mingw-w64 g++ does only default to special C linkage for "main", but not for "wmain" (see <https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/>).
  34. extern "C" int wmain( int /*argc*/, wchar_t * /*argv*/[] );
  35. extern "C" int wmain( int argc, wchar_t * argv[] ) {
  36. #else
  37. int wmain( int argc, wchar_t * argv[] ) {
  38. #endif
  39. #else
  40. int main( int argc, char * argv[] ) {
  41. #endif
  42. try {
  43. if ( argc != 2 ) {
  44. throw std::runtime_error( "Usage: libopenmpt_example_cxx SOMEMODULE" );
  45. }
  46. constexpr std::size_t buffersize = 480;
  47. constexpr std::int32_t samplerate = 48000;
  48. std::ifstream file( argv[1], std::ios::binary );
  49. openmpt::module mod( file );
  50. portaudio::AutoSystem portaudio_initializer;
  51. portaudio::System & portaudio = portaudio::System::instance();
  52. std::vector<float> left( buffersize );
  53. std::vector<float> right( buffersize );
  54. std::vector<float> interleaved_buffer( buffersize * 2 );
  55. bool is_interleaved = false;
  56. #if defined( _MSC_VER ) && defined( _PREFAST_ )
  57. // work-around bug in VS2019 MSVC 16.5.5 static analyzer
  58. is_interleaved = false;
  59. portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, false, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 );
  60. portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag );
  61. portaudio::BlockingStream stream( stream_parameters );
  62. #else
  63. portaudio::BlockingStream stream = [&]()
  64. {
  65. try {
  66. is_interleaved = false;
  67. portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, false, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 );
  68. portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag );
  69. return portaudio::BlockingStream( stream_parameters );
  70. } catch ( const portaudio::PaException & e ) {
  71. if ( e.paError() != paSampleFormatNotSupported ) {
  72. throw;
  73. }
  74. is_interleaved = true;
  75. portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, true, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 );
  76. portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag );
  77. return portaudio::BlockingStream( stream_parameters );
  78. }
  79. }();
  80. #endif
  81. stream.start();
  82. while ( true ) {
  83. std::size_t count = is_interleaved ? mod.read_interleaved_stereo( samplerate, buffersize, interleaved_buffer.data() ) : mod.read( samplerate, buffersize, left.data(), right.data() );
  84. if ( count == 0 ) {
  85. break;
  86. }
  87. try {
  88. if ( is_interleaved ) {
  89. stream.write( interleaved_buffer.data(), static_cast<unsigned long>( count ) );
  90. } else {
  91. const float * const buffers[2] = { left.data(), right.data() };
  92. stream.write( buffers, static_cast<unsigned long>( count ) );
  93. }
  94. } catch ( const portaudio::PaException & pa_exception ) {
  95. if ( pa_exception.paError() != paOutputUnderflowed ) {
  96. throw;
  97. }
  98. }
  99. }
  100. stream.stop();
  101. } catch ( const std::bad_alloc & ) {
  102. std::cerr << "Error: " << std::string( "out of memory" ) << std::endl;
  103. return 1;
  104. } catch ( const std::exception & e ) {
  105. std::cerr << "Error: " << std::string( e.what() ? e.what() : "unknown error" ) << std::endl;
  106. return 1;
  107. }
  108. return 0;
  109. }