# PLUS — расширение MuScriptor Пакет пост-обработки транскрипции. Ни одна стадия не требует GPU и не трогает веса: на вход идёт MIDI (и по желанию аудио), на выходе MIDI, WAV и отчёт. Работает поверх любого движка: MuScriptor Large, Aria-AMT. ## Что внутри | Файл | Что делает | | --- | --- | | `plus_core.py` | нотная модель, чтение/запись MIDI, темп по сетке, `explained_fraction`, `ambiguity_of` | | `plus_transcribe.py` | стадии: cleanup, quantize, hands, pedal, chords, dynamics, report | | `plus_ensemble.py` | слияние нескольких транскрипций голосованием + оценка P/R/F1 | | `plus_render.py` | MIDI в WAV своим синтезатором, чтобы слушать до DAW | | `plus_batch.py` | обработка папки, возобновление, сводка в CSV | ## Стадии **cleanup** — унисоны (тот же питч в пределах 45 мс с перекрытием > 60 %), фантомы (нота короче 120 мс и тише медианы на 35, включая одиночные редкие питчи), обрезка длительности до следующей ноты того же питча. **quantize** — посадка онсетов и длительностей на сетку 1/4 доли с силой 0..1 и свингом. Темп берётся из файла: угадывание кратного по бинарным онсетам математически неоднозначно, поэтому при объяснении сетки ниже 0.7 в отчёт идёт предупреждение `tempo_ambiguous` плюс список альтернатив (`bpm/2`, `bpm*2`, `bpm*3`), а не тихая подмена. **hands** — правая и левая рука жадным проходом по стоимости перехода (питч плюс время) с проверкой, что левая не уехала выше правой на октаву. **pedal** — вывод CC64 по смене гармонии: онсеты кластеризуются в аккорды (окно 60 мс), педаль держится пока набор высотных классов не сменился или не истёк `max_hold` (1.6 с по умолчанию). Ноты продлеваются внутри окна, но не дальше следующей ноты того же питча. **chords** — аккорды по одновременным онсетам, распознавание по шаблонам (`m`, `7`, `maj7`, `dim`, `aug`, `sus2`, `sus4`, `6`, `m6`) и подписи в отчёте. **dynamics** — громкость из аудио: RMS в окне 250 мс от онсета, перцентильный share, степень 0.65, множитель акцента по атаке, кривая в velocity 15..118. Отдельный баланс рук: левая на 0.88, правая на 1.0, чтобы аккомпанемент не перекрывал мелодию. **report** — метрики (нот, длительность, плотность, диапазон, медиана длительности), аномалии (перекрытия, слишком короткие, всплески плотности, плоская громкость) и разбивка по дорожкам. ## Замеры на синтетике с известной правдой Собрано 131 нота: мелодия восьмыми при 72 BPM плюс аккорды каждую долю, джиттер 10 мс, и три вида мусора (нота 10 мс, унисон, тихий фантом). ``` cleanup short_removed=1, unisons_merged=1, ghosts_removed=1 -> ровно 128 нот quantize explained_before=0.992 -> explained_after=1.000, темп 72 из файла hands left=96, right=32 (аккорды 32*3 в левой, мелодия 32 в правой) pedal spans=31, median_hold=0.432 с chords 32 кластера, 32 распознаны report overlaps=0, octave_leaps=0 ``` Ансамбль: три «движка» по 10 % потерянных нот и 4 фантома каждый. ``` F1 слияния 0.992 против лучшего входа 0.949 (muscriptor) точность/полнота слияния 0.99/0.99, ошибка онсета ~10 мс ``` Скорость: пачка из трёх MIDI (включая 30 000 нот) за 1.6 с; рендер 8 секунд звука за 0.27 с. ## Использование ```bash # полный проход по одному файлу python3 plus_transcribe.py --midi take.mid --audio take.wav \ --out take_plus.mid --report take_plus.json # только ритм и руки, без громкости python3 plus_transcribe.py --midi take.mid --stages cleanup,quantize,hands # слияние MuScriptor + Aria и оценка против эталона python3 plus_ensemble.py --inputs mus.mid aria.mid tk.mid \ --out merged.mid --reference truth.mid # послушать результат python3 plus_render.py --midi merged.mid --out merged.wav # папка целиком, с возобновлением python3 plus_batch.py --input-dir takes --out-dir takes_plus ``` ## Ограничения, о которых честно - Темп у бинарных онсетов неоднозначен: чистые восьмые при 72 BPM и четверти при 144 дают одинаковую сетку. Скрипт об этом пишет, а не делает вид, что знает. - Педаль и аккорды эвристические: на плотной педали или кластерах вне мажора и минора возможны ошибки, отчёт это показывает (`named` против `clusters`). - `dynamics` требует ту же запись, с которой снят MIDI; перекрывающиеся ноты делят энергию окна, поэтому громкость относительная, а не абсолютная. - Квантование при `strength 1.0` убивает человеческое дрожание: для живой игры держите 0.7..0.9. ## PLUS Studio — веб-интерфейс `plus_app.py` поднимает страницу на FastAPI: загрузка аудио или MIDI, чекбоксы стадий, ползунки силы квантования и свинга, слияние нескольких файлов, скачивание MIDI, превью WAV и отчёта JSON. Модель грузится лениво при первом запросе, веса берутся из `Nokia123543/muscriptor-large-fork` (или из `--weights`). python plus_app.py --port 8900 # CPU или GPU автоматически python plus_app.py --port 8900 --device cuda --preload --weights /content/model.safetensors Эндпоинты: `GET /health`, `POST /transcribe` (аудио), `POST /process` (MIDI), `POST /merge` (несколько MIDI), `GET /jobs/{id}` (состояние и лог), `GET /jobs/{id}/result.mid|preview.wav|report.json`.