Yet Another File Player (and recorder)

yes i got it now,but i know how to use git ,but i tried to git clone:
git clone https://github.com/h4yn0nnym0u5e/Audio/tree/feature/buffered-SD-audio-EvR
And i have an error :
fatal: repository 'https://github.com/h4yn0nnym0u5e/Audio/tree/feature/buffered-SD-audio-EvR/' not found
Now it's okay,another question,is it only for SGTL5000 audioboard because of the inside sd card?
EDIT: i didn't know you have to go on the branch address and then download Zip,the first time i do that,
excuse me for my leak of practice :oops:
 
Last edited:
Ah, OK, never sure who knows what around here!

You don't clone a branch, it has to be the whole repo, so something like
  • git clone https://github.com/h4yn0nnym0u5e/Audio.git, then
  • git switch feature/buffered-SD-audio-EvR
No, it's intended to work with any filesystem, though most calls default to using SD. Which card slot SD is using is defined at the time you call SD.begin(); you can in fact use both the audio adaptor and the built-in SD slots at the same time - see the AudioTestPlayMultiSD.ino example.
 
git clone https://github.com/h4yn0nnym0u5e/Audio.git git switch feature/buffered-SD-audio-EvR
These are two separate commands, as shown in post # 202. The first clones the git repo into the current folder; the second then switches to the required branch. I generally clone directly into the libraries folder; I can either switch back to the master branch or move the whole clone into a libraries-unused folder if I need to have a different version in use.
1728201280657.png


I myself am confused by what you mean by "do i git at the same entry" o_O ... but maybe I've given you enough clues :giggle:
 
LOL,i was talking about the bash command monitor,with the two commands in indian file...
But what you show me,is really magic,how can you explain that, the buffered folder was not hiden in audio i think,
is it another download to concenate the buffered folder with the audio one?
I want to know! :sick::ninja:
 
Well i tried Buffered examples and had an error with SDpiano:
Code:
SDpiano:142: error: 'inExt' is not a member of 'AudioBuffer'
  142 |     pv[i].playWAVstereo1.createBuffer(32768,AudioBuffer::inExt);
      |                                                          ^~~~~
 
Well I don't know about Indian file, or how the bash command monitor might handle it, but there are two commands, and you should run one, then when the prompt reappears, run the next one. Typing them both at the same prompt one after another is very unlikely to work...

What we're trying to install here is a copy of my fork of Paul's entire Audio library, in the form of a git clone on your PC, in your project libraries folder - it will replace the Teensyduino one. Then switch to the feature/buffered-SD-audio-EvR branch, which will introduce all my buffered audio stuff; 6 new files, edits to another 3, and a folder full of examples.

The error message you show suggests this hasn't worked properly; I'd also be surprised if it's the only error, unless there's a clash with another library which defines an AudioBuffer namespace or class. If you turn on verbose compiler output then we can see if it's picking up the library where you put it...

...actually, there is one other way that can happen, and that's if you're not compiling for Teensy 4.1. The SDpiano requires PSRAM as it has to partially pre-load 261 samples ready for low-latency playback.
 
That's right,i'm using teensy 4.0 & audioboard, i should have bewared of the sketch using PSRAM,
switch to teensy 4.1 for this one.
But i've readen somewhere one can solder a PSRAM on the audioboard,is that true?
 
You can, but it’s not really suitable as a low latency buffer because it’s only 1 bit wide at 20MHz, whereas PSRAM is 4 bits at 88MHz.
 
That's right,i'm using teensy 4.0 & audioboard, i should have bewared of the sketch using PSRAM,
switch to teensy 4.1 for this one.
But i've readen somewhere one can solder a PSRAM on the audioboard,is that true?

You can, but it’s not really suitable as a low latency buffer because it’s only 1 bit wide at 20MHz, whereas PSRAM is 4 bits at 88MHz.

IIRC, the SRAM/flash on the audio board can only be accessed via SPI read/write operations. The PSRAM soldered underneath the Teensy 4.1 acts like normal memory, i.e. you can pass a pointer to an area in the PSRAM to some other function. With the SRAM/flash on the audio board, you have to copy the contents to local memory before you can use it.
 
