Play wav freeze
dbpage
Posts: 217
in Propeller 1
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?
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