52700.fb2
subMel, ac $ bn g, f, ds, ac $ bn f, ds, ac $ bn c]
where subMel = line [ac ds, c, d, ac $ bn c, c, c]
mel3 = loop 2 $ line [pat1 (high c) as g, pat1 g f d]
where pat1 a b c = line [pat a, loop 3 qnr, wnr,
pat b, qnr, hnr, pat c, qnr, hnr]
pat
x
= en (x +:+ x)
mel = flute $ line [mel1, mel2, mel3]
Пример | 315
Добавим в конце звук тарелки:
cha = delay (dur mel1 + dur mel2) $ loop 10 $ rideCymbal $ delay 1 b1
Соберём всё вместе и послушаем:
res = chord [
drums,
harmony,
high mel,
louder 40 cha,
rest 0]
main = out res
В конце стоит фиктивный элемент rest 0 для того чтобы было удобно глушить инструменты комменти-
рованием.
21.6 Эффективное представление музыкальной нотации
Реализация, которую мы рассмотрели не эффективна, Мы могли бы определить тип Track и по-другому.
Мы очень часто пользуемся операцией delay через операцию line. Так в выражении:
q = line [s1, s2, line [loop 2 s3, s4], s5]
Мы будем несколько раз обходить элемент s3 для каждого применения line. К примеру сначала мы
смести все элементы на 3, потом сместим на 5, потом на 10, но вместо этого мы могли бы сразу сместить
все элементы на 18 за один проход. Для этого мы можем закодировать преобразования событий во времени
в типе Track:
data Track t a = Track {
trackDur
:: t,
trackEvents :: TList t a
data TList t a = Empty | Single a | Append (TList t a) (TList t a)
| TFun (Tfm t) (TList t a)
data Tfm t = Tfm ! t ! t
Тип TList позволяет проводить быстрое объединение списков. Дополнительный конструктор TFun обо-
значает линейное преобразование списка во времени. Линейное преобразование кодируется двумя числами,
это масштаб и смещение. Мы считаем, что события в конструкторе Single начинаются в момент времени 0
и длятся 1 единицу времени. Так например событие, которое произошло на 2 единице времени и длилось 4
единицы можно представить так:
TFun (4 2) (Single a)
Значение Tfm k d обозначает линейную функцию
f ( x) = kx + d
Для того чтобы получить настоящие отсчёты по времени мы применяем её к временным координатам
“не преобразованного” события, то есть события Event 0 1 a.
Единственное, что нам нужно для того чтобы встроить этот вариант в библиотеку это написать функцию:
fromTList :: TList t a -> [Event t a]