LinuxBegin.ru - offline-версия от 26.04.2004
Главная | Все темы | Все статьи | Ссылки | Аналоги Windows-программ в Linux | Top 20

OSS API - Цифровой звук
(Основы программирования в Linux.)


- Руслан Попов, 17.11.03, ts.kmc.ru, оригинал -

Данная статья практически полностью базируется
на переводе документации по программированию OSS.

Введение

Цифровой звук является наиболее общим способом представления звука в компьютере. Звук хранится в виде последовательности сэмплов получаемых из аудио сигнала через постоянные интервалы времени. Семпл - это величина амплитуды сигнала в момент измерения. В некомпрессированном цифровом звуке для хранения каждого сэмпла требуется один или более байтов. Требуемое количество байт зависит от количества каналов (моно, стерео) и формата сэмпла (8 или 16 бит, mu-Law и так далее). Временной интервал между символами зависит от частоты оцифровки, обычно выражаемой в семплах получаемых за секунду или в Герцах. Диапазон стандартных частот оцифровки лежит от 8 КГц (телефония) до 48 КГц (DAT магнитофон). Современные цифровые устройства позволяют работать с частотами звука до 96 КГц (DVD звук).

Физическими устройствами используемыми в цифровом звуке являются ADC (аналого-цифровой преобразователь) и DAC (цифро-аналоговый преобразователь). Устройства имеющие одновременно АЦП и ЦАП обычно называются кодеками. Кодек используемый в звуковых картах SoundBlaster часто называют DSP (цифровой сигнальный процессор). По правде говоря, данный термин неверен, так как DSP - это мощные цифровые чипы разработанные для обработки звука.

Параметры оцифровки влияют на качество звука получаемого из записанного сигнала. Самым основным параметром является частота оцифровки, которая ограничивает максимальную частоту в сохраняемом сигнале. Теорема Котельникова-Найквиста говорит, что максимальная частота, которая может быть получена из оцифрованного сигнала равна половине частоты его оцифровки. Например, частота оцифровки 8 КГц позволяет записать сигнал, максимальная частота в котором может быть менее 4 КГц. Более высокие частоты должны быть отфильтрованы перед подачей их на DAC.

Формат кодирования (или размер сэмпла) ограничивает динамический диапазон записываемого сигнала между самым тихим и самым громким сигналами. Теоретически максимальный динамический диапазон рассчитывается так, один бит сэмпла равен 6 ДБ. Это означает, что, например, использование 8-ми битных сэмплов даёт динамический диапазон в 48 ДБ, применение 16-ти битных сэмплов позволяет достичь диапазона в 96 ДБ.

Существует зависимость от качества звука. Количество байт необходимых для хранения аудио сигнала зависит от частоты оцифровки, количества каналов и формата сэмпла. Например, хранение одной секунды звука с одним каналом, 8КГц, 8 бит занимает 8000 байт. Для его передачи потребуется 64 кбит/с поток, что соответствует одному каналу ISDN B. Тот же сигнал сохранённый в виде стерео, 48 КГц, 16 бит займёт 192 КБ. Для его передачи потребуется 1.5 мбит/с поток, что эквивалентно каналу T1.

Посмотрим на это с другой стороны, при максимальном качестве 1 МБ памяти может хранить всего 5.46 секунды сигнала. При параметре оцифровки 8 КГц, 8 бит, моно тот же мегабайт может хранить сигнал длительностью 131 секунду. Существует возможность сократить расходы памяти и стоимость коммуникаций с помощью компрессирования записанного сигнала, но данная тема не рассматривается в данном документе.

OSS предоставляет три вида устройств для работы со звуком. Единственным различием между ними является формат сэмплов по умолчанию при открытии этих устройств. Устройство /dev/dsp использует беззнаковое 8-ми битовое кодирование, в то время как /dev/dspW знаковое 16-ти битное кодирование в формате процессоров Intel, а /dev/audio использует логарифмическое mu-Law кодирование. Других различий между устройствами не существует. По умолчанию все они работают в режиме 8 КГц моно. Существует возможность изменить формат сэмпла с помощью ioctl вызовов, после чего все устройства стануть работать одинаково. Тем не менее, рекомендуется выбирать устройство по типу используемого формата сэмплов. Такой подход предоставляет пользователю больше возможностей при создании символических ссылок на эти устройства.

