Audio Library

Status
Not open for further replies.
So, i would like to know (or be more sure, cos i will spend and money and time) if this idea is feasible to sample audio signal 16b/44.1Khz maybe make some effects with data

The audio library is designed to run only at 16 bit, 44.1 kHz rate. It works very well.

Whether those wireless modules can sustain such a data rate is a good question. I haven't used them, so I really don't know.
 
The audio library is designed to run only at 16 bit, 44.1 kHz rate. It works very well.

Whether those wireless modules can sustain such a data rate is a good question. I haven't used them, so I really don't know.

nRF24L01: Up to 2Mbps on air data rate and SPI up to 10Mbps. so for 16b*44.1kHz*2 channels = 1.35 Mbps. But i dont know if the audio library is able to route same way data to SPI.
 
Each audio frame is 2.9 ms. Disabling interrupts for 30 ms means you'll drop about 9 or 10 frames.

Bit-bashing I/O like this simply isn't an option when using the audio library. I could investigate in detail here, but if the only problem is your bitbash waveform has gaps, that's definitely going to fall into "not a bug" in the audio library.



This really is the way to go.

Perhaps start a new thread, just about that technique. As always, the forum rule is complete code to demonstrate the problem. It doesn't need to be your whole commercial application, but if you want real help on this, you're going to have to write a small demo for just that part, with just the audio lib playing files at the same time.

I will try and create another thread later after I finish trying different variants of interrupt based timings. Also I will order some more microSD cards and run the tests to be sure.

Would it be possible to get a OPEN BY INDEX example for SD cards in the Audio library? Reducing latency by opening by index vs. names helped out a lot in my last project w/ WaveHC and I can not figure out how to port the code over to the Teensy Audio.


Code:
/*
 * This sketch illustrates opening files by index
 * which can significantly reduce latency.
 *
 * How to prepare a test SD:
 * Start with a clean newly formatted SD.
 * First copy the 400 files in the 'fill' folder to the SD.
 * Next copy the 16 files in the 'DTMF' folder to the SD.
 * There should be 416 files in the SD root directory.
 *
 * You must copy the files in the above order so the 'fill'
 * files occur in the directory before the DTMF files.
 *
 * Run this sketch using the prepared SD.  Notice the
 * difference in latency between play by name and
 * play by index.
 */

#include <WaveHC.h>
#include <WaveUtil.h>

SdReader card;    // This object holds the information for the card
FatVolume vol;    // This holds the information for the partition on the card
FatReader root;   // This holds the information for the volumes root directory
FatReader file;   // This object represent the WAV file 
WaveHC wave;      // This is the only wave (audio) object, since we will only play one at a time

// time to play each tone in milliseconds
#define PLAY_TIME 200

/*
 * Define macro to put error messages in flash memory
 */
#define error(msg) error_P(PSTR(msg))

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

  if (!card.init()) error("card.init");

  // enable optimized read - some cards may timeout
  card.partialBlockRead(true);

  if (!vol.init(card)) error("vol.init");

  if (!root.openRoot(vol)) error("openRoot");

  PgmPrintln("Index files");
  indexFiles();

  PgmPrintln("Play files by index");
  playByIndex();

  PgmPrintln("Play files by name");
  playByName();
}

//////////////////////////////////// LOOP
void loop() { }

/////////////////////////////////// HELPERS
/*
 * print error message and halt
 */
void error_P(const char *str) {
  PgmPrint("Error: ");
  SerialPrint_P(str);
  sdErrorCheck();
  while(1);
}
/*
 * print error message and halt if SD I/O error, great for debugging!
 */
void sdErrorCheck(void) {
  if (!card.errorCode()) return;
  PgmPrint("\r\nSD I/O error: ");
  Serial.print(card.errorCode(), HEX);
  PgmPrint(", ");
  Serial.println(card.errorData(), HEX);
  while(1);
}

// Number of files.
#define FILE_COUNT 16

// Files are 'touch tone phone' DTMF tones, P = #, S = *
// Most phones don't have A, B, C, and D tones.
// file names are of the form DTMFx.WAV where x is one of
// the letters from fileLetter[]
char fileLetter[] =  {'0', '1', '2', '3', '4', '5', '6', 
      '7', '8', '9', 'A', 'B', 'C', 'D', 'P', 'S'}; 
      
