Now for MP3 with FS...

wwatson

Well-known member
@FrankB - Your Teensy-WavePlayer works absolutely great with all SD, MSC and LFS devices I have used it with. That is all of them:)

I have not delved into MP3 yet but I want to. What is the status of this with all of the different filesystems to date. Is it something possible?

Thanks...
 
@FrankB - Your Teensy-WavePlayer works absolutely great with all SD, MSC and LFS devices I have used it with. That is all of them:)

I have not delved into MP3 yet but I want to. What is the status of this with all of the different filesystems to date. Is it something possible?

Thanks...

It can play from SD, from serial flash (uses the serial flash library) and from Teensy-Memory (give it a pointer).
No work has been done to add LittlFS so far.

I currently only use the ESP32.
The codecs run fantastic on it too (a ported version), currently I'm at ~10% CPU load for a radio MP3 stream (128kbps Edit: 12% for 320kbps..) for everything (so incl. wifi connection, buffering, and a basic audiomixer. It uses floats, not short).
Plenty left for additional goodies.. Edit Also, it is (better: will be) much easier to play several mp3 simultanously. And perhaps I'll port some of chip`s float-audio-lib parts and use the esp dsp lib/features...
 
Last edited:
It can play from SD, from serial flash (uses the serial flash library) and from Teensy-Memory (give it a pointer).
No work has been done to add LittlFS so far.

I currently only use the ESP32.
The codecs run fantastic on it too (a ported version), currently I'm at ~10% CPU load for a radio MP3 stream (128kbps Edit: 12% for 320kbps..) for everything (so incl. wifi connection, buffering, and a basic audiomixer. It uses floats, not short).
Plenty left for additional goodies.. Edit Also, it is (better: will be) much easier to play several mp3 simultanously. And perhaps I'll port some of chip`s float-audio-lib parts and use the esp dsp lib/features...

With a little tweaking to codec.h and Mp3FilePlayer.ino I can play MP3 files from LittleFS devices. I was able to play them from the QPINand, SPINand (CS 5 and 6), SPIFlash (CS 3 and 4). I do not have QSPIFlash on the T4.1 I was using so have not tested that one yet but I'm sure it will. It was a chop job on codec.h for now. Once I test MSC devices I can figure out a better way to implement it.

Works well:)
 
With a little tweaking to codec.h and Mp3FilePlayer.ino I can play MP3 files from LittleFS devices. I was able to play them from the QPINand, SPINand (CS 5 and 6), SPIFlash (CS 3 and 4). I do not have QSPIFlash on the T4.1 I was using so have not tested that one yet but I'm sure it will. It was a chop job on codec.h for now. Once I test MSC devices I can figure out a better way to implement it.

Works well:)

@wwatson - you should post your changes for LittleFS and/or issue a PR when you get done should be fun to play with. I have used the Library in the past and it works great.
 
@wwatson - you should post your changes for LittleFS and/or issue a PR when you get done should be fun to play with. I have used the Library in the past and it works great.

Mike I just now got MSC working with it:) Everything is now working are all working through FS. I now can setup a sketch(s) to demonstrate all of the filesystems using MP3 files. I will put my version on GitHub. I still am having issues with msFilesystem and myusb.task(). Will figure that out. It will not repeat the play in loop();

At least it is playing MP3 files...
 
@all who want to try this out - I have setup a repo for playing MP3 files with LittleFS using @FrankB's Arduino-Teensy-Codec-lib. In this I have added a sketch to play MP3 files from LittleFS devices. Five files were modified to do this. This is using Arduino 1.8.16 and TD1.56B3. LittleFS devices that I have tested are listed in the sketch Mp3FilePlayerLFS.ino. There are devices not tested but should work as main ones do work.

REPO here:
https://github.com/wwatson4506/Arduino-Teensy-Codec-lib/tree/FS_Usage

Modified files are:
- play_sd_mp3.h
Code:
public:
	void stop(void);
	[COLOR="#FF0000"]int play(FS *fs, const char *filename) {stop();if (!fopen(fs,filename)) return ERR_CODEC_FILE_NOT_FOUND; return play();}[/COLOR]
	int play(const char *filename) {stop();if (!fopen(filename)) return ERR_CODEC_FILE_NOT_FOUND; return play();}
	int play(const size_t p, const size_t size) {stop();if (!fopen(p,size)) return ERR_CODEC_FILE_NOT_FOUND; return play();}
	int play(const uint8_t*p, const size_t size) {stop();if (!fopen(p,size))  return ERR_CODEC_FILE_NOT_FOUND; return play();}
- codecs.h
Code:
public:
	[COLOR="#FF0000"]bool fopen(FS *fs,const char *filename) {ftype=codec_file; AudioStartUsingSPI(); fptr=NULL;	file=fs->open(filename); _fsize=file.size(); _fposition=0;return file != 0;} //FILE[/COLOR]
	bool fopen(const char *filename) {ftype=codec_file; AudioStartUsingSPI(); fptr=NULL; file=SD.open(filename); _fsize=file.size(); _fposition=0; return file != 0;} //FILE
	bool fopen(const uint8_t*p, const size_t size) {ftype=codec_flash; fptr=(uint8_t*)p; _fsize=size; _fposition=0; return true;} //FLASH
	bool fopen(const size_t p, const size_t size) {ftype=codec_serflash; offset=p; _fsize=size; _fposition=0; AudioStartUsingSPI(); serflashinit(); return true;} //SERIAL FLASH
	void fclose(void)

This is just a new overload for using an FS pointer for opening any FS file type.SD, LFS and MSC.

You will have to modify Mp3FilePlayerLFS.ino manually to select the LFS device you want to use.
Code:
// Simple MP3 player example for LittleFS
//
// Requires the audio shield:
//   http://www.pjrc.com/store/teensy3_audio.html
//
// This example code is in the public domain.

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>

#if defined(ARDUINO_TEENSY41)
#include "LittleFS.h" // T4.1 only
#endif

#include <play_sd_mp3.h>

LittleFS_QPINAND   lfsFS;
//LittleFS_QSPIFlash lfsFS;
//LittleFS_SPIFlash  lfsFS;
//LittleFS_SPINAND   lfsFS;
SDClass sd;

// GUItool: begin automatically generated code
AudioPlaySdMp3           playMp31;       //xy=154,78
AudioOutputI2S           i2s1;           //xy=334,89
AudioConnection          patchCord1(playMp31, 0, i2s1, 0);
AudioConnection          patchCord2(playMp31, 1, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=240,153
// GUItool: end automatically generated code

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

  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(5);

  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);

  SPI.setMOSI(7);
  SPI.setSCK(14);
  
//  if (!(SD.begin(10))) { // For external SD card.
//  if (!(sd.begin(BUILTIN_SDCARD))) { // For BUILTIN_SDCARD.
  if (!lfsFS.begin()) { // For LittleFS
//  pinMode(6,OUTPUT); Change these for SPIFlash and SPINAND.
//  digitalWriteFast(6,HIGH); // Ditto
//  if(!lfsFS.begin(6,SPI)) { // Again ditto
  // stop here, but print a message repetitively
    while (1) {
      Serial.println("Unable to access the drive...");
//      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
}

void playFile(const char *filename)
{
  Serial.print("Playing file: ");
  Serial.println(filename);
  // Start playing the file.  This sketch continues to
  // run while the file plays.
  playMp31.play(&lfsFS, filename);
//  playMp31.play(&sd, filename);
    // Simply wait for the file to finish playing.
    while (playMp31.isPlaying()) {
    // uncomment these lines if your audio shield
    // has the optional volume pot soldered
    float vol = analogRead(15);
    vol = vol / 1024;
    sgtl5000_1.volume(vol);
#if 0	
	 Serial.print("Max Usage: ");
	 Serial.print(playMp31.processorUsageMax());
	 Serial.print("% Audio, ");
	 Serial.print(playMp31.processorUsageMaxDecoder());	 	 
	 Serial.print("% Decoding max, ");
	 
	 Serial.print(playMp31.processorUsageMaxSD());	 
	 Serial.print("% SD max, ");
	 	 
	 Serial.print(AudioProcessorUsageMax());	 
	 Serial.println("% All");
	 
	 AudioProcessorUsageMaxReset();
	 playMp31.processorUsageMaxReset();
	 playMp31.processorUsageMaxResetDecoder();
#endif 
	 
	 delay(250);
  }
}


void loop() {
//  playFile("ForTag.mp3");	
//  playFile("Tom.mp3");
//  playFile("Foreverm.mp3");
  //Play a Def Lepard MP3.
  playFile("armaged.mp3"); // Install your own MP3 file on your selected device.
  delay(500);
}

This is a simple proof of functionality of using FrankB's library.
Now for testing MSC:)
 
wwatson said:
@all who want to try this out - I have setup a repo for playing MP3 files with LittleFS using @FrankB's Arduino-Teensy-Codec-lib. In this I have added a sketch to play MP3 files from LittleFS devices. Five files were modified to do this. This is using Arduino 1.8.16 and TD1.56B3. LittleFS devices that I have tested are listed in the sketch Mp3FilePlayerLFS.ino. There are devices not tested but should work as main ones do work.
Thank you - definitely will try it later today.
 
