About Monkey 2 › Forums › Monkey 2 Code Library › new mojo/audio/audio.monkey2
This topic contains 0 replies, has 1 voice, and was last updated by 
 AdamStrange
 1 year, 7 months ago.
- 
		AuthorPosts
 - 
		
			
				
August 31, 2017 at 10:43 am #10131
Here’s a modified version of the base sound and channel class.
In essence i’ve added 256float to each sound that contains the waveform, these can be accessed through the new draw command.
I’ve also added to the sound.new allowing you to create many synth type voices from the base 4 styles.
| Channel will be automatically discarded when it finishes playing, or when it is stopped using [[Channel.Stop]]. #end Enum ChannelFlags AutoDiscard = 1 End Class Channel Extends Resource #rem monkeydoc Creates a new audio channel. If flags is ChannelFlags.AutoDiscard, then the channel will be automatically discarded when it finishes playing, or when it is stopped using [[Stop]]. #end Method New( flags:ChannelFlags = Null ) _flags = flags FlushAutoDiscard() alGenSources( 1, Varptr _alSource ) If _flags & ChannelFlags.AutoDiscard then _autoDiscard.Push( Self ) End Property Flags:ChannelFlags() Return _flags End #- 'jl added #rem monkeydoc True if channel is playing a stereo audio file. #end Property Stereo:bool() Return _stereo Setter( stereo:bool ) _stereo = stereo End #- #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. #end Property Playing:Bool() If Not _alSource Return False Local state := ALState() Return state = AL_PLAYING Or state = AL_PAUSED End #rem monkeydoc True if channel is paused. #end Property Paused:Bool() If Not _alSource Return False Return ALState() = AL_PAUSED Setter( paused:Bool ) If Not Playing Return If paused alSourcePause( _alSource ) Else alSourcePlay( _alSource ) Endif End #rem monkeydoc Channel volume in the range 0 to 1. #end Property Volume:Float() If Not _alSource Return 0 Return _volume Setter( volume:Float ) If Not _alSource Return _volume = Clamp( volume, 0.0, 1.0 ) alSourcef( _alSource, AL_GAIN, _volume ) End #rem monkeydoc Channel playback rate. #end Property Rate:Float() If Not _alSource Return 0 Return _rate Setter( rate:Float ) If Not _alSource Return _rate = rate alSourcef( _alSource, AL_PITCH, _rate ) End #rem monkeydoc Channel pan in the range -1 (left) to 1 (right). #end Property Pan:Float() If Not _alSource Return 0 Return _pan Setter( pan:Float) If Not _alSource Return _pan = Clamp( pan, -1.0, 1.0 )*1.5 If Not _stereo Then Local x := Sin( _pan ) Local z := -Cos( _pan ) alSource3f( _alSource, AL_POSITION, x, 0, z ) else ' Print "stereo "+_pan ' Local angles:ALfloat[] = New ALfloat[2] ' angles[0] = Pi/6.0 - _pan ' angles[1] = -Pi/6.0 - _pan ' ALfloat angles[2] = { (ALfloat)(M_PI/6.0 - angle), (ALfloat)(-M_PI/6.0 - angle) } ' alSourcefv( _alSource, AL_STEREO_ANGLES, Varptr angles[0] ) End If End #- 'JL added #rem monkeydoc Channel playhead in samples. If the channel is playing audio this will return the position of the playhead in the played sound. #end Property Playhead:Double() If Not _alSource Return 0 ' Local proc:ALint ' alGetSourcei( _alSource,AL_BUFFERS_PROCESSED,Varptr proc ) ' Print "processed: "+proc Local playhead:ALfloat alGetSourcef( _alSource, AL_SAMPLE_OFFSET, Varptr playhead ) Return (float(playhead) / _length) ' local pos:float ' return alGetSourcef( _alSource, AL_SAMPLE_OFFSET, Varptr pos ) Setter( playhead:Float ) If Not _alSource Return local _playhead:ALfloat = Clamp( playhead, 0.0, 1.0 ) alSourcef( _alSource, AL_SAMPLE_OFFSET, _playhead ) End #- #rem monkeydoc Channel playhead sample offset. Gets or sets the sample offset of the sound currently playing. If the channel is playing when set, playback is immediately affected. If the channel is not playing when set, the offset will be applied when the Play method is invoked. #end Property PlayheadSample:Int() If Not _alSource Return 0 Local sample:Int alGetSourcei( _alSource, AL_SAMPLE_OFFSET, Varptr sample ) Return sample Setter( sample:Int ) If Not _alSource Return alSourcei( _alSource, AL_SAMPLE_OFFSET, sample ) End #rem monkeydoc Channel playhead time offset. Gets or sets the time offset of the sound currently playing. If the channel is playing when set, playback is immediately affected. If the channel is not playing when set, the offset will be applied when the Play method is invoked. #end Property PlayheadTime:Float() If Not _alSource Return 0 Local time:Float alGetSourcef( _alSource, AL_SEC_OFFSET, Varptr time ) Return time Setter( time:Float ) If Not _alSource Return alSourcef( _alSource, AL_SEC_OFFSET, time ) End #rem monkeydoc Plays 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 ) #- 'jl added _stereo = sound.Stereo _length = sound.Length #- alSourcePlay( _alSource ) End #if __TARGET__<>"emscripten" #rem monkeydoc @hidden - Highly experimental!!!!! #end Method WaitQueued( queued:Int ) While _queued>queued FlushProcessed() If _queued <= queued Return _waiting = True _future.Get() Wend End #rem monkeydoc @hidden - Highly experimental!!!!! #end Method Queue( data:AudioData ) Local buf:ALuint If Not _tmpBuffers _tmpBuffers = New Stack<ALuint> _freeBuffers = New Stack<ALuint> _future = New Future<Int> _waiting = False _queued = 0 _timer = New Timer( 60, Lambda() FlushProcessed() End ) Endif If _freeBuffers.Empty alGenBuffers( 1, Varptr buf ) _tmpBuffers.Push( buf ) Else buf = _freeBuffers.Pop() Endif alBufferData( buf, ALFormat( data.Format ), data.Data, data.Size, data.Hertz ) alSourceQueueBuffers( _alSource, 1, Varptr buf ) _queued += 1 Local state := ALState() If state = AL_INITIAL Or state = AL_STOPPED then alSourcePlay( _alSource ) End #endif #rem monkeydoc Stops channel. #end Method Stop() If Not _alSource Return alSourceStop( _alSource ) If _flags & ChannelFlags.AutoDiscard then Discard() End Protected #rem monkeydoc @hidden #end Method OnDiscard() Override If _alSource alDeleteSources( 1,Varptr _alSource ) _alSource=0 End #rem monkeydoc @hidden #end Method OnFinalize() Override If _alSource alDeleteSources( 1,Varptr _alSource ) End Private Field _flags:ChannelFlags Field _alSource:ALuint Field _volume:Float = 1 Field _rate:Float = 1 Field _pan:Float = 0 #- 'jladded field _stereo:bool = false field _length:int = 0 #- Global _autoDiscard := New Stack<Channel> Method ALState:ALenum() Local state:ALenum alGetSourcei( _alSource, AL_SOURCE_STATE, Varptr state ) Return state End Function FlushAutoDiscard() Local put := 0 For Local chan := Eachin _autoDiscard If Not chan._alSource then Continue If chan.ALState() <> AL_STOPPED then _autoDiscard[ put ] = chan put += 1 Continue Endif chan.Discard() Next _autoDiscard.Resize( put ) End #if __TARGET__<>"emscripten" Field _tmpBuffers:Stack<ALuint> Field _freeBuffers:Stack<ALuint> Field _future:Future<Int> Field _waiting:Bool Field _queued:Int Field _timer:Timer Method FlushProcessed:Int() Local proc:ALint alGetSourcei( _alSource, AL_BUFFERS_PROCESSED, Varptr proc ) If Not proc Return 0 For Local i := 0 Until proc Local buf:ALuint alSourceUnqueueBuffers( _alSource, 1, Varptr buf ) _queued -= 1 If _tmpBuffers.Contains( buf ) _freeBuffers.Push( buf ) Next If _waiting _waiting = False _future.Set( proc ) Endif Return proc End #endif End [/crayon]Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475#rem monkeydocHighly experimental audio module!#endNamespace mojo.audioConst AL_EXT_STEREO_ANGLES:ALenum = 1const AL_STEREO_ANGLES:ALenum = 4144'0x1030const WAVE_SIN:int = 0const WAVE_TRIANGLE:int = 1const WAVE_SQUARE:int = 2const WAVE_SAW:int = 3const WAVE_NOISE:int = 4PrivateFunction ALFormat:ALenum( format:AudioFormat )Local alFormat:ALenumSelect formatCase AudioFormat.Mono8alFormat=AL_FORMAT_MONO8Case AudioFormat.Mono16alFormat=AL_FORMAT_MONO16Case AudioFormat.Stereo8alFormat=AL_FORMAT_STEREO8Case AudioFormat.Stereo16alFormat=AL_FORMAT_STEREO16EndReturn alFormatEndPublic#rem monkeydoc Global instance of the AudioDevice class.#endConst Audio := New AudioDevice#rem monkeydoc The AudioDevice class.#endClass AudioDevice'***** Internal *****#rem monkeydoc @hidden#endMethod Init()Local error:=""_alcDevice = alcOpenDevice( Null )If _alcDevice_alcContext = alcCreateContext( _alcDevice, Null )If _alcContextIf alcMakeContextCurrent( _alcContext )ReturnElseerror = "Failed to make OpenAL current"EndifElseerror = "Failed to create OpenAL context"EndifElseerror = "Failed to create OpenAL device"EndifEndPrivateField _alcDevice:ALCdevice PtrField _alcContext:ALCcontext PtrField _error:StringEnd#rem monkeydoc The Sound class.#endClass Sound Extends Resourcefield _data:float[] = New float[256]#rem monkeydoc Creates a new sound.#endMethod New( data:AudioData )alGenBuffers( 1, Varptr _alBuffer )alBufferData( _alBuffer, ALFormat( data.Format ), data.Data, data.Size, data.Hertz )_format = data.Format_length = data.Length_hertz = data.Hertz' alIsExtensionPresent( AL_EXT_STEREO_ANGLES )'jlAdded#-Select data.FormatCase AudioFormat.Mono8_stereo = False_bits8 = TrueCase AudioFormat.Mono16_stereo = False_bits8 = FalseCase AudioFormat.Stereo8_stereo = True_bits8 = TrueCase AudioFormat.Stereo16_stereo = True_bits8 = FalseEnd SelectIf _stereo ThenLocal diff:float = float(data.Length - 1) / 128Local k:intLocal pos:intFor k = 0 To 127pos = (k * diff)_data[k*2] = GetSample( data, pos )nextFor k = 0 To 127pos = (k * diff)_data[1+k*2] = GetSample( data, pos, 1 )nextElseLocal diff:float = float(data.Length - 1) / 256Local k:intLocal pos:intFor k = 0 To 255pos = (k * diff)_data[k] = GetSample( data, pos )NextEnd If#-EndMethod New( wave:int = 4, footage:int = 2, offset:int = 64, inDiff:int = 0, smooth:int = 0, rough:int = 0 )const Pi2:float = Pi * 2wave = Clamp( wave, 0, 4 )Local length:intSelect footageCase 0 length = 2048Case 1 length = 1024Case 2 length = 512Case 3 length = 256Case 4 length = 128End SelectLocal sine:UByte[] = New UByte[length]local sinData:AudioData = New AudioData( length, AudioFormat.Mono8, 44100, Cast<UByte Ptr>( sine.Data ) )Select waveCase 4 'noise_file = "Noise"For Local i := 0 Until lengthsine[i] = Rnd(255)NextCase 1 'tri_file = "Triangle"Local x1:int = length * .25Local x2:int = length * .5Local x3:int = length *.75Local div:float = float(255) / float(length)For Local i := 0 Until lengthIf i < x1 Thensine[i] = 127 + (i * 2)*divElse If i < x3 Thensine[i] = 255 - ( (i-x1) * 2 ) * divElsesine[i] = ( (i-x3) * 2 ) * divEnd IfNextCase 3 'saw_file = "Saw"Local div:float = float(255) / float(length)For Local i := 0 Until lengthsine[i] = i * divNextCase 2 'square_file = "Square"Local x2:int = length * .5For Local i := 0 Until lengthIf i < x2 Thensine[i] = 255Elsesine[i] = 0End IfNextCase 0 'sin_file = "Sin"For Local i := 0 Until lengthsine[i] = Sin( Float(i)/length * Pi2 ) * 127.5 + 127.5NextEnd selectIf smooth > 0 ThenLocal sm:intLocal i:intFor sm = 1 To (smooth / 2)+1sine[length-1] = float(sine[length-1] + sine[0]) * .5For Local i := 1 Until lengthsine[i-1] = float(sine[i-1] + sine[i]) * .5NextFor Local i := length-2 to 0 Step -1sine[i+1] = float(sine[i+1] + sine[i]) * .5Nextsine[length-1] = float(sine[length-1] + sine[0]) * .5NextEnd IfIf inDiff > 0 and offset > 0 ThenLocal diff:int = inDiffFor Local i := 0 Until lengthIf i Mod offset = 0 Then diff -= inDiffsine[i] = sine[i] - diffNextEnd IfIf rough > 0 Thenrough += roughFor Local i := 0 Until length Step 2sine[i] = Clamp( float(sine[i] + Rnd(-rough, rough)), 0.0, 255.0)NextEnd ifIf smooth > 0 ThenLocal sm:intLocal i:intFor sm = 1 To (smooth / 2)+1sine[length-1] = float(sine[length-1] + sine[0]) * .5For Local i := 1 Until lengthsine[i-1] = float(sine[i-1] + sine[i]) * .5NextFor Local i := length-2 to 0 Step -1sine[i+1] = float(sine[i+1] + sine[i]) * .5Nextsine[length-1] = float(sine[length-1] + sine[0]) * .5NextEnd IfalGenBuffers( 1, Varptr _alBuffer )alBufferData( _alBuffer, ALFormat( sinData.Format ), sinData.Data, sinData.Size, sinData.Hertz )_format = sinData.Format_length = sinData.Length_hertz = sinData.Hertz_stereo = False_bits8 = TrueLocal diff:float = float(length - 1) / 255Local k:intLocal pos:intFor k = 0 To 255pos = k * diff' Print "pos="+k+" linked to "+pos+" length "+length+" data "+GetSample( sinData, pos )_data[k] = GetSampleMono8( sinData, pos )NextsinData.Discard()End methodMethod GetSampleMono8:Float( data:AudioData, index:Int )Return data.Data[index]/128.0-1EndMethod GetSample:Float( data:AudioData, index:Int, channel:int = 0 )Select data.FormatCase AudioFormat.Mono8Return data.Data[index]/128.0-1Case AudioFormat.Stereo8Return data.Data[index*2 + (channel&1)]/128.0-1Case AudioFormat.Mono16Return Cast<Short Ptr>( data.Data )[index]/32767.0Case AudioFormat.Stereo16Return Cast<Short Ptr>( data.Data )[index*2 + (channel&1)]/32767.0End SelectReturn 0Endmethod Draw( canvas:Canvas, x:float, y:int, width:float, height:int, showText:bool = true )Local halfHeight:float = height * .5If showText Thencanvas.Color = Color.LightGreycanvas.DrawText( _file, x, y+height-16 )End ifLocal last:float = y+halfHeightLocal curr:floatLocal xp:float = float(width) / 255Local k:intIf _stereo ThenLocal quarterHeight:float = height * .25last = y+quarterHeightLocal height2:float = height - quarterHeightLocal last2:float = y+height2canvas.Color = Color.DarkGreycanvas.DrawLine( x, y+halfHeight, x+width, y+halfHeight )canvas.Color = Color.White*0.6canvas.DrawLine( x, y+quarterHeight, x+width, y+quarterHeight )canvas.DrawLine( x, y+height2, x+width, y+height2 )If showText Thencanvas.Color = Color.Greycanvas.DrawText( "Left", x, y )canvas.DrawText( "Right", x, y+halfHeight )End ifcanvas.Color = Color.GreenLocal wd:float = width / 256Local x1:float = x+1For k = 1 To 255If k Mod 2 = 0 Thencurr = y+quarterHeight - ( _data[k] * quarterHeight )canvas.DrawLine( x, last, x1, curr )last = currElsecurr = y+height2 - ( _data[k] * quarterHeight )canvas.DrawLine( x, last2, x1, curr )last2 = currEnd Ifx = x1x1 += wdNextElsecanvas.Color = Color.White*0.6canvas.DrawLine( x, y+halfHeight, x+width, y+halfHeight )canvas.Color = Color.GreenLocal wd:float = width / 256Local x1:float = x+1For k = 1 To 255curr = y+halfHeight - ( _data[k] * halfHeight )canvas.DrawLine( x, last, x1, curr )last = currx = x1x1 += wdNextcanvas.DrawLine( x, last, x1, y+halfHeight )End IfEnd method#-'jl added#rem monkeydoc Returns true if a loaded or created sound sample is stereo#endProperty Stereo:bool()Return _stereoSetter( stereo:bool )_stereo = stereoEnd#rem monkeydoc Returns true if a loaded or created sound sample is 8bits#endProperty Bits8:bool()Return _bits8Setter( bits8:bool )_bits8 = bits8End#-#rem monkeydoc The length, in samples, of the sound.#endProperty Length:Int()Return _lengthEnd#rem monkeydoc The format of the sound.#endProperty Format:AudioFormat()Return _formatEnd#rem monkeydoc The playback rate of the sound.#endProperty Hertz:Int()Return _hertzEnd#rem monkeydoc The duration, in seconds, of the sound.#endProperty Duration:Double()Return Double( _length ) / Double( _hertz )EndProperty Filename:String()Return _fileEnd#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]].#endMethod Play:Channel( loop:Bool = False )Local channel := New Channel( ChannelFlags.AutoDiscard )channel.Play( Self, loop )Return channelEnd#rem monkeydoc Loads a sound.#endFunction Load:Sound( path:String )Local data := AudioData.Load( path )If Not data Return NullLocal sound := New Sound( data )sound._file = StripDir( path )data.Discard()Return soundEndProtected#rem monkeydoc @hidden#endMethod OnDiscard() OverrideIf _alBuffer alDeleteBuffers( 1, Varptr _alBuffer )_alBuffer = 0End#rem monkeydoc @hidden#endMethod OnFinalize() OverrideIf _alBuffer alDeleteBuffers( 1, Varptr _alBuffer )EndPrivatefield _file:string = ""Field _alBuffer:ALuintField _format:AudioFormatField _length:IntField _hertz:Int'jl addedfield _stereo:bool = Falsefield _bits8:bool = falseEnd#rem monkeydoc ChannelFlags enum.| Flag | Description|:--------------|:-----------| [crayon-5cba16daeb862720557726 inline="true" ]AutoDiscardHere’s a quick shot of the draw command in operation with 3 different sound shown at different sizes. the bg color is drawn separately – hence the color change.
You can see that both mono and stereo, 8 and 16 bit are correctly displayed along with the base filename (also a new addition)
I’m sure someone might find it useful :0
Attachments:
 - 
		AuthorPosts
 
You must be logged in to reply to this topic.
