CoProducer Measure a mix
Two identical studio headphones on black marble facing each other across a glowing A/B switch, one rimmed in cyan and one in magenta

Listening lab · 8 October 2026 · 12 min

How to run a 432 vs 440 listening test you can trust

NoDAW Labs · Lacquer Vol. 05

A fair 432 vs 440 test uses one approved master rendered two ways, level-matched within 0.1 LU, compared at the same musical position rather than the same clock time, with the labels hidden. Run a blind ABX round first to see if you can tell them apart, then a blind preference round. With 10 trials you need 9 correct before chance stops being a good explanation.

You know the scene. It's late, the session's been good, someone bounces a 432 version and hits play, and everybody in the room nods. "Yeah. Warmer." Maybe it is. But the file was also a touch louder, started on a better chord, and had a label on the screen that said 432. Every one of those things can make a version win before pitch gets a vote.

This essay is about removing those other votes. It's the same discipline you'd use to compare two limiters or two mix revisions, applied to a small pitch move. It costs about fifteen minutes and saves you from making an alternate master on a hunch.

What you're really comparing

Be clear about the variables first. A classic 440-to-432 conversion scales every frequency by 432/440, or 0.981818, which is about 31.77 cents lower. If it's done by resampling, it also runs about 1.85% longer, so a 120 BPM record lands near 117.8 BPM. What 432 Hz actually changes in a finished master covers the full picture, and Resampling, pitch shifting, and tempo change are three different jobs explains when you'd keep the runtime instead.

That means a "432 test" with a classic render is really a test of lower-and-slightly-slower. That's fine, it's what most people actually want to hear. Just know it, and don't credit pitch alone for something that might be tempo.

Then there are the variables that shouldn't be in the test at all: loudness, the chord you start on, the playback chain, the file format, and the label. Anyone who has done mix work knows the slightly louder version sounds clearer, wider, and more expensive for the first few seconds. That bias alone can decide a comparison.

Build both files from one source, then measure them

Use the same approved master for both sides, lossless, with identical processing except the retune. Don't compare a streaming rip against a fresh WAV, and don't compare a 432 file someone posted online against your own 440 bounce. Different masters, different encodes, different everything.

Then measure. Resampling barely changes perceived loudness, but "barely" isn't "zero," and sample peaks can move as the waveform is reconstructed. Turn the louder side down to match the quieter one. Never boost the version you're secretly rooting for.

Switch at the same note, not the same second

Here's the trap that catches most homemade tests. Because the classic 432 render is longer, the same syllable happens later in that file. If your player switches by clock time, you jump to a slightly different place in the music every time, and the error grows the further in you go. Your ear then compares two different moments and calls the difference "tone."

The fix is to map the position: the 432 time for a given note is the 440 time divided by 0.981818. A purpose-built synchronized player does that for you. If you're rigging it yourself in a DAW, put both files on separate tracks, time-stretch nothing, and align a downbeat near your test loop instead of the song start.

A runnable rehearsal

This script rehearses the whole method on synthetic audio: it renders a 30-second clip with a pad chord, a vibrato tone in the vocal range, an 808, and hats, makes a classic 432 version with ffmpeg's soxr resampler, measures both, gain-matches them, prints the clock-drift table, and then does the statistics for scoring a blind round.

#!/usr/bin/env python3
"""listening_test.py: build a fair 440 vs 432 listening test and score it.
Usage: python3 listening_test.py
Needs: numpy, scipy, ffmpeg (with soxr) on PATH.

1. Renders a 30 s SYNTHETIC clip (pad chord, vocal-like tone, 808, hats).
2. Makes a classic 432 version (asetrate + soxr resample), measures both.
3. Gain-matches the 432 side to the 440 side within 0.1 LU.
4. Shows why switching by clock time drifts, and the mapped position.
5. Scores a blind ABX run: how many correct you need, and how often a
   guesser or a genuinely hearing listener passes."""
import re, subprocess
import numpy as np
from scipy import stats

SR, DUR, BPM = 48000, 30.0, 120
R = 432 / 440

