openmpt123_allegro42.hpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /*
  2. * openmpt123_allegro42.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_ALLEGRO42_HPP
  10. #define OPENMPT123_ALLEGRO42_HPP
  11. #include "openmpt123_config.hpp"
  12. #include "openmpt123.hpp"
  13. #if defined(MPT_WITH_ALLEGRO42)
  14. #if defined(__GNUC__) && !defined(__clang__) && !defined(_MSC_VER)
  15. #pragma GCC diagnostic push
  16. #pragma GCC diagnostic ignored "-Wpedantic"
  17. #pragma GCC diagnostic ignored "-Wfloat-conversion"
  18. #endif
  19. #include <allegro.h>
  20. #if defined(__GNUC__) && !defined(__clang__) && !defined(_MSC_VER)
  21. #pragma GCC diagnostic pop
  22. #endif
  23. namespace openmpt123 {
  24. struct allegro42_exception : public exception {
  25. static std::string error_to_string() {
  26. try {
  27. return std::string( allegro_error );
  28. } catch ( const std::bad_alloc & ) {
  29. return std::string();
  30. }
  31. }
  32. allegro42_exception()
  33. : exception( error_to_string() )
  34. {
  35. }
  36. };
  37. class allegro42_raii {
  38. public:
  39. allegro42_raii() {
  40. if ( allegro_init() != 0 ) {
  41. throw allegro42_exception();
  42. }
  43. }
  44. ~allegro42_raii() {
  45. allegro_exit();
  46. }
  47. };
  48. class allegro42_sound_raii {
  49. public:
  50. allegro42_sound_raii() {
  51. if ( install_sound( DIGI_AUTODETECT, MIDI_NONE, NULL ) != 0 ) {
  52. throw allegro42_exception();
  53. }
  54. if ( digi_card == DIGI_NONE ) {
  55. remove_sound();
  56. throw exception( "no audio device found" );
  57. }
  58. }
  59. ~allegro42_sound_raii() {
  60. remove_sound();
  61. }
  62. };
  63. class allegro42_stream_raii : public write_buffers_polling_wrapper_int {
  64. private:
  65. allegro42_raii allegro;
  66. allegro42_sound_raii allegro_sound;
  67. AUDIOSTREAM * stream;
  68. std::size_t bits;
  69. std::size_t channels;
  70. std::uint32_t period_frames;
  71. private:
  72. std::uint32_t round_up_power2(std::uint32_t x)
  73. {
  74. std::uint32_t result = 1;
  75. while ( result < x ) {
  76. result *= 2;
  77. }
  78. return result;
  79. }
  80. public:
  81. allegro42_stream_raii( commandlineflags & flags, std::ostream & log )
  82. : write_buffers_polling_wrapper_int(flags)
  83. , stream(NULL)
  84. , bits(16)
  85. , channels(flags.channels)
  86. , period_frames(1024)
  87. {
  88. if ( flags.use_float ) {
  89. throw exception( "floating point is unsupported" );
  90. }
  91. if ( ( flags.channels != 1 ) && ( flags.channels != 2 ) ) {
  92. throw exception( "only mono or stereo supported" );
  93. }
  94. if ( flags.buffer == default_high ) {
  95. flags.buffer = 1024 * 2 * 1000 / flags.samplerate;
  96. } else if ( flags.buffer == default_low ) {
  97. flags.buffer = 512 * 2 * 1000 / flags.samplerate;
  98. }
  99. if ( flags.period == default_high ) {
  100. flags.period = 1024 / 2 * 1000 / flags.samplerate;
  101. } else if ( flags.period == default_low ) {
  102. flags.period = 512 / 2 * 1000 / flags.samplerate;
  103. }
  104. flags.apply_default_buffer_sizes();
  105. period_frames = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) );
  106. set_queue_size_frames( period_frames );
  107. if ( flags.verbose ) {
  108. log << "Allegro-4.2:" << std::endl;
  109. log << " allegro samplerate: " << get_mixer_frequency() << std::endl;
  110. log << " latency: " << flags.buffer << std::endl;
  111. log << " period: " << flags.period << std::endl;
  112. log << " frames per buffer: " << period_frames << std::endl;
  113. log << " ui redraw: " << flags.ui_redraw_interval << std::endl;
  114. }
  115. stream = play_audio_stream( period_frames, 16, ( flags.channels > 1 ) ? TRUE : FALSE, flags.samplerate, 255, 128 );
  116. if ( !stream ) {
  117. bits = 8;
  118. stream = play_audio_stream( period_frames, 8, ( flags.channels > 1 ) ? TRUE : FALSE, flags.samplerate, 255, 128 );
  119. if ( !stream ) {
  120. throw allegro42_exception();
  121. }
  122. }
  123. }
  124. ~allegro42_stream_raii() {
  125. if ( stream ) {
  126. stop_audio_stream( stream );
  127. stream = NULL;
  128. }
  129. }
  130. public:
  131. bool forward_queue() override {
  132. void * p = get_audio_stream_buffer( stream );
  133. if ( !p ) {
  134. return false;
  135. }
  136. for ( std::size_t frame = 0; frame < period_frames; ++frame ) {
  137. for ( std::size_t channel = 0; channel < channels; ++channel ) {
  138. std::int16_t sample = pop_queue();
  139. if ( bits == 8 ) {
  140. std::uint8_t u8sample = ( static_cast<std::uint16_t>( sample ) + 0x8000u ) >> 8;
  141. std::memcpy( reinterpret_cast<unsigned char *>( p ) + ( ( ( frame * channels ) + channel ) * sizeof( std::uint8_t ) ), &u8sample, sizeof( std::uint8_t ) );
  142. } else {
  143. std::uint16_t u16sample = static_cast<std::uint16_t>( sample ) + 0x8000u;
  144. std::memcpy( reinterpret_cast<unsigned char *>( p ) + ( ( ( frame * channels ) + channel ) * sizeof( std::uint16_t ) ), &u16sample, sizeof( std::uint16_t ) );
  145. }
  146. }
  147. }
  148. free_audio_stream_buffer( stream );
  149. return true;
  150. }
  151. bool unpause() override {
  152. voice_start( stream->voice );
  153. return true;
  154. }
  155. bool pause() override {
  156. voice_stop( stream->voice );
  157. return true;
  158. }
  159. bool sleep( int ms ) override {
  160. rest( ms );
  161. return true;
  162. }
  163. };
  164. static std::string show_allegro42_devices( std::ostream & /* log */ ) {
  165. std::ostringstream devices;
  166. devices << " allegro42:" << std::endl;
  167. devices << " " << "0" << ": Default Device" << std::endl;
  168. return devices.str();
  169. }
  170. } // namespace openmpt123
  171. #endif // MPT_WITH_ALLEGRO42
  172. #endif // OPENMPT123_ALLEGRO42_HPP