crepe-predictor 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.5
2
+ Name: crepe-predictor
3
+ Version: 0.1.0
4
+ Summary: A dependency-light reimplementation of CREPE inference with ONNX
5
+ Keywords: machine learning,speech
6
+ Author: Maxime Poli
7
+ Requires-Python: >=3.13
8
+ Description-Content-Type: text/markdown
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Topic :: Scientific/Engineering
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Requires-Dist: numpy>=2.4.6
18
+ Requires-Dist: onnxruntime>=1.27.0
19
+ Requires-Dist: scipy>=1.18.0
20
+ Project-URL: repository, https://github.com/mxmpl/crepe-predictor
21
+ Import-Name: crepe_predictor
22
+
23
+ # crepe-predictor
24
+
25
+ A dependency-light reimplementation of [CREPE](https://github.com/marl/crepe) pitch estimation, exported to ONNX for inference without PyTorch.
26
+
27
+ - `crepe_predictor.py` — the runtime package: framing, Viterbi-decoded pitch prediction from an ONNX session, and Kaldi-compatible (NCCF, pitch) postprocessing, wrapped in a `CrepePredictor` class.
28
+ - `export_torchcrepe_to_onnx.py` — script to (re-)generate the ONNX checkpoints.
29
+
30
+ ## Installation
31
+
32
+ ```sh
33
+ pip install crepe-predictor
34
+ ```
35
+
36
+ Inference only depends on `numpy`, `onnxruntime`, and `scipy`.
37
+ Exporting checkpoints additionally requires `torch` and `onnxscript`, listed as script dependencies at the top of `export_torchcrepe_to_onnx.py`.
38
+
39
+ ## API
40
+
41
+ Everything is exposed through `crepe_predictor.CrepePredictor`.
42
+
43
+ ### `CrepePredictor(capacity="full", *, checkpoint=None, onnx_providers=None)`
44
+
45
+ Resolves a checkpoint and opens an ONNX Runtime session for it.
46
+
47
+ - `capacity`: `"tiny"`, `"small"`, `"medium"`, `"large"`, or `"full"` — model size, trading accuracy for speed.
48
+ - `checkpoint`: path to a local `.onnx` file. If omitted, the checkpoint matching `capacity` is downloaded and cached under `$CREPE_CACHE_DIR`, `$XDG_CACHE_HOME`, or `~/.cache/crepe_predictor/checkpoints`.
49
+ - `onnx_providers`: ONNX Runtime execution providers, e.g. `["CUDAExecutionProvider", "CPUExecutionProvider"]`. Defaults to `["CPUExecutionProvider"]`.
50
+
51
+ ### `predict(audio, *, viterbi=True, center=True, frame_shift=0.01, frame_length=0.025) -> np.ndarray`
52
+
53
+ Estimates pitch from 16 kHz mono `audio`, returning an `(n_frames, 2)` array of `(POV, pitch)`: probability of voicing in `[0, 1]`, and pitch in Hz.
54
+
55
+ - `viterbi`: decode pitch bins along a Viterbi path enforcing pitch continuity, instead of a per-frame argmax.
56
+ - `center`: pad `audio` so each frame is centered on its timestamp.
57
+ - `frame_shift`, `frame_length`: frame spacing and length in seconds, used to resample the output to the frame count they imply.
58
+
59
+ ### `predict_kaldi(audio, *, viterbi=True, center=True, frame_shift=0.01, frame_length=0.025) -> np.ndarray`
60
+
61
+ Same arguments as `predict`, but returns `(n_frames, 2)` of `(NCCF, pitch)`, compatible with Kaldi's `process-pitch`: unvoiced frames are detected with a voicing HMM, pitch is interpolated over them, and POV is converted to an NCCF value. Raises `ValueError` if no frame is voiced.
62
+
63
+ ## Usage
64
+
65
+ Estimate pitch from a synthetic tone:
66
+
67
+ ```python
68
+ import numpy as np
69
+ from crepe_predictor import CrepePredictor
70
+
71
+ predictor = CrepePredictor("full") # "tiny", "small", "medium", "large", or "full"
72
+
73
+ t = np.arange(16000) / 16000 # 1 second at 16 kHz
74
+ audio = np.sin(2 * np.pi * 220 * t).astype(np.float32) # a 220 Hz tone
75
+
76
+ pov, pitch = predictor.predict(audio).T
77
+ print(pitch[pov > 0.5]) # pitch in Hz for confidently voiced frames
78
+ ```
79
+
80
+ Process a recording and produce Kaldi-compatible pitch features, running on GPU when available:
81
+
82
+ ```python
83
+ from scipy.io import wavfile
84
+ from crepe_predictor import CrepePredictor
85
+
86
+ predictor = CrepePredictor(
87
+ "full",
88
+ onnx_providers=["CUDAExecutionProvider", "CPUExecutionProvider"],
89
+ )
90
+
91
+ sample_rate, audio = wavfile.read("speech.wav")
92
+ assert sample_rate == 16000
93
+ audio = audio.astype("float32") / 32768.0 # int16 PCM -> float32 in [-1, 1]
94
+
95
+ nccf, pitch = predictor.predict_kaldi(audio).T
96
+ ```
97
+
@@ -0,0 +1,74 @@
1
+ # crepe-predictor
2
+
3
+ A dependency-light reimplementation of [CREPE](https://github.com/marl/crepe) pitch estimation, exported to ONNX for inference without PyTorch.
4
+
5
+ - `crepe_predictor.py` — the runtime package: framing, Viterbi-decoded pitch prediction from an ONNX session, and Kaldi-compatible (NCCF, pitch) postprocessing, wrapped in a `CrepePredictor` class.
6
+ - `export_torchcrepe_to_onnx.py` — script to (re-)generate the ONNX checkpoints.
7
+
8
+ ## Installation
9
+
10
+ ```sh
11
+ pip install crepe-predictor
12
+ ```
13
+
14
+ Inference only depends on `numpy`, `onnxruntime`, and `scipy`.
15
+ Exporting checkpoints additionally requires `torch` and `onnxscript`, listed as script dependencies at the top of `export_torchcrepe_to_onnx.py`.
16
+
17
+ ## API
18
+
19
+ Everything is exposed through `crepe_predictor.CrepePredictor`.
20
+
21
+ ### `CrepePredictor(capacity="full", *, checkpoint=None, onnx_providers=None)`
22
+
23
+ Resolves a checkpoint and opens an ONNX Runtime session for it.
24
+
25
+ - `capacity`: `"tiny"`, `"small"`, `"medium"`, `"large"`, or `"full"` — model size, trading accuracy for speed.
26
+ - `checkpoint`: path to a local `.onnx` file. If omitted, the checkpoint matching `capacity` is downloaded and cached under `$CREPE_CACHE_DIR`, `$XDG_CACHE_HOME`, or `~/.cache/crepe_predictor/checkpoints`.
27
+ - `onnx_providers`: ONNX Runtime execution providers, e.g. `["CUDAExecutionProvider", "CPUExecutionProvider"]`. Defaults to `["CPUExecutionProvider"]`.
28
+
29
+ ### `predict(audio, *, viterbi=True, center=True, frame_shift=0.01, frame_length=0.025) -> np.ndarray`
30
+
31
+ Estimates pitch from 16 kHz mono `audio`, returning an `(n_frames, 2)` array of `(POV, pitch)`: probability of voicing in `[0, 1]`, and pitch in Hz.
32
+
33
+ - `viterbi`: decode pitch bins along a Viterbi path enforcing pitch continuity, instead of a per-frame argmax.
34
+ - `center`: pad `audio` so each frame is centered on its timestamp.
35
+ - `frame_shift`, `frame_length`: frame spacing and length in seconds, used to resample the output to the frame count they imply.
36
+
37
+ ### `predict_kaldi(audio, *, viterbi=True, center=True, frame_shift=0.01, frame_length=0.025) -> np.ndarray`
38
+
39
+ Same arguments as `predict`, but returns `(n_frames, 2)` of `(NCCF, pitch)`, compatible with Kaldi's `process-pitch`: unvoiced frames are detected with a voicing HMM, pitch is interpolated over them, and POV is converted to an NCCF value. Raises `ValueError` if no frame is voiced.
40
+
41
+ ## Usage
42
+
43
+ Estimate pitch from a synthetic tone:
44
+
45
+ ```python
46
+ import numpy as np
47
+ from crepe_predictor import CrepePredictor
48
+
49
+ predictor = CrepePredictor("full") # "tiny", "small", "medium", "large", or "full"
50
+
51
+ t = np.arange(16000) / 16000 # 1 second at 16 kHz
52
+ audio = np.sin(2 * np.pi * 220 * t).astype(np.float32) # a 220 Hz tone
53
+
54
+ pov, pitch = predictor.predict(audio).T
55
+ print(pitch[pov > 0.5]) # pitch in Hz for confidently voiced frames
56
+ ```
57
+
58
+ Process a recording and produce Kaldi-compatible pitch features, running on GPU when available:
59
+
60
+ ```python
61
+ from scipy.io import wavfile
62
+ from crepe_predictor import CrepePredictor
63
+
64
+ predictor = CrepePredictor(
65
+ "full",
66
+ onnx_providers=["CUDAExecutionProvider", "CPUExecutionProvider"],
67
+ )
68
+
69
+ sample_rate, audio = wavfile.read("speech.wav")
70
+ assert sample_rate == 16000
71
+ audio = audio.astype("float32") / 32768.0 # int16 PCM -> float32 in [-1, 1]
72
+
73
+ nccf, pitch = predictor.predict_kaldi(audio).T
74
+ ```
@@ -0,0 +1,252 @@
1
+ import hashlib
2
+ import os
3
+ import tempfile
4
+ import urllib.request
5
+ from pathlib import Path
6
+ from typing import Literal
7
+
8
+ import numpy as np
9
+ import onnxruntime as ort
10
+ import scipy.interpolate
11
+ import scipy.optimize
12
+ import scipy.signal
13
+
14
+ __all__ = ["Capacity", "CrepePredictor"]
15
+
16
+ Capacity = Literal["tiny", "small", "medium", "large", "full"]
17
+
18
+ # bin-number-to-cents mapping used by the CREPE classifier (360 pitch bins)
19
+ _CENTS_MAPPING = np.linspace(0, 7180, 360) + 1997.3794084376191
20
+ _SAMPLE_RATE = 16000
21
+ _TIMEOUT = 30
22
+ _REMOTE_URLS = {
23
+ "tiny": "https://media.githubusercontent.com/media/mxmpl/crepe-predictor/main/checkpoints/tiny.onnx",
24
+ "small": "https://media.githubusercontent.com/media/mxmpl/crepe-predictor/main/checkpoints/small.onnx",
25
+ "medium": "https://media.githubusercontent.com/media/mxmpl/crepe-predictor/main/checkpoints/medium.onnx",
26
+ "large": "https://media.githubusercontent.com/media/mxmpl/crepe-predictor/main/checkpoints/large.onnx",
27
+ "full": "https://media.githubusercontent.com/media/mxmpl/crepe-predictor/main/checkpoints/full.onnx",
28
+ }
29
+ _CHECKSUMS = {
30
+ "tiny": "345b8ed787dc94236f237f234c6fb3f3f389291315b44909643c011b7a16f8c7",
31
+ "small": "762e975d7717d5c47265344d3588dfac5bdee7e9a66d666a40f888a20eddbfa8",
32
+ "medium": "00e25a2fbf0b141a2c739609fd1587cb8923daf78162d7dd3404413cc7fbf985",
33
+ "large": "bff31dfecdaca02141cf3c1c3bc984e2ea2bef7de98d9cd6a36bbbe851fb12c4",
34
+ "full": "9046c78f1cf40ebdbad1a2b3d9dc154dab36ef51ab55c6cd4d43776b9f5948ce",
35
+ }
36
+
37
+
38
+ def _frame(audio: np.ndarray, hop_length: int, center: bool) -> np.ndarray:
39
+ """Split ``audio`` into normalized 1024-sample frames expected by CREPE."""
40
+ audio = np.asarray(audio, dtype=np.float32)
41
+ if center:
42
+ # pad so frames are centered on their timestamps (first frame is zero-centered)
43
+ audio = np.pad(audio, 512)
44
+ if len(audio) < 1024:
45
+ raise ValueError(f"audio is too short to form a single 1024-sample frame: got {len(audio)} samples")
46
+ frames = np.lib.stride_tricks.sliding_window_view(audio, 1024)[::hop_length].copy()
47
+ frames -= frames.mean(axis=1, keepdims=True)
48
+ frames /= np.clip(frames.std(axis=1, keepdims=True), 1e-8, None) # avoid /0 on constant (silent) frames
49
+ return frames
50
+
51
+
52
+ def _local_average_cents(salience: np.ndarray, centers: np.ndarray) -> np.ndarray:
53
+ """Weighted average of the cents mapping over a +/-4 bin window around each center."""
54
+ salience = np.pad(salience, ((0, 0), (4, 4)))
55
+ mapping = np.pad(_CENTS_MAPPING, (4, 4))
56
+ index = centers[:, None] + np.arange(9) # window [center-4, center+4] in padded coords
57
+ window = np.take_along_axis(salience, index, axis=1)
58
+ return (window * mapping[index]).sum(axis=1) / window.sum(axis=1)
59
+
60
+
61
+ def _viterbi(log_start: np.ndarray, log_trans: np.ndarray, log_emit: np.ndarray) -> np.ndarray:
62
+ """Generic Viterbi decode. ``log_emit`` has shape (n_frames, n_states)."""
63
+ n_frames = log_emit.shape[0]
64
+ score = log_start + log_emit[0]
65
+ backpointers = np.empty_like(log_emit, dtype=int)
66
+ for t in range(1, n_frames):
67
+ candidates = score[:, None] + log_trans
68
+ backpointers[t] = candidates.argmax(axis=0)
69
+ score = candidates.max(axis=0) + log_emit[t]
70
+ path = np.empty(n_frames, dtype=int)
71
+ path[-1] = score.argmax()
72
+ for t in range(n_frames - 1, 0, -1):
73
+ path[t - 1] = backpointers[t, path[t]]
74
+ return path
75
+
76
+
77
+ def _viterbi_centers(salience: np.ndarray) -> np.ndarray:
78
+ """Viterbi path over the 360 pitch bins with a transition prior enforcing continuity."""
79
+ n = 360
80
+ transition = np.maximum(12 - np.abs(np.subtract.outer(np.arange(n), np.arange(n))), 0.0)
81
+ transition /= transition.sum(axis=1, keepdims=True)
82
+ emission = np.eye(n) * 0.1 + 0.9 / n # fixed self-probability, uniform otherwise
83
+ observations = salience.argmax(axis=1)
84
+ with np.errstate(divide="ignore"):
85
+ return _viterbi(np.full(n, -np.log(n)), np.log(transition), np.log(emission)[:, observations].T)
86
+
87
+
88
+ def _predict(
89
+ session: ort.InferenceSession,
90
+ audio: np.ndarray,
91
+ viterbi: bool = True,
92
+ center: bool = True,
93
+ frame_shift: float = 0.01,
94
+ frame_length: float = 0.025,
95
+ ) -> np.ndarray:
96
+ """Extract the (POV, pitch) per frame from a 16 kHz mono ``audio`` signal.
97
+
98
+ ``session`` runs the CREPE model exported to ONNX (see ``model.export_onnx``). The first
99
+ output column is the probability of voicing, the second the estimated pitch in Hz.
100
+ """
101
+ hop_length = round(_SAMPLE_RATE * frame_shift)
102
+ frames = _frame(audio, hop_length, center)
103
+ salience = np.asarray(session.run(None, {session.get_inputs()[0].name: frames})[0]) # activation matrix, (T, 360)
104
+ confidence = salience.max(axis=1) # heuristic voicing probability
105
+ centers = _viterbi_centers(salience) if viterbi else salience.argmax(axis=1)
106
+ cents = _local_average_cents(salience, centers)
107
+ frequency = 10 * 2 ** (cents / 1200)
108
+ frequency[np.isnan(frequency)] = 0
109
+
110
+ # resample (POV, pitch) to the frame count implied by frame_shift/frame_length
111
+ nsamples = 1 + int((len(audio) - frame_length * _SAMPLE_RATE) / hop_length)
112
+ if nsamples < 1:
113
+ min_samples = int(frame_length * _SAMPLE_RATE)
114
+ raise ValueError(
115
+ f"audio is too short to produce any output frames: got {len(audio)} samples, but "
116
+ f"frame_length={frame_length}s needs at least {min_samples} samples at {_SAMPLE_RATE} Hz"
117
+ )
118
+ data = scipy.signal.resample(np.stack([confidence, frequency], axis=1), nsamples)
119
+ data[data[:, 0] < 1e-2, 0] = 0
120
+ data[data[:, 0] > 1, 0] = 1
121
+ return data
122
+
123
+
124
+ def _predict_voicing(confidence: np.ndarray) -> np.ndarray:
125
+ """Viterbi path over voiced (1) vs unvoiced (0) frames from the voicing confidence."""
126
+ means, variance = np.array([0.0, 1.0]), 0.25 # unvoiced and voiced states
127
+ log_emit = -((confidence[:, None] - means) ** 2) / (2 * variance) # gaussian, up to a constant
128
+ log_start = np.log([0.5, 0.5])
129
+ log_trans = np.log([[0.99, 0.01], [0.01, 0.99]]) # prior on continuous voicing state
130
+ return _viterbi(log_start, log_trans, log_emit)
131
+
132
+
133
+ def _nccf_to_pov(nccf: float) -> float:
134
+ """Normalized cross-correlation to probability of voicing (Povey, ICASSP 2014)."""
135
+ y = -5.2 + 5.4 * np.exp(7.5 * (nccf - 1)) + 4.8 * nccf - 2 * np.exp(-10 * nccf) + 4.2 * np.exp(20 * (nccf - 1))
136
+ return 1 / (1 + np.exp(-y))
137
+
138
+
139
+ def _postprocess(pitch: np.ndarray) -> np.ndarray:
140
+ """Turn the raw (POV, pitch) from :func:`_predict` into (NCCF, pitch) for Kaldi.
141
+
142
+ Unvoiced frames are detected with a voicing HMM and their pitch interpolated, then
143
+ the POV is converted back to an NCCF usable by Kaldi's ``process-pitch``.
144
+ """
145
+ to_remove = _predict_voicing(pitch[:, 0]) == 0 # interpolate pitch values over the unvoiced frames
146
+ if np.all(to_remove):
147
+ raise ValueError("No voiced frames")
148
+ data = pitch[:, 1].copy()
149
+ keep = np.where(~to_remove)[0]
150
+ first, last = keep[0], keep[-1]
151
+ interp = scipy.interpolate.interp1d(keep, data[keep], fill_value="extrapolate")
152
+ data[to_remove] = interp(np.where(to_remove)[0])
153
+ data[:first] = data[first]
154
+ data[last:] = data[last]
155
+ if not np.all(data > 0):
156
+ raise ValueError("Not all pitch values are positive after interpolation")
157
+
158
+ # invert the POV -> NCCF mapping (saturates to 0/1 outside the range spanned by nccf in [0, 1])
159
+ lo, hi = _nccf_to_pov(0.0), _nccf_to_pov(1.0)
160
+ nccf = np.array(
161
+ [
162
+ 0.0
163
+ if pov <= lo
164
+ else 1.0
165
+ if pov >= hi
166
+ else scipy.optimize.bisect(lambda x, pov=pov: _nccf_to_pov(x) - pov, 0, 1)
167
+ for pov in pitch[:, 0]
168
+ ]
169
+ )
170
+ return np.stack([nccf, data], axis=1)
171
+
172
+
173
+ def _cache_dir() -> Path:
174
+ base = os.environ.get("CREPE_CACHE_DIR") or os.environ.get("XDG_CACHE_HOME") or str(Path.home() / ".cache")
175
+ return Path(base) / "crepe_predictor" / "checkpoints"
176
+
177
+
178
+ def _download(capacity: Capacity, destination: Path) -> None:
179
+ destination.parent.mkdir(parents=True, exist_ok=True)
180
+ with tempfile.TemporaryDirectory(dir=destination.parent) as tmp_dir:
181
+ tmp = Path(tmp_dir) / destination.name
182
+ digest = hashlib.sha256()
183
+ with urllib.request.urlopen(_REMOTE_URLS[capacity], timeout=_TIMEOUT) as response, tmp.open("wb") as f:
184
+ for chunk in iter(lambda: response.read(1 << 20), b""):
185
+ f.write(chunk)
186
+ digest.update(chunk)
187
+ if digest.hexdigest() != _CHECKSUMS[capacity]:
188
+ raise ValueError(
189
+ f"checksum mismatch for the {capacity!r} checkpoint: "
190
+ f"expected {_CHECKSUMS[capacity]}, got {digest.hexdigest()}"
191
+ )
192
+ tmp.replace(destination)
193
+
194
+
195
+ def _resolve_checkpoint(capacity: Capacity, checkpoint: str | Path | None) -> Path:
196
+ """Resolve a checkpoint path, downloading and caching the remote one if needed."""
197
+ if checkpoint is not None:
198
+ path = Path(checkpoint)
199
+ if not path.is_file():
200
+ raise FileNotFoundError(f"No checkpoint at {path}")
201
+ return path
202
+ path = _cache_dir() / f"{capacity}.onnx"
203
+ if not path.is_file():
204
+ _download(capacity, path)
205
+ return path
206
+
207
+
208
+ class CrepePredictor:
209
+ """Load a CREPE ONNX checkpoint and run pitch inference."""
210
+
211
+ def __init__(
212
+ self,
213
+ capacity: Capacity = "full",
214
+ *,
215
+ checkpoint: str | Path | None = None,
216
+ onnx_providers: list[str] | None = None,
217
+ ) -> None:
218
+ self.capacity = capacity
219
+ path = _resolve_checkpoint(capacity, checkpoint)
220
+ self.session = ort.InferenceSession(str(path), providers=onnx_providers or ["CPUExecutionProvider"])
221
+
222
+ def predict(
223
+ self,
224
+ audio: np.ndarray,
225
+ *,
226
+ viterbi: bool = True,
227
+ center: bool = True,
228
+ frame_shift: float = 0.01,
229
+ frame_length: float = 0.025,
230
+ ) -> np.ndarray:
231
+ """Estimate (POV, pitch) per frame, as an ``(n_frames, 2)`` array in Hz."""
232
+ return _predict(self.session, audio, viterbi, center, frame_shift, frame_length)
233
+
234
+ def predict_kaldi(
235
+ self,
236
+ audio: np.ndarray,
237
+ *,
238
+ viterbi: bool = True,
239
+ center: bool = True,
240
+ frame_shift: float = 0.01,
241
+ frame_length: float = 0.025,
242
+ ) -> np.ndarray:
243
+ """Like :meth:`predict`, but returns (NCCF, pitch) per frame for use with Kaldi's ``process-pitch``."""
244
+ return _postprocess(
245
+ self.predict(
246
+ audio,
247
+ viterbi=viterbi,
248
+ center=center,
249
+ frame_shift=frame_shift,
250
+ frame_length=frame_length,
251
+ )
252
+ )
@@ -0,0 +1,57 @@
1
+ [project]
2
+ name = "crepe-predictor"
3
+ version = "0.1.0"
4
+ description = "A dependency-light reimplementation of CREPE inference with ONNX"
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ authors = [{ name = "Maxime Poli" }]
8
+ keywords = ["machine learning", "speech"]
9
+ classifiers = [
10
+ "Development Status :: 4 - Beta",
11
+ "Intended Audience :: Science/Research",
12
+ "Operating System :: OS Independent",
13
+ "Programming Language :: Python :: 3 :: Only",
14
+ "Programming Language :: Python :: 3.13",
15
+ "Programming Language :: Python :: 3.14",
16
+ "Topic :: Scientific/Engineering",
17
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
18
+ ]
19
+ dependencies = [
20
+ "numpy>=2.4.6",
21
+ "onnxruntime>=1.27.0",
22
+ "scipy>=1.18.0",
23
+ ]
24
+
25
+ [project.urls]
26
+ repository = "https://github.com/mxmpl/crepe-predictor"
27
+
28
+ [dependency-groups]
29
+ test = [
30
+ "hypothesis>=6.156.4",
31
+ "onnx>=1.22.0",
32
+ "onnxscript>=0.7.1",
33
+ "pytest>=9.1.1",
34
+ "pytest-cov>=7.1.0",
35
+ "torchcrepe>=0.0.24",
36
+ ]
37
+
38
+ [build-system]
39
+ requires = ["flit_core>=4.0.2"]
40
+ build-backend = "flit_core.buildapi"
41
+
42
+ [tool.coverage.run]
43
+ branch = true
44
+ source = ["crepe_predictor.py"]
45
+
46
+ [tool.coverage.report]
47
+ fail_under = 99
48
+
49
+ [tool.pytest]
50
+ addopts = ["--cov=crepe_predictor"]
51
+ filterwarnings = ["ignore:.*LeafSpec.*:FutureWarning"]
52
+
53
+ [tool.ruff]
54
+ line-length = 119
55
+
56
+ [tool.typos.default.extend-words]
57
+ arange = "arange"