высота звука кодирует номер ударного инструмента. Теперь определимся с типами параметров:
type Instr
= Int
type Volume = Int
type Pitch
= Int
Целые числа соответствуют целым числам в протоколе midi. Значения для типов Volumeи Pitchлежат в
диапазоне от 0 до 127.
Введём специальное обозначение для музыкального типа Track:
type Score = Track Double Note
Ноты в midi | 309
Синонимы для нот
Высота ноты
Музыкантам ближе буквенные обозначения для нот нежели коды midi. Определим удобные синонимы:
note :: Int -> Score
note n = Track1 [ Event0 1 ( Note0 64 (60 +n) False)]
Эта функция строит трек, который содержит одну ноту. Нота длится одну целую длительность играется
на инструменте с кодом 0, на средней громкости. Параметр функции задаёт смещение от ноты до первой
октавы. Определим остальные ноты:
a, b, c, d, e, f, g,
as, bs, cs, ds, es, fs, gs,
af, bf, cf, df, ef, ff, gf :: Score
c =note 0;
cs =note 1;
d =note 2;
ds =note 3;
...
Первая буква содержит буквенное обозначение ноты, а вторая либо s (от англ. sharp диез) или f (от англ.
flat бемоль). Все эти ноты находятся в первой октаве, но смещением высоты на 12 единиц мы легко можем
смещать эти ноты в любую другую октаву:
higher :: Int -> Score -> Score
higher n =fmap (\a ->a{ notePitch =12 *n +notePitch a })
lower :: Int -> Score -> Score
lower n =higher ( -n)
high :: Score -> Score
high =higher 1
low :: Score -> Score
low =lower 1
С помощью этих функций мы легко можем смещать группы нот в любую октаву. Функция higher прини-
мает число октав, на которые необходимо сместить вверх высоту во всех нотах трека. Смещение высоты на
12 определяет смещение на одну октаву. Остальные функции определены в через функцию higher.
Длительность ноты
Пока что наши ноты длятся 1 единицу времени. Но нам бы хотелось иметь в распоряжении и другие дли-
тельности. Ноты других длительностей мы можем легко получать с помощью функции stretch, мы просто
изменим масштаб времени и длительность всех нот изменится. Определим несколько синонимов:
bn, hn, qn, en, sn :: Score -> Score
-- (brewis note)
(half note)
(quater note)
bn =stretch 2;
hn =stretch 0.5;
qn =stretch 0.25;
-- (eighth note)
(sizth note)
en =stretch 0.125;
sn =stretch 0.0625;
Эти преобразования отвечают длительностям нот в европейской музыкальной традиции.
Громкость ноты
Пока мы умеем создавать ноты средней громкости, но мы можем определить преобразователи на манер
тех, что изменяли высоту звука октавами:
louder :: Int -> Score -> Score
louder n =fmap $\a ->a{ noteVolume =n +noteVolume a }
quieter :: Int -> Score -> Score
quieter n =louder ( -n)
310 | Глава 21: Музыкальный пример
Смена инструмента
Изначально мы создаём ноты, которые играются на инструменте с кодом 0, в протоколе General Midi этот
номер соответствует роялю. Но с помощью класса Functorмы легко можем изменить инструмент:
instr :: Int -> Score -> Score
instr n =fmap $\a ->a{ noteInstr =n, isDrum = False}
drum :: Int -> Score -> Score
drum n =fmap $\a ->a{ notePitch =n, isDrum = True}
Согласно протоколу midi в случае ударных инструментов высота звука кодирует инструмент. Поэтому
в функции drum мы изменяем именно поле notePitch. Создадим также несколько синонимов для создания
нот, которые играются на барабанах. В этом случае нам не важна высота звука но важна громкость:
bam :: Int -> Score
bam n = Track1 [ Event0 1 ( Note0 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 инструментов, а все остальные
Читать дальше