def ffmpeg(*args):
    subprocess.run(["ffmpeg", "-v", "error", "-y", *args], check=True)

def measure(path):
    err = subprocess.run(["ffmpeg", "-nostats", "-hide_banner", "-i", path, "-af",
                          "ebur128=peak=true:framelog=quiet", "-f", "null", "-"],
                         capture_output=True, text=True).stderr
    s = err[err.rfind("Summary:"):]
    i = float(re.search(r"I:\s+(-?[\d.]+) LUFS", s).group(1))
    tp = float(re.search(r"Peak:\s+(-?[\d.]+) dBFS", s).group(1))
    dur = float(subprocess.run(["ffprobe", "-v", "error", "-show_entries", "format=duration",
                                "-of", "csv=p=0", path], capture_output=True, text=True).stdout)
    return i, tp, dur

# 1. synthetic clip -------------------------------------------------------
t = np.arange(int(SR * DUR)) / SR
rng = np.random.default_rng(7)
def tone(f, amp, harmonics=6, decay=0.0):
    ph = 2 * np.pi * (np.cumsum(f) / SR if np.ndim(f) else f * t)   # integrate frequency -> phase
    y = sum(amp / h * np.sin(h * ph) for h in range(1, harmonics + 1))
    return y * (np.exp(-decay * (t % 2.0)) if decay else 1)
pad = tone(220.0, .10) + tone(277.18, .08) + tone(329.63, .08)          # A3 C#4 E4
vox = tone(440.0 * (1 + 0.004 * np.sin(2 * np.pi * 5.5 * t)), .12, 10)   # A4 with vibrato
beat = (t * BPM / 60) % 1.0
e808 = 0.45 * np.sin(2 * np.pi * 55.0 * t) * np.exp(-3.0 * beat)          # A1 808 on every beat
hats = 0.05 * rng.standard_normal(t.size) * np.exp(-60 * ((t * BPM / 30) % 1.0))
mix = pad + vox + e808 + hats
mix = 0.7 * mix / np.max(np.abs(mix))
stereo = np.stack([mix, np.roll(mix, 24)], axis=1).astype(np.float32)
stereo.tofile("syn_raw.f32")
ffmpeg("-f", "f32le", "-ar", str(SR), "-ac", "2", "-i", "syn_raw.f32", "-c:a", "pcm_s24le", "synthetic_440.wav")

# 2. classic 432 render ----------------------------------------------------
ffmpeg("-i", "synthetic_440.wav", "-af",
       f"asetrate={SR*R:.6f},aresample={SR}:resampler=soxr:precision=28",
       "-c:a", "pcm_s24le", "synthetic_432.wav")
i440, tp440, d440 = measure("synthetic_440.wav")
i432, tp432, d432 = measure("synthetic_432.wav")
print("STEP 2  Same synthetic source, two renders")
print(f"  440: {i440:6.1f} LUFS  {tp440:+5.1f} dBTP  {d440:7.3f} s")
print(f"  432: {i432:6.1f} LUFS  {tp432:+5.1f} dBTP  {d432:7.3f} s   (ratio {d432/d440:.5f}, expected {1/R:.5f})")
print(f"  loudness gap before matching: {i432 - i440:+.2f} LU")

# 3. gain match -----------------------------------------------------------
g = i440 - i432
ffmpeg("-i", "synthetic_432.wav", "-af", f"volume={g:.3f}dB", "-c:a", "pcm_s24le", "synthetic_432_matched.wav")
im, tpm, _ = measure("synthetic_432_matched.wav")
print(f"\nSTEP 3  Apply {g:+.2f} dB to the 432 side")
print(f"  432 matched: {im:6.1f} LUFS  {tpm:+5.1f} dBTP  -> gap now {im - i440:+.2f} LU (target within 0.1)")

# 4. clock drift vs mapped position ----------------------------------------
print("\nSTEP 4  Switching by clock time vs by musical position (120 BPM)")
print("  440 time   432 same-note time   clock-switch error   error in beats")
for s in (10, 30, 60, 120, 180):
    m = s / R
    print(f"  {s:6.1f} s   {m:10.3f} s          {1000*(m - s):8.0f} ms          {(m - s) * BPM / 60:6.2f}")

