Обработка аналоговых данных

Разработка аналогового микшера

Разработка аналогового микшера
Элементы и маршруты
Группы
Разработка кода микшера
Использование стандартной библиотеки микшера
Инициализация
Тестирование


Note: Разделы “Разработка аналогового микшера” и “Использование стандартной библиотеки микшера” - взаимоисключающие. Это зависит от оценки сложности разработки драйвера.

Разработка аналогового микшера

В написании драйвера проще всего начать с аналогового микшера, потому что это самая простая часть устройства: она не требует никаких особенностей в реальном времени и небольшие ошибки не будут иметь серьезных последствий. Так, при использовании DMA есть риск испортить память других процессов (в том числе ядра), поэтому программирование PCM будет рассматриваться позже (см. раздел обработка аудио данных в формате PCM).

Также, если у устройства есть аналоговый вход (например, CD или Line In), легко протестировать аналоговый микшер отдельно от остальной звуковой карты. В противном случае, без регулировки громкости достаточно трудно определить, что код воспроизведения (или захвата) PCM работает корректно.

Если устройство использует один из стандартных кодеков (перечисленных в основные виды кодеков) см. “Использование стандартной библиотеки микшера” далее.

Элементы и маршруты

Если у вас нестандартный или неподдерживаемый кодек, то необходимо определить для него набор элементов микшера. Упрощенный кодек имеет следующую структуру:

analogmixer.png
Рисунок 1. Упрощенный кодек для аналогового микшера

Все объекты называются элементами, а линии – маршрутами микшера. Некоторые элементы носят исключительно информационный характер.

Выходной элемент - элемент типа I/O и содержит только такую информацию как количество содержащихся каналов. Другие элементы обеспечивают управление с помощью callback-функций. Например, элементы громкости имеют callback, использующийся для чтения и установки связанного с ним уровня усиления.

Одним из распространенных механизмов является микширование нескольких входов на уровне АЦП (Аналого-Цифровой Преобразователь). Обычно это делается с помощью набора переключателей вместо мультиплексора.

Последний важный пункт заключается в том, что каждый элемент связан по крайней мере с одним другим элементом. Только элементы типа I/O нарушают это правило.

Состав элементов и маршруты для микшера определяет внутренняя структура устройства.

В качестве примера можно привести стандартную диаграмму AC97, имеющую примерно 13 I/O элементов и приблизительно 43 элемента в сумме.

Чтобы на основе этой диаграммы написать микшер, необходимо создать элемент микшера для каждого объекта диаграммы, а затем создать маршрут для каждой линии.

Поддерживаемые типы элементов

На этом этапе полезно обсудить все поддерживаемые типы элементов, их соответствующие атрибуты, любые связанные элементы управления и функцию, которая используется для их создания:

3D эффект первого типа (3d_effect1)
В данный момент не используется ни в одном драйвере.
Аккумулятор первого типа (accu1)
Этот элемент суммирует входные сигналы для получения выходного. Количество выходных каналов равно количеству входных. Например, для стерео все левые входы суммируются в левый выход, а все правые входы - в правый выход. Эти элементы также могут ослаблять сигнал, и, таким образом, имеют атрибут затухания.

Функция создания: ado_mixer_element_accu1()
Аккумулятор второго типа (accu2)
Этот элемент аналогичен первому типу, за исключением того, что все входные сигналы суммируются вместе и на выходе оказывается моносигнал. Аккумулятор этого типа также имеет атрибут затухания.

Функция создания: ado_mixer_element_accu2()
Аккумулятор третьего типа (accu3)
Этот элемент аналогичен первому типу, но ослабление им сигнала является переменным. В результате этого он имеет функцию управления и атрибут, определяющий количество управляемых каналов

