Tuner¶
Real-time instrument tuner — microphone pitch tracking, instrument string presets, and the SSE/WebSocket pitch stream. See Listening — Microphone In for a tutorial.
Note
The microphone input requires PortAudio (via sounddevice).
Real-time instrument tuner.
Listens to your microphone, tracks pitch with the same YIN algorithm
behind pytheory.audio.detect_pitch(), and tells you what note
you’re playing and how many cents sharp or flat you are.
Terminal:
$ pytheory tune
$ pytheory tune --instrument guitar # lock to the six strings
Browser / JavaScript:
$ pytheory tune --serve
# opens http://localhost:8123 — a strobe tuner page
The served page is a strobe tuner: a segmented disc that drifts clockwise when you’re sharp, counter-clockwise when you’re flat, and freezes when you’re in tune — the same display logic as a Peterson strobe, driven by the YIN pitch track.
The server speaks Server-Sent Events and WebSocket, so any web page or JS app can tap the pitch stream directly — no client library needed:
// SSE
const tuner = new EventSource("http://localhost:8123/stream");
tuner.onmessage = (e) => {
const { freq, note, octave, cents, in_tune } = JSON.parse(e.data);
};
// WebSocket
const ws = new WebSocket("ws://localhost:8123/ws");
ws.onmessage = (e) => { const reading = JSON.parse(e.data); };
CORS is wide open on the stream, so a page served from anywhere (your dev server, a file:// page, CodePen) can connect.
With an instrument preset, readings lock to the nearest string —
the target field says which one — so “tune the D string” never
gets misread as “you’re 80 cents flat of E”:
tuner = Tuner(instrument="guitar")
With chords=True (CLI: pytheory tune --chords) the tuner
also identifies what chord is sounding — strum and the page (and
the stream’s chord field) names it:
$ pytheory tune --serve --chords
- pytheory.tuner.INSTRUMENT_STRINGS = {'banjo': ['D3', 'G3', 'B3', 'D4', 'G4'], 'bass': ['E1', 'A1', 'D2', 'G2'], 'cello': ['C2', 'G2', 'D3', 'A3'], 'guitar': ['E2', 'A2', 'D3', 'G3', 'B3', 'E4'], 'mandolin': ['G3', 'D4', 'A4', 'E5'], 'ukulele': ['G4', 'C4', 'E4', 'A4'], 'viola': ['C3', 'G3', 'D4', 'A4'], 'violin': ['G3', 'D4', 'A4', 'E5']}¶
Open-string targets for common instruments (low to high).
- pytheory.tuner.string_targets(instrument, reference_pitch=440.0)[source]¶
The (name, frequency) tuning targets for an instrument preset.
>>> string_targets("guitar")[0] ('E2', 82.4068...)
- pytheory.tuner.analyze_frame(frame, sample_rate=44100, *, reference_pitch=440.0, fmin=50.0, fmax=1500.0, targets=None)[source]¶
Analyze one audio frame: what note is this, and how far off?
- Parameters:
frame – Mono float array (≥ ~2048 samples for reliable results).
sample_rate – Sample rate in Hz.
reference_pitch – Concert pitch for A4 (default 440; pass 442 for orchestras that tune high, 432 for the adventurous).
fmin/fmax – Pitch search range.
targets – Optional list of (name, freq) tuning targets (e.g. from
string_targets()). The reading then reports cents relative to the nearest target instead of the nearest chromatic note, with the matched name intarget.
- Returns:
Dict with
freq,note,octave,cents(signed, + is sharp), andin_tune(within ±5 cents) — orNoneif the frame has no confident pitch. With targets, alsotargetandtarget_freq.
- class pytheory.tuner.Tuner(*, reference_pitch=440.0, fmin=50.0, fmax=1500.0, device=None, sample_rate=44100, instrument=None, chords=False)[source]¶
Bases:
objectMicrophone-driven tuner — keeps the latest pitch reading.
Example:
tuner = Tuner() tuner.start() while True: reading = tuner.reading # dict or None, updated live