stella-score-reader / docs /frontend-integration-guide.md
CAY96
update docs
bc8019c

Frontend Integration Guide β€” 단일 μŠ€νƒœν”„ Analyze API (v1)

POST /analyze λ₯Ό ν˜ΈμΆœν•΄ ν•œ 쀄 μ˜€μ„  악보 사진(1μž₯ λ˜λŠ” μ—¬λŸ¬ μž₯)을 보내고, λ³‘ν•©λœ 단일 λ©œλ‘œλ”” JSON을 λ°›λŠ”λ‹€. 이전 닀쀑 μŠ€νƒœν”„Β·ν•©μ°½ μŠ€νŽ™κ³Ό ν˜Έν™˜λ˜μ§€ μ•ŠλŠ”λ‹€(ν•„λ“œ ꡬ쑰가 λ‹¨μˆœν™”λ¨).

상세 계약: docs/analyze-api-spec.md (SSOT).


1) ν•œ 쀄 μš”μ•½

ν•­λͺ© λ‚΄μš©
μž…λ ₯ 이미지 ν•œ μž₯λ‹Ή staff ν•œ μ€„λ§Œ μžˆλ‹€κ³  κ°€μ •
Clef / μ‘°ν‘œ 이미지에 없을 수 있음 β†’ score_context 둜 ν•„μˆ˜ 전달
ν™”μŒ μ΅œλŒ€ 2μ„±λΆ€; 응닡 λ©œλ‘œλ””λŠ” 항상 졜고음만
μ—¬λŸ¬ μž₯ μ—…λ‘œλ“œ μˆœμ„œ = 악보 μ§„ν–‰ μˆœμ„œ; μ„œλ²„λŠ” μž₯별 뢄석 ν›„ melody.events ν•˜λ‚˜λ‘œ 병합
μž¬μƒ 핡심 timeline + melody.events + (ν‘œμ‹œμš©) score_context

2) μš”μ²­ λ§Œλ“€κΈ°

2.1 multipart/form-data

  • score_context (ν•„μˆ˜): JSON λ¬Έμžμ—΄ ν•œ 덩어리.
    • μ΅œμ†Œ: { "clef": "treble", "key_signature": { "fifths": 0 } }
    • 선택: time_signature, tempo_bpm_reference, divisions (κΈ°λ³Έ 4/4, null, 4)
  • files: 같은 ν•„λ“œλͺ…μœΌλ‘œ μ—¬λŸ¬ 번 λΆ™μ΄κ±°λ‚˜, 단일 file μ‚¬μš©.
  • options: JSON λ¬Έμžμ—΄. 보톡 { "return_debug": false, "quantization": "1/8" }.

2.2 fetch μ˜ˆμ‹œ (λΈŒλΌμš°μ €)

const scoreContext = {
  clef: "treble",
  key_signature: { fifths: -1 },
  time_signature: "4/4",
  tempo_bpm_reference: 72,
  divisions: 4,
};

const form = new FormData();
form.append("score_context", JSON.stringify(scoreContext));
form.append("options", JSON.stringify({ return_debug: false, quantization: "1/8" }));

for (const file of fileListFromInput) {
  form.append("files", file, file.name);
}

const res = await fetch("/analyze", { method: "POST", body: form });
const data = await res.json();
if (!res.ok) throw new Error(JSON.stringify(data));

2.3 curl μ˜ˆμ‹œ

curl -X POST http://localhost:8000/analyze \
  -F 'score_context={"clef":"treble","key_signature":{"fifths":-1},"time_signature":"4/4","divisions":4}' \
  -F 'options={"return_debug":false,"quantization":"1/8"}' \
  -F "files=@sample_imgs/sample_oneline_0.png" \
  -F "files=@sample_imgs/sample_oneline_chord.png"

3) 응닡 읽기

