---
title: "pianito"
summary: "Piano tuner for the terminal - guided 88-key tuning with real-time pitch detection"
external_url: "https://github.com/4esv/pianito"
links:
  - "https://github.com/4esv/pianito"
  - "https://github.com/4esv/pianito/blob/main/docs/inharmonicity.md"
  - "https://github.com/4esv/pianito/blob/main/.github/workflows/ci.yml"
---

- Stars: 3
- Language: Rust
- Updated: 2026-10-09
- License: MIT
- Topics: audio, cli, music, piano-tuner, piano-tuning, pitch-detection, ratatui, rust, tui, tuner

[GitHub](https://github.com/4esv/pianito)

**A piano tuner for the terminal.** macOS and Linux, with guided coaching.

pianito is a command-line piano tuner. It does real-time pitch detection (the
YIN algorithm), measures each piano's inharmonicity from its own overtones, and
tunes to an equal-tempered scale with a Railsback stretch curve. Then it walks
you through the strings note by note. No GUI, no phone, no account. A
microphone, a terminal, and a piano.

If you searched "CLI piano tuner" or "open source piano tuning app" and landed
here, there isn't another terminal piano tuner as far as I can tell, so this is
the one.

## What it stacks up against

The professional apps are Entropy Piano Tuner (open source), Verituner, TuneLab
and PianoMeter. They set the vocabulary piano technicians scan for:
inharmonicity measurement, per-piano stretch, temperaments, pitch raise, a live
partial display. Here is where pianito stands against that bar today.

| Capability | pianito | Notes |
|---|:---:|---|
| Real-time pitch detection | ✅ | YIN fundamental + FFT partial analysis, with an octave sanity check |
| Inharmonicity measurement | ✅ | fits the stiffness coefficient *B* per note from recorded partials during Profile mode |
| Per-piano stretch tuning | ✅ | `--stretch profile` drives the target frequencies from the measured curve; the default is a generic Railsback curve |
| Equal temperament | ✅ | A4 configurable (concert pitch or the piano's own center) |
| Guided multi-string coaching | ✅ | the mute-and-match unison workflow – pianito's own thing; the pro apps measure, they don't coach |
| Scriptable CLI | ✅ | analyze a WAV, emit a reference tone, dump session history – composable with the rest of your shell |
| Historical / well temperaments | ❌ *not yet* | Verituner and TuneLab ship dozens; pianito is equal-temperament only |
| Pitch raise / overpull | ❌ *not yet* | TuneLab and Verituner compute overpull targets; pianito does not |
| Live spectrum / phase scope | ❌ | partials are measured during profiling, not shown as a running analyzer |
| Mobile / desktop GUI | ❌ *by design* | it runs in a terminal |

pianito already does the two things that separate a real tuner from a
chromatic-tuner app: it measures a specific piano's inharmonicity and
stretches the scale to fit it, and it adds step-by-step unison coaching the
others don't. It does not yet do historical temperaments or pitch-raise
overpull, and it will never be a phone app.

## Installation

### Homebrew (macOS)

```bash
brew install 4esv/tap/pianito
```

### Shell installer (macOS / Linux)

```bash
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/4esv/pianito/releases/latest/download/pianito-installer.sh | sh
```

### cargo

```bash
cargo install pianito
```

### cargo-binstall (prebuilt binary, no compile)

```bash
cargo binstall pianito
```

### From source

Requires Rust 1.82+. On Linux, first install the ALSA
development headers and `pkg-config`. cpal's ALSA backend links against
`libasound` at build time:

```bash
# Debian/Ubuntu
sudo apt install libasound2-dev pkg-config
```

macOS needs nothing extra. It links CoreAudio directly.

```bash
git clone https://github.com/4esv/pianito.git
cd pianito
cargo build --release
```

The binary will be at `target/release/pianito`.

On a minimal Linux install, the runtime also needs `libasound2` (present on
every desktop distro; `sudo apt install libasound2` if it isn't).

## Usage

### Interactive tuning

```bash
# Start interactive tuning (opens the mode-select menu)
pianito

# Resume an interrupted session
pianito --resume

# Tune to A4 = 442 Hz with a lock beep
pianito --a4 442 --beep

# Open the menu with Quick Tune preselected
pianito --quick
```

Flags:

- `--a4 <HZ>` sets the reference frequency for Concert Pitch and Profile
  sessions. It is also the fallback when you skip Quick Tune calibration, and
  it applies to the `reference` and `analyze` subcommands. On `--resume`, the
  session's original A4 wins.
- `--quick` preselects Quick Tune in the mode-select menu.
- `--beep` plays a short beep the moment a string first enters the in-tune
  zone (once per strike; silence re-arms it). It needs an audio output
  device. Without one, tuning continues and the status line shows a warning.
- `--stretch <off|railsback|profile>` picks how tuning targets are stretched:
  pure equal temperament, the built-in Railsback curve (the default), or the
  per-piano curve measured in Profile mode. `profile` falls back to Railsback
  until a profile is loaded.

### Keyboard controls

| Key | Action |
|-----|--------|
| `↑/↓` / `Tab` | Navigate menu options |
| `Enter` | Select / Confirm |
| `Space` | Confirm current step / note |
| `B` | Back to previous step / note |
| `P` | Toggle piano progress display |
| `?` | Toggle the help overlay |
| `S` | Skip current note (on the Quick Tune calibration screen: skip calibration, use the configured A4 reference) |
| `Q` / `Esc` | Quit (progress is saved after each confirmed note) |

Reference tones are played with the `pianito reference` subcommand, not from
the tuning screen.

### Commands

```bash
# Analyze a WAV file for pitch content
pianito analyze recording.wav

# Play a reference tone
pianito reference A4
pianito reference C5 --duration 3.0

# Show tuning session history
pianito history

# Clear all saved sessions
pianito reset
```

## Configuration

Configuration is read from the pianito config directory:

- macOS: `~/Library/Application Support/pianito/config.toml`
- Linux: `~/.config/pianito/config.toml` (or `$XDG_CONFIG_HOME/pianito/config.toml`)

```toml
# Default A4 reference frequency (CLI --a4 overrides)
a4 = 440.0

# Tolerance in cents for the "in tune" zone on the meter,
# direction hints, and the lock beep
tolerance = 5.0

# Beep once when a note locks into the in-tune zone
# (CLI --beep also enables this)
beep = false

# Mode preselected in the menu: "concert" or "quick"
# (CLI --quick preselects quick)
default_mode = "concert"

# How stretch is applied to tuning targets: "off", "railsback", or "profile"
# (CLI --stretch overrides)
stretch = "railsback"
```

## How it works

1. **Pitch Detection**: Uses the YIN algorithm to detect the fundamental frequency from microphone input
2. **Temperament**: Calculates equal temperament frequencies with optional Railsback stretch curve
3. **Tuning Order**: Follows traditional piano tuning order for stability:
   - Temperament octave (F3-F4): 13 notes
   - Octaves upward (F#4-C8): 43 notes
   - Octaves downward (E3-A0): 32 notes
4. **Multi-string Coaching**: Coaches each note based on its string count:
   - A0-A#1 (1 string): tune directly
   - B1-G#3 (bichord, 2 strings): 2 steps. Mute the right string, tune the left, then unmute and match the right
   - A3-C8 (trichord, 3 strings): 4 steps. Mute the outer strings, tune the center, then left and right unisons

### Profile mode

Profile mode measures the whole piano before tuning it. Play all 88 keys
(A0→C8) one at a time, and pianito records each note's deviation in cents. The
profile is saved under the pianito data directory:

- macOS: `~/Library/Application Support/pianito/profiles`
- Linux: `~/.local/share/pianito/profiles` (or `$XDG_DATA_HOME/pianito/profiles`)

Tuning then starts in a new order. The temperament octave (F3-F4) stays
first, and the remaining notes follow worst-deviation-first.

On the profiling screen: `Space` confirms the current note, `B` goes back,
`S` skips a note, `Q`/`Esc` quits.

Profiling also records each note's partial spectrum, and pianito fits those
into a per-piano inharmonicity curve. Tune with `--stretch profile` (or
`stretch = "profile"` in config.toml) to have that curve drive the tuning
targets. [`docs/inharmonicity.md`](https://github.com/4esv/pianito/blob/main/docs/inharmonicity.md) explains why the
stretch is necessary and how it's measured.

## Requirements

- Working microphone with permissions granted
- Terminal with Unicode support

| | macOS | Linux |
|---|---|---|
| Audio backend | CoreAudio, via cpal – works out of the box | ALSA, via cpal |
| Build-time deps | none beyond Rust | `libasound2-dev`, `pkg-config` |
| Runtime deps | none beyond the OS | `libasound2` |

The codebase has no `cfg(target_os)` branches and no macOS-specific APIs.
Audio I/O goes through cpal and paths through the `directories` crate, so Linux
runs the same code rather than a separate port. CI builds and tests on both
macOS and Ubuntu (see [`.github/workflows/ci.yml`](https://github.com/4esv/pianito/blob/main/.github/workflows/ci.yml)).

ALSA loads its plugins via `dlopen` at runtime, so statically-linked musl
builds aren't supported. Use the glibc target (the prebuilt Linux binary
already does).

## License

MIT
