Are you designing a system that involves audio? Maybe an audio product, or a product with an audio subsystem? Here are some tips and tricks that may help you.
Designing audio systems and debugging audio presents some interesting challenges. Sound is ruthlessly real-time; the speaker cone will keep moving, even if your prototype isn't able to keep up with the flow of output samples required. The same is true of a microphone: the microphone diaphragm keeps moving and must be sampled often enough. If you skip one or more samples at the input or the output, then a (very loud) click or pop can result. What's more vexing, the human auditory system, which is the final judge of audio quality, is extremely sensitive, especially to unexpected sounds (or artifacts) that your implementation may introduce.
Of course numerical techniques can be used to test an audio system. But the fact that we have two ears and love to listen to sound presents an opportunity. Thanks to evolution, our ears are designed to pick up unusual, sudden events in the environment. If you are developing and debugging audio, then you can use your ears to help guide you to fixing problems. This article discusses some of the ways to put your ears to work, and other tricks useful in debugging audio.
Set up an initial passthrough test
When dealing with any signal processing application, but especially with audio, there are some ways to set up your debugging environment so that life is simplified throughout the development cycle, and even later when a new version of the product is to be released.
During development you will be using some kind of development environment. It may be a development board, with audio in and out. If the final hardware is not ready, you may use a software simulator (typically provided with the development tools for your target processor) to debug code. Using a well-constructed software simulator can provide significant advantages. For example, typically a software simulator allows disk files to be opened, read, and/or written. Whether using hardware or a simulator, if at all possible set up the development environment so that you can inject a test signal from a disk file and write one or more files of output data from your code. Instead of using standard .wav files, often the development environment requires that such files be in an idiosyncratic format. For example, it may require one audio sample per line written in ASCII text as a floating-point number ranging from +1.0 to -1.0; or one sample per line as a hex number. Sometimes two stereo samples are written on one line.
Whatever the format required by your development environment, it will help you to be able to convert to and from the usual audio formats, such as a .wav format. You may have to write a simple .C program or Matlab routine to do this, or you can use an off-the-shelf audio editor, which will also allow you to examine audio files. Programs for doing this are inexpensive and easy to find. On the PC, candidates include Adobe Audition (http://www.adobe.com/products/audition/). One multi-platform shareware editor is Audacity (http://audacity.sourceforge.net/); a Google search will turn up others. Matlab, Labview, and similar programs can also be used for this purpose. There are more sophisticated audio editing programs, such as Steinberg Cubase (http://www.steinberg.net/983_1.html), but they are overkill for most debugging purposes. With such an editor you want to be able to:
- read files from disk and write files to disk in a variety of formats (including the one required by your debugging environment)
- zoom in to the level of an individual sample
- modify an individual sample
- read out the value of an individual sample
- scale parts of the waveform
- view spectral plots
- cut, copy, and paste waveforms
- listen to sounds
- and finally, generate test waveforms.
At an early stage in the development process, set up a passthrough test. The development system should be able to pass a signal unchanged from the input to the output. One version of such a test involves sending a known signal, like a sine wave, to an A/D converter on a prototype development board, and examining the D/A output on an oscilloscope while listening to a speaker.
Figure 1. With no audio processing in place, an audio device should pass a signal through undamaged.
Ideally another version of this test involves injecting a digital signal after any analog-to-digital converter, and recovering a digital signal before any digital-to-analog converter.
Figure 2. Ideally a signal can be read from disk and injected into the development system, then picked up from the development system and stored to disk.
Any processing modules get dropped between these test points.
Figure 3. The passthrough test provides the backbone for adding more modules.
With this test jig in place, when problems occur later, you can return to the "wire" configuration to ensure that nothing new in the system (such as a new peripheral) has introduced bugs in the basic audio path. You can add modules one at a time to the "wire" setup and thereby isolate which module causes bugs. A variant of this is to provide a bypass switch inside each audio module. The switch can be set in real time from the debugging environment, for example by turning on or off a bit in a memory location. At minimum such a switch can be set at compile/assemble time in the source code.
If you're dealing with a stereo signal, then the two channels have to perform in the same way. One way to verify this is to inject the identical signal into both channels, and write out the stereo results to separate disk files. A simple checksum will typically suffice to verify if the results are identical. If not, then a simple C program or a few lines of Matlab can create a file containing the difference. It is often extremely useful to listen to and examine such a difference file to pinpoint where the signal path diverges unexpectedly.
Audio algorithms that handle more than two channels, such as the well-known 5.1 format (front, left, right left surround, right surround, low-frequency effects or LFE), are now common. If you are dealing with such a multi-channel algorithm, then the principle stated for stereo generally applies to all channels. A possible exception is the LFE channel, which may carry a reduced bandwidth since it focuses on low-frequency effects. A two-trace oscilloscope can verify that the processing for each channel is identical for the path that includes the converters, even if you have to check channels individually in a multi-channel setup. In particular you want to verify that there is no phase inversion between any of the channels. Phase inversion will cause strange results; a sound can completely disappear if it is fed at the same amplitude to two channels whose phases are inverted (180 degrees out of phase).