This is true, though it doesn't preclude using that memory in audio applications: it's what's used for AudioEffectDelayExternal and AudioPlaySerialflashRaw. All the copying of data to / from SPI memory is done within the library, so the user doesn't need to concern themselves with it, apart of course from initially loading files to the SerialFlash filesystem, if that's what they're using.

However, it's not a great choice for audio purposes (due to being so slow), so I didn't implement it as an option for pre-loaded samples or buffer memory in this library extension.
 
IIRC, the SRAM/flash on the audio board can only be accessed via SPI read/write operations. The PSRAM soldered underneath the Teensy 4.1 acts like normal memory, i.e. you can pass a pointer to an area in the PSRAM to some other function. With the SRAM/flash on the audio board, you have to copy the contents to local memory before you can use it.
Hi Michael,do you mean i can place in memory all the functions i created and called them back as i want?
Do you have an example for this,because PSRAM utilisations examples are very rare.
 
Hi Michael,do you mean i can place in memory all the functions i created and called them back as i want?
Do you have an example for this,because PSRAM utilisations examples are very rare.
Generally, you can't put functions into PSRAM because it is not initialized from flash memory like the normal 2 memory banks are. I imagine if you are really dedicated, you could create functions that could be copied to PSRAM, adjust any fixed non-pc relative addresses, and then figure out how to enable execute permission on the memory and i-cache support. Perhaps if you had something that created functions on the fly, it would make sense to use PSRAM to hold functions.

Note, PSRAM is slower than the 2 memory banks that are part of the standard Teensy, though the most recently used blocks of memory will be cached. Typically, PSRAM is used for larger data buffers for things that won't fit into normal memory. I could imagine some of the audio or display applications can use the larger memories provided by the 1-2 PSRAM buffers.

