Browse Source

Updated mojo.audio to use openal.

Mark Sibly 9 years ago
parent
commit
58ed01af63

+ 1 - 1
modules/mojo/app/app.monkey2

@@ -61,7 +61,7 @@ Class AppInstance
 	
 	
 		App=Self
 		App=Self
 	
 	
-		SDL_Init( SDL_INIT_EVERYTHING )
+		SDL_Init( SDL_INIT_EVERYTHING & ~SDL_INIT_AUDIO )
 		
 		
 		_sdlThread=SDL_ThreadID()
 		_sdlThread=SDL_ThreadID()
 		
 		

+ 243 - 67
modules/mojo/audio/audio.monkey2

@@ -1,12 +1,31 @@
 
 
+#rem monkeydoc
+
+Highly experimental audio module!
+
+#end
 Namespace mojo.audio
 Namespace mojo.audio
 
 
 Private
 Private
 
 
-Global _alloced:Int[]
+Function ALFormat:ALenum( format:AudioFormat )
+	Local alFormat:ALenum
+	Select format
+	Case AudioFormat.Mono8
+		alFormat=AL_FORMAT_MONO8
+	Case AudioFormat.Mono16
+		alFormat=AL_FORMAT_MONO16
+	Case AudioFormat.Stereo8
+		alFormat=AL_FORMAT_STEREO8
+	Case AudioFormat.Stereo16
+		alFormat=AL_FORMAT_STEREO16
+	End
+	Return alFormat
+End
 
 
 Public
 Public
 
 
+
 #rem monkeydoc Global instance of the AudioDevice class.
 #rem monkeydoc Global instance of the AudioDevice class.
 #end
 #end
 Const Audio:=New AudioDevice
 Const Audio:=New AudioDevice
@@ -21,16 +40,57 @@ Class AudioDevice
 	#end
 	#end
 	Method Init()
 	Method Init()
 	
 	
-		Mix_Init( 0 )
+		Local error:=""
+
+		_alcDevice=alcOpenDevice( Null )
+		If _alcDevice
+			_alcContext=alcCreateContext( _alcDevice,Null )
+			If _alcContext
+				If alcMakeContextCurrent( _alcContext )
+					Return
+				Else
+					error="Failed to make OpenAL current"
+				Endif
+			Else
+				error="Failed to create OpenAL context"
+			Endif
+		Else
+			error="Failed to create OpenAL device"
+		Endif
+
+	End
+	
+	Private
+	
+	Field _alcDevice:ALCdevice Ptr
+	Field _alcContext:ALCcontext Ptr
+	Field _error:String
+	
+	Field _channels:=New Stack<Channel>
+
+	Method FlushTmpChannels()
+	
+		Local put:=0
+		For Local chan:=Eachin _channels
 		
 		
-		Mix_OpenAudio( 44100,MIX_DEFAULT_FORMAT,2,1024 )
+			If chan.Playing
+				_channels[put]=chan;put+=1
+				Continue
+			Endif
+			
+			chan.Discard()
+		Next
+		_channels.Resize( put )
+	End
+
+	Method AllocTmpChannel:Channel()
+	
+		FlushTmpChannels()
 		
 		
-		Mix_AllocateChannels( 32 )
+		Local channel:=New Channel()
+		_channels.Push( channel )
 		
 		
-		_alloced=New Int[32]
-		For Local i:=0 Until 32
-			_alloced[i]=i+32
-		Next
+		Return channel
 	End
 	End
 	
 	
 End
 End
@@ -39,126 +99,242 @@ End
 #end
 #end
 Class Sound
 Class Sound
 
 
-	#rem monkeydoc Plays a sound.
+	#rem monkeydoc Creates a new sound.
 	#end
 	#end
-	Method Play:Channel( loops:Int=0 )
-
-		Local channel:=New Channel
-		If Not channel.Play( Self,loops ) Return Null
+	Method New( data:AudioData )
+		alGenBuffers( 1,Varptr _alBuffer )
+		alBufferData( _alBuffer,ALFormat( data.Format ),data.Data,data.Size,data.Hertz )
+	End
+	
+	#rem monkeydoc Discards a sound.
+	#end
+	Method Discard()
+		If Not _alBuffer Return
+		alDeleteBuffers( 1,Varptr _alBuffer )
+		_alBuffer=0
+	End
+	
+	#rem monkeydoc Plays a sound through a temporary channel.
+	
+	The returned channel will be automatically discarded when it finishes playing or is stopped with [[Channel.Stop]].
+	
+	#end
+	Method Play:Channel( loop:Bool=False )
+	
+		Local channel:=Audio.AllocTmpChannel()
+		
+		channel.Play( Self,loop )
 		
 		
 		Return channel
 		Return channel
 	End
 	End
-
+	
 	#rem monkeydoc Loads a sound.
 	#rem monkeydoc Loads a sound.
 	#end
 	#end
 	Function Load:Sound( path:String )
 	Function Load:Sound( path:String )
 	
 	
