bbmusic.cpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #include "bbmusic.h"
  2. #if __APPLE__
  3. #include <OpenAL/al.h>
  4. #include <OpenAL/alc.h>
  5. #else
  6. #include <AL/al.h>
  7. #include <AL/alc.h>
  8. #endif
  9. #include "../../../std/async/native/async.h"
  10. #include "../../../std/async/native/async_cb.h"
  11. #include "../../../stb-vorbis/native/stb-vorbis.h"
  12. #include <std/filesystem/native/filesystem.h>
  13. #include <atomic>
  14. namespace{
  15. struct Counter;
  16. Counter *counters;
  17. //little atomic counter class...
  18. //
  19. struct Counter{
  20. Counter *succ;
  21. int source;
  22. std::atomic<int> count{};
  23. Counter( int source ):source( source ){
  24. succ=counters;
  25. counters=this;
  26. }
  27. ~Counter(){
  28. Counter **pred=&counters;
  29. while( Counter *c=*pred ){
  30. if( c==this ){
  31. *pred=succ;
  32. }
  33. pred=&c->succ;
  34. }
  35. }
  36. };
  37. Counter *getCounter( int source ){
  38. for( Counter *c=counters;c;c=c->succ ){
  39. if( c->source==source ) return c;
  40. }
  41. return 0;
  42. }
  43. }
  44. namespace bbMusic{
  45. int playMusic( FILE *file,int callback,int alsource ){
  46. const int buffer_ms=100;
  47. int error=0;
  48. stb_vorbis *vorbis=stb_vorbis_open_file( file,0,&error,0 );
  49. if( !vorbis ) return false;
  50. stb_vorbis_info info=stb_vorbis_get_info( vorbis );
  51. Counter *buffersProcessed=new Counter( alsource );
  52. std::thread thread( [=](){
  53. ALuint source=alsource;
  54. // int length=stb_vorbis_stream_length_in_samples( vorbis );
  55. // float duration=stb_vorbis_stream_length_in_seconds( vorbis );
  56. // bb_printf( "vorbis length=%i, duration=%f, info.sample_rate=%i, info.channels=%i\n",length,duration,info.sample_rate,info.channels );
  57. ALenum format=(info.channels==2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16);
  58. int nsamples=buffer_ms*info.sample_rate/1000;
  59. int buffer_size=nsamples * (info.channels==2 ? 4 : 2);
  60. // bb_printf( "Samples per buffer=%i\n",nsamples );
  61. //polling for paused occasionally fails with only 2 buffers
  62. const int numbufs=3;
  63. ALuint buffers[numbufs];
  64. alGenBuffers( numbufs,buffers );
  65. short *vorbis_data=new short[buffer_size/2];
  66. int n=buffer_size;
  67. for( int i=0;i<numbufs;++i ){
  68. if( n ) n=stb_vorbis_get_samples_short_interleaved( vorbis,info.channels,vorbis_data,buffer_size/2 );
  69. alBufferData( buffers[i],format,vorbis_data,buffer_size,info.sample_rate );
  70. }
  71. alSourceQueueBuffers( source,numbufs,buffers );
  72. alSourcePlay( source );
  73. // bb_printf( "Playing music...\n" );
  74. for(;;){
  75. //decode more...
  76. if( n ) n=stb_vorbis_get_samples_short_interleaved( vorbis,info.channels,vorbis_data,buffer_size/2 );
  77. ALenum state;
  78. for(;;){
  79. alGetSourcei( source,AL_SOURCE_STATE,&state );
  80. if( state==AL_STOPPED ) break;
  81. if( state==AL_PLAYING ){
  82. ALint processed;
  83. alGetSourcei( source,AL_BUFFERS_PROCESSED,&processed );
  84. // if( processed>1 ) bb_printf( "processed=%i\n",processed );
  85. if( processed ) break;
  86. }
  87. std::this_thread::sleep_for( std::chrono::milliseconds( buffer_ms/2 ) );
  88. }
  89. if( state==AL_STOPPED ){
  90. // bb_printf( "AL_STOPPED\n" );
  91. break;
  92. }
  93. buffersProcessed->count+=1;
  94. ALuint buffer;
  95. alSourceUnqueueBuffers( source,1,&buffer );
  96. if( !n ) continue;
  97. alBufferData( buffer,format,vorbis_data,buffer_size,info.sample_rate );
  98. alSourceQueueBuffers( source,1,&buffer );
  99. }
  100. // bb_printf( "Music done.\n" );
  101. alSourceStop( source );
  102. alDeleteBuffers( numbufs,buffers );
  103. delete[] vorbis_data;
  104. stb_vorbis_close( vorbis );
  105. fclose( file );
  106. bbAsync::invokeAsyncCallback( callback );
  107. } );
  108. thread.detach();
  109. return info.sample_rate;
  110. }
  111. int getBuffersProcessed( int source ){
  112. Counter *c=getCounter( source );
  113. if( c ) return c->count;
  114. return 0;
  115. }
  116. void endMusic( int source ){
  117. Counter *c=getCounter( source );
  118. delete c;
  119. }
  120. }