Procházet zdrojové kódy

Added WIP Audio.PlayMusic.

Mark Sibly před 8 roky
rodič
revize
f9c71c9881

+ 26 - 3
modules/mojo/audio/audio.monkey2

@@ -6,6 +6,14 @@ Highly experimental audio module!
 #end
 Namespace mojo.audio
 
+#Import "native/bbmusic.cpp"
+
+#Import "native/bbmusic.h"
+
+Extern Private
+
+Function playMusic:Bool( path:CString,callback:int,source:Int )="bbMusic::playMusic"
+	
 Private
 
 Function ALFormat:ALenum( format:AudioFormat )
@@ -33,6 +41,24 @@ Const Audio:=New AudioDevice
 #end
 Class AudioDevice
 
+	Function PlayMusic:Channel( path:String,finished:Void()=Null,paused:Bool=False )
+		
+		Local channel:=New Channel( Null )
+		
+		Local callback:=async.CreateAsyncCallback( Lambda()
+			channel.Discard()
+			finished()
+		End,True )
+		
+		If Not playMusic( path,callback,channel._alSource ) 
+			async.DestroyAsyncCallback( callback )
+			channel.Discard()
+			Return Null
+		Endif
+		
+		Return channel
+	End
+
 	Internal
 	
 	Method Init()
@@ -434,13 +460,10 @@ Class Channel Extends Resource
 		Local put:=0
 		
 		For Local chan:=Eachin _autoDiscard
-			
 			If Not chan._alSource Continue
 		
 			If chan.ALState()<>AL_STOPPED
-				
 				_autoDiscard[put]=chan;put+=1
-				
 				Continue
 			Endif
 			

+ 106 - 0
modules/mojo/audio/native/bbmusic.cpp

@@ -0,0 +1,106 @@
+
+#include "bbmusic.h"
+
+#include "../../../openal/native/bbopenal.h"
+
+#include "../../../std/async/native/async.h"
+#include "../../../std/async/native/async_cb.h"
+
+#include "../../../stb-vorbis/native/stb-vorbis.h"
+
+namespace bbMusic{
+
+	bool playMusic( const char *path,int callback,int alsource ){
+	
+		int error=0;
+		stb_vorbis *vorbis=stb_vorbis_open_filename( path,&error,0 );
+		if( !vorbis ) return false;
+		
+		std::thread thread( [=](){
+		
+			ALuint source=alsource;
+	
+			stb_vorbis_info info=stb_vorbis_get_info( vorbis );
+
+			int length=stb_vorbis_stream_length_in_samples( vorbis );
+			
+			float duration=stb_vorbis_stream_length_in_seconds( vorbis );
+			
+//			printf( "vorbis length=%i, duration=%f, info.sample_rate=%i, info.channels=%i\n",length,duration,info.sample_rate,info.channels );fflush( stdout );
+
+			const int BUFFER_SIZE=2048;
+			int nsamples=(info.channels==2 ? BUFFER_SIZE/4 : BUFFER_SIZE/2);
+			ALenum format=(info.channels==2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO8);
+			
+			//how long a buffer takes to play?
+			int buffer_ms=nsamples*1000/info.sample_rate;
+			
+			//polling for paused occasionally fails with only 2 buffers
+			ALuint buffers[3];
+			alGenBuffers( 3,buffers );
+			
+			alBufferData( buffers[0],format,0,BUFFER_SIZE,info.sample_rate );
+			alBufferData( buffers[1],format,0,BUFFER_SIZE,info.sample_rate );
+			alBufferData( buffers[2],format,0,BUFFER_SIZE,info.sample_rate );
+			
+			alSourceQueueBuffers( source,3,buffers );
+			
+			short *vorbis_data=new short[BUFFER_SIZE/2];
+			
+			alSourcePlay( source );
+			
+//			printf( "Playing music...\n" );fflush( stdout );
+			
+			for(;;){
+
+				int n=stb_vorbis_get_samples_short_interleaved( vorbis,info.channels,vorbis_data,BUFFER_SIZE/2 );
+				if( !n ) break;
+				
+				ALenum state;
+				
+				for(;;){
+					alGetSourcei( source,AL_SOURCE_STATE,&state );
+					if( state==AL_STOPPED ) break;
+
+					ALint processed;
+					alGetSourcei( source,AL_BUFFERS_PROCESSED,&processed );
+					
+//					if( state!=AL_PLAYING ){
+//						printf( "state=%i\n",state );fflush( stdout );
+//					}
+					
+					if( state==AL_PLAYING && processed ) break;
+				        
+					std::this_thread::sleep_for( std::chrono::milliseconds( buffer_ms ) );
+				}
+				if( state==AL_STOPPED ) break;
+				
+				ALuint buffer;
+				alSourceUnqueueBuffers( source,1,&buffer );
+					
+				alBufferData( buffer,format,vorbis_data,BUFFER_SIZE,info.sample_rate );
+				
+				alSourceQueueBuffers( source,1,&buffer );
+			}
+
+//			printf( "Music done.\n" );fflush( stdout );
+			
+			alSourceStop( source );
+			
+			delete[] vorbis_data;
+			
+			alDeleteBuffers( 2,buffers );
+			
+//			alDeleteSources( 1,&source );
+			
+			stb_vorbis_close( vorbis );
+			
+			bbAsync::invokeAsyncCallback( callback );
+		} );
+		
+		thread.detach();
+
+		return true;
+	}
+}
+