Функция создания: ado_mixer_element_accu3()
Конвертер (converter)
Этот элемент конвертирует одну частоту потока PCM в другую. Имеет атрибут битового разрешения.
Ввод-вывод (io)
Этот элемент фиксирует место, где поступают сигналы на входы или выходы микшера. Как правило, является реальным физическим соединением. Содержит следующие атрибуты: количество каналов в целом и количество каналов, которые он содержит. В простейшем случае стерео существует два канала: передний левый и передний правый.

Функция создания: ado_mixer_element_io()
Мультиплексор первого типа (mux1)
Этот элемент выбирает один из входов для подключения к выходу. В случае нескольких каналов вход каждого канала контролируется отдельно. Например, в диаграмме выше мультиплексор может выбирать левый канал с CD и правый - с микрофона. Атрибуты этого элемента включают количество управляемых им голосов и функцию управления. Функция создания: ado_mixer_element_mux1()
Мультиплексор второго типа (mux2)
Упрощенный мультиплексор первого типа, в котором поддерживаются только моноканалы

Функция создания: ado_mixer_element_mux2()
Pan Control первого типа (pan_control1)
В данный момент не используется ни в одном драйвере.
Импульсно-кодовый модулятор первого типа (pcm1, dac1, adc1)
Этот элемент преобразует цифровой сигнал в аналоговый для выхода или аналоговый в цифровой для входа. Является связующим элементом между аналоговым микшером и цифровыми PCM частями звуковой карты. Этот элемент имеет атрибут, определяющий, каким PCM устройством он является.

Функция создания: ado_mixer_element_pcm1()
Импульсно-кодовый модулятор второго типа (pcm2, dac2, adc2)
Этот элемент используется, когда pcm1 поддерживает несколько подканалов. Каждый активный подканал показывается как элемент pcm2, подключенный через возможный элемент регулировки громкости и отключения звука к элементу pcm1. Обычно эти элементы создаются и поддерживаются API вызовами функции ado_pcm_subchn_mixer_create() и не используются явно при создании микшера.

Функция создания: ado_mixer_element_pcm2()
Переключатель первого типа (sw1)
Массив простых переключателей on-off, один на каждый канал, контролируемый переключателем. Имеет функцию для установки состояния переключателей. Обычно они используются для отключения звука потоков, содержащих более одного канала.

Функция создания: ado_mixer_element_sw1()
Переключатель второго типа (sw2)
Простой переключатель on-off. Имеет функцию для установки состояния. Обычно используется для выключения звука в моноканалах.

Функция создания: ado_mixer_element_sw2()
Переключатель третьего типа (sw3)
Этот элемент представляет собой матрицу, которая контролирует пути сигналов. Чтобы понять работу этого переключателя, представьте вектор-столбец со всеми входными сигналами и вектор-строку всех выходных сигналов. Общее число переключателей будет их произведением. Этот элемент содержит в качестве атрибутов как количество входов и выходов, так и управляющую функцию. Иногда данные переключатели используются там, где обычно мультиплексор применяется для одновременной записи из нескольких источников на входе микшера.

Функция создания: ado_mixer_element_sw3()
Регулятор тона первого типа (tone_control1)
В данный момент не используется ни одним драйвером.
Регулятор громкости первого типа (volume1)
Контролирует амплитуду(или усиление) проходящего через него аналогового сигнала. Атрибуты: количество контролируемых каналов, диапазон усиления и функция управления.

Функция создания: ado_mixer_element_volume1()

Можно связать данные объекта с более сложными элементами. Если будет необходимо иметь доступ к данным объекта позднее, необходимо вызвать ado_mixer_get_element_instance_data(), так как ado_mixer_delement_t является внутренним типом данных.

Группы

Группа – собранные воедино элементы и возможности их управления. Для упрощения написания драйверов группы определяются в отношении функционала либо воспроизведения, либо захвата.

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

Функция создания: ado_mixer_playback_group_create()
Группа захвата
Может содержать до одного элемента контроля громкости, до одного элемента отключения звука и до одного элемента выбора входа.

Функция создания: ado_mixer_capture_group_create()

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

Создание групп микшера

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

Разработка кода микшера

