Cue points in a wav file

Just starting out? Need help? Post your questions and find answers here.
Seymour Clufley
Addict
Addict
Posts: 1233
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Cue points in a wav file

Post by Seymour Clufley »

I am writing code to get the cue points from a RIFF wav file (alternative site). Having obtained the number of cues and the unique ID for each one from a "cues" chunk, it then loops through a "LIST" chunk extracting the text label for each cue.

The code works, but I am clearly doing something wrong because the loop through the "LIST" chunk messes up in between each cue, apparently reading each one twice. You can see this when debugging the unique IDs and txt labels.

Code: Select all

Structure RIFFStructure
  Riff.a[4]
  Length.l
  Wave.a[4]
EndStructure


Structure chunkStructure
  Signature.a[4]
  Length.l
EndStructure


Structure CuePointStructure
  unique_id.l
  label.s
  sample_offset.l
EndStructure


Procedure.b GetWavCuePoints(fn.s,List cue.CuePointStructure())
  Define *RiffPtr.RIFFStructure
  Define *chunkPtr.chunkStructure
  
  f.i = ReadFile(#PB_Any, fn)
  If Not f
    ProcedureReturn #False
  EndIf
  
  loaf.i = Lof(f)
  *Buffer = AllocateMemory(loaf)
  If *Buffer
    If ReadData(f, *Buffer, loaf) = loaf
      *RiffPtr = *Buffer
      
      If PeekS(@*RiffPtr\Riff, 4, #PB_Ascii) = "RIFF" And *RiffPtr\Length = loaf - 8 And PeekS(@*RiffPtr\Wave, 4, #PB_Ascii) = "WAVE"
        Debug "Header Ok"
        
        *chunkPtr = *Buffer + $0C
        
        Repeat
          ChunkName.s = PeekS(@*chunkPtr\Signature, 4, #PB_Ascii)
          
          Debug ""
          Debug "CHUNK: " + ChunkName
          
          Select ChunkName
            Case "cue "
              
              *cuePtr = *chunkPtr
              *cuePtr + 8
              NumCuePoints.w = PeekW(*cuePtr)
              *cuePtr + 4
              Debug "cue points: "+Str(NumCuePoints)
              For cp = 1 To NumCuePoints
                AddElement(cue())
                cue()\unique_id = PeekL(*cuePtr)
                Debug "unique_id: "+Str(cue()\unique_id)
                *cuePtr+4
                play_order_position.i = PeekL(*cuePtr)
                *cuePtr+4
                data_chunk_id.s = PeekS(*cuePtr,4,#PB_Ascii)
                Debug "data_chunk_id: "+data_chunk_id
                *cuePtr+4
                chunk_start.l = PeekL(*cuePtr)
                Debug "chunk_start: "+Str(chunk_start)
                *cuePtr+4
                block_start.i = PeekL(*cuePtr)
                *cuePtr+4
                cue()\sample_offset = PeekL(*cuePtr)
                Debug "sample_offset: "+Str(cue()\sample_offset)
                *cuePtr+4
              Next cp
              
            Case "LIST"
              chklngth.i = *chunkPtr\Length
              Debug "list chunk size: "+Str(chklngth)
              
              list_type_id.s = PeekS(*chunkPtr+12,4,#PB_Ascii)
              ;Debug "list type ID: "+list_type_id
              If list_type_id = "labl"
                *lablPtr = *chunkPtr+12
                lngth.l = PeekL(*lablPtr+4)
                Debug "LNGTH: "+Str(lngth)
                Debug "--------"
                accum_lngth.l = 8
                
                chklngth-4
                While accum_lngth<chklngth
                  uid.l = PeekL(*lablPtr+accum_lngth)
                  Debug "UID: "+uid
                  accum_lngth+4
                  txt.s = ""
                  Repeat
                    this_char.s = PeekS(*lablPtr+accum_lngth,1,#PB_Ascii)
                    accum_lngth+1
                    If Asc(this_char)=0
                      Break
                    Else
                      txt+this_char
                    EndIf
                  ForEver
                  
                  ForEach cue()
                    If cue()\unique_id=uid
                      cue()\label = txt
                      Break
                    EndIf
                  Next
                  
                  Debug "TXT: "+txt
                  accum_lngth+2
                  If Len(txt) % 2
                    Debug Str(Len(txt))+" is odd"
                    accum_lngth-1
                  EndIf
                  
                  Debug "--------"
                Wend
              EndIf
          EndSelect
          
          *chunkPtr + 8 + *chunkPtr\Length
          
        Until *chunkPtr >= *Buffer + *RiffPtr\Length
        
      EndIf
    EndIf
    FreeMemory(*Buffer)
  EndIf
  CloseFile(f)
  
  ProcedureReturn #True
  
EndProcedure

Filename$ = "D:\regions_mono.wav"
NewList cue.CuePointStructure()
GetWavCuePoints(Filename$,cue())
Debug "" : Debug "" : Debug "----------------"
c.i = 0
ForEach cue()
  c+1
  Debug "#"+Str(c)+": "+cue()\label+" @ "+Str(cue()\sample_offset)
Next
It can be tested using this WAV file which has cue points.

I think the mistake is to do with the building up of the "accum_lngth" variable as it works its way through the chunk.

I am sure the mistake is some elementary thing that will turn out to be quite embarrassing, but if anyone could help out, I'd appreciate it.
Last edited by Seymour Clufley on Mon May 03, 2021 1:51 pm, edited 1 time in total.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
Olli
Addict
Addict
Posts: 1071
Joined: Wed May 27, 2020 12:26 pm

Re: Cue points in a wav file

Post by Olli »

Hello Seymour,

just an off-topic remark : your 1st link points to a site which has an non valid date certificate (expired on April, the 26th).

If you had a copy of what it interests you in this document, maybe it will ease the help.

It is very interesting the research about the WAV format : ADPCM sub-format seems to have been removed on W10, what it grows the interest of the subject.
Seymour Clufley
Addict
Addict
Posts: 1233
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Re: Cue points in a wav file

Post by Seymour Clufley »

Thank you - corrected.
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
Olli
Addict
Addict
Posts: 1071
Joined: Wed May 27, 2020 12:26 pm

Re: Cue points in a wav file

Post by Olli »

I find strange, but from far (very far anyway), the case switches.

Case "cue "
Case "LIST"

I say maybe a very big non-sense, but is not it reversed strings ?

Case " euc"
Case "TSIL"

?

Added : I just have read you alternative link. I thank you for this work.
"cue " is equ to PeekL(*Signature) = $63756520
and
"list" is on lower case and equ to PeekL(*S) = $6C696E74
Perhaps I am searching the problem on a wrong way but I consider these signatures as a 32-bits type long integer, instead of string...
Olli
Addict
Addict
Posts: 1071
Joined: Wed May 27, 2020 12:26 pm

Re: Cue points in a wav file

Post by Olli »

Unable to copy/paste a part of code... :?

Just Ac = PeekA(*Address) seems to be more adapted.
If Ac = 0
AddElement(myList() ) ; list of strings
MyList() = Txt$
Txt$ = ""
...
Else
Txt$ + Chr(Ac)
Endif

Etc...
Seymour Clufley
Addict
Addict
Posts: 1233
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Re: Cue points in a wav file

Post by Seymour Clufley »

Thank you, Olli. I think I have it now:

Code: Select all

Structure RIFFStructure
  Riff.a[4]
  Length.l
  Wave.a[4]
EndStructure

Structure chunkStructure
  Signature.a[4]
  Length.l
EndStructure

Structure CuePointerStructure
  unique_id.l
  sample_position.l
  fccChunk.l
  chunk_start.l
  block_start.l
  sample_offset.l
EndStructure


Structure CuePointStructure
  unique_id.l
  label.s
  sample.l
EndStructure

Procedure.b GetWavFileCuePoints(fn.s,List cue.CuePointStructure())
  
  f.i = ReadFile(#PB_Any, fn)
  If Not f
    ProcedureReturn #False
  EndIf
  
  loaf.i = Lof(f)
  *Buffer = AllocateMemory(loaf)
  If *Buffer
    If ReadData(f, *Buffer, loaf) = loaf
      *RiffPtr.RIFFStructure = *Buffer
      
      If PeekS(@*RiffPtr\Riff, 4, #PB_Ascii) = "RIFF" And *RiffPtr\Length = loaf - 8 And PeekS(@*RiffPtr\Wave, 4, #PB_Ascii) = "WAVE"
        ;Debug "Header Ok"
        
        *chunkPtr.chunkStructure = *Buffer + $0C
        
        Repeat
          ChunkName.s = PeekS(@*chunkPtr\Signature, 4, #PB_Ascii)
          
          Debug ""
          Debug "CHUNK: " + ChunkName
          
          Select ChunkName
            Case "cue "
              
              *cuePtr = *chunkPtr
              *cuePtr + 8
              NumCuePoints.w = PeekW(*cuePtr)
              *cuePtr + 4
              Debug "----"
              Debug "cue points: "+Str(NumCuePoints)
              For cp = 1 To NumCuePoints
                Debug "----"
                *cueSetPtr.CuePointerStructure = *cuePtr
                Debug "unique_id: "+Str(*cueSetPtr\unique_id)
                Debug "sample_position: "+Str(*cueSetPtr\sample_position)
                Debug "data_chunk_id: "+PeekS(@*cueSetPtr\fccChunk,4,#PB_Ascii)
                Debug "chunk_start: "+Str(*cueSetPtr\chunk_start)
                ;Debug "chunk_start: "+Str(PeekL(*cuePtr+20))
                ;Debug "block_start: "+Str(*cueSetPtr\block_start)
                Debug "block_start: "+Str(PeekL(*cuePtr+24))
                Debug "sample_offset: "+Str(*cueSetPtr\sample_offset)
                ;Debug "sample_offset: "+Str(PeekL(*cuePtr+28))
                
                AddElement(cue())
                cue()\unique_id = *cueSetPtr\unique_id
                cue()\sample = *cueSetPtr\sample_position
                
                *cuePtr+SizeOf(CuePointerStructure)
              Next cp
              
            Case "LIST"
              chklngth.i = *chunkPtr\Length
              Debug "list chunk size: "+Str(chklngth)
              
              list_type_id.s = PeekS(*chunkPtr+12,4,#PB_Ascii)
              ;Debug "list type ID: "+list_type_id
              If list_type_id = "labl"
                *lablPtr = *chunkPtr+12
                lngth.l = PeekL(*lablPtr+4)
                Debug "LNGTH: "+Str(lngth)
                Debug "--------"
                accum_lngth.l = 8
                
                chklngth-4
                While accum_lngth<chklngth
                  uid.l = PeekL(*lablPtr+accum_lngth)
                  Debug "UID: "+uid
                  accum_lngth+4
                  txt.s = ""
                  Repeat
                    this_char.i = PeekA(*lablPtr+accum_lngth)
                    accum_lngth+1
                    If this_char=0
                      Break
                    Else
                      txt+Chr(this_char)
                    EndIf
                  ForEver
                  
                  If txt="" : accum_lngth+2 : Continue : EndIf
                  
                  ForEach cue()
                    If cue()\unique_id = uid
                      cue()\label = txt
                      Break
                    EndIf
                  Next
                  
                  Debug "TXT: "+txt
                  Debug "ACCUM_LNGTH: "+Str(accum_lngth)
                  accum_lngth+2
                  If Len(txt) % 2
                    Debug Str(Len(txt))+" is odd"
                    accum_lngth-1
                  EndIf
                  
                  Debug "--------"
                Wend
              EndIf
          EndSelect
          
          *chunkPtr + 8 + *chunkPtr\Length
          
        Until *chunkPtr >= *Buffer + *RiffPtr\Length
        
      EndIf
    EndIf
    FreeMemory(*Buffer)
  EndIf
  CloseFile(f)
  
  ProcedureReturn #True
  
EndProcedure

Filename$ = "P:\sox_exper\regions_mono.wav"
NewList cue.CuePointStructure()
GetWavFileCuePoints(Filename$,cue())
Debug "" : Debug "" : Debug "----------------"
c.i = 0
ForEach cue()
  c+1
  Debug "#"+Str(c)+": "+cue()\label+" @ "+Str(cue()\sample)
Next
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."
Post Reply