PCM — это аббревиатура английского слова «импульсно-кодовая модуляция», а китайский перевод — «импульсно-кодовая модуляция». Мы знаем, что в реальной жизни звуки, слышимые человеческими ушами, представляют собой аналоговые сигналы. PCM — это технология, которая преобразует звуки из аналоговых сигналов в цифровые. Ее принцип заключается в простом использовании фиксированной частоты для выборки аналоговых сигналов. серия непрерывных импульсов с различной амплитудой в форме волны. Амплитуды этих импульсов квантуются с определенной точностью. Эти квантованные значения непрерывно выводятся, передаются, обрабатываются или записываются на носители данных, которые составляют цифровой звук. процесс генерации.
Двумя важными показателями сигналов PCM являются частота дискретизации и точность квантования. В настоящее время частота дискретизации звука компакт-диска обычно составляет 44100 Гц, а точность квантования — 16 бит. Обычно при воспроизведении музыки приложение считывает аудиоданные (MP3, WMA, AAC...) с носителя. После декодирования данные PCM наконец отправляются в аудиодрайвер, в свою очередь, при записи аудио. Драйвер непрерывно отправляет выборочные данные PCM обратно в прикладную программу, а прикладная программа выполняет такие задачи, как сжатие и сохранение. Таким образом, две основные задачи аудиодрайвера:
ALSA уже реализовала для нас мощный промежуточный уровень PCM. В своем собственном драйвере ей нужно реализовать только некоторые низкоуровневые функции, требующие доступа к оборудованию.
Для доступа к коду среднего уровня PCM,тыпервый Чтобы включить файлы заголовков<sound/pcm.h>,кроме того,Если вам нужен доступ к какой-либо функции, связанной с hw_param,может также включать<sound/pcm_params.h>。
Каждая звуковая карта может содержать до 4 экземпляров PCM, и каждый экземпляр PCM соответствует файлу устройства PCM. Это ограничение на количество экземпляров PCM связано с битовым размером номера устройства Linux. Если в будущем будут использоваться 64-битные номера устройств, мы сможем создавать больше экземпляров PCM. Но в большинстве случаев во встраиваемых устройствах экземпляра pcm достаточно.
Экземпляр PCM состоит из потока воспроизведения и потока захвата, каждый из которых состоит из одного или нескольких подпотоков.
Во встраиваемых системах это обычно не так сложно, как на рисунке 2.1. В большинстве случаев это звуковая карта и экземпляр PCM. Под PCM есть поток воспроизведения и захвата, а под поток воспроизведения и захвата.
На следующем рисунке перечислены несколько важных структур среднего уровня PCM. Это позволяет нам взглянуть на взаимосвязь между этими структурами с точки зрения UML, прояснить взаимосвязь между ними и помочь нам понять реализацию среднего уровня PCM. .
Средний уровень драйвера alsa предоставил нам API для создания нового PCM:
int snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count,\
struct snd_pcm ** rpcm);
Параметр устройство указывает, какой PCM под звуковой картой создан в данный момент. Первое устройство PCM начинается с 0.
Параметр воспроизведения_count указывает, сколько подпотоков воспроизведения будет иметь этот PCM.
Параметр capture_count указывает, сколько подпотоков захвата будет иметь этот PCM.
Еще один API для настройки интерфейса функции работы PCM:
void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops);
Создание нового PCM можно описать следующей диаграммой последовательности вызова создания нового PCM:
Рисунок 3.1 Диаграмма последовательности нового ПКМ
Каждая структура snd_minor сохраняет контекстную информацию определенного логического устройства под звуковой картой. Она заполняется на этапе создания логического устройства. При использовании логического устройства соответствующую информацию можно получить из структуры. Оборудование PCM не является исключением и также должно использовать эту структуру. Эта структура определена в include/sound/core.h.
struct snd_minor {
int type; /* SNDRV_DEVICE_TYPE_XXX */
int card; /* card number */
int device; /* device number */
const struct file_operations *f_ops; /* file operations */
void *private_data; /* private data for f_ops->open */
struct device *dev; /* device for sysfs */
};
Глобальный массив указателей snd_minor определен в файле sound/sound.c:
static struct snd_minor *snd_minors[256];
Как упоминалось ранее, на этапе регистрации звуковой карты (snd_card_register) будет вызвана функция обратного вызова pcm snd_pcm_dev_register(). В этой функции будет вызвана функция snd_register_device_for_dev():
static int snd_pcm_dev_register(struct snd_device *device)
{
......
/* register pcm */
err = snd_register_device_for_dev(devtype, pcm->card,
pcm->device,
&snd_pcm_f_ops[cidx],
pcm, str, dev);
......
}
Давайте снова введем snd_register_device_for_dev():
int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
const struct file_operations *f_ops,
void *private_data,
const char *name, struct device *device)
{
int minor;
struct snd_minor *preg;
if (snd_BUG_ON(!name))
return -EINVAL;
preg = kmalloc(sizeof *preg, GFP_KERNEL);
if (preg == NULL)
return -ENOMEM;
preg->type = type;
preg->card = card ? card->number : -1;
preg->device = dev;
preg->f_ops = f_ops;
preg->private_data = private_data;
mutex_lock(&sound_mutex);
#ifdef CONFIG_SND_DYNAMIC_MINORS
minor = snd_find_free_minor();
#else
minor = snd_kernel_minor(type, card, dev);
if (minor >= 0 && snd_minors[minor])
minor = -EBUSY;
#endif
if (minor < 0) {
mutex_unlock(&sound_mutex);
kfree(preg);
return minor;
}
snd_minors[minor] = preg;
preg->dev = device_create(sound_class, device, MKDEV(major, minor),
private_data, "%s", name);
if (IS_ERR(preg->dev)) {
snd_minors[minor] = NULL;
mutex_unlock(&sound_mutex);
minor = PTR_ERR(preg->dev);
kfree(preg);
return minor;
}
mutex_unlock(&sound_mutex);
return 0;
}
В конце раздела 4.1 был создан файл устройства, но в разделе 4.1 основное внимание уделяется процессу присвоения массива snd_minors. В этом разделе мы сосредоточимся на файле устройства.
Вернитесь к функции обратного вызова pcm snd_pcm_dev_register():
static int snd_pcm_dev_register(struct snd_device *device)
{
int cidx, err;
char str[16];
struct snd_pcm *pcm;
struct device *dev;
pcm = device->device_data;
......
for (cidx = 0; cidx < 2; cidx++) {
......
switch (cidx) {
case SNDRV_PCM_STREAM_PLAYBACK:
sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
break;
case SNDRV_PCM_STREAM_CAPTURE:
sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
break;
}
/* device pointer to use, pcm->dev takes precedence if
* it is assigned, otherwise fall back to card's device
* if possible */
dev = pcm->dev;
if (!dev)
dev = snd_card_get_device_link(pcm->card);
/* register pcm */
err = snd_register_device_for_dev(devtype, pcm->card,
pcm->device,
&snd_pcm_f_ops[cidx],
pcm, str, dev);
......
}
......
}
Из приведенного выше кода мы видим, что для устройства PCM могут быть созданы два файла устройства: один для воспроизведения и один для захвата. Их правила именования также определены в коде:
snd_pcm_f_ops, snd_pcm_f_ops — это стандартный массив структур файловой системы file_operations, который определен в файле sound/core/pcm_native.c:
const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.aio_write = snd_pcm_aio_write,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_playback_poll,
.unlocked_ioctl = snd_pcm_playback_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
},
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.aio_read = snd_pcm_aio_read,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_capture_poll,
.unlocked_ioctl = snd_pcm_capture_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
}
};
snd_pcm_f_ops передается как параметр snd_register_device_for_dev и записывается в поле f_ops в snd_minors[minor]. Наконец, создайте узел устройства в snd_register_device_for_dev:
snd_minors[minor] = preg;
preg->dev = device_create(sound_class, device, MKDEV(major, minor),
private_data, "%s", name);
В файле sound/core/sound.c есть функция alsa_sound_init(), которая определяется следующим образом:
static int __init alsa_sound_init(void)
{
snd_major = major;
snd_ecards_limit = cards_limit;
if (register_chrdev(major, "alsa", &snd_fops)) {
snd_printk(KERN_ERR "unable to register native major device number %d/n", major);
return -EIO;
}
if (snd_info_init() < 0) {
unregister_chrdev(major, "alsa");
return -ENOMEM;
}
snd_info_minor_register();
return 0;
}
Параметр Major в Register_chrdev такой же, как и основной, когда устройство PCM было создано до использования Device_create. В результате, когда приложение открывает файл устройства /dev/snd/pcmCxDxp, оно вводит функцию обратного вызова открытия snd_fops. будет следующий В разделе описан процесс открытия.
Из предыдущего раздела мы знаем, что при открытии устройства PCM будет вызвана функция обратного вызова snd_fops. Давайте сначала посмотрим на определение snd_fops:
static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
.open = snd_open
};
Следовать заsnd_openфункция,Сначала он берет этот номер устройства из индексного дескриптора,Затем используйте младший номер устройства в качестве индекса.,Удалить структуру snd_minor, заполняемую при регистрации устройства PCM, из глобального множества snd_minors (см. раздел 4.1),Затем возьмем f_ops устройства PCM из структуры snd_minor,и положитьfile->f_opЗаменить наpcmоборудованиеf_ops,Тогда позвоните напрямуюpcmоборудованиеf_ops->open(),затем вернись。потому чтоfile->f_opбыл заменен,после,Все вызовы чтения/записи/ioctl приложения перейдут в собственную функцию обратного вызова устройства PCM.,То есть обратный вызов, определенный в структуре snd_pcm_f_ops, упомянутой в разделе 4.2.
static int snd_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct snd_minor *mptr = NULL;
const struct file_operations *old_fops;
int err = 0;
if (minor >= ARRAY_SIZE(snd_minors))
return -ENODEV;
mutex_lock(&sound_mutex);
mptr = snd_minors[minor];
if (mptr == NULL) {
mptr = autoload_device(minor);
if (!mptr) {
mutex_unlock(&sound_mutex);
return -ENODEV;
}
}
old_fops = file->f_op;
file->f_op = fops_get(mptr->f_ops);
if (file->f_op == NULL) {
file->f_op = old_fops;
err = -ENODEV;
}
mutex_unlock(&sound_mutex);
if (err < 0)
return err;
if (file->f_op->open) {
err = file->f_op->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
}
fops_put(old_fops);
return err;
}
На диаграмме последовательности ниже показано, как приложение в конечном итоге вызывает функцию обратного вызова в структуре snd_pcm_f_ops:
Рисунок 4.3.2.1 Приложение, работающее с устройством PCM
Ссылка: http://t.csdnimg.cn/IQCeC.