Preparing audio waveforms for Arduino PROGMEM

The Arduino lo-fi Beat Box is kicking up some activity and comments on the littleBits site. (Follow this link to the Beat Box project page at littleBits.)

Two littleBits inventors have made considerable progress in suppressing the noisy buzz which seems to plague the el-cheapo lo-fi DAC design. I eventually gave up fighting the buzz and built a proper Small Peripheral Interface (SPI) DAC for the littleBits Arduino. See this page and this page for more information about the SPI DAC design. The main component is the Microchips MCP4921 12-bit SPI-compatible DAC. The audio output is much quieter.

I built a littleBits song player that sings a song in Do-Re-Mi Solfege. It uses the SPI DAC for conversion. Although I completed the project at the beginning of September 2016, I’m just now getting to a write-up for the littleBits site!

If you’re still hacking the Beat Box project, you should check out the ongoing discussion in the littleBits forum. Inventor alexpikkert built a rather spiffy passive low pass filter module using littleBits bitSnaps. I’m waaay too ham-handed for that kind of work, so I’m quite impressed by his implementation.

Another inventor, Frankje, would like to contribute some new drum waveforms. He needs more information about the drum waveforms and the process that I used to make them. So, here goes.

The drum waveforms (AKA “the samples”) are stored in the Arduino’s program memory (PROGMEM). PROGMEM is the non-volatile flash memory where the uploaded sketch resides. PROGMEM is quite big by Arduino standards. The Leonardo (ATmega32u4) has 1K byte EEPROM, 2.5K bytes SRAM (read/write RAM for variables) and 32K bytes of flash memory (PROGMEM). The bootloader uses 4KBytes of PROGMEM leaving 28K bytes for user code and data.

Notice that I said “and data.” The Arduino developers wisely give a sketch direct access to data stored in PROGMEM. A sketch reads data from PROGMEM using an access functions such as pgm_read_byte_near(). Thanks to PROGMEM, Arduino programmers can store a reasonably large amount of non-volatile data along with their code.

By now, if you are using a modern day musical instrument library (i.e., 10+ GBytes of sampled instruments), you’re shreiking in horror. I wanted to keep the Beat Box design small, simple and self-contained — no SD card or bulk flash memory. That means cramming all of the percussion samples into less than 28KBytes. Please remember, our sketch needs to fit into that 28K bytes, too.

Immediately, I chose a sampling rate and size that minimized space without sacrificing too much quality. The Beat Box sample format is 22,050Hz, signed 8-bit, mono. I tried a 10,025Hz sampling rate, but too much of the top end (high frequency brightness) was lost. The Arduino PWM conversion technique provides, at best, 8 or 9 bits of resolution, so its was easy to settle on 8-bit signed. Going mono cut waveform size in half. Stereo would require a second lo-fi DAC as well as upping memory consumption by a factor of two.

I started out sampling a TR-808 kit here and a TR-808 kit there. Nothing sounded as good as the TR-808 samples produced by Michael Fischer. Michael sampled a TR-808 back in September 1994 (!) and his sample set is excellent. He sampled each of the TR-808 voices over a range of knob (parameter) values. I went through the sample set, found the sounds which (to me) represent the 808, and chose sounds with the smallest WAV files from that representative subset.

Then, the torture began.

Michael’s samples are 44,100Hz, 16-bit, mono. So, I first down converted the chosen few waveforms to 22,050Hz, 8-bit, mono and I trimmed the samples as short as I could dare. My main audio editing tool is Sony Sound Forge Audio Studio, but any good audio editor could do the job. I’m most familiar with Sound Forge and can fly with it.

The next step is getting each waveform into a compilable, C-language source file. I converted each 22,050Hz, 8-bit mono WAV file to a RAW audio file. A RAW audio file does not have a header and contains only waveform samples. I wrote a program, raw2c.c, to convert a raw file to a C-language include file containing a formatted, C-language array that is initialized with the waveform samples. The program counts the number of samples and generates a #define for the array length.

Here is the source code for raw2c.c.

I also wrote a simple command script to batch convert all sixteen RAW files and to concatenate the individual include files into a single include file, waveforms.h.

Once I had the waveform.h file, I compiled the entire sketch to see if everything would fit into 28K bytes.

Then I repeated the trim, convert and compile process, again. And, again. And, again. You get the picture. I eventually had to mangle the waveforms. Truly a shame. The final cymbal sounds have only a brief shimmer of their true glory.

There you have it! I applied the same development process to the Do-Re-Mi waveforms although I started out with samples of my vocoded voice. Memory space requirements were even tighter (!) and I had to reduce the sampling rate to 11,025Hz.

Good luck, squeeze away and convert!

Copyright © 2017 Paul J. Drongowski