Кратко говоря, осуществлять запись с данных устройств можно с помощью обычных  системных вызовов: open, close, read и write. Стандартные параметры устройств позволяют записывать и проигрывать речь и другие сигналы с относительно низкими требованиями к качеству. Существует возможность изменять множество параметров устройств через вызовы ioctl описанные ниже. Все кодеки имеют возможность записывать и проигрывать звук. Тем не менее, существуют устройства не имеющие возможностей записывать звук. Большинство устройств могут работать в полудуплексном режиме, т.е. они могут записывать и проигрывать звук, но не одно и другое одновременно. Устройства имеющие возможность одновременно производить оба действия называются полнодуплексными.

Самым простым способом записать звук является использование стандартных команд UNIX, таких как cat и dd. Например, команда cat /dev/dsp > xyz будет записывать данные с устройства в файл с именем xyz, пока пользователь не остановит данный процесс. А команда cat xyz > /dev/dsp может быть использована для проигрывания записанного файла. Следует отметить, что может понадобиться указание источника записи с помощью программы управления микшером.

Звуковые устройства всегда открываются эксклюзивно. Если программа пытается открыть устройство когда оно уже открыто другой программой, драйвер сразу возвращает ошибку (EBUSY).

Общий метод программирования

Крайне рекомендуется ознакомиться с главой Programming Guidelines раздела Introduction документации по OSS и содержимым данного раздела. Это поможет вам избежать общих ошибок при работе с OSS API. Как минимум, вы должны обратиться к ним при возникновении проблем при работе вашей программы. В данной главе рассматривается ряд вещей, которые следует выполнить перед программированием.

Избегайте использования прибамбасов и трюков. Они необязательно сделают вашу программу лучше, но могут сделать её несовместимой с будущими устройствами или версиями OSS.

Открывайте устройство, используя флаги O_RDONLY и O_WRONLY везде где это возможно. Драйвер использует эту информацию во время принятия решений по оптимизации. Используйте флаг O_RDWR только при создании приложение, которое одновременно работает с записью и воспроизведением звука. И даже в этом случае, попытайтесь по возможности закрывать и переоткрывать устройство при переключении с записи на воспроизведение звука и наоборот.

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

Можно явно устанавливать все параметры звукового устройства с помощью программы, но это может вызвать проблемы с новыми устройствами или с профессиональными устройствами.

Всегда проверяйте возвратился ли код -1 после вызова системной функции. Это означает, что драйвер не имеет возможности выполнить запрос сделанный вашей программой.

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

Всегда определяйте параметры оцифровки, так чтобы количество каналов (моно, стерео) устанавливалось перед определением частоты оцифровки. Нарушение данного правила сделает вашу программу несовместимой с звуковыми картами подобными SoundBlaster Pro, которая поддерживает максимально 44.1 КГц в режиме моно и максимально 22.05 КГц в режиме стерео. Программа, которая определит 44.1 КГц для оцифровки, а затем переведёт звуковую карту в стерео режим, будет считать, что устройство работает на частоте 44.1 КГц, когда на самом деле будет использоваться частота оцифровки в 22.05 КГц.

Изучая старые программы, удостоверьтесь, что они следуют данным правилам и что они действительно работают. Множество старых программ сделано на ранних прототипах версий драйвера и они несовместимы с более новыми версиями (2.0 и новее).

Избегайте создания программ, которые работают только в 16-ти битном режиме, так как некоторые устройства могут поддерживать только 8-ми битный звук. Относительно легко создать программу, которая может работать с 8-ми и 16-ти бытным звуком. Это позволяет использовать вашу программу на старом оборудовании. Как минимум, вам следует проверять устройства на возможность работы в 16-ти битном режиме перед отправкой на него 16-ти битных данных. Несовпадение форматов сэмплов обычно приводит к громкиму шуму вместо ожидаемого звука.

Не пытайтесь работать в полнодуплексном режиме, не проверив возможности устройства по поддержке данного режима.

