Chord Detection — User Guide

Offline spectral-template estimation of chords and pitch classes from sustained harmonic audio, with multichannel evidence pooling, confidence scoring, temporal confirmation, TextGrid output, and comparative visualization.

Author: Shai Cohen Affiliation: Department of Music, Bar-Ilan University, Israel Version: 0.4.1 (2026) License: MIT License Repo: https://github.com/ShaiCohen-ops/Praat-plugin_AudioTools
Contents:

What this does

Chord Detection v0.4.1 analyzes exactly one selected Praat Sound. Each frame is Hanning-windowed, spectral peaks are measured and refined, likely harmonic peaks can be softly downweighted, and the remaining evidence is folded into 12 equal-tempered pitch classes. Common chord templates are then scored against the measured pitch-class salience. A best-versus-runner-up confidence margin and a temporal confirmation stage convert frame estimates into chord segments.

What is scored chord-template estimation? The script does not require an exact pitch-class set match. For each plausible root and each implemented chord quality, it scores how much active pitch-class evidence lies inside the template, penalizes unexplained active pitch classes, rewards template coverage and measured root evidence, and uses bass evidence only as a weak tie-breaker. This allows incomplete or uneven spectral evidence to be evaluated without treating every detected pitch class as equally certain.

The script is intended for sustained harmonic material. It is not a general-purpose music-information-retrieval transcription system and does not estimate note octaves, voicings, or slash-chord bass labels. Rootless voicings, inharmonic sounds, dense clusters, weak extensions, and chord changes shorter than the analysis/confirmation timescale can remain ambiguous.

Current signal path Sound → frame RMS / silence test → Hanning frame per channel → FFT spectrum → measured local maxima + parabolic frequency refinement → relative peak threshold + frequency separation + top-K peaks → optional soft harmonic downweighting → A4-referenced pitch classes → per-channel salience normalization + multichannel max pooling → pitch-class floor → scored chord templates → best-vs-runner-up confidence → temporal confirmation / hysteresis → TextGrid + optional visualization

Quick start

  1. Select exactly one Sound object in Praat.
  2. Run CHORD DETECTION.praat.
  3. Choose a preset. Standard Analysis is the default.
  4. If using Custom, set the analysis window, time step, spectral range, tuning reference, peak-evidence controls, confidence margin, and minimum confirmation duration.
  5. Leave Draw visualization enabled if you want the four-panel analysis view.
  6. Enable Show all detections only when frame-by-frame output in the Info window is useful.
  7. Enable Open TextGrid editor if the Sound and resulting TextGrid should open immediately in the editor.
  8. Run the analysis. The resulting TextGrid is named <source>_chords.
Input and output: The script analyzes the selected Sound without modifying it. It creates a TextGrid with two interval tiers: chords and pitch_classes. No resynthesized or processed Sound is produced.

Analysis pipeline

1. Input, multichannel handling, and silence test

The source duration, sampling rate, channel count, and whole-file RMS are measured first. Every channel is copied or extracted separately for spectral analysis. The channel with the highest whole-file RMS is retained as the representative display channel for visualization Panel A, but chord evidence is not restricted to that channel.

Frame relative level: frameRelDb = 20 × log10((frameRMS + ε) / (globalRMS + ε)) If frameRelDb < Silence_floor_dB: frame label = Silence spectral/chord analysis for that frame is skipped

For multichannel material, spectra are measured per channel. Pitch-class salience is normalized within each channel, weighted by that channel's frame RMS relative to the full-frame RMS, and pooled with a maximum across channels. This avoids phase cancellation from fold-down while retaining pitch evidence that may exist mainly in one channel.

2. Frame extraction and FFT

Frames begin at Skip_transient_ms and advance by Time_step_ms while a complete analysis window still fits inside the Sound. Each channel frame is extracted with a Hanning window and converted with To Spectrum: "yes".

Frame start times: Skip_transient, Skip_transient + Time_step, Skip_transient + 2 × Time_step, ... Frame center stored for display: frame_start + 0.5 × Window_size

The FFT uses Praat's fast-spectrum path and may be zero-padded internally. The resulting FFT-bin spacing should therefore not be interpreted as the intrinsic resolving power of the Hanning analysis window; window duration still governs the main time/frequency trade-off.

3. Measured spectral peaks

Only bins inside the requested analysis range are scanned. Local maxima are detected in a single rolling pass. A peak must exceed its left neighbor and be at least as large as its right neighbor. The peak frequency is then refined by a three-point parabolic interpolation in log magnitude, with the sub-bin shift limited to ±0.5 bin.

Channel threshold: effectiveThreshold = channelMaxDb - Relative_peak_threshold_dB Frequency-domain separation: peaks closer than Min_peak_separation_Hz are represented by the stronger peak Peak limit: retain at most Max_peaks_to_keep strongest surviving peaks

