Spec-Zone .ru
спецификации, руководства, описания, API
След: Звук
Передача и Получение сообщений MIDI
Домашняя страница > Звук

Передача и Получение сообщений MIDI

Понимая Устройства, Передатчики, и Получатели

API Звука Java определяет направляющую сообщение архитектуру для данных MIDI, это гибко и удобно, как только Вы понимаете, как это работает. Система основана на проекте соединения модуля: отличные модули, каждый из которых выполняет определенную задачу, могут быть соединены (сетевые), включающие данные, чтобы вытекать из одного модуля другому.

Основной модуль в системе обмена сообщениями API Звука Java MidiDevice интерфейс. 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. Мы теперь должны исследовать точно, что означает параметр метки времени, который является предметом следующего раздела.

Понимание Меток времени

Как Вы уже знаете, у спецификации MIDI есть различные части. Одна часть описывает протокол "провода" MIDI (сообщения, отправленные между устройствами в режиме реального времени), и другая часть описывает Стандартные Файлы MIDI (сообщения, хранившие как события в "последовательностях"). В последней части спецификации каждое событие, сохраненное в стандартном файле MIDI, тегируется со значением синхронизации, которое указывает, когда то событие должно играться. В отличие от этого, сообщения в протоколе провода MIDI, как всегда предполагается, сразу обрабатываются, как только они получаются устройством, таким образом, у них нет никаких сопроводительных значений синхронизации.

API Звука Java добавляет дополнительное скручивание. Это не удивляет, что значения синхронизации присутствуют в MidiEvent объекты, которые хранятся в последовательностях (как мог бы быть считан из файла MIDI), так же, как в Стандартной спецификации Файлов MIDI. Но в API Звука Java, даже сообщения, отправленные между devices⠀” другими словами, сообщения, которые соответствуют MIDI, соединяют protocolâ проводом €”, может быть дан значения синхронизации, известные как метки времени. Именно эти метки времени касаются нас здесь.

Метки времени на сообщениях, Отправленных Устройствам

Метка времени, которая может дополнительно сопровождать сообщения, отправленные между устройствами в 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 в секвенсер. Кроме того, мы не будем входить в механизм игры последовательности. Загрузка и игра последовательностей обсуждаются подробно в Игре, Записи, и Редактировании Последовательностей 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
    }

У реализации мог бы фактически быть единственный объект, который служит и секвенсером значения по умолчанию и синтезатором значения по умолчанию. Другими словами реализация могла бы использовать class, который реализует обоих 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(). Закрытие устройства автоматически закрывает все свои передатчики и получатели.


Проблемы с примерами? Попытайтесь Компилировать и Выполнить Примеры: FAQ.
Жалобы? Поздравление? Предложения? Дайте нам свою обратную связь.

Предыдущая страница: Доступ к Системным ресурсам MIDI
Следующая страница: Введение в Секвенсеры