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


