TACHYON
: WAVEPLAY.fth ." Singe cog bufferless bare-metal SD card wave player - 121107.1500-121226.0000 " ;
{
*** WAVE PLAYER ***
Single non-dedicated cog SD card wave player that takes 11.025kHz 16-bit signed mono wave files
The PLAY word takes 65 code bytes and does not need any buffers or variables
Reads and plays directly from the SD card using multiple block read mode on the same cog
Assumes file is non-fragmented (normal for SD cards)
External file system must locate the virtual address and size and pass this to PLAY
Use a standard RC filter for the output pin. I use 220R and 0.1uF.
Notes: This is a really barebones wave player showing how simply it can be without having to touch an ounce of assembler.
Tachyon is fast enough to pull this one off and smoothly too. Of course a lot of little extras can be added to this quick hack
but it serves it's purpose and keeps it very simple.
RUNMOD is actually the instruction for SPIO (input and output) when the SPIO module is loaded
If you want a sample wave file to try out then here is one that's a mono 11.025kHz signed wave file "POPCORN.wav"
}
: PLAY ( addr blocks rate pin -- )
\ A DUTY APIN \ audio dac using counter A in duty mode
A 7 CTRMODE DUP APIN 1+ BPIN \ (use this instead if you want stereo outputs)
ROT #18 CMD 0= \ Issue a multiple block read command (also loads SPIO module)
IF RES@ DROP \ when ready
CLKFREQ SWAP / DELTA \ sample period
FOR \ for multiple blocks - read samples and update dac counter
#256 FOR \ 256 samples per 512 byte sector
SD@ SD@ \ Get two bytes from the SD card
$80 + B>W \ offset the signed value and combine into 16-bit value
#16 SHL WAITCNT FRQ \ left justify, synch, then update dac counter
NEXT \ next sample
SD@ SD@ 2DROP \ drop the CRC
BEGIN -1 RUNMOD -1 <> UNTIL \ wait for SD card ready
NEXT \ next block
THEN
0 #12 CMD DROP \ STOP multiple block read
;
END
{
( Demo - play a file located at the address for n blocks @ 11.025kHz on P20 )
$5.4000 #6332 #11,025 #P20 PLAY
( If we are always using the same pin and sample frequency we could reduce the call parameters with this: )
: PLAY20 ( addr blocks -- ) #11,025 #P20 PLAY ;
( If that was the POPCORN wave file then we can create a definition for quick play )
: POPCORN $5.4000 #6332 PLAY20 ;
( Here's a version that factors out some code into reusable sections, skips any checking, and consumes just 70 code bytes )
: GetSample ( -- sample )
SD@ SD@ $80 + B>W
;
: WhenReady
BEGIN -1 RUNMOD -1 <> UNTIL
;
: PLAY ( addr blocks rate pin -- )
A DUTY APIN \ audio dac using counter A in duty mode to pin
ROT #18 CMD DROP \ Issue a multiple block read command with addr
WhenReady \ when SD card is ready
CLKFREQ SWAP / DELTA \ set sample period from rate
FOR \ for multiple blocks - read samples and update dac counter
#256 FOR \ 256 samples per 512 byte sector
GetSample \ Get a 16-bit biased sample from the SD card
#16 SHL WAITCNT FRQ \ left justify, synch, then update dac counter
NEXT \ next sample
GetSample DROP \ drop the CRC (treat as a sample)
WhenReady \ wait for SD card ready
NEXT \ next block
0 #12 CMD DROP \ STOP multiple block read
;
}