Архитектура микшера

Внутреннее устройство микшера

Данная глава включает в себя:

Открытие устройства микшера
Контроль групп микшера
Наилучшая группа микшера, относительно субканала PCM
Поиск групп микшера
Уведомления о событиях микшера
Закрытие устройства микшера

Обычно для конструирования микшера требуется небольшое число компонентов, каждый из которых выполняет собственную функцию. Компоненты структуры микшера принято именовать элементами:

Вход (Input)
Соединение, где внешний аналоговый сигнал поступает в микшер.
Выход (Output)
Соединение, где цифровой сигнал исходит из микшера.
АЦП (ADC)
Элемент, конвертирующий аналоговые сигналы в цифровые сэмплы.
ЦАП (DAC)
Элемент, конвертирующий цифровые сэмплы в аналоговые сигналы.
Переключатель (Switch)
Элемент, соединяющий две или более точки микшера вместе. Простейший переключатель может быть использован в качестве контроллера включения звука (mute). Более сложные переключатели могут отключать (mute) каналы в потоке по потдельности и даже формировать матричные соединители, где n входных сигналов соединяются с n выходными.
Контроль громкости (Volume)
Элемент, способный регулировать уровень громкости сигнала посредством затухания или усиления.
Аккумулятор (Accumulator)
Элемент, объединяющий (смешивающий) несколько входных сигналов в один выходной.
Мультиплексор (Multiplexer)
Элемент, позволяющий одному из нескольких входных сигналов стать выходным сигналом элемента.

Объединяя возможности этих элементов возможно конструирование простейшего микшера аудио устройства:

analogmixer.png
Рисунок 1. Структура простейшего микшера

Контроль элементов микшера осуществляется с помощью snd_mixer_element_read() и snd_mixer_element_write(), но данный метод не рекомендуется, поскольку:

Сам по себе интерфейс управления элементом является нижележащим уровнем контроля микшером, он достаточно сложен. Более простым является объединение элементов в группы микшера. Они подразделяются на группы захвата и воспроизведения. Рассмотрим правила объединенияэлементов в группы:

Применяя эти правила к диаграме на рис. 1, получим следующее:

Группа воспроизведения PCM
Элементы B (volume) и C (switch).
Группа воспроизведения MIC
Элементы E (volume) и F (switch).
Группа воспроизведения CD
Элементы L (volume) и M (switch).
Группа воспроизведения MASTER
Элементы H (volume) и I (switch).
Группа захвата MIC
Элемент N (multiplexer) без переключателя или регулятора громкости.
Группа захвата CD
Элемент N (multiplexer) без переключателя или регулятора громкости.
Группа захвата INPUT
Элементы O (volume) и P (switch).

Разделение на группы создало 7 контролируемых сущностей вместо 17 исходных элементов, что существенно упрощает управление микшером. Кроме того, именно группы в общем случае соответствуют интерфейсным ожиданиям пользователя.

Открытие устройства микшера

Для создания соединения с микшером используется snd_mixer_open(). Этот вызов имеет аргументы для выбора аудио устройства и микшера по индексу. В большинстве случаев микшер в устройстве один.

Вызов snd_mixer_open() возвращает дескриптор открытого микшера типа snd_mixer_t *. Сам тип прозрачен для драйвера.

Контроль групп микшера

Наилучший способ контроля групп микшера – схема чтение-модификация-запись. При этом сначала производится анализ диапазонов возможных значений и общих возможностей группы, а уже потом ее конфигурирование.

На первом шаге считываются параметры группы. Каждая группа микшера имеет имя, но оно может быть не уникальным. Уникальными их делает комбинация имени и индекса. Последний определяет порядковый номер реализации группы. В большинстве случаев индекс будет равен 0 – с этого значения начинается индексация.

Для чтения группы используется snd_mixer_group_read(). Аргументами является дескриптор микшера и структура контроля группы snd_mixer_group_t. Для чтения конкретной группы следует установить имя и индекс в snd_mixer_gid_t. Успешный вызов snd_mixer_group_read() приведет к заполнению структуры, возвращающей возможности и параметры указанной группы.

После получения этих сведений можно приступать к их модификации в структуре перед записью новых параметров непосредственно в группу. Для применения параметров (записи) используется функция snd_mixer_group_write().

Наилучшая группа микшера, относительно субканала PCM

В типичном микшере может присутствовать множество элементов контроля группы воспроизведения, а также несколько элементов регулирования громкости и отключения звука.

Так, например, на пути следования потока от входа микшера до выхода, может существовать несколько переключателей, каждый из которого должен быть "включен" для того, чтобы услышать звук. Аналогичная ситуация с регилировкой громкости. Некоторые регуляторы могут влиять сразу на несколько потоков. Типичным примером является группа "Master", которая определяет в том числе общую громкость всех потоков. Такая общая группа, при этом, существовать не обязана.

