| """Konwersja wygenerowanych melodii ABC -> MIDI (fortepian) przez music21. |
| Każda melodia osobno; błędy parsowania nie wywalają całości. |
| """ |
| import sys, re |
| from music21 import converter, instrument |
|
|
| def sanitize(abc: str) -> str: |
| """Usuwa powtórzenia/volty z ciała, ALE ZOSTAWIA akordy [ace]. Nagłówki nietknięte.""" |
| out = [] |
| for ln in abc.split("\n"): |
| if re.match(r"^[A-Za-z]:", ln): |
| out.append(ln); continue |
| b = re.sub(r"\[[12]", "", ln) |
| b = b.replace(":", "") |
| b = re.sub(r"\|\s*[12]", "|", b) |
| b = re.sub(r"\|+", "|", b) |
| b = re.sub(r"\^=|=\^|_=|=_", lambda m: m.group(0).replace("=", ""), b) |
| out.append(b) |
| return "\n".join(out) |
|
|
| def to_midi(abc: str, out_path: str, inst=None) -> bool: |
| try: |
| score = converter.parse(sanitize(abc), format="abc") |
| |
| notes = score.flatten().notesAndRests.stream() |
| if inst is not None: |
| notes.insert(0, inst) |
| notes.write("midi", fp=out_path) |
| return True |
| except Exception as e: |
| print(f" [pominięto] {out_path}: {type(e).__name__}: {e}") |
| return False |
|
|
| def main(): |
| sys.stdout.reconfigure(encoding="utf-8") |
| infile = sys.argv[1] if len(sys.argv) > 1 else "data/generated.abc" |
| prefix = sys.argv[2] if len(sys.argv) > 2 else "data/jig" |
| text = open(infile, encoding="utf-8").read() |
| tunes = [t.strip() for t in text.split("\n\n") if "X:" in t] |
| print(f"melodii do konwersji: {len(tunes)} (z {infile})") |
| ok = 0 |
| for i, tune in enumerate(tunes, 1): |
| path = f"{prefix}_{i}.mid" |
| if to_midi(tune, path): |
| print(f" OK -> {path}") |
| ok += 1 |
| print(f"\ngotowe: {ok}/{len(tunes)} plików MIDI (fortepian) w data/") |
|
|
| if __name__ == "__main__": |
| main() |
|
|