Spaces:
Sleeping
Sleeping
Commit ·
3e92cd5
1
Parent(s): 770a891
feat: chromagram extractor — chroma CQT mean, L1-normalized
Browse files- app/services/chromagram.py +10 -0
- tests/test_chromagram.py +21 -0
app/services/chromagram.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import librosa
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def extract_chroma_mean(y: np.ndarray, sr: int, hop_length: int = 512) -> np.ndarray:
|
| 6 |
+
"""Extract mean chroma CQT vector (12,) for an audio chunk, L1-normalized."""
|
| 7 |
+
chroma = librosa.feature.chroma_cqt(y=y, sr=sr, hop_length=hop_length)
|
| 8 |
+
mean = chroma.mean(axis=1)
|
| 9 |
+
total = mean.sum()
|
| 10 |
+
return mean / total if total > 0 else mean
|
tests/test_chromagram.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
from app.services.chromagram import extract_chroma_mean
|
| 3 |
+
|
| 4 |
+
def sine(freq, duration=2.0, sr=22050):
|
| 5 |
+
t = np.linspace(0, duration, int(sr * duration))
|
| 6 |
+
return np.sin(2 * np.pi * freq * t).astype(np.float32)
|
| 7 |
+
|
| 8 |
+
def test_output_shape():
|
| 9 |
+
assert extract_chroma_mean(sine(440.0), sr=22050).shape == (12,)
|
| 10 |
+
|
| 11 |
+
def test_output_is_normalized():
|
| 12 |
+
chroma = extract_chroma_mean(sine(440.0), sr=22050)
|
| 13 |
+
assert abs(chroma.sum() - 1.0) < 0.05
|
| 14 |
+
|
| 15 |
+
def test_output_non_negative():
|
| 16 |
+
assert (extract_chroma_mean(sine(440.0), sr=22050) >= 0).all()
|
| 17 |
+
|
| 18 |
+
def test_a440_peak_at_index_9():
|
| 19 |
+
# A = pitch class 9 (C=0 … A=9)
|
| 20 |
+
chroma = extract_chroma_mean(sine(440.0, duration=4.0), sr=22050)
|
| 21 |
+
assert np.argmax(chroma) == 9
|