Play RAW from Serial Flash

Status
Not open for further replies.

Frank B

Senior Member
Hi,

https://github.com/FrankBoesing/Teensy-PlayRawFromSerialFlash
EDIT: Please take look at play_serialflash.h for some configuration-options

In the utility-folder you will find a zipfile which contains a tool "wav2trw.exe", similar to wav2sketch, which converts wavefiles to raw-format with additional 4 byte header.
The default is to convert to u-law format, this need only 50% space.
If you want 16-Bit CD-Quality, add the parameter "-16" (call with "wav2trw -16") or double-click the batchfile
(Edit: The old name was wav2raw)

You can upload the files with this Sketch:https://github.com/FrankBoesing/Arduino-Teensy-Codec-lib/tree/master/examples/sd2serialflash

There's a little thing that can be optimized for samplingrates < 44kHz: The Buffer should be allocated dynamically, not fixed size. But I accept pullrequests... :) This should be easy.
Edit: Done

Paul, if you want, you can take it for Teensyduino121.
Maybe one of the other users can do some tests..

My little testsketch ( I have no buttons!) :
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
//#include <Bounce.h>
#include <play_serialflash.h>

//ULAW:
const unsigned int AudioSampleSnare = 0x16900;				//82583__kevoy__snare-drum-4.raw
const unsigned int AudioSampleTomtom =  0x2A000;			//86334__zgump__tom-0104.raw
const unsigned int AudioSampleHihat = 0xFAB00;			//102790__mhc__acoustic-open-hihat2.raw
const unsigned int AudioSampleKick =  0x106D00;			//171104__dwsd__kick-gettinglaid.raw
const unsigned int AudioSampleGong = 0x33500;			//86773__juskiddink__gong.raw
const unsigned int AudioSampleCashregister = 0x0;	//201159__kiddpark__cash-register.raw

/*
16 Bit RAW
const unsigned int AudioSampleSnare = 0xF8A00;				//82583__kevoy__snare-drum-4.raw
const unsigned int AudioSampleTomtom = 0;			//86334__zgump__tom-0104.raw
const unsigned int AudioSampleHihat = 0xD0B00;			//102790__mhc__acoustic-open-hihat2.raw
const unsigned int AudioSampleKick =  0xDCD00;			//171104__dwsd__kick-gettinglaid.raw
const unsigned int AudioSampleGong = 0x9500;			//86773__juskiddink__gong.raw
const unsigned int AudioSampleCashregister = 0xE2100;	//201159__kiddpark__cash-register.raw
*/

// Create the Audio components.  These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioPlaySerialFlash    sound0;
AudioPlaySerialFlash    sound1;  // six memory players, so we can play
AudioPlaySerialFlash    sound2;  // all six sounds simultaneously
AudioPlaySerialFlash    sound3;
AudioPlaySerialFlash    sound4;
AudioPlaySerialFlash    sound5;
AudioMixer4        mix1;    // two 4-channel mixers are needed in
AudioMixer4        mix2;    // tandem to combine 6 audio sources
AudioOutputI2S     headphones;
AudioOutputAnalog  dac;     // play to both I2S audio board and on-chip DAC

// Create Audio connections between the components
//
AudioConnection c1(sound0, 0, mix1, 0);
AudioConnection c2(sound1, 0, mix1, 1);
AudioConnection c3(sound2, 0, mix1, 2);
AudioConnection c4(sound3, 0, mix1, 3);
AudioConnection c5(mix1, 0, mix2, 0);   // output of mix1 into 1st input on mix2
AudioConnection c6(sound4, 0, mix2, 1);
AudioConnection c7(sound5, 0, mix2, 2);
AudioConnection c8(mix2, 0, headphones, 0);
AudioConnection c9(mix2, 0, headphones, 1);
AudioConnection c10(mix2, 0, dac, 0);

// Create an object to control the audio shield.
// 
AudioControlSGTL5000 audioShield;
/*
// Bounce objects to read six pushbuttons (pins 0-5)
//
Bounce button0 = Bounce(0, 5);
Bounce button1 = Bounce(1, 5);  // 5 ms debounce time
Bounce button2 = Bounce(2, 5);
Bounce button3 = Bounce(3, 5);
Bounce button4 = Bounce(4, 5);
Bounce button5 = Bounce(5, 5);
*/

