I think it's pretty simple to use and understand. It's not perfect and optimized but works nice ^^
It comes from 2002 and originally created by Axel Brink, I've remembered it from my earlier experiments with sounds and ported to PB.
8-bit and 16-bit are supported, multichannel sound too (didn't tested yet) and wide range of sample rate.
The test_append_freq() function here is for example only, you decide what and how you will append as sound samples.
The same with PlayWave().
Code: Select all
;Reads sample data and returns an array with bytes which can be
;written to a .wav file
;Uses PCM (i.e. no compression)
;Source, VB5/VB6: By Axel Brink (axel@fmf.nl), 21-May-2002
;Ported to Purebasic by Lunasole, 2016
;WaveData: * Contents: Sample values of type Long
; * Structure: An array(channel, samplenumber)
; * Channels: The number of elements in the first dimension
; determines the number of channels
; * Samples: The number of elements in the second dimension
; determines the number of samples
; 8-bit samples must be between 0 and 255
; 16-bit samples must be between -32768 and 32767
; Note that indexing is supposed to start at 0.
;SampleRate: Number of samples per second. Typically 44100.
;BitsPerSample: Number of bits per sample. 8 or 16.
;RETURN: 0 on error, output array size else
Procedure CreateWaveArray(Array OutputFile.a (1), Array WaveData.l (2), SampleRate, BitsPerSample)
Protected.l Byte1Mask = 255 ;Long integers are stored little endian.
Protected.l Byte2Mask = 65280 ;These bit masks select bytes from a number to store.
Protected.l Byte3Mask = 16711680
Protected.l Byte2Divisor = 256
Protected.l Byte3Divisor = 65536
Protected.l Byte4Divisor = 16777216
Protected.l NumSamples, NumChannels, FileSize, ByteRate
Protected.l BlockAlign ;## "The number of bytes for one sample including all channels.
; I wonder what happens when this number isn't an integer?"
Protected.l ChunkSize ;## "36 + SubChunk2Size, or more precisely:
; 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
; This is the size of the rest of the chunk
; following this number. This is the size of the
; entire file in bytes minus 8 bytes for the
; two fields not included in this count:
; ChunkID and ChunkSize."
Protected.l Subchunk2Size ;## "== NumSamples * NumChannels * BitsPerSample/8
; This is the number of bytes in the data.
; You can also think of this as the size
; of the read of the subchunk following this
; number."
Protected.l SampleNr, ChannelNr
If BitsPerSample <> 8 And BitsPerSample <> 16
ProcedureReturn 0
EndIf
NumChannels = ArraySize(WaveData(), 1) + 1
NumSamples = ArraySize(WaveData(), 2) + 1
ByteRate = SampleRate * NumChannels * BitsPerSample / 8
BlockAlign = NumChannels * BitsPerSample / 8
FileSize = 44 + NumSamples * BlockAlign
ChunkSize = FileSize - 8
Subchunk2Size = NumSamples * NumChannels * BitsPerSample / 8
ReDim OutputFile.a (FileSize - 1)
;** ChunkID **
OutputFile(0) = Asc("R")
OutputFile(1) = Asc("I")
OutputFile(2) = Asc("F")
OutputFile(3) = Asc("F")
;** ChunkSize **
OutputFile(4) = ChunkSize & Byte1Mask ;Little endian
OutputFile(5) = (ChunkSize & Byte2Mask) / Byte2Divisor
OutputFile(6) = (ChunkSize & Byte3Mask) / Byte3Divisor
OutputFile(7) = ChunkSize / Byte4Divisor
;** Format **
OutputFile(8) = Asc("W")
OutputFile(9) = Asc("A")
OutputFile(10) = Asc("V")
OutputFile(11) = Asc("E")
;** Subchunk1ID **
OutputFile(12) = Asc("f")
OutputFile(13) = Asc("m")
OutputFile(14) = Asc("t")
OutputFile(15) = Asc(" ")
;** Subchunk1Size ** (16 for PCM)
OutputFile(16) = 16 & Byte1Mask
OutputFile(17) = (16 & Byte2Mask) / Byte2Divisor
OutputFile(18) = (16 & Byte3Mask) / Byte3Divisor
OutputFile(19) = 16 / Byte4Divisor
;** AudioFormat ** (1 for PCM)
OutputFile(20) = 1 & Byte1Mask
OutputFile(21) = (1 & Byte2Mask) / Byte2Divisor
;** NumChannels **
OutputFile(22) = NumChannels & Byte1Mask
OutputFile(23) = (NumChannels & Byte2Mask) / Byte2Divisor
;** SampleRate **
OutputFile(24) = SampleRate & Byte1Mask
OutputFile(25) = (SampleRate & Byte2Mask) / Byte2Divisor
OutputFile(26) = (SampleRate & Byte3Mask) / Byte3Divisor
OutputFile(27) = SampleRate / Byte4Divisor
;** ByteRate **
OutputFile(28) = ByteRate & Byte1Mask
OutputFile(29) = (ByteRate & Byte2Mask) / Byte2Divisor
OutputFile(30) = (ByteRate & Byte3Mask) / Byte3Divisor
OutputFile(31) = ByteRate / Byte4Divisor
;** BlockAlign **
OutputFile(32) = BlockAlign & Byte1Mask
OutputFile(33) = (BlockAlign & Byte2Mask) / Byte2Divisor
;** BitsPerSample **
OutputFile(34) = BitsPerSample & Byte1Mask
OutputFile(35) = (BitsPerSample & Byte2Mask) / Byte2Divisor
;** Subchunk2ID **
OutputFile(36) = Asc("d")
OutputFile(37) = Asc("a")
OutputFile(38) = Asc("t")
OutputFile(39) = Asc("a")
;** Subchunk2Size **
OutputFile(40) = Subchunk2Size & Byte1Mask
OutputFile(41) = (Subchunk2Size & Byte2Mask) / Byte2Divisor
OutputFile(42) = (Subchunk2Size & Byte3Mask) / Byte3Divisor
OutputFile(43) = Subchunk2Size / Byte4Divisor
If BitsPerSample = 8 ;Samples are unsigned bytes; from 0 to 255
For SampleNr = 0 To NumSamples - 1
For ChannelNr = 0 To NumChannels - 1
OutputFile(44 + BlockAlign * SampleNr + ChannelNr) = WaveData(ChannelNr, SampleNr) & Byte1Mask
Next ChannelNr
Next SampleNr
ElseIf BitsPerSample = 16 ;Samples are 2;s complement signed bytes; from -32768 to 32767
For SampleNr = 0 To NumSamples - 1
For ChannelNr = 0 To NumChannels - 1
OutputFile(44 + BlockAlign * SampleNr + ChannelNr * BitsPerSample / 8) = WaveData(ChannelNr, SampleNr) & Byte1Mask
OutputFile(45 + BlockAlign * SampleNr + ChannelNr * BitsPerSample / 8) = (WaveData(ChannelNr, SampleNr) & Byte2Mask) / Byte2Divisor
Next ChannelNr
Next SampleNr
EndIf
ProcedureReturn ArraySize(OutputFile())
EndProcedure
; additional functions
Procedure test_append_freq(Array Frames.l(2), VOL, FREQ, TIME, QUALITY, MODE)
Protected WMax, i, a = ArraySize(Frames(), 2), i_max = TIME * (QUALITY * 0.001)
If MODE = 8
WMax = 255 * VOL
ElseIf MODE = 16
WMax = 32765 * VOL
EndIf
ReDim Frames(0, a + i_max)
For i = 0 To i_max
Frames(0, i + a) = (WMax * Sin(#PI * 2 * FREQ * i / QUALITY))
Next i
EndProcedure
Procedure PlayWave(Array Frames.l(2), QUALITY, MODE)
;following will dump wav to a file
Protected Dim OutF.a (0)
CreateWaveArray(OutF(), Frames(), QUALITY, MODE)
Define x = CatchSound(#PB_Any, @OutF(0))
If IsSound(x)
; dump to file
; CreateFile(2, "out.wav")
; WriteData(2, OutF(), ArraySize(OutF()))
; CloseFile(2)
PlaySound(x)
Delay(SoundLength(x, #PB_Sound_Millisecond) + 256)
FreeSound(x)
EndIf
EndProcedure
;;;;;;;;;;;;;;;;;
; the first dimension of array is number of channels (0 means 1 channel or 'mono')
; the second contains sound samples for every channel
Dim WaveFrames.l (0, 0)
Define Q = 44100 ; 44100 ; 22050 ; 11025 ; 5512
Define M = 8 ; 8 or 16 bits
Define T = 2 * 1000 ; time in ms
Define F ; frequency
InitSound()
; add some sounds
For F = 0 To 20000 Step 500
test_append_freq (WaveFrames(), 1.0, F, 90, Q, M)
Next F
; pack samples to wave file and play it (or save to disk)
PlayWave(WaveFrames(), Q, M)