Initial commit
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
Basic Pitch service: audio (MP3/WAV/etc.) to MIDI via Spotify's Basic Pitch model.
|
||||
|
||||
Requires Python 3.11 or 3.12 due to tensorflow-macos constraints.
|
||||
See requirements-transcribe.txt for setup instructions.
|
||||
"""
|
||||
import asyncio
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
_IMPORT_ERROR: str | None = None
|
||||
|
||||
try:
|
||||
from basic_pitch.inference import predict # type: ignore
|
||||
from basic_pitch import ICASSP_2022_MODEL_PATH # type: ignore
|
||||
except ImportError as e:
|
||||
_IMPORT_ERROR = (
|
||||
"basic-pitch is not installed. MP3 transcription requires Python 3.11 or 3.12. "
|
||||
"See backend/requirements-transcribe.txt for setup instructions. "
|
||||
f"Original error: {e}"
|
||||
)
|
||||
|
||||
|
||||
async def transcribe_audio_to_midi(audio_bytes: bytes, filename: str) -> bytes:
|
||||
"""Transcribe audio bytes to MIDI bytes using Basic Pitch."""
|
||||
if _IMPORT_ERROR:
|
||||
raise RuntimeError(_IMPORT_ERROR)
|
||||
return await asyncio.get_running_loop().run_in_executor(
|
||||
None, _transcribe, audio_bytes, filename
|
||||
)
|
||||
|
||||
|
||||
def _transcribe(audio_bytes: bytes, filename: str) -> bytes:
|
||||
suffix = Path(filename).suffix or ".mp3"
|
||||
with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as f:
|
||||
f.write(audio_bytes)
|
||||
audio_path = f.name
|
||||
|
||||
_model_output, midi_data, _note_events = predict( # type: ignore[name-defined]
|
||||
audio_path, ICASSP_2022_MODEL_PATH # type: ignore[name-defined]
|
||||
)
|
||||
|
||||
midi_tmp = tempfile.NamedTemporaryFile(suffix=".mid", delete=False)
|
||||
midi_tmp.close()
|
||||
midi_data.write(midi_tmp.name)
|
||||
|
||||
midi_bytes = Path(midi_tmp.name).read_bytes()
|
||||
Path(audio_path).unlink(missing_ok=True)
|
||||
Path(midi_tmp.name).unlink(missing_ok=True)
|
||||
return midi_bytes
|
||||
Reference in New Issue
Block a user