Yet Another File Player (and recorder)

Since in this thread it seems the puzzle pieces might come together i like to say Hi to you all !
@Nic, if you are reading here too, thank you again for your help and providing your great work making the only public "known" way how to play pitch shifted samples with the teensy.
It is working great and reliable from progmem.
@h4yn0nnym0u5e If a similar result can be achivied with the sd card, that would be even more fantastic since then the sample content could be changed during runtime.

We tried out the alternatives like soldered-on Flash (with littlefs) = worst result, extern SPI Flash (somewhat better) etc but all have drawbacks.
Our goal is not hundreds of samples in stereo, we would be very happy with 4-8? voices in mono, but with pitchshifting +- some semitones. If possible, with avoiding soldering psram since this is a hurdle to solder for an average musican, that might be interested in our free project.
 
8 mono voices at near-normal speed should be fine, I think, even without PSRAM. For the SDPiano demo I used 8 stereo voices, each with a 32k buffer, for a total of 256k, or about a quarter of a Teensy 4.x's RAM. So 16k per mono voice would probably be OK, unless you wanted 2x playback (one octave shift), where you're reading the card at the same speed as if the sample was stereo! Extra memory is good to have for getting lowest possible latency from note trigger to first audio, but a decent SD card helps there, and in any case getting substantially under 10ms means tinkering with the depths of the Audio library to process fewer than 128 samples per update.

I believe I've read somewhere that there are suppliers that will ship Teensy 4.1 with PSRAM pre-fitted, so that may be an option if RAM is really squeezed for a particular project. It's not the hardest soldering job in the world, but I appreciate that I'm saying that from the perspective of one with a fair few years' experience!

I'm hopeful that Nic's code can be adapted from its present "just barely in time" file reading strategy to the "predictive / not-from-interrupt" one I've adopted. As long as I can either figure out or ignore all the bits I don't (yet) understand...
 
I believe I've read somewhere that there are suppliers that will ship Teensy 4.1 with PSRAM pre-fitted, so that may be an option if RAM is really squeezed for a particular project. It's not the hardest soldering job in the world, but I appreciate that I'm saying that from the perspective of one with a fair few years' experience!

The company I've used is Proto Supplies. While I've done SMT soldering in the past, I prefer these days to let somebody else do it.

 
Hi guys,

Thanks for this very promising library :)
I am working with @positionhigh on MicroDexed-touch, that's why we are so interested in this library.

I've tested the SDpiano on Teensy 4.1 with PSRAM.
Impressive, I don't hear any latency. I can play 6 voices, that's great.
But can't reach the 8 voices, could it be my SD card that isn't fast enough (SanDisk Ulta 32GB class A1) ?
Is there some log I could add to detect where is the limit ?

I've seen some samples are missing but I don't think it could be a problem, for example :
55: /piano/soft/G3-1-48.wav , /piano/med/G3-49-96.wav , /piano/loud/G3-97-127.wav
56: /piano/soft/Ab3-1-48.wav , (null) , /piano/loud/Ab3-97-127.wav
 
Last edited:
After some tests, I had some doubts, and as I don't have any MIDI synths plugged now, I had simply sent notes from my pc keyboard.
But it seems there is a hardware limitation to only 6 keys pressed at the same time.
So I have drawn a simple bar in Reason that sends MIDI notes to the Teensy with SDpiano. The 8 notes from a scale are played and I think it's playing all of them :) :) :)
 
The company I've used is Proto Supplies. While I've done SMT soldering in the past, I prefer these days to let somebody else do it.

That's great, thanks Michael. It'd be good if there were similar suppliers in the UK and EU, but I guess the trouble is that you either do a lot of last-minute build and test, or have a lot of stock of different configurations.
 
Of course I meant I heard the 8 stereo samples played from Sdpiano.
Good news. I thought it worked, but it's always good to have it confirmed by an independent witness!

There's some rather stale code between lines 333 and 345 of the SDPiano demo which monitors voice allocation and each voice's status - it's #if'd out at the moment, and I can't guarantee it'll work if you just enable it, but it shows the sort of thing you could do to see what all the playback channels are doing. I notice it's a bit naughty in that it assumes 8 voices when there's a perfectly good VOICES macro, or indeed COUNT_OF(pv) ... forgive me, it was just supposed to be a quick bit of test code.
 
