Документация Engee

Комбинирование базовых барабанных ритмов

Содержимое этой страницы также доступно на YouTube: https://youtu.be/Oog_aunpVms


Предположим, у нас есть несколько базовых барабанных ритмов, которые мы хотим свободно комбинировать случайным образом. Например:

Базовые барабанные ритмы

где нота ми означает правую руку, а ля — левую. Эти ритмы можно легко комбинировать для заполнения такта, например 5b -> 5b -> 3 -> 3 или 5a -> 4 -> 4 -> 3 и т. д. Их также можно комбинировать для заполнения двух тактов и т. д. Обратите внимание, что некоторые последовательности, например 5a -> 4 -> 4 -> 3, приводят к чередованию рук: при каждом воспроизведении такой последовательности «ведущая» рука меняется. Это будет важно в дальнейшем.

Цель состоит в том, чтобы иметь возможность воспроизводить произвольные последовательности в течение произвольных периодов времени. Как опробовать это на практике? Мы используем random_notes_sequence для более быстрого создания длинных 8-тактовых последовательностей с помощью Julia.

Определение Notes

Сначала нужно определить экземпляры Notes, которые будут соответствовать этим четырем базовым ритмам.

using MusicManipulations, MusicVisualizations

tpq = 960 # количество импульсов на четвертную ноту
sixt = 240 # длительность шестнадцатой ноты
left = name_to_pitch("A5")
right = name_to_pitch("E6")
88

Напоминание: Note(pitch, intensity, start, duration)

motif1 = [ # мотив 5a
Note(right, 100, 0, sixt),
Note(left, 50, sixt, sixt),
Note(right, 100, 2sixt, sixt),
Note(left, 50, 3sixt, sixt),
Note(left, 50, 4sixt, sixt)
]

motif2 = [ # мотив 5b
Note(right, 100, 0, sixt),
Note(left, 50, sixt, sixt),
Note(left, 50, 2sixt, sixt),
Note(right, 50, 3sixt, sixt),
Note(right, 50, 4sixt, sixt)
]

motif3 = [ # мотив 3
Note(right, 100, 0, sixt),
Note(left, 50, sixt, sixt),
Note(left, 50, 2sixt, sixt),
]

motif4 = [ # мотив 4
Note(right, 100, 0, sixt),
Note(left, 50, sixt, sixt),
Note(right, 50, 2sixt, sixt),
Note(right, 50, 3sixt, sixt),
]

motifs = Notes.([motif1, motif2, motif3, motif4], tpq)
4-element Vector{Notes{Note}}:
 Notes{Note} with 5 notes
 Notes{Note} with 5 notes
 Notes{Note} with 3 notes
 Notes{Note} with 4 notes

Теперь motifs обозначает набор последовательностей нот, из которого можно брать случайные фрагменты. Давайте создадим последовательности длиной в 8 тактов (то есть 32 четвертные ноты):

q = tpq*32

notes, seq = random_notes_sequence(motifs, q)
notes
128 Notes with tpq=960
 Note E6  | vel = 100 | pos = 0, dur = 240
 Note A5  | vel = 50  | pos = 240, dur = 240
 Note E6  | vel = 100 | pos = 480, dur = 240
 Note A5  | vel = 50  | pos = 720, dur = 240
 Note A5  | vel = 50  | pos = 960, dur = 240
 Note E6  | vel = 100 | pos = 1200, dur = 240
 Note A5  | vel = 50  | pos = 1440, dur = 240
  ⋮
 Note E6  | vel = 100 | pos = 29040, dur = 240
 Note A5  | vel = 50  | pos = 29280, dur = 240
 Note A5  | vel = 50  | pos = 29520, dur = 240
 Note E6  | vel = 100 | pos = 29760, dur = 240
 Note A5  | vel = 50  | pos = 30000, dur = 240
 Note E6  | vel = 50  | pos = 30240, dur = 240
 Note E6  | vel = 50  | pos = 30480, dur = 240

