Code archives/Audio/freeaudio streaming

This code has been declared by its author to be Public Domain code.

Download source code

freeaudio streaming by skidracer2014
generate dynamic waveforms and stream them directly to a freeaudio device
' freestream.bmx
' mono 8 bit streaming example for 

Strict
Import pub.freeaudio

Const FRAG=4096

Print "freestream is free streaming..."

fa_Init(0)	' brl.freefreeaudio usually does this

Local buffer:Byte[FRAG*8]
Local writepos

Local sound
Local channel

sound=fa_CreateSound( FRAG*8,8,1,44100,buffer,$80000000)
Print "Sound:"+sound

channel=fa_PlaySound( sound, FA_CHANNELSTATUS_STREAMING ,0)
Print "PlaySound:"+channel

Local streaming
Local lfo#
Local osc1#

While True
'	Print "Status:"+fa_ChannelStatus( channel )	
	Local readpos=fa_ChannelPosition( channel )
	Local write=readpos+FRAG*4-writepos
	Local frags=write/FRAG	
	While frags>0	
		Print "Write to "+writepos
		Local pos=writepos Mod (FRAG*8)
		For Local f=0 Until FRAG				
			Local t=writepos+f	
			lfo=Sin(0.001*t)
			osc1=Sin(t*(lfo+2))
			buffer[pos+f]=128+10*lfo*osc1
		Next
		writepos:+FRAG
		frags:-1
	Wend
	
	If Not streaming And writepos>=FRAG*4
		fa_SetChannelPaused( channel, False )
		streaming=True
	EndIf
	
	Print "."	

	Delay 50
Wend

Comments

skidracer2014
this version uses an int buffer to supply the left and right channels of a stereo 16 bit freeaudio stream

' freestream16.bmx

' stereo 16 bit streaming example

Strict

Import pub.freeaudio

Const FRAG=1024

Print "freestream is free streaming..."

fa_Init(0)	' brl.freefreeaudio usually does this

Local buffer:Int[FRAG*8]
Local writepos

Local sound
Local channel

sound=fa_CreateSound( FRAG*8,16,2,44100,buffer,$80000000)
Print "Sound:"+sound

channel=fa_PlaySound( sound, FA_CHANNELSTATUS_STREAMING ,0)
Print "PlaySound:"+channel

Local streaming
Local lfo#
Local osc1#

While True
'	Print "Status:"+fa_ChannelStatus( channel )	
	Local readpos=fa_ChannelPosition( channel )
	Local write=readpos+FRAG*4-writepos
	Local frags=write/FRAG	
	While frags>0	
		Print "Write to "+writepos
		Local pos=writepos Mod (FRAG*8)
		For Local f=0 Until frag				
			Local t=writepos+f	
			lfo=Sin(0.001*t)
			osc1=Sin(t*(lfo+2))

 		Local l16=$ffff & Int(1000*lfo*osc1)
 		Local r16=$ffff & Int(2000*Sin(t))
	
			buffer[pos+f]=L16|(R16 Shl 16)

		Next
		writepos:+FRAG
		frags:-1
	Wend
	
	If Not streaming And writepos>=FRAG*4
		fa_SetChannelPaused( channel, False )
		streaming=True
	EndIf
	
	Print "."	

	Delay 50
Wend




skidracer2014
this version streams a stereo 16 bit ogg file

' freestreamogg.bmx
' streaming stereo 16 bit OGG decoder to freeaudio 

Strict

Import pub.freeaudio
Import pub.oggvorbis

Type TAudioStream
	Const FRAG=8000

	Field buffer:Int[FRAG*8]
	Field writepos
	
	Field sound
	Field channel
	Field streaming
	
	Method PlayStereo16(freq)
		sound=fa_CreateSound( FRAG*8,16,2,freq,buffer,$80000000)
		Print "Sound:"+sound		
		channel=fa_PlaySound( sound, FA_CHANNELSTATUS_STREAMING ,0)
		Print "Channel:"+channel
		streaming=False
	End Method
	
	Method Poll()
		Local readpos=fa_ChannelPosition( channel )
		Local write=readpos+FRAG*4-writepos
		Local frags=write/FRAG	

		While frags>0	
			Print "Write to "+writepos
			Local pos=writepos Mod (FRAG*8)
			Local res=ReadBuffer(Byte Ptr(buffer)+pos*4,FRAG*4)
			Print "ReadBuffer="+res
			writepos:+FRAG
			frags:-1
		Wend
			
		If Not streaming And writepos>=FRAG*4
			fa_SetChannelPaused( channel, False )
			streaming=True
		EndIf
	End Method

	Method ReadBuffer%(buffer:Byte Ptr,bytes)
	End Method