No problem. I try it.

Got problem with this line Serial.printf("; ocnt=%d, myOcnt=%d\n", ocnt, myOcnt);
These variables are not found.

Anyway it's very interesting to see the progress of allocation :
No voice played #0 128, 0, 0 / #1 128, 0, 0 / #2 128, 0, 0 / #3 128, 0, 0 / #4 128, 0, 0 / #5 128, 0, 0 / #6 128, 0, 0 / #7 128, 0, 0
1 voice playing #0 128, 0, 0 / #1 128, 0, 0 / #2 128, 0, 0 / #3 128, 0, 0 / #4 128, 0, 0 / #5 128, 0, 0 / #6 128, 0, 0 / #7 60, 1, 3
...
2 voices playing #0 128, 0, 0 / #1 128, 0, 0 / #2 128, 0, 0 / #3 128, 0, 0 / #4 128, 0, 0 / #5 128, 0, 0 / #6 62, 1, 3 / #7 60, 4, 4
...
8 voices playing #0 72, 4, 4 / #1 71, 4, 4 / #2 69, 4, 4 / #3 67, 4, 4 / #4 65, 4, 4 / #5 64, 4, 4 / #6 62, 4, 4 / #7 60, 4, 4
after releasing notes #0 128, 0, 0 / #1 128, 0, 0 / #2 128, 0, 0 / #3 128, 0, 0 / #4 128, 0, 0 / #5 128, 0, 0 / #6 128, 0, 0 / #7 128, 0, 0
 
Made a quick test with only 4 voices allocated.
When I send the same MIDI notes, I only hear the first 4 notes played.
The next 4 ones are ignored. Could it be possible to "steal the oldest" voices in the allocation (like I had on my old synths to manage polyphony :) ) ?
Or better, have the choice between several allocation modes ?
 
Sure, much better (or at least different) allocation strategies are possible ... but it's not intended as a demo of MIDI note allocation, just of the buffered playback! Actually, you may have found a bug ... I thought if you sent the same note again it would re-start using the same voice, but it looks as if I've failed in some way. I've raised an issue in my github, but not regarding it as urgent.
 
Ah ok, sorry I haven't checked in the code. I thought the voice allocation for buffer playback wasn't related to MIDI notes.
About the "bug" I haven't sent several times the same note, I have used the 8 notes from a scale.
If you need me to test it further let me know, I would be very happy to help as your library may solve that SD playback we had dreamt of :)

We hadn't made use of PSRAM yet as Mark (positionhigh) thinks that MicroDexed-touch users will have some difficulties with soldering those tiny things (I had success with that but I really don't like SMD soldering).
I've spent a lot of time with SerialFlash with a flash chip but this has some limitations, at least with Nic's library.
Your approach with buffers could also be a help for flash.
Pitch shifting is working great, I have to admit that I don't understand Nic's code for that part.

