Выбрать главу

высота звука кодирует номер ударного инструмента. Теперь определимся с типами параметров:

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 = Track 1 [Event 0 1 (Note 0 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 = 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 неударных