Yet Another File Player (and recorder)

NXP does appear to still be making the chip, but not in sufficient volume for PJRC to continue Teensy 3.2. We had large orders open for over 2 years but they could only ship small quantity. Why, we still don't know, though I kinda suspect production problems or lack of wafer capacity for this older 90nm silicon. Ultimately we had to give up and just discontinue Teensy 3.2.

If you're making a custom product using the Teensy 3.2 code (as I know @zachtos is) my advice is to plan ahead for NXP to have late deliveries and only partial if you're ordering large quantity.
Yes, we buy 5K+ at a time, and have had a hard time since before covid. It has not been easy to find. I would love a comparable audio solution from an abundant chip, but have not been able to I find anything better yet. I would gladly pay someone to get this on a st micro or esp32. We've tried, but Paul seems magical with nxp.
 
Yes, we buy 5K+ at a time, and have had a hard time since before covid. It has not been easy to find. I would love a comparable audio solution from an abundant chip, but have not been able to I find anything better yet. I would gladly pay someone to get this on a st micro or esp32. We've tried, but Paul seems magical with nxp.
Have you tried the MQSR/MQSL pins (10/12) on the Teensy 4.0 and 4.1? The problem is those pins are used by the primary SPI bus, but you could use the 2nd SPI if you have a display (note, on the Teensy 4.0, you need to solder wires for the 2nd SPI bus that are on the bottom of the Teensy). Of course if you are doing your own PCBs, you can route whatever pins you want.
 

h4yn0nnym0u5e:​

I have read this entire thread and have not found a solution for playing directly from the LittleFS file system. I also looked in your examples and did not find any examples of using the LittleFS instead of a SD card. Could you point me in the direction of an example for using LittleFS?

Thanks,
Ed
 
Hi Ed

I confess I've never tried any variant of LittleFS for anything! However ... the play() method is declared as
bool play(const char* filename, FS& fs = SD, bool paused = false, float startFrom = 0.0f)
Thus as standard it uses SD as its filesystem, starts immediately, and plays from the beginning of the named file. So if you do something like this:
C++:
#include <Audio.h>
#include <LittleFS.h>

AudioPlayWAVstereo playObj;
LittleFS_SPIFlash myLittleFS; // change to suit your LittleFS option

void setup()
{
  int chipSelect = 6;
  myLittleFS.begin(chipSelect);
}

void loop()
{
  if (!playObj.isPlaying())
    playObj.play("myAudio.wav", myLittleFS);
}
you may find it works. I've compiled it, but not tested it... I did try a sort of similar thing using the SD filesystems on both the audio adaptor and the built-in SD card, at the same time.

It's unlikely to be soon, but I can probably give this a try at some point if you can't get it to work based on the above fragment. If you can, please let us all know :giggle:
 
...of course, that code fragment absolutely can't work. No audio memory, no way of getting output ... the list is endless! But everything else is in other examples. I think...

EDIT: the closest to what you want is in the Audio library examples at Audio/examples/Buffered/AudioTestPlayMultiSD. That's the dual-SD example I referred to in the previous post, and shows the principle of playing from a different filesystem.
 
Last edited:

@h4yn0nnym0u5e:​

I created a bare bones project to try using your Audio system to play from QSPI Flash Memory using LittleFS, and it does not work. My code tells me that it is playing, but nothing is coming out of the audio system which is comprised of a PCM5102 with a 10w stereo amp. This exact same hardware setup works with FrankB's audio play wav code so the problem is not the hardware. I only use PlatformIO, as in my opinion the Arduino user interface is a toy made for children. It has almost no ability to help a professional Coder to develop and trouble shoot large programs. I have tried to attached to this message the entire project so you can have a look at it, but it is to large to attach to this post (38MB).
Any idea where or how to get it to you?
Regards,
Ed
 
I only use PlatformIO, as in my opinion the Arduino user interface is a toy made for children. It has almost no ability to help a professional Coder to develop and trouble shoot large programs.
I can understand and sympathise with your opinion, but it remains the fact that a lot of users use nothing but the Arduino IDE, and on this forum it is the canonical way of supplying a concise example of code that reproduces an issue. Often the very effort of stripping your code down to the bare minimum reveals the issue immediately, and if not, the example and solution remain on the forum for the edification of future readers :giggle:

In Real Life I am a professional programmer, and we don't use PlatformIO (VSCode gcc/Python/Flutter mainly these days, IAR in the past, and silicon vendors' IDEs when we have to, though they tend to be deeply nasty). But on occasion Arduino IDE gets used because it's the right tool for the job - assuming the job is quick and dirty!