The Teensy 4.1 product page (https://www.pjrc.com/store/teensy41.html) lists the memories as:
  • RAM1 (512 kilobytes): tightly coupled for fastest performance; 'FASTRUN' functions are copied here from flash memory.
  • RAM2 (512 kilobytes): Slightly slower memory, optimized for DMA usage. The 'new/malloc' functions allocate from RAM2. You can put static/global variables into RAM2 using 'DMAMEM'.
  • Flash (8 megabytes): Holds the initial code/data that is copied at program startup to RAM1/RAM2. Holds constant data (declared with 'const'), as well as data declared with 'PROGMEM' and functions declared with 'FLASHMEM'. The top of the flash memory holds the space used for emulating 'EEPROM' variables. The unused area of the flash memory can be accessed via LittleFS via open, read, write, and close.
  • PSRAM1 (8 megabytes): Holds the first PSRAM memory chip soldered on to the Teensy 4.1. PSRAM memories are not initialized at program startup.
  • PSRAM2 (8 megabytes) or certain flash memories (up to 256 megabytes). If a second PSRAM2 chip is soldered on, it acts similar to the first PSRAM chip. If a flash memory chip is soldered on, the flash memory will hold the contents that were previously written to. Typically, external flash memory is used by LittleFS to hold a file system, and you access it like a SD card, i.e. open up a file, use read/write, and then close of the file.

For example, if you declare static/global arrays as 'EXTMEM' they are allocated in the PSRAM memories. The EXTMEM arrays are not initialized, so you must explicitly clear or fill them them with data.

You can also use the 'extmem_malloc' function to dynamically allocate PSRAM memory at runtime. If there is no PSRAM memory, it will allocate memory from the 2nd memory bank like 'malloc' does.

Here is a test PaulS wrote to test PSRAM that I've used in the past to verify that I soldered on the PSRAM1 chip correctly.

The only way I've used PSRAM is via the MTP sketch that presents the various file systems as MTP devices that I can access from my Linux system via its file explorer program. This sketch will access the flash memory chip soldered on the audio adapter along with the flash memory chip on the old prop. shield.
 

Attachments

  • MtpPSram.ino
    11.6 KB · Views: 39
Last edited:
Hi, Michael,sorry for the delay,many things to do in my life,
Thank you for bringing some lightning on this subject i find complexe
i'm testing so much things so that i have too much questions to ask ,i prefer to let them be delayed and matured :unsure:
(y)
 

@h4yn0nnym0u5e:​

Hi! I'm working with your feature/buffered-SD-for-PR branch. I'm using a teensy 4.0 to run a AudioPlayWAVstereo playing files from the SD card, and I'm having an issue. It seems that when I play a file the buffer doesn't refill after its initial fill. I could get more or less of the beginning of a file to play by calling createBuffer with different buffer sizes, but then it just stops and doesn't continue to play from the filesystem. I've tried this with both directly playing files by name, and by using AudioPreload objects (which are awesome btw) with different buffer sizes to the same effect. Is this intentional behavior? I'm not using any other external memory, and my wav files are megabytes in size so loading them all fully into memory might not be an option for me.
 
Should work. Please post a minimal but complete sketch, using the </> code tags to preserve formatting, that demonstrates the issue you’re having, and I’ll take a look.

I assume you’re using an audio adaptor and its built in SD card slot.
 
Should work. Please post a minimal but complete sketch, using the </> code tags to preserve formatting, that demonstrates the issue you’re having, and I’ll take a look.

I assume you’re using an audio adaptor and its built in SD card slot.
Right, I'm loading files from the offboard SD card slot on the AudioShield Rev D for Teensy 4.X
I've commented as much of my findings here as I reasonably could, but given the nature of SD cards irregularities and wav file differences, you may need to play around with some of the buffer sizes to fully reproduce the issues I'm seeing. I've at least attached a zip containing the 1.2MB "chime.wav" file I'm testing with if that helps.

My actual issue is in a much larger program that is exhibiting effect #2 in the comments here. The length of the beginning chunk of the wav file that gets played is directly tied to the buffer size in that case, so a buffer of 65536 bytes plays twice as much of the wav file as a buffer of 32768 bytes before the player advertises isPlaying() == false. I've also tried adding an extra 25ms buffer before replaying to avoid to classic issue with AudioPlaySdWav not reporting isPlaying() == true right away, so that's not the problem.

C++:
#include <Arduino.h>
#include <Audio.h>

AudioPreload preload;
AudioPlayWAVstereo player;
AudioOutputI2S i2s_out;
AudioControlSGTL5000 sgtl5000;
AudioConnection connections[]{{player, 0, i2s_out, 0}, {player, 1, i2s_out, 1}};

void setup() {
  Serial.begin(115200);

  // Initialize SD reader
  SPI.setMOSI(7);
  SPI.setSCK(14);
  while (!SD.begin(10)) {
    Serial.println("Unable to access the SD card! Retrying...");
    delay(1000);
  }

  // Initialize Audio stuff
  AudioMemory(16);

  sgtl5000.enable();
  sgtl5000.volume(0.5f);

  /**
   * Allocate buffers for the AudioPreload and the AudioPlayWAVstereo
   *
   * I'm noticing some weirdness with different buffer sizes that cause a
   * combination of 3 different effects:
   * 1) Audio output "buzzes", probably due to constantly replaying the file
   * 2) wav file play gets truncated, and the file replays intermittently after
   * less than a second
   * 3) Teensy crashes and reboots after a while, usually only when one of the
   * first two issues is encountered
   *
   * Note: The buffer sizes here were manually adjusted until things worked (or
   * didnt) I originally tested with an SD card with 52 files on it. Replacing
   * that SD card with one that has a single wav file on it greatly reduced
   * these buffer size threshold values. To me that indicates that the issue is
   * somehow linked with the time it takes the SD reader to find and open the
   * file.
   *
   * AudioPreload min buffer size was originally found to be 1329 bytes
   * AudioPlayWAVstereo min buffer size was originally found to be 1248 bytes
   *
   * Further testing shows that values above this minimum threshold is somewhat
   * inconsistent, and changes with whether or not we're using an
   * AudioPreloader. When we're using minimally sized buffers, it seems like
   * odd-sized buffers have more problems than even-sized ones, although
   * sometimes it seems that even sufficiently large odd-sized buffers are
   * problematic as well.
   *
   * Also it seems like these same effects can be repeated with larger buffer
   * sizes too (see line 108).
   */

  /**
   * Preload our file
   */

  // Works fine but seems to crash sometimes, intermittently
  //   preload.preLoad("chime.wav", MemBuffer::inHeap, 810);

  // Seems to work fine
  preload.preLoad("chime.wav", MemBuffer::inHeap, 1024);
  //   preload.preLoad("chime.wav", MemBuffer::inHeap, 1023);
  //   preload.preLoad("chime.wav", MemBuffer::inHeap, 900);

  // These cause the audio output to sort of "buzz" (eventually crash sometimes)
  //   preload.preLoad("chime.wav", MemBuffer::inHeap, 809);
  //   preload.preLoad("chime.wav", MemBuffer::inHeap, 808);

  /**
   * Create wav player buffer
   * WITHOUT PRELOADER (playing from filename only)
   */

  // Just right (but may ocassionally crash)
  //   player.createBuffer(1248, MemBuffer::inHeap);
  //   player.createBuffer(1250, MemBuffer::inHeap);
  //   player.createBuffer(1252, MemBuffer::inHeap);

  // These cause the audio output to sort of "buzz" (eventually crash sometimes)
  //   player.createBuffer(1246, MemBuffer::inHeap);
  //   player.createBuffer(1247, MemBuffer::inHeap);
  //   player.createBuffer(1249, MemBuffer::inHeap);
  //   player.createBuffer(1251, MemBuffer::inHeap);

  /**
   * Create wav player buffer
   * WITH PRELOADER
   */

  // Just right
  //   player.createBuffer(1024, MemBuffer::inHeap);
  //   player.createBuffer(2024, MemBuffer::inHeap);
  //   player.createBuffer(2026, MemBuffer::inHeap);

  // These cause the audio output to sort of "buzz" (eventually crash sometimes)
  //   player.createBuffer(1023, MemBuffer::inHeap);
  //   player.createBuffer(1025, MemBuffer::inHeap);
  //   player.createBuffer(1225, MemBuffer::inHeap);

  // These cause the file to replay ~ once/sec (eventually crash sometimes)
  player.createBuffer(1022, MemBuffer::inHeap);
  //   player.createBuffer(2025, MemBuffer::inHeap);
}

void loop() {
  // Repeat the file when it's done playing
  // NOTE: with small enough buffers, this causes the file to constantly replay
  // from the beginning. I can't say if this is EVERY time through loop, but
  // it's enough to produce a steady buzz instead of playing the whole file.
  // Other times we just get the first chunk of the file repeating once or twice
  // every second.
  if (!player.isPlaying()) {
    // This can be reproduced with either preload or directly passing filename
    player.play(preload);
    // player.play("chime.wav");
  }
}
 

Attachments

  • chime.zip
    523.6 KB · Views: 28
Last edited:
I think you just need to allocate big enough, sensibly-sized buffers. The following works for me:
C++:
#include <Audio.h>

AudioPreload preload;
AudioPlayWAVstereo player;
AudioOutputI2S i2s_out;
AudioControlSGTL5000 sgtl5000;
AudioConnection connections[]{{player, 0, i2s_out, 0}, {player, 1, i2s_out, 1}};

void setup() {
  Serial.begin(115200);

  // Initialize SD reader
  while (!SD.begin(10)) {
    Serial.println("Unable to access the SD card! Retrying...");
    delay(1000);
  }

  AudioMemory(16);

  sgtl5000.enable();
  sgtl5000.volume(0.5f);
 
  preload.preLoad("chime.wav", MemBuffer::inHeap, 2560); // Seems to work fine
    
  player.createBuffer(4096, MemBuffer::inHeap); // multiple of 512 (SD sector size)
}

void loop() {
  // Repeat the file when it's done playing
  if (!player.isPlaying()) {
    // This can be reproduced with either preload or directly passing filename
    //player.play(preload);
    player.play("chime.wav");
  }
}
Most of my testing has been done using a Teensy 4.1 and its built-in SD slot; using that I can squeeze the buffer sizes down a bit, but the above values seem to be what's needed when using the slower audio adaptor slot. Either way, it's highly advised (i.e. I never tested it any other way...) to use buffers which are a multiple of 512 bytes, which is an SD sector size and hence more efficient when the buffer is re-filled.
 
I think you just need to allocate big enough, sensibly-sized buffers. The following works for me:
C++:
#include <Audio.h>

AudioPreload preload;
AudioPlayWAVstereo player;
AudioOutputI2S i2s_out;
AudioControlSGTL5000 sgtl5000;
AudioConnection connections[]{{player, 0, i2s_out, 0}, {player, 1, i2s_out, 1}};

void setup() {
  Serial.begin(115200);

  // Initialize SD reader
  while (!SD.begin(10)) {
    Serial.println("Unable to access the SD card! Retrying...");
    delay(1000);
  }

  AudioMemory(16);

  sgtl5000.enable();
  sgtl5000.volume(0.5f);
 
  preload.preLoad("chime.wav", MemBuffer::inHeap, 2560); // Seems to work fine
   
  player.createBuffer(4096, MemBuffer::inHeap); // multiple of 512 (SD sector size)
}

void loop() {
  // Repeat the file when it's done playing
  if (!player.isPlaying()) {
    // This can be reproduced with either preload or directly passing filename
    //player.play(preload);
    player.play("chime.wav");
  }
}
Most of my testing has been done using a Teensy 4.1 and its built-in SD slot; using that I can squeeze the buffer sizes down a bit, but the above values seem to be what's needed when using the slower audio adaptor slot. Either way, it's highly advised (i.e. I never tested it any other way...) to use buffers which are a multiple of 512 bytes, which is an SD sector size and hence more efficient when the buffer is re-filled.
Oh I see! The 512 byte multiple is a nice tip - I mostly just assumed it should be aligned to a word boundary but didn't see any actual restrictions on size. My main project is also intended to run on a Teensy 4.1 with the built-in SD reader as well, so I'll test there and hopefully won't have as much of a bottleneck. In the mean time, I've allocated buffers in powers of two up to 524288 bytes getting more and more of the file to play. At that point I realized I'm just loading the file into heap memory and doing less of a true buffer operation ¯\_(ツ)_/¯
I'll report back when I have more info, thanks for your help looking into this!
 
Thanks h4yn0nnym0u5e for your job, as Mattkuebrich i work on a audio multitrack player, im a noob in electronic but i start to understand more and more, day after day i read datasheet and forum.

I have few more questions, do you think Teensy 4.1, can :
- play 16 wav tracks on a USB key
- transmit them on TDM16 to a DAC
- control the DAC with I2C or I2S (i don't know :)
- display a little UI on a led/crystal screen for manage the DAC (Mute, and level)
- cherry on cake, use all the system like a soundcard (connected to a computer)

I would like to do a remplacement system of the Cymatic LP 16 but i dont need Midi.

thanks in advance
 
It's a fairly steep learning curve! My advice is always to try to get one element at a time working, there are often unforeseen interactions if you try to do everything at once... Google is often better at finding relevant posts than the forum search, but do check post dates as some threads can be quite out of date. If you use "known technology" then Teensyduino has a wealth of examples to try. There's a tutorial on the PJRC website that helps you get going with the audio library.
  • not sure about playing from a USB drive (presumably using the Teensy 4.1 Host driver). There's no obvious reason why not, though it's probably slower than using an SD card in the 4.1's on-board SDIO slot
  • TDM transmit to DAC, absolutely, though there aren't many off-the-shelf options available. But you can grab an 8-channel one from AliExpress for not much outlay, and there are threads here and here on how to get set up
  • control of the DAC is usually I²C (I²S is a high-speed audio protocol, e.g. supported by the PJRC audio adaptor). The audio library has many control objects that insulate you from having to know details of the setup for supported devices
  • UI on an LCD, yes. Driver support varies; again, better to Google a bit, and get a well-supported simple one to begin with
  • Making the Teensy appear as an audio interface to the PC via USB is built-in to Teensyduino, for 2i2o. There's experimental work going on trying to achieve 8i8o. It seems to work for most who've tried it, though you'll see one user seems to have intractable problems using it on his Mac
It probably makes most sense for a self-described n00b to get a starter kit of all-PJRC products; Teensy, audio adaptor, LCD and USB host cable. Example code and helpful forum threads should be plentiful, and unless you manage to do something spectacularly daft you're unlikely to encounter a problem that hasn't been seen before :)
 
Allright, that many informations thanks again. i agree that like start climbing on Everest Mount 😅 but i like to do in this way. My goal is connect a Adau1966 A (DAC with 16 Analog Output) to the Teesy and play loop on a usb key. With your informations i understand my project can work in this configuration but i have to dig a lot about electronic and coddig for make a working prototype. I hope i add some news here if i done my project. See you
 
Back
Top