I am now sure PSRAM with buffers preload/cache is the way to go.
I don't know if it could be of any use but I've read that PSRAM could be used with LittleFS as a RAM disk.
Code:
EXTMEM char buf[8 * 1024 * 1024];  This allocates all 8Mb of the PSRAM for the RAM disk.
LittleFS_RAM myfs;
...

 if (!myfs.begin(buf, sizeof(buf))) {
    Serial.printf("Error starting %s\n", "RAM DISK);
  }

David
 
Made another SDpiano test with inHeap and only 16KB buffers. Always with 8 notes, so 8 stereo samples, and it still works fine.
Do you think 16KB isn't enough ? That means refill above 8KB.
Of course with MicroDexed-touch we won't have much RAM left so I guess PSRAM will be our only choice.
 
Made another SDpiano test with inHeap and only 16KB buffers. Always with 8 notes, so 8 stereo samples, and it still works fine.
Do you think 16KB isn't enough ? That means refill above 8KB.
Of course with MicroDexed-touch we won't have much RAM left so I guess PSRAM will be our only choice.
How much buffer is "enough" is always likely to be application-dependent. If you have a fairly benign one that exits loop() or calls yield() or delay() often, then the EventResponder will have frequent opportunities to re-fill the buffers and you'll get away with them being smaller. But anything that hogs the CPU (say, a blocking display) will need bigger buffers to ensure the playback object isn't starved. I'd guess a MIDI synth will probably be OK, because you'll need to be responsive to MIDI messages, so there should be places to slip in the odd yield() call. But ... bear in mind that call may take longer than expected, because it's doing SD card reads behind the scenes!

The only reason I chose 32kB buffers for SDPiano was the size of the PSRAM. 88 notes x 3 = 264 samples gave me a sensible choice of 60 sectors pre-loaded for each sample, taking 7920kB and leaving space for 8x 32kB buffers. One could play around a bit with 16x 16kB buffers to see if you could get that many voices, or drop the pre-load to 58 sectors to keep the buffers at 32kB, and so on. 16 stereo voices still only requires a total read rate of 2.7MB/s, but even when a decent SD card can reach 22MB/s for a single file, that drops significantly when multiple files are involved - to about 10MB/s total when reading 16 files in 8kB chunks, say. Plus you do get the occasional longer read time due to SD internal weirdnesses.

Ideally you develop your code to be flexible, then test carefully to see how much margin you have, and make sure it's enough. For a given value of enough.
 
Can I ask for suggestions on how to record multiple files in a row, pretty much the way audio recorders work? The current way Record.ino works replaces the recorded file each time you record. I need it to record a new file each time pressing the record button, with a differently numbered filename.

If there is a code around that already does that, or any suggestions on how to achieve it using the AudioRecordWAVstereo type, I will really appreciate it.
 
Something like this should help - I haven't tested it beyond compiling it, though:
Code:
/*
 * Generate a new sequential filename in the
 * recFileName buffer. Adjust recFileFormat to your
 * needs.
 */
char recFileName[20];
void setNextRecFileName(void)
{
  static int recFileNum = 1;
  const char* recFileFormat = "Rec-%09d.wav";

  // Search filesystem for unused filename - may take some
  // time on first call if there are a lot of files already
  // recorded!
  do
  {
    snprintf(recFileName, sizeof recFileName, recFileFormat, recFileNum);
  } while (SD.exists(recFileName));
}

/*
 * Start recording in a new file
 */
bool startNewRecording(AudioRecordWAVstereo& rec)
{
  setNextRecFileName();
  return rec.recordSD(recFileName); 
}
It's really up to you as the programmer to determine how to manage your file naming, creation and deletion - the AudioRecordWAVstereo class can't help you there, apart from returning "false" if it can't start the recording for some reason!
 
Thanks you @h4yn0nnym0u5e. I couldn't find out how to adapt your code to mine honestly, but found an old sketch a friend made some time ago for multifile recording using an eeprom function. Hard for me to fully understand it but it works, just PM me anyone interested on it; I don't post it here because it is a bit offtopic.
 
Hi,

I'm trying to adjust the volume while playing a WAV using AudioPlayWAVstereo, but ain't working for me.
AudioPlayWAVstereo is routed to a mixer and the mixer to a I2S output. Volume set by a pot and float on mixerX.gain(0, 2*vol);.

Strange enough it works for AudioRecordWAVstereo, but not for AudioPlayWAVstereo.

The mixers in question (mixer1 and mixer2) are shared by both Record and Play functions. Changing the volume does nothing on playback.
If I create a separate mixer for playback and change the volume on it, then nothing is played at all.

Here is my the whole code if needed:

Code:
//Use following Audio Library (SD Buffered)
//https://github.com/h4yn0nnym0u5e/Audio/tree/feature/buffered-SD
//A backup was saved in SoundRecorder archive folder.
//Replace content on /usr/share/arduino/hardware/teensy/avr/libraries/Audio

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <Bounce.h>
#include <SerialFlash.h>
#include <EEPROM.h>

AudioInputI2S            i2s1;           //xy=220,698
AudioInputUSB            usb2;           //xy=223,853
AudioPlayWAVstereo       playWAVstereo1;     //xy=244,609
AudioRecordWAVstereo     recordWAVstereo1; //xy=321,98
AudioMixer4              mixer1;         //xy=468,691
AudioMixer4              mixer2;         //xy=467,769
AudioMixer4              mixer3;         //xy=470,854
AudioMixer4              mixer4;         //xy=482,934
AudioAnalyzePeak         peak1;          //xy=603,576
AudioOutputI2S2          i2s2_1;         //xy=643,732
AudioOutputUSB           usb1;           //xy=649,914
AudioConnection          patchCord1(i2s1, 0, mixer1, 0);
AudioConnection          patchCord2(i2s1, 1, mixer2, 0);
AudioConnection          patchCord3(i2s1, 0, mixer3, 0);
AudioConnection          patchCord4(i2s1, 1, mixer4, 0);
AudioConnection          patchCord5(playWAVstereo1, 0, mixer1, 2);
AudioConnection          patchCord6(playWAVstereo1, 1, mixer2, 2);
AudioConnection          patchCord7(usb2, 0, mixer1, 1);
AudioConnection          patchCord8(usb2, 1, mixer2, 1);
AudioConnection          patchCord9(mixer1, 0, i2s2_1, 0);
AudioConnection          patchCord10(i2s1, 0, recordWAVstereo1, 0);
AudioConnection          patchCord11(i2s1, 0, peak1, 0);
AudioConnection          patchCord12(mixer2, 0, i2s2_1, 1);
AudioConnection          patchCord13(i2s1, 1, recordWAVstereo1, 1);
AudioConnection          patchCord14(mixer3, 0, usb1, 0);
AudioConnection          patchCord15(mixer4, 0, usb1, 1);

//Set clock speed
//#if defined(__IMXRT1062__)
//extern "C" uint32_t set_arm_clock(uint32_t frequency);
//#endif

//uint32_t count, prior_count;
//uint32_t prior_msec;
//uint32_t count_per_second;

//bool flip = true;


const int pdnADC = 22;
const int VOLUME_POT = 19;

Bounce buttonRecord = Bounce(10, 8);
Bounce buttonStop =   Bounce(9, 8);  // 8 = 8 ms debounce time
Bounce buttonPlay =   Bounce(7, 8);

#define SDCARD_CS_PIN    BUILTIN_SDCARD
#define SDCARD_MOSI_PIN  11  // not actually used
#define SDCARD_SCK_PIN   13  // not actually used

// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording, 2=playing

int File_Number;
int address = 10;

// The file where data is recorded
File frec;

void setup() {

//Set clock speed 96MHz
//#if defined(__IMXRT1062__)
//    set_arm_clock(96000000);
//#endif
//  Serial.begin(1000000); // edit for highest baud your board can use
//  while (!Serial) ;
//#if defined(__IMXRT1062__)
//    //set_arm_clock(24000000);
//    Serial.print("F_CPU_ACTUAL=");
//    Serial.println(F_CPU_ACTUAL);
//#endif

  AudioMemory(12);

  pinMode(pdnADC, OUTPUT);
  delay(500);               // wait for 10 seconds
  digitalWrite(pdnADC, HIGH);    // set the PDN on

  //mdr20190828 IOMUXC stuff below was required to get SDcard to work and to attach to other boards.
  //from: https://forum.pjrc.com/threads/57167-Teensy-4-0-I2S-Support?p=213128&viewfull=1#post213128
  // defaults were all 0x10B0 which is keeper, Medium speed (100 Mhz), drive strength = R0/6 = 150/6 = 25 ohms (the second strongest drive strength available) // My changes were:
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_09 = 0x8; // MCLK, low speed, drive strength at R0 (150 ohm).
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_10 = 0x8; // LRCLK
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_11 = 0x8; // BCKL
  IOMUXC_SW_PAD_CTL_PAD_GPIO_B1_01 = 0x8; // OUT1A
  IOMUXC_SW_PAD_CTL_PAD_GPIO_B1_00 = 0x8; // IN1
  IOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_04 = 0x8; // OUT2
  IOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_05 = 0x8; // LRCLK2
  IOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_06 = 0x8; // BCLK2
  IOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_07 = 0x8; // MCLK2
  IOMUXC_SW_PAD_CTL_PAD_GPIO_B1_00 = 0x8; // IN2
  //IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_00 = 0x8;
  //IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_01 = 0x8;
  //IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_02 = 0x8;
  //IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_03 = 0x8;
  //IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_04 = 0x8;
  //IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_05 = 0x8;

  
  // Configure the pushbutton pins
  pinMode(7, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);

  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
   }
  }

    // SD audio objects need buffers configuring:
  const size_t sz = 65536;
  const AudioBuffer::bufType bufMem = AudioBuffer::inHeap;
  playWAVstereo1.createBuffer(sz,bufMem);
  recordWAVstereo1.createBuffer(sz,bufMem);

  //Filename numbers
  File_Number = EEPROM16_Read(address);
  Serial.print("Actual File Number: ");
  Serial.println(File_Number);

}