Всегда читайте и записывайте полные сэмплы. Например, в 16-ти битном стерео режиме каждый сэмпл состоит из 4 байт (по 2 байта на каждый канал). В таком случае программа должна всегда читать количество сэмплов кратное четырём. Невыполнение данного правила приводит к потере синхронизации между программой и устройством, что вызывает появление шума вместо звука или обмен каналов между собой.

Всегда закрывайте устройства, если они не используюся. Это позволит другим программам использовать устройство. Разрабатывайте интерактивные программы, которые открывают устройство только по требованию пользователя или когда программа желает проверить параметры оцифровки (в таком случае аккуратно обрабатывайте EBUSY ситуацию). Тем не менее, устройство может быть открытым, если необходимо запретить его использование другими программами.

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

Простое программирование звука

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

Определения для программы

Все программы работающие с OSS API должны подключать заголовочный файл [soundcard.h], который содержит определения API. Также необходимо подключить заголовочные файлы [ioctl.h], [unistd.h] и [fcntl.h]. Обязательно следует определить файловый указатель и буфер для хранения звуковых данных во время работы приложения. Ниже приведён пример определений для простой программы для работы со звуком:

// Стандартные заголовочные файлы
#include [ioctl.h]
#include [unistd.h]
#include [fcntl.h]
#include [sys/soundcard.h]
// Стандартные определения
#define BUF_SIZE 4096
int audio_fd;
unsigned char audio_buffer[BUF_SIZE];

Макрос BUF_SIZE используется для определения размера буфера выделенного для хранения данных. При увеличении размера буфера снижается нагрузка на систему благодаря передачи большего количества данных за один вызов. Тем не менее, использование небольшого буфера даёт хороший результат при записи. Нормальный буфер имеет размер между 1024 и 4096 байт.

Выбор и открытие звукового устройства

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

Звуковые устройства описанные выше являются символическими ссылками на реальные устройства. Например, /dev/dsp обычно указывает на /dev/dsp0, которое является первым определённым в системе звуковым устройством. Пользователь имеет возможность изменить используемое устройство с помощью создания новой символической ссылки.

Рекомендуется открывать устройство в режиме только чтения (O_RDONLY) или в режиме только записи (O_WRONLY). Полнодуплексный режим работы (O_RDWR) следует использовать только с в случае необходимости.

Следующий фрагмент кода может быть использован для открытия выбранного устройства определённого как DEVICE_NAME. Значением переменное open_mode должно быть O_WRONLY, O_RDONLY или O_RDWR. Другие флаги неопределены и не должны использоваться со звуковыми устройствами.

if (audio_fd = open(DEVICE_NAME, open_mode, 0)) == -1)
{
    /* Не удалось открыть устройство */
    perror(DEVICE_NAME);
    exit(1);
}

Рекомендуется, чтобы программа отображала сообщение об ошибке, возвращаемое функцией open, с помощью стандартных методов, таких как perror или strerror. Эта информация может оказаться важной для пользователя или для группы поддержки пытающейся определить причину невозможности открыть звуковое устройство. Нет необходимости отбрабатывать различные сообщения об ошибках разным образом. Только ошибка EBUSY (устройство занято) должно обрабатываться программой, путём повторной попытки открыть устройство через некоторое время, хотя не гарантируется, что это устройство станет после этого доступным.

Простое приложения для записи звука

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

int len;
if ((len = read(audio_fd, audio_buffer, count)) == -1)
{
    perror("audio read");
    exit(1);
}

В этом примере переменная count определяет количество байт, которое программа желает получить от устройства. Оно должно быть меньше или равно размеру audio_buffer. Также, оно должно быть кратно размеру сэмпла. Рекомендуется использовать число кратное степени двойки (т.е. 4, 8, 16, 32 и так далее).

Количество полученных от устройства байт может быть использовано для получения временного разрешения. Поток данных (байты в секунду) зависит от частоты оцифровки, формата сэмпла и количества каналов. Например, при использовании режима 8 КГц 16-ти битной стерео поток данных будет равен 8000 * 2 * 2 = 32000 байт в секунду. Это единственный способ для определения момента прекращения записи. Следует отметить, что для звуковых устройств нет понятия конец файла (EOF).

Простое приложение для проигрывания звука

Приложение для проигрывания звука работает аналогично вышеописанному приложению. Единственным различием между этими программами будет то, что данная программ использует функцию write вместо read.

