Shop OBEX P1 Docs P2 Docs Learn Events
Play wav freeze — Parallax Forums

Play wav freeze

I wonder if there is a better solution to resolve a freeze condition with the following code after playing some wav files.

The following code snippet, written by Raymond Allen is taken from OBEX.

I added some diagnostic print statements and a Watchdog timer during the play loop to prevent the freeze. None of the print statements execute, which means the wav file headers are properly formatted. Another process (not shown) gets the next file to play after each wav file finishes playing. There is no issue with the other process. The following code does not exit the Player loop for some files without the Watchdog code. I suspect it is a simple miscalculation on the number of samples, but there could be more variations from wav file to wav file that cause the freeze.

Is there a better solution?
PUB PlayWav | i,j,k,n,m,SampleRate,Samples,Channels,ChunkSize,SamplePeriod,PlayTime,Watchdog
' Open the WAV file  (NOTE:  Only plays stereo, 16-bit PCM WAV Files !!!!!!!!!!!!!)

  repeat                                                             ' Endlessly play files
'   print(string("Waiting for newwavfile",CR))
    repeat while strcomp(@newwavfile,@oldwavfile)                    ' Endlessly check for new file to play
    bytemove(@oldwavfile,@newwavfile,strsize(@newwavfile))

'   Open file
    i:=\sd[wav].popen(@newwavfile,"r")
    sd[wav].popen(@newwavfile,"r")
    if (i<>0)
      print(string("File not found",CR))
      Err++
   
'   Plays only   16-bit PCM WAV Files !!!!!!!!!!!!!!!!
'   See here for header format:  https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ 
    i:=sd[wav].pread(@Header, 44-8) 'read data words to input stereo buffer

'   Get bits per sample (must be 16)
    if (Header[35]<<8+Header[34])<>16                              ' Bits per sample
      print(string("Error:  Not 16 bits per sample!",CR))
      Err++

'   Get audio format, must be PCM
    if (Header[21]<<8+Header[20])<>1                               ' Audio Format $1000 for PCM (read backwards as $0001)
      print(string("Error:  Not uncompressed PCM data",CR))
      Err++

'   Get sample rate from header
    SampleRate:=Header[27]<<24+Header[26]<<16+Header[25]<<8+Header[24] ' Sample rate

'   Get number of channels from header
    Channels:=Header[23]<<8+Header[22]                             ' 1 = Mono, 2 = Stereo, etc

'   Find data
    i := 0
    repeat
      sd[wav].pread(@Header[36], 1)
      if Header[36] == "d"
        sd[wav].pread(@Header[37], 1)
        if Header[37] == "a"
          sd[wav].pread(@Header[38], 1)
          if Header[38] == "t"         
            sd[wav].pread(@Header[39], 1)
            if Header[39] == "a"
              quit      
      if i++ == 200
        print(string("'data' not found",CR))
        quit                                                        
      
'   Get number of  samples from header
    sd[wav].pread(@Header[40],4) 'read subchunk size
    ChunkSize:=Header[43]<<24+Header[42]<<16+Header[41]<<8+Header[40]
    Samples:=(ChunkSize>>1)/Channels

'   Calculate play time
    SamplePeriod:=CLKFREQ/SampleRate  'number of clocks between samples'1814'for 44.1ksps,  5000 'for 16ksps 
    m := Samples/SampleRate
     
'   Start ASM player in a new cog
    parameter1:=@buff1                                             'address of first buffer         
    parameter2:=@buff2                                             'address of second buffer        
    parameter3:=SamplePeriod                                       'time between samples            
    parameter4:=Samples                                            'total number of samples         
    parameter5:=Channels                                           'number of channels              
    parameter6:=wavLongs ' (wavWords-1)/Channels                   'number of samples in each buffer    #####
    parameter7:=volume&$FFFFF                                      'volume control (0..65535)       
   
'   Synchronization
    wavready := true                                               ' Signal ready         
    repeat until ctlready                                          ' Wait for Play signal   '#####
    Playing := true                                                ' Signal playing       
    k := COGNEW(@ASMWAV,@parameter1)
   
'   Keep filling buffers until end of file
'    note:  using alternating buffers to keep data always at the ready...
    n:=wavWords-1
    buff1[n]:=1                                                    ' Set empty flags at end of buffers
    buff2[n]:=1
    i:=n*2                                                         ' Number of bytes to read
   
'   Keep buffers filled until got all samples
    j:=samples*Channels*2
    PlayTime := cnt

'   Player loop
    repeat
      Watchdog := wavLongs                                                      '#####
      repeat until buff1[n] == 1                                   ' Wait until buffer empty
        if not Watchdog--                                                       '#####
          quit                                                                  '#####
      if sd[wav].pread(@buff1, i)<>i                               ' Read data words to input stereo buffer
        quit 
      buff1[n]:=0                                                  ' Clear empty flag
      j-=i
      if j=<0
        quit
      
      Watchdog := wavLongs                                                      '#####
      repeat until buff2[n] == 1                                   'wait until buffer empty
        if not Watchdog--                                                       '#####
          quit                                                                  '#####
      if sd[wav].pread(@buff2, i)<>i
        quit                                                       ' Read data words to input stereo buffer   
      buff2[n]:=0                                                  'clear empty flag
      j-=i
      if j=<0
        quit
   
