Spaces:
Sleeping
Sleeping
| """ | |
| core/models.py | |
| ๋ด๋ถ ๋ฐ์ดํฐ ๊ตฌ์กฐ ์ ์. | |
| ๋ชจ๋ core ๋ชจ๋ ๊ฐ์ ๊ณต์ ๋๋ ๋ฐ์ดํฐ ํด๋์ค. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass, field | |
| from typing import Optional | |
| class NoteEvent: | |
| """ | |
| ์ ๋ณด์์ ์ถ์ถํ ๋จ์ผ ์ํ/์ผํ ์ด๋ฒคํธ. | |
| pitch=0 ์ด๋ฉด ์ผํ(rest)๋ก ์ทจ๊ธ. | |
| duration์ 4๋ถ์ํ ๊ธฐ์ค์ ๋น์จ (์: 1.0 = 4๋ถ์ํ, 0.5 = 8๋ถ์ํ, 2.0 = 2๋ถ์ํ). | |
| """ | |
| pitch: int # MIDI ๋ฒํธ (0 = rest, 60 = C4) | |
| start: float # ์์ ์๊ฐ (4๋ถ์ํ ๊ธฐ์ค beat) | |
| duration: float # ์ง์ ์๊ฐ (4๋ถ์ํ ๊ธฐ์ค beat) | |
| staff: int = 1 # ๋ณดํ ๋ฒํธ (1 = ์๋จ, 2 = ํ๋จ) | |
| voice: int = 1 # ์ฑ๋ถ ๋ฒํธ | |
| part_hint: Optional[int] = None # ํํธ ๋ฐฐ๋ถ ํํธ (None = ๋ฏธ์ง์ ) | |
| class ConvertOptions: | |
| """ | |
| ๋ณํ ํ์ดํ๋ผ์ธ ์ต์ . | |
| """ | |
| part_count: int = 0 # ์ถ๋ ฅ ํํธ ์ (0 = ๋์ ๋ฐ์ ์ ๊ธฐ์ค ์๋ ๊ฐ์ง) | |
| strict_mode: bool = False # True์ด๋ฉด ๊ฒฝ๊ณ ๋ฅผ ์๋ฌ๋ก ์ฒ๋ฆฌ | |
| prefer_sharps: bool = True # True = ์ฌ๋ฆผํ(#), False = ๋ด๋ฆผํ(b) | |
| mock_mode: bool = True # True์ด๋ฉด MockOMRAdapter ์ฌ์ฉ | |
| default_tempo: int = 120 # ๊ธฐ๋ณธ ํ ํฌ BPM | |
| pdf_dpi: int = 150 # PDF ๋ ๋๋ง ํด์๋ | |
| # 150dpi: ๋น ๋ฅธ ์ฒ๋ฆฌ, ๊ธฐ๋ณธ๊ฐ | |
| # 300dpi: Audiveris ์ค์ ์ฌ์ฉ ์ ๊ถ์ฅ (์ธ์๋ฅ ํฅ์) | |
| preprocess_enabled: bool = True # OpenCV ์ ์ฒ๋ฆฌ ์ฌ์ฉ ์ฌ๋ถ (audiveris ๋ชจ๋๋ง ์ ์ฉ) | |
| blur_enabled: bool = True # GaussianBlur ๋ ธ์ด์ฆ ์ ๊ฑฐ ์ฌ์ฉ ์ฌ๋ถ | |
| binarize_enabled: bool = False # ์ด์งํ ์ฌ์ฉ ์ฌ๋ถ (๊ธฐ๋ณธ off: Audiveris ์์ฒด ์ด์งํ ์ ๋ขฐ) | |
| binarize_method: str = "otsu" # ์ด์งํ ๋ฐฉ์: "otsu" | "adaptive" (binarize_enabled=True ์) | |
| deskew_enabled: bool = False # ๊ธฐ์ธ๊ธฐ ๋ณด์ (์คํ์ , ๊ธฐ๋ณธ off) | |
| debug_dir: str = "" # ์ค๊ฐ ๊ฒฐ๊ณผ๋ฌผ ์ ์ฅ ๋๋ ํ ๋ฆฌ (๋น ๋ฌธ์์ด = ์ ์ฅ ์ ํจ) | |
| engine: str = "" # OMR ์์ง ์ง์ : "" | "audiveris" | "homr" | "oemer" | "clarity" | |
| # ๋น ๋ฌธ์์ด์ด๋ฉด mock_mode์ ๋ฐ๋ผ ์๋ ์ ํ | |
| pdf_pages: list = field(default_factory=list) | |
| # ์ฒ๋ฆฌํ PDF ํ์ด์ง ๋ฒํธ ๋ชฉ๋ก (1-based). ๋น ๋ฆฌ์คํธ = ์ ์ฒด ์ฒ๋ฆฌ | |
| # ์) [1, 3] โ 1, 3ํ์ด์ง๋ง ์ฒ๋ฆฌ | |
| class ConvertResult: | |
| """ | |
| ๋ณํ ๊ฒฐ๊ณผ. | |
| mml: ๋ง๋น๋ ธ๊ธฐ MML ์์ฑ ๋ฌธ์์ด "MML@p1,p2,p3;" | |
| part1/2/3: ํํธ๋ณ ๋ณธ๋ฌธ ๋ฌธ์์ด (MML@, ; ์ ์ธ, ๋ด๋ถ ๊ฒ์ฌ์ฉ) | |
| """ | |
| success: bool | |
| mml: str = "" # ์ต์ข ๋ง๋น๋ ธ๊ธฐ MML "MML@[t<BPM>]p1,p2,p3;" | |
| part1: str = "" # Part 1 ๋ณธ๋ฌธ (๋ด๋ถ ๊ฒ์ฌ/๋๋ฒ๊ทธ์ฉ) | |
| part2: str = "" | |
| part3: str = "" | |
| warnings: list[str] = field(default_factory=list) | |
| debug_info: dict = field(default_factory=dict) | |
| def parts(self) -> list[str]: | |
| """ํํธ ๋ณธ๋ฌธ ๋ชฉ๋ก์ ๋ฆฌ์คํธ๋ก ๋ฐํ.""" | |
| return [self.part1, self.part2, self.part3] | |
| def format_output(self) -> str: | |
| """์ฝ์/ํ์ผ ์ถ๋ ฅ์ฉ ํฌ๋งท. ํํธ๋ณ ๊ฐ๋ณ MML@...;๋ก ์ถ๋ ฅ.""" | |
| lines = [] | |
| for i, mml in enumerate([self.part1, self.part2, self.part3], start=1): | |
| lines.append(f"Part {i}") | |
| lines.append(mml if mml else "MML@r1;") | |
| lines.append("") | |
| if self.warnings: | |
| lines.append("--- Warnings ---") | |
| for w in self.warnings: | |
| lines.append(f" [WARN] {w}") | |
| return "\n".join(lines).strip() | |
| class WarningMessage: | |
| """๊ฒฝ๊ณ ๋ฉ์์ง (ํ์์ ๊ตฌ์กฐํ๋ ๊ฒฝ๊ณ ๋ก ์ฌ์ฉ).""" | |
| code: str | |
| message: str | |
| context: Optional[str] = None | |
| # --------------------------------------------------------------------------- | |
| # ๋ฉํฐ ์์ง ๋น๊ต์ฉ ๋ฐ์ดํฐ ๊ตฌ์กฐ | |
| # --------------------------------------------------------------------------- | |
| class EngineRunResult: | |
| """ | |
| ๋จ์ผ OMR ์์ง ์คํ ๊ฒฐ๊ณผ. | |
| ์๋ ์ ์๋ง์ผ๋ก ์ต์ข ํ์ ์ ๋ด๋ฆฌ์ง ์๋๋ค. | |
| heuristic_summary๋ ์ฐธ๊ณ ์งํ์ผ ๋ฟ, ์ต์ข ํ์ง ํ๋จ์ ์ฌ์ฉ์๊ฐ ์ง์ ํ์ธํด์ผ ํ๋ค. | |
| """ | |
| engine_name: str | |
| success: bool | |
| stage: str = "" # ์คํจ ๋จ๊ณ: "init" | "preprocess" | "omr" | "parse" | "convert" | |
| warnings: list[str] = field(default_factory=list) | |
| error_message: str = "" | |
| # ์ถ๋ ฅ ํ์ผ ๊ฒฝ๋ก (์ ์ฅ๋ ๊ฒฝ์ฐ) | |
| output_xml_path: str = "" # ์์ง ์ถ๋ ฅ MusicXML ๊ฒฝ๋ก | |
| output_mml_path: str = "" # ์ ์ฅ๋ MML ํ ์คํธ ํ์ผ ๊ฒฝ๋ก | |
| output_notes_json_path: str = "" # ์ ์ฅ๋ notes.json ๊ฒฝ๋ก | |
| output_notes_txt_path: str = "" # ์ ์ฅ๋ notes.txt ๊ฒฝ๋ก | |
| output_debug_path: str = "" # ์ ์ฅ๋ debug.json ๊ฒฝ๋ก | |
| # ์ ๋ ์งํ (์ฐธ๊ณ ์ฉ) | |
| note_count: int = 0 | |
| chord_count: int = 0 # ๋์ ๋ฐ์ ๋ ธํธ ๊ทธ๋ฃน ์ | |
| part_note_counts: list[int] = field(default_factory=list) | |
| warning_count: int = 0 | |
| # ์์ธ ๋ฐ์ดํฐ | |
| debug_info: dict = field(default_factory=dict) | |
| heuristic_summary: dict = field(default_factory=dict) | |
| mml_parts: list[str] = field(default_factory=list) | |
| notes_dump: list[dict] = field(default_factory=list) # ์ฌ๋์ด ๊ฒํ ํ๊ธฐ ์ข์ ๋ ธํธ ๋ชฉ๋ก | |
| class ComparisonReport: | |
| """ | |
| ์ฌ๋ฌ OMR ์์ง ๋น๊ต ์คํ ๊ฒฐ๊ณผ ์ง๊ณ. | |
| ์ค์: user_review_priority=True ๋ ํญ์ True์ฌ์ผ ํ๋ค. | |
| ์ต์ข ์์ง ์ ํ์ ์ฌ์ฉ์๊ฐ ์ง์ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ด๋ณด๊ณ ํ๋จํ๋ค. | |
| suggested_engine์ ๋จ์ ์ฐธ๊ณ ์ฉ์ด๋ฉฐ ์๋ ๊ฒฐ์ ์ด ์๋๋ค. | |
| """ | |
| input_file: str | |
| timestamp: str = "" | |
| runs: list[EngineRunResult] = field(default_factory=list) | |
| user_review_priority: bool = True # ํญ์ True โ ์ต์ข ํ์ ์ ์ฌ์ฉ์ ์ง์ ํ์ธ | |
| comparison_summary: str = "" | |
| notes_for_manual_review: list[str] = field(default_factory=list) | |
| suggested_engine: str = "" # ์๋ ์ฐธ๊ณ ์ถ์ฒ (์ต์ข ํ์ ์๋) | |