Привет, Хабр!

Представляю твоему вниманию AcroTab — мое приложение в соавторстве с opencode, которое должно быть под рукой у каждого уважающего себя гитариста (у меня, например, есть).

кг/ам tl;dr: https://acrotab.org (предупреждаю: уберите детей от экрана гитару подальше, затягивает)

Пролог (выгорание)

Я родился в час ночи 15 ноября, был 12 день растущей луны
Был осенний, как всегда пасмурный, свинцово‑серый день (а может и солнечный, не помню, но точно ноябрьский). Я сидел на кресле в офисе, левой рукой пил кофе из кружки с надписью «Кавалерово» (незадолго до этого я ездил туда проведать бабушку), а правой многозначительно гонял тачпадом курсор по экрану рабочего ноутбука. Мы называли это «шевелить мышкой» (кто «мы»?!).

Тянулся этот, маловероятно солнечный, день неспешно, примерно со скоростью песни Никольского Я сам из тех. А хотелось чтобы хотя бы как От любви к любви.

Шевелить мышкой было неприятно, тачпад постоянно лагал из‑за отслеживающего активность софта (который мы, кстати, и разрабатывали). Однако, не шевелить было нельзя — ведь тогда мне потом напишет начальник и спросит почему я ничего не делаю.

А делать было, как обычно, нечего: я уже подвинул ту кнопку вправо, продвинул задачу в тесты во всех 4 трекерах и поставил на сборку (сборка будет длиться всего лишь 2 часа, правда, если не упадет в конце). Курить я уже ходил 20 минут назад, надо потерпеть еще хотя бы полчасика.

В этот момент я понял, что больше не хочу этим заниматься. Написал по собственному, отработал 2 недели, еще пару недель ничего не делал, слетал в Тайланд (но это уже совсем другая история), отметил новый год, отоспался.

Еще три месяца к компу вообще не подходил (разве что Rocksmith или Neural DSP включить). Потом потихоньку начал открывать vscode, шариться по GitHub, пописывать код, но без особого энтузиазма. Однако, настроение мое улучшилось.

Чем мы займемся сегодня, Брэйн?

Итак, что мы имеем: похеренная карьера, мизерный запас денег, куча свободного времени и желание сделать, наконец, что‑то полезное. Осталось только придумать что.

Какие были идеи для приложения:

  • QRx — выбираем небольшой файл/текст/etc, кодируем в несколько QR‑кодов, на другом устройстве сшиваем обратно. Как прикол можно использовать вспышку и фронталку для сигнала от принимающего телефона, что код распознан.

  • Grider — наработки с прошлого безработного периода, мой фреймворк поверх vue, который отображает сериализованный грид с именоваными слотами, в которые вставляются виджеты (компоненты с описанием их api в моем формате). Прикол в том что грид мы можем подменять на горячую, абсолютный адаптив, красивые анимации перестройки грида и смены виджетов. До кучи интерфейс над всем этим, по сути no‑code редактор для веб‑приложений.

  • GitChat — мессенджер поверх GitHub или любого другого (gitflic вроде какой‑то есть) гит‑хостинга. По сути — гит клиент.

  • zLoop — модульный метроном, где из блоков можно составлять сложные ритмы.

  • Kozel Online — тоже из старых недоделок. Собственно, онлайн игра в козла. Бонусом редактор правил, чтобы создавать другие карточные игры.

Но делать их мне не захотелось, хотя некоторые идеи вполне актуальны, имхо.

Grider — на нем был сделан мой сайт, в целом, механизм — конфетка, но мне надоело пилить GUI, слишком много настроек надо реализовывать. Но теперь, когда я знаком с магией вайбкодинга, думаю, что скоро вернусь и доделаю.

QRx, zLoop — сомнительная польза, забил.

Kozel — столько воды утекло, мне уже не с кем в него играть)

GitChat — идея в том, чтобы обходить блокировки, но околонулевая анонимность. Но блин, даже сервер в теории не нужен, чисто паразитом быть, что‑то в этом есть.

Итого идеи вроде были, но реализовывать их не хотелось. Пока ждал вдохновения, насматривался на всякие библиотеки (например, Tone.js), лазил по GitHub, смотрел че люди вообще нонче делают, чертыхался на вездесcущий react. Статьи почитывал всякие.

И везде только и разговоров, что о вайбкодинге, агентах, llm'ах. Ну и в какой‑то момент наткнулся на Zen | OpenCode. И если раньше жаба давила заряжать денежку на какую‑то угадайку, то забесплатно уже можно и попробовать.

Для начала, скормил ей пару репозиториев, написанных на реакте, попросил переписать на vue. Она справилась. В итоге дал репу побольше (openDAW) и попросил выдернуть из нее Piano roll. Она что‑то делала целый день, постоянно падала (просто перестает что‑либо делать, пока не пнешь), в итоге уперлась в лимит на день. На следующий день я продолжил ее мучать, она «доделала» задачу. Результат не был похож на Piano Roll.

