Spaces:
Runtime error
Runtime error
Richard Zhu commited on
Commit Β·
d4afb7f
1
Parent(s): bdd05b2
initial commit
Browse files- chord_harp/.DS_Store +0 -0
- chord_harp/ChordEstimation +1 -0
- chord_harp/ChordRecognitionMIDITrainedExtractor +1 -0
- chord_harp/Dockerfile +34 -0
- chord_harp/README.md +21 -0
- chord_harp/app.py +114 -0
- chord_harp/packages.txt +5 -0
- chord_harp/patch_madmom.py +33 -0
- chord_harp/requirements.txt +7 -0
chord_harp/.DS_Store
ADDED
|
Binary file (8.2 kB). View file
|
|
|
chord_harp/ChordEstimation
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Subproject commit bdd05b2236130f01ca2875a30297f3f368fbf470
|
chord_harp/ChordRecognitionMIDITrainedExtractor
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Subproject commit 19b81f694dae7782111b1be0e39e66f42fa1615f
|
chord_harp/Dockerfile
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.8-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# Install system dependencies
|
| 6 |
+
COPY packages.txt /app/packages.txt
|
| 7 |
+
RUN apt-get update && \
|
| 8 |
+
xargs apt-get install -y --no-install-recommends < /app/packages.txt && \
|
| 9 |
+
rm -rf /var/lib/apt/lists/*
|
| 10 |
+
|
| 11 |
+
# Install Python dependencies
|
| 12 |
+
COPY requirements.txt /app/requirements.txt
|
| 13 |
+
ENV PIP_NO_BUILD_ISOLATION=1
|
| 14 |
+
RUN pip install --no-cache-dir -U pip wheel Cython
|
| 15 |
+
RUN pip install --no-cache-dir setuptools==59.8.0
|
| 16 |
+
RUN pip install --no-cache-dir numpy==1.23.5
|
| 17 |
+
RUN pip install --no-cache-dir -r /app/requirements.txt
|
| 18 |
+
RUN pip install --no-cache-dir --no-build-isolation madmom
|
| 19 |
+
|
| 20 |
+
# Apply madmom patch
|
| 21 |
+
COPY patch_madmom.py /app/patch_madmom.py
|
| 22 |
+
RUN python /app/patch_madmom.py
|
| 23 |
+
RUN python -c "import madmom; print('madmom import OK')"
|
| 24 |
+
|
| 25 |
+
# Copy the rest of the app
|
| 26 |
+
COPY . /app
|
| 27 |
+
|
| 28 |
+
# Set working directory to chord recognition repo so model files are found
|
| 29 |
+
WORKDIR /app/ChordRecognitionMIDITrainedExtractor
|
| 30 |
+
|
| 31 |
+
ENV PORT=7860
|
| 32 |
+
EXPOSE 7860
|
| 33 |
+
|
| 34 |
+
CMD ["python", "/app/app.py"]
|
chord_harp/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Automatic Chord Recognition
|
| 3 |
+
emoji: π΅
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
# Automatic Chord Recognition (PyHARP)
|
| 11 |
+
|
| 12 |
+
This is a [PyHARP](https://github.com/TEAMuP-dev/pyharp) app that estimates chord progressions from audio using the model proposed in:
|
| 13 |
+
|
| 14 |
+
> Wu, Y., & Li, W. (2019). Automatic Audio Chord Recognition With MIDI-Trained Deep Feature and BLSTM-CRF Sequence Decoding Model. *IEEE/ACM Transactions on Audio, Speech, and Language Processing*, 27(2), 355β366.
|
| 15 |
+
|
| 16 |
+
## How it works
|
| 17 |
+
1. Upload an audio file in HARP
|
| 18 |
+
2. The model extracts a Harmonic CQT spectrogram
|
| 19 |
+
3. A CNN extracts pitch class features (trained on MIDI data)
|
| 20 |
+
4. A BLSTM-CRF decodes the chord label sequence
|
| 21 |
+
5. Time-stamped chord labels are returned and displayed in HARP
|
chord_harp/app.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
|
| 4 |
+
import sys
|
| 5 |
+
import os
|
| 6 |
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "ChordRecognitionMIDITrainedExtractor"))
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
import networks as N
|
| 10 |
+
from librosa.core import cqt, load, note_to_hz
|
| 11 |
+
import const as C
|
| 12 |
+
import utils as U
|
| 13 |
+
|
| 14 |
+
from pyharp import ModelCard, build_endpoint, LabelList, AudioLabel, OutputLabel
|
| 15 |
+
import gradio as gr
|
| 16 |
+
|
| 17 |
+
# ββ Load models once at startup ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 18 |
+
cnn_feat_extractor = N.FullCNNFeatExtractor()
|
| 19 |
+
cnn_feat_extractor.load(C.DEFAULT_CONVNETFILE)
|
| 20 |
+
|
| 21 |
+
decoder = N.NBLSTMCRF()
|
| 22 |
+
decoder.load("nblstm_crf.model")
|
| 23 |
+
|
| 24 |
+
# ββ Model card βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 25 |
+
model_card = ModelCard(
|
| 26 |
+
name="Automatic Chord Recognition",
|
| 27 |
+
description="Estimates chord progressions from audio using a CNN feature extractor trained on MIDI data and a BLSTM-CRF sequence decoder. Outputs time-stamped chord labels.",
|
| 28 |
+
author="Wu & Li (2019), wrapped by PyHARP",
|
| 29 |
+
tags=["chord recognition", "harmony", "MIR"],
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
# ββ Processing function βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 33 |
+
def process_fn(input_audio_path: str) -> LabelList:
|
| 34 |
+
# Load audio
|
| 35 |
+
y, sr = load(input_audio_path, sr=C.SR)
|
| 36 |
+
|
| 37 |
+
# Extract Harmonic-CQT
|
| 38 |
+
fmin = note_to_hz("C1")
|
| 39 |
+
hcqt = np.stack([
|
| 40 |
+
np.abs(cqt(
|
| 41 |
+
y, sr=C.SR, hop_length=C.H, n_bins=C.BIN_CNT,
|
| 42 |
+
bins_per_octave=C.OCT_BIN, fmin=fmin * (h + 1),
|
| 43 |
+
filter_scale=2, tuning=None
|
| 44 |
+
)).T.astype(np.float32)
|
| 45 |
+
for h in range(C.CQT_H)
|
| 46 |
+
])
|
| 47 |
+
|
| 48 |
+
# Extract deep feature
|
| 49 |
+
feat = cnn_feat_extractor.GetFeature(U.PreprocessSpec(hcqt)).data
|
| 50 |
+
|
| 51 |
+
# Decode chord label sequence
|
| 52 |
+
labels = decoder.argmax(feat)
|
| 53 |
+
|
| 54 |
+
# Build LabelList for HARP
|
| 55 |
+
output_labels = LabelList()
|
| 56 |
+
cur_label = labels[0]
|
| 57 |
+
st = 0
|
| 58 |
+
|
| 59 |
+
for i in range(labels.size):
|
| 60 |
+
if labels[i] != cur_label:
|
| 61 |
+
ed = i
|
| 62 |
+
feat_seg = feat[st:ed, :]
|
| 63 |
+
chord_sign = U.voc.ChordSignature7thbass(cur_label, feat_seg, sevenths=True, inv=True)
|
| 64 |
+
start_sec = float(st * C.H) / C.SR
|
| 65 |
+
end_sec = float(ed * C.H) / C.SR
|
| 66 |
+
|
| 67 |
+
if chord_sign != "N":
|
| 68 |
+
output_labels.labels.append(
|
| 69 |
+
AudioLabel(
|
| 70 |
+
t=start_sec,
|
| 71 |
+
label=chord_sign,
|
| 72 |
+
duration=end_sec - start_sec,
|
| 73 |
+
description=f"Chord: {chord_sign} ({start_sec:.2f}s - {end_sec:.2f}s)",
|
| 74 |
+
)
|
| 75 |
+
)
|
| 76 |
+
cur_label = labels[i]
|
| 77 |
+
st = i
|
| 78 |
+
|
| 79 |
+
# Handle last segment
|
| 80 |
+
feat_seg = feat[st:labels.size, :]
|
| 81 |
+
chord_sign = U.voc.ChordSignature7thbass(cur_label, feat_seg)
|
| 82 |
+
start_sec = float(st * C.H) / C.SR
|
| 83 |
+
end_sec = float(labels.size * C.H) / C.SR
|
| 84 |
+
if chord_sign != "N":
|
| 85 |
+
output_labels.labels.append(
|
| 86 |
+
AudioLabel(
|
| 87 |
+
t=start_sec,
|
| 88 |
+
label=chord_sign,
|
| 89 |
+
duration=end_sec - start_sec,
|
| 90 |
+
description=f"Chord: {chord_sign} ({start_sec:.2f}s - {end_sec:.2f}s)",
|
| 91 |
+
)
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
return output_labels
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
# ββ Gradio endpoint βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 98 |
+
with gr.Blocks() as demo:
|
| 99 |
+
input_components = [
|
| 100 |
+
gr.Audio(type="filepath", label="Input Audio").harp_required(True),
|
| 101 |
+
]
|
| 102 |
+
|
| 103 |
+
output_components = [
|
| 104 |
+
gr.JSON(label="Output Labels"),
|
| 105 |
+
]
|
| 106 |
+
|
| 107 |
+
app = build_endpoint(
|
| 108 |
+
model_card=model_card,
|
| 109 |
+
input_components=input_components,
|
| 110 |
+
output_components=output_components,
|
| 111 |
+
process_fn=process_fn,
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
demo.queue().launch(share=True, show_error=False, pwa=True)
|
chord_harp/packages.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gcc
|
| 2 |
+
g++
|
| 3 |
+
libsndfile1
|
| 4 |
+
ffmpeg
|
| 5 |
+
git
|
chord_harp/patch_madmom.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Patch madmom to fix deprecated sklearn and other compatibility issues.
|
| 4 |
+
"""
|
| 5 |
+
import os
|
| 6 |
+
import site
|
| 7 |
+
|
| 8 |
+
def patch_file(filepath, old, new):
|
| 9 |
+
if not os.path.exists(filepath):
|
| 10 |
+
print(f"Skipping (not found): {filepath}")
|
| 11 |
+
return
|
| 12 |
+
with open(filepath, "r") as f:
|
| 13 |
+
content = f.read()
|
| 14 |
+
if old in content:
|
| 15 |
+
content = content.replace(old, new)
|
| 16 |
+
with open(filepath, "w") as f:
|
| 17 |
+
f.write(content)
|
| 18 |
+
print(f"Patched: {filepath}")
|
| 19 |
+
else:
|
| 20 |
+
print(f"No patch needed: {filepath}")
|
| 21 |
+
|
| 22 |
+
# Find madmom install location
|
| 23 |
+
site_packages = site.getsitepackages()[0]
|
| 24 |
+
madmom_base = os.path.join(site_packages, "madmom")
|
| 25 |
+
|
| 26 |
+
# Fix sklearn.externals.joblib import
|
| 27 |
+
patch_file(
|
| 28 |
+
os.path.join(madmom_base, "ml", "hmm.py"),
|
| 29 |
+
"from sklearn.externals import joblib",
|
| 30 |
+
"import joblib"
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
print("Madmom patching complete.")
|
chord_harp/requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio==5.28.0
|
| 2 |
+
git+https://github.com/TEAMuP-dev/pyharp.git@v0.3.0
|
| 3 |
+
chainer==7.8.1
|
| 4 |
+
librosa
|
| 5 |
+
scipy
|
| 6 |
+
mir_eval
|
| 7 |
+
joblib
|