I have tried to attached to this message the entire project so you can have a look at it, but it is to large to attach to this post (38MB). Any idea where or how to get it to you?
If you can provide a link to a DropBox or Google drive I could take a look (feel free to send a message via a Conversation if you're not comfortable posting the link in a public forum).

Can't promise I'll find anything if you truly have 38MB of code: that sounds like about 0.5M lines if they're of reasonable length. On the other hand, 38MB of project files sounds like unjustified bloat - a quick search has failed to reveal how much space I'd need to install PlatformIO just in order to look at this one issue ... which probably means the answer is "a lot".
 
How about this compressed file of just the Main.cpp, I am sure you can figure out the rest.

About the IDE, I also have used many different IDE systems over the past 5 decades, and you are correct many of them had steep learning curves. I have been retired now for more than 10 years and cannot afford to pay the ridiculous prices these companies want for their systems, and I use a Mac and have been using Macs since 1982. These facts limit my choices considerable.
Regards,
Ed
 

Attachments

  • main.cpp.zip
    16.1 KB · Views: 44

@h4yn0nnym0u5e: said: a quick search has failed to reveal how much space I'd need to install PlatformIO just in order to look at this one issue ... which probably means the answer is "a lot".​

Just for example on my Mac:
Xcode.app uses: 23.41GB
Wireshark uses: 232.1 MB
WhatsApp uses: 328 MB
VMware Fusion uses: 1.26GB
Visual Studio Code.app uses: 567.5MB
Visual Studio.app: uses: 1.63GB

No Platform IO is not very big compared to a few other apps I have installed on my Mac (276).

Regards,
Ed
 
I'll take a look.

In the mean time, here's a sketch I adapted to use SD+LittleFS rather than two SD cards. I haven't got a QSPI flash so it uses a LittleFS RAM disc and copies an audio file to it from the SD card during setup(). Assuming the filesystem abstraction works as intended, the same principle should be fine for your use case. Also, I have an audio adaptor set to the upper I²C address, not your PCM5102; again, I can't imagine that's important to this issue.

Apologies, there's a certain amount of performance-measuring cruft left in:
C++:
// Play back sounds from SD card and LittleFS
// Tested on Teensy 4.1, changes may be needed for 3.x

#include <SD.h>
#include <Bounce.h>
#include <Audio.h>
#include <LittleFS.h>

SDClass sd1; // actually an SD card

// Use a LittleFS RAM disc, as I don't have a QSPI Flash:
EXTMEM char litlPSRAM[1024 * 1024]; // allocate 1MB of PSRAM for LittleFS
LittleFS_RAM sd2; // actually LittleFS - leave the name to minimise changes to code

// GUItool: begin automatically generated code
AudioInputI2S            i2s2;           //xy=103,82
AudioAnalyzePeak         peak1;          //xy=276,127
AudioRecordQueue         queue1;         //xy=279,82
AudioPlayWAVstereo       playRaw1;       //xy=296,179
AudioPlayWAVstereo       playRaw2;       //xy=305,222
AudioAmplifier           amp1;           //xy=476,173
AudioAmplifier           amp2;  //xy=481,218
AudioOutputI2S           i2s1;           //xy=661,194

AudioConnection          patchCord1(i2s2, 0, queue1, 0);
AudioConnection          patchCord2(i2s2, 0, peak1, 0);
AudioConnection          patchCord3(playRaw1, 0, amp1, 0);
AudioConnection          patchCord4(playRaw2, 0, amp2, 0);
AudioConnection          patchCord5(amp1, 0, i2s1, 0);
AudioConnection          patchCord6(amp2, 0, i2s1, 1);

AudioControlSGTL5000     sgtl5000_1;     //xy=279,306
// GUItool: end automatically generated code


// Bounce objects to easily and reliably read the buttons
Bounce buttonPlay1 =   Bounce(30, 8);
Bounce buttonPlay2 =   Bounce(31, 8);

//==================================================================
bool FileCopy(FS& fsSrc,const char* fnSrc, FS& fsDst, const char* fnDst)
{
  File src,dst;
  bool success = false;

  src = fsSrc.open(fnSrc,FILE_READ);
  if (src)
  {
    fsDst.remove(fnDst);
    dst = fsDst.open(fnDst,FILE_WRITE);
    if (dst)
    {
      char buf[1024];
      int got;

      do
      {
        got = src.read(buf, sizeof buf);
        if (got > 0)
          dst.write(buf,got);
      } while (got > 0);
      dst.close();
      success = true;
    }
    src.close();
  }

  return success;
}

//==================================================================
const char* copiedFile = "sine330.wav";

void setup() {

  // Configure the pushbutton pins
  pinMode(30, INPUT_PULLUP);
  pinMode(31, INPUT_PULLUP);

  // Audio connections require memory
  AudioMemory(10);

  while (!Serial)
    ;
  Serial.println("Started!");

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.setAddress(HIGH);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.2);

  playRaw1.createBuffer(2048,AudioBuffer::inHeap);
  playRaw2.createBuffer(4096,AudioBuffer::inHeap);

  amp1.gain(0.5f);
  amp2.gain(0.5f);

  // initialize the internal Teensy 4.1 SD card
  if (!sd1.begin(BUILTIN_SDCARD))
    Serial.println("error sd1.begin");

  // initialize the LittleFS filesystem
  if (!sd2.begin(litlPSRAM, sizeof litlPSRAM ))
    Serial.println("error sd2.begin");

  // copy an audio file from SD to LittleFS
  if (FileCopy(sd1,"sine330.wav", sd2,copiedFile))
    Serial.printf("%s copied OK\n", copiedFile);
  else
    Serial.printf("Couldn't copy %s\n", copiedFile);

  queue1.begin();
}


