Browse Source

Music tweaks/fixes. Attempt to implement PlayheadTime/PlayheadSample for music.

Mark Sibly 7 years ago
parent
commit
c95e4dae68

+ 9 - 6
bananas/musictest/musictest.monkey2

@@ -18,7 +18,7 @@ Using mojo..
 Class MyWindow Extends Window
 Class MyWindow Extends Window
 	
 	
 	Field _channel:Channel
 	Field _channel:Channel
-
+	
 	Method New( title:String="Simple mojo app",width:Int=640,height:Int=480,flags:WindowFlags=Null )
 	Method New( title:String="Simple mojo app",width:Int=640,height:Int=480,flags:WindowFlags=Null )
 
 
 		Super.New( title,width,height,flags )
 		Super.New( title,width,height,flags )
@@ -30,7 +30,8 @@ Class MyWindow Extends Window
 		
 		
 		_channel=Audio.PlayMusic( "asset::ACDC_-_Back_In_Black-sample.ogg",Lambda()
 		_channel=Audio.PlayMusic( "asset::ACDC_-_Back_In_Black-sample.ogg",Lambda()
 		
 		
-			If _channel StartMusic()
+			Print "Music Finished!"
+			
 		End )
 		End )
 		
 		
 	End
 	End
@@ -38,18 +39,20 @@ Class MyWindow Extends Window
 	Method OnRender( canvas:Canvas ) Override
 	Method OnRender( canvas:Canvas ) Override
 		
 		
 		RequestRender()
 		RequestRender()
-	
+		
 		canvas.DrawText( 
 		canvas.DrawText( 
 		"Music Test: Hit [Enter] to "+
 		"Music Test: Hit [Enter] to "+
 		(_channel ? "stop" Else "start")+
 		(_channel ? "stop" Else "start")+
-		(_channel ? ", [Space] to "+(_channel.Paused ? "resume" Else "pause") Else ""),
+		(_channel ? ", [Space] to "+(_channel.Paused ? "resume" Else "pause") Else "")+
+		(_channel ? ",  Sample="+_channel.PlayheadSample+", Time="+_channel.PlayheadTime Else ""),
 		0,0 )
 		0,0 )
 		
 		
 		'Stop/Start?
 		'Stop/Start?
 		If Keyboard.KeyHit( Key.Enter ) Or Mouse.ButtonHit( MouseButton.Left )
 		If Keyboard.KeyHit( Key.Enter ) Or Mouse.ButtonHit( MouseButton.Left )
 			If _channel 
 			If _channel 
-				_channel.Stop()
-				_channel=Null
+				'_channel.Stop()
+				'_channel=Null
+				StartMusic()
 			Else
 			Else
 				StartMusic()
 				StartMusic()
 			Endif
 			Endif

+ 57 - 4
modules/mojo/audio/audio.monkey2

@@ -12,10 +12,15 @@ Namespace mojo.audio
 
 
 Extern Private
 Extern Private
 
 
-Function playMusic:Bool( path:CString,callback:int,source:Int )="bbMusic::playMusic"
+Function playMusic:Int( path:CString,callback:Int,source:Int )="bbMusic::playMusic"
+Function getBuffersProcessed:Int( source:Int )="bbMusic::getBuffersProcessed"
+Function endMusic:Void( source:Int )="bbMusic::endMusic"
 	
 	
 Private
 Private
 
 
+Const MUSIC_BUFFER_MS:=100
+Const MUSIC_BUFFER_SECS:=0.1
+
 Function ALFormat:ALenum( format:AudioFormat )
 Function ALFormat:ALenum( format:AudioFormat )
 	Local alFormat:ALenum
 	Local alFormat:ALenum
 	Select format
 	Select format
@@ -56,20 +61,28 @@ Class AudioDevice
 	
 	
 	#end
 	#end
 	Method PlayMusic:Channel( path:String,finished:Void()=Null,paused:Bool=False )
 	Method PlayMusic:Channel( path:String,finished:Void()=Null,paused:Bool=False )