+ 5 - 0
modules/mojo/audio/native/bbmusic.h

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

+ 14 - 0
modules/openal/native/bbopenal.h

@@ -0,0 +1,14 @@
+
+#include <bbplatform.h>
+
+#if BB_MACOS
+
+#include <OpenAL/al.h>
+#include <OpenAL/alc.h>
+
+#else
+
+#include <AL/al.h>
+#include <AL/alc.h>
+
+#endif

+ 5 - 0
modules/std/async/async.monkey2

@@ -37,6 +37,11 @@ End
 #end
 Function CreateAsyncCallback:Int( func:Void(),oneshot:bool )="bbAsync::createAsyncCallback"
 
+'Should be invoked on mx2 thread. No effect if callback has been posted.
+#rem monkeydoc @hidden
+#end
+Function DestroyAsyncCallback:Int( callback:Int )="bbAsync::destroyAsyncCallback"
+
 'Can be invoked on any thread.
 #rem monkeydoc @hidden
 #end

+ 22 - 3
modules/std/async/native/async_cb.cpp

@@ -17,8 +17,8 @@ namespace bbAsync{
 	
 		AsyncCallback *succ;
 		bbFunction<void()> func;
-		bool oneshot;
 		std::atomic<int> posted;
+		bool oneshot;
 		int id;
 		
 		void dispatch(){
@@ -29,7 +29,7 @@ namespace bbAsync{
 			
 			if( !oneshot ) return;
 
-			std::unique_lock<std::mutex> lock( cb_mutex );
+			std::lock_guard<std::mutex> lock( cb_mutex );
 			
 			succ=cb_free;
 			cb_free=this;
@@ -49,14 +49,33 @@ namespace bbAsync{
 			cb=new AsyncCallback;
 		}
 		cb->func=func;
-		cb->oneshot=oneshot;
 		cb->posted=0;
+		cb->oneshot=oneshot;
 		cb->id=++cb_nextid;
 		cb->succ=cb_first;
 		cb_first=cb;
 		
 		return cb->id;
 	}
+	
+	void destroyAsyncCallback( int callback ){
+	
+		std::lock_guard<std::mutex> lock( cb_mutex );
+	
+		AsyncCallback **prev=&cb_first;
+		
+		while( AsyncCallback *cb=*prev ){
+			if( cb->id!=callback ){
+				prev=&cb->succ;
+				continue;
+			}
+			if( cb->posted ) return;	//OOPS, can't destroy posted callback.
+			*prev=cb->succ;
+			cb->succ=cb_free;
+			cb_free=cb;
+			return;
+		}
+	}
 
 	void invokeAsyncCallback( int callback ){
 

+ 2 - 0
modules/std/async/native/async_cb.h

@@ -10,6 +10,8 @@ namespace bbAsync{
 
 	int createAsyncCallback( bbFunction<void()> func,bool oneshot );
 	
+	void destroyAsyncCallback( int callback );
+	
 	void invokeAsyncCallback( int callback );
 }