Базовые структуры MIDI
Ввод-вывод MIDI
Для чтения и записи MIDI-файла можно использовать функции load
и save
. Они расширяют интерфейс FileIO
. Их синтаксис следующий:
load(filename) -> midi::MIDIFile
Загрузите MIDI-файл, содержащийся в filename
(он должен заканчиваться на .mid), и верните его как MIDIFile
.
save(filename, data::MIDIFile)
Запишите MIDIFile
как файл .mid в указанный объект filename
.
save(filename, notes::Notes)
Создайте файл MIDIFile
непосредственно из notes
, используя формат 1, а затем сохраните его.
MIDIFile
#
MIDI.MIDIFile
— Type
MIDIFile <: Any
Тип, представляющий файл с данными MIDI.
Поля
-
format::UInt16
: формат файла. Может иметь значение 0, 1 или 2. -
tpq::Int16
: временное деление дорожки, количество импульсов на четвертную ноту. -
tracks::Array{MIDITrack, 1}
: массив содержащихся дорожек.
MIDITrack
Наиболее важное поле MIDIFile
— tracks
. Оно содержит столько дорожек, сколько пожелает пользователь. Дорожки содержат всю «музыкальную» информацию в виде «событий», о которых мы упоминали в разделе MIDI: минимальные необходимые знания.
#
MIDI.MIDITrack
— Type
MIDITrack <: Any
Объект MIDITrack
— это просто контейнер для TrackEvents
, так как его единственное поле — events::Vector{TrackEvent}
.
Фрагменты дорожки начинаются с четырех байтов, которые записываются как «MTrk», за которыми следуют длина дорожки в байтах (см. описание readvariablelength
) и последовательность событий.
MIDITrack
реализует функции isempty
и empty!
.
#
MIDI.TrackEvent
— Type
TrackEvent <: Any
Абстрактный супертип для всех событий MIDI.
Все события дорожки начинаются со значения времени переменной длины (см. описание readvariablelength
) и имеют поле dT
, которое его содержит. Это число указывает, через сколько импульсов с момента последнего события происходит текущее событие.
Затем MIDIEvent
возобновляется с сообщением MIDI-канала, определенным в constants.jl
. Затем следуют 1 или 2 байта в зависимости от сообщения канала (см. описание MIDI.EVENTTYPETOLENGTH
). Если допустимое сообщение канала не идентифицировано, используется предыдущее сообщение. После этого команда MIDI кодируется.
И MetaEvent
, и SysexEvent
возобновляются с определенного байта (см. описание constants.jl
).
Сами события TrackEvent
можно разделить на три типа.
abstract type MIDIEvent <: TrackEvent end
abstract type MetaEvent <: TrackEvent end
struct SysexEvent <: TrackEvent
dT::Int
data::Array{UInt8,1}
end
Различные MIDI- и метасобытия, встречающиеся в MIDI-файле, имеют собственные типы. Дополнительные сведения см. в разделах Метасобытия и MIDI-события ниже.
Метасобытия
#
MIDI.SequenceNumberEvent
— Type
SequenceNumberEvent <: MetaEvent
Событие SequenceNumberEvent
содержит номер последовательности в MIDI-файлах типов 0 и 1, или номер паттерна в MIDI-файлах типа 2.
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события. -
number::Int
: порядковый номер.
#
MIDI.TextEvent
— Type
TextEvent <: MetaEvent
Событие TextEvent
содержит текст в MIDI-файле.
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события. -
text::String
: текст события.
#
MIDI.CopyrightNoticeEvent
— Type
CopyrightNoticeEvent <: MetaEvent
Событие CopyrightNoticeEvent
содержит уведомление об авторских правах в MIDI-файле.
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события. -
text::String
: уведомление об авторских правах в тексте.
#
MIDI.TrackNameEvent
— Type
TrackNameEvent <: MetaEvent
Событие TrackNameEvent
содержит либо имя MIDI-последовательности (в файлах MIDI типа 0 или MIDI типа 2, или в первой дорожке файла MIDI типа 1), либо имя MIDI-дорожки (в других дорожках файла MIDI типа 1).
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события. -
text::String
: название дорожки в тексте.
#
MIDI.InstrumentNameEvent
— Type
InstrumentNameEvent <: MetaEvent
Событие InstrumentNameEvent
содержит имя инструмента, который будет использоваться в треке.
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события. -
text::String
: название инструмента в тексте.
#
MIDI.LyricEvent
— Type
LyricEvent <: MetaEvent
Событие LyricEvent
содержит текст песни (обычно слоги) в MIDI-файле.
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события. -
text::String
: текст песни.
#
MIDI.MarkerEvent
— Type
MarkerEvent <: MetaEvent
Событие MarkerEvent
содержит текст маркера.
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события. -
text::String
: текст маркера.
#
MIDI.CuePointEvent
— Type
CuePointEvent <: MetaEvent
Событие CuePointEvent
содержит реплику в MIDI-файле.
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события. -
text::String
: сигнал в тексте.
#
MIDI.MIDIChannelPrefixEvent
— Type
MIDIChannelPrefixEvent <: MetaEvent
Событие MIDIChannelPrefixEvent
содержит номер канала, на который отправляются следующие мета-сообщения.
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события. -
channel::Int
: номер канала.
#
MIDI.EndOfTrackEvent
— Type
EndOfTrackEvent <: MetaEvent
Событие EndOfTrackEvent
обозначает конец трека.
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события.
#
MIDI.SetTempoEvent
— Type
SetTempoEvent <: MetaEvent
Событие SetTempoEvent
устанавливает темп MIDI-последовательности в микросекундах на четверть ноты.
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события. -
tempo::Int
: темп в микросекундах на четвертную ноту.
#
MIDI.TimeSignatureEvent
— Type
TimeSignatureEvent <: MetaEvent
Событие TimeSignatureEvent
содержит временную подпись MIDI-последовательности.
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события. -
numerator::Int
: числитель тактового размера. -
denominator::Int
: знаменатель тактового размера. -
clockticks::Int
: количество импульсов MIDI-часов за один отсчет метронома. -
notated32nd_notes::Int
: количество тридцать вторых нот на долю.
#
MIDI.KeySignatureEvent
— Type
KeySignatureEvent <: MetaEvent
Событие KeySignatureEvent
содержит ключевую подпись и шкалу MIDI-файла.
Поля:
-
dT::Int
: время дельты в импульсах. -
metatype::UInt8
: байт метатипа события. -
semitones::Int
: количество бемолей или диезов. -
scale::Int
: тональность MIDI-файла; 0, если тональность мажорная, 1, если тональность минорная.
MIDI-события
#
MIDI.NoteOffEvent
— Type
NoteOffEvent <: MIDIEvent
Событие NoteOffEvent
информирует MIDI-устройство об освобождении ноты.
Поля:
-
dT::Int
: время дельты в импульсах. -
status::UInt8
: байт статуса события. -
note::Int
: нота, которую следует перестать воспроизводить. -
velocity::Int
: сила воспроизведения ноты.
#
MIDI.NoteOnEvent
— Type
NoteOnEvent <: MIDIEvent
Событие NoteOnEvent
сообщает MIDI-устройству о необходимости сыграть ноту. Событие NoteOnEvent
с скоростью 0 действует как NoteOffEvent
.
Поля:
-
dT::Int
: время дельты в импульсах. -
status::UInt8
: байт статуса события. -
note::Int
: нота, которую следует начать воспроизводить. -
velocity::Int
: сила воспроизведения ноты.
#
MIDI.AftertouchEvent
— Type
AftertouchEvent <: MIDIEvent
Событие AftertouchEvent
информирует MIDI-устройство о том, что на ноту было оказано давление.
Поля:
-
dT::Int
: время дельты в импульсах. -
status::UInt8
: байт статуса события. -
note::Int
: нота, к которой применяется давление. -
pressure::Int
: величина применяемого давления.
#
MIDI.ControlChangeEvent
— Type
ControlChangeEvent <: MIDIEvent
Событие ControlChangeEvent
информирует MIDI-устройство об изменении значения контроллера.
Поля:
-
dT::Int
: время дельты в импульсах. -
status::UInt8
: байт статуса события. -
controller::Int
: номер контроллера. -
value::Int
: значение, полученное контроллером.
#
MIDI.ProgramChangeEvent
— Type
ProgramChangeEvent <: MIDIEvent
Событие ProgramChangeEvent
информирует MIDI-устройство о выборе номера программы в определенном канале.
Поля:
-
dT::Int
: время дельты в импульсах. -
status::UInt8
: байт статуса события. -
program::Int
: номер новой программы.
#
MIDI.ChannelPressureEvent
— Type
ChannelPressureEvent <: MIDIEvent
Событие ChannelPressureEvent
информирует MIDI-устройство о необходимости применить давление к определенному каналу.
Поля:
-
dT::Int
: время дельты в импульсах. -
status::UInt8
: байт статуса события. -
pressure::Int
: величина применяемого давления.
#
MIDI.PitchBendEvent
— Type
PitchBendEvent <: MIDIEvent
Событие PitchBendEvent
информирует MIDI-устройство об изменении высоты тона в определенном канале.
Поля:
-
dT::Int
: время дельты в импульсах. -
status::UInt8
: байт статуса события. -
pitch::Int
: значение изменения высоты тона.
Вспомогательные функции
#
MIDI.qpm
— Function
qpm(midi)
Возвращает исходное значение QPM (количество четвертных нот в минуту), с которым был экспортирован файл MIDIFile
. Это значение является постоянным и не меняется даже при возникновении события изменения темпа. Если значение не найдено, возвращается 120. Чтобы получить список QPM с течением времени, используйте функцию tempochanges
.
#
MIDI.bpm
— Function
bpm(midi)
Возвращает BPM, в который был экспортирован заданный MIDIFile
. Возвращает QPM, если он не найден.
#
MIDI.ms_per_tick
— Function
ms_per_tick(tpq, qpm)
ms_per_tick(midi::MIDIFile)
Возвращает количество миллисекунд, которое составляет один тик, исходя из количества четвертных нот в минуту qpm
и тиков на четверть ноты tpq
.
#
MIDI.addevent!
— Function
addevent!(track::MIDITrack, time::Int, event::TrackEvent)
Добавляет событие в track
в заданное время time
. time
указывается в абсолютном времени, а не в относительном.
Чтобы добавить несколько событий за один раз, используйте вместо этого функцию addevents!
.
#
MIDI.addevents!
— Function
addevents!(track::MIDITrack, times, events)
Добавляет заданные события events
в указанную дорожку track
в указанные моменты времени times
с внутренним преобразованием абсолютного времени в относительное.
Использование этой функции эффективнее, чем цикл по отдельным вызовам addevent!
.
#
MIDI.trackname
— Function
trackname(track::MIDI.MIDITrack)
Возвращает название дорожки track
в виде строки, находя TrackNameEvent
.
Если такого события нет, возвращается сообщение "No track name found"
.
#
MIDI.addtrackname!
— Function
addtrackname!(track::MIDI.MIDITrack, name::String)
Добавляет название для дорожки track
, присоединяя TrackNameEvent
к началу track
.
#
MIDI.findtextevents
— Function
findtextevents(eventtype, track)
Находит все текстовые события, указанные в поле по eventtype
в track
. В качестве eventtype
могут быть указаны TextEvent, LyricEvent, MarkerEvent
, что позволит найти соответствующие мета-события.
Для удобства эта функция не возвращает сами события. Вместо этого она возвращает три вектора: первый — это строки событий, второй — индексы событий в track
, а третий — абсолютная позиция событий (от начала track
).
Примечание. Распространенные редакторы музыкальных партитур, такие как MuseScore, GuitarPro и т. д., не экспортируют тексты песен и текстовую информацию при экспорте MIDI-файлов.
Примечание. Cubase может считывать события маркеров, а MuseScore — события текстов песен. На данный момент нам не известен ни один редактор, который мог бы считывать текстовые события.
#
MIDI.tempochanges
— Function
tempochanges(midi)
Возвращает вектор кортежей (position, tempo) для всех темповых событий в данном MIDIFile
, где position - абсолютное время (от начала файла) в тиках, а tempo - четверть ноты в минуту. Возвращает [(0, 120.0)], если темповые события отсутствуют.
Низкоуровневый API
В этом разделе демонстрируется низкоуровневый API, который позволяет считывать байты из файла и преобразовывать их в структуры Julia.
#
MIDI.readvariablelength
— Function
readvariablelength(f::IO)
Числа переменной длины в MIDI-файлах представлены в виде последовательности байтов. Если первый бит равен 0, то перед нами последний байт в последовательности. Остальные 7 бит указывают на номер.
Другие полезные функции, которые не экспортируются:
writeevent
readMIDIevent
readmetaevent
readsysexevent
get_abs_pos
Наконец, сведения о типах сообщений, типах событий и т. д. см. в файле MIDI/src/constants.jl
.
MIDI: минимальные необходимые знания
Этот раздел представляет собой краткий курс по формату MIDI. Дополнительные сведения см. на странице в Википедии, в официальных спецификациях MIDI и в подробном руководстве на сайте recordingblogs.com.
MIDI-файл обычно состоит из частей, называемых дорожками, которые воспроизводятся одновременно. Каждая дорожка может иметь 16 различных каналов с номерами от 0 до 15. Каждый канал можно рассматривать как отдельный инструмент, хотя на протяжении дорожки этот инструмент может меняться. Дорожка содержит события. Есть три типа событий: MIDI-события, метасобытия и эксклюзивные системные события (SYSEX). Время наступления каждого события отсчитывается с момента последнего события (dT) в импульсах. Количество импульсов на четвертную ноту указывается в параметре tpq
MIDI-файла (MIDIFile.tpq
, см. описание MIDIFile
).
-
События MIDI связаны как с самими нотами, так и со звуковой текстурой, например способом воспроизведения ноты или перемещением колеса высоты тона.
-
Метасобытия связаны с добавлением текста об авторских правах, сведений об авторстве, название дорожек и т. д.
-
События SYSEX служат для передачи произвольных данных. Их содержимое зависит от предполагаемого получателя.