- Заметки
- Создание бит-машины во Flutter
За последние 30 дней: 2 просмотра, 2 посетителя.
Создание бит-машины во Flutter
Эта статья о том, как построить бит-машину с секвенсором в Dart/Flutter.
Автор оригинального текста Ken Reilly. Оригинальная статья тут.
Вступление
С первого релиза Flutter в 2018 году прошло не так много времени, однако он уже набрал довольно-таки большую популярность. Компании и предприниматели стараются не тратить много денег на разработку приложений, так что поиск наилучшего и наиболее эффективного пути для создания мобильных приложений и программного обеспечения в целом не прекращается. Flutter работает на всех основных мобильных платформах, а поддержка WEB и всех основных настольных операционных систем находятся в стадии активной разработки.
В этой статье мы рассмотрим довольно-таки простую демо-версию драм-машины и узнаем о базовых шаблонах, используемых при разработке такого приложения.
Чтобы получить и запустить окружение Flutter, посетите эту страницу загрузки. А чтобы получить копию исходного коды этого проекта, зайдите на этот репозиторий.
Ключевые понятия
В создании этого демо-приложения мы будем использовать несколько ключевых понятий Flutter для использования всех возможностей Dart и во избежание написания множества ненужных шаблонов или копирования. Все это окажет большое влияние на читаемость, надежность и производительность кода.
Получение возможного максимума из языка может дать большую разницу между созданием неказистого, подверженного ошибкам приложения и настоящим произведением искусства. Dart обладает множеством функций, помогающих создавать интерактивный асинхронный UX, обладающий управляемым состоянием.
Ключевые понятия:
- Виджеты Flutter Container, SizedBox, и Column;
- Использование Timer и Stopwatch в работе с интервалами времени;
- Реализация сервисов с помощью Stream / StreamController.
Все вышеперечисленное комбинируется различными способами для достижения дизайна, в котором рендеринг пользовательского интерфейса и логика управления аккуратно организованы в классы с простыми в использовании интерфейсами и свойствами.
Обзор
Архитектура и пользовательское взаимодействие бит-машины очень просты для того, чтобы имитировать драм-машины 70-х и 80-х годов, в которых были ограничены такие ресурсы, как механические переключатели, схемы и 8-битные процессоры. Также создание машин, доступных для музыкантов, требует минимизированных затрат на проектирование и сборку.
Каркас основного пользовательского интерфейса разделен на четыре виджета «передней панели», каждый из которых обеспечивает интерактивность, а машинная логика содержится в сервисе воспроизведения сэмплов и в сервисе управления звуком.
Это аналогично тому, как компоненты настоящего аппаратного инструмента передают данные через патч или MIDI-кабели (оба эти варианта до сих пор широко используются в аудиотехнике и создании музыки).
Начало приложения
Приложение инициализируется в main.dart:
Сначала функция main фиксирует приложение в портретной ориентации, убеждаясь в том, что все виджеты инициализированы, а затем настраивая ориентацию устройства. Строение пользовательского интерфейса остается простым благодаря столбцу, отображающему четыре основных виджета интерфейса. Рассмотрим виджеты и классы, используемые для обработки ввода данных пользователя, эффективного рендеринга и обновления пользовательского интерфейса по мере необходимости.
Основные виджеты
Четыре главных виджета в Scaffold расширяют обычный базовый класс для соединения с движком аудио. Этот класс находится в views/base-class.dart:
Классы BaseWidget и BaseState расширяют StatefulWidget и State соответственно и реализуют внутренний Stream, который подсоединяет слушателя к AudioEngine при инициализации и обновляет состояние при получении сигнала от сервиса. Так что каждый виджет, расширяющий BaseWidget, будет перестраиваться каждый раз, когда звуковой сервис отправляет сигнал о том, что внутри него произошло событие и пользовательский интерфейс необходимо перестроить.
Панель индикации
Самый верхний компонент в построении это views/display.dart:
DisplayPanel отображает BPM и положение шага в верхней части экрана, а также автоматически обновляется при получении основным классом сигнала. При нажатии на индикатор BPM открывается диалоговое окно BPMSelector со списком вариантов от 1 до 256. Выбор одного из них настроит BPM на аудио-сервис.
Индикаторы шагов генерируются, и каждый из них «загорается» при работающем сервисе и при соответствии текущего шага индексу в каждом цикле рендеринга.
Паттерн sequencer (секвенсор)
Виджет редактора секвенсора располагается в views/sequencer.dart:
Секвенсор — это StatelessWidget, поскольку сам он не предоставляет никакой интерактивности, но зато рендерит виджеты Track, которые дают UX каждому треку.
Для каждого сэмпла создается расширенная строка с меткой слева и дорожкой, которая автоматически расширяется, чтобы соответствовать оставшемуся пространству в строке.
Sequencer Track
Описание редактора Sequencer Track находится в views/track.dart:
Виджет Track расширяет BaseWidget, и теперь он автоматически перестраивается при получении сигнала из аудио-сервиса. Каждая дорожка включает в себя сгенерированный список из восьми индикаторов нот, которые при нажатии передают событие в звуковой движок, который затем переключает состояние включения / выключения ноты внутри и сигнализирует об обновлении. Цвет каждого индикатора блока нот определяется тем, существует ли нота в текущей позиции и воспроизводится ли она в данный момент. Когда ноты нет, цвет меняется в каждом столбце для видимости и взаимодействия.
Transport Control
Виджет контроля перемещения находится в views/transport.dart:
Класс Transport создает ряд кнопок контроля перемещения, каждая из которых вызывает onTap при нажатии, запуская событие изменения состояния для сервиса, который, в свою очередь, сигнализирует об обновлении виджета через его базовый класс. Когда кнопка соответствует текущему состоянию механизма, она отключается путем передачи null методу onPressed для MaterialButton.
Pad Bank
Pad Bank определяется в views/pad-bank.dart:
PadBank расширяет StatelessWidget, и, так как он не обладает изменяемыми свойствами, то и не требует State. Этот виджет выводит Container в треть высоты доступного пространства на родительском элементе с двумя строками виджетов Pad, каждый с определенным размером и значением, полученным из текущего индекса List.
Drum Pad
Виджет drum pad определен в views/pad.dart:
Виджет Pad не имеет состояния и берет три final параметра за аргумент. Три геттера определяются для получения DRUM_SAMPLE вместе с соответствующими цветом и именем сэмпла. Когда пад нажимается, PadEvent передаются аудио-сервису для дальнейшей работы.
Теперь давайте взглянем на внутреннюю работу бит-машины.
Sampler
Определения сэмплов, загрузка и воспроизведение находятся в services/sampler.dart:
Типы сэмплов определяются с DRUM_SAMPLE, а соответствующие имена файлов и цвета инициализируются в службе, которая загружает аудиофайлы во время инициализации приложения. Когда воспроизведение вызывается на сэмплере из звукового движка, воспроизводится соответствующий кэшированный аудиофайл.
AudioEngine (Аудио-сервис)
Код аудио сервиса находится в services/audio-service.dart:
Сервис AudioEngine управляет состоянием управления перемещения, обрабатывает входные события, выполняет quantization входящих нот при записи, сохраняет данные дорожек и сигнализирует всем прослушивающим виджетам о необходимости обновления пользовательского интерфейса.
Классы событий определены для каждого типа требуемого события звукового сервиса, а класс Signal определен для использования в качестве универсального сигнала для обновления пользовательского интерфейса. В более сложных сценариях класс Signal может быть расширен для передачи различных типов сигналов пользовательскому интерфейсу.
Определяются разрешение и шаг, а также состояние управления, BPM, начальные данные трека, вычисления Timer / Watch / _tick , а также StreamController со слушателем, позволяющим прослушивающим виджетам получать сигналы.
Когда виджет управления (например, drum pad) вызывает событие экземпляра Event, метод использует универсальные шаблоны для переключения типа события и выполнения правильного действия. Таким образом, все входящие сообщения маршрутизируются через одно место и обрабатываются соответствующим образом. Каждый тип события соответствует некоторому набору операций внутри сервиса. Каждый из control, edit, next, и synchronize запускает сигнал в пользовательский интерфейс после завершения всех обновлений.
Архитектура звукового сервиса позволяет производить обновления на лету без перезапуска сервиса, например, включение записи с простым изменением состояния, которое приведет к тому, что будущие входящие ноты будут передаваться в метод process, а также возможность настройки темпа в середине паттерна и синхронизации (synchronize) текущего запущенного таймера с новым BPM.
При получении EditEvent, данные события используются для изменения логического значения для состояния включения / выключения ноты для этой дорожки и позиции шага. Когда звуковой сервис запускается, создается периодический таймер, который продвигает секвенсор один раз на значение _tick и вызывает next, который либо увеличивает, либо сбрасывает таймер, а затем проверяет примечание для каждой дорожки на текущем шаге, наконец сбрасывая quantization _watch и сигнализируя пользовательскому интерфейсу.
Заключение
Этот проект демонстрирует возможности Flutter SDK для быстрого проектирования и разработки приложений.
Flutter — это отличный выбор для создания интерактивных кроссплатформенных приложений с высокими производительностью и надежностью. Благодаря поддержке мобильных устройств, ПК и веб-приложений разработчики могут создавать высококачественные приложения, которые везде отлично работают и просты в обслуживании, с общим синтаксисом и почти универсальной поддержкой библиотек и пакетов.
Все это значительно упрощает задачу по поддержанию актуальности большой мультиплатформенной платформы с постоянными функциями, безупречной надежностью и сверхбыстрой производительностью.
Желаю успехов в ваших будущих проектах Flutter!
Подборка заметок