Data AcQuisition And Real-Time Analysis
Scope - Spectrum - Spectrogram - Signal Generator
Software for Windows
Science with your Sound Card!
The following is from the Daqarta Help system:



Spectrum Analyzer

Signal Generator

(Absolutely FREE!)


Pitch Tracker


DaqMusiq Generator
(Free Music... Forever!)

Engine Simulator

LCR Meter

Remote Operation

DC Measurements

True RMS Voltmeter

Sound Level Meter

Frequency Counter
    Spectral Event

    MHz Frequencies

Data Logger

Waveform Averager


Post-Stimulus Time
Histogram (PSTH)

THD Meter

IMD Meter

Precision Phase Meter

Pulse Meter

Macro System

Multi-Trace Arrays

Trigger Controls


Spectral Peak Track

Spectrum Limit Testing

Direct-to-Disk Recording



Frequency response

Distortion measurement

Speech and music

Microphone calibration

Loudspeaker test

Auditory phenomena

Musical instrument tuning

Animal sound

Evoked potentials

Rotating machinery


Product test

Contact us about
your application!

Sound Card THD Meter Mini-App


Note: Before using this automated THD meter, you may want to review the fundamentals of distortion measurement.

THD_Meter is a macro mini-app included with Daqarta that provides a resizeable meter showing Total Harmonic Distortion. It can generate a test signal and analyze the distortion of amplifiers, speakers, microphones, or other systems. It can also be used without the signal generator to analyze the distortion of external oscillators or similar signals.

You can run THD_Meter by hitting the F8 key followed by the 'T' key, or hitting CTRL+F8 to open the Macro Dialog and double-clicking on THD_Meter in the Macro List.

THD_Meter uses a Custom Controls dialog that allows adjustment of various parameters. You can open this Help topic by right-clicking anywhere in the dialog, or on the separate meter itself.

The THD_Meter macro sets Spectrum display mode and loads the THDmeter.GEN Generator setup. It also opens a Custom Controls dialog with an edit/slider control to set the Fundamental (test) frequency, a Max Harmonic control, an adjustable time constant for the meter, an Internal/ External Source button, an Arbitrary / Spectral Line Lock frequency mode button, a channel select button, a Raw / Spectrum Average / Wave Average button, a time constant reset button, and a Percent / dB meter toggle button.

THD_Meter was used for the THD+N and THD 25 measurements under the Harmonic Distortion section of Sound Card Performance Tests.

Initial Set-Up:

The meter itself is a separate Custom Meter that shows both THD+N (Total Harmonic Distortion plus noise) and THD, as well as the selected channel. If that channel is not active, the meter shows "No Signal" plus the channel. Note that THD_Meter does not automatically toggle Input on (nor set Input Channels, Lines, or Levels), so the default Left In channel may show "No Signal" unless you do that manually.

In either Internal or External Source modes, it is important that you adjust output and input signal levels for best results. If the input signal is too high, it can overload the input and cause distortion. As the input approaches clipping, you will see a forest of spectrum peaks rise up. Reduce the level until you are satisfied that the system is not near clipping. (This is usually visible in the Spectrum display before it becomes audible.)

Note that clipping can be caused by excessive levels at the sound card input even when the amp (or other device under test) is not itself clipping. Some sound card outputs may have clipping or other increased distortion when run at maximum levels. And of course the device under test will distort when driven at high levels.

When you measure the distortion of any device or system, the operating level is an important parameter. With most amplifiers, the distortion increases gently with level until there is a dramatic increase near clipping. But at low levels the situation is reversed due to the presence of crossover distortion. As the waveform passes through zero and the amp switches between "push" and "pull" circuitry, there is a glitch in the waveform. It's roughly the same size at all levels, so it's a bigger percentage of a smaller waveform.

The meter continues to run after closing the control dialog, until it its closed separately.

When you close the THD Meter Controls and THD Meter windows, the THDmeter.GEN Generator setup remains loaded and active. The setup includes current output volume settings, so if you have changed them and want to use those settings again in the future, save the setup. (Current input settings are always saved automatically when Daqarta is closed, and restored on the next restart.)

How It Works:

THD+N is computed by integrating the energy of the spectrum at all frequencies higher than the fundamental, plus all frequencies below the fundamental (except DC), and dividing that by the energy at the fundamental. The sSig() (Spectrum Sigma) function is used to integrate energies.

THD is computed by including only frequencies that are integer multiples of the fundamental, then dividing by the energy at the fundamental. For both THD+N and THD the measurement is limited to frequencies below the Nyquist frequency, which is 24000 Hz (half the sample rate).