@All - Quick update. Now have MSC working properly with MP3. Will update Arduino-Teensy-Codec-lib and create a sketch specifically for playing MP3 files with MSC.
 
Another update. Not out off the woods yet with MP3 and MSC:( After further testing I am still not able to repeat playing an MP3 file intermittently. With the T4.1 breakout board I see it hitting stop() three times and then getting a decode_res of -6 or -9 when it fails. This is here marked in red:
Code:
//decoding-interrupt
//__attribute__ ((optimize("O2"))) <- does not work here, bug in g++
void decodeMp3(void)
{
	AudioPlaySdMp3 *o = mp3objptr;
	int db = o->decoding_block;

	if ( o->decoded_length[db] > 0 ) return; //this block is playing, do NOT fill it

	uint32_t cycles = ARM_DWT_CYCCNT;
	int eof = false;

	switch (o->decoding_state) {

	case 0:
		{

			o->sd_left = o->fillReadBuffer( o->file, o->sd_buf, o->sd_p, o->sd_left, MP3_SD_BUF_SIZE);
			if (!o->sd_left) { eof = true; 
				goto mp3end; }
			o->sd_p = o->sd_buf;

			uint32_t cycles_rd = (ARM_DWT_CYCCNT - cycles);
			if (cycles_rd > o->decode_cycles_max_read )o-> decode_cycles_max_read = cycles_rd;
			break;
		}

	case 1:
		{
			// find start of next MP3 frame - assume EOF if no sync found
			int offset = MP3FindSyncWord(o->sd_p, o->sd_left);

			if (offset < 0) {
				//Serial.println("No sync"); //no error at end of file
				eof = true;
				goto mp3end;
			}

			o->sd_p += offset;
			o->sd_left -= offset;

			int decode_res = MP3Decode(o->hMP3Decoder, &o->sd_p, (int*)&o->sd_left,o->buf[db], 0);

			switch(decode_res)
			{
				case ERR_MP3_NONE:
				{
					MP3GetLastFrameInfo(o->hMP3Decoder, &o->mp3FrameInfo);
					o->decoded_length[db] = o->mp3FrameInfo.outputSamps;
					break;
				}

				case ERR_MP3_MAINDATA_UNDERFLOW:
				{
					break;
				}

				default :
				{
					AudioPlaySdMp3::lastError = decode_res;
					[COLOR="#FF0000"]eof = true;[/COLOR]
					break;
				}
			}

			cycles = (ARM_DWT_CYCCNT - cycles);
			if (cycles > o->decode_cycles_max ) o->decode_cycles_max = cycles;
			break;
		}
	}//switch

mp3end:

	o->decoding_state++;
	if (o->decoding_state >= DECODE_NUM_STATES) o->decoding_state = 0;
	if (eof) o->stop();
}

These are the error code defs:
Code:
	ERR_MP3_INVALID_FRAMEHEADER =  -6,
	ERR_MP3_INVALID_HUFFCODES =    -9,

On the T4.1 breakout board -9 is seen the most with occasional -6 as it loops. On the T4.0 breakout board -6 is seen the most.
On both boards if you do an upload or power off or disconnect/connect it will always play the MP3 once but fail most of the time with a repeat play in loop().

It's like the reads to the buffer of the second play are corrupted. Not sure...

Here is the sketch for MSC:
Code:
// Simple MP3 player example for USB drives.
//
// Requires the audio shield:
//   http://www.pjrc.com/store/teensy3_audio.html
//
// This example code is in the public domain.

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <USBHost_t36.h>
#include <USBHost_ms.h>

#include <play_sd_mp3.h>
// Setup USBHost_t36 and as many HUB ports as needed.
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
USBHub hub4(myusb);

msFilesystem msFS1(myusb);
msController drive1(myusb);
USBMSCDevice mscDrive;

// GUItool: begin automatically generated code
AudioPlaySdMp3           playMp31;       //xy=154,78
AudioOutputI2S           i2s1;           //xy=334,89
AudioConnection          patchCord1(playMp31, 0, i2s1, 0);
AudioConnection          patchCord2(playMp31, 1, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=240,153
// GUItool: end automatically generated code

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

  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(5);

  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);

  // Initialize USBHost_t36
  myusb.begin();
  delay(100);
  
  if (!mscDrive.begin(&drive1)) {
  // stop here, but print a message repetitively
    while (1) {
      Serial.println("Unable to access the drive...");
      delay(500);
    }
  }
  myusb.Task();
}

void playFile(const char *filename)
{
int error = 0;
  Serial.print("Playing file: ");
  Serial.println(filename);
  // Start playing the file.  This sketch continues to
  // run while the file plays.
  if(msFS1 == 0)
    Serial.printf("mscFS no longer valid...\r\n");
    error = playMp31.play(&msFS1, filename);
	Serial.printf("play() error = %d\r\n",error);
  // Simply wait for the file to finish playing.
    while (playMp31.isPlaying()) {
  // uncomment these lines if your audio shield
  // has the optional volume pot soldered
    float vol = analogRead(15);
    vol = vol / 1024;
    sgtl5000_1.volume(vol);
//   delay(250);
  }
}


void loop() {
//  playFile("ForTag.mp3");	
//  playFile("Tom.mp3");
//  playFile("Foreverm.mp3");
  playFile("armaged.mp3");
  delay(100);
}

This is using USBHost_t36-FS_Integration_MSC found here:
https://github.com/KurtE/USBHost_t36/tree/FS_Integration_MSC

Will keep testing to find out what's going wrong. Dang so close yet not there:)
 
Last edited:
Since I'm currently back on topic (ESP), and I also briefly struggled with the -6 and -.9, I can say that this is almost 100% an offset issue. The decoder reads wrong data.
Whenever it is called, the frame header should be in byte 1 and 2. If this is not the case, such errors often occur.
It looks like something is not synchronized. Since the rest of the code works fine (for other file types) it must be the reading somehow.
(Or you found a bug.. this first after many years!)

I hope this helps...

When you get it running, i'll be happy to merge it.
 
Since I'm currently back on topic (ESP), and I also briefly struggled with the -6 and -.9, I can say that this is almost 100% an offset issue. The decoder reads wrong data.
Whenever it is called, the frame header should be in byte 1 and 2. If this is not the case, such errors often occur.
It looks like something is not synchronized. Since the rest of the code works fine (for other file types) it must be the reading somehow.
(Or you found a bug.. this first after many years!)

I hope this helps...

When you get it running, i'll be happy to merge it.

Thanks Frank. That gives me something to work with. Like you said it is working consistently with SD (BUILTIN and External) and LFS. So suspect is MSC implementation. Maybe hexdump the frame header upon initial play and next play then compare to find a clue. Might also go back and use UsbMSCFat to check it's response. Things are still in development with MSC so it all might change.

Again thanks for the clues:)
 
@wwatson
Downloaded your lib this morning to give it a try and I am not seeing any errors:
Code:
Playing file: SDTEST1.mp3
play() error = 0
but the problem is nothing is playing. This is using a USB Drive.
 
@wwatson
Downloaded your lib this morning to give it a try and I am not seeing any errors:
Code:
Playing file: SDTEST1.mp3
play() error = 0
but the problem is nothing is playing. This is using a USB Drive.

I just tried SDTEST(1-4).mp3 and I could not get them to play either. I have been using regular MP3 music file I have. More to go on for testing. Did you ever have a chance test with LittleFS? I never did try SDTEST*.mp3 files on that either.

@defragster - Downloaded KurtE's MemHexDump library. Will use that for testing.

Edit: Just tried SDTEST*.mp3 with LittleFS devices and they do work. Back to testing MSC and MP3...
 
Last edited:
I just tried SDTEST(1-4).mp3 and I could not get them to play either. I have been using regular MP3 music file I have. More to go on for testing. Did you ever have a chance test with LittleFS? I never did try SDTEST*.mp3 files on that either.

@defragster - Downloaded KurtE's MemHexDump library. Will use that for testing.

Edit: Just tried SDTEST*.mp3 with LittleFS devices and they do work. Back to testing MSC and MP3...

Going to see if I can modify the sketch to use it in conjunction with MTP and see what happens at some point today. :)
 
yeah it was but still didn't work

I am gaining some debug info using MemoryHexDump and sprinkling in some Serial.prints. Going through both LFS and MSC use of the MP3 player with SDTEST1.mp3 step by step comparing the debug info. MCS gets about 4 frames in before it fails with error -9. So now into the next function that is setting the errror.
 
@mjs513 @FrankB @All - I Got it:)
I did a lot of debugging and testing but nothing was working. If the MP3 file had a MP3 ID3 info header it would not play it would fail after reading 4 frames of data and if it did not have an MP3 ID3 info header it would play once but would not replay -9 error. As it turned out I was getting incomplete reads into sb_buf. So I was thinking this is going to be one of those MSC is to slow things:( But then I started to remember back in the day's of uSDFS when I was testing different types of buffering to speed MSC up. Then I remembered using malloc() was causing grief and was really slow. To make a long 4 days of debugging and testing short I changed sd_buf from being calloced() to a global uint8_t sd_buf[]. That took care of the failures. Tested SD, SDIO, LFS and MSC they all work.

Have an errand to run but will zip up Arduino-Teensy-Codec-lib and post it for testing when I get back.
I really love success:)
 
