Глава 9. Интерфейс RawMIDI

Предыдущая  Содержание  Следующая V*D*V

Обзор

Интерфейс 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 миллисекунд.

 

Предыдущая  Содержание  Следующая