-		
-		Local channel:=New Channel( ChannelFlags.AutoDiscard )
+
+		'DO NOT use AutoDiscard here or music wont receive 'stop' signal!
+		'		
+		Local channel:=New Channel( ChannelFlags.Music )
 		
 		
 		Local callback:=async.CreateAsyncCallback( Lambda()
 		Local callback:=async.CreateAsyncCallback( Lambda()
+			channel.Discard()
+			endMusic( channel._alSource )
 			finished()
 			finished()
 		End,True )
 		End,True )
 		
 		
 		path=filesystem.RealPath( path )
 		path=filesystem.RealPath( path )
 		
 		
-		If Not playMusic( path,callback,channel._alSource ) 
+		Local sampleRate:=playMusic( path,callback,channel._alSource )
+	
+		If Not sampleRate
 			async.DestroyAsyncCallback( callback )
 			async.DestroyAsyncCallback( callback )
 			Return Null
 			Return Null
 		Endif
 		Endif
 		
 		
+		channel._sampleRate=sampleRate
+		
 		Return channel
 		Return channel
 	End
 	End
 
 
@@ -208,6 +221,7 @@ End
 Enum ChannelFlags
 Enum ChannelFlags
 	
 	
 	AutoDiscard=1
 	AutoDiscard=1
+	Music=2
 End
 End
 
 
 Class Channel Extends Resource
 Class Channel Extends Resource
@@ -321,6 +335,24 @@ Class Channel Extends Resource
 		Local sample:Int
 		Local sample:Int
 		alGetSourcei( _alSource,AL_SAMPLE_OFFSET,Varptr sample )
 		alGetSourcei( _alSource,AL_SAMPLE_OFFSET,Varptr sample )
 		
 		
+		If _flags & ChannelFlags.Music
+			
+			Local samplesPerBuffer:=MUSIC_BUFFER_MS * _sampleRate / 1000
+			
+			Local buffersProcessed:=getBuffersProcessed( _alSource )
+			
+			sample+=samplesPerBuffer * buffersProcessed
+			
+			If sample<_sample
+'				Print "Sample catch up:"+sample+"->"+_sample
+				While sample<_sample
+					sample+=samplesPerBuffer
+				Wend
+			Else
+				_sample=sample
+			Endif
+		Endif
+		
 		Return sample
 		Return sample
 				
 				
 	Setter( sample:Int )
 	Setter( sample:Int )
@@ -344,6 +376,22 @@ Class Channel Extends Resource
 		Local time:Float		
 		Local time:Float		
 		alGetSourcef( _alSource,AL_SEC_OFFSET,Varptr time )
 		alGetSourcef( _alSource,AL_SEC_OFFSET,Varptr time )
 		
 		
+		If _flags & ChannelFlags.Music
+			
+			Local buffersProcessed:=getBuffersProcessed( _alSource )
+			
+			time+=MUSIC_BUFFER_SECS * buffersProcessed
+			
+			If time<_time
+'				Print "Time catchup: "+time+"->"+_time
+				While time<_time
+					time+=MUSIC_BUFFER_SECS
+				Wend
+			Else
+				_time=time
+			Endif
+		Endif
+		
 		Return time
 		Return time
 		
 		
 	Setter( time:Float )
 	Setter( time:Float )
@@ -461,6 +509,11 @@ Class Channel Extends Resource
 	Field _rate:Float=1
 	Field _rate:Float=1
 	Field _pan:Float=0
 	Field _pan:Float=0
 	
 	
+	Field _sampleRate:Int
+	
+	Field _time:float
+	Field _sample:Int
+	
 	Global _autoDiscard:=New Stack<Channel>
 	Global _autoDiscard:=New Stack<Channel>
 	
 	
 	Method ALState:ALenum()
 	Method ALState:ALenum()

+ 91 - 34
modules/mojo/audio/native/bbmusic.cpp

@@ -17,6 +17,48 @@
 #include "../../../sdl2/SDL/src/core/android/SDL_android.h"
 #include "../../../sdl2/SDL/src/core/android/SDL_android.h"
 #endif
 #endif
 
 