void setup() {
/*
  // Configure the pushbutton pins for pullups.
  // Each button should connect from the pin to GND.
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
*/
  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(10);

  // turn on the output
  audioShield.enable();
  audioShield.volume(0.5);

  // by default the Teensy 3.1 DAC uses 3.3Vp-p output
  // if your 3.3V power has noise, switching to the
  // internal 1.2V reference can give you a clean signal
  //dac.analogReference(INTERNAL);

  // reduce the gain on mixer channels, so more than 1
  // sound can play simultaneously without clipping
  mix1.gain(0, 0.4);
  mix1.gain(1, 0.4);
  mix1.gain(2, 0.4);
  mix1.gain(3, 0.4);
  mix2.gain(1, 0.4);
  mix2.gain(2, 0.4);
}
/*
void loop() {
  // Update all the button objects
  button0.update();
  button1.update();
  button2.update();
  button3.update();
  button4.update();
  button5.update();

  // When the buttons are pressed, just start a sound playing.
  // The audio library will play each sound through the mixers
  // so any combination can play simultaneously.
  //
  if (button0.fallingEdge()) {
    sound0.play(AudioSampleSnare);
  }
  if (button1.fallingEdge()) {
    sound1.play(AudioSampleTomtom);
  }
  if (button2.fallingEdge()) {
    sound2.play(AudioSampleHihat);
  }
  if (button3.fallingEdge()) {
    sound3.play(AudioSampleKick);
  }
  if (button4.fallingEdge()) {
    sound4.play(AudioSampleGong);
  }
  if (button5.fallingEdge()) {
    sound5.play(AudioSampleCashregister);
  }

}
*/
void loop() {
	sound4.play(AudioSampleGong);
	delay(500);
	sound0.play(AudioSampleSnare);
	delay(200);
	sound1.play(AudioSampleTomtom);
	delay(100);
	sound2.play(AudioSampleHihat);
	delay(100);
	sound3.play(AudioSampleKick);
	delay(100);
	
	sound5.play(AudioSampleCashregister);
	delay(8000);

}

regards,
Frank
 
Last edited:
neat!

thanks for doing this!

i only gave it a quick try, but it's working for me. definitely faster than SDraw/SDwav. here's a drum sample from teensy vs a 808 bass drum, triggered from the same pulse. the teensy is lagging just about 5 ms behind:

Screen Shot 2014-12-30 at 23.45.44.jpg




edit: for the sake of completeness, here's SD_wav (playing 16 bit stereo) vs Flash_Raw vs a 808 (each): (ie, from top to bottom: Serial Flash, 808, 808, SD Wav)

Screen Shot 2014-12-31 at 11.41.58.png


as you can see, the latency for Serial Flash is slightly better (~5 ms vs ~6 ms), presumably reflecting the 500-1500us until AudioPlaySdWav :: play returns.

that's not really significant and pretty good in either case; looks as if improvements to latency will mostly come from interfacing with the API **




**
the code i used is fairly simplistic, basically:


Code:
void loop() {
	
        if (LCLK) {
        sound0.play(0x0);
        LCLK = false; 
	}


where volatile uint8_t LCLK is set by an external interrupt. the trigger-pulse goes into a NPN transistor, which is attached to a gpio and pulls it low when it saturates.
 

Attachments

  • Screen Shot 2014-12-31 at 11.27.19.jpg
    Screen Shot 2014-12-31 at 11.27.19.jpg
    56.1 KB · Views: 240
Last edited:
Awesome. Upgrading my project to flash as soon as possible!

I'll redo my polyphony test first, this should give a good idea of cpu usage improvements.

It also frees up the SDCard to either record the session or to play back sampled tracks like vocals :)
 
Forgot to mention on windows, audacity can save as a raw file if you prefer a gui.

Hi, i just saw that there's a bug with 16BIT PCM. I'll look a this the next days..

Audacity: Yes, but i choosed Pauls format, which has 4 Bytes (Format + Length) before the data.
 
Hi,
bugs are fixed, and there's a new wav2raw.exe Should work ok now ;-)
Uses now SPI-Transactions, and the buffer is removed.

Edit: Best speed should be at 120MHz, because of fastest SPI.
Edit @mxxx: I think the 5 ms are because of update() which is at 2.9ms intervalls + the unknown time between these updates when the triggering occurs.
 
Last edited:
This is really nice Frank, Danke.

I wonder if it is possible to use a different chip of that family with more capacity, shouldnt be a problem if i understood the specs correctly.

W25Q128FV W25Q256FV maybe?
 
Frank, nice work on the SPI flash playing!

I would like to ask you to consider a couple changes. Hopefully these aren't too much trouble?

The main thing I'd like to request is reserving the first 4096 bytes of the flash for a simple allocation table. The details can be worked out later. At this early stage, the main concern is writing tools that avoid writing to the first 4k. Whatever format gets defined, we'll use 0xFF to mean unused.

The other minor edit you might consider is calling AudioStartUsingSPI() and AudioStopUsingSPI(). These are implemented in spi_interrupt.h in the audio library. You can see example usage in play_sd_raw.cpp.


Edit: I started a new thread to discuss the flash allocation table format.
 
Last edited:
The other minor edit you might consider is calling AudioStartUsingSPI() and AudioStopUsingSPI(). These are implemented in spi_interrupt.h in the audio library. You can see example usage in play_sd_raw.cpp.

No problem. In the codecs, i use it..It looks like that i have forgotten it this time.. :)
For the 4096 reserved bytes: there's nothing to change in the player (ok, for the "FAT" later, mybe). And for the little sketch which loads the files into the spiflash, i can add a #define - switch.
For the codecs (yes, they all can play from flash too), it's the same.
 
This is really nice Frank, Danke.