'   Reset synchronization
    Playing := false
    wavready := false
   
DAT
  ORG 0
ASMWAV
'load input parameters from hub to cog given address in par
        movd    :par,#pData1             
        mov     x,par
        mov     y,#7  'input 7 parameters
:par    rdlong  0,x
        add     :par,dlsb
        add     x,#4
        djnz    y,#:par

setup
        'setup output pins
        MOV DMaskR,#1
        ROL DMaskR,OPinR
        OR DIRA, DMaskR
        MOV DMaskL,#1
        ROL DMaskL,OPinL
        OR DIRA, DMaskL
        'setup counters
        OR CountModeR,OPinR
        MOV CTRA,CountModeR
        OR CountModeL,OPinL
        MOV CTRB,CountModeL
        'Wait for SPIN to fill table
        MOV WaitCount, CNT
        ADD WaitCount,BigWait
        WAITCNT WaitCount,#0
        'setup loop table
        MOV LoopCount,SizeBuff
        MOV pData,pData1
        MOV nTable,#1
        'setup loop counter
        MOV WaitCount, CNT
        ADD WaitCount,dRate

MainLoop
        CMP      nSamples,#0 wz,wc 'see if out of samples
        IF_e JMP #Done
        SUB      nSamples,#1
        waitcnt  WaitCount,dRate
        cmp      nChannel,#2 wz,wc
        if_e jmp #Stereo     'otherwise, assume mono
Mono
        call  #ReadWithVolume
        mov   right,y
        mov   left,right
        jmp   #Output
        
Stereo
        call  #ReadWithVolume
        mov   right,y
        add   pData,#2
        call  #ReadWithVolume
        mov   left,y 

Output
        ADD   pData,#2     
        MOV   FRQA,Right      
        MOV   FRQB,Left      

        'loop
        DJNZ LoopCount,#MainLoop

        WRword one,pData  'set empty flag 
        
        MOV LoopCount,SizeBuff        
        'switch table       ?
        CMP nTable,#1 wz
        IF_Z JMP #SwitchToTable2
SwitchToTable1
        MOV nTable,#1
        MOV pData,pData1
        JMP #MainLoop
SwitchToTable2
        MOV nTable,#2
        MOV pData,pData2
        JMP #MainLoop[s][/s]        
                
Done
        
         'now stop
        COGID thisCog
        COGSTOP thisCog          

'Multiply routine adapted from manual
'' Multiply x[15..0] by y[15..0] (y[31..16] must be 0)
' on exit, product in y[31..0]
ReadWithVolume
        mov   x,dVolume
        rdword y,pData
        add   y,twos
        and   y,ffff
        shl x,#16 'get multiplicand into x[31..16]
        mov t,#16 'ready for 8 multiplier bits
        shr y,#1 wc 'get initial multiplier bit into c
:loop
        if_c add y,x wc 'if c set, add multiplicand to product
        rcr y,#1 wc 'put next multiplier in c, shift prod.
        djnz t,#:loop 'loop until done

ReadWithVolume_ret
        ret 'return with product in y[31..0]


'Working variables
thisCog long 0
x       long 0
y       long 0
t       long 0
dlsb    long    1 << 9
BigWait long 100000
twos    long $8000_8000
ffff    long $ffff
        
'Loop parameters
nTable  long 0
WaitCount long 0
pData   long 0
LoopCount long 0
Left    long 0
Right   long 0
Zero    long 0
one     long 1       

'setup parameters
DMaskR  long 0 'right output mask
OPinR   long Pin_Right 'right channel output pin #                   
DMaskL  long 0 'left output mask 
OPinL   long Pin_Left 'left channel output pin #                       
CountModeR long %00011000_00000000_00000000_00000000
CountModeL long %00011000_00000000_00000000_00000000


'PASM input parameters                         SPIN:
pData1   res 1 'Address of first data table    parameter1:=@buff1          to pass @buff1 to ASM                   
pData2   res 1 'Address of second data table   parameter2:=@buff2          to pass @buff2 to ASM                   
dRate    res 1 'clocks between samples         parameter3:=SamplePeriod    to pass sample rate to ASM              
nSamples res 1 '#samples to play               parameter4:=Samples         to pass number of samples to ASM        
nChannel res 1 '#channels (1 or 2)             parameter5:=Channels        to pass number of channels to ASM       
SizeBuff res 1 '#samples in each buffer        parameter6:=wavLongs        to pass number of samples in each buffer
dVolume  res 1 'volume byte                    parameter7:=volume&$FFFFF   to pass volume to ASM                   

Sign In or Register to comment.