//==================================================================
volatile uint32_t audio_update_count;
void audio_timer_early_hook(void)
{
  audio_update_count++;
}

//==================================================================
void printInstrument(AudioPlayWAVstereo& o, const char* nam)
{
  Serial.printf("%s: low-water: %u; worst read time: %uus; updates: %u; update count %lu\n",
                nam,
                o.bufferAvail.getLowest(),
                o.readMicros.getHighest(),
                o.bufferAvail.getUpdates(),
                audio_update_count);
  o.bufferAvail.reset();
  o.readMicros.reset();
}


//==================================================================
void loop() {
  buttonPlay1.update();
  buttonPlay2.update();

  if (buttonPlay1.fallingEdge()) {
    Serial.println("Play 1");
    playRaw1.play("sine220.wav", sd1);
  }
  if (buttonPlay2.fallingEdge()) {
    Serial.println("Play 2");
    playRaw2.play(copiedFile, sd2);
  }

  static uint32_t next = 0;
  if (millis() >= next)
  {
    next = millis() + 250;
    
    if (playRaw1.isPlaying())
      printInstrument(playRaw1,"playRaw1");
    
    if (playRaw2.isPlaying())
      printInstrument(playRaw2,"playRaw2");
  }

  // ideally this would be supported by the Audio library,
  // but for now we hack our own audio update count
  if (queue1.available())
  {
    queue1.readBuffer();
    queue1.freeBuffer();

    audio_timer_early_hook();
  }
}
 

h4yn0nnym0u5e:​

Actually this is remarkably similar to mine in the way it works, first initialize the SD card, then initialize the LittleFS Ram (in my case LittleFSFlash), then copy the file from the SD to the LittleFS file. In my case there are 8 files that make up clock sounds, (Tick, Tock, Bong etc). Then play the file(s). When my code uses the playWAVfile command it does not return any error but nothing comes out of the speaker. If you have a look at my code you will notice the similarities.

Regards,
Ed
 
I've had a look, and can't immediately see what the problem is.

I'm always a bit nervous when there's lots of duplicate code and strings, and more so when the duplicates aren't quite so - like your SD directory being CLKSNDS but LittleFS uses CLKSNDSS, and the latter is in there no fewer than 8 times, and the filenames are all in there twice. But ... in the time available, I can't actually see a place that's caused the problems. I have to assume you've turned on all the debug and you're 100% sure that all files are exactly where you expect them to be!

I can see you use MTP, which I know can interfere with audio, but as you delay() the loop() while the sounds play, I don't think that can be the cause.

