> built 2026-10-13 19:58 UTC from 0fb1c0b (main) · denote 0.0.5. Details: build_info.json

# index.html.md

<!-- generated by epythet -->

# denote

Portal and facade for audio-to-symbol tools (MIDI, chords, pitch from audio).

Denote wraps multiple audio analysis backends behind a unified Python interface.
Each backend is an optional dependency — install only what you need.

## Install

```bash
pip install denote                          # core only (librosa)
pip install denote[basic_pitch]             # + MIDI transcription
pip install denote[torchcrepe]              # + GPU pitch estimation
pip install denote[all]                     # everything
```

## Quick Start

```python
import denote

# Audio to MIDI (requires basic-pitch)
result = denote.transcribe("song.wav")
result.midi  # pretty_midi.PrettyMIDI object
result.notes  # list of NoteEvent(start_time, end_time, pitch, velocity)

# Pitch estimation (requires torchcrepe, or falls back to librosa pYIN)
pitch = denote.get_pitch("vocal.wav")
pitch.times  # timestamps in seconds
pitch.frequencies  # Hz (NaN for unvoiced)
pitch.confidence  # voicing confidence

# Beat tracking (uses librosa, no extra install needed)
beats = denote.get_beats("song.wav")
beats.beats  # beat times in seconds
beats.tempo  # BPM estimate
```

## Choose Your Backend

```python
# See what's available
denote.list_backends()  # all registered backends
denote.list_backends("pitch")  # ['librosa_pyin', 'torchcrepe']

# Specify a backend explicitly
pitch = denote.get_pitch("vocal.wav", backend="librosa_pyin")
```

## Service-Level Access

For more control, access backends via the service layer:

```python
# Per-backend service handle
denote.services.basic_pitch.transcribe("song.wav", onset_threshold=0.3)
denote.services.torchcrepe.get_pitch("vocal.wav", model="tiny", device="cuda")

# Native adapter (full backend API)
denote.services.basic_pitch.adapter
```

## Supported Backends

| Backend               | Task               | License    | Install                           |
|-----------------------|--------------------|------------|-----------------------------------|
| Basic Pitch (Spotify) | MIDI transcription | Apache 2.0 | `pip install denote[basic_pitch]` |
| torchcrepe            | Pitch estimation   | MIT        | `pip install denote[torchcrepe]`  |
| librosa pYIN          | Pitch estimation   | ISC        | (included)                        |
| librosa beats         | Beat tracking      | ISC        | (included)                        |

More backends coming: pesto-pitch, madmom, autochord, beat-this, demucs.

## Plugin System

Register your own backends:

```python
import denote

denote.register_backend(
    "my_tool",
    config={
        "name": "my_tool",
        "tasks": ["transcribe"],
        "pip_install": "my-tool",
    },
    adapter=MyAdapter(config),
)
```

<p class="epythet-aggregates">This documentation as a single file: <a href="denote.md">denote.md</a> (Markdown, for agents).</p>


# _autosummary/denote.align.html.md

# denote.align

Align a symbolic score to a recording of the same music (audio-to-score sync).