-		Local data:=DataBuffer.Load( path )
+		Local data:=AudioData.Load( path )
 		If Not data Return Null
 		If Not data Return Null
 		
 		
-		Local rwops:=SDL_RWFromMem( data.Data,data.Length )
-		Local chunk:=Mix_LoadWAV_RW( rwops,1 )
-		If Not chunk Return Null
-		
-		Return New Sound( chunk )
+		Local sound:=New Sound( data )
+
+		data.Discard()
+		Return sound
 	End
 	End
 	
 	
 	Private
 	Private
 	
 	
-	Field _chunk:Mix_Chunk Ptr
-	
-	Method New( chunk:Mix_Chunk Ptr )
-		_chunk=chunk
-	End
-	
+	Field _alBuffer:ALuint
+
 End
 End
 
 
-#rem monkeydoc The Channel class.
-#end
 Class Channel
 Class Channel
 
 
-	#rem monkeydoc Creates a new channel.
+	#rem monkeydoc Creates a new audio channel
 	#end
 	#end
 	Method New()
 	Method New()
+		Audio.FlushTmpChannels()
+		alGenSources( 1,Varptr _alSource )
 	End
 	End
-
-	#rem monkeydoc True if this channel is playing audio.
+	
+	#rem monkeydoc True if channel is playing audio.
 	
 	
 	If the channel is playing audio but is in the paused state, this property will still return true.
 	If the channel is playing audio but is in the paused state, this property will still return true.
 
 
 	#end
 	#end
 	Property Playing:Bool()
 	Property Playing:Bool()
-		If Invalid Return False
-		Return Mix_Playing( _id & 31 )
+		If Not _alSource Return False
+		UpdateALState()
+		Return _alState=AL_PLAYING Or _alState=AL_PAUSED
+	End
+	
+	#rem monkeydoc True if channel is paused.
+	#end
+	Property Paused:Bool()
+		If Not _alSource Return False
+		UpdateALState()
+		Return _alState=AL_PAUSED
+	Setter( paused:Bool )
+		If Not Playing Return
+		If paused
+			alSourcePause( _alSource )
+		Else
+			alSourcePlay( _alSource )
+		Endif
+		UpdateALState()
 	End
 	End
 	
 	
 	#rem monkeydoc Channel volume in the range 0 to 1.
 	#rem monkeydoc Channel volume in the range 0 to 1.
 	#end
 	#end
 	Property Volume:Float()
 	Property Volume:Float()
-		If Invalid Return 0
 		Return _volume
 		Return _volume
 	Setter( volume:Float )
 	Setter( volume:Float )
-		If Invalid Return
+		If Not _alSource Return
 		_volume=Clamp( volume,0.0,1.0 )
 		_volume=Clamp( volume,0.0,1.0 )
-		Mix_Volume( _id & 31,_volume*128 )
+		alSourcef( _alSource,AL_GAIN,_volume )
 	End
 	End
-
+	
 	#rem monkeydoc Channel playback rate.
 	#rem monkeydoc Channel playback rate.
 	#end	
 	#end	
 	Property Rate:Float()
 	Property Rate:Float()
-		Return 1
+		Return _rate
 	Setter( rate:Float )
 	Setter( rate:Float )
+		If Not _alSource Return
+		_rate=rate
+		alSourcef( _alSource,AL_PITCH,_rate )
 	End
 	End
 	
 	
-	#rem monkeydoc Channel pan in the range -1 to 1.
-	#end
+	#rem monkeydoc Channel pan in the range -1 (left) to 1 (right).
+	#end	
 	Property Pan:Float()
 	Property Pan:Float()
-		Return 0
-	Setter( pan:Float )
+		Return _pan
+	Setter( pan:Float)
+		If Not _alSource Return
+		_pan=Clamp( pan,-1.0,1.0 )
+		Local x:=Sin( _pan ),z:=-Cos( _pan )
+		alSource3f( _alSource,AL_POSITION,x,0,z )
 	End
 	End
 	
 	
-	#rem monkeydoc Channel paused state.
+	#rem monkeydoc Discard channel resources.
 	#end
 	#end
-	Property Paused:Bool()
-		If Invalid Return False
-		Return Mix_Paused( _id & 31 )
-	Setter( paused:Bool )
-		If Invalid Return
-		Mix_Pause( _id & 31 )
+	Method Discard()
+		If Not _alSource Return
+		alDeleteSources( 1,Varptr _alSource )
+		_alSource=0
+	End
+
+	#rem monkeydoc Play a sound through the channel.
+	#end
+	Method Play( sound:Sound,loop:Bool=False )
+		If Not _alSource Or Not sound Or Not sound._alBuffer Return
+		
+		alSourcei( _alSource,AL_LOOPING,loop ? AL_TRUE Else AL_FALSE )
+		
+		alSourcei( _alSource,AL_BUFFER,sound._alBuffer )
+		
+		alSourcePlay( _alSource )
 	End
 	End
 	
 	