Ok - Here is the attached zip of Arduino-Teensy-Codec-lib. Apparently the zip is to big so I updated my REPO:
https://github.com/wwatson4506/Arduino-Teensy-Codec-lib/tree/FS_Usage

This is only for testing not going to submit a PR until there is something more than a workaround for the issue. I know using a global array for a buffer is bad. That's why I hope you guy's might be able to come up with a better way of doing it. Also wondering if MTP_Teensy is using calloc/malloc for buffering MSC drives. That could be an issue:)

So far I cannot get any MP3 files to fail that does not fail on my one PC.
Ran out of time to play...
 
Last edited:
Ok - Here is the attached zip of Arduino-Teensy-Codec-lib. Apparently the zip is to big so I updated my REPO:
https://github.com/wwatson4506/Arduino-Teensy-Codec-lib/tree/FS_Usage

This is only for testing not going to submit a PR until there is something more than a workaround for the issue. I know using a global array for a buffer is bad. That's why I hope you guy's might be able to come up with a better way of doing it. Also wondering if MTP_Teensy is using calloc/malloc for buffering MSC drives. That could be an issue:)

So far I cannot get any MP3 files to fail that does not fail on my one PC.
Ran out of time to play...

Just downloaded your updates and gave it a try and looks like you got it solved. Played on first try. Now to have some fun :)
 
@wwatson and all interested parties.

You got me inspired this morning so I did a modification to the MTP_test sketch so instead of logging data it will play mp3 files on the device or a single hardcoded file in the sketch. PS. I also did a hack to play_sd_wav.h and .cpp so the sketch will also play wav files as well. Did test it from USB Drive. There are improvements that you can do to the sketch-error checking and maybe a next key for when playing all files but again this is a test :) So I am attaching a copy of the hacked up play_sd_wav files for the audio library and the mtp_test_audio sketch :) Have fun.

View attachment Audio.zip

Code:
#include <SD.h>
#include <MTP_Teensy.h>
#include <Audio.h>
#include <play_sd_mp3.h>

// GUItool: begin automatically generated code
AudioPlaySdMp3           playMp31;       //xy=154,78
AudioPlaySdWav           playWav; //xy=154,422
AudioOutputI2S           i2s1;           //xy=334,89
AudioMixer4              mixer1;         //xy=323,326
AudioConnection          patchCord1(playMp31, 0, mixer1, 0);
AudioConnection          patchCord2(playMp31, 1, mixer1, 1);
AudioConnection          patchCord3(playWav, 0, mixer1, 2);
AudioConnection          patchCord4(playWav, 1, mixer1, 3);
AudioConnection          patchCord5(mixer1, i2s1);
AudioControlSGTL5000     sgtl5000_1;     //xy=240,153
// GUItool: end automatically generated code
float volume = 0.5f;
char filename[] = "SDTEST1.mp3";

//---------------------------------------------------
// Select drives you want to create
//---------------------------------------------------
#define USE_SD  1         // SDFAT based SDIO and SPI
#ifdef ARDUINO_TEENSY41
#define USE_LFS_RAM 1     // T4.1 PSRAM (or RAM)
#else
#define USE_LFS_RAM 0     // T4.1 PSRAM (or RAM)
#endif
#ifdef ARDUINO_TEENSY_MICROMOD
#define USE_LFS_QSPI 0    // T4.1 QSPI
#define USE_LFS_PROGM 1   // T4.4 Progam Flash
#define USE_LFS_SPI 0     // SPI Flash
#define USE_LFS_NAND 0
#define USE_LFS_QSPI_NAND 0
#define USE_LFS_FRAM 0
#else
#define USE_LFS_QSPI 1    // T4.1 QSPI
#define USE_LFS_PROGM 1   // T4.4 Progam Flash
#define USE_LFS_SPI 1     // SPI Flash
#define USE_LFS_NAND 1
#define USE_LFS_QSPI_NAND 0
#define USE_LFS_FRAM 0
#endif
#define USE_MSC 1    // set to > 0 experiment with MTP (USBHost.t36 + mscFS)
#define USE_SW_PU  1 //set to 1 if SPI devices does not have PUs,
                     // https://www.pjrc.com/better-spi-bus-design-in-3-steps/


#define DBGSerial Serial
FS *myfs;
File dataFile, myFile;  // Specifes that dataFile is of File type
int record_count = 0;
bool write_data = false;
uint8_t current_store = 0;

#define BUFFER_SIZE_INDEX 128
uint8_t write_buffer[BUFFER_SIZE_INDEX];
#define buffer_mult 4
uint8_t buffer_temp[buffer_mult*BUFFER_SIZE_INDEX];

int bytesRead;
uint32_t drive_index = 0;


//=============================================================================
// Global defines
//=============================================================================

MTPStorage storage;
MTPD    mtpd(&storage);

//=============================================================================
// MSC & SD classes
//=============================================================================
#if USE_SD==1
#define USE_BUILTIN_SDCARD
#if defined(USE_BUILTIN_SDCARD) && defined(BUILTIN_SDCARD)
#define CS_SD  BUILTIN_SDCARD
#else
#define CS_SD 10
#endif
#endif


// SDClasses 
#if USE_SD==1
  // edit SPI to reflect your configuration (following is for T4.1)
  #define SD_MOSI 11
  #define SD_MISO 12
  #define SD_SCK  13

  #define SPI_SPEED SD_SCK_MHZ(16)  // adjust to sd card 

  #if defined (BUILTIN_SDCARD)
    const char *sd_str[]={"sdio","sd1"}; // edit to reflect your configuration
    const int cs[] = {BUILTIN_SDCARD,10}; // edit to reflect your configuration
  #else
    const char *sd_str[]={"sd1"}; // edit to reflect your configuration
    const int cs[] = {10}; // edit to reflect your configuration
  #endif
  const int nsd = sizeof(sd_str)/sizeof(const char *);
  
SDClass sdx[nsd];
#endif

// =======================================================================
// Set up MSC Drive file systems on different storage media
// =======================================================================
#if USE_MSC == 1
#include <USBHost_t36.h>
#include <USBHost_ms.h>

// Add USBHost objectsUsbFs
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub(myusb);

// MSC objects.
msController drive1(myusb);
msController drive2(myusb);
msController drive3(myusb);
msController drive4(myusb);

msFilesystem msFS1(myusb);
msFilesystem msFS2(myusb);
msFilesystem msFS3(myusb);
msFilesystem msFS4(myusb);
msFilesystem msFS5(myusb);

// Quick and dirty
msFilesystem *pmsFS[] = {&msFS1, &msFS2, &msFS3, &msFS4, &msFS5};
#define CNT_MSC  (sizeof(pmsFS)/sizeof(pmsFS[0]))
uint32_t pmsfs_store_ids[CNT_MSC] = {0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL};
char  pmsFS_display_name[CNT_MSC][20];

msController *pdrives[] {&drive1, &drive2, &drive3, &drive4};
#define CNT_DRIVES  (sizeof(pdrives)/sizeof(pdrives[0]))
bool drive_previous_connected[CNT_DRIVES] = {false, false, false, false};
#endif


// =======================================================================
// Set up LittleFS file systems on different storage media
// =======================================================================

#if USE_LFS_FRAM == 1 || USE_LFS_NAND == 1 || USE_LFS_PROGM == 1 || USE_LFS_QSPI == 1 || USE_LFS_QSPI_NAND == 1 || \
  USE_LFS_RAM == 1 || USE_LFS_SPI == 1
#include <LittleFS.h>
#endif

#if USE_LFS_RAM==1
const char *lfs_ram_str[] = {"RAM1"};  // edit to reflect your configuration
const int lfs_ram_size[] = {4'000'000}; // edit to reflect your configuration
const int nfs_ram = sizeof(lfs_ram_str)/sizeof(const char *);
LittleFS_RAM ramfs[nfs_ram];
#endif

#if USE_LFS_QSPI==1
const char *lfs_qspi_str[]={"QSPI"};     // edit to reflect your configuration
const int nfs_qspi = sizeof(lfs_qspi_str)/sizeof(const char *);
LittleFS_QSPIFlash qspifs[nfs_qspi];
#endif

#if USE_LFS_PROGM==1
const char *lfs_progm_str[]={"PROGM"};     // edit to reflect your configuration
const int lfs_progm_size[] = {1'000'000}; // edit to reflect your configuration
const int nfs_progm = sizeof(lfs_progm_str)/sizeof(const char *);
LittleFS_Program progmfs[nfs_progm];
#endif

#if USE_LFS_SPI==1
const char *lfs_spi_str[]={"sflash5", "sflash6"}; // edit to reflect your configuration
const int lfs_cs[] = {5, 6}; // edit to reflect your configuration
const int nfs_spi = sizeof(lfs_spi_str)/sizeof(const char *);
LittleFS_SPIFlash spifs[nfs_spi];
#endif

