Page 1 of 1

Get WAV Attributes (length, rate, ...)

Posted: Tue May 03, 2011 5:24 am
by kenmo
I'm surprised that the native PB sound library doesn't have any functions for analyzing a loaded sound...

So for my own purposes, I pulled together bits and pieces of code, and wrote:

CatchWavAttribute(*WAV.i, Attribute.i) and
LoadWavAttribute(File.s, Attribute.i)

(in the style of CatchSound and LoadSound). All constants and return values are described in the code.

Note: this currently works ONLY with files of the most basic PCM format. (No FLAC, OGG, etc.)

Code: Select all

; +-------------------+-------+
; | WavAttributes.pbi | kenmo |
; +-------------------+-------+
; |  5.02.2011 - Version 1.0
; |  6.01.     - Version 1.1 (removed a debug, fixed Samples attribute)

;-
;- WAV Constants

;   _Attribute      _Value   _Description            _Units
#WAV_BitDepth     =  $00    ; Sample depth *          bits  / sample
#WAV_BitRate      =  $01    ; Total data rate         bits  / second
#WAV_ByteDepth    =  $02    ; Sample depth *          bytes / sample
#WAV_ByteRate     =  $03    ; Total data rate         bytes / second
#WAV_Channels     =  $04    ; Number of channels      channels
#WAV_ChunkSize    =  $05    ; Total audio data size   bytes
#WAV_Format       =  $06    ; File format             PCM or Unknown
#WAV_Milliseconds =  $07    ; Total audio length      milliseconds
#WAV_SampleRate   =  $08    ; Sample rate *           samples / second
#WAV_Samples      =  $09    ; Number of samples *     samples
#WAV_Seconds      =  $0A    ; Total audio length      seconds
;                           * refers to one channel only

; Return Values
#WAV_PCM      =  0
#WAV_Unknown  = -1
#WAV_NoFile   = -2
#WAV_ReadFail = -3
#WAV_NoBuffer = -4

;-
;- WAV Procedures

Procedure.i CatchWavAttribute(*WAV.i, Attribute.i)
  Protected Value.i = #WAV_Unknown
  
  If (*Wav)
    If ((PeekL(*Wav) = 'FFIR') And (PeekL(*Wav + 12) = ' tmf') And (PeekL(*Wav + 36) = 'atad'))
      Select (Attribute)
        Case #WAV_BitDepth
          Value = PeekU(*WAV + 34)
        Case #WAV_BitRate
          Value = PeekL(*WAV + 28)<<3
        Case #WAV_ByteDepth
          Value = PeekU(*WAV + 34)>>3
        Case #WAV_ByteRate
          Value = PeekL(*WAV + 28)
        Case #WAV_Channels
          Value = PeekU(*WAV + 22)
        Case #WAV_ChunkSize
          Value = PeekL(*WAV + 40)
        Case #WAV_Format
          Value = #WAV_PCM
        Case #WAV_Milliseconds
          Value = PeekL(*WAV + 40) / (PeekL(*Wav + 28) / 100) * 10
        Case #WAV_Samples
          Value = PeekL(*WAV + 40) / PeekU(*Wav + 32)
        Case #WAV_SampleRate
          Value = PeekL(*WAV + 24)
        Case #WAV_Seconds
          Value = PeekL(*WAV + 40) / PeekL(*Wav + 28)
      EndSelect
    EndIf
  EndIf
  
  ProcedureReturn (Value)
EndProcedure