-	#rem monkeydoc Plays a sound through the channel.
+	#rem monkeydoc @hidden - Highly experimental!!!!!
 	#end
 	#end
-	Method Play:Bool( sound:Sound,loops:Int=0 )
+	Method Queue( data:AudioData )
 	
 	
-		Local id:=_id & 31
-		If _alloced[id]<>_id id=-1
+		If _waiting Return
+		
+		If Not _buffers
+		
+			Local n_bufs:=4	'need some work on these magic numbers!
+		
+			_buffers=New ALuint[n_bufs]
+			_ubuffers=New ALuint[n_bufs]
+			_pbuffers=New Stack<ALuint>
 
 
-		id=Mix_PlayChannel( id,sound._chunk,loops )
-		If id<0 Return False
+			For Local i:=0 Until n_bufs
+				Local buf:ALuint
+				alGenBuffers( 1,Varptr buf )
+				_buffers[i]=buf
+				_pbuffers.Push( buf )
+			Next
+			
+			_future=New Future<Int>
+			
+			_waiting=False
+			
+			_timer=New Timer( 25,Lambda()
+			
+				FlushProcessed()
+			End )
+			
+		Endif
+		
+		If _pbuffers.Empty
+		
+			FlushProcessed()
+			
+			If _pbuffers.Empty
+				_waiting=True
+				If Not _future.Get() Return
+			Endif
+			
+		Endif
 		
 		
-		_alloced[id]+=32
-		_id=_alloced[id]
+		Local buf:=_pbuffers.Pop()
 		
 		
-		_volume=1
+		alBufferData( buf,ALFormat( data.Format ),data.Data,data.Size,data.Hertz )
+		
+		alSourceQueueBuffers( _alSource,1,Varptr buf )
+		
+		UpdateALState()
+		If _alState=AL_INITIAL Or _alState=AL_STOPPED alSourcePlay( _alSource )
 		
 		
-		Return True
 	End
 	End
 	
 	
-	#rem monkeydoc Stops the channel.
+	#rem monkeydoc Stops channel.
 	#end
 	#end
 	Method Stop()
 	Method Stop()
-		If Invalid Return
-		Mix_HaltChannel( _id )
+		If Not _alSource Return
+		alSourceStop( _alSource )
 	End
 	End
-	
+
 	Private
 	Private
 	
 	
-	Field _id:Int=0
+	Field _alSource:ALuint
+	Field _alState:ALenum=AL_INITIAL
 	Field _volume:Float=1
 	Field _volume:Float=1
+	Field _rate:Float=1
+	Field _pan:Float=0
 	
 	
-	Property Invalid:Bool()
-		Return _alloced[_id&31]<>_id
-	End
+	Field _buffers:ALuint[]
+	Field _ubuffers:ALuint[]
+	Field _pbuffers:Stack<ALuint>
+	Field _future:Future<Int>
+	Field _waiting:Bool
+	Field _timer:Timer
+	
+	Method FlushProcessed:Int()
+	
+		Local proc:ALint
+		alGetSourcei( _alSource,AL_BUFFERS_PROCESSED,Varptr proc )
+'		Print "processed: "+proc
+		If Not proc Return 0
+				
+		alSourceUnqueueBuffers( _alSource,proc,_ubuffers.Data )
+				
+		For Local i:=0 Until proc
+			_pbuffers.Push( _ubuffers[i] )
+		Next
 
 
+		If _waiting 
+			_waiting=False
+			_future.Set( proc )
+		Endif
+		
+		Return proc
+		
+	End
+	
+	Method UpdateALState:ALenum()
+		alGetSourcei( _alSource,AL_SOURCE_STATE,Varptr _alState )
+		Return _alState
+	End
+	
 End
 End

+ 1 - 1
modules/mojo/module.json

@@ -1,5 +1,5 @@
 {
 {
 	"module":"mojo",
 	"module":"mojo",
 	"version":"1.0.0",
 	"version":"1.0.0",
-	"depends":["freetype","emscripten","std","sdl2","sdl2-mixer","gles20"]
+	"depends":["freetype","emscripten","std","sdl2","gles20","openal"]
 }
 }

+ 2 - 1
modules/mojo/mojo.monkey2

@@ -5,7 +5,7 @@ Namespace mojo
 #Import "<std>"
 #Import "<std>"
 #Import "<sdl2>"
 #Import "<sdl2>"
 #Import "<gles20>"
 #Import "<gles20>"
-#Import "<sdl2-mixer>"
+#Import "<openal>"
 
 
 #Import "app/app"
 #Import "app/app"
 #Import "app/event"
 #Import "app/event"
@@ -41,6 +41,7 @@ Using emscripten..
 Using std..
 Using std..
 Using sdl2..
 Using sdl2..
 Using gles20..
 Using gles20..
+Using openal..
 Using mojo..
 Using mojo..
 
 
 Private
 Private