I wonder if it is possible to use a different chip of that family with more capacity, shouldnt be a problem if i understood the specs correctly.

W25Q128FV W25Q256FV maybe?

I don't know.
But if the specs are the same, it should work!
 
Does anyone make a larger chip in SOIC-8 package?

The Winbond datasheet mentions only WSON-8, SOIC-16, and TFBGA-24.
 
Does anyone make a larger chip in SOIC-8 package?

The Winbond datasheet mentions only WSON-8, SOIC-16, and TFBGA-24.

i can't be sure of course but everything above 128Mbit seems to be SOIC-16 (including W25Q256FV above).
mouser for example now carries those: S25FL127S (Spansion), which seem to be pin compatible to the Winbond one. same thing though: 128Mb = SOIC-8; 256Mb/512Mb/1Gb = SOIC-16.
 
PJRC coud sell a shield with some of SOIC-16...
Or a SD- shield with a "co-processor" (ARM) with SDIO.. maybe this makes more sense
 
I ordered a couple of the Spansion S25FL127S part, for testing. At the moment, I believe I only have the Winbond ones.

Maybe a future board could have the larger 16 pin footprint. Or maybe someone like Onehorse will make an add-on board and sell it via Tindie?

Odds are good for larger sizes to eventually become available in SOIC-8 package. But as they move to smaller transistors for small dies, even larger ones are likely to appear on the market in those other packages.
 
I would buy such shields (ok, if shipping to germany is not too expensive, which is often a problem (from US) :-( )
 
I would buy such a board as well.

Currently I have hard times to find someone selling those chips in Germany, @Frank where do you have it from, Digikey?

I think the WSON8 chips can be soldered as well, at least i managed it some ages ago :)
 
for the time being, 16MB isn't so bad, i'd say. at least for wavetables, that means considerable more space and then there's still the SD card (which, given the output is stereo, will probably be sufficient for many purposes. polyphony is nice, of course, but often you'd really need/want individual outputs per voice (well, in my view)).

Or a SD- shield with a "co-processor" (ARM) with SDIO.. maybe this makes more sense

how fast is SDIO? .. or what is it for? wouldn't some parallel SRAM / FSMC combination (and teensy 3++, assuming it has enough pins) make more sense?

Currently I have hard times to find someone selling those chips in Germany

yep, those are difficult to get around here. i got mine from alibaba (and shipped to Germany in < 2 weeks (see Frank's post below))
 
Last edited:
how fast is SDIO? .. or what is it for? wouldn't some parallel SRAM / FSMC combination (and teensy 3++, assuming it has enough pins) make more sense?

I not really sure if "SDIO" is the correct name. SD-Cards have two interfaces: SPI and 4-Bit parallel. The second allows the maximum speed (several MB/s).

Im Mikrocontroller.net -forum gibts einen Thread dazu, die haben herausgefunden wie man diesen Modus in Software benutzen kann. Auf einem AVR. Vermutlich schafft man mit ARM ( und DMA?) mehr Speed... müsste mal jemand ausprobieren(Zaunpfahl). Aber ich habe zu viel mit anderen Projekten zu tun...
 
Last edited:
I not really sure if "SDIO" is the correct name. SD-Cards have two interfaces: SPI and 4-Bit parallel. The second allows the maximum speed (several MB/s).

ah, that thing. verstehe. bzw, i see. looks like trouble in an open source/DIY context though. (also Hold/IO3 is tied to 3v3 on the audio adapter)
 
ah, that thing. verstehe. bzw, i see. looks like trouble in an open source/DIY context though. (also Hold/IO3 is tied to 3v3 on the audio adapter)

Maybe not with concentrating on reading only.. (no CRC-code?)
Hmm..i think i have to buy another teensy (my existing are soldered & all pins used...)
Just out of curiosity how fast reading could be. In Assembler.
 
Last edited:
Hi,
Edit: Best speed should be at 120MHz, because of fastest SPI.

That's handy - my teensy crashes after a while if i try 144Mhz :) 120Mhz is my sweet spot for performance and stability.

Re: RAW, I'm using 44khz, 16bit mono RAW files exported straight from Audacity with no header, and it seems to be working "fine", I assume I'm losing a couple of bytes off the front, but it seems to be defaulting to playing correctly.
 
@Pensive: Luck... :)

The header is:
- 3 Byte Length Information (24 Bit)
- 1 Byte Format Information:
0x01: u-law encoded, 44100 Hz
0x02: u-law encoded, 22050 Hz
0x03: u-law encoded, 11025 Hz
0x81: 16 Bit PCM, 44100 Hz
0x82: 16 Bit PCM, 22050 Hz
0x83: 16 Bit PCM, 11025 Hz​
All Mono.
This is the same as Pauls format (used by wav2sketch), and his invention.
Wav2raw (as wav2sketch) converts stereo to mono.
 
Last edited:
That's pretty lucky, I have 16 of these samples which all play pretty well! It explains why a couple seem to cut off. None of them overshoot though. :)

Better use wav2raw :)
 
Status
Not open for further replies.
Back
Top