End Type

Type TOggStream Extends TAudioStream
	
	Field source:Object
	Field stream:TStream
	Field ogg:Byte Ptr

	Field samples
	Field channels
	Field freq
	Field size

	Method Open( url:Object )
		source=url
		stream=ReadFile(url)
		ogg=Decode_Ogg(stream,readfunc,seekfunc,closefunc,tellfunc,samples,channels,freq)
		size=samples*2*channels		
		PlayStereo16(freq)
	End Method
		
	Method ReadBuffer%(buffer:Byte Ptr,bytes)	
		If Not ogg Return		
		If bytes>size bytes=size
		If bytes=0 Return 0
		Local err=Read_Ogg( ogg,buffer,bytes )
		If err Return -1
		size:-bytes
'		If size=0 Open source
		Return bytes
	End Method

	Function readfunc( buf@Ptr,size,nmemb,src:Object )
		Local bytes=TStream(src).Read(buf,size*nmemb)
		Return bytes/size
	End Function
	
	Function seekfunc( src_obj:Object,off0,off1,whence )
		Local off
		Local src:TStream=TStream(src_obj)
		off=off0	'WARNING 32 bit BADNESS
		Local res=-1
		Select whence
			Case 0
				res=src.Seek(off)			'SEEK_SET
			Case 1
				res=src.Seek(src.Pos()+off)	'SEEK_CUR
			Case 2
				res=src.Seek(src.Size()+off)	'SEEK_END
		End Select
		If res>=0 Return 0
		Return -1
	End Function
	
	Function closefunc( src:Object )
	End Function
	
	Function tellfunc( src:Object )
		Return TStream(src).Pos()
	End Function

End Type

fa_Init(0)	' brl.freefreeaudio usually does this

Local ogg:TOggStream

ogg=New TOggStream

ogg.Open("bouncy.ogg")

Print "ogg.size="+ogg.size+" ogg.channels="+ogg.channels+" ogg.freq="+ogg.freq

Print "freestream is free streaming..."

While True
	ogg.Poll
	Print "."	
	Delay 50
Wend




BlitzSupport2014
This is cool, but both the 8-bit and the ogg versions have a 'fluttering' sound throughout for me -- the sound plays but has a constant "p-p-p-p-p-p-p" playing through it while doing so. I did find one ogg that played without flutter*, but all three that I tried are 16-bit according to Audacity.

The 16-bit demo was also flutter-free.

* Actually, on headphones, that has the flutter too, just quieter.

Just tried reducing FRAG to 1000 on that quiet flutter track and it was a lot better during a couple of plays, but now after trying the 'noisy tracks' (they stayed noisy) the 'quiet' track is doing it too!

They all play fine with this:

ogg$ = "xyz.ogg"
sound:TSound = LoadSound (ogg$)
PlaySound sound
Delay 5000


For what it's worth, sound card is "Creative SB Audigy 2 (WDM)".


BlitzSupport2014
Oh, and right now the first demo is flutter-free and the 16-bit version is fluttering!


skidracer2014
James, you may want to make FRAG bigger not smaller, I have modified the first example to more realistic size.


Derron2014
I used 8k or bigger to make cracklings go away.

Reason is: If you eg. only update the streams buffer 30 times a second, this means your buffer must in all cases be longer than 1/33 = 33ms. If your buffer is slightly smaller you have chances to play some milliseconds of "old sound".

In the examples using "Delay 50" this means that at least 50 ms are gone till the next buffer refill.

Maybe there is one having time and knowledge to write it in a threaded way so some kind of autopolling can be achieved (regardless of delays, or occupation of the mainthread like resource loading).

To make it more convertible I wrapped the code in a way so that TChannel/TSound get used.


I do not post the code here because it is not PD but zlib/libpng-licenced:
https://github.com/GWRon/Dig/blob/3ea47f1e85680dc4403c490751fdd069a2d28047/base.sfx.soundstream.bmx

Code contains some simple "Clone"-Method to enable crossfading to the same sound (as you cannot share the buffers but the source bank/stream).


Code Archives Forum