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
