ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [WPF] CasstteDeck MP3
    WPF 2026. 10. 22. 17:11

     

     

     

     

    minecraft-font

    #WPF #CSharp #DotNet8 #OnnxRuntime #SpotifyAI #성능최적화

    💡 CassetteDeck MP3

    Spotify AI 채보 시스템 구축기

    🎯웹에서 재생가능한 플레이어도 많을 정도로 흔한 mp3 플레이어는 널리고 널렸다.

    문득 ZZZ(젠레스 존 제로)의 카세트 플레이어 굿즈를 보고 영감을 얻어, 화면 구석에 띄워두는 로컬 감성 굿즈 겸 나만의 실용적인 MP3 프로그램을 직접 만들게 되었습니다.

     

    Aurale 프로그램 홈 화면
    재생 화면(좌) 서랍(우)

    🛠️ 개발 환경 (Tech Stack)
    • Framework & UI: .NET 8.0 (C# 12) / WPF SDK
    • IDE & Tools: Antigravity IDE (Agentic AI Pair Programming)
    • Audio Engine: NAudio (WASAPI / Resampling / Virtual Synth)
    • AI Model: Spotify Basic Pitch (ONNX Runtime In-Process Inference)
    📼 1차 MVP (로컬 감성 덱)
    • MP3 기본 재생 및 정밀 탐색 제어
    • 유튜브 음원 원클릭 자동 다운로더
    • ZZZ 카세트 테이프 & 릴 휠 물리 회전 레트로 UI
    🎹 2차 MVP (AI 채보 & 연주)
    • Spotify Basic Pitch AI 기반 다성음(화음) 자동 분석
    • 3옥타브 QWERTY 가상 건반 실시간 연주 시스템
    • 원신/로아 등 인게임 악기 연주용 텍스트 악보 생성

     

     

     

    • "채보 버튼 누르고 5초간 멍때리는 속도": 파이썬 인터프리터가 켜지고 무거운 AI 패키지를 임포트하느라 매번 4~5초의 지연이 발생해 실시간 연주 감성이 뚝 끊겼습니다.
    • "배포 지옥 (너 파이썬 깔려 있어?)": 프로그램을 친구나 다른 유저에게 배포하려면 "파이썬 3.10 깔고, pip install basic-pitch 치고..." 같은 황당한 요구를 해야 했습니다.


     

    음원을 AI가 분석해서 채보한걸 연주 하는 화면

     

    "이럴 바엔 파이썬을 완전히 걷어내고 C# 안에서 직접 돌리자!"
    모델 내부를 뜯어보니 다행히 225KB 크기의 경량 ONNX 가중치 파일(nmp.onnx)로 구성되어 있었습니다. 이에 Microsoft.ML.OnnxRuntime을 활용해 외부 파이썬 의존성 없이 순수 C# 인메모리 상에서 100% 독립 실행되는 고속 엔진으로 전면 이식을 시작했습니다.

    🔄 C# AI 채보 엔진 동작 흐름

    • [1단계: 오디오 전처리] → NAudio로 모든 음원을 22,050Hz Mono 32-bit Float 파형으로 디코딩 및 고품질 리샘플링
    • [2단계: 슬라이딩 윈도잉] → 43,844 샘플 단위 윈도우 생성 (30프레임 오버랩 7,680 샘플, 3,840 샘플 제로 패딩)
    • [3단계: ONNX 인메모리 추론] → Microsoft.ML.OnnxRuntime 세션으로 윈도우별 Note[172, 88] 및 Onset[172, 88] 텐서 병렬 추론
    • [4단계: 윈도우 언랩 (Unwrap)] → 각 윈도우 앞뒤 15프레임씩 오버랩을 잘라내고 원본 시간축 행렬로 완벽 병합
    • [5단계: 논문 후처리 C# 포팅] → 120~2,200Hz BPF, 차분 온셋 보강, 시간역순 극댓값 피크 탐색, Melodia 트릭으로 다성부 화음 음표 추출
    • [6단계: QWERTY 악보 포맷팅] → 3옥타브 건반 매핑, 60ms 이내 동시 타건 화음 (AG) 괄호 결합 및 쉼표(-) 자동 표기

    ⚠️ 포팅 시 겪었던 실수 및 주의사항

    • ONNX 입력 텐서 차원 불일치: 모델 입력 형상이 [batch, 43844, 1] 3차원이므로 DenseTensor<float> 생성 시 차원을 정확히 일치시켜야 세션 에러가 안 납니다.

    • 윈도우 경계 오차 (Unwrap Slicing): 슬라이딩 윈도우 결과를 합칠 때 앞뒤 15프레임(총 30프레임)씩 오버랩을 잘라내지 않으면, 윈도우 경계선 부근에서 음표 타이밍이 어긋나 불협화음이 발생합니다.

    • 시간 변환 매직 넘버 보정: 프레임 인덱스를 실제 초(Seconds)로 변환할 때 윈도우 오프셋 공식(window_offset = (256/22050)*(172 - 43844/256) + 0.0018)을 빠뜨리지 않아야 원곡 리듬과 1ms 단위로 일치합니다.

    ✨ 실측 벤치마크 결과 & 인사이트

    파이썬 원본과 신규 C# 라이브러리에 동일한 복합 멜로디 음원(화음 + 16분음표 + 쉼표)을 넣고 1:1 비교 검증을 진행했습니다:

    • 품질 일치도: 추출된 노트 수(6개), 화음 (AG), 쉼표(-), 악보 텍스트 (AG) S F H - Q까지 100% 완벽 일치
    • 처리 속도: 기존 Python 약 4,500ms ➔ C# 인프로세스 약 220ms (약 20배 초고속화 달성! ⚡)
    • 배포 혁신: nmp.onnx 모델을 DLL 내부 EmbeddedResource로 탑재하여 외부 파일 누락 위험을 원천 차단
    // 1. 오디오 로드 및 22,050Hz 리샘플링
    float[] audioSamples = BasicPitchAudioResampler.LoadAndResample(audioFilePath);
    
    // 2. 인프로세스 ONNX 추론 (43,844 샘플 슬라이딩 윈도우)
    using var model = new BasicPitchModel();
    foreach (var window in windows)
    {
        var (noteOut, onsetOut) = model.RunWindow(window);
        windowOutputs.Add(noteOut);
        onsetOutputs.Add(onsetOut);
    }
    
    // 3. 윈도우 언랩 및 후처리 알고리즘 디코딩
    var frames = BasicPitchDecoder.UnwrapOutput(windowOutputs, originalLength);
    var onsets = BasicPitchDecoder.UnwrapOutput(onsetOutputs, originalLength);
    var rawEvents = BasicPitchDecoder.DecodeNotes(frames, onsets, onsetThresh: 0.55f, frameThresh: 0.35f);
    
    // 4. QWERTY 악보 포맷팅 (동시 타건 화음 묶음 처리)
    string sheet = FormatQwertySheet(notes); // 220ms 만에 완료!

    ☕ 마치며: 속도 혁신을 넘어, '장기적 유지보수성'을 잡다

    이번 C# 라이브러리 이식은 단순히 "속도를 20배(5초➔0.2초) 빠르게 만들었다"는 것 이상의 의미가 있었습니다.

    파이썬과 C#이라는 서로 다른 2개의 런타임 환경을 오가며 발생하던 의존성 충돌(Dependency Hell)과 배포의 불확실성을 완전히 걷어내고, .NET 단일 생태계로 코드를 일원화함으로써 장기적인 유지보수 비용을 획기적으로 낮췄습니다.

    동시에 기존 파이썬 코드는 버리지 않고 python_ver 별도 아카이브로 안전하게 격리 보존해 두어, 언제든 원본 알고리즘과 대조·검증할 수 있는 안정성까지 확보했습니다.

    결과적으로 "유저에게는 0.2초 만에 악보가 뜨는 쾌적한 경험을, 개발자에게는 배포 지옥 없는 깔끔하고 단단한 유지보수 환경"을 모두 챙길 수 있었습니다!

     

    하지만, 만들고 나서도 결국 유튜브를 찾게되는게 어쩔수 없나봅니다~!

    🔗 참고 자료

Designed by Tistory.