4. Soft harmonic downweighting

If Downweight harmonic duplicates is enabled, a peak can be treated as a likely harmonic when it lies near an integer multiple 2–8 of a lower-frequency retained peak and falls within Harmonic_tolerance_cents. The lower peak must not be more than 24 dB below the candidate peak.

The likely harmonic is not deleted. Its contribution is multiplied by Harmonic_residual_weight_percent / 100. Thus the Standard value of 12 means that 12% of that peak's relative-magnitude evidence is retained.

Important: Harmonic classification is a spectral heuristic, not source separation. A true chord tone can coincide with a harmonic relation and therefore be downweighted; conversely, a harmonic that does not satisfy the tested relation remains fully weighted.

5. Frequency to pitch-class evidence

Each retained peak is mapped to the nearest equal-tempered MIDI semitone relative to the user-selected A4 reference. Octave information is then discarded and evidence is accumulated into 12 pitch classes.

MIDI estimate: midi = 69 + 12 × log2(f / Tuning_A4_Hz) Pitch class: pc = round(midi) mod 12 Per-peak evidence: relative magnitude × harmonic factor

After multichannel pooling, pitch-class salience is normalized by the strongest pitch class in that frame. Only pitch classes at or above Pitch_class_floor_percent remain active for chord scoring and for the pitch_classes TextGrid tier.

6. Monophonic, dyadic, and chord decisions

If no pitch class survives the floor, the frame is labeled No clear pitch. If one pitch class survives, or if the Monophonic Melody preset is active, the strongest pitch class is reported directly. With exactly two active pitch classes, the script reports the two measured pitch classes rather than inferring a harmonic root from a dyad.

With three or more active pitch classes, only active pitch classes are considered as candidate roots. Each of the 15 implemented chord qualities is scored for each candidate root.

Chord-candidate score

score = mean template salience - 0.18 × unexplained active salience + 0.12 × template coverage + 0.05 × measured root salience + bass tie-breaker bass tie-breaker = 0.10 only when candidate root = measured bass pitch class

The bass term is intentionally small, so ordinary inversions remain primarily template-driven rather than being relabeled according to the lowest detected pitch.

7. Confidence and ambiguity

The strongest candidate is compared with the best non-equivalent runner-up. Symmetry-equivalent roots are excluded from the runner-up competition for augmented triads and diminished-seventh chords.

confidence = 100 × (bestScore - secondScore) / |bestScore| clamped to 0–100 percent If confidence ≥ Min_confidence_percent: report winning chord template else: report "Ambiguous: " + active pitch classes

This means that a chord extension can be present in the measured pitch classes yet still fail the confidence margin if a simpler competing template scores nearly as well.

8. Temporal confirmation

Frame labels are converted to the chords tier through consecutive-frame confirmation rather than by deleting short segments after the fact.

required confirmation frames: ceiling(Min_chord_duration_ms / Time_step_ms) A new label becomes stable only after that many consecutive frames. When confirmed, the segment boundary is placed at the start of the pending run.

If no interpretation ever reaches confirmation, the script preserves the longest unconfirmed run rather than returning an entirely empty chord tier.

Parameters & presets

Form controls

ParameterDefaultImplemented behavior
PresetStandard AnalysisSelects one of five configured analysis modes or Custom.
Window_size_ms120Hanning analysis-window duration. Values below 20 ms are clamped to 20 ms.
Time_step_ms50Frame-start spacing. Values below 5 ms are clamped to 5 ms.
Skip_transient_ms10Analysis begins this far after the start of the Sound.
Min_frequency_Hz60Lowest FFT frequency considered. Must remain below Nyquist.
Max_frequency_Hz4000Highest requested analysis frequency; clamped to 0.98 × Nyquist when necessary.
Tuning_A4_Hz440Equal-tempered frequency-to-pitch mapping reference. Hard allowed range: 300–500 Hz.
Relative_peak_threshold_dB35Retain local maxima no more than this many dB below the strongest spectral peak in that channel/frame. Clamped to 6–80 dB.
Min_peak_separation_Hz12Minimum spacing between retained spectral peaks; minimum enforced value 1 Hz.
Harmonic_tolerance_cents45Tolerance around integer harmonic ratios 2–8. Clamped to 5–150 cents.
Downweight_harmonic_duplicatesYesEnable/disable soft harmonic evidence reduction.
Harmonic_residual_weight_percent12Percentage of evidence retained for a likely harmonic. Clamped to 0–100%.
Max_peaks_to_keep36Maximum retained peaks per channel/frame after thresholding and separation. Enforced range 4–128.
Pitch_class_floor_percent22Pitch-class salience needed to remain active after frame normalization. Enforced range 5–90%.
Min_confidence_percent7Minimum best-vs-runner-up score margin. Clamped to 0–100%.
Min_chord_duration_ms200Converted to a minimum consecutive-frame count with ceiling().
Silence_floor_dB-45Frame RMS threshold relative to whole-file RMS.
Show_all_detectionsNoPrint every frame label, active pitch classes, and confidence to the Info window.
Draw_visualizationYesDraw the four-panel analysis visualization.
Open_TextGrid_editorNoOpen the source Sound and resulting TextGrid in View & Edit after analysis.
Preset behavior: Presets replace the time/spectral evidence and decision parameters listed below. Tuning_A4_Hz, Downweight_harmonic_duplicates, Silence_floor_dB, and the output switches remain user-controlled. Custom keeps the form values.