Procedure.i LoadWavAttribute(File.s, Attribute.i)
  Protected Value.i   = #WAV_NoFile
  Protected FID.i     = #Null
  Protected *Buffer.i = #Null
  
  If (File)
    FID = ReadFile(#PB_Any, File)
    If (FID)
      *Buffer = AllocateMemory(44)
      If (*Buffer)
        If (ReadData(FID, *Buffer, 44) = 44)
          Value = CatchWavAttribute(*Buffer, Attribute)
        Else
          Value = #WAV_ReadFail
        EndIf
        FreeMemory(*Buffer)
      Else
        Value = #WAV_NoBuffer
      EndIf
      CloseFile(FID)
    Else
      Value = #WAV_ReadFail
    EndIf
  EndIf
  
  ProcedureReturn (Value)
EndProcedure

;-
;  EOF
and an example:

Code: Select all

XIncludeFile "WavAttributes.pbi"

File.s = OpenFileRequester("Open WAV", "", "WAV Files|*.wav|All Files|*.*", 0)

If (File)

  Debug "File: " + GetFilePart(File)
  Debug ""
  Format.i = LoadWavAttribute(File, #WAV_Format)
  If (Format = #WAV_Unknown)
    Debug "WAV_Format: Unknown"
  ElseIf (Format < 0)
    Debug "Load Error: " + Str(Format)
  Else
    Select (Format)
      Case #WAV_PCM
        Debug "WAV_Format: PCM"
    EndSelect
    Debug "WAV_SampleRate: "   + Str(LoadWavAttribute(File, #WAV_SampleRate  ))
    Debug "WAV_BitDepth: "     + Str(LoadWavAttribute(File, #WAV_BitDepth    ))
    Debug "WAV_ByteDepth: "    + Str(LoadWavAttribute(File, #WAV_ByteDepth   ))
    Debug "WAV_Channels: "     + Str(LoadWavAttribute(File, #WAV_Channels    ))
    Debug "WAV_BitRate: "      + Str(LoadWavAttribute(File, #WAV_BitRate     ))
    Debug "WAV_ByteRate: "     + Str(LoadWavAttribute(File, #WAV_ByteRate    ))
    Debug "WAV_ChunkSize: "    + Str(LoadWavAttribute(File, #WAV_ChunkSize   ))
    Debug "WAV_Samples: "      + Str(LoadWavAttribute(File, #WAV_Samples     ))
    Debug "WAV_Milliseconds: " + Str(LoadWavAttribute(File, #WAV_Milliseconds))
    Debug "WAV_Seconds: "      + Str(LoadWavAttribute(File, #WAV_Seconds     ))
  EndIf
EndIf

Should come in handy once in a while!

* edit: got rid of a Debug that shouldn't have been left in the procedure

* edit June 1, 2011: (fixed Total # Samples bug)

Re: Get WAV Attributes (length, rate, ...)

Posted: Tue May 03, 2011 8:47 am
by c4s
kenmo wrote:Should come in handy once in a while!
Indeed!
Thank you.

Re: Get WAV Attributes (length, rate, ...)

Posted: Thu Jun 02, 2011 2:04 am
by kenmo
Fixed a (important) bug (in calculating the total number of samples) in case anyone is using this!

Re: Get WAV Attributes (length, rate, ...)

Posted: Thu Jun 02, 2011 10:12 am
by c4s
I'm going to use it soon, thanks!

Edit:
Is PeekL(*WAV + 40) / PeekU(*Wav + 32) correct or should it be PeekU(*WAV + 40) / PeekU(*Wav + 32)?
And I think Line 8 (";- WAV ConFileants") is wrong... ;-)

Re: Get WAV Attributes (length, rate, ...)

Posted: Thu Jun 02, 2011 6:54 pm
by kenmo
Whoops, a rogue find/replace changed that comment.

But the PeekL and PeekU are correct:
- the value at offset 40 is NumSamples * NumChannels * BytesPerSample (32-bit value)
- the value at offset 32 is NumChannels * BytesPerSample (16-bit value)
So the quotient is just NumSamples.

See (here), one of many useful references.

Also, a hint: For simplicity, the Load procedure opens/reads/closes a .wav file every time you call it. For efficiency (if you are reading many attributes on the same file) it would be better to read the first 44 bytes of the file into your own buffer, then call the Catch procedure on this pointer. The rest of the file (the actual audio data) doesn't need to be included.

Re: Get WAV Attributes (length, rate, ...)

Posted: Sat Jun 11, 2011 7:04 pm
by chris319
Hi Kenmo -

As you will discover, properly analyzing a wav file is not a trivial undertaking. Unfortunately, the format given at stanford.edu is obsolete as it does not take into account files which use the WAVEFORMATEXTENSIBLE format.

A valid wav file will have a WAVEFORMATEX structure but it may or may not have the two-byte cbSize variable; thus, the WAVEFORMATEX structure itself may be 16 or 18 bytes in length. In addition, it may or may not have a WAVEFORMATEXTENSIBLE structure which is cbSize (usually 22) bytes in length, including a 16-byte GUID. This will throw off the location of the "data" string (subchunk2ID). For these reasons, my modified version only looks at the "RIFF" and "fmt " strings. In addition, I convert these strings to lower case as there is no requirement that I'm aware of that they be in a particular case. In a file you could encounter either case.

There is much more to do but this is a start.

Code: Select all

    ; +-------------------+-------+
    ; | WavAttributes.pbi | kenmo |
    ; +-------------------+-------+
    ; |  5.02.2011 - Version 1.0
    ; |  6.01.     - Version 1.1 (removed a debug, fixed Samples attribute)
    ; |  6.11.2011 - Version 1.2 (modified by chris319 to accomodate cbSize varaible and
    ;                             incorporated case conversion of "RIFF", "fmt " and
    ;                             "data" strings - this version is still incomplete)
    ;-
    ;- WAV Constants

    ;   _Attribute      _Value   _Description            _Units
    #WAV_BitDepth     =  $00    ; Sample depth *          bits  / sample
    #WAV_BitRate      =  $01    ; Total data rate         bits  / second
    #WAV_ByteDepth    =  $02    ; Sample depth *          bytes / sample
    #WAV_ByteRate     =  $03    ; Total data rate         bytes / second
    #WAV_Channels     =  $04    ; Number of channels      channels
    #WAV_ChunkSize    =  $05    ; Total audio data size   bytes
    #WAV_Format       =  $06    ; File format             PCM or Unknown
    #WAV_Milliseconds =  $07    ; Total audio length      milliseconds
    #WAV_SampleRate   =  $08    ; Sample rate *           samples / second
    #WAV_Samples      =  $09    ; Number of samples *     samples
    #WAV_Seconds      =  $0A    ; Total audio length      seconds
    ;                           * refers to one channel only

    ; Return Values
    #WAV_PCM      =  0
    #WAV_Unknown  = -1
    #WAV_NoFile   = -2
    #WAV_ReadFail = -3
    #WAV_NoBuffer = -4

    ;-
    ;- WAV Procedures

    Procedure.i CatchWavAttribute(*WAV.i, Attribute.i)
      Protected Value.i = #WAV_Unknown
     
      If (*Wav)
        riff$ = PeekS(*Wav, 4): fmt$ = PeekS(*Wav + 12, 4) ;: data1$ = PeekS(*Wav + 36, 4): data2$ = PeekS(*Wav + 38, 4)
        If LCase(riff$) = "riff" And LCase(fmt$) = "fmt " ;And (LCase(data1$) = "data" Or LCase(data2$) = "data")
          Select (Attribute)
            Case #WAV_BitDepth
              Value = PeekU(*WAV + 34)
            Case #WAV_BitRate
              Value = PeekL(*WAV + 28)<<3
            Case #WAV_ByteDepth
              Value = PeekU(*WAV + 34)>>3
            Case #WAV_ByteRate
              Value = PeekL(*WAV + 28)
            Case #WAV_Channels
              Value = PeekU(*WAV + 22)
            Case #WAV_ChunkSize
              Value = PeekL(*WAV + 40)
            Case #WAV_Format
              Value = #WAV_PCM
            Case #WAV_Milliseconds
              Value = PeekL(*WAV + 40) / (PeekL(*Wav + 28) / 100) * 10
            Case #WAV_Samples
              Value = PeekL(*WAV + 40) / PeekU(*Wav + 32)
            Case #WAV_SampleRate
              Value = PeekL(*WAV + 24)
            Case #WAV_Seconds
              Value = PeekL(*WAV + 40) / PeekL(*Wav + 28)
          EndSelect
        EndIf
      EndIf
     
      ProcedureReturn (Value)
    EndProcedure

    Procedure.i LoadWavAttribute(File.s, Attribute.i)
      Protected Value.i   = #WAV_NoFile
      Protected FID.i     = #Null
      Protected *Buffer.i = #Null
     
      If (File)
        FID = ReadFile(#PB_Any, File)
        If (FID)
          *Buffer = AllocateMemory(44)
          If (*Buffer)
            If (ReadData(FID, *Buffer, 44) = 44)
              Value = CatchWavAttribute(*Buffer, Attribute)
            Else
              Value = #WAV_ReadFail
            EndIf
            FreeMemory(*Buffer)
          Else
            Value = #WAV_NoBuffer
          EndIf
          CloseFile(FID)
        Else
          Value = #WAV_ReadFail
        EndIf
      EndIf
     
      ProcedureReturn (Value)
    EndProcedure

    ;-
    ;  EOF

Code: Select all

    XIncludeFile "WavAttributes.pbi"

    File.s = OpenFileRequester("Open WAV", "", "WAV Files|*.wav|All Files|*.*", 0)

    If (File)

      Debug "File: " + GetFilePart(File)
      Format.i = LoadWavAttribute(File, #WAV_Format)
      If (Format = #WAV_Unknown)
        Debug "WAV_Format: Unknown"
      ElseIf (Format < 0)
        Debug "Load Error: " + Str(Format)
      Else
        Debug "Valid wav file": Debug ""
        Select (Format)
          Case #WAV_PCM
            Debug "WAV_Format: PCM"
        EndSelect
        Debug "WAV_SampleRate: "   + Str(LoadWavAttribute(File, #WAV_SampleRate  ))
        Debug "WAV_BitDepth: "     + Str(LoadWavAttribute(File, #WAV_BitDepth    ))
        Debug "WAV_ByteDepth: "    + Str(LoadWavAttribute(File, #WAV_ByteDepth   ))
        Debug "WAV_Channels: "     + Str(LoadWavAttribute(File, #WAV_Channels    ))
        Debug "WAV_BitRate: "      + Str(LoadWavAttribute(File, #WAV_BitRate     ))
        Debug "WAV_ByteRate: "     + Str(LoadWavAttribute(File, #WAV_ByteRate    ))
        Debug "WAV_ChunkSize: "    + Str(LoadWavAttribute(File, #WAV_ChunkSize   ))
        Debug "WAV_Samples: "      + Str(LoadWavAttribute(File, #WAV_Samples     ))
        Debug "WAV_Milliseconds: " + Str(LoadWavAttribute(File, #WAV_Milliseconds))
        Debug "WAV_Seconds: "      + Str(LoadWavAttribute(File, #WAV_Seconds     ))
      EndIf
    EndIf
Here is some recommended reading on the WAVEFORMATEX and WAVEFORMATEXTENSIBLE structures:

http://msdn.microsoft.com/en-us/library ... 85%29.aspx

http://msdn.microsoft.com/en-us/library ... 85%29.aspx

Re: Get WAV Attributes (length, rate, ...)

Posted: Mon Mar 10, 2014 10:14 am
by doctornash
might I request for this to be extended please to load the actual sample values of the .wav file into an array?

Re: Get WAV Attributes (length, rate, ...)

Posted: Sat Mar 15, 2014 4:50 am
by kenmo
Sure, that is easy as long as you are working with bare-bones WAV files (as chris319 said, the format can get more complicated).

The samples are stored as integers (8 or 16 bit) but it's easy to normalize them to floats from -1 to +1 if you want. I can write an example for you.

Re: Get WAV Attributes (length, rate, ...)

Posted: Sat Mar 15, 2014 10:37 am
by doctornash
Kenmo 'bare bones' is sufficient, so yes please an example would be fabulous! :D

Re: Get WAV Attributes (length, rate, ...)

Posted: Sun Mar 16, 2014 9:17 pm
by kenmo
This is quick'n'dirty (it's thrown together from some WAV programs I had) and it's not exactly what you asked for (doesn't load the samples into an array, instead they are stored in a memory block as doubles, but you can still step through them).........

It should get you started at loading / analyzing / editing / saving basic 16-bit WAV files.

Code: Select all

;
; Read/Create/Edit/Save WAV files
; by kenmo
; 3/16/2014
;

;-
;- Structures

Structure STDWAV
  ChunkID.l
  ChunkSize.l
  Format.l
  ;
  Subchunk1ID.l
  Subchunk1Size.l
  AudioFormat.u
  NumChannels.u
  SampleRate.l
  ByteRate.l
  BlockAlign.u
  BitsPerSample.u
  ;
  Subchunk2ID.l
  Subchunk2Size.l
EndStructure

Structure FULLWAVE
  NumChannels.u
  SampleRate.l
  BitsPerSample.u
  ;
  NumSamples.l
  MemorySize.l
EndStructure

;-
;- Procedures

Procedure.i ReadFileToMemory(File.s)
  Protected *Result = #Null
  
  Protected FN.i = ReadFile(#PB_Any, File)
  If (FN)
    Protected n.i = Lof(FN)
    If (n > 0)
      *Result = AllocateMemory(n)
      If (*Result)
        If (ReadData(FN, *Result, n) <> n)
          FreeMemory(*Result)
          *Result = #Null
        EndIf
      EndIf
    EndIf
    CloseFile(FN)
  EndIf
  
  ProcedureReturn (*Result)
EndProcedure

Procedure.i LoadStdWav(File.s)
  Protected *SW.STDWAV = #Null
  
  Protected n.i = FileSize(File)
  If (n > SizeOf(STDWAV))
    *SW = ReadFileToMemory(File)
    If (*SW)
      Protected Valid.i = #True
      Valid = Valid * Bool(*SW\ChunkID = 'FFIR')
      Valid = Valid * Bool(*SW\ChunkSize <= n - 8)
      Valid = Valid * Bool(*SW\Format = 'EVAW')
      Valid = Valid * Bool(*SW\Subchunk1ID = ' tmf')
      Valid = Valid * Bool(*SW\Subchunk1Size = 16)
      Valid = Valid * Bool(*SW\AudioFormat = 1)
      Valid = Valid * Bool(*SW\ByteRate = *SW\SampleRate * *SW\NumChannels * *SW\BitsPerSample / 8)
      Valid = Valid * Bool(*SW\BlockAlign = *SW\NumChannels * *SW\BitsPerSample / 8)
      Valid = Valid * Bool(*SW\BitsPerSample % 8 = 0)
      Valid = Valid * Bool(*SW\Subchunk2ID = 'atad')
      Valid = Valid * Bool(*SW\Subchunk2Size <= n - SizeOf(STDWAV))
      If (Not Valid)
        FreeMemory(*SW)
        *SW = #Null
      EndIf
    EndIf
  EndIf
  
  ProcedureReturn (*SW)
EndProcedure

Procedure.i CreateFullWave(NumChannels.i, SampleRate.i, BitsPerSample.i, NumSamples.i)
  Protected *FW.FULLWAVE = #Null
  
  If ((NumChannels >= 1) And (SampleRate > 0) And (BitsPerSample % 8 = 0) And (NumSamples > 0))
    Protected BlockAlign.i = NumChannels * (BitsPerSample / 8)
    Protected n.i = SizeOf(FULLWAVE) + BlockAlign * NumSamples * SizeOf(DOUBLE)
    *FW = AllocateMemory(n)
    If (*FW)
      *FW\NumChannels   = NumChannels
      *FW\SampleRate    = SampleRate
      *FW\BitsPerSample = BitsPerSample
      *FW\NumSamples    = NumSamples
      *FW\MemorySize    = n
    EndIf
  EndIf
  
  ProcedureReturn (*FW)
EndProcedure

Procedure.i LoadFullWave(File.s)
  Protected *FW.FULLWAVE = #Null
  
  Protected *SW.STDWAV = LoadStdWav(File)
  If (*SW)
    Protected NumSamples.l = *SW\Subchunk2Size / (*SW\NumChannels * *SW\BitsPerSample / 8)
    *FW = CreateFullWave(*SW\NumChannels, *SW\SampleRate, *SW\BitsPerSample, NumSamples)
    If (*FW)
      Protected Offset.d, Scaling.d
      Protected c.i, i.i
      Protected *Read, *Write
      Select (*SW\BitsPerSample)
        Case 16 ; 16-bit, -32768 to +32767
          Scaling =  1.0 / 32767.5
          Offset  =  0.5
          For c = 0 To *SW\NumChannels - 1
            *Read  = *SW + SizeOf(STDWAV)   + c * 2
            *Write = *FW + SizeOf(FULLWAVE) + c * NumSamples * SizeOf(DOUBLE)
            For i = 0 To NumSamples - 1
              PokeD(*Write, (PeekW(*Read) + Offset) * Scaling)
              *Read  + *SW\BlockAlign
              *Write + SizeOf(DOUBLE)
            Next i
          Next c
        Case 8 ; 8-bit, 0 to 255
          Scaling =  1.0 / 127.5
          Offset  = -127.5
          ;
        Default
          ;
      EndSelect
    EndIf
    FreeMemory(*SW)
  EndIf
  
  ProcedureReturn (*FW)
EndProcedure

Procedure.i FullWaveSamplePointer(*FW.FULLWAVE, Channel.i)
  Protected *Result = #Null
  If (*FW And (Channel >= 0) And (Channel < *FW\NumChannels))
    *Result = *FW + SizeOf(FULLWAVE) + Channel * (*FW\NumSamples * SizeOf(DOUBLE))
  EndIf
  ProcedureReturn (*Result)
EndProcedure

Procedure.i SaveWave(*FW.FULLWAVE, File.s)
  Protected Result.i = #False
  
  If (*FW And File)
    Protected FN.i = CreateFile(#PB_Any, File)
    If (FN)
      Protected BlockAlign.i = (*FW\NumChannels * *FW\BitsPerSample / 8)
      Protected SampleBytes.i = (*FW\NumSamples * BlockAlign)
      WriteLong(FN, 'FFIR')
      WriteLong(FN, 36 + SampleBytes)
      WriteLong(FN, 'EVAW')
      ;
      WriteLong(FN, ' tmf')
      WriteLong(FN, 16)
      WriteUnicodeCharacter(FN, 1)
      WriteUnicodeCharacter(FN, *FW\NumChannels)
      WriteLong(FN, *FW\SampleRate)
      WriteLong(FN, *FW\SampleRate * BlockAlign)
      WriteUnicodeCharacter(FN, BlockAlign)
      WriteUnicodeCharacter(FN, *FW\BitsPerSample)
      ;
      WriteLong(FN, 'atad')
      WriteLong(FN, SampleBytes)
      ;
      Protected Offset.d, Scaling.d
      Protected c.i, i.i
      Select (*FW\BitsPerSample)
        Case 16
          Scaling =  32767.5
          Offset  =  0.5
          For i = 0 To *FW\NumSamples - 1
            For c = 0 To *FW\NumChannels - 1
              WriteWord(FN, Int(Scaling * PeekD(*FW + SizeOf(DOUBLE) * (i + c * *FW\NumSamples)) - Offset))
            Next c
          Next i
        Case 8
          ;
        Default
          ;
      EndSelect
      CloseFile(FN)
      Result = #True
    EndIf
  EndIf
  
  ProcedureReturn (Result)
EndProcedure

Procedure.i DuplicateFullWave(*FWI.FULLWAVE)
  Protected *FWO.FULLWAVE = #Null
  
  If (*FWI)
    *FWO = AllocateMemory(*FWI\MemorySize)
    If (*FWO)
      CopyMemory(*FWI, *FWO, *FWI\MemorySize)
    EndIf
  EndIf
  
  ProcedureReturn (*FWO)
EndProcedure

;-
;- Example Program

Structure DOUBLEBUFFER
  d.d[0]
EndStructure

InFile.s = OpenFileRequester("Load WAV", GetHomeDirectory(), "WAV Files|*.wav", 0)
If (InFile)

  *Wave.FULLWAVE = LoadFullWave(InFile)
  If (*Wave)
    
    *Sample.DOUBLEBUFFER = FullWaveSamplePointer(*Wave, 0)
        
    Delta = *Wave\NumSamples / 1000
    i = 0
    While (i < *Wave\NumSamples)
      Debug "Sample " + Str(i) + ": " + StrD(*Sample\d[i], 5)
      i + Delta
    Wend
    
    FreeMemory(*Wave)
  Else
    Debug "WAV could not be loaded!"
  EndIf
EndIf

;-

Re: Get WAV Attributes (length, rate, ...)

Posted: Fri Jun 06, 2014 4:32 am
by chris319
The samples are stored as integers (8 or 16 bit)
There can also be 24-bit samples. They are not contained within 32 bits, just three bytes together.

Re: Get WAV Attributes (length, rate, ...)

Posted: Fri Jun 06, 2014 9:32 am
by chris319
Hi Kenmo -

Your code fails on valid wav files.

A wav file checker is a good thing to have, but malformed wav files will not play or cannot be opened, so it is important that a file checker program get it right.

PureBasic already has WAVEFORMATEX and WAVEFORMATEXTENSIBLE structures built into it, so let's use them. Don't confuse the file header with these structures. They are not the same.

This code will get you started. The wav file format has been kludged to accomodate advances in audio. The WAVEFORMATEXTENSIBLE structure is for audio with more than 2 channels or samples larger than 16 bits.

To find the audio data, search for the subchunk2ID string "data", then advance 4 more bytes to get past subchunk2Size.

Code: Select all

    ; Read WAV files
    ; original code by kenmo
    ; fixes started by chris319 ; 6/5/2014
    
    
OpenConsole()
    
Structure FILEHEADER
ChunkID.s{4}
ChunkSize.l
Format.s{4}
Subchunk1ID.s{4}
Subchunk1Size.l
;START OF WAVEFORMATEX or WAVEFORMATEXTENSIBLE STRUCTURE
wFormatTag.w
nChannels.w
nSamplesPerSec.l
nAvgBytesPerSec.l
nBlockAlign.w
wBitsPerSample.w
cbSize.w
EndStructure  

myFH.FILEHEADER

InFile.s = OpenFileRequester("Load WAV", GetHomeDirectory(), "WAV Files|*.wav", 0)
If InFile = "": End: EndIf
ReadFile(0,InFile)
ReadData(0, @myFH, SizeOf(myFH))
CloseFile(0)

PrintN(GetFilePart(InFile) + Chr(10))
PrintN("ChunkID: " + myFH\chunkID)
;PrintN("ChunkSize: " + Str(myFH\ChunkSize))
PrintN("Format: " + myFH\Format)
PrintN("SubChunk1ID: " + myFH\Subchunk1ID)
Print("Chunk1Size: " + Str(myFH\Subchunk1Size))
If myFH\Subchunk1Size <> 18
  PrintN("  Old file format.")
EndIf

If myFH\cbSize = 22
  PrintN("WAVEFORMATEXTENSIBLE")
  PrintN("cbSize: " + Str(myFH\cbSize))
EndIf

PrintN("Channels: " + Str(myFH\nChannels))
PrintN("Samples per second: " + Str(myFH\nSamplesPerSec))
PrintN("Average bytes per second: " + Str(myFH\nAvgBytesPerSec))
PrintN("Block align: " + Str(myFH\nBlockAlign))
PrintN("Bits per sample: " + Str(myFH\wBitsPerSample))
If cbSize = 22: PrintN("cbSize: " + Str(myFH\cbSize)): EndIf

If myFH\nChannels > 2 Or myFH\wBitsPerSample > 16: PrintN("WAVEFORMATEXTENSIBLE" + Chr(10)): EndIf

Repeat
Delay(100)
ForEver

Re: Get WAV Attributes (length, rate, ...)

Posted: Sun Jun 08, 2014 7:36 am
by chris319
This code reports an error if a file has > 2 channels or > 16-bit samples and does not contain a proper cbSize field or if subchunk1size is incorrect.

Code: Select all

; Read WAV files
        ; original code by kenmo
        ; fixes started by chris319 ; 6/5/2014
       
       
    OpenConsole()
       
    Structure WAVEFILEHEADER
    ChunkID.s{4}
    ChunkSize.l
    Format.s{4}
    Subchunk1ID.s{4}
    Subchunk1Size.l
    ;START OF WAVEFORMATEX or WAVEFORMATEXTENSIBLE STRUCTURE
    wFormatTag.w
    nChannels.w
    nSamplesPerSec.l
    nAvgBytesPerSec.l
    nBlockAlign.w
    wBitsPerSample.w
    cbSize.w
    EndStructure 

    myFH.WAVEFILEHEADER

    InFile.s = OpenFileRequester("Load WAV", GetHomeDirectory(), "WAV Files|*.wav", 0)
    If InFile = "": End: EndIf
    ReadFile(0,InFile)
    ReadData(0, @myFH, SizeOf(myFH))
    CloseFile(0)

    PrintN("File name: " + GetFilePart(InFile) + Chr(10))
    PrintN("ChunkID: " + myFH\chunkID)
    ;PrintN("ChunkSize: " + Str(myFH\ChunkSize))
    PrintN("Format: " + myFH\Format)
    PrintN("SubChunk1ID: " + myFH\Subchunk1ID)
    Print("Chunk1Size: " + Str(myFH\Subchunk1Size))
    
    If myFH\Subchunk1Size <> 18
      PrintN("  Obsolete wav file format.")
    EndIf

    If myFH\cbSize = 22
      PrintN("WAVEFORMATEXTENSIBLE")
      PrintN("cbSize: " + Str(myFH\cbSize))
    EndIf

    PrintN("Channels: " + Str(myFH\nChannels))
    PrintN("Samples per second: " + Str(myFH\nSamplesPerSec))
    PrintN("Average bytes per second: " + Str(myFH\nAvgBytesPerSec))
    PrintN("Block align: " + Str(myFH\nBlockAlign))
    PrintN("Bits per sample: " + Str(myFH\wBitsPerSample))
    If cbSize = 22: PrintN("cbSize: " + Str(myFH\cbSize)): EndIf

    If myFH\nChannels > 2 Or myFH\wBitsPerSample > 16: PrintN("WAVEFORMATEXTENSIBLE" + Chr(10)): EndIf
    
    If (myFH\nChannels > 2 Or myFH\wBitsPerSample > 16) And (myFH\cbSize <> SizeOf(WAVEFORMATEXTENSIBLE) - SizeOf(WAVEFORMATEX) Or (myFH\Subchunk1Size <> 18))
      PrintN("ERROR: This file is not compliant with wav file specifications.")
    EndIf
    
    Repeat
    Delay(100)
    ForEver