#if USE_LFS_NAND == 1
const char *nspi_str[]={"WINBOND1G", "WINBOND2G"};     // edit to reflect your configuration
const int nspi_cs[] = {3, 4}; // edit to reflect your configuration
const int nspi_nsd = sizeof(nspi_cs)/sizeof(int);
LittleFS_SPINAND nspifs[nspi_nsd]; // needs to be declared if LittleFS is used in storage.h
#endif

#if USE_LFS_QSPI_NAND == 1
const char *qnspi_str[]={"WIN-M02"};     // edit to reflect your configuration
const int qnspi_nsd = sizeof(qnspi_str)/sizeof(const char *);
LittleFS_QPINAND qnspifs[qnspi_nsd]; // needs to be declared if LittleFS is used in storage.h
#endif

void storage_configure()
{
  DateTimeFields date;
  breakTime(Teensy3Clock.get(), date);
  const char *monthname[12]={
    "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
  DBGSerial.printf("Date: %u %s %u %u:%u:%u\n",
    date.mday, monthname[date.mon], date.year+1900, date.hour, date.min, date.sec);

    
  #if USE_SD==1
    #if defined SD_SCK
      SPI.setMOSI(SD_MOSI);
      SPI.setMISO(SD_MISO);
      SPI.setSCK(SD_SCK);
    #endif

    for(int ii=0; ii<nsd; ii++)
    { 
      #if defined(BUILTIN_SDCARD)
        if(cs[ii] == BUILTIN_SDCARD)
        {
          DBGSerial.printf("!! Try installing BUILTIN SD Card");
          if(!sdx[ii].begin(BUILTIN_SDCARD))
          { 
            Serial.printf("SDIO Storage %d %d %s failed or missing",ii,cs[ii],sd_str[ii]);  Serial.println();
          }
          else
          {
            storage.addFilesystem(sdx[ii], sd_str[ii]);
            uint64_t totalSize = sdx[ii].totalSize();
            uint64_t usedSize  = sdx[ii].usedSize();
            Serial.printf("SDIO Storage %d %d %s ",ii,cs[ii],sd_str[ii]); 
            Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
          }
        }
        else if(cs[ii]<BUILTIN_SDCARD)
      #endif
      {
        pinMode(cs[ii],OUTPUT); digitalWriteFast(cs[ii],HIGH);
        if(!sdx[ii].begin(cs[ii])) 
        { Serial.printf("SD Storage %d %d %s failed or missing",ii,cs[ii],sd_str[ii]);  Serial.println();
        }
        else
        {
          storage.addFilesystem(sdx[ii], sd_str[ii]);
          uint64_t totalSize = sdx[ii].totalSize();
          uint64_t usedSize  = sdx[ii].usedSize();
          Serial.printf("SD Storage %d %d %s ",ii,cs[ii],sd_str[ii]); 
          Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
        }
      }
    
    }
    #endif

#if USE_LFS_RAM==1
  for (int ii=0; ii<nfs_ram;ii++) {
    if (!ramfs[ii].begin(lfs_ram_size[ii])) {
      DBGSerial.printf("Ram Storage %d %s failed or missing",ii,lfs_ram_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(ramfs[ii], lfs_ram_str[ii]);
      uint64_t totalSize = ramfs[ii].totalSize();
      uint64_t usedSize  = ramfs[ii].usedSize();
      DBGSerial.printf("RAM Storage %d %s %llu %llu\n", ii, lfs_ram_str[ii],
        totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_PROGM==1
  for (int ii=0; ii<nfs_progm;ii++) {
    if (!progmfs[ii].begin(lfs_progm_size[ii])) {
      DBGSerial.printf("Program Storage %d %s failed or missing",ii,lfs_progm_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(progmfs[ii], lfs_progm_str[ii]);
      uint64_t totalSize = progmfs[ii].totalSize();
      uint64_t usedSize  = progmfs[ii].usedSize();
      DBGSerial.printf("Program Storage %d %s %llu %llu\n", ii, lfs_progm_str[ii],
        totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_QSPI==1
  for(int ii=0; ii<nfs_qspi;ii++) {
    if(!qspifs[ii].begin()) {
      DBGSerial.printf("QSPI Storage %d %s failed or missing",ii,lfs_qspi_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(qspifs[ii], lfs_qspi_str[ii]);
      uint64_t totalSize = qspifs[ii].totalSize();
      uint64_t usedSize  = qspifs[ii].usedSize();
      DBGSerial.printf("QSPI Storage %d %s %llu %llu\n", ii, lfs_qspi_str[ii], totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_SPI==1
  for (int ii=0; ii<nfs_spi;ii++) {
    if (USE_SW_PU == 1) {
      pinMode(lfs_cs[ii],OUTPUT);
      digitalWriteFast(lfs_cs[ii],HIGH);
    }
    if (!spifs[ii].begin(lfs_cs[ii], SPI)) {
      DBGSerial.printf("SPIFlash Storage %d %d %s failed or missing",ii,lfs_cs[ii],lfs_spi_str[ii]);      DBGSerial.println();
    } else {
      storage.addFilesystem(spifs[ii], lfs_spi_str[ii]);
      uint64_t totalSize = spifs[ii].totalSize();
      uint64_t usedSize  = spifs[ii].usedSize();
      DBGSerial.printf("SPIFlash Storage %d %d %s %llu %llu\n", ii, lfs_cs[ii], lfs_spi_str[ii],
        totalSize, usedSize);
    }
  }
#endif
#if USE_LFS_NAND == 1
  for(int ii=0; ii<nspi_nsd;ii++) {
    if (USE_SW_PU == 1) {
      pinMode(nspi_cs[ii],OUTPUT);
      digitalWriteFast(nspi_cs[ii],HIGH);
    }
    if(!nspifs[ii].begin(nspi_cs[ii], SPI)) {
      DBGSerial.printf("SPIFlash NAND Storage %d %d %s failed or missing",ii,nspi_cs[ii],nspi_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(nspifs[ii], nspi_str[ii]);
      uint64_t totalSize = nspifs[ii].totalSize();
      uint64_t usedSize  = nspifs[ii].usedSize();
      DBGSerial.printf("Storage %d %d %s %llu %llu\n", ii, nspi_cs[ii], nspi_str[ii],
        totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_QSPI_NAND == 1
  for(int ii=0; ii<qnspi_nsd;ii++) {
    if(!qnspifs[ii].begin()) {
       DBGSerial.printf("QSPI NAND Storage %d %s failed or missing",ii,qnspi_str[ii]); DBGSerial.println();
    } else {
      storage.addFilesystem(qnspifs[ii], qnspi_str[ii]);
      uint64_t totalSize = qnspifs[ii].totalSize();
      uint64_t usedSize  = qnspifs[ii].usedSize();
      DBGSerial.printf("Storage %d %s %llu %llu\n", ii, qnspi_str[ii], totalSize, usedSize);
    }
  }
#endif

}

void setup()
{

  // Open serial communications and wait for port to open
  while (!Serial && !DBGSerial.available() && millis() < 5000) 

  DBGSerial.print(CrashReport);
  DBGSerial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
  delay(3000);
  
  //Setup Audio
  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(40);

  sgtl5000_1.enable();
  sgtl5000_1.volume(volume);


  //This is mandatory to begin the mtpd session.
  mtpd.begin();
  
  storage_configure();

  #if USE_MSC == 1
  myusb.begin();
  DBGSerial.print("Initializing MSC Drives ...");
  DBGSerial.println("\nInitializing USB MSC drives...");
  DBGSerial.println("MSC and MTP initialized.");
  checkMSCChanges();
  #endif
  DBGSerial.println("\nSetup done");
  menu();
}

int ReadAndEchoSerialChar() {
  int ch = DBGSerial.read();
  if (ch >= ' ') DBGSerial.write(ch);
  return ch;
}

uint8_t storage_index = '0';
void loop()
{
  if ( DBGSerial.available() ) {
    uint8_t command = DBGSerial.read();
    int ch = DBGSerial.read();
    if ('2'==command) storage_index = CommandLineReadNextNumber(ch, 0);
    while (ch == ' ') ch = DBGSerial.read();

    uint32_t fsCount;
    switch (command) {
    case '1':
      // first dump list of storages:
      fsCount = storage.getFSCount();
      DBGSerial.printf("\nDump Storage list(%u)\n", fsCount);
      for (uint32_t ii = 0; ii < fsCount; ii++) {
        if ( ii == storage_index )
          DBGSerial.print("STORE");
        else
          DBGSerial.print("store");
        DBGSerial.printf(":%u storage:%x name:%s fs:%x\n", ii, mtpd.Store2Storage(ii),
          storage.getStoreName(ii), (uint32_t)storage.getStoreFS(ii));
      }
      //DBGSerial.println("\nDump Index List");
      //storage.dumpIndexList();
      break;
    case '2':
      if (storage_index < storage.getFSCount()) {
        DBGSerial.printf("Storage Index %u Name: %s Selected\n", storage_index,
          storage.getStoreName(storage_index));
        myfs = storage.getStoreFS(storage_index);
        current_store = storage_index;
      } else {
        DBGSerial.printf("Storage Index %u out of range\n", storage_index);
      }
      break;

    case 'l': listFiles(); break;
    case 'e': eraseFiles(); break;
    case 'p':
    {
      write_data = true;   // sets flag to continue to write data until new command is received
      int errorMp3 = 0;
      DBGSerial.print("Playing file: ");
      DBGSerial.println(filename);
      // Start playing the file.  This sketch continues to
      // run while the file plays.
      errorMp3 = playMp31.play(myfs, filename);
      DBGSerial.printf("play() error = %d\r\n",errorMp3);
      // Simply wait for the file to finish playing.
      if(errorMp3 != 0) write_data = false;
      break;
    }
    case 'P': 
      playDir(myfs);
      break;
    case'r':
      DBGSerial.println("Reset");
      mtpd.send_DeviceResetEvent();
      break;
    case 'R':
      DBGSerial.print(" RESTART Teensy ...");
      delay(100);
      SCB_AIRCR = 0x05FA0004;
      break;
    case '\r':
    case '\n':
    case 'h': menu(); break;
    }
    while (DBGSerial.read() != -1) ; // remove rest of characters.
  } else {
    #if USE_MSC == 1
    checkMSCChanges();
    #endif
    mtpd.loop();
  }

  if (write_data) {
    while (playMp31.isPlaying()) {
      // uncomment these lines if your audio shield
      // has the optional volume pot soldered
      //  float vol = analogRead(15);
      //  vol = vol / 1024;
      //  sgtl5000_1.volume(vol);
      //   delay(250);
    }
  }
}

#if USE_MSC == 1
void checkMSCChanges() {
  myusb.Task();

  USBMSCDevice mscDrive;
  PFsLib pfsLIB;
  for (uint8_t i=0; i < CNT_DRIVES; i++) {
    if (*pdrives[i]) {
      if (!drive_previous_connected[i]) {
        if (mscDrive.begin(pdrives[i])) {
          Serial.printf("\nUSB Drive: %u connected\n", i);
          pfsLIB.mbrDmp(&mscDrive, (uint32_t)-1, Serial);
          Serial.printf("\nTry Partition list");
          pfsLIB.listPartitions(&mscDrive, Serial);
          drive_previous_connected[i] = true;
        }
      }
    } else {
      drive_previous_connected[i] = false;
    }
  }
  bool send_device_reset = false;
  for (uint8_t i = 0; i < CNT_MSC; i++) {
    if (*pmsFS[i] && (pmsfs_store_ids[i] == 0xFFFFFFFFUL)) {
      Serial.printf("Found new Volume:%u\n", i); Serial.flush();
      // Lets see if we can get the volume label:
      char volName[20];
      if (pmsFS[i]->mscfs.getVolumeLabel(volName, sizeof(volName)))
        snprintf(pmsFS_display_name[i], sizeof(pmsFS_display_name[i]), "MSC%d-%s", i, volName);
      else
        snprintf(pmsFS_display_name[i], sizeof(pmsFS_display_name[i]), "MSC%d", i);
      pmsfs_store_ids[i] = storage.addFilesystem(*pmsFS[i], pmsFS_display_name[i]);

      // Try to send store added. if > 0 it went through = 0 stores have not been enumerated
      if (mtpd.send_StoreAddedEvent(pmsfs_store_ids[i]) < 0) send_device_reset = true;
    }
    // Or did volume go away?
    else if ((pmsfs_store_ids[i] != 0xFFFFFFFFUL) && !*pmsFS[i] ) {
      if (mtpd.send_StoreRemovedEvent(pmsfs_store_ids[i]) < 0) send_device_reset = true;
      storage.removeFilesystem(pmsfs_store_ids[i]);
      // Try to send store added. if > 0 it went through = 0 stores have not been enumerated
      pmsfs_store_ids[i] = 0xFFFFFFFFUL;
    }
  }
  if (send_device_reset) mtpd.send_DeviceResetEvent();
}
#endif


void menu()
{
  DBGSerial.println();
  DBGSerial.println("Menu Options:");
  DBGSerial.println("\t1 - List Drives (Step 1)");
  DBGSerial.println("\t2# - Select Drive # for Logging (Step 2)");
  DBGSerial.println("\tl - List files on disk");
  DBGSerial.println("\te - Erase files on disk with Format");
  DBGSerial.println("\tp - Play Hardcoded test file");
  DBGSerial.println("\tP - Play all music on selected device");
  DBGSerial.println("\tr - Reset MTP");
  DBGSerial.printf("\t%s","R - Restart Teensy");
  DBGSerial.println("\th - Menu");
  DBGSerial.println();
}

void listFiles()
{
  DBGSerial.print("\n Space Used = ");
  DBGSerial.println(myfs->usedSize());
  DBGSerial.print("Filesystem Size = ");
  DBGSerial.println(myfs->totalSize());

  printDirectory(myfs);
}

void eraseFiles()
{
  //DBGSerial.println("Formating not supported at this time");
  DBGSerial.println("\n*** Erase/Format started ***");
  myfs->format(1, '.', DBGSerial);
  DBGSerial.println("Completed, sending device reset event");
  mtpd.send_DeviceResetEvent();
}

void printDirectory(FS *pfs) {
  DBGSerial.println("Directory\n---------");
  printDirectory(pfs->open("/"), 0);
  DBGSerial.println();
}

void printDirectory(File dir, int numSpaces) {
  while (true) {
    File entry = dir.openNextFile();
    if (! entry) {
      //DBGSerial.println("** no more files **");
      break;
    }
    printSpaces(numSpaces);
    DBGSerial.print(entry.name());
    if (entry.isDirectory()) {
      DBGSerial.println("/");
      printDirectory(entry, numSpaces + 2);
    } else {
      // files have sizes, directories do not
      printSpaces(36 - numSpaces - strlen(entry.name()));
      DBGSerial.print("  ");
      DBGSerial.println(entry.size(), DEC);
    }
    entry.close();
  }
}

void printSpaces(int num) {
  for (int i = 0; i < num; i++) {
    DBGSerial.print(" ");
  }
}


uint32_t CommandLineReadNextNumber(int &ch, uint32_t default_num) {
  while (ch == ' ') ch = DBGSerial.read();
  if ((ch < '0') || (ch > '9')) return default_num;

  uint32_t return_value = 0;
  while ((ch >= '0') && (ch <= '9')) {
    return_value = return_value * 10 + ch - '0';
    ch = DBGSerial.read();
  }
  return return_value;
}

void playFile(const char *filename)
{
  int filetype;

  filetype = 0;
  if (strstr(filename, ".MP3") != NULL || strstr(filename, ".mp3") != NULL) {
      filetype = 1;
  } else if (strstr(filename, ".WAV") != NULL || strstr(filename, ".wav") != NULL ) {
      filetype = 2;
  } else {
      filetype = 0;
  }
  if (filetype > 0) {
    DBGSerial.print("Playing file: ");
    DBGSerial.println(filename);
    
    switch (filetype) {
      case 1 :
        mixer1.gain(0, volume);
        mixer1.gain(1, volume);
        mixer1.gain(2, 0.0f);
        mixer1.gain(3, 0.0f);
        playMp31.play(myfs, filename);
        delay(5);
        while (playMp31.isPlaying()) {
        }
        break;
      case 2 :
        mixer1.gain(0, 0.0f);
        mixer1.gain(1, 0.0f);
        mixer1.gain(2, volume);
        mixer1.gain(3, volume);
        playWav.play(myfs, filename);
        delay(5);
        while (playWav.isPlaying()) {
        }
        break;
    }
  }
}

void playDir(FS *pfs) {
  DBGSerial.println("Playing files on device");
  playAll(pfs->open("/"));
  DBGSerial.println();
}

void playAll(File dir){
  char filename[64];
  char filnam[64];
   while(true) {
     File entry =  dir.openNextFile();
     if (! entry) {
       // no more files
       // rewind to begining of directory and start over
       dir.rewindDirectory();
       break;
     }
     //DBGSerial.print(entry.name());
     if (entry.isDirectory()) {
       //DBGSerial.println("Directory/");
       //do nothing for now
       //DBGSerial.println(entry.name());
       playAll(entry);
     } else {
       // files have sizes, directories do not
       //DBGSerial.print("\t\t");
       //DBGSerial.println(entry.size(), DEC);
       // files have sizes, directories do not
       strcpy(filename, dir.name());
       if(strlen(dir.name()) > 0) strcat(filename, "/");
       strcat(filename, strcpy(filnam, entry.name()));
       playFile(filename);
     }
   entry.close();
 }
}
 
Ok got carried away. I edited the rest of the codec library so you can play aac and flac files as well as editing raw in the audio library. Unfortunately not sure why FLAC files are not playing - not getting any errors and says its playing - maybe something with using audio buffers that its using from the audio library. Anyway here are all the changed files for the audio lib and the codec library:

View attachment Audio.zip

View attachment Arduino-Teensy-Codec-lib-FS_Usage.zip

and the changed sketch:
Code:
#include <SD.h>
#include <MTP_Teensy.h>
#include <Audio.h>
#include <play_sd_mp3.h>
#include <play_sd_aac.h>
#include <play_sd_flac.h>

// GUItool: begin automatically generated code
AudioPlaySdMp3           playMp31;       //xy=154,78
AudioPlaySdWav           playWav; //xy=154,422
AudioPlaySdRaw           playRaw; //xy=154,422
AudioPlaySdAac           playAac; //xy=154,422
AudioPlaySdFlac          playFlac;
AudioOutputI2S           i2s1;           //xy=334,89
AudioMixer4              mixer1;         //xy=323,326
AudioMixer4              mixer2;         //xy=323,326
AudioMixer4              mixer3;
AudioConnection          patchCord1(playMp31, 0, mixer1, 0);
AudioConnection          patchCord2(playMp31, 1, mixer1, 1);
AudioConnection          patchCord3(playWav, 0, mixer1, 2);
AudioConnection          patchCord5(playAac, 0, mixer2, 0);
AudioConnection          patchCord6(playAac, 1, mixer2, 1);
AudioConnection          patchCord7(playRaw, 0, mixer2, 2);
AudioConnection          patchCord8(playFlac, 0, mixer3, 0);
AudioConnection          patchCord9(playFlac, 1, mixer3, 1);
AudioConnection          patchCord10(mixer1, i2s1);
AudioConnection          patchCord11(mixer2, i2s1);
AudioConnection          patchCord12(mixer3, i2s1);
AudioControlSGTL5000     sgtl5000_1;     //xy=240,153
// GUItool: end automatically generated code
float volume = 0.7f;
char filename[] = "SDTEST1.mp3";

//---------------------------------------------------
// Select drives you want to create
//---------------------------------------------------
#define USE_SD  1         // SDFAT based SDIO and SPI
#ifdef ARDUINO_TEENSY41
#define USE_LFS_RAM 1     // T4.1 PSRAM (or RAM)
#else
#define USE_LFS_RAM 0     // T4.1 PSRAM (or RAM)
#endif
#ifdef ARDUINO_TEENSY_MICROMOD
#define USE_LFS_QSPI 0    // T4.1 QSPI
#define USE_LFS_PROGM 1   // T4.4 Progam Flash
#define USE_LFS_SPI 0     // SPI Flash
#define USE_LFS_NAND 0
#define USE_LFS_QSPI_NAND 0
#define USE_LFS_FRAM 0
#else
#define USE_LFS_QSPI 1    // T4.1 QSPI
#define USE_LFS_PROGM 1   // T4.4 Progam Flash
#define USE_LFS_SPI 1     // SPI Flash
#define USE_LFS_NAND 1
#define USE_LFS_QSPI_NAND 0
#define USE_LFS_FRAM 0
#endif
#define USE_MSC 1    // set to > 0 experiment with MTP (USBHost.t36 + mscFS)
#define USE_SW_PU  1 //set to 1 if SPI devices does not have PUs,
                     // https://www.pjrc.com/better-spi-bus-design-in-3-steps/


#define DBGSerial Serial
FS *myfs;
File dataFile, myFile;  // Specifes that dataFile is of File type
int record_count = 0;
bool write_data = false;
uint8_t current_store = 0;

#define BUFFER_SIZE_INDEX 128
uint8_t write_buffer[BUFFER_SIZE_INDEX];
#define buffer_mult 4
uint8_t buffer_temp[buffer_mult*BUFFER_SIZE_INDEX];

int bytesRead;
uint32_t drive_index = 0;


//=============================================================================
// Global defines
//=============================================================================

MTPStorage storage;
MTPD    mtpd(&storage);

//=============================================================================
// MSC & SD classes
//=============================================================================
#if USE_SD==1
#define USE_BUILTIN_SDCARD
#if defined(USE_BUILTIN_SDCARD) && defined(BUILTIN_SDCARD)
#define CS_SD  BUILTIN_SDCARD
#else
#define CS_SD 10
#endif
#endif


// SDClasses 
#if USE_SD==1
  // edit SPI to reflect your configuration (following is for T4.1)
  #define SD_MOSI 11
  #define SD_MISO 12
  #define SD_SCK  13

  #define SPI_SPEED SD_SCK_MHZ(16)  // adjust to sd card 

  #if defined (BUILTIN_SDCARD)
    const char *sd_str[]={"sdio","sd1"}; // edit to reflect your configuration
    const int cs[] = {BUILTIN_SDCARD,10}; // edit to reflect your configuration
  #else
    const char *sd_str[]={"sd1"}; // edit to reflect your configuration
    const int cs[] = {10}; // edit to reflect your configuration
  #endif
  const int nsd = sizeof(sd_str)/sizeof(const char *);
  
SDClass sdx[nsd];
#endif

// =======================================================================
// Set up MSC Drive file systems on different storage media
// =======================================================================
#if USE_MSC == 1
#include <USBHost_t36.h>
#include <USBHost_ms.h>

// Add USBHost objectsUsbFs
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub(myusb);

// MSC objects.
msController drive1(myusb);
msController drive2(myusb);
msController drive3(myusb);
msController drive4(myusb);

msFilesystem msFS1(myusb);
msFilesystem msFS2(myusb);
msFilesystem msFS3(myusb);
msFilesystem msFS4(myusb);
msFilesystem msFS5(myusb);

// Quick and dirty
msFilesystem *pmsFS[] = {&msFS1, &msFS2, &msFS3, &msFS4, &msFS5};
#define CNT_MSC  (sizeof(pmsFS)/sizeof(pmsFS[0]))
uint32_t pmsfs_store_ids[CNT_MSC] = {0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL};
char  pmsFS_display_name[CNT_MSC][20];

msController *pdrives[] {&drive1, &drive2, &drive3, &drive4};
#define CNT_DRIVES  (sizeof(pdrives)/sizeof(pdrives[0]))
bool drive_previous_connected[CNT_DRIVES] = {false, false, false, false};
#endif


// =======================================================================
// Set up LittleFS file systems on different storage media
// =======================================================================

#if USE_LFS_FRAM == 1 || USE_LFS_NAND == 1 || USE_LFS_PROGM == 1 || USE_LFS_QSPI == 1 || USE_LFS_QSPI_NAND == 1 || \
  USE_LFS_RAM == 1 || USE_LFS_SPI == 1
#include <LittleFS.h>
#endif

#if USE_LFS_RAM==1
const char *lfs_ram_str[] = {"RAM1"};  // edit to reflect your configuration
const int lfs_ram_size[] = {4'000'000}; // edit to reflect your configuration
const int nfs_ram = sizeof(lfs_ram_str)/sizeof(const char *);
LittleFS_RAM ramfs[nfs_ram];
#endif

#if USE_LFS_QSPI==1
const char *lfs_qspi_str[]={"QSPI"};     // edit to reflect your configuration
const int nfs_qspi = sizeof(lfs_qspi_str)/sizeof(const char *);
LittleFS_QSPIFlash qspifs[nfs_qspi];
#endif

#if USE_LFS_PROGM==1
const char *lfs_progm_str[]={"PROGM"};     // edit to reflect your configuration
const int lfs_progm_size[] = {1'000'000}; // edit to reflect your configuration
const int nfs_progm = sizeof(lfs_progm_str)/sizeof(const char *);
LittleFS_Program progmfs[nfs_progm];
#endif

#if USE_LFS_SPI==1
const char *lfs_spi_str[]={"sflash5", "sflash6"}; // edit to reflect your configuration
const int lfs_cs[] = {5, 6}; // edit to reflect your configuration
const int nfs_spi = sizeof(lfs_spi_str)/sizeof(const char *);
LittleFS_SPIFlash spifs[nfs_spi];
#endif

#if USE_LFS_NAND == 1
const char *nspi_str[]={"WINBOND1G", "WINBOND2G"};     // edit to reflect your configuration
const int nspi_cs[] = {3, 4}; // edit to reflect your configuration
const int nspi_nsd = sizeof(nspi_cs)/sizeof(int);
LittleFS_SPINAND nspifs[nspi_nsd]; // needs to be declared if LittleFS is used in storage.h
#endif

#if USE_LFS_QSPI_NAND == 1
const char *qnspi_str[]={"WIN-M02"};     // edit to reflect your configuration
const int qnspi_nsd = sizeof(qnspi_str)/sizeof(const char *);
LittleFS_QPINAND qnspifs[qnspi_nsd]; // needs to be declared if LittleFS is used in storage.h
#endif

void storage_configure()
{
  DateTimeFields date;
  breakTime(Teensy3Clock.get(), date);
  const char *monthname[12]={
    "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
  DBGSerial.printf("Date: %u %s %u %u:%u:%u\n",
    date.mday, monthname[date.mon], date.year+1900, date.hour, date.min, date.sec);

    
  #if USE_SD==1
    #if defined SD_SCK
      SPI.setMOSI(SD_MOSI);
      SPI.setMISO(SD_MISO);
      SPI.setSCK(SD_SCK);
    #endif

    for(int ii=0; ii<nsd; ii++)
    { 
      #if defined(BUILTIN_SDCARD)
        if(cs[ii] == BUILTIN_SDCARD)
        {
          DBGSerial.printf("!! Try installing BUILTIN SD Card");
          if(!sdx[ii].begin(BUILTIN_SDCARD))
          { 
            Serial.printf("SDIO Storage %d %d %s failed or missing",ii,cs[ii],sd_str[ii]);  Serial.println();
          }
          else
          {
            storage.addFilesystem(sdx[ii], sd_str[ii]);
            uint64_t totalSize = sdx[ii].totalSize();
            uint64_t usedSize  = sdx[ii].usedSize();
            Serial.printf("SDIO Storage %d %d %s ",ii,cs[ii],sd_str[ii]); 
            Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
          }
        }
        else if(cs[ii]<BUILTIN_SDCARD)
      #endif
      {
        pinMode(cs[ii],OUTPUT); digitalWriteFast(cs[ii],HIGH);
        if(!sdx[ii].begin(cs[ii])) 
        { Serial.printf("SD Storage %d %d %s failed or missing",ii,cs[ii],sd_str[ii]);  Serial.println();
        }
        else
        {
          storage.addFilesystem(sdx[ii], sd_str[ii]);
          uint64_t totalSize = sdx[ii].totalSize();
          uint64_t usedSize  = sdx[ii].usedSize();
          Serial.printf("SD Storage %d %d %s ",ii,cs[ii],sd_str[ii]); 
          Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
        }
      }
    
    }
    #endif

#if USE_LFS_RAM==1
  for (int ii=0; ii<nfs_ram;ii++) {
    if (!ramfs[ii].begin(lfs_ram_size[ii])) {
      DBGSerial.printf("Ram Storage %d %s failed or missing",ii,lfs_ram_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(ramfs[ii], lfs_ram_str[ii]);
      uint64_t totalSize = ramfs[ii].totalSize();
      uint64_t usedSize  = ramfs[ii].usedSize();
      DBGSerial.printf("RAM Storage %d %s %llu %llu\n", ii, lfs_ram_str[ii],
        totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_PROGM==1
  for (int ii=0; ii<nfs_progm;ii++) {
    if (!progmfs[ii].begin(lfs_progm_size[ii])) {
      DBGSerial.printf("Program Storage %d %s failed or missing",ii,lfs_progm_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(progmfs[ii], lfs_progm_str[ii]);
      uint64_t totalSize = progmfs[ii].totalSize();
      uint64_t usedSize  = progmfs[ii].usedSize();
      DBGSerial.printf("Program Storage %d %s %llu %llu\n", ii, lfs_progm_str[ii],
        totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_QSPI==1
  for(int ii=0; ii<nfs_qspi;ii++) {
    if(!qspifs[ii].begin()) {
      DBGSerial.printf("QSPI Storage %d %s failed or missing",ii,lfs_qspi_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(qspifs[ii], lfs_qspi_str[ii]);
      uint64_t totalSize = qspifs[ii].totalSize();
      uint64_t usedSize  = qspifs[ii].usedSize();
      DBGSerial.printf("QSPI Storage %d %s %llu %llu\n", ii, lfs_qspi_str[ii], totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_SPI==1
  for (int ii=0; ii<nfs_spi;ii++) {
    if (USE_SW_PU == 1) {
      pinMode(lfs_cs[ii],OUTPUT);
      digitalWriteFast(lfs_cs[ii],HIGH);
    }
    if (!spifs[ii].begin(lfs_cs[ii], SPI)) {
      DBGSerial.printf("SPIFlash Storage %d %d %s failed or missing",ii,lfs_cs[ii],lfs_spi_str[ii]);      DBGSerial.println();
    } else {
      storage.addFilesystem(spifs[ii], lfs_spi_str[ii]);
      uint64_t totalSize = spifs[ii].totalSize();
      uint64_t usedSize  = spifs[ii].usedSize();
      DBGSerial.printf("SPIFlash Storage %d %d %s %llu %llu\n", ii, lfs_cs[ii], lfs_spi_str[ii],
        totalSize, usedSize);
    }
  }
#endif
#if USE_LFS_NAND == 1
  for(int ii=0; ii<nspi_nsd;ii++) {
    if (USE_SW_PU == 1) {
      pinMode(nspi_cs[ii],OUTPUT);
      digitalWriteFast(nspi_cs[ii],HIGH);
    }
    if(!nspifs[ii].begin(nspi_cs[ii], SPI)) {
      DBGSerial.printf("SPIFlash NAND Storage %d %d %s failed or missing",ii,nspi_cs[ii],nspi_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(nspifs[ii], nspi_str[ii]);
      uint64_t totalSize = nspifs[ii].totalSize();
      uint64_t usedSize  = nspifs[ii].usedSize();
      DBGSerial.printf("Storage %d %d %s %llu %llu\n", ii, nspi_cs[ii], nspi_str[ii],
        totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_QSPI_NAND == 1
  for(int ii=0; ii<qnspi_nsd;ii++) {
    if(!qnspifs[ii].begin()) {
       DBGSerial.printf("QSPI NAND Storage %d %s failed or missing",ii,qnspi_str[ii]); DBGSerial.println();
    } else {
      storage.addFilesystem(qnspifs[ii], qnspi_str[ii]);
      uint64_t totalSize = qnspifs[ii].totalSize();
      uint64_t usedSize  = qnspifs[ii].usedSize();
      DBGSerial.printf("Storage %d %s %llu %llu\n", ii, qnspi_str[ii], totalSize, usedSize);
    }
  }
#endif

}

void setup()
{

  // Open serial communications and wait for port to open
  while (!Serial && !DBGSerial.available() && millis() < 5000) 

  DBGSerial.print(CrashReport);
  DBGSerial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
  delay(3000);
  
  //Setup Audio
  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(50);

  sgtl5000_1.enable();
  sgtl5000_1.volume(volume);


  //This is mandatory to begin the mtpd session.
  mtpd.begin();
  
  storage_configure();

  #if USE_MSC == 1
  myusb.begin();
  DBGSerial.print("Initializing MSC Drives ...");
  DBGSerial.println("\nInitializing USB MSC drives...");
  DBGSerial.println("MSC and MTP initialized.");
  checkMSCChanges();
  #endif
  DBGSerial.println("\nSetup done");
  menu();
}

int ReadAndEchoSerialChar() {
  int ch = DBGSerial.read();
  if (ch >= ' ') DBGSerial.write(ch);
  return ch;
}

uint8_t storage_index = '0';
void loop()
{
  if ( DBGSerial.available() ) {
    uint8_t command = DBGSerial.read();
    int ch = DBGSerial.read();
    if ('2'==command) storage_index = CommandLineReadNextNumber(ch, 0);
    while (ch == ' ') ch = DBGSerial.read();

    uint32_t fsCount;
    switch (command) {
    case '1':
      // first dump list of storages:
      fsCount = storage.getFSCount();
      DBGSerial.printf("\nDump Storage list(%u)\n", fsCount);
      for (uint32_t ii = 0; ii < fsCount; ii++) {
        if ( ii == storage_index )
          DBGSerial.print("STORE");
        else
          DBGSerial.print("store");
        DBGSerial.printf(":%u storage:%x name:%s fs:%x\n", ii, mtpd.Store2Storage(ii),
          storage.getStoreName(ii), (uint32_t)storage.getStoreFS(ii));
      }
      //DBGSerial.println("\nDump Index List");
      //storage.dumpIndexList();
      break;
    case '2':
      if (storage_index < storage.getFSCount()) {
        DBGSerial.printf("Storage Index %u Name: %s Selected\n", storage_index,
          storage.getStoreName(storage_index));
        myfs = storage.getStoreFS(storage_index);
        current_store = storage_index;
      } else {
        DBGSerial.printf("Storage Index %u out of range\n", storage_index);
      }
      break;

    case 'l': listFiles(); break;
    case 'e': eraseFiles(); break;
    case 'p':
    {
      write_data = true;   // sets flag to continue to write data until new command is received
      int errorMp3 = 0;
      DBGSerial.print("Playing file: ");
      DBGSerial.println(filename);
      // Start playing the file.  This sketch continues to
      // run while the file plays.
      errorMp3 = playMp31.play(myfs, filename);
      DBGSerial.printf("play() error = %d\r\n",errorMp3);
      // Simply wait for the file to finish playing.
      if(errorMp3 != 0) write_data = false;
      break;
    }
    case 'P': 
      playDir(myfs);
      break;
    case'r':
      DBGSerial.println("Reset");
      mtpd.send_DeviceResetEvent();
      break;
    case 'R':
      DBGSerial.print(" RESTART Teensy ...");
      delay(100);
      SCB_AIRCR = 0x05FA0004;
      break;
    case '\r':
    case '\n':
    case 'h': menu(); break;
    }
    while (DBGSerial.read() != -1) ; // remove rest of characters.
  } else {
    #if USE_MSC == 1
    checkMSCChanges();
    #endif
    mtpd.loop();
  }

  if (write_data) {
    while (playMp31.isPlaying()) {
      // uncomment these lines if your audio shield
      // has the optional volume pot soldered
      //  float vol = analogRead(15);
      //  vol = vol / 1024;
      //  sgtl5000_1.volume(vol);
      //   delay(250);
    }
  }
}

#if USE_MSC == 1
void checkMSCChanges() {
  myusb.Task();

  USBMSCDevice mscDrive;
  PFsLib pfsLIB;
  for (uint8_t i=0; i < CNT_DRIVES; i++) {
    if (*pdrives[i]) {
      if (!drive_previous_connected[i]) {
        if (mscDrive.begin(pdrives[i])) {
          Serial.printf("\nUSB Drive: %u connected\n", i);
          pfsLIB.mbrDmp(&mscDrive, (uint32_t)-1, Serial);
          Serial.printf("\nTry Partition list");
          pfsLIB.listPartitions(&mscDrive, Serial);
          drive_previous_connected[i] = true;
        }
      }
    } else {
      drive_previous_connected[i] = false;
    }
  }
  bool send_device_reset = false;
  for (uint8_t i = 0; i < CNT_MSC; i++) {
    if (*pmsFS[i] && (pmsfs_store_ids[i] == 0xFFFFFFFFUL)) {
      Serial.printf("Found new Volume:%u\n", i); Serial.flush();
      // Lets see if we can get the volume label:
      char volName[20];
      if (pmsFS[i]->mscfs.getVolumeLabel(volName, sizeof(volName)))
        snprintf(pmsFS_display_name[i], sizeof(pmsFS_display_name[i]), "MSC%d-%s", i, volName);
      else
        snprintf(pmsFS_display_name[i], sizeof(pmsFS_display_name[i]), "MSC%d", i);
      pmsfs_store_ids[i] = storage.addFilesystem(*pmsFS[i], pmsFS_display_name[i]);

      // Try to send store added. if > 0 it went through = 0 stores have not been enumerated
      if (mtpd.send_StoreAddedEvent(pmsfs_store_ids[i]) < 0) send_device_reset = true;
    }
    // Or did volume go away?
    else if ((pmsfs_store_ids[i] != 0xFFFFFFFFUL) && !*pmsFS[i] ) {
      if (mtpd.send_StoreRemovedEvent(pmsfs_store_ids[i]) < 0) send_device_reset = true;
      storage.removeFilesystem(pmsfs_store_ids[i]);
      // Try to send store added. if > 0 it went through = 0 stores have not been enumerated
      pmsfs_store_ids[i] = 0xFFFFFFFFUL;
    }
  }
  if (send_device_reset) mtpd.send_DeviceResetEvent();
}
#endif


void menu()
{
  DBGSerial.println();
  DBGSerial.println("Menu Options:");
  DBGSerial.println("\t1 - List Drives (Step 1)");
  DBGSerial.println("\t2# - Select Drive # for Logging (Step 2)");
  DBGSerial.println("\tl - List files on disk");
  DBGSerial.println("\te - Erase files on disk with Format");
  DBGSerial.println("\tp - Play Hardcoded test file");
  DBGSerial.println("\tP - Play all music on selected device");
  DBGSerial.println("\tr - Reset MTP");
  DBGSerial.printf("\t%s","R - Restart Teensy");
  DBGSerial.println("\n\th - Menu");
  DBGSerial.println();
}

void listFiles()
{
  DBGSerial.print("\n Space Used = ");
  DBGSerial.println(myfs->usedSize());
  DBGSerial.print("Filesystem Size = ");
  DBGSerial.println(myfs->totalSize());

  printDirectory(myfs);
}

void eraseFiles()
{
  //DBGSerial.println("Formating not supported at this time");
  DBGSerial.println("\n*** Erase/Format started ***");
  myfs->format(1, '.', DBGSerial);
  DBGSerial.println("Completed, sending device reset event");
  mtpd.send_DeviceResetEvent();
}

void printDirectory(FS *pfs) {
  DBGSerial.println("Directory\n---------");
  printDirectory(pfs->open("/"), 0);
  DBGSerial.println();
}

void printDirectory(File dir, int numSpaces) {
  while (true) {
    File entry = dir.openNextFile();
    if (! entry) {
      //DBGSerial.println("** no more files **");
      break;
    }
    printSpaces(numSpaces);
    DBGSerial.print(entry.name());
    if (entry.isDirectory()) {
      DBGSerial.println("/");
      printDirectory(entry, numSpaces + 2);
    } else {
      // files have sizes, directories do not
      printSpaces(36 - numSpaces - strlen(entry.name()));
      DBGSerial.print("  ");
      DBGSerial.println(entry.size(), DEC);
    }
    entry.close();
  }
}

void printSpaces(int num) {
  for (int i = 0; i < num; i++) {
    DBGSerial.print(" ");
  }
}


uint32_t CommandLineReadNextNumber(int &ch, uint32_t default_num) {
  while (ch == ' ') ch = DBGSerial.read();
  if ((ch < '0') || (ch > '9')) return default_num;

  uint32_t return_value = 0;
  while ((ch >= '0') && (ch <= '9')) {
    return_value = return_value * 10 + ch - '0';
    ch = DBGSerial.read();
  }
  return return_value;
}


void playFile(const char *filename)
{
  int filetype;
  int errAudio = 0;
  
  filetype = 0;
  if (strstr(filename, ".MP3") != NULL || strstr(filename, ".mp3") != NULL) {
      filetype = 1;
  } else if (strstr(filename, ".WAV") != NULL || strstr(filename, ".wav") != NULL ) {
      filetype = 2;
  } else if (strstr(filename, ".AAC") != NULL || strstr(filename, ".aac") != NULL) {
      filetype = 3;
  } else if (strstr(filename, ".RAW") != NULL || strstr(filename, ".raw") != NULL) {
      filetype = 4;
  } else if (strstr(filename, ".FLA") != NULL || strstr(filename, ".fla") != NULL ) {
      filetype = 5;    
  } else {
      filetype = 0;
  }
  if (filetype > 0) {
    DBGSerial.print("Playing file: ");
    DBGSerial.println(filename);
    
    switch (filetype) {
      case 1 :
        mixer1.gain(0, volume);
        mixer1.gain(1, volume);
        mixer1.gain(2, 0.0f);
        mixer1.gain(3, 0.0f);
        errAudio = playMp31.play(myfs, filename);
        if(errAudio != 0) {
          Serial.printf("Audio Error: %d\n", errAudio);
          break;
        }
        delay(5);
        while (playMp31.isPlaying()) {
        }
        break;
      case 2 :
        mixer1.gain(0, 0.0f);
        mixer1.gain(1, 0.0f);
        mixer1.gain(2, volume);
        mixer1.gain(3, volume);
        errAudio = playWav.play(myfs, filename);
        if(errAudio == 0) {
          Serial.printf("Audio Error: %d\n", errAudio);
          break;
        }
        delay(5);
        while (playWav.isPlaying()) {
        }
        break;
      case 3 :
        mixer2.gain(0, volume);
        mixer2.gain(1, volume);
        mixer2.gain(2, 0.0f);
        errAudio = playAac.play(myfs, filename);
        if(errAudio != 0) {
          Serial.printf("Audio Error: %d\n", errAudio);
          break;
        }
        delay(5);
        while (playAac.isPlaying()) {
        }
        break;
      case 4 :
        mixer2.gain(0, 0.0f);
        mixer2.gain(1, 0.0f);
        mixer2.gain(2, volume);
        errAudio = playRaw.play(myfs, filename);
        if(errAudio == 0) {
          Serial.printf("Audio Error: %d\n", errAudio);
          break;
        }
        delay(5);
        while (playRaw.isPlaying()) {
        }
        break;
      case 5 :
        mixer3.gain(0, volume);
        mixer3.gain(1, volume);
        errAudio =playFlac.play(myfs, filename);
        if(errAudio != 0) {
          Serial.printf("Audio Error: %d\n", errAudio);
          break;
        }
        delay(5);
        while (playFlac.isPlaying()) {
        }
        break;
    }
  }
}

void playDir(FS *pfs) {
  DBGSerial.println("Playing files on device");
  playAll(pfs->open("/"));
  DBGSerial.println();
}

void playAll(File dir){
  char filename[64];
  char filnam[64];
   while(true) {
     File entry =  dir.openNextFile();
     if (! entry) {
       // no more files
       // rewind to begining of directory and start over
       dir.rewindDirectory();
       break;
     }
     //DBGSerial.print(entry.name());
     if (entry.isDirectory()) {
       //DBGSerial.println("Directory/");
       //do nothing for now
       //DBGSerial.println(entry.name());
       playAll(entry);
     } else {
       // files have sizes, directories do not
       //DBGSerial.print("\t\t");
       //DBGSerial.println(entry.size(), DEC);
       // files have sizes, directories do not
       strcpy(filename, dir.name());
       if(strlen(dir.name()) > 0) strcat(filename, "/");
       strcat(filename, strcpy(filnam, entry.name()));
       playFile(filename);
     }
   entry.close();
 }
}
 
Back
Top