But here is the new and improved (cross-platform) code:
Code: Select all
;Raw Sound Creation Demo
;By: Joakim L. Christiansen
;Version: 1.1
EnableExplicit
; Structure WAVEFORMATEX ;may need to uncomment on Linux or Mac
; wFormatTag.w
; nChannels.w
; nSamplesPerSec.l
; nAvgBytesPerSec.l
; nBlockAlign.w
; wBitsPerSample.w
; cbSize.w
; EndStructure
Global format.WAVEFORMATEX
Procedure setWaveFormat(bitsPerSample=16,samplesPerSec=44100)
format\wFormatTag = #WAVE_FORMAT_PCM
format\nChannels = 1 ;mono
format\wBitsPerSample = bitsPerSample ;8/16
format\nSamplesPerSec = samplesPerSec ;8000 Hz, 11025 Hz, 22050 Hz, and 44100 Hz
format\nBlockAlign = (format\nChannels * format\wBitsPerSample) / 8 ;equal to the product of nChannels and wBitsPerSample divided by 8 (bits per byte).
format\nAvgBytesPerSec = format\nSamplesPerSec * format\nBlockAlign ;equal to the product of nSamplesPerSec and nBlockAlign
EndProcedure
Procedure.l createTone(*address,volume.d,duration,frequency.d) ;returns needed size if address=0
;creates a triangle wave
Protected sample.l, result
Protected pos, dir, waveY.d, maxValue, centerValue
Protected samplesPerHalfWave, waveIncrementValue.d, size
Protected samplesSinceHalfWave, waveTop.d
size = (format\nSamplesPerSec / 1000*duration) * format\wBitsPerSample / 8
If *address = 0 ;return size needed
ProcedureReturn size
EndIf
If format\wBitsPerSample = 8
maxValue = 255
ElseIf format\wBitsPerSample = 16
maxValue = 65535
EndIf
centerValue = (maxValue/2)
waveTop = volume/1000 * centerValue
samplesPerHalfWave = (format\nSamplesPerSec / frequency) / 2
waveIncrementValue = waveTop / samplesPerHalfWave
For pos=0 To size
If dir=0
waveY + waveIncrementValue
Else
waveY - waveIncrementValue
EndIf
samplesSinceHalfWave + 1
If samplesSinceHalfWave >= samplesPerHalfWave
dir = dir!1 ;switch it
samplesSinceHalfWave = 0
EndIf
If format\wBitsPerSample = 8
sample = centerValue + waveY
PokeA(*address+pos,sample)
ElseIf format\wBitsPerSample = 16
sample = waveY
PokeW(*address+pos,sample)
pos+1
EndIf
Next
EndProcedure
Procedure.l createToneEx(*address,volumeAtStart.d,volumeAtEnd.d,duration.d,frequencyAtStart.d,frequencyAtEnd.d=0,noiseAtStart.d=0,noiseAtEnd.d=0,backToStart=0) ;returns needed size if address=0
;creates a triangle wave with added effects
Protected sample.l, result, pos, dir, waveY.d, maxValue, centerValue
Protected samplesPerHalfWave.d, waveIncrementValue.d, size, samplesSinceHalfWave, waveTop.d
Protected frequency.d, frequencyIncrementValue.d, noise.d, noiseIncrementValue.d, lastNoise.d, halfSize, loop, loops
Protected waveYpos.d, volumeIncrementValue.d, volume.d
volume = volumeAtStart
frequency = frequencyAtStart
noise = noiseAtStart
size = (format\nSamplesPerSec / 1000*duration) * format\wBitsPerSample / 8
If *address = 0 ;return size needed
If backToStart
ProcedureReturn size * 2
Else
ProcedureReturn size
EndIf
EndIf
If format\wBitsPerSample = 8
maxValue = 255
ElseIf format\wBitsPerSample = 16
maxValue = 65535
EndIf
centerValue = (maxValue/2)
waveTop = centerValue
samplesPerHalfWave = (format\nSamplesPerSec / frequency) / 2
waveIncrementValue = waveTop / samplesPerHalfWave
volumeIncrementValue = (volumeAtEnd - volumeAtStart) / size * format\wBitsPerSample/8
frequencyIncrementValue = (frequencyAtEnd - frequencyAtStart) / size * format\wBitsPerSample/8
noiseIncrementValue = (noiseAtEnd - noiseAtStart) / size * format\wBitsPerSample/8
If backToStart: loops=1: EndIf
For loop=0 To loops
For pos=pos To size
If volumeAtEnd
volume + volumeIncrementValue
EndIf
If frequencyAtEnd
frequency + frequencyIncrementValue
samplesPerHalfWave = (format\nSamplesPerSec / frequency) / 2
waveIncrementValue = waveTop / samplesPerHalfWave
EndIf
If noiseAtEnd
noise + noiseIncrementValue
EndIf
If dir=0
waveYpos + waveIncrementValue
Else
waveYpos - waveIncrementValue
EndIf
samplesSinceHalfWave + 1
If samplesSinceHalfWave >= samplesPerHalfWave
If dir=0
waveYpos = waveTop
Else
waveYpos = -waveTop
EndIf
dir = dir!1 ;switch it
samplesSinceHalfWave = 0
EndIf
waveY = waveYpos
;waveY = waveY*16
;waveY + Random(90000)
If noiseAtStart
If lastNoise >= noise; Random(noise) = 0
Select 0
Case 0: waveYpos = Random(maxValue)-centerValue
Case 1: waveY = Random(maxValue)-centerValue
;Case 2: waveYpos = Random(maxValue) ; waveY + Random(90000)
EndSelect
lastNoise = 1
Else
lastNoise+1
EndIf
EndIf
;adjust volume
waveY = volume/1000 * waveY
If format\wBitsPerSample = 8
sample = centerValue + waveY
PokeA(*address+pos,sample)
ElseIf format\wBitsPerSample = 16
sample = waveY
PokeW(*address+pos,sample)
pos+1
EndIf
Next
If backToStart ;morph back
If volumeAtEnd
volume = volumeAtEnd
EndIf
If frequencyAtEnd
frequency = frequencyAtEnd
EndIf
If noiseAtEnd
noise = noiseAtEnd
EndIf
volumeIncrementValue = -volumeIncrementValue
frequencyIncrementValue = -frequencyIncrementValue
noiseIncrementValue = -noiseIncrementValue
size+size
EndIf
Next
EndProcedure
Procedure addWaveHeader(*address,dataSize,channels,samplesPerSec,blockAlign,bitsPerSample)
; RIFF Chunk
PokeL(*address+ 0, 'FFIR')
PokeL(*address+ 4, dataSize + 36)
PokeL(*address+ 8, 'EVAW')
; FORMAT Chunk
PokeL(*address+ 12, ' tmf')
PokeL(*address+ 16, $0010)
PokeW(*address+ 20, $01)
PokeW(*address+ 22, channels)
PokeL(*address+ 24, samplesPerSec)
PokeL(*address+ 28, samplesPerSec * channels * (bitsPerSample / 8) )
PokeW(*address+ 32, blockAlign)
PokeW(*address+ 34, bitsPerSample)
; DATA Chunk
PokeL(*address+ 36, 'atad')
PokeL(*address+ 40, dataSize)
EndProcedure
Procedure catchTone(volume,duration,frequency)
Protected *memory, id, size
size = createTone(0,volume,duration,frequency) ;get size
*memory = AllocateMemory(44+size)
createTone(*memory+44,volume,duration,frequency) ;write wave data to memory
addWaveHeader(*memory,size,format\nChannels,format\nSamplesPerSec,format\nBlockAlign,format\wBitsPerSample)
id = CatchSound(#PB_Any,*memory)
FreeMemory(*memory)
ProcedureReturn id
EndProcedure
Procedure catchToneEx(volumeAtStart,volumeAtEnd,duration,frequencyAtStart,frequencyAtEnd=0,noiseAtStart=0,noiseAtEnd=0,backToStart=0)
Protected *memory, id, size
size = createToneEx(0,volumeAtStart,volumeAtEnd,duration,frequencyAtStart,frequencyAtEnd,noiseAtStart,noiseAtEnd,backToStart) ;get size
*memory = AllocateMemory(44+size)
createToneEx(*memory+44,volumeAtStart,volumeAtEnd,duration,frequencyAtStart,frequencyAtEnd,noiseAtStart,noiseAtEnd,backToStart) ;write wave data to memory
addWaveHeader(*memory,size,format\nChannels,format\nSamplesPerSec,format\nBlockAlign,format\wBitsPerSample)
id = CatchSound(#PB_Any,*memory)
FreeMemory(*memory)
ProcedureReturn id
EndProcedure
InitSound()
setWaveFormat(16,44100)
Define snd_explosion, snd_laser, snd_shoot, snd_pacman, snd_flying
snd_explosion = catchToneEx(1000,100, 1000, 50,20, 20,100)
snd_laser = catchToneEx(1000,100, 300, 1500,200)
snd_shoot = catchToneEx(1000,1, 500, 200,10, 1,10)
snd_pacman = catchToneEx(500,0, 100, 400,200, 0,0, #True)
snd_flying = catchToneEx(500,0, 200, 10,0, 5,10, #True)
PlaySound(catchToneEx(1000,100, 2000, 200,40, 0,0)):Delay(2000)
PlaySound(catchToneEx(1000,100, 2000, 10,200, 100,1 )):Delay(2000)
PlaySound(snd_flying): Delay(400)
PlaySound(snd_flying): Delay(400)
PlaySound(snd_flying): Delay(400)
PlaySound(snd_flying): Delay(400)
PlaySound(snd_pacman): Delay(200)
PlaySound(snd_pacman): Delay(200)
PlaySound(snd_explosion): Delay(1000)
PlaySound(snd_laser): Delay(300)
PlaySound(snd_laser): Delay(300)
PlaySound(snd_shoot): Delay(1000)
PlaySound(catchToneEx(1000,100, 2000, 200,10, 1,100 )):Delay(2000)
;Have fun! ;-)
I and many others have always wanted to know more about how to do this, so when I found some nice Windows API functions by random which I could use (why are they so hard to find?) I just had to make a demo and show you guys.
It basically lets you define the analog signal sent to the speakers, more about that here:
http://en.wikipedia.org/wiki/Pulse-code_modulation
The API documentation:
http://msdn.microsoft.com/en-us/library ... 10%29.aspx
Another example (not mine) but for recording:
http://www.purebasic.fr/english/viewtop ... =12&t=7009
I also trimmed that example down if anyone wants it just say so. I plan making a voice recognition software actually.
I should probably explain some more, but.. we have Google, etc.
EDIT:
Would anyone want to have an old-school sound effects contest (recreating Commodore/Atari sounds, etc) ?
First entry in contest (me yes):
http://www.purebasic.fr/english/viewtop ... 23#p321423
Code: Select all
EnableExplicit
Structure tone
frequency.l
duration.l
EndStructure
Global hWo, outHdr.WAVEHDR, result
Global format.WAVEFORMATEX ;wave format info
Global outBufferLength = 1000000 ;1mb
Global NewList tone.tone()
Define i
Procedure.l randomEx(min,max)
ProcedureReturn min+Random(max-min)
EndProcedure
Procedure.l minMax(value,min,max)
If value > max
ProcedureReturn max
ElseIf value < min
ProcedureReturn min
Else
ProcedureReturn value
EndIf
EndProcedure
Procedure.s waveOutError(result)
Protected error$ = Space(1000)
waveOutGetErrorText_(result,@error$,1000)
ProcedureReturn error$
EndProcedure
Procedure addTone(frequency,duration)
AddElement(tone())
tone()\frequency = frequency
tone()\duration = duration
EndProcedure
Procedure playTone(*outHdr.WAVEHDR,volume.d,frequency.d,length) ;volum 0-1000
Protected sample.l, result
Protected pos, dir, waveY.d = 0, maxValue, vol.d, centerValue
Protected samplesPerHalfWave.d, waveIncrementValue.d
samplesPerHalfWave = format\nSamplesPerSec / frequency
length = (format\nSamplesPerSec / 1000*length) * format\wBitsPerSample / 8
If format\wBitsPerSample = 8
maxValue = 255
ElseIf format\wBitsPerSample = 16
maxValue = 65535
EndIf
centerValue = (maxValue/2)+1
vol = volume/1000 * centerValue
waveIncrementValue = vol / samplesPerHalfWave
If length <= outBufferLength
For pos=0 To length
If dir = 1
If waveY < vol
waveY + waveIncrementValue
If waveY >= vol
dir = 0
EndIf
EndIf
Else
If waveY > -vol
waveY - waveIncrementValue
If waveY <= -vol
dir = 1
EndIf
EndIf
EndIf
;Debug sample
If format\wBitsPerSample = 8
sample = minMax(centerValue + waveY,0,maxValue)
PokeA(*outHdr\lpData+pos,sample)
ElseIf format\wBitsPerSample = 16
sample = waveY
PokeW(*outHdr\lpData+pos,sample)
pos+1
EndIf
Next
*outHdr\dwBufferLength = length ;tell how much we wrote
;send to output buffer
result = waveOutWrite_(hWo,@OutHdr,SizeOf(WAVEHDR))
If result <> #MMSYSERR_NOERROR
Debug waveOutError(result)
EndIf
Else
Debug "error, length too big"
Debug length
Debug *outHdr\dwBufferLength
EndIf
EndProcedure
Procedure listOutDevices() ;run to enumerate devices if needed
Protected cap.WAVEOUTCAPS, devices, deviceId
devices = waveOutGetNumDevs_()
If devices
For deviceId=#WAVE_MAPPER To devices-1
If waveOutGetDevCaps_(deviceId,@cap,SizeOf(WAVEOUTCAPS)) = #MMSYSERR_NOERROR
Debug deviceId
Debug PeekS(@cap\szPname) ;max 32 btw
EndIf
Next
EndIf
EndProcedure
Procedure waCallback(hWi.l, uMsg.l, dwInstance.l, dwParam1.l, dwParam2.l)
Protected *outHdr.WAVEHDR, byte.a, pos, sampleSize=1, x
Select uMsg
Case #WOM_OPEN
Debug "open"
Case #WOM_CLOSE
Debug "close"
Case #WOM_DONE
;Debug "done"
*outHdr.WAVEHDR = dwParam1
If NextElement(tone())
playTone(@outHdr,500,tone()\frequency,tone()\duration)
Else
Debug "done playing"
EndIf
EndSelect
EndProcedure
format\wFormatTag = #WAVE_FORMAT_PCM
format\nChannels = 1 ;mono
format\wBitsPerSample = 16 ;8/16
format\nSamplesPerSec = 44100 ;8000 Hz, 11025 Hz, 22050 Hz, and 44100 Hz
format\nBlockAlign = (format\nChannels * format\wBitsPerSample) / 8 ;equal to the product of nChannels and wBitsPerSample divided by 8 (bits per byte).
format\nAvgBytesPerSec = format\nSamplesPerSec * format\nBlockAlign ;equal to the product of nSamplesPerSec and nBlockAlign
result = waveOutOpen_(@hWo,#WAVE_MAPPER+1,@format,@waCallback(),0,#CALLBACK_FUNCTION|#WAVE_MAPPED|#WAVE_FORMAT_DIRECT)
If result <> #MMSYSERR_NOERROR
Debug waveOutError(result)
EndIf
outHdr\dwBufferLength = outBufferLength
outHdr\lpData = AllocateMemory(outHdr\dwBufferLength)
result = waveOutPrepareHeader_(hWo,@outHdr,SizeOf(WAVEHDR))
If result <> #MMSYSERR_NOERROR
Debug waveOutError(result)
EndIf
For i=0 To 100
addTone(randomEx(400,4000),randomEx(80,500))
Next
FirstElement(tone())
playTone(@outHdr,500,tone()\frequency,tone()\duration)
OpenWindow(0,0,0,180,50,"Sound demo",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
result = waveOutReset_(hWo) ;stop playback
If result <> #MMSYSERR_NOERROR
Debug waveOutError(result)
EndIf
result = waveOutClose_(hWo)
If result <> #MMSYSERR_NOERROR
Debug waveOutError(result)
EndIf