Prototype Audio Manipulation Class

Community Forums/Showcase/Prototype Audio Manipulation Class

RexRhino(Posted 2005) [#1]
Here is part of the audio class I am working on (It will have a bunch of functions like echo/reverb, envelope functions, oscillators, etc., in the future). Right now I want to make sure it is working properly cross-platform (and also, there is very little info on using audio in blitzmax, so I wanted to get something out so people dont have the sample problems I had). It is 16 bit stereo 44.1khz only, but I don't think it is worth working on anything less... most machines have more than enough memory and speed to handle that.

If people could try it out (especially Mac and Linux users, but everyone in general), and let me know if it is working properly (maybe try creating a 440 reference tone or something if you are unsure how my FM sample included in the code is supposed to sound). The main thing I am worried about is the Little Endian / Big Endian samples sounding different on different platforms. Once I know it is working properly on all platforms, then I can throw in all the fun synthesis features.

Strict

Local x:Int 
Local sound:Jsound = New JSound
sound.create_sample(44100)

For x = 0 To (44100 - 1)
	Local l_ch:Short = sinewave(10, sinewave(.01, 440, x), x)
	Local r_ch:Short = sinewave(10, sinewave(.01, 440, x, 100), x, 100)
	
	sound.write(x,l_ch,r_ch)
Next

Local sound_2:TSound = sound.get_sound()
PlaySound sound_2
Input x

'***********************************************************************
'sinewave(amp_prc, freq_hz, k_smp, offset)
'	amp_prc - Percentage value for amplitude. You may give values greater than 100 if you want the wave overdriven.
'	freq_hz - This is the frequency of the sine wave in hz (cycles per second)
'	k_smp - this is a sample offset. You must run the sinewave function for each sample in a sound
'	offset - this is the wave offset. It effects where in the cycle a wave begins.
'***********************************************************************
Function sinewave:Short(amp_prc:Float, freq_hz:Float, k_smp:Int, offset:Int = 0)
	Const bias = 32767 'using a short, so we want a positive 16 value. Add the bias to bring wav up to positive values
	Const radian = 57.2957795 'we need degrees, not radians, so we must use a conversion
	Local p:Float = (44100 / freq_hz) 'this is the number of samples per wave cycle
	Local amp:Float = (amp_prc / 100) * 32767 'amp is 0-1 in the equation... we are converting from a percentage from amp_prc
	Local val:Float = (amp * Sin((2 * Pi * (k_smp + offset) * radian) / p)) + amp 'finally, we calculate the value
	Return val
End Function

Type JSound

	Field working_sample:TAudioSample
	Field playing_sample:TSound
	Field sound_bank:TBank 
	
	Field dur_bytes:Int
	Field dur:Int
	
	Method New()
		working_sample:TAudioSample = Null
		playing_sample:TSound = Null
		sound_bank:TBank = Null
		FlushMem
	End Method
	
	'***********************************************************************
	'.create_sample(duration)
	'	duration - This is the number of samples in the sound. There are 44100 samples in one second of CD quality audio
	'***********************************************************************
	Method create_sample(duration:Int)
		dur = duration;
		dur_bytes = duration * 4
		sound_bank:TBank = CreateBank(dur_bytes)
	End Method
	
	'***********************************************************************
	'.write(offset, l_val, r_val)
	'	offset - this is the sample you want to set the value for. 
	'	l_val - the is the left value of the 
	'***********************************************************************
	Method write(offset:Int, l_val:Short, r_val:Short)
		If sound_bank = Null Then Return
		
		Local l_ch:Int = (offset * 4)
		Local r_ch:Int = (offset * 4) + 2
		
		PokeShort(sound_bank, l_ch, l_val)
		PokeShort(sound_bank, r_ch, r_val)
	End Method
	
	'***********************************************************************
	'.read_left(offset)
	'	offset - this is the sample you want to get the value for. 
	'***********************************************************************
	Method read_left:Short(offset:Int)
		Local l_ch:Int = (offset * 4)
		Return PeekShort(sound_bank, l_ch)
	End Method
	
	'***********************************************************************
	'.read_right(offset)
	'	offset - this is the sample you want to get the value for. 
	'***********************************************************************
	Method read_right:Short(offset:Int)
		Local r_ch:Int = (offset * 4) + 2
		Return PeekShort(sound_bank, r_ch)
	End Method
	
	'***********************************************************************
	'.get_sound() 
	'***********************************************************************
	Method get_sound:TSound()
		working_sample:TAudioSample = CreateStaticAudioSample(BankBuf(sound_bank), dur, 44100, SF_STEREO16LE)
		playing_sample:TSound = LoadSound(working_sample)
		FlushMem
		Return playing_sample
	End Method
End Type



Damien Sturdy(Posted 2005) [#2]
Good start there. Id like to see where this goes. If you get stuck on something just give us an email as im workin on something very similar! :) (im also sure that as always, this forum will help)


Booticus(Posted 2005) [#3]
Well, I get some wierd scratchy static with an underlying tone and then it hangs. The output stays there displaying the 44100 and just sits there and hangs, I have to do a Force Quit to get out of it (Windows equivalent of a CTRL-ALT-DELETE for Task Manager). Im a G4 blue and white, 1.2ghz and no fancy sound cards just what came with the Mac. Lemme know if you want to troubleshoot!

-Mario


LAB[au](Posted 2005) [#4]
It works here. Win32 beta, Creative audigy soundcard. I'd like to see this project going further :)


RexRhino(Posted 2005) [#5]
I already created lowpass filter, echo, envelopes, and others, and plan to post them when I get a few more...

I am concerned about the audio problems on Mac. Booticus, Can you try changing the audio file from SF_STEREO16LE to SF_STEREO16BE and see if that works on your mac? I think it is a problem with the Little Endian, Big Endian thing.

Is there a statement to detect platform in Blitz?


Booticus(Posted 2005) [#6]
Hey Rex! Yep that did the trick. Sounds a lot more pleasant. A wierd flangy tone. Im no audio guy so forgive my feeble description of the sound. As for the statement to detect platform...ya'd think there wouldn't be because of the whole cross-platform thingie, but I dont know for sure. Anyone? Anyone? Bueler? ;)


Booticus(Posted 2005) [#7]
Oh FWIW it works fine on Win32 Beta. XP, on board audio.


Dirk Krause(Posted 2005) [#8]
very cool, Rex, can't wait to see this! Are you going into the direction synthesizer?


Yan(Posted 2005) [#9]
Is there a statement to detect platform in Blitz?

http://www.blitzwiki.org/index.php/Compiler_Directives


BlitzSupport(Posted 2005) [#10]
Nice! Sticking this at the top plays it correctly in both Windows and OS X here (just change the constant in CreateStaticAudioSample at the bottom to AUDIOFORMAT):

?Win32
Const AUDIOFORMAT = SF_STEREO16LE
?Linux
Const AUDIOFORMAT = SF_STEREO16LE
?MacOS
Const AUDIOFORMAT = SF_STEREO16BE
?

Looking forward to hearing some cool FX!


Hotcakes(Posted 2005) [#11]
There are more compiler directives which may be more appropriate in certain circumstances and these are ?PPC, ?x86, ?BigEndian and ?LittleEndian.