+#include <atomic>
+
+namespace{
+
+	struct Counter;
+
+	Counter *counters;
+	
+	//Yikes! We have to make a little atomic counter class!
+	//
+	struct Counter{
+		
+		Counter *succ;
+		int source;
+		std::atomic<int> count=0;
+		
+		Counter( int source ):source( source ){
+			succ=counters;
+			counters=this;
+		}
+		
+		~Counter(){
+			Counter **pred=&counters;
+			
+			while( Counter *c=*pred ){
+				if( c==this ){
+					*pred=succ;
+				}
+				pred=&c->succ;
+			}
+		}
+	};
+	
+	Counter *getCounter( int source ){
+		for( Counter *c=counters;c;c=c->succ ){
+			if( c->source==source ) return c;
+		}
+		return 0;
+	}
+}
+	
+
 namespace bbMusic{
 namespace bbMusic{
 
 
 #if __ANDROID__
 #if __ANDROID__
@@ -61,36 +103,35 @@ namespace bbMusic{
 		return ::fopen( path,mode );
 		return ::fopen( path,mode );
 	}
 	}
 
 
-	bool playMusic( const char *path,int callback,int alsource ){
+	int playMusic( const char *path,int callback,int alsource ){
 	
 	
+		const int buffer_ms=100;
+			
 		FILE *file=fopen( path,"rb" );
 		FILE *file=fopen( path,"rb" );
 		if( !file ) return false;
 		if( !file ) return false;
 	
 	
 		int error=0;
 		int error=0;
 		stb_vorbis *vorbis=stb_vorbis_open_file( file,0,&error,0 );
 		stb_vorbis *vorbis=stb_vorbis_open_file( file,0,&error,0 );
 		if( !vorbis ) return false;
 		if( !vorbis ) return false;
+
+		stb_vorbis_info info=stb_vorbis_get_info( vorbis );
+		
+		Counter *buffersProcessed=new Counter( alsource );
 		
 		
 		std::thread thread( [=](){
 		std::thread thread( [=](){
 		
 		
 			ALuint source=alsource;
 			ALuint source=alsource;
 	
 	
-			stb_vorbis_info info=stb_vorbis_get_info( vorbis );
-			
 //			int length=stb_vorbis_stream_length_in_samples( vorbis );
 //			int length=stb_vorbis_stream_length_in_samples( vorbis );
 //			float duration=stb_vorbis_stream_length_in_seconds( vorbis );
 //			float duration=stb_vorbis_stream_length_in_seconds( vorbis );
 //			bb_printf( "vorbis length=%i, duration=%f, info.sample_rate=%i, info.channels=%i\n",length,duration,info.sample_rate,info.channels );
 //			bb_printf( "vorbis length=%i, duration=%f, info.sample_rate=%i, info.channels=%i\n",length,duration,info.sample_rate,info.channels );
 
 
 			ALenum format=(info.channels==2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16);
 			ALenum format=(info.channels==2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16);
 
 
-//			int buffer_size=8192;
-//			int nsamples=buffer_size / (info.channels==2 ? 4 : 2);
-//			int buffer_ms=nsamples*1000/info.sample_rate;
-			
-			int buffer_ms=100;
 			int nsamples=buffer_ms*info.sample_rate/1000;
 			int nsamples=buffer_ms*info.sample_rate/1000;
 			int buffer_size=nsamples * (info.channels==2 ? 4 : 2);
 			int buffer_size=nsamples * (info.channels==2 ? 4 : 2);
 			
 			
-//			bb_printf( "buffer_size=%i, buffer_ms=%i\n",buffer_size,buffer_ms );
+//			bb_printf( "Samples per buffer=%i\n",nsamples );
 			
 			
 			//polling for paused occasionally fails with only 2 buffers
 			//polling for paused occasionally fails with only 2 buffers
 			
 			
@@ -101,56 +142,61 @@ namespace bbMusic{
 			
 			
 			short *vorbis_data=new short[buffer_size/2];
 			short *vorbis_data=new short[buffer_size/2];
 			
 			
+			int n=buffer_size;
+			
 			for( int i=0;i<numbufs;++i ){
 			for( int i=0;i<numbufs;++i ){
-				int n=stb_vorbis_get_samples_short_interleaved( vorbis,info.channels,vorbis_data,buffer_size/2 );
+			
+				if( n ) n=stb_vorbis_get_samples_short_interleaved( vorbis,info.channels,vorbis_data,buffer_size/2 );
+				
 				alBufferData( buffers[i],format,vorbis_data,buffer_size,info.sample_rate );
 				alBufferData( buffers[i],format,vorbis_data,buffer_size,info.sample_rate );
 			}
 			}
-			
+
 			alSourceQueueBuffers( source,numbufs,buffers );
 			alSourceQueueBuffers( source,numbufs,buffers );
 			
 			
 			alSourcePlay( source );
 			alSourcePlay( source );
 			
 			
 //			bb_printf( "Playing music...\n" );
 //			bb_printf( "Playing music...\n" );
-			
+
 			for(;;){
 			for(;;){
 
 
+				//decode more...				
+				if( n ) n=stb_vorbis_get_samples_short_interleaved( vorbis,info.channels,vorbis_data,buffer_size/2 );
+					
 				ALenum state;
 				ALenum state;
 				
 				
-				int n=stb_vorbis_get_samples_short_interleaved( vorbis,info.channels,vorbis_data,buffer_size/2 );
-				if( !n ){
-					//no more data, wait for audio to stop...
-					for(;;){
-						alGetSourcei( source,AL_SOURCE_STATE,&state );
-						if( state==AL_STOPPED ) break;
-						std::this_thread::sleep_for( std::chrono::milliseconds( buffer_ms/2 ) );
-					}
-					break;
-				}
-				
 				for(;;){
 				for(;;){
 				
 				
 					alGetSourcei( source,AL_SOURCE_STATE,&state );
 					alGetSourcei( source,AL_SOURCE_STATE,&state );
+					
 					if( state==AL_STOPPED ) break;
 					if( state==AL_STOPPED ) break;
+						
+					if( state==AL_PLAYING ){
 
 
-					ALint processed;
-					alGetSourcei( source,AL_BUFFERS_PROCESSED,&processed );
-					
-//					if( processed>1 )  bb_printf( "processed=%i\n",processed );
-					
-					if( state==AL_PLAYING && processed ) break;
+						ALint processed;
+						alGetSourcei( source,AL_BUFFERS_PROCESSED,&processed );
 						
 						
+//						if( processed>1 )  bb_printf( "processed=%i\n",processed );
+							
+						if( processed ) break;
+					}
+
 					std::this_thread::sleep_for( std::chrono::milliseconds( buffer_ms/2 ) );
 					std::this_thread::sleep_for( std::chrono::milliseconds( buffer_ms/2 ) );
 				}
 				}
+				
 				if( state==AL_STOPPED ){
 				if( state==AL_STOPPED ){
 //					bb_printf( "AL_STOPPED\n" );
 //					bb_printf( "AL_STOPPED\n" );
 					break;
 					break;
 				}
 				}
 				
 				
+				buffersProcessed->count+=1;
+					
 				ALuint buffer;
 				ALuint buffer;
 				alSourceUnqueueBuffers( source,1,&buffer );
 				alSourceUnqueueBuffers( source,1,&buffer );
-					
-				alBufferData( buffer,format,vorbis_data,buffer_size,info.sample_rate );
 				
 				
+				if( !n ) continue;
+				
+				alBufferData( buffer,format,vorbis_data,buffer_size,info.sample_rate );
+					
 				alSourceQueueBuffers( source,1,&buffer );
 				alSourceQueueBuffers( source,1,&buffer );
 			}
 			}
 
 
@@ -158,7 +204,7 @@ namespace bbMusic{
 			
 			
 			alSourceStop( source );
 			alSourceStop( source );
 			
 			
-			alDeleteBuffers( 2,buffers );
+			alDeleteBuffers( numbufs,buffers );
 			
 			
 			delete[] vorbis_data;
 			delete[] vorbis_data;
 			
 			
@@ -170,8 +216,19 @@ namespace bbMusic{
 		} );
 		} );
 		
 		
 		thread.detach();
 		thread.detach();
-
-		return true;
+		
+		return info.sample_rate;
+	}
+	
+	int getBuffersProcessed( int source ){
+		Counter *c=getCounter( source );
+		if( c ) return c->count;
+		return 0;
+	}
+	
+	void endMusic( int source ){
+		Counter *c=getCounter( source );
+		delete c;
 	}
 	}
 }
 }
 
 

+ 6 - 1
modules/mojo/audio/native/bbmusic.h

@@ -1,5 +1,10 @@
 
 
 namespace bbMusic{
 namespace bbMusic{
 
 
-	bool playMusic( const char *path,int callback,int source );
+	int playMusic( const char *path,int callback,int source );
+	
+	int getBuffersProcessed( int source );
+	
+	void endMusic( int source );
+	
 }
 }