Вопрос или проблема
Я пытаюсь захватить и воспроизвести звук с помощью программы на C. Для этого я нашел этот учебник.. Вот программа, которую я запускаю:-
/**
* Ян Ньюмарч
*/
#define PERIOD_SIZE 1024
#define BUF_SIZE (PERIOD_SIZE * 2)
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
void print_pcm_state(snd_pcm_t *handle, char *name) {
switch (snd_pcm_state(handle)) {
case SND_PCM_STATE_OPEN:
printf("состояние открыто %s\n", name);
break;
case SND_PCM_STATE_SETUP:
printf("состояние настройка %s\n", name);
break;
case SND_PCM_STATE_PREPARED:
printf("состояние подготовлено %s\n", name);
break;
case SND_PCM_STATE_RUNNING:
printf("состояние выполняется %s\n", name);
break;
case SND_PCM_STATE_XRUN:
printf("состояние xrun %s\n", name);
break;
default:
printf("состояние другое %s\n", name);
break;
}
}
int setparams(snd_pcm_t *handle, char *name) {
snd_pcm_hw_params_t *hw_params;
int err;
if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
fprintf (stderr, "не удается выделить структуру параметров оборудования (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) {
fprintf (stderr, "не удается инициализировать структуру параметров оборудования (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
fprintf (stderr, "не удается установить тип доступа (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
fprintf (stderr, "не удается установить формат образца (%s)\n",
snd_strerror (err));
exit (1);
}
unsigned int rate = 48000;
if ((err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &rate, 0)) < 0) {
fprintf (stderr, "не удается установить частоту дискретизации (%s)\n",
snd_strerror (err));
exit (1);
}
printf("Частота для %s составляет %d\n", name, rate);
if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, 2)) < 0) {
fprintf (stderr, "не удается установить количество каналов (%s)\n",
snd_strerror (err));
exit (1);
}
snd_pcm_uframes_t buffersize = BUF_SIZE;
if ((err = snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &buffersize)) < 0) {
printf("Невозможно установить размер буфера %li: %s\n", BUF_SIZE, snd_strerror(err));
exit (1);;
}
snd_pcm_uframes_t periodsize = PERIOD_SIZE;
fprintf(stderr, "текущий размер периода %d\n", periodsize);
if ((err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &periodsize, 0)) < 0) {
printf("Невозможно установить размер периода %li: %s\n", periodsize, snd_strerror(err));
exit (1);
}
if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) {
fprintf (stderr, "не удается установить параметры (%s)\n",
snd_strerror (err));
exit (1);
}
snd_pcm_uframes_t p_psize;
snd_pcm_hw_params_get_period_size(hw_params, &p_psize, NULL);
fprintf(stderr, "размер периода %d\n", p_psize);
snd_pcm_hw_params_get_buffer_size(hw_params, &p_psize);
fprintf(stderr, "размер буфера %d\n", p_psize);
snd_pcm_hw_params_free (hw_params);
if ((err = snd_pcm_prepare (handle)) < 0) {
fprintf (stderr, "не удается подготовить аудио интерфейс к использованию (%s)\n",
snd_strerror (err));
exit (1);
}
return 0;
}
int set_sw_params(snd_pcm_t *handle, char *name) {
snd_pcm_sw_params_t *swparams;
int err;
snd_pcm_sw_params_alloca(&swparams);
err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0) {
fprintf(stderr, "Ошибочная конфигурация для этого PCM: нет доступных конфигураций\n");
exit(1);
}
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, PERIOD_SIZE);
if (err < 0) {
printf("Невозможно установить начальный порог: %s\n", snd_strerror(err));
return err;
}
err = snd_pcm_sw_params_set_avail_min(handle, swparams, PERIOD_SIZE);
if (err < 0) {
printf("Невозможно установить минимальную доступность: %s\n", snd_strerror(err));
return err;
}
if (snd_pcm_sw_params(handle, swparams) < 0) {
fprintf(stderr, "не удается установить параметры ПО:\n");
exit(1);
}
return 0;
}
/************** некоторый код из latency.c *****************/
main (int argc, char *argv[])
{
int i;
int err;
int buf[BUF_SIZE];
snd_pcm_t *playback_handle;
snd_pcm_t *capture_handle;
snd_pcm_hw_params_t *hw_params;
FILE *fin;
size_t nread;
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
if (argc != 3) {
fprintf(stderr, "Использование: %s in-card out-card\n", argv[0]);
exit(1);
}
/**** Исходящая карта *******/
if ((err = snd_pcm_open (&playback_handle, argv[2], SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
fprintf (stderr, "не удается открыть аудиоустройство %s (%s)\n",
argv[2],
snd_strerror (err));
exit (1);
}
setparams(playback_handle, "воспроизведение");
set_sw_params(playback_handle, "воспроизведение");
/*********** Входящая карта **********/
if ((err = snd_pcm_open (&capture_handle, argv[1], SND_PCM_STREAM_CAPTURE, 0)) < 0) {
fprintf (stderr, "не удается открыть аудиоустройство %s (%s)\n",
argv[1],
snd_strerror (err));
exit (1);
}
setparams(capture_handle, "захват");
set_sw_params(capture_handle, "захват");
if ((err = snd_pcm_link(capture_handle, playback_handle)) < 0) {
printf("Ошибка соединения потоков: %s\n", snd_strerror(err));
exit(0);
}
if ((err = snd_pcm_prepare (playback_handle)) < 0) {
fprintf (stderr, "не удается подготовить аудио интерфейс для использования (%s)\n",
snd_strerror (err));
exit (1);
}
/**************** вставить что-то в буфер воспроизведения ****************/
if (snd_pcm_format_set_silence(format, buf, 2*BUF_SIZE) < 0) {
fprintf(stderr, "ошибка тишины\n");
exit(1);
}
int n = 0;
while (n++ < 2) {
if (snd_pcm_writei (playback_handle, buf, BUF_SIZE) < 0) {
fprintf(stderr, "ошибка записи\n");
exit(1);
}
}
/************* КОПИЯ ************/
while (1) {
int nread;
if ((nread = snd_pcm_readi (capture_handle, buf, BUF_SIZE)) != BUF_SIZE) {
if (nread < 0) {
fprintf (stderr, "чтение из аудио интерфейса не удалось (%s)\n",
snd_strerror (nread));
} else {
fprintf (stderr, "чтение из аудио интерфейса не удалось после %d кадров\n", nread);
}
snd_pcm_prepare(capture_handle);
continue;
}
if ((err = snd_pcm_writei (playback_handle, buf, nread)) != nread) {
if (err < 0) {
fprintf (stderr, "запись в аудио интерфейс не удалась (%s)\n",
snd_strerror (err));
} else {
fprintf (stderr, "запись в аудио интерфейс не удалась после %d кадров\n", err);
}
snd_pcm_prepare(playback_handle);
}
}
snd_pcm_drain(playback_handle);
snd_pcm_close (playback_handle);
exit (0);
}
Я скомпилировал ее и запустил с следующими аргументами:-
./playback-capture hw:0 hw:0
До сих пор мой код работает нормально, но теперь я решаю запустить эту программу с использованием USB звуковой карты. Для этого я редактирую
/etc/modprobe.d/alsa-base.conf
Вот изменения :-
Я заменяю
options snd_usb_audio index=-2
options snd_hda_intel index=-1
на
options snd_usb_audio index=-1
options snd_hda_intel index=-2
И я заменяю
# Запретить загрузку snd-usb-audio в качестве первой звуковой карты
options snd-usb-audio index=-1
на
# Запретить загрузку snd-usb-audio в качестве первой звуковой карты
options snd-usb-audio index=-1
Теперь, когда я запускаю свой код, я получаю следующий вывод:-
Частота для воспроизведения составляет 48000
текущий размер периода 1024
размер периода 1024
размер буфера 2048
Частота для захвата составляет 48000
не удается установить количество каналов (недопустимый аргумент)
Так что, может кто-нибудь скажет мне, что мне еще нужно сделать, чтобы запустить мой код с использованием звуковой карты.
Примечание:- Я выполнил команду
aplay -l
и получил следующий вывод:-
card 0: Device [USB PnP Sound Device], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: PCH [HDA Intel PCH], device 0: ALC221 Analog [ALC221 Analog]
Subdevices: 1/1
Subdevice #0: subdevice #0
В рамках проекта я хочу иметь пример Full Duplex ALSA Advanced Linux Sound Architecture, который может:
- Захватывать звук с звуковой карты через разъем для микрофона (розовый).
- Воспроизводить на выходе другой USB звуковой карты (выход зеленого разъема).
Внесение некоторых небольших изменений в ваш код приводит к реализации примера двустороннего захвата-воспроизведения ALSA.
Код
/**
* Ян Ньюмарч
*/
/*
* Имя файла: rec-play-inline.c
*
* компилировать: gcc rec-play-inline.c -o rec-play-inline -lasound
*
* запустить: ./rec-play-inline "plughw:2,0" "plughw:0,0"
*
*
* */
#define PERIOD_SIZE 1024
#define BUF_SIZE (PERIOD_SIZE * 2)
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
void print_pcm_state(snd_pcm_t *handle, char *name) {
switch (snd_pcm_state(handle)) {
case SND_PCM_STATE_OPEN:
printf("состояние открыто %s\n", name);
break;
case SND_PCM_STATE_SETUP:
printf("состояние настройка %s\n", name);
break;
case SND_PCM_STATE_PREPARED:
printf("состояние подготовлено %s\n", name);
break;
case SND_PCM_STATE_RUNNING:
printf("состояние выполняется %s\n", name);
break;
case SND_PCM_STATE_XRUN:
printf("состояние xrun %s\n", name);
break;
default:
printf("состояние другое %s\n", name);
break;
}
}
int setparams(snd_pcm_t *handle, char *name) {
snd_pcm_hw_params_t *hw_params;
int err;
if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
fprintf (stderr, "не удается выделить структуру параметров оборудования (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) {
fprintf (stderr, "не удается инициализировать структуру параметров оборудования (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
fprintf (stderr, "не удается установить тип доступа (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
fprintf (stderr, "не удается установить формат образца (%s)\n",
snd_strerror (err));
exit (1);
}
unsigned int rate = 48000;
if ((err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &rate, 0)) < 0) {
fprintf (stderr, "не удается установить частоту дискретизации (%s)\n",
snd_strerror (err));
exit (1);
}
printf("Частота для %s составляет %d\n", name, rate);
if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, 2)) < 0) {
fprintf (stderr, "не удается установить количество каналов (%s)\n",
snd_strerror (err));
exit (1);
}
snd_pcm_uframes_t buffersize = BUF_SIZE;
if ((err = snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &buffersize)) < 0) {
printf("Невозможно установить размер буфера %li: %s\n", (long int)BUF_SIZE, snd_strerror(err));
exit (1);;
}
snd_pcm_uframes_t periodsize = PERIOD_SIZE;
fprintf(stderr, "текущий размер периода %d\n", (int) periodsize);
if ((err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &periodsize, 0)) < 0) {
printf("Невозможно установить размер периода %li: %s\n", periodsize, snd_strerror(err));
exit (1);
}
if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) {
fprintf (stderr, "не удается установить параметры (%s)\n",
snd_strerror (err));
exit (1);
}
snd_pcm_uframes_t p_psize;
snd_pcm_hw_params_get_period_size(hw_params, &p_psize, NULL);
fprintf(stderr, "размер периода %d\n", (int)p_psize);
snd_pcm_hw_params_get_buffer_size(hw_params, &p_psize);
fprintf(stderr, "размер буфера %d\n", (int)p_psize);
snd_pcm_hw_params_free (hw_params);
if ((err = snd_pcm_prepare (handle)) < 0) {
fprintf (stderr, "не удается подготовить аудио интерфейс к использованию (%s)\n",
snd_strerror (err));
exit (1);
}
return 0;
}
int set_sw_params(snd_pcm_t *handle, char *name) {
snd_pcm_sw_params_t *swparams;
int err;
snd_pcm_sw_params_alloca(&swparams);
err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0) {
fprintf(stderr, "Ошибочная конфигурация для этого PCM: нет доступных конфигураций\n");
exit(1);
}
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, PERIOD_SIZE);
if (err < 0) {
printf("Невозможно установить начальный порог: %s\n", snd_strerror(err));
return err;
}
err = snd_pcm_sw_params_set_avail_min(handle, swparams, PERIOD_SIZE);
if (err < 0) {
printf("Невозможно установить минимальную доступность: %s\n", snd_strerror(err));
return err;
}
if (snd_pcm_sw_params(handle, swparams) < 0) {
fprintf(stderr, "не удается установить параметры ПО:\n");
exit(1);
}
return 0;
}
/************** некоторый код из latency.c *****************/
int main (int argc, char *argv[])
{
int i;
int err;
int buf[BUF_SIZE];
snd_pcm_t *playback_handle;
snd_pcm_t *capture_handle;
snd_pcm_hw_params_t *hw_params;
FILE *fin;
size_t nread;
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
///Проверка аргументов командной строки
if (argc != 3) {
fprintf(stderr, "Использование: %s входная звуковая карта выходная звуковая карта\n", argv[0]);
exit(1);
}
/**** Исходящая карта *******/
if ((err = snd_pcm_open (&playback_handle, argv[1], SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
fprintf (stderr, "не удается открыть аудиоустройство %s (%s)\n",
argv[2],
snd_strerror (err));
exit (1);
}
setparams(playback_handle, "воспроизведение");
set_sw_params(playback_handle, "воспроизведение");
/*********** Входящая карта **********/
if ((err = snd_pcm_open (&capture_handle, argv[2], SND_PCM_STREAM_CAPTURE, 0)) < 0) {
fprintf (stderr, "не удается открыть аудиоустройство %s (%s)\n",
argv[1],
snd_strerror (err));
exit (1);
}
setparams(capture_handle, "захват");
set_sw_params(capture_handle, "захват");
///Закомментировано EM
/*
if ((err = snd_pcm_link(capture_handle, playback_handle)) < 0) {
printf("Ошибка соединения потоков: %s\n", snd_strerror(err));
exit(0);
}
*/
if ((err = snd_pcm_prepare (playback_handle)) < 0) {
fprintf (stderr, "не удается подготовить аудио интерфейс для использования (%s)\n",
snd_strerror (err));
exit (1);
}
/**************** вставить что-то в буфер воспроизведения ****************/
if (snd_pcm_format_set_silence(format, buf, 2*BUF_SIZE) < 0) {
fprintf(stderr, "ошибка тишины\n");
exit(1);
}
///Закомментировано EM
/*
int n = 0;
while (n++ < 2) {
if (snd_pcm_writei (playback_handle, buf, BUF_SIZE) < 0) {
fprintf(stderr, "ошибка записи\n");
exit(1);
}
}
*/
///
/************* Захват и воспроизведение голоса ***************/
while (1) {
int nread;
if ((nread = snd_pcm_readi (capture_handle, buf, BUF_SIZE)) != BUF_SIZE) {
if (nread < 0) {
fprintf (stderr, "чтение из аудио интерфейса не удалось (%s)\n",
snd_strerror (nread));
} else {
fprintf (stderr, "чтение из аудио интерфейса не удалось после %d кадров\n", nread);
}
snd_pcm_prepare(capture_handle);
continue;
}
///добавлено EM
snd_pcm_prepare(playback_handle);
///
if ((err = snd_pcm_writei (playback_handle, buf, nread)) != nread) {
if (err < 0) {
fprintf (stderr, "запись в аудио интерфейс не удалась (%s)\n",
snd_strerror (err));
} else {
fprintf (stderr, "запись в аудио интерфейс не удалась после %d кадров\n", err);
}
snd_pcm_prepare(playback_handle);
}
}
snd_pcm_drain(playback_handle);
snd_pcm_close (playback_handle);
exit (0);
return 0;
}
Объяснение
Моя встроенная звуковая карта: plughw:0,0
и внешняя USB звуковая карта: plughw:2,0
Я узнал эти имена, выполнив следующие команды:
- Чтобы получить список устройств воспроизведения:
aplay --list--devices
- Чтобы получить список устройств захвата:
arecord --list-devices
Источник
Добавление snd_pcm_prepare(playback_handle);
перед вызовом writei()
решает проблему из
ALSA: сбой буфера при вызове snd_pcm_writei
Ответ или решение
Решение проблемы воспроизведения захваченного звука с использованием USB звуковой карты
Если вы столкнулись с проблемой воспроизведения звука при использовании USB звуковой карты в контексте вашей программы на C, основанной на ALSA (Advanced Linux Sound Architecture), следующее руководство поможет вам разобраться с возможными ошибками и предложит решения.
Описание проблемы
Вы упомянули, что после изменения конфигурации в файле /etc/modprobe.d/alsa-base.conf
ваша программа выдает ошибку:
cannot set channel count (Invalid argument)
Это говорит о том, что ALSA не смогла установить нужное количество каналов для звуковой карты. Ваша программа ожидает, что звуковая карта поддерживает настройку с двумя каналами (стерео).
Шаги для устранения проблемы
Вот несколько шагов, которые могут помочь вам решить данную проблему:
-
Проверка поддерживаемых форматов:
Убедитесь, что ваша USB звуковая карта поддерживает 2 канала (стерео) с форматомSND_PCM_FORMAT_S16_LE
на частоте 48000 Гц. Используйте команду:arecord -D plughw:2,0 --dump-hw-params
Это даст вам представление о поддерживаемых частотах и форматах.
-
Замена
hw:0
иhw:1
на правильные значения:
Убедитесь, что вы передаете правильные идентификаторы звуковых карт в командной строке при запуске вашей программы. Например, если USB звуковая карта имеет индекс 1, необходимо запускать программу следующей командой:./playback-capture plughw:1,0 plughw:1,0
-
Проверка настроек ALSA:
Некоторые звуковые карты могут иметь ограничения по количеству каналов или частотам. Если вы изменили настройки ALSA, рекомендуется вернуть их в исходное состояние и попробовать снова. -
Изменения в коде программы:
- Убедитесь, что вы правильно обрабатываете параметры в ваших функциях
setparams
иset_sw_params
. В вашем коде не хватает обработки ошибок для получения информации о том, например, если попытка выставить количество каналов не удалась. - Вместо
SND_PCM_FORMAT_S16_LE
, попробуйте использоватьSND_PCM_FORMAT_S16
или проверить другие форматы, чтобы найти поддерживаемый вариант.
- Убедитесь, что вы правильно обрабатываете параметры в ваших функциях
-
Обновление драйверов:
Убедитесь, что у вас установлены последние версии ALSA и драйверов, поддерживающих вашу USB звуковую карту. -
Тестирование:
После внесения изменений протестируйте программу, как только вы внесете изменения. Используйте различные образцы аудиоформатов и частот для дальнейшей диагностики.
Заключение
Приведенные шаги помогут вам устранить проблему с воспроизведением звука через USB звуковую карту в вашей программе на C. Обратите внимание, что для успешной работы с ALSA важно учитывать совместимость параметров и возможностей звуковых устройств. Если у вас останутся вопросы или сложности, не стесняйтесь обращайтесь за поддержкой на специализированные форумы или просите помощь у сообщества разработчиков.