void loop() {

  float vol = analogRead(VOLUME_POT);
  vol = vol / 1024;
  mixer1.gain(0, 2*vol);
  mixer2.gain(0, 2*vol);
  Serial.print("Digital volume is: ");
  Serial.println(vol);
  delay(250);

  // First, read the buttons
  buttonRecord.update();
  buttonStop.update();
  buttonPlay.update();

  // Respond to button presses
  if (buttonRecord.fallingEdge()) {
    Serial.println("Record Button Press");
    if (mode == 2) stopPlaying();
    if (mode == 0) startRecording();
  }
  if (buttonStop.fallingEdge()) {
    Serial.println("Stop Button Press");
    if (mode == 1) stopRecording();
    if (mode == 2) stopPlaying();
  }
  if (buttonPlay.fallingEdge()) {
    Serial.println("Play Button Press");
    if (mode == 1) stopRecording();
    if (mode == 0) startPlaying();
//    if (mode == 2) playPrevious();
  }

  // If we're playing or recording, carry on...
  if (mode == 1) {
    continueRecording();
  }
  if (mode == 2) {
    continuePlaying();
  }
}


void startRecording() {
  Serial.print("startRecording RECORD_");
  Serial.println(File_Number);
  char buffer[40];
  sprintf(buffer, "%d.wav", File_Number);
  if (SD.exists(buffer)) {  
    // The SD library writes new data to the end of the
    // file, so to start a new recording, the old file
    // must be deleted before new data is written.
    SD.remove(buffer);
  }
  frec = SD.open(buffer, FILE_WRITE);
  if (frec) {
    
patchCord9.disconnect();
patchCord12.disconnect();
delay(200);
patchCord9.connect();
patchCord12.connect();
  
  recordWAVstereo1.recordSD(buffer);
  mode = 1;
  }
}

