Audio Library

Status
Not open for further replies.
I'm new to teensy. I have been working on a project to record and play back voice. I'm using the audio adapter and storing the audio on the sdcard in raw format.

I read several posts here about trying to use the SdFat library with the audio adapter for faster sd access, but wasn't sure if anyone actually got it working. I was able to use it with the following changes. I'm using Arduino 1.6.6/Teensyduino and SdFat from git.

1. modify SdFat/SdSpiTeensy3.cpp

Code:
  //CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
  CORE_PIN7_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
  CORE_PIN12_CONFIG = PORT_PCR_MUX(2);
  //CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
  CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);

2. changes to teensy audio library

i. hardware/teensy/avr/libraries/Audio/play_sd_raw.h

a. change
Code:
#include "SD.h"
to
Code:
#include "SdFat.h"


b. add under private:
Code:
 SdFat SD;

ii. hardware/teensy/avr/libraries/Audio/play_sd_raw.cpp

a. in
Code:
bool AudioPlaySdRaw::play(const char *filename):

change
Code:
 rawfile = SD.open(filename);
        if (!rawfile) {
to
Code:
 if (!rawfile.open(filename,O_READ)) {

iii. hardware/teensy/avr/libraries/Audio/play_sd_wav.h

a. change
Code:
#include "SD.h"
to
Code:
#include "SdFat.h"

b. add under private:
Code:
 SdFat SD;

hardware/teensy/avr/libraries/Audio/play_sd_wav.cpp would need a similar change to play_sd_raw.cpp above (I'm just using raw format).


Here's a modified version of the recorder example. In addition to using SdFat, it calculates the min, max, and average time for the sd writes.

Code:
// Record sound as raw data to a SD card, and play it back.
//
// Requires the audio shield:
//   http://www.pjrc.com/store/teensy3_audio.html
//
// Three pushbuttons need to be connected:
//   Record Button: pin 0 to GND
//   Stop Button:   pin 1 to GND
//   Play Button:   pin 2 to GND
//
// This example code is in the public domain.
//

#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SdFat.h>

// define below to print timing information
#define WRITE_TEST 1
#ifdef WRITE_TEST
uint32_t mintime,maxtime,tottime,navg;
#endif

// GUItool: begin automatically generated code
AudioInputI2S            i2s2;           //xy=105,63
AudioAnalyzePeak         peak1;          //xy=278,108
AudioRecordQueue         queue1;         //xy=281,63
AudioPlaySdRaw           playRaw1;       //xy=302,157
AudioOutputI2S           i2s1;           //xy=470,120
AudioConnection          patchCord1(i2s2, 0, queue1, 0);
AudioConnection          patchCord2(i2s2, 0, peak1, 0);
AudioConnection          patchCord3(playRaw1, 0, i2s1, 0);
AudioConnection          patchCord4(playRaw1, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=265,212
// GUItool: end automatically generated code

const int pinRec=24;
const int pinStop=25;
const int pinPlay=26;

// Bounce objects to easily and reliably read the buttons
Bounce buttonRecord = Bounce(pinRec, 8);
Bounce buttonStop =   Bounce(pinStop, 8);  // 8 = 8 ms debounce time
Bounce buttonPlay =   Bounce(pinPlay, 8);


// which input on the audio shield will be used?
const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;

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

SdFat SD;

// The file where data is recorded
File frec;

void setup() {
  // Configure the pushbutton pins
  pinMode(pinRec, INPUT_PULLUP);
  pinMode(pinStop, INPUT_PULLUP);
  pinMode(pinPlay, INPUT_PULLUP);

#ifdef WRITE_TEST
    mintime=1000000;
    maxtime=0;
    tottime=0;
    navg=0;
#endif    

  // Audio connections require memory, and the record queue
  // uses this memory to buffer incoming audio.
  AudioMemory(60);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();
  sgtl5000_1.inputSelect(myInput);
  sgtl5000_1.volume(0.5);

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


void loop() {
  // 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 we're playing or recording, carry on...
  if (mode == 1) {
    continueRecording();
  }
  if (mode == 2) {
    continuePlaying();
  }

  // when using a microphone, continuously adjust gain
  if (myInput == AUDIO_INPUT_MIC) adjustMicLevel();
}


void startRecording() {
  Serial.println("startRecording");
  if (SD.exists("RECORD.RAW")) {
    // 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("RECORD.RAW");
  }
  frec = SD.open("RECORD.RAW", FILE_WRITE);
  if (frec) {
    queue1.begin();
    mode = 1;
  }
}

void continueRecording() {
  if (queue1.available() >= 2) {
    byte buffer[512];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer.  The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer+256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // write all 512 bytes to the SD card
#ifdef WRITE_TEST      
      elapsedMicros usec = 0;
#endif      
   frec.write(buffer, 512);
    // Uncomment these lines to see how long SD writes
    // are taking.  A pair of audio blocks arrives every
    // 5802 microseconds, so hopefully most of the writes
    // take well under 5802 us.  Some will take more, as
    // the SD library also must write to the FAT tables
    // and the SD card controller manages media erase and
    // wear leveling.  The queue1 object can buffer
    // approximately 301700 us of audio, to allow time
    // for occasional high SD card latency, as long as
    // the average write time is under 5802 us.
#ifdef WRITE_TEST
      uint32_t t=usec;
      tottime+=t;
      navg++;
      if (t>maxtime) {
        maxtime=t;      
      }
      if (t<mintime) {
        mintime=t;
      }
#endif   
  }
}

void stopRecording() {
  Serial.println("stopRecording");
  queue1.end();
  if (mode == 1) {
    while (queue1.available() > 0) {
      frec.write((byte*)queue1.readBuffer(), 256);
      queue1.freeBuffer();
    }
    frec.close();
#ifdef WRITE_TEST
    Serial.println("sdcard write timing (usec)");
    Serial.print("minimum write time=");
    Serial.println(mintime);
    Serial.print("maximum write time=");
    Serial.println(maxtime);
    Serial.print("average write time=");
    Serial.println(tottime/(float)navg);
    mintime=1000000;
    maxtime=0;
    tottime=0;
    navg=0;
#endif
  }
  mode = 0;
}


void startPlaying() {
  Serial.println("startPlaying");
  playRaw1.play("RECORD.RAW");
  mode = 2;
}

void continuePlaying() {
  if (!playRaw1.isPlaying()) {
    playRaw1.stop();
    mode = 0;
  }
}

void stopPlaying() {
  Serial.println("stopPlaying");
  if (mode == 2) playRaw1.stop();
  mode = 0;
}

void adjustMicLevel() {
  // TODO: read the peak1 object and adjust sgtl5000_1.micGain()
  // if anyone gets this working, please submit a github pull request :-)
}


Here's some timing results:

1. original SD library (recorder example with WRITE_TEST lines added). times in usec

Samsung 16 GB card

minimum write time=1357
maximum write time=10800
average write time=1553.58

Sandisk Ultra 16 GB card

minimum write time=1546
maximum write time=13079
average write time=1887.80

2. SdFat version

Samsung 16 GB

minimum write time=275
maximum write time=8962
average write time=450.00

Sandisk Ultra 16 GB

minimum write time=275
maximum write time=78657 (??!)
average write time=665.92


So it looks like SdFat does significantly improve the write speed :cool:
 
Using the playMem to play out of program memory

Another way to do it might be to install the timing RTC crystal, stitch the last half second of the sample onto the start of a duplicate sample, and implement a Half second cross fade at the calculated time.

I think the granular synth object would be a much better place to start though. Neater, more capable, etc.
 
Sorry, Newbie here.

In reading the datasheet for the SGTL5000 (http://www.nxp.com/files/analog/doc/data_sheet/SGTL5000.pdf), I noticed that this chip can pass audio through from Line In to Headphone Out directly (without going through ADC and DAC) for low power operation.

If I use the Audio Visual Design Tool and add I2S1 for Input and I2S2 for Output (with SGTL5000 for control), will the chip use this low power path? Or do I have to specify this in any way?

Thanks for pointing me in the right direction!

Best,

Jose
 
Hi! I'm trying to build a simple wav player but I'm getting stuck at even getting my files to play. The example files are working, but not my own. Is there a cap of how big files you can play? Yet I have only used the example sketches and changed the file name. I'm using a Teensy 3.2 with the audio board.

I'm sort of new to this so if there is any more information you need I'll try to contribute.
 
WAV files need to be 16 bit, 44.1 kHz. No other formats are supported.

If your files seem ok, create a tiny WAV file and post it here.
 
For a small WAV file, put it into a ZIP archive.

When you reply here, click the "Go Advanced" button. Then the message post will have a field at the bottom to attach files. The forum limit the file size.
 
The file looks good. Can you post the complete program you're using? Yes, even if it's just a slight edit to the example code, please post *exactly* what you're using.

If I can't seen anything wrong, I'll put the file on a card here and try running your program. But I need the exact code, so I can duplicate the problem on a board here.
 
// Simple WAV file player example
//
// Three types of output may be used, by configuring the code below.
//
// 1: Digital I2S - Normally used with the audio shield:
// http://www.pjrc.com/store/teensy3_audio.html
//
// 2: Digital S/PDIF - Connect pin 22 to a S/PDIF transmitter
// https://www.oshpark.com/shared_projects/KcDBKHta
//
// 3: Analog DAC - Connect the DAC pin to an amplified speaker
// http://www.pjrc.com/teensy/gui/?info=AudioOutputAnalog
//
// To configure the output type, first uncomment one of the three
// output objects. If not using the audio shield, comment out
// the sgtl5000_1 lines in setup(), so it does not wait forever
// trying to configure the SGTL5000 codec chip.
//
// The SD card may connect to different pins, depending on the
// hardware you are using. Uncomment or configure the SD card
// pins to match your hardware.
//
// Data files to put on your SD card can be downloaded here:
// http://www.pjrc.com/teensy/td_libs_AudioDataFiles.html
//
// This example code is in the public domain.

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

AudioPlaySdWav playWav1;
// Use one of these 3 output types: Digital I2S, Digital S/PDIF, or Analog DAC
AudioOutputI2S audioOutput;
//AudioOutputSPDIF audioOutput;
//AudioOutputAnalog audioOutput;
AudioConnection patchCord1(playWav1, 0, audioOutput, 0);
AudioConnection patchCord2(playWav1, 1, audioOutput, 1);
AudioControlSGTL5000 sgtl5000_1;

// Use these with the audio adaptor board
#define SDCARD_CS_PIN 10
#define SDCARD_MOSI_PIN 7
#define SDCARD_SCK_PIN 14

// Use these for the SD+Wiz820 or other adaptors
//#define SDCARD_CS_PIN 4
//#define SDCARD_MOSI_PIN 11
//#define SDCARD_SCK_PIN 13

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

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

// Comment these out if not using the audio adaptor board.
// This may wait forever if the SDA & SCL pins lack
// pullup resistors
sgtl5000_1.enable();
sgtl5000_1.volume(0.5);

SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
if (!(SD.begin(SDCARD_CS_PIN))) {
// stop here, but print a message repetitively
while (1) {
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.
playWav1.play(filename);

// A brief delay for the library read WAV info
delay(5);

// Simply wait for the file to finish playing.
while (playWav1.isPlaying()) {
// uncomment these lines if you audio shield
// has the optional volume pot soldered
//float vol = analogRead(15);
//vol = vol / 1024;
// sgtl5000_1.volume(vol);
}
}


void loop() {
playFile("CROSSREF.WAV"); // filenames are always uppercase 8.3 format
delay(500);
/*playFile("SDTEST2.WAV");
delay(500);
playFile("SDTEST3.WAV");
delay(500);
playFile("SDTEST4.WAV");
delay(1500);*/
}

Here's a pdf with pictures aswel:
 

Attachments

  • Dokument1.pdf
    242.9 KB · Views: 456
I hear a smal 'click' when i run the code but nothing more. The seriel monitor keeps posting "Playing file: CROSSREF.WAV" every second.
 
I am using AudioSynthToneSweep to generate a short (.01 to .1 sec) tonesweep and would like to save it to store it directly in memory (not SD card) for subsequent processing. I have been unable to find a means of recording such an internally-generated sound. The wav2sketch program referred to by the design tool notes for PlayMemory1 is unavailable (error 404 from Github, and not available with a Github search), and the SamplePlayer example does not save the samples to memory as far as I can tell. Can anyone help me?

I need to perform, once, a complex FFT of the tonesweep using code I published on the forum last year. Can I retrieve the tonesweep from memory as an array to do this? Alternatively, is there a way of saving the tonesweep as an array of amplitude numbers rather than as a wav file?
 
Last edited:
... and would like to save it to store it directly in memory (not SD card) for subsequent processing. I have been unable to find a means of recording such an internally-generated sound.

The queue objects are the way you get real-time audio between the audio system and your code running in the Arduino sketch.
 
I'm running the code from #1266 with a SD card loaded with the WAV file from #1263. It's playing the file correctly.
Hi If by #1266 you mean the WavFilePlayer example, I also now have it outputting chirp WAV files generated by Audacity. I have been studying your LED array example but the mention of the 12bit DAC makes me think I should perhaps stick to the queue object. I just want files I can convert to FFT-processable numbers. It's quite disconcerting how compact and powerful the Teensyduino code is, and it took me a week to figure out that a fresh audio design page can be obtained by dragging a box around the objects and deleting them!
 
I'm running the code from #1266 with a SD card loaded with the WAV file from #1263. It's playing the file correctly.

Well, that's very frustrating. Do you have any idea what the problem might be? It confuses me so much that i can play the example files but not my own. Because obviously there is nothing wrong with the files.
 
Do you have any idea what the problem might be?

Sorry, I don't know.

I just copied the copy you provided onto a SD card and copied your code into an Arduino window, uploaded, and it started playing that clip with the woman talking about deciding what to do with your life.

There's nothing wrong with the file or the code. Maybe try again with another SD card?

I just don't know what else I can do to help here. At least this can keep you from going farther down the path of fiddling with the code and the WAV file. They're both perfectly fine. Whatever's wrong, it's not the files.
 
Status
Not open for further replies.
Back
Top