Примерно так выглядел результат
Из второй серии скриншот не стал делать.
Из второй серии скриншот не стал делать.

Вот тут стало понятно, что она слепая, и описывать визуальные ошибки ей бесполезно. Но, тем не менее, даже в огромной кодовой базе она очень быстро разбирается, и если сама не справляется, то, по крайней мере, может проконсультировать.

Так чем же мы займемся сегодня, Брэйн?

Отвечу песней.

Около 3х (трёх) лет назад я решил научиться играть на гитаре. Первые два года я гонял 3 (три) рифа металлики по вечерам, и особого успеха не поимел. А потом мне порекомендовали Rocksmith 2014 Remastered.

И это перевернуло мою жизнь И ничего особо не поменялось, просто вместо трёх (3) рифов металлики я начал гонять их песни целиком пару (2–3) раз в неделю. А потом не только металлику. Ну и со временем набрал себе плейлист из понравившихся песен и обзавелся привычкой играть рандомные песни в режиме Nonstop Play.

В общем, Rocksmith — отличная вещь, но у него есть ряд недостатков:

  • долго запускается

  • часто вылетает

  • требует специальный формат файлов

  • требует установки

  • нужен комп и нормальная звуковуха

  • если говорить про свежий Rocksmith+ — то там вообще подписка за 15 баксов в месяц, которые еще и брать не хотят из моих грязных рук.

Из бесплатных альтернатив натыкался только на Slopsmith, но пока я таки собрался его установить к ним пришел ubisoft и прикрыл лавочку по авторским соображениям (они использовали проприетарный формат psarc).

Ну и чёт захотелось мне сделать свой Rocksmith, с метрономом и тюнерами.

Так как я Señor frontend developer в запасе, писать решил на Quasar + capacitor для сборки под мобилку. Для работы с табулатурами использовал alphaTab, для работы с 3d (три‑д) использовал babylon.js, так как уже работал с ним. Для всего остального есть opencode, в частности Big Pickle — их бесплатная модель.

Итак, приступим

Плеер

По сути все приложение пляшет вокруг alphaTab, это он парсит табулатуру и он же ее озвучивает. Я завязался на его события и с помощью Big Pickle написал перегонятели в свой формат.

Основные типы
export interface NoteInfo {
  isSlideStart: boolean;
  isSlideEnd: boolean;
  slideInType: model.SlideInType;
  slideOutType: model.SlideOutType;
  slideTargetId: number | null;

  vibrato: model.VibratoType;

  isBendStart: boolean;
  isBendEnd: boolean;
  isBendHold: boolean;
  isPrebend: boolean;
  bendType: model.BendType;
  bendStyle: model.BendStyle;
  bendPoints: Array<{ offset: number; value: number }>;

  isHarmonic: boolean;
  harmonicType: model.HarmonicType;
  harmonicValue: number;

  isDead: boolean;

  isPalmMute: boolean;
  isLetRing: boolean;

  tieOriginId: number | null;
}

export interface EnrichedNote extends NoteInfo {
  id: number;
  string: number;
  fret: number;
  absoluteStart: number;
  absoluteEnd: number;
  masterBarStart: number;

  slideGroupId: number | null;
  bendGroupId: number | null;
  bendIndex: number | null;
  hammerPullGroupId: number | null;
  tieGroupId: number | null;
  letRingGroupId: number | null;
  palmMuteGroupId: number | null;
  accent: number;
}

export interface NoteGroup {
  id: number;
  type: EffectType;
  noteIds: number[];
  absoluteStart: number;
  absoluteEnd: number;
}

Самая неприятная часть работы — разбираться с alphaTab.

Например, его сайт нормально не открывается из России. Кроме того, их документация явно устарела, попытки подружить его с vite провалились (то что они в доке и примерах советуют не работает) — в итоге просто скопировал его исходники в проект. И не пожалел, потом нейронкой их допиливал под свои нужды.

AlphaTab хранит данные очень неприятно для обработки, поэтому я просто при загрузке трека перегонял их в 2 массива.

  • ноты (конкретно нота на грифе)

  • группы (легатные поливы, слайды/бенды/etc.)

Из интересных нюансов, alphaTab каким‑то хитрым образом реализует повторы кусков песни, пытаться с ним синхронизироваться было западло, поэтому я просто один раз перегоняю все ноты в абсолютные координаты (absoluteStart и absoluteEnd), подменяя мудреную логику на обычный массив с кучей дубликатов. Почти всю логику реализовал Big Pickle, я ему пару раз сказал что что‑то не так, он поправил, все отлично.

