Внутреннее устройство микшера
Данная глава включает в себя:
Обычно для конструирования микшера требуется небольшое число компонентов, каждый из которых выполняет собственную функцию. Компоненты структуры микшера принято именовать элементами:
Объединяя возможности этих элементов возможно конструирование простейшего микшера аудио устройства:
Контроль элементов микшера осуществляется с помощью snd_mixer_element_read() и snd_mixer_element_write(), но данный метод не рекомендуется, поскольку:
Сам по себе интерфейс управления элементом является нижележащим уровнем контроля микшером, он достаточно сложен. Более простым является объединение элементов в группы микшера. Они подразделяются на группы захвата и воспроизведения. Рассмотрим правила объединенияэлементов в группы:
Применяя эти правила к диаграме на рис. 1, получим следующее:
Разделение на группы создало 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().
В типичном микшере может присутствовать множество элементов контроля группы воспроизведения, а также несколько элементов регулирования громкости и отключения звука.
Так, например, на пути следования потока от входа микшера до выхода, может существовать несколько переключателей, каждый из которого должен быть "включен" для того, чтобы услышать звук. Аналогичная ситуация с регилировкой громкости. Некоторые регуляторы могут влиять сразу на несколько потоков. Типичным примером является группа "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);}
Структуру из примера следует занулить и затем через поле 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