[`align_score()`](_autosummary/denote.align.html.md#denote.align.align_score) answers “which moment of the score is playing at time *t*
of this recording?” (and the reverse), for a score and a performance that
differ in tempo, rubato, intro length, even key. It is the bridge you need to
cut a score and a recording at the same musical point, to compare several
scores of a piece against one recording, or to put score positions on a
recording’s timeline.

Method: chroma features on both sides (the score’s piano roll folded to pitch
classes, no synthesis needed; the audio’s constant-Q chroma), cosine distance,
and dynamic time warping (`librosa.sequence.dtw`). By default the recording
is matched as a *subsequence* of the score, so a clip of the first minute finds
its place inside a full-length score. `transpose="auto"` also tries all
twelve transpositions of the score and keeps the best, because arrangements
are often in another key.

The result’s `cost` (mean cosine distance along the path, 0 = identical
pitch content, 1 = unrelated) is comparable across scores aligned to the same
recording, which makes it a ranking of how faithful each score is.

### Module Attributes

| [`DFLT_FRAME_RATE`](_autosummary/denote.align.html.md#denote.align.DFLT_FRAME_RATE)   | Default analysis rate (frames per second) for both score and audio.     |
|--------------------------------------------------------------------|-------------------------------------------------------------------------|
| [`DFLT_AUDIO_SR`](_autosummary/denote.align.html.md#denote.align.DFLT_AUDIO_SR)     | Default sample rate the audio is resampled to before chroma extraction. |

### Functions

| [`align_audio`](_autosummary/denote.align.html.md#denote.align.align_audio)(audio, reference, \*[, sr, ...])   | Align two recordings of the same music (a cover, a render, a remix).   |
|-------------------------------------------------------------------------------------------------|------------------------------------------------------------------------|
| [`align_score`](_autosummary/denote.align.html.md#denote.align.align_score)(score, audio, \*[, sr, ...])       | Align a score to a recording of (part of) the same music.              |
| [`audio_chroma`](_autosummary/denote.align.html.md#denote.align.audio_chroma)(audio, \*[, sr, frame_rate, ...]) | L2-normalized 12 x frames constant-Q chroma of a recording.            |
| [`score_chroma`](_autosummary/denote.align.html.md#denote.align.score_chroma)(score, \*[, frame_rate])          | L2-normalized 12 x frames chroma of a score (drums excluded).          |

### Classes

| [`ScoreAlignment`](_autosummary/denote.align.html.md#denote.align.ScoreAlignment)(score_times, audio_times, cost)   | A monotone time map between a score's timeline and a recording's.   |
|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------|

### denote.align.DFLT_AUDIO_SR *= 22050*

Default sample rate the audio is resampled to before chroma extraction.

### denote.align.DFLT_FRAME_RATE *= 10.0*

Default analysis rate (frames per second) for both score and audio.

### *class* denote.align.ScoreAlignment(score_times, audio_times, cost, transpose=0, raw=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A monotone time map between a score’s timeline and a recording’s.

#### score_times

Score times in seconds, increasing (one per path step).

#### audio_times

The matching recording times in seconds.

#### cost

Mean cosine distance along the path (lower is a closer match).

#### transpose

Semitones the score was shifted to match the recording.

#### raw

Backend detail (the warping path in frames, the frame rate).

#### audio_to_score(t)

Score time(s) sounding at recording time(s) `t`.

#### *property* score_span *: [tuple](https://docs.python.org/3/builtins/stdtypes.html#tuple)*

`(first, last)` score times covered by the recording.

#### score_to_audio(t)

Recording time(s) at which score time(s) `t` sound.

### denote.align.align_audio(audio, reference, , sr=None, reference_sr=None, subsequence=False, transpose=0, frame_rate=10.0, audio_sr=22050)

Align two recordings of the same music (a cover, a render, a remix).

The same chroma DTW as [`align_score()`](_autosummary/denote.align.html.md#denote.align.align_score), with a recording in the
score’s place: `score_times` are times in `reference`. The `cost` is
a measure of how closely `audio` follows `reference`’s harmony and
melody, tolerant of tempo differences and blind to timbre, which makes it
a fidelity score for generated versions of a piece.

* **Parameters:**
  * **audio** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), `ndarray`]) – The recording to place on the reference’s timeline.
  * **reference** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), `ndarray`]) – The reference recording.
  * **sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Sample rate when `audio` is an array.
  * **reference_sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Sample rate when `reference` is an array.
  * **subsequence** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Match `audio` anywhere inside `reference` (default
    `False`: both start and end together).
  * **transpose** (`Union`[[`int`](https://docs.python.org/3/builtins/functions.html#int), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Semitones, or `"auto"` to try all twelve.
  * **frame_rate** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Analysis frames per second.
  * **audio_sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Rate both are resampled to before analysis.
* **Return type:**
  [`ScoreAlignment`](_autosummary/denote.align.html.md#denote.align.ScoreAlignment)
* **Returns:**
  A [`ScoreAlignment`](_autosummary/denote.align.html.md#denote.align.ScoreAlignment) (`score_*` refers to `reference`).

### denote.align.align_score(score, audio, , sr=None, subsequence=True, transpose=0, frame_rate=10.0, audio_sr=22050)

Align a score to a recording of (part of) the same music.

* **Parameters:**
  * **score** – A `pretty_midi.PrettyMIDI` or a `.mid` path (other score
    formats when `audiate` is installed).
  * **audio** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), `ndarray`]) – A recording: a path, or an array with `sr`.
  * **sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Sample rate when `audio` is an array.
  * **subsequence** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Match the recording anywhere inside the score (default),
    e.g. a one-minute clip against a full score. `False` forces the
    two to start and end together.
  * **transpose** (`Union`[[`int`](https://docs.python.org/3/builtins/functions.html#int), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Semitones to shift the score’s pitch classes, or `"auto"`
    to try all twelve and keep the lowest-cost one.
  * **frame_rate** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Analysis frames per second for both sides.
  * **audio_sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Rate the audio is resampled to before analysis.
* **Return type:**
  [`ScoreAlignment`](_autosummary/denote.align.html.md#denote.align.ScoreAlignment)
* **Returns:**
  A [`ScoreAlignment`](_autosummary/denote.align.html.md#denote.align.ScoreAlignment).

Example:

```default
a = denote.align_score("theme.mid", "recording_first_minute.wav")
a.audio_to_score(60.0)      # where the score is when the clip ends
a.cost                      # compare scores of the same piece
```

### denote.align.audio_chroma(audio, , sr=None, frame_rate=10.0, audio_sr=22050)

L2-normalized 12 x frames constant-Q chroma of a recording.

* **Return type:**
  `ndarray`

### denote.align.score_chroma(score, , frame_rate=10.0)

L2-normalized 12 x frames chroma of a score (drums excluded).

* **Return type:**
  `ndarray`


# _autosummary/denote.backends.basic_pitch.adapter.html.md

# denote.backends.basic_pitch.adapter

Adapter for Basic Pitch (Spotify) — audio to MIDI transcription.

### Classes

| [`Adapter`](_autosummary/denote.backends.basic_pitch.adapter.html.md#denote.backends.basic_pitch.adapter.Adapter)(config)   | Basic Pitch adapter for polyphonic note transcription.   |
|--------------------------------------------------------------------|----------------------------------------------------------|

### *class* denote.backends.basic_pitch.adapter.Adapter(config)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Basic Pitch adapter for polyphonic note transcription.

#### transcribe(audio, , sr=None, \*\*kwargs)

Transcribe audio to MIDI using Basic Pitch.

* **Parameters:**
  * **audio** – File path or numpy array.
  * **sr** – Sample rate (required if audio is an array).
  * **\*\*kwargs** – Normalized parameters (onset_threshold, min_note_length, etc.)
* **Returns:**
  TranscriptionResult with .midi, .notes, and .raw fields.


# _autosummary/denote.backends.basic_pitch.compat.html.md

# denote.backends.basic_pitch.compat

Make basic-pitch importable and runnable against today’s dependencies.

basic-pitch pins old dependencies. On Python 3.12, pip resolves only 0.3.0 on
macOS, and no release at all on Linux (each pins a TensorFlow with no cp312
wheel), so it gets installed with `--no-deps`. Either way it runs against current
scipy, setuptools and stray TensorFlow leftovers. Three things then break (issue #2):

1. `import tensorflow` succeeds on an empty `tensorflow/` namespace folder (left
   by an uninstall), so basic-pitch sets `TF_PRESENT` and later fails on
   `import tensorflow.lite`.
2. resampy < 0.4.3, which basic-pitch pins, imports `pkg_resources`, removed
   from setuptools >= 81.
3. basic-pitch < 0.4 calls `scipy.signal.gaussian`, removed in scipy >= 1.13
   (now `scipy.signal.windows.gaussian`).

Each fix below is applied only when its failure condition holds, and each is scoped
as narrowly as the failure allows. Nothing is patched globally that a working
install would see: a real TensorFlow is left alone, `pkg_resources` is stubbed
only while resampy is imported, and the scipy alias is visible to basic-pitch’s
`note_creation` module only.

Use [`import_basic_pitch()`](_autosummary/denote.backends.basic_pitch.compat.html.md#denote.backends.basic_pitch.compat.import_basic_pitch) rather than importing `basic_pitch` directly.

### Module Attributes

| [`MODEL_RUNTIMES`](_autosummary/denote.backends.basic_pitch.compat.html.md#denote.backends.basic_pitch.compat.MODEL_RUNTIMES)     | Model runtimes basic-pitch can use, by import name, and the pip name to suggest.      |
|---------------------------------------------------------------------|---------------------------------------------------------------------------------------|
| [`ONNX_MODEL_RELPATH`](_autosummary/denote.backends.basic_pitch.compat.html.md#denote.backends.basic_pitch.compat.ONNX_MODEL_RELPATH) | The ONNX serialization of the ICASSP 2022 model, relative to the basic_pitch package. |

### Functions

| [`hide_broken_tensorflow`](_autosummary/denote.backends.basic_pitch.compat.html.md#denote.backends.basic_pitch.compat.hide_broken_tensorflow)()              | Make `import tensorflow` fail if TensorFlow is only an empty folder.                 |
|----------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
| [`import_basic_pitch`](_autosummary/denote.backends.basic_pitch.compat.html.md#denote.backends.basic_pitch.compat.import_basic_pitch)()                  | Import basic-pitch with the fixes above; return `(predict, model_path)`.             |
| [`is_namespace_leftover`](_autosummary/denote.backends.basic_pitch.compat.html.md#denote.backends.basic_pitch.compat.is_namespace_leftover)(name)           | True if `name` is importable only as an empty namespace package.                     |
| [`model_path`](_autosummary/denote.backends.basic_pitch.compat.html.md#denote.backends.basic_pitch.compat.model_path)(basic_pitch)               | The model file to load: ONNX when onnxruntime is usable, else basic-pitch's default. |
| [`preload_resampy`](_autosummary/denote.backends.basic_pitch.compat.html.md#denote.backends.basic_pitch.compat.preload_resampy)()                     | Import resampy even when `pkg_resources` is gone (setuptools >= 81).                 |
| [`restore_scipy_gaussian`](_autosummary/denote.backends.basic_pitch.compat.html.md#denote.backends.basic_pitch.compat.restore_scipy_gaussian)(note_creation) | Give `note_creation` a `scipy.signal.gaussian` if scipy no longer has one.           |

### denote.backends.basic_pitch.compat.MODEL_RUNTIMES *= {'coremltools': 'coremltools', 'onnxruntime': 'onnxruntime', 'tensorflow': 'tensorflow', 'tflite_runtime': 'tflite-runtime'}*

Model runtimes basic-pitch can use, by import name, and the pip name to suggest.

### denote.backends.basic_pitch.compat.ONNX_MODEL_RELPATH *= PosixPath('saved_models/icassp_2022/nmp.onnx')*

The ONNX serialization of the ICASSP 2022 model, relative to the basic_pitch package.

### denote.backends.basic_pitch.compat.hide_broken_tensorflow()

Make `import tensorflow` fail if TensorFlow is only an empty folder.

basic-pitch decides at import time whether TensorFlow is present from a bare
`import tensorflow`. If the leftover was already imported (say, by an earlier
`import basic_pitch`), it is evicted and basic-pitch’s `TF_PRESENT` reset,
which still takes effect as long as `basic_pitch.inference` is not yet
imported. Returns True if it hid a leftover.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### denote.backends.basic_pitch.compat.import_basic_pitch()

Import basic-pitch with the fixes above; return `(predict, model_path)`.

### denote.backends.basic_pitch.compat.is_namespace_leftover(name)

True if `name` is importable only as an empty namespace package.

That is what an uninstall leaves behind when stray files kept the folder: the
import succeeds, but nothing in it works.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

```pycon
>>> is_namespace_leftover('json')
False
>>> is_namespace_leftover('surely_not_an_installed_module_xyz')
False
```

### denote.backends.basic_pitch.compat.model_path(basic_pitch)

The model file to load: ONNX when onnxruntime is usable, else basic-pitch’s default.

ONNX loads the same everywhere, unlike the TensorFlow SavedModel, whose loader
breaks across TensorFlow versions.

* **Return type:**
  [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)

### denote.backends.basic_pitch.compat.preload_resampy()

Import resampy even when `pkg_resources` is gone (setuptools >= 81).

resampy < 0.4.3 uses only `pkg_resources.resource_filename`. A stub providing
it is put in `sys.modules` for the duration of resampy’s import and removed
after, so no other library ever sees a partial `pkg_resources`. Returns True
if the stub was needed.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### denote.backends.basic_pitch.compat.restore_scipy_gaussian(note_creation)

Give `note_creation` a `scipy.signal.gaussian` if scipy no longer has one.

Only that module’s `scipy` name is replaced, by a proxy of the real scipy, so
scipy itself is not modified. Returns True if the alias was needed.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)


# _autosummary/denote.backends.basic_pitch.config.html.md

# denote.backends.basic_pitch.config

Configuration for the Basic Pitch (Spotify) backend.


# _autosummary/denote.backends.basic_pitch.html.md

# denote.backends.basic_pitch

### Modules

| [`adapter`](_autosummary/denote.backends.basic_pitch.adapter.html.md#module-denote.backends.basic_pitch.adapter)   | Adapter for Basic Pitch (Spotify) — audio to MIDI transcription.       |
|-------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------|
| [`compat`](_autosummary/denote.backends.basic_pitch.compat.html.md#module-denote.backends.basic_pitch.compat)     | Make basic-pitch importable and runnable against today's dependencies. |
| [`config`](_autosummary/denote.backends.basic_pitch.config.html.md#module-denote.backends.basic_pitch.config)     | Configuration for the Basic Pitch (Spotify) backend.                   |


# _autosummary/denote.backends.html.md

# denote.backends

Backend packages for denote. Each subpackage provides a config.py and adapter.py.

### Modules

| [`basic_pitch`](_autosummary/denote.backends.basic_pitch.html.md#module-denote.backends.basic_pitch)     |    |
|-----------------------------------------------------------------------------------------------------|----|
| [`librosa_beats`](_autosummary/denote.backends.librosa_beats.html.md#module-denote.backends.librosa_beats) |    |
| [`librosa_pyin`](_autosummary/denote.backends.librosa_pyin.html.md#module-denote.backends.librosa_pyin)   |    |
| [`torchcrepe`](_autosummary/denote.backends.torchcrepe.html.md#module-denote.backends.torchcrepe)       |    |


# _autosummary/denote.backends.librosa_beats.adapter.html.md

# denote.backends.librosa_beats.adapter

Adapter for librosa beat tracking.

### Classes

| [`Adapter`](_autosummary/denote.backends.librosa_beats.adapter.html.md#denote.backends.librosa_beats.adapter.Adapter)(config)   | librosa beat tracker adapter.   |
|--------------------------------------------------------------------|---------------------------------|

### *class* denote.backends.librosa_beats.adapter.Adapter(config)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

librosa beat tracker adapter.

#### get_beats(audio, , sr=None, \*\*kwargs)

Track beats in audio using librosa.

* **Parameters:**
  * **audio** – File path or numpy array.
  * **sr** – Sample rate (required if audio is an array).
  * **\*\*kwargs** – Normalized parameters (hop_length, start_bpm, tightness).
* **Returns:**
  BeatResult with .beats, .tempo fields.


# _autosummary/denote.backends.librosa_beats.config.html.md

# denote.backends.librosa_beats.config

Configuration for the librosa beat tracking backend.


# _autosummary/denote.backends.librosa_beats.html.md

# denote.backends.librosa_beats

### Modules

| [`adapter`](_autosummary/denote.backends.librosa_beats.adapter.html.md#module-denote.backends.librosa_beats.adapter)   | Adapter for librosa beat tracking.                   |
|---------------------------------------------------------------------------------------------------------|------------------------------------------------------|
| [`config`](_autosummary/denote.backends.librosa_beats.config.html.md#module-denote.backends.librosa_beats.config)     | Configuration for the librosa beat tracking backend. |


# _autosummary/denote.backends.librosa_pyin.adapter.html.md

# denote.backends.librosa_pyin.adapter

Adapter for librosa pYIN — probabilistic pitch estimation.

### Classes

| [`Adapter`](_autosummary/denote.backends.librosa_pyin.adapter.html.md#denote.backends.librosa_pyin.adapter.Adapter)(config)   | librosa pYIN adapter for monophonic pitch estimation.   |
|--------------------------------------------------------------------|---------------------------------------------------------|

### *class* denote.backends.librosa_pyin.adapter.Adapter(config)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

librosa pYIN adapter for monophonic pitch estimation.

#### get_pitch(audio, , sr=None, \*\*kwargs)

Estimate pitch from audio using pYIN.

* **Parameters:**
  * **audio** – File path or numpy array.
  * **sr** – Sample rate (required if audio is an array).
  * **\*\*kwargs** – Normalized parameters (min_frequency, max_frequency,
    hop_length, frame_length).
* **Returns:**
  PitchResult with .times, .frequencies, .confidence fields.


# _autosummary/denote.backends.librosa_pyin.config.html.md

# denote.backends.librosa_pyin.config

Configuration for the librosa pYIN backend.


# _autosummary/denote.backends.librosa_pyin.html.md

# denote.backends.librosa_pyin

### Modules

| [`adapter`](_autosummary/denote.backends.librosa_pyin.adapter.html.md#module-denote.backends.librosa_pyin.adapter)   | Adapter for librosa pYIN — probabilistic pitch estimation.   |
|--------------------------------------------------------------------------------------------------------|--------------------------------------------------------------|
| [`config`](_autosummary/denote.backends.librosa_pyin.config.html.md#module-denote.backends.librosa_pyin.config)     | Configuration for the librosa pYIN backend.                  |


# _autosummary/denote.backends.torchcrepe.adapter.html.md

# denote.backends.torchcrepe.adapter

Adapter for torchcrepe — GPU-accelerated pitch estimation.

### Classes

| [`Adapter`](_autosummary/denote.backends.torchcrepe.adapter.html.md#denote.backends.torchcrepe.adapter.Adapter)(config)   | torchcrepe adapter for pitch/F0 estimation.   |
|--------------------------------------------------------------------|-----------------------------------------------|

### *class* denote.backends.torchcrepe.adapter.Adapter(config)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

torchcrepe adapter for pitch/F0 estimation.

#### get_pitch(audio, , sr=None, \*\*kwargs)

Estimate pitch from audio using CREPE.

* **Parameters:**
  * **audio** – File path or numpy array.
  * **sr** – Sample rate (required if audio is an array).
  * **\*\*kwargs** – Normalized parameters (min_frequency, max_frequency,
    model, device, hop_length).
* **Returns:**
  PitchResult with .times, .frequencies, .confidence fields.


# _autosummary/denote.backends.torchcrepe.config.html.md

# denote.backends.torchcrepe.config

Configuration for the torchcrepe backend.


# _autosummary/denote.backends.torchcrepe.html.md

# denote.backends.torchcrepe

### Modules

| [`adapter`](_autosummary/denote.backends.torchcrepe.adapter.html.md#module-denote.backends.torchcrepe.adapter)   | Adapter for torchcrepe — GPU-accelerated pitch estimation.   |
|------------------------------------------------------------------------------------------------------|--------------------------------------------------------------|
| [`config`](_autosummary/denote.backends.torchcrepe.config.html.md#module-denote.backends.torchcrepe.config)     | Configuration for the torchcrepe backend.                    |


# _autosummary/denote.base.html.md

# denote.base

Core types and result dataclasses for denote.

### Classes

| [`BeatResult`](_autosummary/denote.base.html.md#denote.base.BeatResult)(beats[, downbeats, tempo, raw, ...])   | Result of beat/downbeat tracking.                                 |
|----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|
| [`ChordResult`](_autosummary/denote.base.html.md#denote.base.ChordResult)(intervals[, labels, raw, backend])    | Result of chord recognition.                                      |
| [`NoteEvent`](_autosummary/denote.base.html.md#denote.base.NoteEvent)(start_time, end_time, pitch, velocity)  | A single note event with timing, pitch, and optional pitch bends. |
| [`PitchResult`](_autosummary/denote.base.html.md#denote.base.PitchResult)(times, frequencies, confidence)       | Result of pitch/F0 estimation.                                    |
| [`TranscriptionResult`](_autosummary/denote.base.html.md#denote.base.TranscriptionResult)(midi[, notes, raw, backend])  | Result of audio-to-MIDI transcription.                            |

### *class* denote.base.BeatResult(beats, downbeats=<factory>, tempo=None, raw=None, backend='')

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Result of beat/downbeat tracking.

### *class* denote.base.ChordResult(intervals, labels=<factory>, raw=None, backend='')

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Result of chord recognition.

### *class* denote.base.NoteEvent(start_time, end_time, pitch, velocity, pitch_bends=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A single note event with timing, pitch, and optional pitch bends.

### *class* denote.base.PitchResult(times, frequencies, confidence, raw=None, backend='')

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Result of pitch/F0 estimation.

### *class* denote.base.TranscriptionResult(midi, notes=<factory>, raw=None, backend='')

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Result of audio-to-MIDI transcription.


# _autosummary/denote.html.md

# denote

Denote — Portal and facade for audio-to-symbol tools.

Simple usage:

```default
import denote
result = denote.transcribe("song.wav")         # audio -> MIDI
chords = denote.get_chords("song.wav")         # audio -> chord labels
pitch = denote.get_pitch("vocal.wav")           # audio -> F0
beats = denote.get_beats("song.wav")            # audio -> beat times
sync = denote.align_score("song.mid", "song.wav")  # score <-> recording time map

denote.list_backends()                          # see what's available
denote.list_backends('pitch')                   # backends for a task
```

Service-level access:

```default
denote.services.basic_pitch.transcribe("song.wav", onset_threshold=0.3)
denote.services.torchcrepe.get_pitch("vocal.wav", model='tiny')
```

Native adapter access:

```default
denote.services.basic_pitch.adapter  # raw adapter instance
```

### Functions

| [`get_beats`](_autosummary/denote.html.md#denote.get_beats)(audio, \*[, sr, backend])   | Track beats in audio.         |
|----------------------------------------------------------------------------------------|-------------------------------|
| [`get_chords`](_autosummary/denote.html.md#denote.get_chords)(audio, \*[, sr, backend])  | Recognize chords from audio.  |
| [`get_pitch`](_autosummary/denote.html.md#denote.get_pitch)(audio, \*[, sr, backend])   | Estimate pitch/F0 from audio. |
| [`transcribe`](_autosummary/denote.html.md#denote.transcribe)(audio, \*[, sr, backend])  | Transcribe audio to MIDI.     |

### denote.get_beats(audio, , sr=None, backend=None, \*\*kwargs)

Track beats in audio.

* **Parameters:**
  * **audio** – File path (str/Path) or numpy array.
  * **sr** – Sample rate (required when audio is an array).
  * **backend** – Backend name. Defaults to ‘librosa_beats’.
  * **\*\*kwargs** – Backend-specific parameters (hop_length, start_bpm).
* **Returns:**
  BeatResult with .beats, .downbeats, .tempo fields.

### denote.get_chords(audio, , sr=None, backend=None, \*\*kwargs)

Recognize chords from audio.

* **Parameters:**
  * **audio** – File path (str/Path) or numpy array.
  * **sr** – Sample rate (required when audio is an array).
  * **backend** – Backend name.
  * **\*\*kwargs** – Backend-specific parameters.
* **Returns:**
  ChordResult with .intervals, .labels fields.

### denote.get_pitch(audio, , sr=None, backend=None, \*\*kwargs)

Estimate pitch/F0 from audio.

* **Parameters:**
  * **audio** – File path (str/Path) or numpy array.
  * **sr** – Sample rate (required when audio is an array).
  * **backend** – Backend name. Defaults to ‘torchcrepe’ if installed,
    falls back to ‘librosa_pyin’.
  * **\*\*kwargs** – Backend-specific parameters (min_frequency, max_frequency,
    model, device, hop_length).
* **Returns:**
  PitchResult with .times, .frequencies, .confidence fields.

### denote.transcribe(audio, , sr=None, backend=None, \*\*kwargs)

Transcribe audio to MIDI.

* **Parameters:**
  * **audio** – File path (str/Path) or numpy array.
  * **sr** – Sample rate (required when audio is an array).
  * **backend** – Backend name. Defaults to ‘basic_pitch’ if installed.
  * **\*\*kwargs** – Backend-specific parameters (onset_threshold, min_note_length, etc.)
* **Returns:**
  TranscriptionResult with .midi, .notes, and .raw fields.

### Modules

| [`align`](_autosummary/denote.align.html.md#module-denote.align)             | Align a symbolic score to a recording of the same music (audio-to-score sync).   |
|----------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
| [`backends`](_autosummary/denote.backends.html.md#module-denote.backends)       | Backend packages for denote.                                                     |
| [`base`](_autosummary/denote.base.html.md#module-denote.base)               | Core types and result dataclasses for denote.                                    |
| [`registry`](_autosummary/denote.registry.html.md#module-denote.registry)       | Backend discovery, registration, and lazy loading.                               |
| [`services`](_autosummary/denote.services.html.md#denote.services)              | Lazy mapping of backend names → ServiceHandle instances.                         |
| [`translation`](_autosummary/denote.translation.html.md#module-denote.translation) | Parameter translation between normalized and native backend interfaces.          |
| [`util`](_autosummary/denote.util.html.md#module-denote.util)               | Audio loading and utility functions for denote.                                  |


# _autosummary/denote.registry.html.md

# denote.registry

Backend discovery, registration, and lazy loading.

### Functions

| [`clear_registry`](_autosummary/denote.registry.html.md#denote.registry.clear_registry)()                          | Clear all registered backends.                         |
|--------------------------------------------------------------------------------------------|--------------------------------------------------------|
| [`get_backend`](_autosummary/denote.registry.html.md#denote.registry.get_backend)(name)                         | Get a backend's config and lazily-loaded adapter.      |
| [`get_config`](_autosummary/denote.registry.html.md#denote.registry.get_config)(name)                          | Get a backend's config without loading its adapter.    |
| [`get_default_backend`](_autosummary/denote.registry.html.md#denote.registry.get_default_backend)(task)                 | Return the default backend name for a given task.      |
| [`list_backends`](_autosummary/denote.registry.html.md#denote.registry.list_backends)([task])                     | List registered backends, optionally filtered by task. |
| [`register_backend`](_autosummary/denote.registry.html.md#denote.registry.register_backend)(name, config[, adapter]) | Register a backend (for third-party plugins).          |

### denote.registry.clear_registry()

Clear all registered backends. Useful for testing.

### denote.registry.get_backend(name)

Get a backend’s config and lazily-loaded adapter.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  Dict with ‘config’ and ‘adapter’ keys.

### denote.registry.get_config(name)

Get a backend’s config without loading its adapter.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### denote.registry.get_default_backend(task)

Return the default backend name for a given task.

Looks for backends with ‘default_for’ containing the task.
Falls back to the first registered backend for that task.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### denote.registry.list_backends(task=None)

List registered backends, optionally filtered by task.

* **Parameters:**
  **task** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – If set, only return backends that support this task
  (‘transcribe’, ‘pitch’, ‘chords’, ‘beats’, ‘separate’).
* **Return type:**
  [`List`](https://docs.python.org/3/library/typing.html#typing.List)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]
* **Returns:**
  Sorted list of backend names.

### denote.registry.register_backend(name, config, adapter=None)

Register a backend (for third-party plugins).

* **Parameters:**
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Unique backend identifier.
  * **config** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – BACKEND_CONFIG dict with at minimum ‘name’ and ‘tasks’.
  * **adapter** ([`Any`](https://docs.python.org/3/library/typing.html#typing.Any)) – Optional pre-instantiated adapter. If None, will be loaded
    lazily from the module path if available.


# _autosummary/denote.services.html.md

# denote.services

### denote.services *= <ServiceCollection backends=['basic_pitch', 'librosa_beats', 'librosa_pyin', 'torchcrepe']>*

Lazy mapping of backend names → ServiceHandle instances.

Supports both dict-style and attribute-style access:

```default
services['basic_pitch']
services.basic_pitch
```


# _autosummary/denote.translation.html.md

# denote.translation

Parameter translation between normalized and native backend interfaces.

### Functions

| [`make_kwargs_translator`](_autosummary/denote.translation.html.md#denote.translation.make_kwargs_translator)(param_map, \*[, ...])   | Create a function that translates normalized kwargs to native kwargs.   |
|-------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
| [`validate_param`](_autosummary/denote.translation.html.md#denote.translation.validate_param)(name, value, config)            | Validate a single parameter against its config constraints.             |

### denote.translation.make_kwargs_translator(param_map, , on_unsupported='warn')

Create a function that translates normalized kwargs to native kwargs.

* **Parameters:**
  * **param_map** ([`Dict`](https://docs.python.org/3/library/typing.html#typing.Dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]]) – 

    Mapping of normalized_name -> native config dict.
    Each config dict can have:
    - ’native_name’: str — the backend’s parameter name
    - ’coerce’: callable — transform the value
    - ’default’: Any — default value if not provided
    - None — parameter is not supported by this backend
  * **on_unsupported** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – What to do with params not in param_map.
    ‘warn’ (default), ‘raise’, or ‘ignore’.
* **Return type:**
  [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)
* **Returns:**
  A function that translates kwargs dicts.

### denote.translation.validate_param(name, value, config)

Validate a single parameter against its config constraints.

Checks ‘min’, ‘max’, and ‘choices’ constraints.

* **Return type:**
  [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)


# _autosummary/denote.util.html.md

# denote.util

Audio loading and utility functions for denote.

### Functions

| [`ensure_file_path`](_autosummary/denote.util.html.md#denote.util.ensure_file_path)(audio, \*[, sr])            | Ensure audio is available as a file path.                  |
|-----------------------------------------------------------------------------------------------|------------------------------------------------------------|
| [`load_audio`](_autosummary/denote.util.html.md#denote.util.load_audio)(audio, \*[, sr, mono, target_sr]) | Load audio from a file path or validate an existing array. |

### denote.util.ensure_file_path(audio, , sr=None)

Ensure audio is available as a file path.

If audio is already a path, return it as a string.
If it’s an array, write to a temporary WAV file and return the path.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### denote.util.load_audio(audio, , sr=None, mono=True, target_sr=None)

Load audio from a file path or validate an existing array.

* **Parameters:**
  * **audio** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), `ndarray`]) – File path (str/Path) or numpy array of audio samples.
  * **sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Sample rate. Required when audio is an array. Ignored for file paths
    (detected automatically).
  * **mono** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If True, convert to mono.
  * **target_sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – If set, resample to this rate.
* **Return type:**
  [`Tuple`](https://docs.python.org/3/library/typing.html#typing.Tuple)[`ndarray`, [`int`](https://docs.python.org/3/builtins/functions.html#int)]
* **Returns:**
  Tuple of (audio_array, sample_rate).
* **Raises:**
  * [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If audio is an array and sr is not provided.
  * [**FileNotFoundError**](https://docs.python.org/3/builtins/exceptions.html#FileNotFoundError) – If audio is a path that doesn’t exist.


# about-this-build.html.md

<!-- generated by epythet -->

# About this build

This documentation was built on **2026-10-13 19:58 UTC** from commit <a href="https://github.com/thorwhalen/denote/commit/0fb1c0b98d16abbd1a7a569dc08000ea1242b95c"><code>0fb1c0b</code></a> on branch <code>main</code>, for **denote 0.0.5** (from <code>pyproject.toml</code>).

#### NOTE
Nothing suggests a mismatch: the tree was clean at the commit above, and the documented version is the one on PyPI.

## Source

|                     |                                                                                                                                                          |
|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| Commit              | <a href="https://github.com/thorwhalen/denote/commit/0fb1c0b98d16abbd1a7a569dc08000ea1242b95c"><code>0fb1c0b98d16abbd1a7a569dc08000ea1242b95c</code></a> |
| Branch              | <code>main</code>                                                                                                                                        |
| Tags at this commit | <code>0.0.5</code>                                                                                                                                       |
| Working tree        | clean                                                                                                                                                    |
| Remote              | <code>https://github.com/thorwhalen/denote</code>                                                                                                        |

## Continuous integration

|              |                                                                                            |
|--------------|--------------------------------------------------------------------------------------------|
| Repository   | <code>thorwhalen/denote</code>                                                             |
| Run          | <a href="https://github.com/thorwhalen/denote/actions/runs/38458642605">38458642605</a>    |
| Ref          | <code>refs/heads/main</code>                                                               |
| Event commit | <code>60dca056776c63e3b9bf5f16f6fb2d828fef9a97</code> (in the history of the built commit) |

## Tools

|          |         |
|----------|---------|
| epythet  | 0.2.17  |
| Sphinx   | 9.1.0   |
| docutils | 0.22.4  |
| Python   | 3.12.15 |

## Configuration as resolved

|               |                                                                   |
|---------------|-------------------------------------------------------------------|
| theme         | <code>auto</code> (Sphinx theme <code>sphinxawesome_theme</code>) |
| accent        | <code>#8c3f00</code>                                              |
| api_generator | <code>autosummary</code>                                          |
| ignore        | <code>tests/</code>, <code>scrap/</code>, <code>examples/</code>  |
| agent_outputs | <code>true</code>                                                 |
| aggregates    | <code>md</code>                                                   |
| ai_artifacts  | <code>true</code>                                                 |

## Package on PyPI

Latest release: <a href="https://pypi.org/project/denote/0.0.5/">0.0.5</a>, the same as the documented version.

## Reproduce

```bash
git clone https://github.com/thorwhalen/denote && cd denote
git checkout 0fb1c0b98d16abbd1a7a569dc08000ea1242b95c
pip install "epythet==0.2.17"
epythet quickstart . --ignore tests/ scrap/ examples/
```

The same data, for machines: <a href="build_info.json"><code>build_info.json</code></a> (schema version 1).


# ai-agents.html.md

<!-- generated by epythet -->

# For AI agents

`denote` ships artifacts for coding agents alongside its code. This page lists
them, says where each lives in the repository, and points at the
machine-readable copies of this documentation.

## Skills

Skills are folders holding a `SKILL.md` (the [Agent Skills](https://agentskills.io) format): a description that tells an agent when to use it and a body with the procedure. Install one into your agent with `gh skill` (any host: `--agent claude-code`, `copilot`, `cursor`, `codex`, `gemini`), or use the copy bundled in the wheel.

### `opsward`

Run opsward to assess and improve this project’s AI agent setup. Use when the user says ‘opsward’, ‘check my setup’, ‘improve my AI config’, or wants a full audit and remediation.

Source: [`.claude/skills/opsward`](https://github.com/thorwhalen/denote/tree/HEAD/.claude/skills/opsward).

### `opsward-diagnose`

Diagnose the health of this project’s AI agent setup. Use when the user asks to check, audit, or score the project’s CLAUDE.md, skills, docs, or agent configuration.

Source: [`.claude/skills/opsward-diagnose`](https://github.com/thorwhalen/denote/tree/HEAD/.claude/skills/opsward-diagnose).

### `opsward-generate`

Generate missing AI setup artifacts for this project. Use when the user asks to scaffold, create, or bootstrap CLAUDE.md, docs, skills, or agent configuration.

Source: [`.claude/skills/opsward-generate`](https://github.com/thorwhalen/denote/tree/HEAD/.claude/skills/opsward-generate).

### `opsward-maintain`

Check documentation and AI setup for staleness, drift, or inconsistency. Use when the user asks to maintain, refresh, or update project docs and AI configuration.

Source: [`.claude/skills/opsward-maintain`](https://github.com/thorwhalen/denote/tree/HEAD/.claude/skills/opsward-maintain).

## Subagents

Subagents are Markdown files with a frontmatter (`name`, `description`, `tools`) and a system prompt as the body. Copy one into your project’s `.claude/agents/` (or your agent host’s equivalent) to delegate the task it describes.

### `setup-auditor`

Read-only diagnostic agent that audits the project’s AI setup (CLAUDE.md, skills, docs, rules). Use when you want to check the health of the project’s agent configuration without making changes.

Source: [`.claude/agents/setup-auditor.md`](https://github.com/thorwhalen/denote/tree/HEAD/.claude/agents/setup-auditor.md).

## Instruction files

Files agents read before working in this repository.

- [`CLAUDE.md`](https://github.com/thorwhalen/denote/tree/HEAD/CLAUDE.md): read by Claude Code

## Machine-readable documentation

This site publishes the same documentation in forms that fit an agent’s context window:

- [`llms.txt`](https://thorwhalen.github.io/denote/llms.txt): an index of every page with a one-line description ([llms.txt](https://llmstxt.org) format)
- [`denote.md`](https://thorwhalen.github.io/denote/denote.md): the whole documentation as one Markdown file
- `<page>.html.md`: a rendered Markdown twin of every page, advertised from each page’s `<head>` with `<link rel="alternate" type="text/markdown">`
- [`objects.inv`](https://thorwhalen.github.io/denote/objects.inv): the Sphinx inventory: a symbol-to-URL index (`sphobjinv convert plain objects.inv -`)


# api.html.md

# API reference

| [`denote`](_autosummary/denote.html.md#module-denote)   | Denote — Portal and facade for audio-to-symbol tools.   |
|-------------------------------------------------------------------------|---------------------------------------------------------|