The Max Harmonic control allows you to restrict the harmonics included in the THD measurement. (It has no effect on THD+N.) You might want to use this to compare with discrete measurements of the first few harmonics, for example. (See Sound Card Performance Tests.)

The default setting is 25, but frequencies are also restricted to less than 24000 Hz. In the default Spectral Lime Lock mode, the default fundamental is 984.375 Hz (nearest spectral line to 1000), so the actual maximum harmonic would be 24, or 23625 Hz.

Note that distortion measurements do not require any system calibration, since they are always measured as a percentage of the fundamental amplitude.

Spectral Line Lock:

By default the controls start with the frequency mode set to Spectral Line Lock. This causes the test frequency to move only to frequencies that fall exactly on spectral lines, so no window function is needed. This gives a greatly improved noise floor and hence better resolution of low THD levels. If you toggle it to Arbitrary, a Blackman-Harris window is automatically used.

For comparison, toggle the channel from Left In to Left Out. This causes the meter to look at the original signal, as generated, without passing through the output D/A and the input A/D. The default fundamental is 984.375 Hz, the nearest spectral line to the standard 1000 Hz test frequency.

With Spectral Line Lock active, THD+N reads 0.0012% and THD alone reads 0.0002%. Toggling to Arbitrary Frequency and setting Fundamental to 1000 Hz causes those readings to jump to 0.1335% and 0.0180%, respectively. Those are the lowest readings you could get, assuming perfect D/A and A/D and device under test. So, unless you absolutely must use 1000 Hz or another specified frequency, use the closest Line Lock frequency instead.

External Source:

To measure the THD of an external oscillator, toggle the Internal Source button to External Source. In this mode the Generator is toggled off and the Fundamental control is disabled, along with the frequency mode and Raw / Average buttons. Spectrum Cursor Peak and Track modes are enabled so Daqarta can automatically find the oscillator fundamental. Since that won't be locked to a spectrum line, a Blackman-Harris window function is used.

Meter Time Constant:

For noisy signals, the Time Constant Exponent control provides simple smoothing of jittery meter values. The control value ranges from 0 to 10 and is applied as a power of 2 to determine the actual time constant, which then ranges from 1 (no smoothing) to 1024 (about 10 seconds). The default exponent is 5, which yields a time constant of 32.

The smoothed value is reset with the current instantaneous value whenever you change any control that could affect the reading, including the test frequency, Max Harmonics, channel, or Internal/External, Arbitrary/Locked, or Raw/Average modes.

You can use the TC Reset button to reset the smoothed value manually. You might want to do this when using a high time constant, if something changes in the system under test and you don't want to wait for the meter to settle. Note, however, that a reset just starts smoothing from the current value, which may happen to be high due to jitter.

Spectrum and Waveform Averaging:

Clicking the Raw button once toggles Spectrum Average. This activates a continuous Exponential average with a 32-frame time-constant. The averaged spectrum shows the same fundamental and harmonic peaks, but with an averaged noise floor that reduces the jumpiness of the meter readouts while still showing the same average values.

A second click toggles Waveform Average, likewise a continuous Exponential average with a 32-frame time-constant. But unlike spectrum averaging, waveform averaging actually reduces the noise floor... here by a factor of 32, or 15 dB. Although this means the THD+N values are no longer correct (due to the noise reduction), this does allow reading correct THD values in the presence of noise that might otherwise mask a low THD system.

THD_Meter Macro Listing:

The overall THD_Meter mini-app actually consists of the initial THD_Meter macro which launches both the Custom Controls dialog handler _THD_Ctrls, and also the Custom Meter handler _THD_Task. _THD_Ctrls invokes another macro called _THD_Freq to handle fundamental frequency changes.

