Here is a little utility for checking the read and write integrity of monaural 24-bit wav files under PureBasic.
You can use any monaural 24-bit wav file which has the old-fashioned wav header as the input file. I created a sine-wave file as my input file. This made it easy to verify that the output file was an exact replica of the data contained in the input file. The output file is a raw sound file with the extension .snd. There is no wav file header. I leave it to the reader to add one if he wants, or to add multichannel support. I use the program Goldwave to read the .snd file. The program reads the PCM data from the file into a buffer and then reads the data from the buffer and writes it back into the .snd file. It also reports the time this takes. You can also do multiple iterations for extended time testing by enabling the For/Next loop which is currently commented out. There are some hard-coded values for reading the wav file contents which is not the best way to do it; that's another project for the reader.
;24-bit test.pb
;CHECKS INTEGRITY OF 24-BIT READING AND WRITING ROUTINES
;UPDATED ON 3/5/12
OpenConsole()
Global format.WAVEFORMATEX: Global offset.l, sample.l
Global Dim sampleData.d(1)
filename$ = ".\24 bit test.wav"
result = ReadFile(1, filename$)
If result = 0
MessageRequester("Error", "Unable to open file " + fileName$)
End
EndIf
FileSeek(1, 24)
sampleRate = ReadLong(1)
FileSeek(1, 34)
format\wBitsPerSample = ReadWord(1)
If format\wBitsPerSample <> 24
MessageRequester("Error", "Incorrect bit depth. Must be 24 bits.")
End
EndIf
format\nBlockAlign = format\wBitsPerSample / 8
Repeat ;GO PAST HEADER
seek$ = LCase(Right(seek$ + Chr(ReadByte(1)), 4))
Until seek$ = "data"
subChunk2Size = ReadLong(1) ;SIZE OF AUDIO DATA IN BYTES
*soundBuffer = AllocateMemory(subChunk2Size)
bytes_read = ReadData(1, *soundbuffer, subChunk2Size)
CloseFile(1)
CreateFile(1, ".\24 bit output.snd")
bufferEnd = (*soundBuffer - 0) + subChunk2Size
now = ElapsedMilliseconds()
For ct2 = 1 To 10
;THERE ARE TWO DIFFERENT WAYS TO READ 24-BIT SAMPLES FROM THE BUFFER. METHOD #1 IS FASTER.
*offset = *soundBuffer - 1 ;USE FOR METHOD #1
;*offset = *soundBuffer ;USE FOR METHOD #2
Repeat
;METHOD #1
sample.l = PeekL(*offset) >> 8
;METHOD #2
; lsb.u = PeekU(*offset) ;USE UNSIGNED PUREBASIC TYPE "UNICODE"
; msb.b = PeekB(*offset + 2) ;THIS BYTE MUST BE SIGNED
; sample = msb << 16 | lsb
WriteData(1, @sample, 3)
*offset + format\nBlockAlign
Until *offset >= bufferEnd
Next
PrintN(Str(ElapsedMilliseconds() - now))
CloseFile(1)
FreeMemory(*soundbuffer)
PrintN(Chr(10) + "Press any key to exit.")
While Inkey() = "": Wend
This code has been updated based on subsequent testing and research.
The fastest way to read a 24-bit sound sample from a buffer is to read a long (32-bit) variable from an address which is one byte lower than the address of the desired sample, then to shift the long variable to the right by eight bits. Reading a sample in this way places it in the 24 most significant bits of the variable. The right shift moves the sample to the 24 least significant bits.
For ct2 = 1 To 10
;THERE ARE TWO DIFFERENT WAYS TO READ 24-BIT SAMPLES FROM THE BUFFER. METHOD #1 IS FASTER.
*offset.Long = *soundBuffer - 1 ;USE FOR METHOD #1
Repeat
;METHOD #1
sample.l = *offset\l >> 8
WriteData(1, @sample, 3)
*offset + format\nBlockAlign
Until *offset >= bufferEnd
Next
Eliminating the disk writes, 100 iterations with pointers took 31.734 seconds. 100 iterations with PeekL() took 30.922 seconds. PeekL() is faster but by a rather small amount.
It's a toss-up whether the pointer is faster than PeekL().
The example below allocates four more bytes for the sound buffer than needed. It then establishes a pointer for the sound buffer which is four bytes into the previously-allocated memory. The original pointer is used to deallocate the memory.
but your pointer approach probably will be faster.
What should be faster is to process more samples in a loop and write them to a file at once instead of one sample a time.
The purpose of this exercise is to demonstrate that a sine wave could be read from a file into a buffer and that the samples could be written back to a different file and still be recongnizable as a sine wave. In practice one would write the entire buffer to disk using WriteData().