void continueRecording() {
  // nothing to do here!
}

void stopRecording() {
  Serial.println("stopRecording");
  recordWAVstereo1.stop();

  patchCord9.disconnect();
  patchCord12.disconnect();
  delay(200);
  patchCord9.connect();
  patchCord12.connect();
  
  File_Number = File_Number + 1;
  EEPROM16_Write(address, File_Number);
  mode = 0;
}


void startPlaying() {
  Serial.print("startPlaying RECORD_");
  Serial.println(File_Number - 1);
  
  char buffer[40];
  sprintf(buffer, "%d.wav", File_Number - 1);
  
  playWAVstereo1.playSD(buffer);
  patchCord1.disconnect();
  patchCord2.disconnect();
  mode = 2;
}

void continuePlaying() {
  if (!playWAVstereo1.isPlaying()) 
  {
    Serial.println("endOfPlayback");
   patchCord1.connect();
   patchCord2.connect();
    mode = 0;
  }
}
  
void stopPlaying() {
  Serial.println("stopPlaying");
  playWAVstereo1.stop();
  patchCord1.connect();
  patchCord2.connect();
  mode = 0;
}

void EEPROM16_Write(uint8_t a, uint16_t b) {
  EEPROM.write(a, lowByte(b));
  EEPROM.write(a + 1, highByte(b));
}

