Vocal Visualizer — 오선보 없는 성악 훈련 영상
악보 못 읽는 사람이 클래식 가곡을 부를 수 있게 만드는 영상 포맷
한 줄 요약
저작권 프리 클래식 MIDI → FluidSynth 반주 렌더링 → 피치+가사 시각화 영상 → YouTube 업로드
문제 정의
- 오선보를 읽지 못하는 사람은 성악곡의 음 높낮이를 알 수 없다
- 가라오케는 대중가요 전용이고, 클래식 가곡용은 없다
- 외국어 가곡(독일어, 이탈리아어, 라틴어)은 발음 장벽까지 있다
- 교회 성가대, 성악 입문자, 합창단 초심자가 혼자 연습할 도구가 없다
해결책
화면 구성
┌─────────────────────────────────────────────┐
│ │
│ ♪ Heidenröslein (들장미) │ ← 곡 제목
│ Franz Schubert, Op.3 No.3 │ ← 작곡가
│ │
│ ────────────────────────────────────── │
│ │
│ 자 아 인 크나 │ ← 한글 발음
│ Sah ein Knab' ein │ ← 원어 가사
│ ● │
│ ● │ ← 피치 높낮이
│ ● │ (높은 음 = 위)
│ ● │ (낮은 음 = 아래)
│ │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ ← 진행 바
│ ▶ 0:32 / 2:15 │
│ │
└─────────────────────────────────────────────┘
재생 중:
- 현재 음절 = 볼드 + 하이라이트
- 마커(●)가 피치 위치에 표시
- 지나간 음절 = 회색 처리
- 다음 음절 = 미리보기 (연한 색)
핵심 기능
| 기능 | 설명 |
| 피치 높낮이 시각화 | 음절마다 Y축 위치 = MIDI note 피치. 높은 음은 위, 낮은 음은 아래 |
| 실시간 마커 | 현재 불러야 할 음절에 볼드 + 마커 표시 |
| 한글 발음 표기 | 외국어 가사 위에 한글 발음. 수작업 교정 포함 |
| 원어 가사 | 독일어/이탈리아어/라틴어 원문 그대로 |
| 반주 오디오 | FluidSynth MIDI 렌더링 (피아노/오르간) |
대상 레퍼토리
즉시 가능 (PD + MIDI 확보)
| | | 장르 | 예시 | 길이 | 난이도 |
| | | 독일 가곡 (Lied) | Schubert 들장미, 보리수, 송어 | 2~4분 | 입문 |
| | | 이탈리아 아리아 | Caro mio ben, O sole mio (전통곡) | 2~3분 | 입문 |
| | | 라틴 종교음악 | Ave Maria, Panis Angelicus | 3~5분 | 중급 |
| | | 한국 가곡 | 동심초, 가고파, 그리운 금강산 | 3~4분 | 입문 |
| | | 찬송가 | Amazing Grace, 주 하나님 지으신 | 2~3분 | 입문 |
저작권 기준
- 작곡가 사후 70년 경과 = Public Domain
- MIDI 파일 = PD MIDI만 사용 (MIDI-SOURCES.md 레지스트리)
- 가사 = 원어 PD, 한글 발음은 자체 제작
- 반주 = FluidSynth 렌더링 (자체 생산)
제작 파이프라인
[1] 선곡 + MIDI 확보
└── MIDI-SOURCES.md에서 PD 곡 선택
└── 없으면 MuseScore에서 PD 악보 → MIDI 내보내기
[2] 가사 + 발음 매핑 (수작업 포함)
├── 원어 가사 입력
├── 자동 한글 발음 변환 (초안)
└── ★ 사용자 수작업 교정 ← 여기서 품질 결정
[3] MIDI → pitch_timeline.json 추출
├── 각 음표: {time_ms, note, pitch_hz, duration_ms}
└── 각 음절: {syllable_original, syllable_korean, time_ms}
[4] FluidSynth → 반주 WAV 렌더링
└── 피아노/오르간 사운드폰트
[5] pitch_timeline.json → 프레임 생성 (parksy-image)
├── 가사 배치 (원어 + 한글 발음)
├── 피치 높낮이 Y축 매핑
├── 마커 + 볼드 하이라이트
└── 프레임 PNG 시퀀스
[6] FFmpeg: 프레임 + WAV → MP4
[7] YouTube @visualizer-parksy 업로드
크로스 레포 역할
| | 단계 | 레포 | 이유 |
| | [1]~[4] | **parksy-audio** (마스터) | MIDI 원천, 오디오 렌더링, 피치 데이터 추출 |
| | [5]~[6] | **parksy-image** (영상화) | 프레임 생성, FFmpeg 영상 합성 |
| | [7] | **parksy-image** | YouTube 업로드 파이프라인 |
데이터 인터페이스 (audio → image)
{
"title": "Heidenröslein",
"composer": "Franz Schubert",
"opus": "Op.3 No.3",
"language": "de",
"duration_ms": 135000,
"audio_file": "heidenroslein_piano.wav",
"timeline": [
{
"time_ms": 2400,
"duration_ms": 600,
"midi_note": 67,
"pitch_hz": 392.0,
"syllable": "Sah",
"pronunciation_kr": "자",
"is_rest": false
},
{
"time_ms": 3000,
"duration_ms": 600,
"midi_note": 69,
"pitch_hz": 440.0,
"syllable": "ein",
"pronunciation_kr": "아인",
"is_rest": false
}
]
}
발음 교정 워크플로우
[자동] MIDI + 가사 → 음절 분리 → 한글 자동 변환 (초안)
↓
[수동] 사용자가 pronunciation_kr 필드 교정
"Knab'" → "크나프" (자동) → "크나" (교정)
"Röslein" → "뢰슬라인" (자동) → "뢰슬라인" (OK)
↓
[확정] 교정된 JSON → 영상 렌더링
교정 도구: JSON 직접 편집 또는 간단한 HTML 에디터 (추후)
시리즈 구성안
시즌 1: 성악 입문 10곡
| | | # | 곡명 | 언어 | 작곡가 |
| | | 1 | Heidenröslein (들장미) | 독일어 | Schubert |
| | | 2 | Caro mio ben | 이탈리아어 | Giordani |
| | | 3 | Ave Maria | 라틴어 | Gounod/Bach |
| | | 4 | 동심초 | 한국어 | 이수인 |
| | | 5 | Amazing Grace | 영어 | Traditional |
| | | 6 | An die Musik (음악에게) | 독일어 | Schubert |
| | | 7 | Voi che sapete | 이탈리아어 | Mozart |
| | | 8 | Panis Angelicus | 라틴어 | Franck |
| | | 9 | 가고파 | 한국어 | 김동진 |
| | | 10 | Danny Boy | 영어 | Traditional |
채널 전략
- 채널: @visualizer-parksy
- 형식: 2~4분 영상
- 썸네일: 곡 제목 + 피치 시각화 프리뷰
- 설명란: 원어 가사 전문 + 한글 발음 전문
평가
| 항목 | 점수 |
| 독창성 | 90 |
| 실현 가능성 | 85 |
| 저작권 안전 | 95 |
| 시장성 | 80 |
| 파이프라인 궁합 | 92 |
| 콘텐츠 확장성 | 85 |
| 발음 교정 (수작업 플러그) | +5 |
| **총점** | **93/100** |
필요 신규 코드
| | 모듈 | 레포 | 기능 |
| | `midi_to_timeline.py` | parksy-audio | MIDI 파싱 → pitch_timeline.json |
| | `lyric_mapper.py` | parksy-audio | 가사 음절 분리 + 자동 한글 변환 초안 |
| | `vocal_frame_generator.py` | parksy-image | timeline JSON → 프레임 PNG 시퀀스 |
| | `vocal_video.py` | parksy-image | 프레임 + WAV → MP4 (FFmpeg) |
상태: 컨셉 확정 — 구현 대기
작성: 2026-02-23