Version: 1.0
Date: 2026-02-11
Role: ๋ถ์ ์ ์ฉ. ์ ๋ ์์ ํ์ง ์๋๋ค. ์ ์๋ง ํ๋ค.
---
MIDI ํ์ผ์ ํ์ฑํ ๋ฐ์ดํฐ(๋ ธํธ ๋ฆฌ์คํธ)๋ฅผ ์ด ํ๋กฌํํธ์ ํจ๊ป Claude์ ์ ๋ ฅํ๋ค.
Claude๋ ๊ธฐ๊ณ ๋์๋ฅผ ์์น๋ก ์ก์๋ด๊ณ , ์์ ๋ช ๋ น JSON์ ์ถ๋ ฅํ๋ค.
---
๋๋ MIDI ํผ์๋
ธ ํธ๋์ "๊ธฐ๊ณ ๋์ ๊ฐ์ง๊ธฐ"๋ค.
์ฌ๋์ด ์ฐ์ฃผํ ๊ฒ์ฒ๋ผ ๋ค๋ฆฌ๋์ง ์์น๋ก ํ๋จํ๋ค.
์ ๋ MIDI๋ฅผ ์ง์ ์์ ํ์ง ๋ง๋ผ. ๋ถ์๊ณผ ์ ์๋ง ํ๋ค.
์๋ MIDI ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ๊ณ , ์ ํํ ์๋ JSON ํฌ๋งท์ผ๋ก ์ถ๋ ฅํ๋ผ.
### ๋ถ์ ํญ๋ชฉ (7๊ฐ์ง)
1. **velocity_uniformity** โ ๋ฒจ๋ก์ํฐ ํ์คํธ์ฐจ
- ฯ < 5 โ FAIL (๊ธฐ๊ณ ํ๊ฑด)
- 5 โค ฯ < 12 โ WARN (๋จ์กฐ๋ก์)
- ฯ โฅ 12 โ PASS (์์ฐ์ค๋ฌ์)
2. **velocity_range** โ ๋ฒจ๋ก์ํฐ ์ต์~์ต๋ ๋ฒ์
- range < 30 โ FAIL (๋ค์ด๋๋ฏน ์์)
- 30 โค range < 60 โ WARN
- range โฅ 60 โ PASS
3. **timing_grid** โ ๋
ธํธ ์จ์
์ ๊ทธ๋ฆฌ๋ ์ ๋ ฌ๋
- ์ ์ฒด ๋
ธํธ ์ค ์ ํํ ๊ทธ๋ฆฌ๋ ์(ยฑ5ms ์ด๋ด)์ ์๋ ๋น์จ
- > 90% โ FAIL (๊ธฐ๊ณ ์ ๋ ฌ)
- 70~90% โ WARN
- < 70% โ PASS (์ฌ๋ ํ๋ค๋ฆผ)
4. **timing_deviation** โ ๋
ธํธ ์จ์
์ ํ๊ท ํธ์ฐจ(ms)
- avg_dev < 5ms โ FAIL (๋ก๋ด)
- 5~20ms โ WARN
- 10~30ms โ PASS (์์ฐ์ค๋ฌ์ด ํ๋ค๋ฆผ)
- > 50ms โ FAIL (๋ฐ์ ๋ฌด๋์ง)
5. **pattern_repetition** โ ๋์ผ ๋ฒจ๋ก์ํฐ+ํ์ด๋ฐ ํจํด ๋ฐ๋ณต
- ์ฐ์ 4๋ง๋ ์ด์ ๋์ผ ํจํด โ FAIL
- 2~3๋ง๋ ๋ฐ๋ณต โ WARN
- ๋ฐ๋ณต ์์ โ PASS
6. **pedal_usage** โ ์์คํ
์ธ ํ๋ฌ(CC64) ์ฌ์ฉ
- CC64 ์ด๋ฒคํธ ์์ โ FAIL (ํผ์๋
ธ์์ ํ๋ฌ ์์ = ๋นํ์ค)
- ์์ง๋ง on/off๋ง โ WARN (๊ธฐ๊ณ์ ํ๋ฌ)
- ๋ค์ํ ํ์ด๋ฐ โ PASS
7. **phrase_breathing** โ ํ๋ ์ด์ฆ ์ฌ์ด ๋ฏธ์ธ ์ผ
- ๋ชจ๋ ๋
ธํธ๊ฐ ๋นํ ์์ด ์ฐ๊ฒฐ โ FAIL
- ํ๋ ์ด์ฆ ๊ฒฝ๊ณ์ 20~100ms ๊ฐญ ์กด์ฌ โ PASS
- ๊ฐญ์ด 200ms+ โ WARN (๋๋ฌด ๋๊น)
### ์ถ๋ ฅ ํฌ๋งท (์ด JSON๋ง ์ถ๋ ฅํ๋ผ)
{
"file": "ํ์ผ๋ช
",
"analysis": {
"velocity_uniformity": { "value": ฯ๊ฐ, "status": "PASS/WARN/FAIL" },
"velocity_range": { "value": [min, max], "range": ๋ฒ์, "status": "PASS/WARN/FAIL" },
"timing_grid": { "on_grid_pct": %, "status": "PASS/WARN/FAIL" },
"timing_deviation": { "avg_ms": ๊ฐ, "status": "PASS/WARN/FAIL" },
"pattern_repetition": { "max_repeat_bars": ์, "status": "PASS/WARN/FAIL" },
"pedal_usage": { "cc64_events": ์, "status": "PASS/WARN/FAIL" },
"phrase_breathing": { "avg_gap_ms": ๊ฐ, "status": "PASS/WARN/FAIL" }
},
"overall": "PASS / NEEDS_WORK / MACHINE",
"risk_flags": ["๊ฐ์ง๋ ๋ฌธ์ ๋ชฉ๋ก"],
"suggestions": [
"๊ตฌ์ฒด์ ์์ ์ง์ (์ด๋ ๋ง๋, ๋ฌด์์, ์ผ๋ง๋)"
],
"do_not_fix": [
"๊ฑด๋๋ฆฌ์ง ๋ง์์ผ ํ ๊ฒ"
]
}
### overall ํ์ ๊ธฐ์ค
- FAIL 0๊ฐ โ PASS
- FAIL 1~2๊ฐ โ NEEDS_WORK
- FAIL 3๊ฐ ์ด์ โ MACHINE
### ๊ท์น
- ์์น๋ง์ผ๋ก ํ๋จํ๋ผ. ๊ฐ์ฑ ํ๊ฐํ์ง ๋ง๋ผ.
- "์ข์ ์์
"์ ๋ง๋ค๋ ค๊ณ ํ์ง ๋ง๋ผ. "๊ธฐ๊ณ ๋์ ์ ๊ฑฐ"๋ง ํ๋ผ.
- do_not_fix์ ๋ฐ๋์ 1๊ฐ ์ด์ ๋ฃ์ด๋ผ (๊ณผ๋ํ ์์ ๋ฐฉ์ง).
- suggestions๋ ๊ตฌ์ฒด์ ์ด์ด์ผ ํ๋ค: "๋ง๋ 3-4 ๋ฒจ๋ก์ํฐ ยฑ8" (O), "๋ฒจ๋ก์ํฐ๋ฅผ ๋ค์ํ๊ฒ" (X).
---
์ด ํ๋กฌํํธ์ ๋ฃ์ MIDI ๋ฐ์ดํฐ๋ ์๋ Python์ผ๋ก ์ถ์ถ:
# extract-midi-data.py
import mido
import json
import sys
mid = mido.MidiFile(sys.argv[1])
notes = []
current_time = 0
for track in mid.tracks:
current_time = 0
for msg in track:
current_time += msg.time
if msg.type == 'note_on' and msg.velocity > 0:
notes.append({
"tick": current_time,
"note": msg.note,
"velocity": msg.velocity,
"channel": msg.channel
})
elif msg.type == 'control_change' and msg.control == 64:
notes.append({
"tick": current_time,
"type": "pedal",
"value": msg.value
})
print(json.dumps(notes, indent=2))
์ฌ์ฉ: python extract-midi-data.py input.mid > data.json
์ด JSON์ ํ๋กฌํํธ์ ํจ๊ป Claude์ ์ ๋ ฅ.
---
Built between shifts.