52700.fb2
Согласно протоколу midi в случае ударных инструментов высота звука кодирует инструмент. Поэтому
в функции drum мы изменяем именно поле notePitch. Создадим также несколько синонимов для создания
нот, которые играются на барабанах. В этом случае нам не важна высота звука но важна громкость:
bam :: Int -> Score
bam n = Track 1 [Event 0 1 (Note 0 n 35 True)]
Номер 35 кодирует “бочку”.
Паузы
Слово silence верно отражает смысл, но оно слишком длинное. Давайте определим несколько синони-
мов:
rest :: Double -> Score
rest = silence
wnr = rest 1;
bnr = bn wnr;
hnr = hn wnr;
qnr = qn wnr;
enr = en wnr;
snr = sn wnr;
21.4 Перевод в midi
Теперь мы можем составить какую нибудь мелодию:
q = line [c, c, hn e, hn d, bn e, chord [c, e]]
Мы можем составлять мелодии, но пока мы не умеем их интерпретировать. Для этого нам нужно написать
функцию:
render :: Score -> Midi
Мы реализуем простейший случай. Будем считать, что у нас только 15 инструментов, а все остальные
инструменты – ударные. Мы запишем нашу музыку на один трек midi-файла, распределив 15 неударных
инструментов по разным каналам. Ещё одно упрощение заключается в том, что мы зададим фиксированное
разрешение по времени для всех возможных мелодий. Будем считать, что 96 ударов для одной четверти нам
достаточно. Принимая во внимания эти посылки мы можем написать такую функцию:
import qualified Codec.Midi as M
render :: Score -> Midi
render s = M.Midi M.SingleTrack (M.TicksPerBeat divisions) [toTrack s]
divisions :: M.Ticks
divisions = 96
toTrack :: Score -> M.Track
toTrack = undefined
Мы загрузили модуль Codec.Midi под псевдонимом M, так мы сможем отличать низкоуровневые опре-
деления от тех, что мы определили сами. Теперь перед каждым именем из модуля Codec.Midi необходимо
писать приставку M.
В нашей упрощённой реализации на одном канале может играть только один инструмент. В самом начале
мы назначим инструмент на канал с помощью сообщения ProgramChange. Для этого нам необходимо понять
какому инструменту какой канал соответствует. В библиотеке HCodecs каналы идут от нуля до 15. Девятый
канал предназначен для ударных. Представим, что у нас есть функция, которая распределяет нотную запись
по инструментам:
Перевод в midi | 311
type MidiEvent = Event Double Note
groupInstr :: Score -> ([[MidiEvent]], [MidiEvent])
Эта функция принимает нотную запись, а возвращает пару. Первый элемент содержит список списков нот
для неударных инструментов, каждый подсписок содержит ноты только для одного инструмента. Второй
элемент пары содержит все ноты для ударных инструментов. Представим также, что у нас есть функция,