Preset values

PresetWindow / stepBandPeak thresholdPeaksPC floorConfidenceConfirm
Quick Scan160 / 100 ms80–3000 Hz30 dB2428%9%300 ms
Standard Analysis120 / 50 ms60–4000 Hz35 dB3622%7%200 ms
Fine Detail160 / 25 ms45–5500 Hz40 dB4820%5%100 ms
Polyphonic Dense180 / 60 ms45–6000 Hz42 dB6418%5%180 ms
Monophonic Melody80 / 25 ms70–3000 Hz30 dB2030%0%50 ms

Other preset-specific values

PresetSkipPeak sep.Harmonic toleranceHarmonic residual weight
Quick Scan20 ms20 Hz55 cents15%
Standard Analysis10 ms12 Hz45 cents12%
Fine Detail5 ms8 Hz40 cents10%
Polyphonic Dense10 ms6 Hz45 cents15%
Monophonic Melody5 ms25 Hz60 cents8%

Chord templates

The current chord model contains 15 templates. These are scored templates, not exact-set dictionary lookups.

Triads Major 0,4,7 Minor 0,3,7 Diminished 0,3,6 Augmented 0,4,8 Sus2 0,2,7 Sus4 0,5,7 Seventh chords Dom7 0,4,7,10 Maj7 0,4,7,11 Min7 0,3,7,10 m7b5 0,3,6,10 Dim7 0,3,6,9 Other four-note templates 6 0,4,7,9 m6 0,3,7,9 add9 0,2,4,7 m(add9) 0,2,3,7
Inversions: Because scoring is based primarily on pitch-class membership, ordinary inversions normally map to the same chord template. Bass evidence contributes only a small tie-breaker. The script does not output slash-chord notation.
Not implemented as dedicated templates: full 9th chords, major/minor 9ths, minor-major 7th, augmented 7th, 7sus2/7sus4, add11, altered dominants, polychords, quartal sets, and arbitrary clusters. A dyad is reported as measured pitch classes rather than forced into a chord template.

Output & visualization

TextGrid

The result is named <source>_chords and contains two interval tiers.

TierContents
chordsTemporally confirmed frame interpretations. The prefix before the first confirmed interpretation may remain blank. If no label ever reaches confirmation, the longest unconfirmed run is retained as a fallback.
pitch_classesActive pitch classes for each analysis-frame start interval, such as C E G. These are pitch classes, not octave-specific note transcriptions. The interval before Skip_transient is intentionally blank.

Info window

The script reports input/preset metadata, channel-pooling behavior, pitch range, thresholds, total frames, confirmed-segment count, and the strongest decision frame. With Show all detections enabled, it additionally prints each frame's estimated label, active pitch classes, and confidence.

Visualization

A — Analysis Channel: waveform of the strongest-RMS channel used only as the representative display channel, with confirmed-segment boundaries.

B — Confirmed Chord Timeline: colored blocks for the final chord-tier segments.

C — Best-Frame Pitch-Class Evidence: normalized 12-class salience for the highest-confidence representative frame; members of the winning template are highlighted when a chord template won. The pitch-class floor is shown as a reference line.

D — Decision Confidence: frame-by-frame best-versus-runner-up score margin with the configured confidence threshold.

The footer reports frame count, segment count, A4 reference, peak limit, pitch-class floor, and confirmation duration. Sharps are rendered with the musical symbol in the visualization while TextGrid labels retain the plain-text # convention.

Interpretation & limits

What the labels mean

A chord label means that, for the current spectral evidence and parameters, one implemented pitch-class template achieved a sufficient margin over the strongest competing template and then persisted long enough to pass temporal confirmation. It should not be interpreted as ground-truth harmonic analysis.

Ambiguous: ... means that active pitch classes were measured but the winning template did not exceed the configured confidence margin. No clear pitch means that no pitch class survived the salience floor. Silence means that the frame fell below the configured RMS threshold relative to the whole file.

Known model limits

Appropriate use: The tool is useful for exploratory annotation, teaching, checking sustained harmonic passages, and comparing parameter-dependent chord interpretations inside Praat. For formal transcription or corpus research, inspect the TextGrid against the source and document the selected preset and parameter values.

Edge conditions enforced by the script