Close=                             ;Close any open data file
Task="-_THD_Task"                  ;Uninstall task, if present
TrackThresh=-20                    ;Peak Track details
TrackMin=200                       ; for External Source
SpectWind=BkHr                     ;Window for Arbitrary Freq
UI=Input                           ;Save current Input state
A.LoadGEN="THDmeter"               ;Load Gen setup
Input=UI                           ;Restore initial Input state
Ctrls="<<THD Meter Controls"       ;Custom Controls title
Ctrl0="<<Fundamental Frequency"    ;Ctrl0 label
Ctrl1="<<Max Harmonic"
Ctrl3="<<Time Constant Exponent"
Btn0="Internal Source"
Btn1="Spectral Line Lock"
Avg=0                              ;Start with Average off
Btn3="Raw"                         ;Non-Avg
Btn4="TC Reset"
UT=TrigMode                        ;Save trigger mode for exit
TrigMode=GenSync                   ;Trigger on Gen Sync
Ctrl0="<S(20,10000)"               ;Fund freq range
Ctrl1="<S(2,25)"                   ;Max harmonics to include
Ctrl1="<p(0)"                      ;Integer display
Ctrl2="<X"                         ;No Ctrl2
Ctrl3="<S(0,10)"                   ;TC Exponent range
Ctrl3="<p(0)"                      ;Integer display
Ctrl0=984.375                      ;Fund freq default
Ctrl1=25                           ;Include up to 25 harmonics
Ctrl3=5                            ;Time Constant Exp. default
QT=32                              ;TC default (2^Ctrl3)
UK=1                               ;Reset TC on next update
Btn0="<T"                          ;Btn0 = Toggle type
Btn2="<M"                          ;Momentary
Btn3="<M(2)"                       ;Momentary, posns 0-2
Btn0=0                             ;Internal source default
Btn1=0                             ;Line Lock default
Btn2=0                             ;Left In default chan
Btn3=0                             ;Raw (non-Avg) default
Btn5=0                             ;Percent default
Mtr0="<<THD Meter"                 ;Meter title
Mtr0="<C(0)"                       ;Black meter text
Mtr0="<B(hFFFFFF)"                 ;White meter background
Mtr0="<H4900"                      ;Right-click opens this Help
Task="_THD_Task"                   ;Install task that does the work
@_THD_Ctrls=Ctrls              ;Open Custom Controls dialog
;Mtr0=                  ;Uncomment for Mtr0 close
;Task="-_THD_Task"      ; on Ctrls close

_THD_Ctrls Macro Listing:

IF.Ctrls=0                 ;Ctrl0 = Freq change?
    @_THD_Freq                     ;Use separate macro

IF.Ctrls=1                     ;Ctrl1 = Max Harmonics?
    UN=Ctrl1                       ;Get integer
    Ctrl1=UN                       ;Show integer
    UK=1                           ;Reset TC next update

IF.Ctrls=3                     ;Ctrl3 = TC Exponent?
    Q3=Ctrl3                       ;Get integer
    Ctrl3=Q3                       ;Show integer
    QT=2^Q3                        ;TC = 2^Ctrl3

IF.Ctrls=4                     ;Btn0 = Int/Ext Source?
    IF.Btn0=1                      ;External?
        Btn0="External Source"
        Ctrl0="<D"                     ;Disable Freq
        Btn1="<D"                      ;Disable Arb/Line Lock
        IF.U3=2                        ;Wave avg running?
            U3=0                           ;Switch to Raw if so
            Avg=0                          ;Avg off
        IF.U3=1                        ;Spectrum avg running?
            Avg=1                          ;Restart if so
        SpectWindOn=1              ;Use windowing
        SpectPeak=1                ;Use peak track
        Gen=0                      ;No Generator
    ELSE.                          ;Else Internal source
        Btn0="Internal Source"
        Ctrl0="<N"                     ;Re-enable Ctrl0 Freq
        Btn1="<N"                      ;Re-enable Btn1 Arb/Line Lock
        SpectWindOn=Btn1               ;Use Window if Arb
        Gen=1                          ;Generator on
        @_THD_Freq                     ;Set freq
    UK=10                          ;Reset TC on 10th update

IF.Ctrls=5                     ;Btn1 = Arb/Locked freq?
    IF.Btn1=1                      ;Arb freq?
        Btn1="Arbitrary Frequency"
        Fstep=Dir                  ;Allow any freq entry
        Btn1="Spectral Line Lock"
        Fstep=Line                 ;Allow only Line steps
    SpectWindOn=Btn1                   ;Use Window if Arb
    @_THD_Freq                         ;Update freq

IF.Ctrls=6                     ;Btn2 = Channel?
    Ch=(Ch+1) & 3                  ;Next chan, 0-3 only
    Btn2="" + Ch(c)                ;Show chan
    UK=1                           ;Reset TC on next update