# 5. scoring a blind ABX run -----------------------------------------------
print("\nSTEP 5  Blind ABX scoring (one-sided binomial, chance = 50%)")
for n in (8, 10, 12, 16, 20):
    k = next(k for k in range(n + 1) if stats.binom.sf(k - 1, n, 0.5) < 0.05)
    print(f"  {n:2d} trials: need {k:2d} correct for p<0.05   (P(guess 7/10 or better) = "
          f"{stats.binom.sf(6, 10, 0.5):.3f})" if n == 10 else f"  {n:2d} trials: need {k:2d} correct for p<0.05")
rng = np.random.default_rng(440432)
order = rng.choice(["X=A(440)", "X=B(432)"], size=12)
print("  example randomized 12-trial key:", " ".join(o[2] for o in order))
for p_true in (0.5, 0.65, 0.75, 0.9):
    k12 = next(k for k in range(13) if stats.binom.sf(k - 1, 12, 0.5) < 0.05)
    passes = np.mean(rng.binomial(12, p_true, size=100000) >= k12)
    print(f"  listener right {int(p_true*100)}% of the time: passes a 12-trial test in {100*passes:5.1f}% of sessions")

for f in ("syn_raw.f32", "synthetic_440.wav", "synthetic_432.wav", "synthetic_432_matched.wav"):
    subprocess.run(["rm", "-f", f])

Real output from this machine. The clip is synthetic, generated by the script, not a commercial recording.

STEP 2  Same synthetic source, two renders
  440:  -14.5 LUFS   -3.1 dBTP   30.000 s
  432:  -14.5 LUFS   -3.1 dBTP   30.556 s   (ratio 1.01852, expected 1.01852)
  loudness gap before matching: +0.00 LU

STEP 3  Apply +0.00 dB to the 432 side
  432 matched:  -14.5 LUFS   -3.1 dBTP  -> gap now +0.00 LU (target within 0.1)

STEP 4  Switching by clock time vs by musical position (120 BPM)
  440 time   432 same-note time   clock-switch error   error in beats
    10.0 s       10.185 s               185 ms            0.37
    30.0 s       30.556 s               556 ms            1.11
    60.0 s       61.111 s              1111 ms            2.22
   120.0 s      122.222 s              2222 ms            4.44
   180.0 s      183.333 s              3333 ms            6.67

STEP 5  Blind ABX scoring (one-sided binomial, chance = 50%)
   8 trials: need  7 correct for p<0.05
  10 trials: need  9 correct for p<0.05   (P(guess 7/10 or better) = 0.172)
  12 trials: need 10 correct for p<0.05
  16 trials: need 12 correct for p<0.05
  20 trials: need 15 correct for p<0.05
  example randomized 12-trial key: A B B B B A B B A A B B
  listener right 50% of the time: passes a 12-trial test in   2.0% of sessions
  listener right 65% of the time: passes a 12-trial test in  15.1% of sessions
  listener right 75% of the time: passes a 12-trial test in  39.0% of sessions
  listener right 90% of the time: passes a 12-trial test in  89.1% of sessions

Three things to take from that. First, on this clip the loudness gap after resampling rounded to zero and true peak didn't move, so the gain-match step applied nothing. That won't always be true for a dense, limited master, and the only way to know for your track is to measure it. Second, look at the drift table: switching by clock time is 185 ms off at ten seconds, over a full second off at one minute, and more than six beats off at three minutes. That's not a subtle bias, it's a different part of the song. Third, the statistics are stricter than intuition. Getting 7 out of 10 right feels convincing, but a coin does that or better about 17% of the time.

No synced player? Cut matched loops instead

If you're working from files, the simplest fair setup is two short loops that contain exactly the same notes, level-matched, with neutral names. This little script does it with ffmpeg. You give it the 440 master, the 432 render, a start time, and a length in 440 time. It works out where those same notes live in the 432 file, cuts both, turns only the louder one down, then shuffles them into blind_1.wav and blind_2.wav with the answer in a key file you hand to your helper.