// index of DTMF files in the root directory
uint16_t fileIndex[FILE_COUNT];
/*
 * Find files and save file index.  A file's index is is the
 * index of it's directory entry in it's directory file. 
 */
void indexFiles(void) {
  char name[10];
  
  // copy flash string to RAM
  strcpy_P(name, PSTR("DTMFx.WAV"));
  
  for (uint8_t i = 0; i < FILE_COUNT; i++) {
    
    // Make file name
    name[4] = fileLetter[i];
    
    // Open file by name
    if (!file.open(root, name)) error("open by name");
    
    // Save file's index (byte offset of directory entry divided by entry size)
    // Current position is just after entry so subtract one.
    fileIndex[i] = root.readPosition()/32 - 1;   
  }
  PgmPrintln("Done");
}
/*
 * Play file by index and print latency in ms
 */
void playByIndex(void) {
  for (uint8_t i = 0; i < FILE_COUNT; i++) {
    
    // start time
    uint32_t t = millis();
    
    // open by index
    if (!file.open(root, fileIndex[i])) {
      error("open by index");
    }
    
    // create and play Wave
    if (!wave.create(file)) error("wave.create");
    wave.play();
    
    // print time to open file and start play
    Serial.println(millis() - t);
    
    // stop after PLAY_TIME ms
    while((millis() - t) < PLAY_TIME);
    wave.stop();
    
    // check for play errors
    sdErrorCheck();
  }
  PgmPrintln("Done");
}
/*
 * Play file by name and print latency in ms
 */
void playByName(void) {
  char name[10];
  
  // copy flash string to RAM
  strcpy_P(name, PSTR("DTMFx.WAV"));
  
  for (uint8_t i = 0; i < FILE_COUNT; i++) {
    // start time
    uint32_t t = millis();
    
    // make file name
    name[4] = fileLetter[i];
    
    // open file by name
    if (!file.open(root, name)) error("open by name"); 
    
    // create wave and start play
    if (!wave.create(file))error("wave.create");
    wave.play();
    
    // print time
    Serial.println(millis() - t);
    
    // stop after PLAY_TIME ms
    while((millis() - t) < PLAY_TIME);
    wave.stop();
    
    // check for play errors
    sdErrorCheck();
  }
  PgmPrintln("Done");
}
 
Last edited:
nRF24L01: ..... But i dont know if the audio library is able to route same way data to SPI.

The audio library does not have any code to read or write the nRF24L01 over SPI. You'll need to do that.

The audio library does provide these 2 queue objects, so you can put 128 sample blocks directly into the library, and extract 128 sample blocks from the library, which you'd use to feed into the nRF24L01.

http://www.pjrc.com/teensy/beta/gui/?info=AudioPlayQueue
http://www.pjrc.com/teensy/beta/gui/?info=AudioRecordQueue

That part, talking to the nRF24L01 over SPI, it up to you. You can't expect the audio library to have any sort of ability to "route data to SPI" in any way that might magically work with the nRF24L01 (having never even used this chip before). The audio library doesn't support that chip, so you have to talk to the nRF24L01.

I can't predict how your nRF24L01 will end up. But I can point you to a project I did a few months ago, using the play queue object.

http://community.arm.com/groups/embedded/blog/2014/05/23/led-video-panel-at-maker-faire-2014

In this project, Teensy 3.1 was able to read uncompressed 90x48 pixel video AND interleaved 16 bit 44 kHz audio from the SD card over SPI. It easily maintained a 30 frames/sec video rate to the 4320 LEDs with OctoWS2811 while also streaming the mono 44 kHz audio to the DAC (which discarded the low 4 bits).

As you can see in the video, and many thousands of people saw live at Maker Faire in San Mateo a few months ago, it worked very well.

If you undertake this project, I'm very confident the audio library will be easily up to the task. It's very efficient, leaving nearly all the CPU power available while streaming to the DAC or I2S (audio shield). But the library doesn't provide generic SPI streaming to any type of device, so communicating with the nRF24L01 will be entirely your responsibility.
 
Last edited:
Last edited:
I was thinking about it and maybe it would be easier to use I2S output from audio board and use http://www.sparkfun.com/products/11924 as trasmitter which should support I2S input.

Yes, that looks like a much easier approach. The library supports I2S very well.

On SPI, I can confirm the SPI is plenty fast enough to stream audio. The library already supports playing multiple WAV files simultaneously from the SD card, which is read via SPI. As you can see in the 4320 LED demo and also the new Recorder example, the SPI is fast enough.

But whether a nRF24L01 can really keep up is a good question. Sure, Nordic's website says "1Mbps and 2Mbps on-air data-rate", but what is the actual realistic end-to-end throughput?
 
I've updated the Biquad filter object. Previously, it was pretty difficult to use. You had to come up with an array of coefficients using a pretty cryptic process. It also used to require passing in an array for the coefficients and state storage, which allowed the most frugal use of memory, but it was easy to do things incorrectly and end up with all sorts of impossible-to-understand problems.

Now the filter has 4 functions to easily configure lowpass, highpass, bandpass and notch response. You can also pass in a set of coefficients, as doubles with the exact format generated by nearly all IIR biquad filter calculation tools.

http://www.pjrc.com/teensy/beta/gui/?info=AudioFilterBiquad
 
Now the filter has 4 functions to easily configure lowpass, highpass, bandpass and notch response. You can also pass in a set of coefficients, as doubles with the exact format generated by nearly all IIR biquad filter calculation tools.

http://www.pjrc.com/teensy/beta/gui/?info=AudioFilterBiquad

Oh that sounds a lot easier to use, and in particular to change dynamically. Each of the four stages gives -6dB/octave? Or do they give -12bB/octave?
 
There are only 2 milestones left for the 1.0 release.

But several of the examples are still broken, or confusing. Several objects aren't fully documented, or may have other confusing or problematic APIs that I haven't seen (being too close to the low-level design).

I like to ask everyone watching this thread to please give the latest audio library a try. Do all the examples actually work? Is there stuff that's confusing or too complex for many people to figure out?

Now is the time to beta test and comment. Soon the 1.0 API will be frozen and many types of changes will be (mostly) beyond reach.
 
Thoughts on micGain, the current implementation hides the granularity ( 4 levels ) and I think could be confusing. I have been using
Code:
 unsigned short micGain(unsigned int n) { return modify(0x002A, n&3, 3); }

Edit:

But on closer examination of the new code perhaps I need to test it before I complain :)
 
Last edited:
Testing with a mic micGain looks like it is working. Tomorrow I will hook the mic input to a signal generator and see what I can see.....
 
I've been meaning to make a start but my breakout has not arrived yet :-/

I don't want to solder anything until i've got all the pins available on a breadboard.

incidentally I bought this flash chip.

http://www.ebay.co.uk/itm/301006935484?_trksid=p2059210.m2749.l2649&ssPageName=STRK:MEBIDX:IT

which links to this techsheet:
http://www.buydisplay.com/download/manual/W25Q128FV_Datasheet.pdf


The code isn't identical but as far as i can tell it is a suitable chip for use on the audio board, once support is available, can anyone confirm?
 
I've been meaning to make a start but my breakout has not arrived yet :-/

I don't want to solder anything until i've got all the pins available on a breadboard.

incidentally I bought this flash chip.

http://www.ebay.co.uk/itm/301006935484?_trksid=p2059210.m2749.l2649&ssPageName=STRK:MEBIDX:IT

which links to this techsheet:
http://www.buydisplay.com/download/manual/W25Q128FV_Datasheet.pdf


The code isn't identical but as far as i can tell it is a suitable chip for use on the audio board, once support is available, can anyone confirm?

OK, just compared the techsheet with the one on PJRC site and they are identical so this one should work fine....fingers crossed. Answering my own question here...

Sorry, Paul, I would actually love to help but I'm not at a point where i can connect the audio board to the teensy yet. the by product of geeking with 16 month old twins...everything takes ages....in 1 hour chunks..
 
anyone can tell me what i'm missing here?

i'm trying to loop a file, so i've added this to play_sd_wav --


Code:
bool AudioPlaySdWav::seek(const char *filename, [B]uint32_t pos[/B])
{
	stop();
	__disable_irq();
	wavfile = SD.open(filename);
       [B] wavfile.seek(pos);[/B]
	__enable_irq();
	if (!wavfile) return false;
	buffer_length = 0;
	buffer_offset = 0;
	state_play = STATE_STOP;
	data_length = 20;
	header_offset = 0;
	state = STATE_PARSE1;
	return true;
}

it returns true, but there's no sound (unless pos = 0).
 
Status
Not open for further replies.
Back
Top