uint16_t EEPROM16_Read(uint8_t a) {
  return word(EEPROM.read(a + 1), EEPROM.read(a));
}
 
You're only changing the gain on channel 0 of mixer1 and mixer2 ... the playback object is connected to channel 2 of those mixers.

I'm not sure what you mean by "it works for AudioRecordWAVstereo"; the recordWAVstereo1 object is connected directly to the I2S input, not via any mixer, so you won't be able to change the record level.
 
This is awesome, it works fine to me. I'm using your dynamic audio core, with VScode and platformIO extension, it gives some warnings about redefinitions when compiling (but as I said, it works):
Code:
In file included from src/play_wav_buffered.h:34:0,

src/AudioBuffer.h:35:0: warning: "SCOPE_ENABLE" redefined
 #define SCOPE_ENABLE(...) 
 ^
In file included from /Users/jesus/.platformio/packages/framework-arduinoteensy/libraries/Audio/analyze_fft256.h:31:0,
                 from /Users/jesus/.platformio/packages/framework-arduinoteensy/libraries/Audio/Audio.h:61,

/Users/jesus/.platformio/packages/framework-arduinoteensy/cores/teensy4/AudioStream.h:66:0: note: this is the location of the previous definition
 #define SCOPE_ENABLE() pinMode(SCOPE_PIN,OUTPUT)
 ^
In file included from src/play_wav_buffered.h:34:0,

src/AudioBuffer.h:36:0: warning: "SCOPE_HIGH" redefined
 #define SCOPE_HIGH(...) 
 ^
In file included from /Users/jesus/.platformio/packages/framework-arduinoteensy/libraries/Audio/analyze_fft256.h:31:0,
                 from /Users/jesus/.platformio/packages/framework-arduinoteensy/libraries/Audio/Audio.h:61,

/Users/jesus/.platformio/packages/framework-arduinoteensy/cores/teensy4/AudioStream.h:67:0: note: this is the location of the previous definition
 #define SCOPE_HIGH() digitalWrite(SCOPE_PIN,scope_pin_value = 1)
 ^
In file included from src/play_wav_buffered.h:34:0,

src/AudioBuffer.h:37:0: warning: "SCOPE_LOW" redefined
 #define SCOPE_LOW(...) 
 ^
In file included from /Users/jesus/.platformio/packages/framework-arduinoteensy/libraries/Audio/analyze_fft256.h:31:0,
                 from /Users/jesus/.platformio/packages/framework-arduinoteensy/libraries/Audio/Audio.h:61,

/Users/jesus/.platformio/packages/framework-arduinoteensy/cores/teensy4/AudioStream.h:68:0: note: this is the location of the previous definition
 #define SCOPE_LOW() digitalWrite(SCOPE_PIN,scope_pin_value = 0)
 ^
In file included from src/play_wav_buffered.h:34:0,

src/AudioBuffer.h:38:0: warning: "SCOPE_TOGGLE" redefined
 #define SCOPE_TOGGLE(...)
 ^
In file included from /Users/jesus/.platformio/packages/framework-arduinoteensy/libraries/Audio/analyze_fft256.h:31:0,
                 from /Users/jesus/.platformio/packages/framework-arduinoteensy/libraries/Audio/Audio.h:61,

/Users/jesus/.platformio/packages/framework-arduinoteensy/cores/teensy4/AudioStream.h:69:0: note: this is the location of the previous definition
 #define SCOPE_TOGGLE() digitalWrite(SCOPE_PIN,scope_pin_value = !scope_pin_value)
 ^
questions:
I have only copied the AudioBuffer.h & cpp and play_wav_buffered.h & cpp, should I copy any other file?
I'm using AudioPlayWAVbuffered to play mono files, instead of AudioPlayWAVstereo as you suggest in the documentation, is there something I should worry about this?

thanks!
 
Yes, I should really tidy up the SCOPE macros, they're only intended for real-time debugging purposes...

The only other file that might be useful is the revised gui/index.html, as that allows you to place the new objects into a design, rather than using some other object and then editing the design tool output. But it's not essential.

