Но мы можем исправить эту ситуацию, сместив все ноты на время самой первой ноты, конечно смещать
необходимо только в том случае если время окажется отрицательным:
alignEvents ::[ MidiEvent] ->[ MidiEvent]
alignEvents es
|d <0
=map (delay (abs d)) es
|otherwise =es
whered =minimum $map eventStart es
Вызовем эту функцию сразу после функции trackEvents в функции groupInstr. Второй нюанс заключа-
ется в том, что каждый трек в midi-файле должен заканчиваться специальным сообщением, в библиотеке
HCodecsоно обозначается с помощью конструктора TrackEnd. В самом конце необходимо добавить сообще-
ние (0, TrackEnd):
toTrack :: Score -> M.Track M.Ticks
toTrack =addEndMsg .tfmTime .mergeInstr .groupInstr
addEndMsg :: M.Track M.Ticks -> M.Track M.Ticks
addEndMsg =( ++[(0, M.TrackEnd)])
Теперь мы можем проверить, что у нас получилось. Создадим файл:
module Main where
import System
import Track
import Score
import Codec.Midi
out =( >>system ”timidity tmp.mid”) .
exportFile ”tmp.mid” .render
В функции out мы переводим нотную запись в значение типа Midi, затем сохраняем это значение в файле
tmp .mid и в самом конце запускаем файл с помощью проигрывателя timidity. Вместо timidity вы можете
воспользоваться вашим любимым проигрывателем midi-файлов. Теперь загрузим модуль Mainв интерпре-
татор. Послушаем ноту до:
*Main>out c
314 | Глава 21: Музыкальный пример
Далее следуют сообщения из проигрывателя timidity и долгожданный звук. Мы слышим ноту до, сыг-
ранную на рояле. Наберём какую-нибудь мелодию:
*Main> letx =line [c, hn e, hn e, low b, c]
*Main>out x
Сыграем в два раза быстрее, на другом инструменте:
*Main>out $instr 15 $hn x
Сыграем канон. Канон это когда одна и та же мелодия ведётся в разных голосах с запаздыванием. Сыграем
двухголосный канон:
*Main>out $instr 80 (loop 3 x) =:=delay 2 (instr 65 $low $loop 3 x)
Номера инструментов можно посмотреть по справке к протоколу General Midi. Это дополнение к прото-
колу midi определяет какие номера каким инструментам должны соответствовать. Звучит ужасно, но звучит!
21.5 Пример
Опираясь на примитивы композиции, которые мы определил в модуле Score, мы можем написать мело-
дию. Ниже приведён небольшой пример. Инструменты:
closedHiHat =drum 42;
rideCymbal =drum 59;
cabasa =drum 69;
maracas
=drum 70;
tom
=drum 45;
flute
=instr 73;
piano
=instr 0;
Ударная секция:
b1 =bam 100
b0 =bam 84
drums1 =loop 80 $chord [
tom
$line [qn b1, qn b0, hnr],
maracas $line [hnr, hn b0]
]
drums2 =quieter 20 $cabasa $loop 120 $en $line [b1, b0, b0, b0, b0]
drums3 =closedHiHat $loop 50 $en (line [b1, loop 12 wnr])
drums =drums1 =:=drums2 =:=drums3
Уже сейчас мы можем загрузить эту партию в интерпретатор и послушать, вызвав out drums. Аккорды к
мелодии:
c7
=chord [c, e, b]
gs7 =chord [low af, c, g]
g7
=chord [low g, low bf, f]
harmony =piano $loop 12 $lower 1 $bn $line [bn c7, gs7, g7]
Мелодия:
ac =louder 5
mel1 =bn $line [bnr, subMel, ac $stretch (1 +1 /8) e, c,
subMel, enr]
wheresubMel =line [g, stretch 1.5 $qn g, qn f, qn g]
mel2 =loop 2 $qn $line [subMel, ac $bn ds, c, d, ac $bn c, c, c, wnr,
subMel, ac $bn g, f, ds, ac $bn f, ds, ac $bn c]
wheresubMel =line [ac ds, c, d, ac $bn c, c, c]
mel3 =loop 2 $line [pat1 (high c) as g, pat1 g f d]
wherepat1 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и по-другому.
Читать дальше