MCP4725 Sample Player

This is another chance to get out the MCP4725 digital to analogue converter module. This time I want it to play a sampled sound as a type of “effect” based on a trigger.

These are the key Arduino tutorials for the main concepts used in this project:

If you are new to Arduino, see the Getting Started pages.

Parts list

  • Arduino Uno
  • MCP4725 breakout board
  • 1x button
  • Cheap (i.e. not an expensive amp or computer!) means of playing audio from a headphone jack
  • Breadboard and jumper wires

The Circuit

I’m using the MCP4725 output board I built in MCP4725 Digital to Analog Converter – Part 2 but you can use it directly as described in the first part of MCP4725 Digital to Analog Converter.

In particular, as I have a Sparkfun module I’m using the trick of plugging the board directly into the Arduino and powering it using A2 and A3 (see the previous projects for details).

The trigger for the sample playing is a button linked between digital pin 8 and ground.  You can use any digital pin that works for you.

2020-11-08 14.36.52

The Code

The button code is quite simple.  It is using the “internal pull-ups” for the IO pin so the pin will be HIGH when the button isn’t pressed and LOW when it is.

The sample playing code is using the low-level I2C code from the Mozzi library, so you’ll need to ensure Mozzi is installed (as describe in the various Mozzi projects on this site) but it isn’t using Mozzi itself.  I have two helper functions to initialise and write to the MCP4725 as described in the MCP4725 and Mozzi – Part 2 project.

The actual playing code is very simple – when triggered it literally just cycles through the entire stored sample pushing each value out to the MCP4725 over I2C.  This means that the Arduino can’t do anything else whilst playing the sample, keeping things very simple.

Samples need to be a list of 16-bit values.  Well actually, the should be 12-bit values for writing to the MCP4725, but the raw data types we can use are either 8 or 16 bits.

To create the sample in the first place, I found a WAV file of the sound I wanted to play (in this case a cannon firing) and used the Goldwave application to do the following:

  • “Resample” the sound from the existing CD quality sound at 44kHz down to a sample rate of 8192Hz.
  • Save the file as a WAV file with the properties: 16-bit, signed, MONO.

Then I used the application I found here – http://colinjs.com/wavtocodeV1_0/wavtocode.htm to create some code containing all the numbers for the sample and included it into my Arduino code.  I used the application to create a set of 16-bit samples which end up looking something like this.

#define NUM_ELEMENTS 6985

BYTE data[NUM_ELEMENTS] = {
32738, 32787, 32754, 32778, 32764, 32765, 32783, 32732, // 0-7
32855, 32128, 32018, 31944, 32598, 32767, 33134, 33048, // 8-15

...

32760, 32761, 32764, 32764, 32766, 32766, 32767, 32768, // 6976-6983
32768};// 6984-6984

But to use it with the Arduino there are two things to be done:

  • Replace BYTE, “data” and NUM_ELEMENTS with things that mean something in my own code.
  • Set the data array up as a const PROGMEM array so it isn’t taking up space in the Arduino’s working memory (which isn’t very big).

This means I end up with a data array like the following:

#define NUM_ELEMENTS 6985
const PROGMEM uint16_t wtdata[NUM_ELEMENTS] = {
32738, 32787, 32754, 32778, 32764, 32765, 32783, 32732, // 0-7
32855, 32128, 32018, 31944, 32598, 32767, 33134, 33048, // 8-15

...

32760, 32761, 32764, 32764, 32766, 32766, 32767, 32768, // 6976-6983
32768};// 6984-6984

Now this is a 16-bit sample, as previously mentioned, but the MCP4725 require 12-bit values, so I need to divide by 16 (or shift right 4 times) to get the values into the right range.  I also need to pull the samples out of PROGMEM meaning that my “dacWrite” call looks like this.

for (int i=0; i<NUM_ELEMENTS; i++) {
  dacWrite(pgm_read_word(&(wtdata[i])) >> 4);
}

If you’re paying attention you might also spot that the original WAV file was a signed 16-bit file, but the application to create code always generates unsigned 16-bit files – hence so many of the 32767/32768 values – this is “zero” in the 0 to 65536, 16-bit range.

Now remember I said to resample to 8192Hz?  This is because during tests I could that I could output a 512-number sine wave when running at full speed to generate a frequency of around 18Hz.  This means it was outputting a full wave (all 512 values) approximately every 55mS.  At this rate, it was managing to write out a single sample every 110uS which corresponds to a sample frequency of around 9.2kHz.

Now it would be possible to accurately specify a sample rate and use Goldwave to resample my starting file to that exact rate, but for this application – basically triggering a sound effect – resampling to 8192 is close to 9200 that the slight difference in pitch doesn’t matter to me.

Find it on GitHub here.

Closing Thoughts

This makes a great “effects unit”.  It would be nice to be able to trigger multiple sounds by using different buttons, but what I really want to do is to be able to trigger it using a MIDI signal.

Kevin

Leave a comment