В целях демонстрации рассмотрим упрощенный кодек, показанный на предыдущем рисунке. В оставшейся части этой главы показано написание кода для такого кодека.

Полный код примера микшера из этой главы доступен в Исходный код микшера.

Сначала необходимо разобраться с некоторыми основными требованиями к драйверу. Необходимо предусмотреть стандартную точку входа для вызова io-audio при инициализации чипа.

Инициализация

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

Если разрабатывается собственный аудиомикшер, то следующей задачей (после того, как ctrl_init() произвела основную часть инициализации) будет выделение памяти и инициализация новой структуры ado_mixer_t. Сделайте это с помощью ado_mixer_create(). Вся касающаяся этого микшера информация будет записана в структуре, поэтому необходимо где-то сохранить копию возвращенного указателя (обычно в структуре контекста), чтобы позднее у вас был к нему доступ. Структура ado_mixer_t является внутренней и драйверу не требуется знать что находится внутри.

Ниже приведен пример инициализации драйвера:

int example_mixer( ado_card_t *card, HW_CONTEXT_T *example )
{
int32_t status;
if ( (status = ado_mixer_create( card, "Example", &example->mixer, example )) != EOK )
return (status);
return (0);
}
ado_ctrl_dll_init_t ctrl_init;
int ctrl_init( HW_CONTEXT_T **hw_context, ado_card_t *card, char *args )
{
example_t *example;
if ( (example = (example_t *)ado_calloc( 1, sizeof( example_t ) )) == NULL )
{
ado_error( "Unable to allocate memory (%s)\n", strerror( errno ) );
return (-1);
}
*hw_context = example;
/* Ниже производится проверка доступности аппаратных средств. */
if ( example_mixer( card, *hw_context ) != 0 )
return (-1);
else
return (0);
}

Если необходимо выделить память для микшера, создайте функцию очистки памяти, которую io-audio вызовет при завершении работы микшера (см. ado_mixer_set_destroy_func()).

Также можно создать функцию, вызываемую при перезагрузке аппаратной части микшера, но обычно в этом нет необходимости (см. ado_mixer_set_reset_func()).

Создание микшера

Следующим шагом необходимо сделать описание микшера из составляющих его частей. Как было описано ранее, микшер состоит из элементов микшера, путей и групп. В этом примере: из 17 элементов, 18 путей и 8 групп. Элементы и пути сравнительно просто идентифицировать.

Элементы - это любые из объектов, а пути - то, по чему данные могут перемещаться между ними. Используйте описанную ниже функцию для создания элементов, а функцию ado_mixer_element_route_add() - для создания путей.


Note: Не забудьте подсчитать количество точечных источников и приемников в качестве элементов. Хотя они не отображаются в виде символов на диаграмме, они являются важной частью архитектуры.

Определение групп проблематичнее. Правила группировки упрощают выбор способа разделения элементов и делают драйвер более определенным по поведению. Группами являются: главный выход (Master Output), входное усиления (Input Gain), PCM OUT, MIC OUT, CD OUT, PCM IN, MIC IN и CD IN.

groups.png
Рисунок 2. Группы в примере аналогового микшера

Группы PCM IN, MIC IN и CD IN включают в себя мультиплексор, но определяют разный для него вход.

При написании микшера первым делом создайте элементы и пути, затем передайте указатели на требуемые элементы функциям, создающим группы микшера.

Ниже приведена часть кода, создающая главную группу, включающую в себя все элементы и пути:

int build_example_mixer( MIXER_CONTEXT_T *example, ado_mixer_t *mixer )
{
int error = 0;
ado_mixer_delement_t *pre_elem = NULL,
*elem = NULL;
/* ################ */
/* Выходная группа */
/* ################ */
if ( (example->output_accu = ado_mixer_element_accu1( mixer, SND_MIXER_ELEMENT_OUTPUT_ACCU, 0 )) == NULL )
error++;
pre_elem = example->output_accu;
if ( !error && (elem = ado_mixer_element_volume1( mixer, "Output Volume", 2, output_range, example_master_vol_control,
(void *)EXAMPLE_MASTER_LEFT, NULL )) == NULL )
error++;
if ( !error && ado_mixer_element_route_add( mixer, pre_elem, elem ) != 0 )
error++;
example->master_vol = elem;
pre_elem = elem;
if ( !error && (elem = ado_mixer_element_sw2( mixer, "Output Mute", example_master_mute_control,
(void *)EXAMPLE_MASTER_LEFT, NULL )) == NULL )
error++;
if ( !error && ado_mixer_element_route_add( mixer, pre_elem, elem ) != 0 )
error++;
example->master_mute = elem;
pre_elem = elem;
if ( !error && (elem = ado_mixer_element_io( mixer, "Output", SND_MIXER_ETYPE_OUTPUT, 0, 2, stereo_voices )) == NULL )
error++;
if ( !error && ado_mixer_element_route_add( mixer, pre_elem, elem ) != 0 )
error++;
if ( !error && (example->master_grp = ado_mixer_playback_group_create( mixer, SND_MIXER_MASTER_OUT, SND_MIXER_CHN_MASK_STEREO,
example->master_vol, example->master_mute )) == NULL )
error++;
return (0);
}

Не следует стремиться определить все элементы микшера в его группах. Элементы микшера и его группы подразумеваются дополняющими друг друга. Лучшим решением будут простые элементы микшера или переключатели. Группы микшера создаются, чтобы помочь разработчику приложений выяснить, какие элементы микшера связаны между собой и с конкретным соединением (например, PCM OUT).

В этом примере микшера ни одна из входных групп (PCM IN, MIC IN, CD IN) не имеет элемента управления громкостью или выключением звука. Они необходимы еще и потому, что содержат переключатель выбора источника для захвата данных, а не только элементы управления громкостью и выключением звука. Если упущен элемент микшера в аппаратной части, то можно определить его как NULL, если это необходимо для группы.

Использование стандартной библиотеки микшера

Если устройство использует один из стандартных кодеков (перечисленных в основные виды кодеков), количество необходимых действий уменьшается.

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

Необходимо разобраться с некоторыми основными требованиями к драйверу и предусмотреть стандартную точку входа для вызова io-audio для инициализации чипа.

Инициализация

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

После успешного определения наличия устройства необходимо смапировать память и инициализировать мьютекс в структуре контекста. Мьютекс используется для эксклюзивного доступа потока к регистрам устройства. Захватывать мьютекс следует при любых действиях с регистрами.


Note: Мьютекс при этом должен быть заблокированным как можно меньшее количество времени.

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

Так как кодек является стандартным, то для создания структуры микшера и загрузки соответствующей библиотеки используется функция ado_mixer_dll():

int32_t ado_mixer_dll( ado_card_t *card,
char *mixer_dll,
uint32_t version,
void *params,
void *callbacks,
ado_mixer_t **rmixer );

Аргументы ado_mixer_dll() включают в себя:

Типы данных и содержание структур параметров и обратных вызовов зависят от загружаемой библиотеки микшера; см. основные виды кодеков для дополнительной информации.

Структура параметров – основа корректной работы микшера. Она сообщает библиотеке микшера о функциях драйвера (осуществляющих чтение и запись регистров кодека). Эта структура содержит указатели на hw_context и (обычно) указатели на callback-функции работы с регистрами. hw_context - обычно (но необязательно) представляет собой тот же контекст, определенный в начале функции ctrl_init(). Контекст передается обратно в качестве параметра, когда библиотека микшера вызывает функции чтения или записи.


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

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

Тестирование

Для тестирования кода запустите драйвер и подайте на вход одного из кодеков аналоговый сигнал (line, CD, etc.). Далее, используя GUI mixer, попробуйте изменить громкость звука в динамиках. Если это произойдет успешно, можно начать изучение следующей главы.




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