Spec-Zone .ru
спецификации, руководства, описания, API
|
API Звука Java определяет направляющую сообщение архитектуру для данных MIDI, это гибко и удобно, как только Вы понимаете, как это работает. Система основана на проекте соединения модуля: отличные модули, каждый из которых выполняет определенную задачу, могут быть соединены (сетевые), включающие данные, чтобы вытекать из одного модуля другому.
Основной модуль в системе обмена сообщениями API Звука Java MidiDevice
(интерфейс языка Java). MidiDevices
включайте секвенсеры (которые записывают, играют, загружают, и редактируют последовательности сообщений MIDI с меткой времени), синтезаторы (которые генерируют звуки когда инициировано сообщениями MIDI), и порты входа и выхода MIDI, через которые данные прибывают из и идут во внешние устройства MIDI. Функциональность, обычно требуемая портов MIDI, описывается основой MidiDevice
интерфейс. Sequencer
и Synthesizer
интерфейсы расширяются MidiDevice
интерфейс, чтобы описать дополнительную характеристику функциональности секвенсеров MIDI и синтезаторов, соответственно. Реальные классы, которые функционируют как секвенсеры или синтезаторы, должны реализовать эти интерфейсы.
A MidiDevice
обычно имеет один или более вспомогательных объектов, которые реализуют Receiver
или Transmitter
интерфейсы. Эти интерфейсы представляют "разъемы" или "порталы", которые соединяют устройства вместе, разрешая данные течь в и из них. Соединяясь a Transmitter
из одного MidiDevice
к a Receiver
из другого можно создать сеть модулей в который потоки данных от одного до другого.
MidiDevice
интерфейс включает методы для того, чтобы определить, сколько возражают передатчик и получатель, что устройство может поддерживать одновременно, и другие методы для того, чтобы получить доступ к тем объектам. У выходного порта MIDI обычно есть по крайней мере один Receiver
через который могут быть получены исходящие сообщения; точно так же синтезатор обычно отвечает на сообщения, отправленные Receiver
или Receivers
. У входного порта MIDI обычно есть по крайней мере один Transmitter
, который распространяет входящие сообщения. Полнофункциональный секвенсер поддерживает обоих Receivers
, которые получают сообщения во время записи, и Transmitters
, которые отправляют сообщения во время воспроизведения.
Transmitter
интерфейс включает методы для установки и запросов получателей, которым передатчик отправляет MidiMessages
. Установка получателя устанавливает соединение между двумя. Receiver
интерфейс содержит метод, который отправляет a MidiMessage
к получателю. Как правило, этот метод вызывается a Transmitter
. Оба Transmitter
и Receiver
интерфейсы включают a close
метод, который освобождает ранее соединенный передатчик или получатель, делая это доступный для различного соединения.
Мы теперь исследуем, как использовать передатчики и получатели. Прежде, чем добраться до типичного случая соединения двух устройств (таких как сцепление секвенсера к синтезатору), мы исследуем более простой случай, куда Вы отправляете сообщение MIDI непосредственно с Вашей прикладной программы на устройство. Изучение этого простого сценария должно облегчить понимать, как API Звука Java принимает меры отправлять сообщения MIDI между двумя устройствами.
Скажем, Вы хотите создать сообщение MIDI с нуля и затем отправить его некоторому получателю. Можно создать новое, пробел ShortMessage
и затем заполните это данными MIDI, используя следующий ShortMessage
метод:
void setMessage(int command, int channel, int data1, int data2)
Как только у Вас есть сообщение, готовое передаться, можно отправить его a Receiver
объект, используя это Receiver
метод:
void send(MidiMessage message, long timeStamp)
Параметр метки времени будет объяснен на мгновение. Пока, мы только упомянем, что его значение может быть установлено в-1, если Вы не заботитесь об определении точного времени. В этом случае устройство, получающее сообщение, попытается ответить на сообщение как можно скорее.
Прикладная программа может получить получатель для a MidiDevice
вызывая устройство getReceiver
метод. Если устройство не может обеспечить получатель для программы (обычно, потому что получатели всего устройства уже находятся в использовании), a MidiUnavailableException
бросается. Иначе, получатель, возвращенный из этого метода, доступен для непосредственного использования программой. Когда программа закончила использовать получатель, она должна вызвать получатель close
метод. Если программа пытается вызвать методы на получатель после вызова close
, IllegalStateException
может быть брошен.
Как конкретный простой пример отправки сообщения, не используя передатчик, давайте отправлять Сообщение На сообщении к получателю по умолчанию, который обычно связывается с устройством, таким как выходной порт MIDI или синтезатор. Мы делаем это, создавая подходящее ShortMessage
и передача этого как параметр Receiver's
send
метод:
ShortMessage myMsg = new ShortMessage(); // Start playing the note Middle C (60), // moderately loud (velocity = 93). myMsg.setMessage(ShortMessage.NOTE_ON, 0, 60, 93); long timeStamp = -1; Receiver rcvr = MidiSystem.getReceiver(); rcvr.send(myMsg, timeStamp);
Этот код использует статическое целочисленное поле ShortMessage
, а именно, NOTE_ON
, для использования в качестве байта состояния сообщения MIDI. Другим частям сообщения MIDI дают явные числовые значения как параметры setMessage
метод. Нуль указывает, что примечание должно играться, используя MIDI номер канала 1; эти 60 указывают на Середину примечания C; и эти 93 являются произвольным ключевым вниз скоростным значением, которое обычно указывает, что синтезатор, который в конечном счете играет примечание, должен играть его несколько громко. (Листы спецификации MIDI точная интерпретация скорости до реализации синтезатора его текущего инструмента.) Это сообщение MIDI тогда отправляется получателю с меткой времени-1. Мы теперь должны исследовать точно, что означает параметр метки времени, который является предметом следующего раздела.
Глава 8, "Краткий обзор Пакета MIDI," объяснил, что у спецификации MIDI есть различные части. Одна часть описывает протокол "провода" MIDI (сообщения, отправленные между устройствами в режиме реального времени), и другая часть описывает Стандартные Файлы MIDI (сообщения, хранившие как события в "последовательностях"). В последней части спецификации каждое событие, сохраненное в стандартном файле MIDI, тегируется со значением синхронизации, которое указывает, когда то событие должно играться. В отличие от этого, сообщения в протоколе провода MIDI, как всегда предполагается, сразу обрабатываются, как только они получаются устройством, таким образом, у них нет никаких сопроводительных значений синхронизации.
API Звука Java добавляет дополнительное скручивание. Это не удивляет, что значения синхронизации присутствуют в MidiEvent
объекты, которые хранятся в последовательностях (как мог бы быть считан из файла MIDI), так же, как в Стандартной спецификации Файлов MIDI. Но в API Звука Java, даже сообщения, отправленные между устройствами — другими словами, сообщениям, которые соответствуют протоколу провода MIDI — можно дать значения синхронизации, известные как метки времени. Именно эти метки времени касаются нас здесь. (Синхронизация оценивает в MidiEvent
объекты обсуждаются подробно в Главе 11, "Игра, Запись, и Редактирование Последовательностей MIDI.")
Метка времени, которая может дополнительно сопровождать сообщения, отправленные между устройствами в API Звука Java, очень отличается от значений синхронизации в стандартном файле MIDI. Значения синхронизации в файле MIDI часто основаны на музыкальных понятиях, таких как удары и темп, и синхронизация каждого события измеряет время, законченное начиная с предыдущего события. Напротив, метка времени на сообщении, отправленном устройству Receiver
возразите всегда измеряет абсолютное время в микросекундах. Определенно, это измеряет число микросекунд, законченных, так как устройство, которому принадлежит получатель, было открыто.
Этот вид метки времени разрабатывается, чтобы помочь компенсировать задержки, представленные операционной системой или прикладной программой. Важно понять, что эти метки времени используются для незначительных корректировок синхронизации, чтобы не реализовать сложные очереди, которые могут запланировать события в абсолютно произвольные времена (как MidiEvent
значения синхронизации делают).
Метка времени на сообщении, отправленном устройству (через a Receiver
) может предоставить точную информацию о синхронизации устройству. Устройство могло бы использовать эту информацию, когда это обрабатывает сообщение. Например, это могло бы скорректировать синхронизацию события несколькими миллисекундами, чтобы соответствовать информацию в метке времени. С другой стороны не все устройства поддерживают метки времени, таким образом, устройство могло бы полностью проигнорировать метку времени сообщения.
Даже если устройство поддерживает метки времени, оно не могло бы запланировать событие в течение точно времени, когда Вы запрашивали. Невозможно ожидать отправлять сообщение, метка времени которого очень далека в будущем, и имейте устройство, обрабатывают это, как Вы предназначали, и, конечно, невозможно ожидать, что устройство правильно запланирует сообщение, метка времени которого находится в прошлом! Именно до устройства, чтобы решить, как обработать метки времени, слишком далеки в будущем или находятся в прошлом. Отправитель не знает то, что устройство рассматривает, чтобы быть слишком далеким, или была ли у устройства какая-либо проблема с меткой времени. Это невежество подражает поведению внешних аппаратных устройств MIDI, которые отправляют сообщения, никогда не зная, были ли они получены правильно. (Протокол провода MIDI однонаправлен.)
Некоторые устройства отправляют сообщения, к которым добавляют метку времени (через a Transmitter
). Например, сообщения, отправленные входным портом MIDI, могли бы быть штампованы со временем входящее сообщение, достигнутое порт. На некоторых системах механизмы обработки событий заставляют определенное количество точности синхронизации быть потерянным во время последующей обработки сообщения. Метка времени сообщения позволяет исходной информации о синхронизации быть сохраненной.
Чтобы учиться, поддерживает ли устройство метки времени, вызовите следующий метод MidiDevice
:
long getMicrosecondPosition()
Этот метод возвращается-1, если устройство игнорирует метки времени. Иначе, это возвращает текущее понятие устройства времени, которое Вы, поскольку отправитель может использовать в качестве смещения, определяя метки времени для сообщений, которые Вы впоследствии отправляете. Например, если Вы хотите отправить сообщение с меткой времени для пяти миллисекунд в будущем, можно получить текущую позицию устройства в микросекундах, добавить 5000 микросекунд, и использования что как метка времени. Имейте в виду что MidiDevice's
понятие времени всегда помещает нуль времени в то время, когда устройство было открыто.
Теперь, со всем этим объяснение меток времени как фон, давайте возвратимся к send
метод Receiver
:
void send(MidiMessage message, long timeStamp)
timeStamp
параметр выражается в микросекундах согласно понятию устройства получения времени. Если устройство не поддерживает метки времени, оно просто игнорирует timeStamp
параметр. Вы не обязаны метку времени сообщения, которые Вы отправляете получателю. Можно использовать-1 для timeStamp
параметр, чтобы указать, что Вы не заботитесь о корректировке точной синхронизации; Вы только оставляете это до устройства получения, чтобы обработать сообщение, как только это может. Однако, не желательно передаться-1 с некоторыми сообщениями и явными метками времени с другими сообщениями, отправленными тому же самому получателю. Выполнение так, вероятно, вызовет неисправности в результирующей синхронизации.
Мы видели, как можно отправить сообщение MIDI непосредственно получателю, не используя передатчик. Теперь давайте смотреть на большее количество общего падежа, где Вы не создаете сообщения MIDI с нуля, но просто соединяете устройства вместе так, чтобы один из них мог отправить сообщения MIDI другому.
Конкретный случай, который мы возьмем в качестве нашего первого примера, соединяет секвенсер с синтезатором. После того, как это соединение делается, запуская выполнение секвенсера заставит синтезатор генерировать аудио от событий в текущей последовательности секвенсера. Пока, мы проигнорируем процесс загрузки последовательности от файла MIDI в секвенсер. Кроме того, мы не будем входить в механизм игры последовательности. Загрузка и игра последовательностей обсуждаются подробно в Главе 11, "Игра, Запись, и Редактирование Последовательностей MIDI." Загрузка инструментов в синтезатор обсуждается в Главе 12, "Синтезируя Звук." Пока, все, чем мы интересуемся, - то, как сделать соединение между секвенсером и синтезатором. Это будет служить иллюстрацией более общего процесса соединения передатчика одного устройства к получателю другого устройства.
Для простоты мы будем использовать секвенсер по умолчанию и синтезатор по умолчанию. (См. Главу 9, "Получая доступ к Системным ресурсам MIDI," для больше об устройствах по умолчанию и как получить доступ к устройствам не по умолчанию.)
Sequencer seq; Transmitter seqTrans; Synthesizer synth; Receiver synthRcvr; try { seq = MidiSystem.getSequencer(); seqTrans = seq.getTransmitter(); synth = MidiSystem.getSynthesizer(); synthRcvr = synth.getReceiver(); seqTrans.setReceiver(synthRcvr); } catch (MidiUnavailableException e) { // handle or throw exception }
У реализации мог бы фактически быть единственный объект, который служит и секвенсером по умолчанию и синтезатором по умолчанию. Другими словами реализация могла бы использовать класс, который реализует обоих Sequencer
взаимодействуйте через интерфейс и Synthesizer
интерфейс. В этом случае, вероятно, не было бы необходимо сделать явное соединение, которое мы сделали в коде выше. Для мобильности, тем не менее, более безопасно не принять такую конфигурацию. При желании можно протестировать на это условие, конечно:
if (seq instanceof Synthesizer)
хотя явное соединение выше должно работать в любом случае.
Предыдущий пример кода, иллюстрированный непосредственное соединение между передатчиком и получателем. Но, что, если Вы должны отправить то же самое сообщение MIDI многократным получателям? Например, предположите, что Вы хотите получить данные MIDI от внешнего устройства, чтобы управлять внутренним синтезатором, одновременно записывая данные к последовательности. Эта форма соединения, иногда называемого, как "разветвляются" или как "разделитель", является прямой. Следующие операторы показывают, как создать соединение разветвления на выходе, через которое сообщения MIDI, достигающие входного порта MIDI, отправляются обоим a Synthesizer
объект и a Sequencer
объект. Мы предполагаем, что Вы уже получили и открыли эти три устройства: входной порт, секвенсер, и синтезатор. (Чтобы получить входной порт, Вы должны будете выполнить итерации по всем элементам, возвращенным MidiSystem.getMidiDeviceInfo
.)
Synthesizer synth; Sequencer seq; MidiDevice inputPort; // [obtain and open the three devices...] Transmitter inPortTrans1, inPortTrans2; Receiver synthRcvr; Receiver seqRcvr; try { inPortTrans1 = inputPort.getTransmitter(); synthRcvr = synth.getReceiver(); inPortTrans1.setReceiver(synthRcvr); inPortTrans2 = inputPort.getTransmitter(); seqRcvr = seq.getReceiver(); inPortTrans2.setReceiver(seqRcvr); } catch (MidiUnavailableException e) { // handle or throw exception }
Этот код представляет двойной вызов MidiDevice.getTransmitter
метод, присваивая результаты inPortTrans1
и inPortTrans2
. Как отмечалось ранее устройству могут принадлежать многократные передатчики и получатели. Каждый раз MidiDevice.getTransmitter()
вызывается для данного устройства, другой передатчик возвращается, до больше не доступны, в котором времени будет выдано исключение.
Чтобы учиться, сколько поддерживают передатчики и получатели устройство, можно использовать следующий MidiDevice
метод:
int getMaxTransmitters()
int getMaxReceivers
()
Эти методы возвращают общее количество, принадлежавшее устройству, не в настоящий момент доступному числу.
Передатчик может передать сообщения MIDI только к одному получателю за один раз. (Каждый раз Вы вызываете Transmitter's setReceiver
метод, существующее Receiver
, если таковые вообще имеются, заменяется недавно указанным. Можно сказать, есть ли у передатчика в настоящий момент получатель, вызывая Transmitter.getReceiver
.) Однако, если у устройства есть многократные передатчики, оно может отправить данные больше чем одному устройству за один раз, соединяя каждый передатчик с различным получателем, как мы видели в случае входного порта выше.
Точно так же устройство может использовать свои многократные получатели, чтобы получить больше чем от одного устройства за один раз. Код многократного получателя это требуется, является прямым, походя непосредственно на код многократного передатчика выше. Для единственного получателя также возможно получить сообщения больше чем от одного передатчика за один раз.
Как только Вы делаетесь с соединением, можно освободить его ресурсы, вызывая close
метод для каждого передатчика и получателя, который Вы получили. Transmitter
и Receiver
интерфейсы у каждого есть a close
метод. Отметьте тот вызов Transmitter.setReceiver
не закрывает текущий получатель передатчика. Получатель оставляют открытым, и он может все еще получить сообщения от любого другого передатчика, это соединяется с ним.
Если Вы также делаетесь с устройствами, можно так же сделать их доступными для других прикладных программ, вызывая MidiDevice.close()
. Закрытие устройства автоматически закрывает все свои передатчики и получатели.