It is currently Sat May 18, 2013 9:47 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 12 posts ] 
Author Message
 Post subject: 24-Bit WAV Files
PostPosted: Sun Feb 12, 2012 3:38 am 
Offline
Enthusiast
Enthusiast

Joined: Mon Oct 24, 2005 1:05 pm
Posts: 386
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.

Code:
;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


Top
 Profile  
 
 Post subject: Re: 24-Bit WAV Files
PostPosted: Mon Mar 05, 2012 8:48 pm 
Offline
Enthusiast
Enthusiast

Joined: Mon Oct 24, 2005 1:05 pm
Posts: 386
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.


Top
 Profile  
 
 Post subject: Re: 24-Bit WAV Files
PostPosted: Tue Mar 06, 2012 6:14 am 
Offline
Addict
Addict

Joined: Sun Aug 08, 2004 5:21 am
Posts: 1084
Location: Netherlands
chris319 wrote:
from an address which is one byte lower

You have to be careful with this approach since you start reading from an address outside of the memory you have allocated.


Top
 Profile  
 
 Post subject: Re: 24-Bit WAV Files
PostPosted: Tue Mar 06, 2012 5:25 pm 
Offline
Addict
Addict
User avatar

Joined: Sat Aug 15, 2009 6:59 pm
Posts: 1024
Would be even faster if you dont use PeekL and use a pointer instead.


Top
 Profile  
 
 Post subject: Re: 24-Bit WAV Files
PostPosted: Tue Mar 06, 2012 10:18 pm 
Offline
Enthusiast
Enthusiast

Joined: Mon Oct 24, 2005 1:05 pm
Posts: 386
wilbert wrote:
chris319 wrote:
from an address which is one byte lower

You have to be careful with this approach since you start reading from an address outside of the memory you have allocated.

Why would that be a problem? The bits from that address will be shifted off anyway.


Top
 Profile  
 
 Post subject: Re: 24-Bit WAV Files
PostPosted: Tue Mar 06, 2012 10:30 pm 
Offline
Addict
Addict
User avatar

Joined: Sat Aug 15, 2009 6:59 pm
Posts: 1024
chris319 wrote:
wilbert wrote:
chris319 wrote:
from an address which is one byte lower

You have to be careful with this approach since you start reading from an address outside of the memory you have allocated.

Why would that be a problem? The bits from that address will be shifted off anyway.

Because it could be that this memory address is not allocated at all and that would result in a invalid memory access = crash.


Top
 Profile  
 
 Post subject: Re: 24-Bit WAV Files
PostPosted: Wed Mar 07, 2012 12:48 am 
Offline
Enthusiast
Enthusiast

Joined: Mon Oct 24, 2005 1:05 pm
Posts: 386
Thorium wrote:
chris319 wrote:
wilbert wrote:
chris319 wrote:
from an address which is one byte lower

You have to be careful with this approach since you start reading from an address outside of the memory you have allocated.

Why would that be a problem? The bits from that address will be shifted off anyway.

Because it could be that this memory address is not allocated at all and that would result in a invalid memory access = crash.

Then I will allocate it.

What is the trick for reading this buffer with pointers? I'm fooling around with pointers and structures with no success.


Top
 Profile  
 
 Post subject: Re: 24-Bit WAV Files
PostPosted: Wed Mar 07, 2012 12:55 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jul 25, 2005 3:51 pm
Posts: 2393
Location: Utah, USA
chris319 wrote:
What is the trick for reading this buffer with pointers? I'm fooling around with pointers and structures with no success.


Try something like this:
Code:
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

_________________
Image


Top
 Profile  
 
 Post subject: Re: 24-Bit WAV Files
PostPosted: Wed Mar 07, 2012 1:19 am 
Offline
Enthusiast
Enthusiast

Joined: Mon Oct 24, 2005 1:05 pm
Posts: 386
The pointer works as suggested, thanks.

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.


Top
 Profile  
 
 Post subject: Re: 24-Bit WAV Files
PostPosted: Wed Mar 07, 2012 6:30 am 
Offline
Enthusiast
Enthusiast

Joined: Mon Oct 24, 2005 1:05 pm
Posts: 386
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.

Code:
;24-bit test.pb
;CHECKS INTEGRITY OF 24-BIT READING AND WRITING ROUTINES
;UPDATED ON 3/6/12

#FRAMES_PER_BUFFER = 3

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

Repeat ;GO PAST HEADER
  seek$ = LCase(Right(seek$ + Chr(ReadByte(1)), 4))
Until seek$ = "data"

subChunk2Size = ReadLong(1) ;SIZE OF AUDIO DATA IN BYTES

;THIS TRICK IS TO PREVENT ACCESSING UNALLOCATED MEMORY
*bufferPtr = AllocateMemory(subChunk2Size + 4)
*soundBuffer = *bufferPtr + 4

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.Long = *soundBuffer - 1 ;USE FOR METHOD #1
;*offset.l = *soundBuffer - 1 ;USE FOR METHOD #1
;*offset = *soundBuffer ;USE FOR METHOD #2

Repeat
;METHOD #1
;  sample.l = PeekL(*offset) >> 8
  sample.l = *offset\l >> 8

;METHOD #2
;  lsb.u = PeekU(*offset) ;USE PUREBASIC TYPE "UNICODE"
;  msb.b = PeekB(*offset + 2)
;  sample = msb << 16 | lsb

  WriteData(1, @sample, #FRAMES_PER_BUFFER)
  *offset + #FRAMES_PER_BUFFER
Until *offset >= bufferEnd

Next

PrintN(Str(ElapsedMilliseconds() - now))

CloseFile(1)
FreeMemory(*bufferPtr)
PrintN(Chr(10) + "Press any key to exit.")

While Inkey() = "": Wend
End


Top
 Profile  
 
 Post subject: Re: 24-Bit WAV Files
PostPosted: Wed Mar 07, 2012 7:13 am 
Offline
Addict
Addict

Joined: Sun Aug 08, 2004 5:21 am
Posts: 1084
Location: Netherlands
You could write your own Peek function (based on your method 2) that doesn't reed outside of the specified memory address like this
Code:
Procedure Peek24(*MemoryBuffer)
  EnableASM
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    MOV edx, *MemoryBuffer
    MOVSX eax, byte [edx + 2]
    SAL eax, 16
    MOV ax, word [edx]
  CompilerElse
    MOV rdx, *MemoryBuffer
    MOVSX rax, byte [rdx + 2]
    SAL rax, 16
    MOV ax, word [rdx]
  CompilerEndIf
  DisableASM
  ProcedureReturn
EndProcedure
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.


Top
 Profile  
 
 Post subject: Re: 24-Bit WAV Files
PostPosted: Wed Mar 07, 2012 7:26 am 
Offline
Enthusiast
Enthusiast

Joined: Mon Oct 24, 2005 1:05 pm
Posts: 386
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().


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 12 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye