Go Down

Topic: Audio Shield, 16 bit, 44.1 kHz stereo (CD quality) (Read 2 times) previous topic - next topic

Paul Stoffregen

I'm thinking about making a CD quality audio shield.  It's intended for Teensy3, which has an I2S audio interface with DMA, and might be useful for other future boards?

I'm looking for feedback on what audio interface features are worth the extra physical size and cost?

For example, lots of connectors add considerably to the physical size.  I'm hoping to keep this thing in the form-factor of a breadboard-friendly module.  Even at the normal Arduino shield size, Sparkfun's Codec Shield has 2 pots and 2 stereo headphone jacks, and solder pads for other stuff (rather than more connectors).

Some CODECs have only a mono input with stereo output.  Is stereo input a big deal?

Another feature I'm considering is a 1 watt amplifier for an 8 ohm speaker.  It does add a few dollars cost (realistically times 2 or more once sold by distributors like Sparkfun).  But a highly efficient class-D amp with power management build onto a small board might be really useful for battery powered projects?  Or maybe the extra cost isn't worthwhile?

This project is at a very early stage.  I'm really curious to hear any feedback about what audio ins, outs, connections and other features might be useful (and worth the extra hardware size + cost)?


<thinking out loud>
- stereo input enables possibly sound locating sketches which is fun
- you could have a stereo input prepared shield, you could add the second channel later? (if possible?)
- room for an equalizer chip
- a possibility to directly amp input to output => use the shield standalone as mini amp e.g. for smartphone
- room for a signal generator chip? sinus, triangle, square, diff noises, ...
- switch output phase?  => experiments with anti-sound?

my 2 cents,
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)


Many years ago, Jan 2009, I a wrote library for the Adafruit Wave Shield that still has lot of users.  This library has about 4,000 downloads a year.

One issue that I never solved is the ability to rapidly play a sequence of recordings.  This include terminating one recording early and quickly, in less than a millisecond, starting another.

This is useful in generating sound for games, interactive art, and other applications.

Recently forum user CrossRoads said he was interested in building a 1284 based device to record 16-bit 44.1 ksps sound from things like a snare drum or cymbal.  The sound files would then be transferred to a PC for cleanup.  The sound would then be played in response to sensors in something like a drum pad.

A snare drum is interesting since the first 2 ms of the sound determines much of the quality of the reproduction and must start in a fraction of a millisecond of the right time.

I became very interested in a better solution to the problem of rapid play of a sequence of portions of sound files.

I now have a prototype that can start any of about 25 sound files in about 50 microseconds and switch to another in about the same time.

It depends on playing contiguous files, which not a problem since files copied to a freshly formatted SD are contiguous.

I buffer the first 512 byte block of each file in a SoundStream object that also has the sample rate, start SD lbn, start data offset, and sample count for the file. 

I trigger sound file changes in a pin change ISR.  The biggest problem is terminating a recording.  You must clock all 512 bytes from an SD once you start a block read. 

I have optimized the SD read on AVR so I can read two random blocks in about three milliseconds so I can just throw one away.  For 16-bit 44.1 ksps Wave files you have 5.8 ms per 512 byte block.

I run the DAC using Serial port 1 in SPI mode.  CrossRoads plans to use a AD5662 DAC.  It take about four microseconds to send a sample to the DAC.  The DAC is accessed in the timer 1 ISR.


Vref chip arrived yesterday while we were taking son to college. Will breadboard up & test soon ...
Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Paul Stoffregen

I'd be very curious to hear more about your SoundStream code?

Over the last couple weeks, I've been working on something that might be pretty similar, so far called AudioStream.  It passes audio between inherited objects using 256 byte blocks (128 samples).  At the moment, this is all at a pretty early stage of development.

I did some simplistic speed tests on Teensy3.  The SD library, based on the old version of SdFat, was able to read from 2 open files, each at about 230 kbytes/sec, by alternating calling read(buffer, 512) on each.  I didn't measure the latency from initial file open to availability of the first 512 bytes.

My understanding is the SdFat library caches just 1 block of 512 bytes.  Is that right?  What if it also cached a block of the FAT table?  Or perhaps a short list of the first dozen cluster addresses for each file?  Would that allow rapid seeking within the first many kbytes of the file?


I plan to redo the Wave Shield code but not with SdFat.  It has a custom SD reader.  This library started on 168 Arduinos with 1 KB total RAM and used half blocks or 256 bytes.


was able to read from 2 open files, each at about 230 kbytes/sec, by alternating calling read(buffer, 512) on each.

With the 1284 I can read from two contiguous files on a cheap SD at about this speed, about 220 KB/sec each.  On an industrial SD this call to read a block takes 924 micros or 554 KB/sec.
Code: [Select]

  uint32_t m = micros();
  if (!card.readBlock(lbn, buf)) error("readBlock");
  Serial.println(micros() - m);

If I read a single stream, I can use multi-block read commands.  Then reading a block takes 824 micros or 620 KB/sec.

This is on a 16 MHz AVR with a 8 MHz SPI bus.

On Teensy 3.0 and Due I cache a FAT block and a data block.  If you read a multiple of 512 bytes and start on a 512 byte boundary, I read directly into your buffer and don't use the data cache.

Or perhaps a short list of the first dozen cluster addresses for each file?  Would that allow rapid seeking within the first many kbytes of the file?

Sound files go so fast that a dozen clusters is not worth it.  12 32K clusters 16-bit 44.1 mono is 4.4 seconds.

I have played with other cache possibilities but nothing beats contiguous files.  I spent my career around the world's largest physics experiments and most RTOS  used there, like VxWorks, have real-time file extensions that depend on contiguous files.

SD firmware in cameras records video in contiguous files.  Contiguous files are required to meet a cards performance spec.

SdFat has had this call starting with the first release:

bool SdBaseFile::createContiguous    (    SdBaseFile *     dirFile,
      const char *     path,
      uint32_t     size

Create and open a new contiguous file of a specified size.

    This function only supports short DOS 8.3 names. See open() for more information.

    [in]   dirFile   The directory where the file will be created.
    [in]   path   A path with a valid DOS 8.3 file name.
    [in]   size   The desired file size.

    The value one, true, is returned for success and the value zero, false, is returned for failure. Reasons for failure include path contains an invalid DOS 8.3 file name, the FAT volume has not been initialized, a file is already open, the file already exists, the root directory is full or an I/O error.

For the CrossRoads prototype I create a 100 MB file, record, and then truncate with this call:

bool SdBaseFile::truncate    (    uint32_t     length   )    

Truncate a file to a specified length. The current file position will be maintained if it is less than or equal to length otherwise it will be set to end of file.

    [in]   length   The desired length for the file.

    The value one, true, is returned for success and the value zero, false, is returned for failure. Reasons for failure include file is read only, file is a directory, length is greater than the current file size or an I/O error occurs.

Opening files on FAT volumes is slow since the directory entry can be anywhere.  There may even be unused entries before the entry to be opened.

My main point is that the end hardware/software product needs to be very agile in playing a series of files with no time gaps.

It must integrate into ISRs for sensors.  I have a interrupts safe file start function to switch streams in an ISR or normal code and the call take about 30 usec.

Go Up