| 1 | # Clover Pitch |
| 2 | |
| 3 | A native macOS real-time pitch visualiser — a from-scratch clone of |
| 4 | [pitchreader.com](https://pitchreader.com). It listens to a microphone, detects |
| 5 | the fundamental frequency you sing or play, and scrolls it across a piano-roll |
| 6 | timeline against note gridlines. |
| 7 | |
| 8 | Live only — there is no recording mode. |
| 9 | |
| 10 | ## Features |
| 11 | |
| 12 | - **Live pitch-over-time graph** — a scrolling piano-roll where the last few |
| 13 | seconds of your pitch are drawn as a glowing trace, weighted by detection |
| 14 | confidence. Sharps/flats rows are shaded like piano keys; octave C lines are |
| 15 | emphasised and labelled. |
| 16 | - **Active note readout** — a large current-note display with octave, exact Hz, |
| 17 | and a ±50-cent tuner needle ("in tune" when within 5¢). |
| 18 | - **Top bar** |
| 19 | - **Microphone** — pick any CoreAudio input device; switching is live. |
| 20 | - **Transpose** — ±12 semitones; shifts every displayed label (like a capo). |
| 21 | - **Note labels** — Sharps, Flats, Solfège (fixed Do), or German (H/B). |
| 22 | - **Range** — vocal/instrument presets (Bass … Soprano, Wide, Full) that set |
| 23 | the visible vertical span. |
| 24 | |
| 25 | ## How it works |
| 26 | |
| 27 | - `AudioEngine` captures mono audio via `AVAudioEngine`, down-mixes, and runs a |
| 28 | YIN pitch detector (`PitchDetector.swift`) at a 512-sample hop over a 2048 |
| 29 | window. Detected samples land in a lock-guarded ring buffer. |
| 30 | - `PitchGraphView` is a SwiftUI `Canvas` driven by `TimelineView(.animation)`; |
| 31 | each frame it snapshots the ring buffer and repaints the trace against the |
| 32 | note grid. The clock is `CACurrentMediaTime()` so the trace stays in sync |
| 33 | with the audio timeline. |
| 34 | - Input-device enumeration/selection goes through the CoreAudio HAL |
| 35 | (`CoreAudioDevices.swift`), and the selected device is set on the input |
| 36 | AudioUnit via `kAudioOutputUnitProperty_CurrentDevice`. |
| 37 | |
| 38 | The detector is accurate to a fraction of a cent on synthetic tones across the |
| 39 | full E2–A5 range (see the note-name test compiled from `PitchDetector.swift`). |
| 40 | |
| 41 | ## Build & run |
| 42 | |
| 43 | ```sh |
| 44 | ./run.sh # builds a signed Pitch.app and launches it |
| 45 | ./build.sh # just build Pitch.app |
| 46 | ``` |
| 47 | |
| 48 | The app must be a signed `.app` bundle (not a bare binary) so the microphone |
| 49 | TCC prompt appears — `NSMicrophoneUsageDescription` lives in the generated |
| 50 | `Info.plist`. `build.sh` signs with the first available codesigning identity, |
| 51 | or run `./build.sh --create-cert` once to mint a reusable self-signed |
| 52 | `Pitch Dev` identity (same pattern as the Sequencer). |