A really quick test would be to switch temporarily to playing your sounds back direct from the SD card files (you'd have to change the directory name, of course :giggle:). If that works, then it points the finger at LittleFS itself, and in particular the QSPI option, as I've had the (PS)RAM one working.

You could also try using the RAM disc option, as you're only currently using two quite short sounds so they should fit in a big array, or the heap.

As an aside, I don't believe you need 8 playback objects - at most you're only going to be playing two sounds at once, the first being either Tick or Tock, and the second being Chimes or a Bong. So two objects are enough. Unless I've missed something, which is entirely possible.

It might be worth your zipping up and posting one or two of your audio files - it's possible my WAV header parser is more picky than Frank's.

I can't immediately see which Teensy and Teensyduino you're using - did you say?
 
I am using the Teensy4.1. The version of Teensyduino that is currently be used by PlatformIO is 1.59.
Here are the two files Tick and Tock
 

Attachments

  • Tick&Tock.zip
    16.1 KB · Views: 43
Thanks for those. I've plugged them into the above sketch, using file paths like yours, and they play without issue from SD or LittleFS_RAM. I also tried heap instead of PSRAM, which worked as expected. So that's good, one possibility knocked off.

I've ordered some QSPI flash parts, but it'll be some time before I can test them due to other commitments. If you could bring yourself to install the Arduino IDE just temporarily, and try my example using LittleFS_RAM in heap, then on your QSPI Flash, that could be revealing. If both work, then PlatformIO is doing something; if RAM works but not QSPI, that points the finger at LittleFS_QSPIFlash. If nothing works, not even SD card playback, I'll be very surprised :). Obviously you'd have to change the file paths to suit your setup as it is.

One thing that did occur to me. The buffered playback requires EventResponder to be available and working; I think you'd get compilation errors if it's not available (as it ought to be, as part of Teensyduino 1.59), so the only way it could then fail is if PlatformIO does something weird with yield(), like have its own copy for some reason. I'm clutching at straws here...
 
H4yn:
I have attached to this post similar code that uses FrankB's Wave Player code which is kind-of old but it does work just fine using QSPI_Flash. If you have a moment check it out, as it is not very complicated.

Regards,
Ed
 

Attachments

  • main.cpp
    106.8 KB · Views: 490
I've just spotted a nasty in both of your main.cpp files, may or may not be relevant. At the end of the copy to Flash you flush the files, and then close the RAM data file. Yup, the RAM file, not the Flash one... That's actually in two places, because you have multiple early return points, a practice I have to say I heartily despise ... it looks as if you actually return leaving both files open if the source file is of zero length 🤬
 
I have made changes to the copy files from SD to Flash code to close any open files for all the error return points as you have suggested, (I think this could have been written a little more elegantly as you so apply have described it), however making these changes nothing. It still thinks it is playing the file(s) and there is no sound coming from the speakers.
However the other program using the Wave Player code Tick and Tock and Chimes just fine, which proves the problem does not lie with LittleFS_Flash system. It must be something else not related to LittleFS.
I would like to use your Audio lib instead of FrankB's because it much more up to date, but it simply does not play nice with LittleFS_Flash so I will continue to use FrankB's Wave Player.

Thanks for your support!
Ed
 
Well ... 517 lines to do a file copy did seem a tad extreme ... compared to my 34 :giggle: but not relevant to the discussion at hand ... and mine's only for a quick demo.

Yes, it's maybe not worth pursuing my library for your use, there's only so many hours in the day. I'll report back here for posterity when I've had a chance to try out the Flash parts I've ordered. And who knows, it's possible someone will chime in with "oh yes, I used it with PlatformIO and found I had to <do stuff> to make it work".

All the best.
 
I've just pushed an update on a new branch - would be very grateful to anyone who can spare the time to try it out. There should be no changes needed to any sketches you already have working and stable.

However, it's come to my notice that there were some serious issues with filesystem access during playback / recording, due to yield() calls being rather more liberally scattered about inside libraries than I'd understood. There are a couple of options provided to deal with this: you can either:
  1. wrap your SD (or other filesystem) accesses in calls to AudioEventResponder::disableResponse() and AudioEventResponder::enableResponse(). All yield() processing of pending events is masked, so keep these as short as feasible. It's OK to leave a file open and responses unmasked while you do other stuff, provided you re-mask before a read, write, close etc.
  2. Call AudioEventResponder::setForceResponse(true) in setup() to signal that your code will generate responses to EventResponder triggers generated by the buffered playback / record objects. In this case you must also call AudioEventResponder::runPolled() from your code, sufficiently often to ensure triggered events get their responses in a timely manner.
Note that AudioEventResponder::runPolled() only executes one triggered event, and it's quite possible more than that will be pending: if you get a return value > 0 then more remain - it's up to you to decide when to execute the next one. If you've buffered plenty of audio data then you have more flexibility in that decision.
 
i don't know how to download with your PR update,if i select audio and download code,the buffered examples doesn't appears
 
You may need to re-start the Arduino IDE once you've downloaded the above link to your libraries - then the buffered examples should show in the "Examples from Custom Libraries" section:
1728139289837.png
 
Sorry,i don't see the Buffered folder,it goes from Analysis -> dynamics directly and i don't see it neither in the windows explorer,
please explain me what to do when i am at this page:
i think the download of the PR is not the same as the regular library download process,i never did this before.
EDIT:I just see the examples were bound for SGTL5000 -> audioboard,but do they work for TDM?
 
It's not yet a Pull Request, just one of my many branches. But ... assuming you don't want to use git, you would:
  • download a ZIP of the branch:
    1728142064507.png


  • extract the files:
    1728142578656.png


  • giving you:
    1728142704945.png


  • then rename it:
    1728142786197.png

All being well, it will now be picked up instead of the version included with Teensyduino - you can see that in the verbose compilation output:
1728143128430.png


This will effectively replace any other updates you're using - if you want to use multiple updates at once then you probably need to learn to use git, and figure out how to merge the various branches into one of your own...
 
Back
Top