#!/usr/bin/env bash
# make_blind_pair.sh: cut the SAME musical passage from a 440 master and its
# classic 432 render, level-match them, and hide which is which.
# Usage: ./make_blind_pair.sh master_440.wav master_432.wav START_SEC LENGTH_SEC
set -euo pipefail
A="$1"; B="$2"; S="$3"; L="$4"
R=$(python3 -c "print(432/440)")
S432=$(python3 -c "print($S/$R)"); L432=$(python3 -c "print($L/$R)")   # same notes, later and longer
lufs() { ffmpeg -nostats -hide_banner -i "$1" -af ebur128=framelog=quiet -f null - 2>&1 \
         | awk '/Summary:/{f=1} f&&/ I:/{print $2; exit}'; }
ffmpeg -v error -y -ss "$S"    -t "$L"    -i "$A" -c:a pcm_s24le loop_440.wav
ffmpeg -v error -y -ss "$S432" -t "$L432" -i "$B" -c:a pcm_s24le loop_432.wav
IA=$(lufs loop_440.wav); IB=$(lufs loop_432.wav)
G=$(python3 -c "print(round(min(0.0, $IA-$IB),2))")       # only ever turn the 432 side DOWN
H=$(python3 -c "print(round(min(0.0, $IB-$IA),2))")       # ...or the 440 side down
ffmpeg -v error -y -i loop_440.wav -af "volume=${H}dB" -c:a pcm_s24le loop_440_m.wav
ffmpeg -v error -y -i loop_432.wav -af "volume=${G}dB" -c:a pcm_s24le loop_432_m.wav
if [ $((RANDOM % 2)) -eq 0 ]; then X=loop_440_m.wav; Y=loop_432_m.wav; K="1=440 2=432"
else X=loop_432_m.wav; Y=loop_440_m.wav; K="1=432 2=440"; fi
mv "$X" blind_1.wav; mv "$Y" blind_2.wav; echo "$K" > KEY_dont_peek.txt
rm -f loop_440.wav loop_432.wav loop_440_m.wav loop_432_m.wav
echo "440 loop: start ${S}s len ${L}s   ${IA} LUFS"
echo "432 loop: start ${S432}s len ${L432}s   ${IB} LUFS"
echo "blind_1: $(lufs blind_1.wav) LUFS   blind_2: $(lufs blind_2.wav) LUFS   key saved to KEY_dont_peek.txt"

Here's the real run, on a synthetic 90-second pair (a sustained chord, a vibrato tone, and an 808 pulse generated by ffmpeg, then resampled to 432):

$ ./make_blind_pair.sh synthetic_master_440.wav synthetic_master_432.wav 60 20
440 loop: start 60s len 20s   -12.9 LUFS
432 loop: start 61.111111111111114s len 20.37037037037037s   -13.0 LUFS
blind_1: -13.0 LUFS   blind_2: -13.0 LUFS   key saved to KEY_dont_peek.txt
durations: blind_1 20.370375s  blind_2 20.000000s

Notice that the 432 loop starts at 61.1 seconds, not 60, and runs 20.37 seconds instead of 20. That's the same passage; it just takes longer to play. Hide the timeline while you listen, because the loop length is the one giveaway left once the levels are matched.

Pick a passage that can actually reveal it

Choose 15 to 30 seconds of a song you know well, ideally one you made or have the rights to. Exposed vocals, sustained chords, piano tails, strings, pads, and long sub notes show pitch movement clearly. A dense, distorted chorus full of percussion can hide a third of a semitone almost completely. Which songs reveal a 432 Hz retune, and which hide it goes deep on choosing material, including why your own catalog beats famous examples.

Loop that passage. Short loops beat full-song playthroughs because auditory memory for fine pitch and tone fades in seconds.

Round one: can you tell them apart at all?

Before asking which is better, find out whether you can reliably hear a difference under blind conditions. That's what an ABX test does. You hear A (440) and B (432) labeled, then an unlabeled X that is secretly one of them, and you say which. Repeat at least ten times with the key randomized.

