음성 파일을 MIDI로 변환하는 Web 앱을 만든 이야기 〜라이브러리 선정의 시행착오〜
요약
본 글은 음성 파일(MP3/WAV)을 MIDI 파일로 변환하는 Web 앱 개발 과정을 다루며, 특히 라이브러리 선정 과정에서 겪었던 기술적 난관과 해결책을 정리합니다. 초기에는 단음 전용인 `librosa`의 `pyin` 함수를 사용했으나, 화음을 처리할 수 있는 Basic Pitch(Spotify)로 전환하며 성능을 크게 개선했습니다. 또한, Python 버전 호환성 문제와 음원 분리(Demucs)를 통한 정밀도 향상 등 여러 기술적 고려 사항을 상세히 설명합니다.
핵심 포인트
- 음성-MIDI 변환 시 단음 전용 라이브러리(`librosa.pyin`)는 밴드 사운드에 부적합하며, 화음을 처리하는 Basic Pitch(Spotify)가 효과적이다.
- Basic Pitch 사용을 위해 Python 버전 호환성 문제(Python 3.13 미지원)를 해결하고자 별도의 가상 환경(venv)을 구축해야 했다.
- 음원 분리 모델인 Demucs(Meta)를 사용하여 악기별로 음원을 분리한 후, 각 파트를 개별적으로 MIDI 변환하는 것이 정밀도 높은 결과를 얻는 핵심 방법이다.
- 템포 및 키 분석은 독립적인 기능으로 `librosa`를 사용하며, 최신 버전의 API 변경 사항(`beat_track` 반환값)에 주의해야 한다.
「청음(耳コピ)이 힘들어서 AI에게 맡기고 싶다」
그런 동기에서, 음성 파일(MP3·WAV)을 MIDI로 변환하는 Web 앱을 만들었습니다.
단순해 보이지만 라이브러리 선정 과정에서 몇 번이나 막혔기에, 그 경위를 정리합니다.
기술 스택 (최종 구성)
- 프론트엔드: React (Vite)
- 백엔드: FastAPI (Python 3.11)
- 음원 분리: Demucs (Meta)
- MIDI 변환: Basic Pitch (Spotify)
- 템포·키 검출: librosa
완성된 앱은 두 개의 페이지로 구성되어 있습니다.
MIDI 변환 페이지
- MP3/WAV 업로드
- 분리 패턴 선택 (심플·표준·상세)
- 변환 시작 →
combined.mid다운로드
템포·키 분석 페이지
- 음성 파일을 업로드하는 것만으로 템포(BPM)와 키를 빠르게 표시
처음에 시도한 것은 librosa의 pyin 함수를 이용한 피치 검출(Pitch Detection)입니다.
f0, voiced_flag, _ = librosa.pyin(
audio,
fmin=librosa.note_to_hz("C2"),
...
결과: 사용할 수 없었음
pyin은 단음 악기(보컬이나 단음 플루트 등)를 위한 알고리즘입니다.
밴드나 오케스트라처럼 여러 소리가 겹치는 악곡에는 대응하지 못하며, 출력된 MIDI는 「곡이라고 간주할 수 없는 수준」이었습니다.
다음에 채택한 것이 Basic Pitch (Spotify 제작)입니다.
Basic Pitch는 뉴럴 네트워크(Neural Network) 기반의 피치 검출 라이브러리로, 복음(화음)에 대응합니다.
피아노나 기타의 코드도 검출할 수 있는 것이 큰 강점입니다.
from basic_pitch.inference import predict
_, midi_data, _ = predict(
wav_path,
...
결과: 대폭 개선
단음밖에 잡지 못했던 pyin과 비교하여, 화음·복음이 올바르게 검출되게 되었습니다.
완벽하지는 않지만, 곡으로서 인식할 수 있는 수준의 MIDI가 출력됩니다.
Basic Pitch를 도입하려 했을 때, Python 3.13에서는 동작하지 않는다는 문제에 직면했습니다.
ERROR: No matching distribution found for tensorflow<2.15.1,>=2.4.1
Basic Pitch는 TensorFlow에 의존하고 있으며, TensorFlow 2.15 이하는 Python 3.11까지만 대응합니다.
Python 3.13에서는 pkg_resources의 사양 변경으로 인해 빌드 자체가 실패합니다.
해결책: Python 3.11 가상 환경을 별도로 생성
# Python 3.11을 설치한 후
py -3.11 -m venv venv311
venv311\Scripts\pip install basic-pitch
기존의 Python 3.13 환경에는 영향을 주지 않으며, FastAPI와 Demucs도 3.11 환경으로 이전함으로써 해결했습니다.
밴드 사운드를 그대로 Basic Pitch에 넣으면, 여러 악기 소리가 혼재되어 정밀도가 떨어집니다.
그래서 Demucs (Meta 제작)로 악기별로 음원을 분리한 후 MIDI로 변환하는 2단계 구성으로 만들었습니다.
from demucs.pretrained import get_model
from demucs.apply import apply_model
model = get_model("htdemucs")
...
사용 모델과 분리 패턴
| 모델 | 분리 파트 |
|---|---|
| htdemucs | drums, bass, other, vocals |
| htdemucs_6s | drums, bass, guitar, piano, other, vocals |
드럼은 MIDI 변환에 적합하지 않기 때문에 모든 패턴에서 제외하였고, 나머지 파트를 각각 Basic Pitch에 적용하여 하나의 MIDI로 통합하고 있습니다.
combined_midi = pretty_midi.PrettyMIDI()
for stem in ["vocals", "bass", "other"]:
# 파트별로 WAV를 저장하여 Basic Pitch로 변환
...
MIDI 변환과는 독립된 분석 기능으로서, librosa를 사용하여 템포(Tempo)와 키(Key)를 검출하고 있습니다.
이것은 단순한 통계 처리이므로 정밀도와 속도 모두 문제가 없습니다.
# 템포 검출
tempo, _ = librosa.beat.beat_track(y=audio, sr=sr)
tempo_val = float(np.atleast_1d(tempo)[0]) # 3.13 대응을 위해 atleast_1d를 사용
...
주의: librosa 0.11 이후, beat_track의 반환값이 스칼라(Scalar)가 아닌 배열(Array)로 변경되었습니다.
따라서 float(tempo)가 아니라 float(np.atleast_1d(tempo)[0])로 작성해야 합니다.
| 용도 | 처음에 시도한 것 | 문제점 | 채택한 것 |
|---|---|---|---|
| MIDI 변환 | librosa (pyin) | 단음만 대응 · 다음(Polyphony)에 취약 | Basic Pitch (Spotify) |
| ... |
librosa.pyin은 단음 전용: 밴드 사운드에는 사용할 수 없음 -
Basic Pitch는 Python 3.13 미지원: 3.11 가상 환경을 별도로 생성할 필요가 있음 -
librosa 0.11의 beat_track 반환값 변경: atleast_1d로 대처 -
음원을 혼합한 후 Basic Pitch를 적용하면 정밀도가 떨어짐: 파트별로 개별 변환하는 것이 정답 -
음성→MIDI 변환은 "해보니 생각보다 어려운" 영역이었습니다.
특히 Basic Pitch의 Python 3.13 미지원 부분은 놓치기 쉬운 지점이었으나, 가상 환경을 분리함으로써 해결했습니다.
정밀도는 아직 완벽하지 않지만, Basic Pitch + Demucs의 조합으로 실용적인 수준에는 도달했습니다.
같은 작업을 시도하려는 분들에게 참고가 되기를 바랍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기