Sounds Good To Me

BlitzMax Forums/BlitzMax Beginners Area/Sounds Good To Me

dw817(Posted 2016) [#1]
Nope. No BlitzMAX example code, not a lick, this one has me completely stumped.

Now back in time you could create speaker effects many different ways. One method I had that sounded like a sword clinging against a shield was:
FOR I=1000 TO 1 STEP -1:SOUND 750+RND()*I,.1:NEXT
BlitzMAX - at least to my knowledge has no way to create sound internally, not easily anyways.

So has someone made a routine or code or what have you that will allow you to play a note of a particular pitch from too low to hear to too high to hear and at a duration that can be anywhere from 4 long seconds to as small as a thousandth of a second ?


Derron(Posted 2016) [#2]
Have a look at this code archive entry by skidracer/nitro/simonarmstrong


Original request of me was to provide streamed-ogg-support but his initial code was a dynamic created sound.


It is not exactly what you want but maybe it is of help.


bye
Ron


Midimaster(Posted 2016) [#3]
Dont know, whether it helps... but some time ago I build a software sythesizer for education purposes.

Download:
http://www.midimaster.de/download/hoerensehen_demo.exe



Inside the code there is a part which show how to create samples and prepare it for the TSound format of BlitzMax. Also shown how to do it in real time:




dw817(Posted 2016) [#4]
Hi Derron. I looked at that code - and listened. Sounded ok for the first part. Changed this line:
	osc1=Sin(t*(lfo+10))
to listen to a higher pitch.

Problem is there is a nasty warbling in the background, some type of feedback. Definitely not good.

You mean no-one has written a clean sound generator ? Even the Atari 2600, primitive as it was, did a fine job for what the resources they had. I'd settle for that.

SOUND LINK.

MM, your program crashes on FLIP 0 no graphics are set up ?

Failing this I know years ago I would play MIDI instruments on one of my adventure games (the upper 128) to create sound effects - so that is also a possibility if someone has written code to pluck MIDI instruments individually.

This might actually be the route to take as your piano pitches are already set.


Midimaster(Posted 2016) [#5]
The code is non-runable sample! This are only some lines from my 2000-lines-code, which show elementary technics of sound generating and converting.

MIDI will not help you, because it plays only given 120 music instruments and 8 terrible sound effects.

With my algorithm you will be able to create any waveform and process it to the audio device.

Undesired sound effect result from 3 reasons:

1. Distortion, means no value should touch the 255 or be higher
For  Local k%=0 Until 44100
	RohWert[k]=RohWert[k]/maxwert
Next



2. Wrong zero point alignment, a volume-value of 0 should result in a 128
For  Local k%=0 Until 44100
	sample.samples[k]=(RohWert[k]*127.0+128) Mod 256
Next



3. The 100 Start- and 100 End- values of a sample need to form a locigal curve.
' altes Sample wegen Knacksern ausblenden
		For       Local hh#=100 To 0 Step -3
			SetChannelVolume Channel1,hh/100
			Delay 1
		Next

	
	' Sound aus Sample erzeugen
		sound:TSound=LoadSound( sample,True )


		
		StopChannel Channel1
		channel1=CueSound(sound)
		SetChannelVolume Channel1,0
		
		ResumeChannel Channel1
	' neues Sample wegen Knacksern einblenden
		For      Local hh#=0 To 100 Step 3
			SetChannelVolume Channel1,hh/100
			Delay 1
		Next
EndIf



Derron(Posted 2016) [#6]
The sound distortion might come from the "streaming code". If you preallocate the sample and fill it with the needed data, it should work.


But if you pre-define everything, checkout
CreateStaticAudioSample()
CreateAudioSample()

Or check out this Thread.


bye
Ron


dw817(Posted 2016) [#7]
Hi Derron and company. Here is the best I have gotten so far in creating pitch playing.



If I can do away with the opening and ending click or gurgle, I may be able to write a useful function to give users the ability to generate sound much like you could do with the PC speaker years ago.

. . .

To answer the above, FreeStream16 does not maintain a solid note, it wavers like getting a reception on a radio.


grable(Posted 2016) [#8]
Using
SetAudioDriver "OpenAL"
Seems to remove most of the popping for me, but there is still one at the end with your sample.
You will need to have OpenAL installed for this driver to work though.

Loading one from a file has no clicking, so i suspect the waveform that you generate is not correct as Midimaster suggested.

After some searching i found a way to generate a sine wave here.
The result is below, with no popping. I have no idea what im doing here btw, i just fudged the numbers until a reasonable sound came out ;)
SuperStrict

SetAudioDriver "OpenAL"
Local sample:TAudioSample=CreateAudioSample( 3000,11025,SF_MONO8 )

Local amplitude:Float = 255
Local samplerate:Float = 11025
Local frequency:Float = 11025
Local phase:Float
Local delta:Float = 2.0 * Pi * frequency / samplerate

For Local k:Int=0 Until 3000
	sample.samples[k]=amplitude * Sin(phase)
	phase :+ delta
Next

Local sound:TSound=LoadSound(sample)

PlaySound sound
Input
Also searched the forum and found this: Realtime Synthesis with openAL
Should give you something to chew on for a while ;)


Casaber(Posted 2016) [#9]
The ending pop happens becuase the value in the wave at the end is not 127 (middle for 8bit samples). For exmaple if you fill it with any constant value,
you would get silence, but at the end it would trip down to zero, so if that trip is.. large enough, then we hear a pop.

If I could lend that code above and show what I mean

SuperStrict

SetAudioDriver "OpenAL"
Local sample:TAudioSample=CreateAudioSample( 3000,11025,SF_MONO8 )

Local amplitude:Float = 255
Local samplerate:Float = 11025
Local frequency:Float = 11025
Local phase:Float
Local delta:Float = 2.0 * Pi * frequency / samplerate

For Local k:Int=0 Until 3000
	sample.samples[k]=amplitude * Sin(phase)
	phase :+ delta
Next

' Overwrite the waveform and using this wave instead.
For Local k:Int=0 Until 3000
  sample.samples[k]= 127 ' Fill the waveform with all middle value for a 8bit wave, means no pop at the end (fill with for example 0 or 255 or any value large enough from the middle, that would give silence with an ending pop)
Next

Local sound:TSound=LoadSound(sample)

PlaySound sound
Input


The original code fills the wave with alternating 0 and 255 which basically creates a square wave, so whichever it starts or ends on 0 or 255, will make a pop. As it's not middle (127).

Abrupt changes always creates pops with sounds.
I would look into volume control first. Vvolume ramping is the most practical way as you need to do it alot.
If you're doing softsynths then you could do it on the actual wave data without wasting any power, as you're building the waves anyways yourself.


dw817(Posted 2016) [#10]
For a bit. Here is what I tried to work with:

Strict

SetAudioDriver "OpenAL"
Global sample:TAudioSample=CreateAudioSample(256,22050,SF_MONO8 )

Local sound:TSound=LoadSound(sample),i

For i=255 To 0 Step -1
  tone i
  Delay 1
Next
Input

Function tone(n)
  Local a#=255,s#=11025,f#=11025,p#,d#=n/256.0*Pi*f*s,i
  For i=0 Until 256
    sample.samples[i]=a*Sin(p)
    p:+d
  Next
  PlaySound LoadSound(sample)
EndFunction


But it doesn't create a high to low sound, just a weird warble.


grable(Posted 2016) [#11]
So the problem with dw817s sample had more to do with the sample rate(Hz) then, as he also use 127 for the entire sample.

Maybe modern audio hardware just dont like weird sample rates?


Casaber(Posted 2016) [#12]
No he does not use 127 for the entire sample he fills every other byte. Thats key here.
If he filled every then it would make perfect silence with no pops at ens or start.

But he fills every other byte, that would make it into 0 127 0 127 0 127 which is a square.


grable(Posted 2016) [#13]
Ah yes, i forgot about that.. And i didnt notice as i was testing it, that he also skipped the last byte, making it always 0 ;)


Casaber(Posted 2016) [#14]
The original code clicks at the start (but not end) if you fill it with all 127's I don´t know why that is. It should not click at the start either.

The next examples works as they should if you fill with 127 only (no clicks at all).

EDIT
I tried some things with the original to find what made the click
adding SetAudioDriver "OpenAL" at the original code muffles the click it does not dissapear all the way, it just muffles it.

I thought maybe it's the length or the odd samplerate. It was not the samplerate but the length. Changing from 256 to 3000 made the start click dissapear aswell perfectly. No idea why. Something with buffersizes I guess.


Casaber(Posted 2016) [#15]
Nope I was wrong, it was not the buffersize. I´m not sure what's difference that creeped between those two. I don't see it.

But this does work perfectly.

SuperStrict

SetAudioDriver "OpenAL"
Local sample:TAudioSample=CreateAudioSample( 256,11025,SF_MONO8 )

For Local k:Int=0 Until 256
  sample.samples[k]= 127 
Next

Local sound:TSound=LoadSound(sample)

PlaySound sound
Input



Casaber(Posted 2016) [#16]
I find it amazing that OpenAl comes standard with Bmax I think I will open the manual for this one. Great potential.

It seems that setting the driver sets some default parameters (filters etc).

Looking into this, in practice you could build your own soundfontplayer by hand using this (it would be fun).


dw817(Posted 2016) [#17]
Hi Casaber. Actually I don't hear anything here w this code. Now, if you change the until 256 to until 256 step 2 you get a harsh sound but an equally harsh click.

Unless there is improvement I was thinking of dissecting a .WAV file tomorrow and see if it's possible to build one in BlitzMAX that can be 1/100th of a second and at any pitch, then load it and play it.


Casaber(Posted 2016) [#18]
Haha it's my offical hello world with sound. Silence without clicks ;)

I´m buzy trying to understand the byte format when jumping into 16bit, look here.
Mind you that I changed from 8bit into 16bit Little Endian. 8 bits sounds very harsch
I guess you want that sometimes, but you could do that in 16bit aswell by double everything (like zooming pixels and make them retro graphic pixels)

I think I´m just tired now, but it seem like it uses bytes when you read it.
Also as you can see I got the content wrong, the sinue is fucked up. This is what I call, the first step learning a tool aka the swearing period.


SetAudioDriver "OpenAL"
Graphics 800,600
Const SampleSize:Int = 100000
Global sample:TAudioSample = CreateAudioSample(SampleSize, 44100, SF_STEREO16LE)
Global sampleData:Byte[SampleSize]
Print sample.length ; Print getSampleLength(sample)
SeedRnd MilliSecs()
For Local m:Int = 0 Until SampleSize
	sample.samples[m] = Sin(m)*128
' 	sample.samples[m] = 32767 ' 65535 ' uncomment for the byte test
Next
Local sound:TSound = LoadSound(sample)
Local channel:TChannel = PlaySound(sound)

Repeat
	Cls
	If channel.playing() Then		drawWave(sample)
	If KeyDown(KEY_SPACE) And Not channel.playing() Then channel = PlaySound(sound)
Flip
Until KeyDown(KEY_ESCAPE)
End

Function getSampleLength:Float(sample:TAudioSample)
	Return Float( (sample.length / Float( (sample.hertz * 60) ) ) * 60)
End Function

Function drawWave(sample:TAudioSample)
	For x = 0 Until sample.length Step 1
		y = sample.samples[x]
                if y = 32767 then end  ' if found 32767 as setup above (but it's never found, its a wrapping byte)
		Plot x,y
	Next
End Function



Casaber(Posted 2016) [#19]
oops Stereo I saw that now. That explains the sinus. Okay, but that's probably not everythng wrong with it. This is one of those things you need to sit down and take your time with.

Is it okay if I skip this one? Lots of details to learn with Bmax. I can't even get my white noise random generator to work in Bmax which is frustrating (There's no long unsigned).

btw this is it if your interested for later, it's perfect for explosions and shoots, it's short and sweet, it's a common "linear congrunetial" but its very cheap.

I guess that maters only when you do realtime synthesis. But, it doubles as an allaround random number generator.
' White noise or pseudo random numbers. Seed with anything.
Function RandomNumber:Long()
Seed = (Seed * 196314165) + 907633515
Return Seed
End Function


Midimaster(Posted 2016) [#20]
some changes from me:



more is not possible...

I think the best would be to define a ring buffer and send samples continously direct to openal. I did something similar in my "recorder". http://www.blitzbasic.com/Community/posts.php?topic=90830 But there it was a ring buffer for fetching audio in. The same must be possible for audio out.

found this:

http://www.blitzbasic.com/Community/posts.php?topic=95529