Глава 9. Интерфейс RawMIDI |
Предыдущая Содержание Следующая |
ОбзорИнтерфейс raw MIDI (необработанный MIDI) используется для доступа к аппаратным портам MIDI, которые могут быть доступны как поток байтов. Он не используется для чипов-синтезаторов, которые непосредственно не понимают MIDI.
ALSA обрабатывает управление файлом и буфером. Все, что необходимо сделать, это написать код для перемещения данных между буфером и оборудованием.
API rawMIDI определён в <sound/rawmidi.h>. КонструкторЧтобы создать устройство rawmidi, вызовите функцию snd_rawmidi_new:
struct snd_rawmidi *rmidi; err = snd_rawmidi_new(chip->card, "MyMIDI", 0, outs, ins, &rmidi); if (err < 0) return err; rmidi->private_data = chip; strcpy(rmidi->name, "My MIDI"); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
Первым аргументом является указателем карты, второй аргумент - строка идентификации.
Третий аргумент является индексом этого компонента. Вы можете создать до 8 устройств rawmidi.
Четвертый и пятый аргументы - это количество выходных и входных субпотоков, соответственно, этого устройства (субпоток эквивалентен порту MIDI).
Чтобы указать возможности устройства, установите поле info_flags. Установите SNDRV_RAWMIDI_INFO_OUTPUT, если есть хотя бы один выходной порт, SNDRV_RAWMIDI_INFO_INPUT, если есть хотя бы один входной порт, и SNDRV_RAWMIDI_INFO_DUPLEX, если устройство может одновременно обрабатывать ввод и вывод.
После создания устройства rawmidi для каждого субпотока необходимо установить операции (обратные вызовы). Для установки операций для всех субпотоков устройства имеются вспомогательные функции:
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mymidi_output_ops); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mymidi_input_ops);
Операции обычно определяются следующим образом:
static struct snd_rawmidi_ops snd_mymidi_output_ops = { .open = snd_mymidi_output_open, .close = snd_mymidi_output_close, .trigger = snd_mymidi_output_trigger, };
Эти обратные вызовы описаны в разделе Обратные вызовы.
Если есть более одного субпотока, каждому из них необходимо дать уникальное имя:
struct snd_rawmidi_substream *substream; list_for_each_entry(substream, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams, list) { sprintf(substream->name, "My MIDI Port %d", substream->number + 1); } /* что-нибудь для SNDRV_RAWMIDI_STREAM_INPUT */
Обратные вызовыВо всех обратных вызовах закрытые данные, которые установлены для устройства rawmidi, могут быть доступны как substream->rmidi->private_data.
Если имеется более одного порта, ваши обратные вызовы могут определить индекс порта из данных структуры snd_rawmidi_substream, передаваемой в каждый обратный вызов:
struct snd_rawmidi_substream *substream; int index = substream->number;
Обратный вызов open
static int snd_xxx_open(struct snd_rawmidi_substream *substream);
Он вызывается, когда субпоток открывается. Вы можете здесь проинициализировать оборудование, но ещё не должны начинать передачу/приём данных. Обратный вызов close
static int snd_xxx_close(struct snd_rawmidi_substream *substream);
Угадайте, зачем.
Последовательность работы обратных вызовов open и close устройства rawmidi управляется с помощью мьютекса и они могут спать. Обратный вызов trigger для выходных субпотоков
static void snd_xxx_output_trigger(struct snd_rawmidi_substream *substream, int up);
Он вызывается с ненулевым параметром up, когда есть данные в буфере субпотока, который должен быть передан.
Чтобы прочитать данные из буфера, вызовите snd_rawmidi_transmit_peek. Она вернёт количество прочитанных байт; оно будет меньше, чем количество запрошенных байтов, если в буфер нет больше данных. После того, как данные были успешно переданы, вызовите snd_rawmidi_transmit_ack, чтобы удалить данные из буфера субпотока:
unsigned char data; while (snd_rawmidi_transmit_peek(substream, &data, 1) == 1) { if (snd_mychip_try_to_transmit(data)) snd_rawmidi_transmit_ack(substream, 1); else break; /* аппаратный буфер FIFO полон */ }
Если вы знаете заранее, что оборудование примет данные, можно использовать функцию snd_rawmidi_transmit, которая считывает данные и сразу удаляет их из буфера:
while (snd_mychip_transmit_possible()) { unsigned char data; if (snd_rawmidi_transmit(substream, &data, 1) != 1) break; /* данных больше нет */ snd_mychip_transmit(data); }
Если вы знаете заранее, сколько байтов могут быть приняты, в функциях snd_rawmidi_transmit* можно использовать буфер большего размера, чем единица.
Обратный вызов trigger не должен спать. Если перед передачей буфера субпотока оказалось, что аппаратный FIFO полон, вы должны продолжить передавать данные позже, либо в обработчике прерывания, либо с помощью таймера, если оборудование не имеет прерывания передачи MIDI.
Обратный вызов trigger вызывается с нулевым параметром up, если передача данных должна быть прервана. Обратный вызов trigger для субпотоков ввода
static void snd_xxx_input_trigger(struct snd_rawmidi_substream *substream, int up);
Он вызывается с ненулевым параметром up для разрешения получения данных, или с нулевым параметром up для запрета приёма данных.
Обратный вызов trigger не должен спать; фактическое чтение данных из устройства обычно выполняется в обработчике прерывания.
Если приём данных разрешён, ваш обработчик прерывания должен вызывать snd_rawmidi_receive для всех полученных данных:
void snd_mychip_midi_interrupt(...) { while (mychip_midi_available()) { unsigned char data; data = mychip_midi_read(); snd_rawmidi_receive(substream, &data, 1); } }
Обратный вызов drain
static void snd_xxx_drain(struct snd_rawmidi_substream *substream);
Используется только с выходными субпотоками. Эта функция должна ждать, пока не будут переданы все данные, считанные из буфера субпотока. Это гарантирует, что устройство может быть закрыто, а драйвер выгружен, без потери данных.
Этот обратный вызов не является обязательным. Если drain не установлен в структуре struct snd_rawmidi_ops, ALSA вместо этого будет просто ждать в течение 50 миллисекунд.
|
Предыдущая Содержание Следующая |