Установка параметров оцифровки

Существует три параметра, которые влияют на качество звука и, соответственно, на требования к памяти и полосе пропускания. Перечислим их:

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

Изменение параметров оцифровки возможно только после открытия устройства и до первого вызова функций read, write или ioctl для используемого устройства. В случае изменения параметров оцифровки во время работы устройства, его поведение не определено. Устройство должно быть проинициализировано с помощью функции ioctl с параметром SNDCTL_DSP_RESET перед изменением параметров оцифровки.

Выбор формата сэмпла

Формат сэмпла является важным параметром влияющим на качество звука. OSS API поддерживает несколько различных форматов сэмплов, но большинство устройств поддерживает только некоторые из них. В заголовочном файле [soundcard.h] определены следующие идентификаторы для форматов сэмпла.
  • AFMT_QUERY - Не является звуковым форматом, но данный идентификатор используется для определения текущего формата.
  • AFMT_MU_LAW - Логарифмическое mu-law кодирование сэмпла.
  • AFMT_A_LAW - Логарифмическое F-law кодирование семпла.
  • AFMT_IMA_ADPCM - Формат компрессии (4:1), где 16-ти битная последовательность представляет 4 сэмпла. Существует несколько различных ADPCM форматов, данный формат определён Interactive Multimedia Association. Формат Creative ADPCM используемый в SoundBlaster 16 не совместим с данным форматом.
  • AFMT_U8 - Стандартный беззнаковый 8-ми битный формат используемый в звуковых картах PC.
  • AFMT_S16_LE - Стандартный знаковый 16-ти битный (intel) формат используемый в звуковых картах PC.
  • AFMT_S16_BE - Вариант предыдущего формата для платформ M68K, PowerPC, SPARC и так далее.
  • AFMT_S16_NE - Знаковый 16-ти битный формат, порядок следования байт в слове зависит от используемого процессора.
  • AFMT_S8 - Знаковый 8-ми битный формат.
  • AFMT_S32_LE - Знаковый 32-х битный (intel) формат. Используется для 24-х битного звука, причем для хранения используются старшие 24-е бита, остальные биты сброшены в 0.
  • AFMT_S32_BE - Вариант предыдущего формата для платформ M68K, PowerPC, SPARC и так далее.
  • AFMT_U16_LE - Беззнаковый 16-ти битный (intel) формат.
  • AFMT_U16_BE - Вариант предыдущего формата для платформ M68K, PowerPC, SPARC и так далее.
  • AFMT_MPEG - Формат звука MPEG MP2/MP3 (пока не поддерживается).
Важно понимать, что в большинстве устройств на аппаратном уровне поддерживается только формат AFMT_U8, хотя более новые устройства могут поддерживать только 16-ти бытные форматы. Распространёнными форматами являются AFMT_S16_LE и AFMT_MU_LAW. На многих устройствах формат AFMT_MU_LAW эмулируется с помощью программного преобразования между mu-law и 8-ми битным форматом. Это приводит к некоторому снижению качества по сравнению с обычным 8-ми битным форматом.

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

Вышеприведённые форматы сэмпла выбраны таким образом, чтобы AFMT_U8 определялся как 8, а AFMT_SB16_LE - 16. Это делает идентификаторы совместимыми со старыми вызовами ioctl, в которых приходилось указывать количество бит на сэмпл. Данное утверждение верно только для этих двух идентификаторов, таким образом, в программе не следует использовать идентификаторы для указания размера сэмплов.

Идентификаторы AFMT_S32_XX разработаны для использования в приложениях работающих с семплами размером более 16-ти бит. Для каждого сэмпла в данном случае выделяется 32 бита (int на большинстве платформ). Для хранения информации используются старшие 24-е бита, остальные сброшены в 0.

Идентификатор AFMT_SB16_NE является макросом. Он переопределяется как AFMT_SB16_LE или AFMT_SB16_BE в зависимости от используемого процессора. Идентификатор AFMT_SB32_NE ведёт себя аналогичным образом.