Теперь при необходимости их можно записать в файл MIDI, просто вызвав save("drums_patterns.mid", notes). Кроме того, с помощью MuseScore можно визуализировать и напечатать результат. Этот интерфейс предоставляет функция musescore.

musescore("drums_patterns.png", notes)
Последовательность из 32 тактов

Это предварительно подготовленный вариант — ваша случайная последовательность, скорее всего, будет другой

Получилось неплохо, но есть проблема: В последовательности не учитывается тот факт, что при воспроизведении некоторых ритмов (5b и 4) ведущая рука меняется. Эта проблема решается в следующем разделе.

Добавление чередования рук и текста песни

Обратите внимание, что random_note_sequence также возвращает индексы мотивов, которые использовались для создания последовательности:

seq
30-element Vector{Int64}:
 1
 3
 1
 4
 4
 2
 2
 3
 3
 1
 ⋮
 2
 3
 2
 1
 1
 1
 3
 3
 4

С помощью этой информации мы можем добавить правильные «метки». Для чередования рук достаточно заменить соответствующие ноты ми на ля и наоборот. Определим ряд структур наподобие метаданных.

accent1 = ("5a", false)
accent2 = ("5b", true)
accent3 = ("3", false)
accent4 = ("4", true)
accents = [accent1, accent2, accent3, accent4]
4-element Vector{Tuple{String, Bool}}:
 ("5a", 0)
 ("5b", 1)
 ("3", 0)
 ("4", 1)

Первый элемент каждого кортежа — это просто название ритма, которое также будет отображаться в партитуре как «текст песни». Второй элемент кортежа обозначает, меняется ли для ритма ведущая рука.

Функция, которая «обращает» метку ноты, выглядит следующим образом:

inverse!(n::Note) = (n.pitch = (n.pitch == left ? right : left));

Функция, которая «подсчитывает» длительность каждого ритма для размещения текста песни в правильных позициях в партитуре, выглядит так:

note_length(s::String) = parse(Int, s[1]);

(Помните, что sixt — это длительность шестнадцатой ноты.) Теперь мы инициализируем пустой объект MIDITrack и добавим в него все события.

track = MIDITrack()
ℓ = 0
right_leads = true

for i in 1:length(seq)

    s = accents[seq[i]][1]
    le = MIDI.LyricEvent(0, MIDI.LYRICEV, s)
    addevent!(track, ℓ*sixt, le)

    if !right_leads # Обращаем ноты
        for j in ℓ+1:ℓ+note_length(s)
            inverse!(notes[j])
        end
    end

    global ℓ += note_length(s)

    change = accents[seq[i]][2]
    global right_leads = xor(right_leads, change)
end

addnotes!(track, notes)
notes
128 Notes with tpq=960
 Note E6  | vel = 100 | pos = 0, dur = 240
 Note A5  | vel = 50  | pos = 240, dur = 240
 Note E6  | vel = 100 | pos = 480, dur = 240
 Note A5  | vel = 50  | pos = 720, dur = 240
 Note A5  | vel = 50  | pos = 960, dur = 240
 Note E6  | vel = 100 | pos = 1200, dur = 240
 Note A5  | vel = 50  | pos = 1440, dur = 240
  ⋮
 Note A5  | vel = 100 | pos = 29040, dur = 240
 Note E6  | vel = 50  | pos = 29280, dur = 240
 Note E6  | vel = 50  | pos = 29520, dur = 240
 Note A5  | vel = 100 | pos = 29760, dur = 240
 Note E6  | vel = 50  | pos = 30000, dur = 240
 Note A5  | vel = 50  | pos = 30240, dur = 240
 Note A5  | vel = 50  | pos = 30480, dur = 240

Наконец, с целью визуализации мы снова используем функцию musescore, передав файл MIDI в качестве входных данных:

musescore("drums_patterns_with_names.png", MIDIFile(1, 960, [track]))
Правильная последовательность из 32 тактов

Разве не здорово, что даже текст песни можно отобразить так легко?