It actually doesn't matter which object you use to play any sort of WAV file, you can have a AudioPlayWAVoct object and play a mono file through it if you want. If you look at play_wav_buffered.h, you'll see all the classes map directly to AudioPlayWAVbuffered. The only reason to distinguish them is for the design tool, so it knows how many outputs to show. The record objects are subtly different, but not by much, just enough to have the correct number of input queues.
 
Yes, I should really tidy up the SCOPE macros, they're only intended for real-time debugging purposes...
Thanks @h4yn0nnym0u5e , I'll comment all references to SCOPE in audioBuffer to avoid the warnings.
The supported files are WAV 16bit with 44.1, 22 or 11kHz correct? would it be possible to change the playback speed by using the sample rate? would be a great add on.
 
The supported files are WAV 16bit with 44.1, 22 or 11kHz correct?
Just 44.1kHz, same as the standard WAV playback object (which has a lot of code referring to other sample frequencies, but doesn’t actually implement them…).

would it be possible to change the playback speed by using the sample rate? would be a great add on.
Indeed it would, but it’s a huge additional level of complexity! I’ve undertaken to port my EventResponder-driven read approach to the variable playback library at some point, but it’ll be a while before I can get to it.
 
thanks for the prompt answer @h4yn0nnym0u5e,
I have one last question, would it be possible to adapt the parseWAVheader to make a WAV to RAW file convertor? I would like to give a try but maybe I'm wrong about audio file formats and something else has to be done apart from removing the metadata header
 
thanks for the prompt answer @h4yn0nnym0u5e,
I have one last question, would it be possible to adapt the parseWAVheader to make a WAV to RAW file convertor? I would like to give a try but maybe I'm wrong about audio file formats and something else has to be done apart from removing the metadata header

You could certainly use it to help convert WAV to RAW. I found this site to be helpful in terms of parsing the file format: http://soundfile.sapp.org/doc/WaveFormat/. One thing to bear in mind is that it is not a "header" as such - the format is specified as a series of "chunks", with no guarantee that the data chunk is the last one in the file. Indeed, I've encountered several files with chunks placed after the audio data, so if you just used everything after the data chunk header your audio would be quite likely to have a click at the end.

Here's a quick demo using the AudioWAVdata class to parse files and then printing useful information:
Code:
#include "SD.h"
#include "Audio.h"

void printWAVheader(const char* fname)
{
  AudioWAVdata wavData;
  File file=SD.open(fname);
  
  Serial.print(fname);
  if (file)
  {
    wavData.parseWAVheader(file);
    if (wavData.chanCnt > 0)    
      Serial.printf(": data starts at: %08X; %d bytes; %d bits/sample; %d channels; sample rate %dHz\n",
                    wavData.firstAudio,wavData.audioSize,wavData.bitsPerSample,wavData.chanCnt,
                    (int)(4294967296000.0f * 8 / wavData.bitsPerSample / wavData.chanCnt / wavData.bytes2millis + 0.5f)); // check this calculation!
    else                
      Serial.println(": not a WAV file");
            
  
    file.close();
  }
  else
    Serial.println(": not found");
}

void setup() {
  while (!Serial)
    ;
  SD.begin(BUILTIN_SDCARD);

  printWAVheader("not-there");
  printWAVheader("test.txt");
  printWAVheader("drums/44k-Kick-ff-2.wav");
  printWAVheader("drums/44k-Ride-Bell.wav");
}

void loop() {


}
You'll obviously have to change the filenames to suit your system. Points to note:
  • I don't 100% trust the sample rate calculation, so treat that with caution
  • if .chanCnt comes back as zero, then it doesn't look like a valid WAV file
  • firstAudio is where the raw data starts in the file, and audioSize is the raw data size in bytes
My output from this is:
Code:
not-there: not found
test.txt: not a WAV file
drums/44k-Kick-ff-2.wav: data starts at: 0000002C; 23402 bytes; 16 bits/sample; 1 channels; sample rate 44100Hz
drums/44k-Ride-Bell.wav: data starts at: 0000002C; 1406032 bytes; 16 bits/sample; 2 channels; sample rate 44100Hz
 
Back
Top