Приведём количество бит требуемых для хранения сэмпла для различных форматов:
  • 4 бита - формат IMA ADPCM;
  • 8 бит - для 8-ми битных форматов, mu-law и A-law;
  • 16 бит - для 16-ти битных форматов;
  • 32 бита - для 32-х битных форматов;
  • неопределено - для формата MPEG.
Установка используемого формата производится с помощью вызова ioctl с параметром SNDCTL_DSP_SETFMT. Нижеприведённый фрагмент кода устанавливает формат AFMT_SB16_LE:

int format;
format = AFMT_SB16_LE;
if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format) == -1)
{
    /* фатальная ошибка */
    perror("SNDCTL_DSP_SETFMT");
    exit(1);
}
if (format != AFMT_SB16_LE)
{
    /* Устройство не поддерживает запрошенный формат. 
    Программа должна использовать другой формат (например,
    возвращенный в переменной format или вывести сообщение
    об ошибке и завершить работу. */
}

Для определения текущего формата в вызове ioctl следует использовать параметр AFMT_QUERY.

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

Программа может определить поддерживаемые форматы с помощью вызова ioctl с параметром SNDCTL_DSP_GETFMTS, код приведён ниже:

int mask;
if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &mask) == -1)
{
    /* Обработка фатальной ошибки */
}
if (mask & AFMT_MPEG)
{
    /* Устройство поддерживает формат MPEG */
}

SNDCTL_DSP_GETFMTS возвращает только аппаратно поддерживаемые форматы. Драйвер может поддерживать дополнительные форматы, программно преобразуя их к аппаратным. Эти форматы не отображаются при запросе поддерживаемых форматов, но могут быть выбраны с помощью SNDCTL_DSP_SETFMT. Программное преобразование формата приводит к нагрузке на процессор. Этого следует избегать.

Формат AFMT_MU_LAW поддерживается всеми устройствами. OSS до версии 3.6 всегда отображала этот формат в списке поддерживаемых. Более поздние версии отображают данный формат только если он действительно аппаратно поддерживается устройством. Данный формат обычно используется с приложениями и звуковыми файлами портированными с систем использующими данный формат (например, SunOS). Выбор количества каналов

Большинство современных устройств поддерживают стерео режим. Стандартным режимом является моно. Приложение может указать количество каналов с помощью вызова ioctl с параметром SNDCTL_DSP_CHANNELS. Некоторые устройства могут поддерживать до 16 каналов и больше.

int channels = 2; /* 1=моно, 2=стерео */
if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels) == -1)
{
    /* Фатальная ошибка */
    perror("SNDCTL_DSP_CHANNELS");
    exit(1);
}
if (channels != 2)
{
    /* Устройство не поддерживает стерео режим */
}

Приложение должно указать формат сэмпла и режим перед установкой частоты оцифровки. Существуют устройства, у которых максимальная частота оцифровки зависит от используемого режима. Программа может работать неправильно, если сначала установить частоту оцифровки, а затем изменить режим. Изменение параметров оцифровки следует производить перед первым вызовом функций read или write.

Приложение должно проверять значение возвращаемое через аргумент ioctl. Многие старые устройства совместимые с SoundBlaster 1/2 не поддерживали стерео режим. Также, существуют устройства, которые работают только в стерео режиме.

Выбор частоты оцифровки

Частота оцифровки является параметром, который определяет качество звука. OSS API позволяет выбирать частоту в пределах от 1 Гц до 2 ГГц. Тем не менее, на практике используется ограниченный набор частот. Минимальной частотой обычно является 5 КГц, а максимальная - сильно варьируется. Некоторые из старых устройств поддерживают 22.05 КГц для проигрывания и 11.025 КГц для записи. Следующее поколение устройств поддерживает 44.1 КГц для моно режима и 22.05 КГц для стерео. Современные устройства поддерживают частоту оцифровки до 96 КГц (качество DVD), но большинство устройств поддерживает 44.1 КГц (качество CD Audio).

Стандартной частотой оцифровки является 8 КГц. Тем не менее, приложение не должно зависить от значений по умолчанию.

Драйвер всегда использует частоту максимально приближённую к запрашиваемой и возвращает её значение программе. Приложение всегда должно проверять возвращаемое значение на совпадение с запрашиваемой частотой. Различие в несколько процентов можно игнорировать, так как оно не воспринимается на слух. Большая разница означает, что устройство не способно работать на запрашиваемой частоте или что устройство настроено на использование фиксированной частоты.