3.1 μ΅œμ†Œ ꡬ독 ν•„λ“œ

  • melody.events: μž¬μƒΒ·νŽΈμ§‘μ˜ 본체. μ‹œκ°„ 순.
  • timeline.divisions: duration_div / onset_div 의 척도(ν•œ 4λΆ„μŒν‘œ = divisions).
  • timeline.time_signature: λ§ˆλ”” 길이 해석·UI용.
  • score_context: μ‘°ν‘œΒ·clef ν‘œμ‹œ(μ„œλ²„ 에코; ν΄λΌμ΄μ–ΈνŠΈκ°€ 보낸 값이 μ •λ‹΅).

3.2 이벀트 νƒ€μž…

  • note: step, octave, alter, duration_div, onset_div ν•„μˆ˜. pitch_midi κ°€ 있으면 μž¬μƒμ— μ‚¬μš© κ°€λŠ₯.
  • rest: duration_div, onset_div 만으둜 μΆ©λΆ„.

μ‹œκ°„ 계산:

  • μ ˆλŒ€ μ‹œκ°(초)은 μ„œλ²„κ°€ κ°•μ œν•˜μ§€ μ•ŠλŠ”λ‹€.
  • beat = onset_div / divisions 둜 λ§ˆλ”” λ‚΄ μœ„μΉ˜λ₯Ό 계산할 수 μžˆλ‹€.

3.3 μ—¬λŸ¬ μž₯ 이미지 UI 연동

  • segment_map: 각 원본 이미지가 melody.events 의 μ–΄λŠ 인덱슀 ꡬ간에 ν•΄λ‹Ήν•˜λŠ”μ§€.
  • events[].segment_order / bbox: νŠΉμ • μŒμ„ 원본 크둭 μœ„μ— ν•˜μ΄λΌμ΄νŠΈν•  λ•Œ μ‚¬μš©(없을 수 있음).

3.4 warnings

  • 치λͺ…적이지 μ•Šμ•„λ„ ν’ˆμ§ˆ μ €ν•˜λ₯Ό λ‚˜νƒ€λ‚Ό 수 μžˆλ‹€.
  • UIμ—μ„œλŠ” ν† μŠ€νŠΈ/λ°°μ§€λ‘œ λ…ΈμΆœν•˜κ³ , μ‚¬μš©μžμ—κ²Œ β€œμžλ™ 인식이 λΆˆμ•ˆμ •ν•  수 μžˆμŒβ€μ„ μ•ˆλ‚΄ν•˜λŠ” μš©λ„λ₯Ό ꢌμž₯.

4) 였λ₯˜ 처리

μ½”λ“œ ν”„λ‘ νŠΈ μ•‘μ…˜ μ˜ˆμ‹œ
400 score_context / options / 파일 ν•„λ“œ 검증
413 μ—…λ‘œλ“œ μ „ ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μš©λŸ‰ 사전 차단
415 ν™•μž₯자 ν•„ν„°
422 β€œμ΄ 사진은 ν•œ 쀄 μ•…λ³΄λ‘œ μΈμ‹λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€β€ + 재촬영 μ•ˆλ‚΄

5) λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ λ©”λͺ¨ (κΈ°μ‘΄ ν”„λ‘ νŠΈ λŒ€λΉ„)

  • detection.staffs[].voices[] ꡬ쑰 제거 β†’ melody 단일 λΈ”λ‘μœΌλ‘œ 톡합.
  • score_type_estimate, is_accompaniment, include_accompaniment μ˜΅μ…˜: v1 μŠ€νŽ™ μ—†μŒ.
  • Clef/key λŠ” 더 이상 β€œμ„œλ²„ 좔정”이 μ•„λ‹ˆλΌ ν΄λΌμ΄μ–ΈνŠΈκ°€ score_context 둜 μ œκ³΅ν•΄μ•Ό 함.

6) 참고 파일

  • docs/analyze-api-spec.md β€” ν•„λ“œΒ·μ˜λ―ΈΒ·κ²½κ³  μ½”λ“œ 전체
  • docs/analyze-response-format-examples.json β€” 볡뢙 κ°€λŠ₯ν•œ μ˜ˆμ‹œ JSON