The last block of the output is worth a second look, because it cuts both ways. A listener who really hears the difference 75% of the time only passes a 12-trial test in about 39% of sessions. So one failed run doesn't prove you can't hear it. Run another session another day. If you pass repeatedly, the difference is real for you on that material.

Round two: which do you prefer?

Now the question you actually care about. Keep it blind. Have your helper play the two in random order and ask you only which you prefer and how confident you are, on a simple 1 to 5 scale. Record preference and confidence separately. A string of low-confidence "B, I guess" answers tells you something very different from a string of confident ones.

Describe what you hear in concrete words before you name a winner: vocal chest weight, cymbal edge, bass sustain, piano brightness, perceived pace, tension. Avoid "better," "natural," or "healing" in your notes. Those words decide the answer before the listening does.

Then reverse the order, take five minutes of silence, and come back. Repeated switching creates fatigue and false certainty fast. A preference that only exists after twenty minutes of flipping is usually exhaustion talking. Test again the next day at a moderate level, on headphones and on speakers.

The biases and their controls

BiasHow it fools youControl
LoudnessThe louder side sounds clearer and widerMeasure both, match within 0.1 LU, lower the louder side
Labels"432" or "healing" primes the answerHidden labels, helper drives the switch
Position driftClock-time switching compares different notesMap position, or align a downbeat near the loop
Source mismatchDifferent masters or encodes differ in many waysBoth sides rendered from one lossless master
Order and fatigueWhatever plays second, or last, can winReverse order, short sessions, silence between rounds
Small samplesA lucky streak feels like proofAt least 10 trials, score against chance, repeat another day

The fifteen-minute protocol

  1. Render 440 and 432 from the same lossless master. Measure both and match within 0.1 LU.
  2. Pick one revealing 15 to 30 second loop from a song you know.
  3. Write a randomized key for 10 to 12 ABX trials. Have someone else run it.
  4. Score it against chance. If you can't pass, stop here and keep the original.
  5. If you can, run 10 blind preference trials, logging preference and confidence separately.
  6. Break for five minutes. Reverse the order. Repeat on a second playback system tomorrow.
  7. Save the loop location, system, date, and source file with your notes.

What to do with the answer

No reliable difference, or no consistent preference? That's a good result. It means the record is fine as it is, and you just saved yourself from managing a second master. If the 432 version keeps winning blind, across days and systems, it has earned a full render. At that point it becomes a delivery job, and 432 Hz mastering: LUFS and true peak after a retune covers getting it out the door without clipping or cheating the comparison.

The same habits pay off everywhere else in your work. Level-matched, position-aligned comparison is the backbone of Reference matching without guesswork, and it's also why streaming platforms turn loud masters down: they remove the loudness vote for listeners, so you might as well remove it for yourself first.

Can you actually hear the difference between 432 and 440 Hz?
Often, yes, on a sustained vocal or chord when you switch quickly at the same musical position. The change is about 31.77 cents, a third of a semitone. Whether you prefer it is a separate question, and a blind test is the only fair way to answer either.
How many trials does a blind ABX test need?
Ten is a practical minimum. With 10 trials you need 9 correct for p<0.05 against guessing; 7 out of 10 happens by pure chance about 17% of the time. Sixteen trials need 12 correct.
How closely should the two versions be level-matched?
Within 0.1 LU of integrated loudness. Measure both renders and turn the louder one down to match. Never boost the side you want to win, and never touch the monitor knob between switches.
Why does the 432 version drift out of sync?
A classic resampled 432 version is about 1.85% longer, so the same note arrives later. At one minute in, switching by clock time lands about 1.1 seconds off. A good player maps the musical position instead.
What if I don't hear a reliable difference?
That's a valid result. Keep the original master. A preference that doesn't survive blind switching, reversed order, and a break isn't a strong enough reason to make an alternate.

A free one-pager

The checklist we run before every release.

Loudness, true peak, mono, tails, export settings and the metadata basics, on one page. Leave your email and it opens straight away, or read it here first. No email needed for that.