При использовании некоторых профессиональных устройств, частота оцифровки может быть зафиксирована внешним источником (S/PDIF, AES/EBU, ADAT или мировыми часами). В таком случае вызов SNDCTL_DSP_SPEED не сможет изменить частоту оцифровки и программе будет возвращено текущее значение частоты. Данный тип исключительной ситуации описан в документации по соответствующему драйверу.

Следующий фрагмент кода демонстрирует изменение частоты оцифровки:

int speed = 11025;
if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed) == -1)
{
    /* Фатальная ошибка */
    perror("SNDCTL_DSP_SPEED");
    exit(1)
}
if ( /* возвращённое значение сильно отличается от запрошенного */)
{
    /* Устройство не поддерживает запрашиваемую частоту */
}

Приложение должно указать формат сэмпла и режим перед установкой частоты оцифровки. Существуют устройства, у которых максимальная частота оцифровки зависит от используемого режима. Программа может работать неправильно, если сначала установить частоту оцифровки, а затем изменить режим. Изменение параметров оцифровки следует производить перед первым вызовом функций read или write.

Другие ioctl вызовы

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

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

Вызов ioctl с параметром SNDCTL_DSP_SYNC может использоваться для остановки приложения до момента завершения проигрывания блока данных (данный вызов не применим во время записи). После этого устройство освобождается и управление передается программе. Следует отметить, что данный вызов может занять некоторое время, которое зависит от объема проигнрываемых данных. Закрытие любого звукового устройства вызывает вызов SNDCTL_DSP_SYNC. Крайне рекомендуется закрывать устройство и открывать его вновь вместо использования SNDCTL_DSP_SYNC.

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

Вызов ioctl с параметром SNDCTL_DSP_POST является более простой версией вызова SNDCTL_DSP_SYNC. Данный вызов даёт драйверу указание приостановить воспроизведение. Данный вызов не блокирует приложение.

Вызовы всех этих ioctl приводят к появлению щелчков или пауз при воспроизведении. Данные вызовы следует использовать только в случае необходимости.

Существует несколько мест в программе где следует использовать эти вызовы. Вызов SNDCTL_DSP_POST применяется для остановки воспроизведения на относительно долгое время. Вызовы SNDCTL_DSP_RESET и SNDCTL_DSP_SYNC должны использоваться перед изменением параметров оцифровки. Тем не менее, грамотнее будет переоткрыть устройство и назначить новые параметры.

Приложение должно использовать вызовы SNDCTL_DSP_SYNC и SNDCTL_DSP_RESET для переключения между режимами записи и воспроизведения. Тем не менее, грамотнее будет переоткрыть устройство.

Интерпретирование данных

Кодирование звуковых данных зависит от используемого формата. Существует несколько наиболее используемых форматов, которые рассмотрены ниже.

Формат mu-law (логарифмическое кодирование)

Данный формат берёт своё начало в цифровой телефонии. Каждый сэмпл представляется в виде 8-ми битового значения, которое получено с помощью компрессии из 16-ти битного. Из-за логарифмического кодирования, значение должно преобразовываться в линейный формат перед его использованием (два значения в этом формате нельзя складывать без предварительного преобразования). Процедура преобразования не рассматривается в данном документе. Избегайте использования данного формата, используйте линейные 8-ми и 16-ти битные форматы.

Формат 8-ми битный беззнаковый

Это стандартный (SoundBlaster) формат, который поддерживается практически всеми устройствами. Каждый сэмпл занимает 8-ми битный байт. Значение 0 представляет минимальный уровень, а 255 - максимальный. В качестве нулевого уровня используется значение 128 (0x80).
В языке C для этого формата используется тип unsigned char. Для преобразования беззнакового 8-ми битного числа в знаковое необходимо из него вычесть 128. Аналогичное действие осуществляется с помощью логической операции XOR:

value ^= 0x80;
 
Формат 16-ти битный знаковый

Данный формат является непортируемым и одновременно зависит от архитектуры процессора и устройства. В данной ситуации лучше всего использовать формат представления 16-ти битного числа принятый на архитектуре Intel.

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