О наилучшей группе для регулирования параметров конерктного субканала PCM может сообщить драйвер. Ее идентификатор можно получить с помощью snd_pcm_channel_setup() или snd_pcm_plugin_setup():

memset( &setup, 0, sizeof( setup ) );
memset( &group, 0, sizeof( group ) );
setup.channel = SND_PCM_CHANNEL_PLAYBACK;
setup.mixer_gid = &group.gid;
if ( (rtn = snd_pcm_plugin_setup( pcm_handle, &setup )) < 0 )
{
return (-1);
}


Note: Структуру из примера следует занулить и затем через поле mixer_gid передать указатель на буфер для возвращения идентификатора группы.

Следует отметить, что лучшая группа может отличаться от вызова к вызову. Это зависит от состояния субканала. Следует помнить, что субканалы не создаются до тех пор, пока не будут установлены параметры канала. Точно так же и группа субканала микшера не будет доступна, пока не будет создан подканал.

Поиск групп микшера

Полный перечень групп можно получить с помощью snd_mixer_groups(). Вызов обычно используется дважды: сначала определяется число групп, а затем их идентификаторы. Аргументами функции являются дескриптор микшера и структура snd_mixer_group_t. Последняя содержит указатель на массив идентификаторов групп snd_mixer_gid_t и его размер. Пример заполнения структуры (функция snd_strerror() печатает сообщения об ошибках):

while ( 1 )
{
memset( &groups, 0, sizeof( groups ) );
if ( (ret = snd_mixer_groups( mixer_handle, &groups ) < 0) )
{
fprintf( stderr, "snd_mixer_groups API call - %s", snd_strerror( ret ) );
}
mixer_n_groups = groups.groups_over;
if ( mixer_n_groups > 0 )
{
groups.groups_size = mixer_n_groups;
groups.pgroups = (snd_mixer_gid_t *)malloc( sizeof( snd_mixer_gid_t ) * mixer_n_groups );
if ( groups.pgroups == NULL )
fprintf( stderr, "Unable to malloc group array - %s", strerror( errno ) );
groups.groups_over = 0;
groups.groups = 0;
if ( snd_mixer_groups( mixer_handle, &groups ) < 0 )
fprintf( stderr, "No Mixer Groups " );
if ( groups.groups_over > 0 )
{
free( groups.pgroups );
continue;
} else {
printf( "sorting GID table \n" );
snd_mixer_sort_gid_table( groups.pgroups, mixer_n_groups, snd_mixer_default_weights );
break;
}
}
}

Уведомления о событиях микшера

В общем случае все приложения микшера должны отслеживать изменения в нем. Это достигается путем добавления в очередь события-уведомления, для всех приложений, кроме того, которое вызвало изменения. Драйвер отправит эти события всем приложениям, которые имеют открытый дескриптор микшера и не использовали snd_mixer_set_filter() для маскирования данных событий.

Приложения могут использовать snd_mixer_read() для получения событий в очереди микшера. Ее аргументами являются дескриптор микшера и структура, описывающая callback-функции по типам событий.

Функция select() при этом позволяет момент времени, когда требуется вызвать snd_mixer_read(). Дескриптор файла для select() может быть получен с помощью snd_mixer_file_descriptor().

Пример:

static void mixer_callback_group( void *private_data, int cmd, snd_mixer_gid_t *gid )
{
switch ( cmd )
{
case SND_MIXER_READ_GROUP_VALUE:
printf( "Mixer group %s %d changed value \n", gid->name, gid->index );
break;
case SND_MIXER_READ_GROUP_ADD:
break;
case SND_MIXER_READ_GROUP_REMOVE:
break;
}
}
int mixer_update( int fd, void *data, unsigned mode )
{
snd_mixer_callbacks_t callbacks = { 0, 0, 0, 0 };
callbacks.group = mixer_callback_group;
snd_mixer_read( mixer_handle, &callbacks );
return (Pt_CONTINUE);
}
int main( void )
{
snd_mixer_t *mixer_handle;
int ret;
if ( (ret = snd_mixer_open( &mixer_handle, 0, 0 ) < 0) )
printf( "Unable to open/read mixer - %s", snd_strerror( ret ) );
PtAppAddFd( NULL, snd_mixer_file_descriptor( mixer_handle ), Pt_FD_READ, mixer_update, NULL );
...
}

Закрытие устройства микшера

Для закрытия дескриптора микшера достаточно вызвать функцию snd_mixer_close().




Предыдущий раздел: Библиотека libasound