Собственно, массивы, очевидно, отсортированы по absoluteStart.

Ну и дальше все просто, имею currentTick, ноты имеют absoluteStart, вычитаю одно из другого — получаю координату z. Координаты x, y вычисляются из fret и string.

Ноты группируются по absoluteStart, назовем такую группу фреймом. Фреймы рисуются если входят в некий промежуток, примерно такой: [-50, RENDER_LENGTH]. Если в фрейме больше одной ноты — считаем это аккордом.

Когда доезжают до 0, оставляю координаты в 0, применяю всякие анимации (например, меняю scale). Доезжают до -20, начинаю уменьшать scale. Везде использую встроенные в babylon Lerp.

Помимо этого использую clipping pane (они нужны для легатных поливов, так как ноты я замораживаю в нуле, а вот длинные штуки надо обрезать).

Важно отметить, что легатные поливы рисуются назад во времени от последней входящей ноты (чтобы не исчезли с вместе с фреймом первой ноты).

Вот и весь алгоритм. Дальше только ветвления по типам нот и легатных приемов. Я сам реализовал только слайды, остальное скинул на Big Pickle.

При расчете координаты z еще домножаю на DENSITY, который можно менять кручением колесика или pinch zoom'ом.

В остальном, хренова туча обвязок вокруг alphaTab API

Нрааица
Нрааица

Работа с файлами

Подключаем папку fs, кешируем пути в indexedDB. Избранное/метадату треков храним в LocalStorage.

  • acro‑fav.json (если отсутствует, создается в корне подключенной папки) — хранит избранное

  • [имя песни].json создаются рядом с песнями и хранят данные о треках, строях, а также инфу по синхронизации с видео/аудио

Тюнер

Использовал pitchy, над ним некоторые манипуляции провел Big Pickle. Хорошо работает в линию, с микрофона похуже.

Тюнер в действии
  1. Выбираем строй

  2. PROFIT!

Метроном/секвенсор

Пляшет вокруг Tone.js

Все что нужно, чтобы научиться класть гвозди в ударку и не только.
Все что нужно, чтобы научиться класть гвозди в ударку и не только.

Функции:

  1. полноценный секвенсор;

  2. регулировка bpm;

  3. пресеты (встроенные и пользовательские);

  4. импорт/экспорт;

  5. можно выбирать семплы на дорожки;

  6. крутилка для свингеров.

Для староверов

Все мы любим Songsterr за огромную базу табулатур и темную тему. Он почти идеален, один движок звуковой чего стоит. Однако, зациклить фрагмент песни, изменить темп или транспонировать минусовку можно только за денюшку. Это... это печально.

Поэтому в моём бесплатном приложении есть возможность отображать табулатуры в обычном формате, выглядит примерно так:

Основные элементы управления

В меню выбора треков есть возможность mute/solo, ну и регуляторы громкости.

В общем, есть весь необходимый функционал для работы с табами. Также есть возможность использовать YouTube видео как playback.

Пример с видео Youtube

Если вы еще не поняли, получилась шоколадка. За такое по 10 баксов в месяц заряжают, а я Д'Артаньян а у меня бесплатно и без регистрации (и даже без рекламы).

Работает как PWA, есть еще apk (пока только на сайте, RuStore чёт не торопится модерировать).

Табулатуры в самом приложении не поставляются (по юридическим соображениям), зато есть пачка отборных табов с рутрекера в группе телеграм (ссылка есть в приложении).

Приложуха в андроиде назначается на форматы gp и midi. Но основной сценарий использования — подключить папку с табами и радоваться жизни.

Планы на будущее

  • переписать на $mol;

  • редактор для синхронизации с видео/аудиофайлами;

  • генератор рифов (будет собранную из кубиков последовательность по выбранному ладу);

  • генератор бэкинг‑трека;

  • набор уроков на разные техники игры (гаммы всякие, скачки между струнами, флажолеты и так далее);

  • отдельный «репозиторий табулатур»;

  • светлая тема;

  • плейлисты (типа нонстоп);

  • синхронизация 2х устройств.

Чуть более отдаленная перспектива:

  • vst‑бридж;

  • realtime распознаватель нот;

  • мини‑игры.

Но это если будет спрос, разумеется.

Не торопясь упасть

Спасибо, что дочитали! На данный момент приложение, на мой взгляд, полностью рабочее. Времени заняло 2,5 (два с половиной) месяца. В полезности я не сомневаюсь, UX будем прокачивать.

Так что милости прошу, приветствую конструктивную критику и рацпредложения. Вступайте в группу.

В завершение этого потока сознания Игра с огнем в моем исполнении:

Осторожно, тяжелый метал

PS. Дал прочитать сей опус дипсику, ему не понравилось. Значит, все сделал правильно!