При использовании 16-ти битных знаковых данных, в программах на языке C необходимо использовать тип signed short, но данное правило верно только для архитектуры Intel. К тому же стандарт языка C не гарантирует, что тип signed short везде будет иметь размер 16 бит. Поэтому, использование типа signed short для буфера данных рассматривается как программная ошибка, хотя данный метод повсеместно используется.

В данном случае следует использовать массив значений unsigned char и принудительно преобразовывать данные передаваемые драйверу. Например:

unsigned char devbuf[4096];
int applicbuf[4096];
int i, p=0;
/* Поместим 2048 16-ти битных сэмплов в applicbuf[] */
for (i=0; i<2048; i+=2)
{
    /* сначала отправляем младший байт, а затем старший */
    devbuf[p++] = (unsigned char)(applicbuf[i] & 0xFF);
    devbuf[p++] = (unsigned char)((applicbuf[i] >> 8) & 0xFF);
}
/* Передаём данные драйверу... */

Преобразование записанных данных производится аналогичным образом.

Формат AFMT_S16_NE может использоваться в случае локальных преобразований данных. Данный формат автоматически принимает соответствующую архитектуре форму. В данном случае для хранения данных можно использовать тип signed short.

Форматы 24-х и 32-х битные знаковые

Форматы AFMT_S32_LE, AFMT_S32_BE и AFMT_S32_NE используются для хранения данных соответствующей точности. Данные выравниваются в сторону старших битов, неиспользуемые биты сбрасываются в 0.

Кодирование стерео данных

При использовании стерео режима на каждый момент времени приходится два сэмпла. Сэмпл для левого канала всегда идёт впереди семпла для правого. Сэмплы для обоих каналов имеют одинаковый формат.

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

Многоканальность

OSS поддерживает профессиональное многоканальное оборудование (до 16 моно или до 8 стерео каналов). В зависимости от используемого устройства существует два способа работы с несколькими каналами. В некоторых случаях драйвер поддерживает оба метода одновременно.

Одно устройство с несколькими каналами

В данном случае существует одно устройство (например, /dev/dsp), которое работает с множеством каналов. Приложение может запросить необходимое число каналов через вызов SNDCTL_DSP_CHANNELS. Данные будут закодированы аналогично данным получаемым в стерео режиме.

Множество устройств

В данном случае существует несколько устройств (например, /dev/dspM .. /dev/dspM+N, где N - количество каналов). Приложение или несколько приложений могут открыть соответствующие устройства. Также возможно открытие устройств в стерео режиме.

Использование обоих методов

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

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

Если кратко, то оставьте работу с микшером соответствующему программному обеспечению. Не встраивайте эту функциональность в своё приложение!

Выводы

Приведённый материал является достаточным для создания простых приложений для работы со звуковыми устройствами. Существует множество других ioctl вызовов, но обычно они не используются. Тем не менее, в приложениях реального времени (например, в играх, системах конференций, анализаторах звука и так далее) требуется использовать более изощрённые методы. Их мы рассмотрим позднее...

Просмотров: 901  |  Рейтинг статьи: 5 (Голосов: 1).
5
Дата размещения статьи: 2003-11-22 17:54:51



5 последних поступлений в раздел Основы программирования в Linux.:

29.12.03: Однострочник месяца на Perl: Приключение с произвольными архивами ("Резолв Дот Конф, простой... м-м-м, скажем, сисадмин. Недавно его жесточайшим образом заставили устан...)
29.12.03: Создание man-страниц (Работа каждой программы, запускаемой из UNIX shell'а, описана в man-странице. В этой заметке мы рассм...)
10.12.03: Новая рассылка "Программирование в Linux с нуля" (Автор рассылки (программист под Linux со стажем) поэтапно научит читателей профессиональному програм...)
06.12.03: Занимательное пингвиностроение. Фундамент (Этим коротким вступлением я собираюсь начать небольшой цикл статей, посвященный некоторым аспектам на...)
22.11.03: OSS API - Цифровой звук (Крайне рекомендуется ознакомиться с главой Programming Guidelines раздела Introduction документации п...)

Комментарии:

LinuxBegin.ru © 2003-2004 Valery V. Kachurov | Условия использования материалов | О проекте