IF.Ctrls=7                     ;Btn3 = Raw / Avg modes
    Trig=1                             ;Trigger for Avg
    U3=U3+1                        ;Next mode
    IF.U3=>2                       ;Limit to 0-2
    IF.U3=2                        ;Roll to Wave avg?
        IF.Btn0=1                      ;Ext source?
            U3=0                           ;Force Raw instead
    IF.U3=0                        ;Raw?
        Avg=0                          ;No Average
    IF.U3=1                        ;Spectrum Avg?
        Btn3="Spectrum Average"
        SpavgMode=Exp                  :Exponential mode
        SpavgFrames=32                 ;32 frames
        Avg=1                          ;Start Avg
    IF.U3=2                        ;Wave Avg?
        Btn3="Wave Average"
        WavgMode=Exp                   ;Exonential mode
        WavgFrames=32                  ;32 frames
        Avg=0                          ;(Spect) avg off
        Spect=0                        ;Spect display off
        Avg=1                          ;Start Wave avg
        Spect=1                        ;Display in Spect mode
    UK=10                          ;Reset TC on 10th update

IF.Ctrls=8                     ;Btn4 = TC Reset
    UK=1                           ;Reset on next update

IF.Ctrls=9                     ;Btn5 = Percent/dB
    U5=Btn5                        ;Mode copy (use if Ctrls closed)

_THD_Freq Macro Listing:

IF.Btn1=0                          ;Spectral Line Lock?
    IF.Ctrl0?u=!0                      ;Inc/Decrement?
        L.0.ToneFreq=>Ctrl0?u          ;Add/sub Line step so
        L.0.ToneFreq=Ctrl0             ;Else line-limited direct
    L.0.ToneFreq=Ctrl0             ;Arb freq = direct set
A=L.0.ToneFreq                     ;Copy freq for test
IF.A=0                             ;0 (after Line limit)?
    A=SmplRate/1024                    ;Set min Line if so
Ctrl0=A                            ;Show (limited) Ctrl0 freq value
IF.U3=1                            ;Spectrum Avg active?
    Avg=1                              ;Restart if so
IF.U3=2                            ;Waveform avg active?
    Spect=0                            ;Spect display off
    Avg=1                              ;Restart Wave avg
    Spect=1                            ;Display in Spect mode
UK=10                              ;Reset TC on 10th update

_THD_Task Macro Listing:

IF.Gen=1                               ;Generator on (Internal Source)?
    F=L.0.ToneFreq                     ;Fund = tone freq
    F=Posn?F                           ;Else Fund = tracked freq
X=SpectWindOn * 4                  ;0 if Line Lock, 4 if Arb
I=cint(F * 1024 / SmplRate)    ;Integer spectral line
A=Sp(Ch,I)                         ;Magnitude of fund peak
IF.A=0                                 ;No fundamental?
    Mtr0="No Signal" +n + Ch(c)
    L=sSig(3,I-X-1)                        ;Energy below fund
    U=sSig(I+X+1,511)                      ;Energy above fund
    D=100*(sqrt(U^2 + L^2) / A)            ;Total THD+N as % of fund

    N=2                                    ;Start w. 2nd harmonic
    T=0                                    ;Total energy so far
    H=N * F                                ;Freq of 2nd harm
    WHILE.H=<(SmplRate/2)              ;Test all freqs in range
        I=cint(H * 1024 / SmplRate)            ;Spectral line
        U=Sp(Ch,I)                             ;Magnitude at line
        T=sqrt(T^2 + U^2)                      ;Combine w. total (RMS)
        N=N + 1                                ;Next harmonic
        IF.N=>UN                               ;Past limit?
            LoopBreak=2                            ;Stop if so
        H=N * F                                ;Else next freq
    H=100 * T / A                          ;THD as % of fund
    IF.(D + H)=>100                        ;Err if no signal
        Mtr0="No Signal" +n + Ch(c)
        UK=UK-1                            ;TC countdown after change
        IF.UK=0                            ;Reset at 0
            Q=D                                ;Accums = latest
        Q=Q + (D - Q) / QT                 ;Apply TC to display values
        R=R + (H - R) / QT
        IF.U5=0                            ;Percent?
            Mtr0=Q(0.4) + "% THD+N" +n + R(0.4) + "% THD " +UN +n + Ch(c)
        ELSE.                              ;Else show dB
            D=20 * log10(Q/100)            ;Convert TC accums to dB
            H=20 * log10(R/100)
            Mtr0=D(0.2) + " dB THD+N" +n + H(0.2) + " dB THD " +UN +n + Ch(c)

IF.Mtr0?E=1                            ;Meter close?
    Task="-_THD_Task"                      ;Uninstall task
    Avg=0                                  ;Average off
    TrigMode=UT                            ;Restore Trigger mode

See also Macro Examples and Mini-Apps


Questions? Comments? Contact us!

We respond to ALL inquiries, typically within 24 hrs.
Over 35 Years of Innovative Instrumentation
© Copyright 2007 - 2021 by Interstellar Research
All rights reserved