52700.fb2
уровнем громкости Velocity. Конструктор NoteOff обозначает отпускание клавиши, параметры имеют тот
же смысл, что и в случае NoteOn.
Думаю что такое высота и громкость примерно понятно, но что такое канал? Считается, что один испол-
нитель может управлять сразу несколькими генераторами тона. Управление распределяется по каналам. На
каждом канале мы можем управлять отдельным инструментом. Немного о высоте и громкости. Они кодиру-
ются целыми числами из диапазона от 0 до 127. Ноте до первой октавы ( C) соответствует цифра 60, ноте ля
первой октавы ( A) соответствует номер 69. Одно число кодирует сразу и октаву и ступень лада.
Может показаться странным параметр Velocity в конструкторе NoteOff, он обозначает отпускание клави-
ши с определённой громкостью. Обычно этот параметр игнорируется и в него записывают среднее значение
64 или начальное значение 0.
Также мы будем играть разными инструментами. Инструменты в протоколе midi называются програм-
мами. Мы можем установить определённый инструмент на данном канале с помощью сообщения:
306 | Глава 21: Музыкальный пример
ProgramChange {
channel :: !Channel,
preset
:: !Preset }
Целое число Preset указывает на код инструмента. Теперь посмотрим, что же такое midi-файл:
data Midi = Midi {
fileType :: FileType,
timeDiv
:: TimeDiv,
tracks
:: [Track Ticks] }
midi-файл состоит из трёх значений. Это обозначение типа файла:
data FileType = SingleTrack | MultiTrack | MultiPattern
По типу midi-файлы могут различаться на файлы с одним треком, файлы с несколькими треками, и
файлы, которые содержат группы треков, которые называют узорами (pattern). По смыслу трек соответствует
партии инструмента.
Тип TimeDiv кодирует скорость записи сообщений. Различают два варианта:
data TimeDive = TicksPerBeat Int
| TicksPerSecond Int Int
Первый конструктор говорит о том, что разрешение времени закодировано в формате PPQN, он указы-
вает на число ударов в одной четвертной длительности. Второй конструктор говорит о том, что разрешение
кодируется в формате SMPTE, оно указывает на число кадров в секунде.
Теперь посмотрим, что такое трек:
type Track a = [(a, Message)]
Трек это список событий с временными отсчётами. Время в midi отсчитывается относительно предыдуще-
го события. Например в следующей записи три события произошли одновременно и затем спустя 10 тактов
произошли ещё два события:
[(0, e1), (0, e2), (0, e3), (10, e4), (0, e5)]
21.2 Музыкальная запись в виде событий
Писать музыку в виде событий midi очень неудобно, пусть даже и через HCodecs, необходимо придумать
надстройку над протоколом midi. Я долго думал об этом и в итоге пришёл к выводу, что наиболее простой
и податливый способ представления музыки на нотном уровне реализован в языке Csound. Там ноты пред-
ставлены в виде последовательности событий. Каждое событие начинается в определённый момент и длится
некоторое время. Событие содержит код инструмента и набор параметров, которые могут включать в себя
громкость, высоту звука и какие-то специфические для данного инструмента настройки. Обязательными
параметрами события являются лишь номер инструмента, который играет ноту, начало события и длитель-