Pocket Audio Distortion Meter |
A low-cost audio distortion and amplitude measuring tool. |
Calibrating the input gain of amplifiers and other devices (DSPs, equalisers) in an audio system is critical to optimise the signal to noise ratio and dynamic range of the system. Test equipment needed to accurately set gains is typically expensive or complicated for the average system builder to operate. Traditionally, a sine wave tone is played through the system and gains are set as high as possible without visible clipping on an oscilloscope.
My solution is a low cost device which uses a microcontroller (MCU) to detect total harmonic distortion + noise for any frequency between 20Hz and 1.5KHz. Additionally, crossover frequency calibration and gain matching (set multiple amps to the same level) is incorporated.
To determine the level of distortion, the input signal is sampled briefly (128 samples) and stored in an array. One wavelength of the fundamental frequency is then resampled/interpolated to 128 samples, then it is processed with an open source Fast Fourier transform algorithm. Resampling the waveform results in the output spectrum of the FFT having frequency bins that correspond to the harmonics of the fundamental frequency - the 1st bin corresponds to the magnitude of the fundamental frequency, the 3rd bin is the magnitude of the 3rd harmonic and so on.
THD can then be calculated according to the following formula (taken from Wiki):
The resulting THD value is then displayed by a series of LEDs.
As the ATMega MCU features a 10-bit ADC giving a theoretical maximum dynamic range of 60dB or minimum THD+N of 0.1%, it is therefore necessary to adjust the input signal to near the full scale of the ADC in order to optimise the signal-to-noise ratio of the device and detect signal distortion approaching 0.1% THD+N. This can be accomplished with an autoranging pre-amplifier. The autoranging circuit consists of an input attenuator, followed by an adjustable gain amplifier to bring the signal close to the full scale range of the ADC without clipping (just under 5Vpp.)
74HC4066 analog switches allow the MCU to switch in 8 different valued resistors. This gives a gain range of 2 (all switches open) to 512 (all switches closed). The gain can be adjusted in 256 increments between 2 and 512 by setting the 8 the 74HC4066 switches with a binary value according the formula val=(gain/2)-1. e.g. for a gain of 128, we set (128/2)-1 = 63, which in binary is b00111111.
If the amplitude is too low (less than ~45%FS), the MCU increases the gain. If the ADC is being driven to clipping (more than ~95%FS), the gain is decreased. To minimise the time required for the MCU to find the optimal gain level, the gain is adjusted through a number of preset levels having an exponential relationship - each preset gain level being approximately 1.5x the previous level.
Note: Ground in the above schematic refers to a virtual-ground at 2.5v, shown in the powersupply schematic. This allows the ADC to sample an AC signal.
As the samples taken are reletively short (128 samples), the useful bandwidth for any given sampling rate is narrow. The ADC prescaler of the MCU is set to 16 which results in a theoretical maximum sampling rate of around 77KHz giving a minimum frequency of 600Hz for 128 samples. The maximum frequency usable frequency for distortion measurement was found to be around 2x-3x the minimum frequency before measurement of the harmonics is compromised by noise and jitter. Processing overheads reduce the actual sampling rate to slightly lower than 77KHz. By adding a microsecond delay before each sample, the sampling rate can be lowered and low frequencies are able to be analysed. A rotary encoder on the device allows selection of 6 different delays, giving the following usable ranges:
Note that there is some overlap between bands and bandwidth actually gets better as the delay increases. Adding a delay lowers jitter compared to just sampling as fast as possible in a loop. With some optimisation you could probably get away with 5 bands.
(Editor note 29/10/2025: Using delays and a free-running loop to time the sampling is very crude, although it worked well enough here. A better way would be to sample at the maximum rate using a timer interrupt, then copy every Nth sample into a buffer, adjusting N to achieve different sample rates).
The crossover frequency calibration mode works on the principle that at the crossover frequency, the amplitude will be -3dB (for Butterworth alignment) or -6dB (for Linkwitz-Riley alignment).
To calibrate the crossover frequency, the crossover is initially disabled (or set as high as possible for lowpass, as low as possible for high pass). A tone at the desired frequency is then played through the system. In crossover mode, the gain of the autoranging circuit is fixed at a preset level, the input signal is sampled and the LED display guides the user to set the volume of their source to the optimal amplitude (65-85% of the ADC range). The user then presses the rotary button on the device which changes the LED display to a relative amplitude - blue LEDs are assigned to 0dB, -3dB and -6dB amplitudes, yellow for inbetween amplitudes and red for over 0dB and under -6dB. By adjusting the crossover dial on the amplifier the LEDs will show the appropriate level (-3dB or -6dB) when the desired crossover frequency is reached.
Gain matching two or more amplifiers can also be done by using the same setup routine. After pressing the rotary button, the probes are removed from the first amplifier and inserted into another amplifier. Then the gain of the amplifier is adjusted until 0dB is indicated by the LEDs
The powersupply for this project consists of a latching switch circuit (design from EEVBlog), a low dropout 5V regulator and a virtual ground. As it is intended to be powered by a 9V battery, a voltage divider allows the battery voltage to be monitored by software (and an LED lit when it drops below 6V). An additional transistor is added to the latching circuit which allows the MCU to turn off the device by bringing a pin High, C2 holds the output low to prevent startup noise from the MCU oscillating the latching circuit. This is used to automatically turn off the device after 10minutes to prevent accidental battery drain.
I wanted to use RGB leds so i could have different colour cues for the different modes, however i only had 5 IO pins left on the MCU. I used Charlieplexing to accomplish this. Charlieplexing uses tristate logic to multiplex LEDs; each LED is connected to a unique combination of IO pins and polarity, then two pins are configured as VCC and GND to light the appropriate LED and all other pins are set to HighZ.
There are a couple of downsides to this type of multiplexing. Firstly, for more than one LED to be lit concurrently, they must all share a common anode or common cathode, otherwise additional LEDs could also light. I also found that by using combinations of different coloured LEDs (i.e. RGB leds), there can be some leakage if there are two series "unlit" LEDs with a low forward voltage drop in parallel with a single high forward voltage drop LED. The worst case is two red LEDs (2.0Vf) in parallel with a Blue or White LED (3.4Vf). When the Blue/White LED is lit, the Red LEDs dimly light although it is hardly noticable. Note: the LEDs in RGB packages seem to be more closely matched in forward voltage. Single package red LEDs can be as low as 1.7Vf and White as high as 4.0V which could be problematic.
Multiple LEDs that don't share pins can be lit at once by rapidly alternating between them and persistance of vision causes them to all seem on at once. However this is CPU intensive and not practical for time critical processing - having an interrupt rapidly alternate the LEDs and another thread run the main loop either had a drastic effect on performance or very noticable flicker.
Five IO pins allows for 20 LEDs, or in this case 6RGB leds (=18 LEDs) and two extra status leds. Note that one of the RGB leds has to be opposite polarity (common anode rather than common cathode or vice versa). The RGB leds are used as a multifunction display and the two status leds are assigned to "Freq Error" (sampling error) and "No Signal".
Current limiting resistors on each IO pin instead of one for each individual LED simplifies the circuit, however results in LEDs with lower forward voltage drawing more current than others.
(MCU Program) Arduino Sketch v1.1 [.ino 20.6Kb]
Rotary Encoder Library [external link]
DigitalWriteFast Library [external link]
Pressing and holding the momentary switch of the rotary encoder for around 2 seconds will toggle between modes. All LEDs will flash YELLOW to indicate Crossover mode, all LEDs will flash RED to indicate Distortion Mode. When you power up the device, it is set to distortion mode.
Rotating the rotary encoder will select the bandwidth - a white LED will flash to indicate the currently selected band. The 6 LEDs each correspond to the bands detailed above.
Pressing the momentary switch briefly will toggle between THD+N or 3rd-Order-Only measurement - all LEDs will flash PINK to indicate 3rd-Order-Only measurement, all LEDs will flash GREEN to indicate THD+N measurement.
The LED Display for distortion mode is as follows:
Play a sine wave test tone through the system and slowly turn up the gain of the amplifier. The "no signal" led should go out and the 1st LED will display blue (<0.75% distortion), indicating a clean signal. Continue increasing the gain until the LED display indicates distortion. It is not uncommon for the LED display to 'spike' as you are turning up the gain - this is fine as long as when you stop turning it settles back down. Once you have hit distortion (display shows green, yellow or red constantly) back down gain control slightly until it returns to blue (<0.75% distortion).
THD+N measurement is more suited to setting gains for a Sound Quality application, or for testing potentially faulty equipment. 3rd-Order-Only measurement is useful if you have a noisy amplifier and/or only want to detect hard clipping, so it is more suited to an SPL application.
Ideally speakers or an equivalent resistive load should be connected to the output of the amplifier while performing the above.
When first entering Crossover mode, the LED display guides the user to set their source to the appropriate volume level for accurate measurement by the ADC. The display is as follows:
Note: if optimal volume cannot be achieved, a slighty low/high level will suffice.
If calibrating a crossover, the test tone played should be at the desired crossover frequency and the crossover should be initially disabled. If it is not possible to disable the crossover, set the frequency as high as possible for Low-Pass and as low as possible for High-Pass
Once the source volume has been set correctly (blue LED displayed), press the momentary switch briefly to change the LED display to a relative level:
Adjust the crossover frequency until the correct level is shown. This is -3dB for Butterworth Filters (generally 6dB/octave, 18dB/oct) and -6dB for Linkwitz-Riley (generally 12dB/oct, 24dB/oct).
press the momentary switch briefly changes the display back to the ADC level.
To gain-match two amplifiers, adjust the gain for the first amplifier, unplug the first amplifier then hot plug the tuner into the second amplifier, adjusting the gain on the amplifier until 0dB calibration is shown.