Вопрос или проблема
Я использую alsa-lib-1.2.9
в своей встроенной системе Linux, и теперь я хочу воспроизводить несколько аудио из разных потоков, поэтому я погуглил в интернете (я не знаком с ALSA) и обнаружил, что могу использовать dmix
.
Вот основная часть моего /etc/asound.conf
на плате.
pcm.!default
{
type asym
playback.pcm "play_softvol"
capture.pcm "cap_chn0"
}
pcm.play_softvol {
type softvol
slave {
pcm play_chn0
}
control {
name "Громкость динамика"
}
min_dB -60.0
max_dB -10.0
resolution 50
}
pcm.play_chn0 {
type plug
slave {
pcm dmixer
}
}
pcm.dmixer {
type dmix
ipc_key 77235
ipc_key_add_uid true
slave {
pcm "hw:0,0"
period_time 0
period_size 320
buffer_size 2560
rate 32000
}
}
ctl.dmixer {
type hw
card 0
}
pcm.cap_chn0 {
type plug
slave {
pcm dsnooper
}
}
pcm.tloop_cap {
type plug
slave.pcm "hw:Loopback,0,0"
}
pcm.dsnooper {
type dsnoop
ipc_key 77236
ipc_key_add_uid true
slave {
pcm "hw:0,0"
channels 4
rate 16000
}
bindings {
0 0
1 1
2 2
3 3
}
}
ctl.dsnooper {
type hw
card 0
}
Я думаю, что default
устройство использует play_softvol
->play_chn0
->dmix
.
Поэтому я попытался воспроизвести 2 PCM файла следующим образом,
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <pthread.h>
pthread_t tid, tid2;
static snd_pcm_t *playback_handle;
static int size;
static snd_pcm_uframes_t frames;
void *play_func(void *arg)
{
char *buffer;
int ret;
FILE *fp = fopen(arg, "rb");
if(fp == NULL)
return 0;
buffer = (char *) malloc(size);
fprintf(stderr, "size = %d\n", size);
while (1)
{
ret = fread(buffer, 1, size, fp);
if(ret == 0)
{
fprintf(stderr, "конец файла на входе\n");
break;
}
while(ret = snd_pcm_writei(playback_handle, buffer, frames)<0)
{
usleep(2000);
if (ret == -EPIPE)
{
/* EPIPE означает переполнение */
fprintf(stderr, "произошло переполнение\n");
snd_pcm_prepare(playback_handle);
}
else if (ret < 0)
{
fprintf(stderr, "ошибка от writei: %s\n", snd_strerror(ret));
}
}
}
free(buffer);
return NULL;
}
void *play_func2(void *arg)
{
char *buffer;
int ret;
FILE *fp = fopen(arg, "rb");
if(fp == NULL)
return 0;
buffer = (char *) malloc(size);
fprintf(stderr, "size = %d\n", size);
while (1)
{
ret = fread(buffer, 1, size, fp);
if(ret == 0)
{
fprintf(stderr, "конец файла на входе\n");
break;
}
while(ret = snd_pcm_writei(playback_handle, buffer, frames)<0)
{
usleep(2000);
if (ret == -EPIPE)
{
/* EPIPE означает переполнение */
fprintf(stderr, "произошло переполнение\n");
snd_pcm_prepare(playback_handle);
}
else if (ret < 0)
{
fprintf(stderr, "ошибка от writei: %s\n", snd_strerror(ret));
}
}
}
free(buffer);
return NULL;
}
int main(int argc, char *argv[])
{
int ret;
int dir=0;
snd_pcm_uframes_t periodsize;
snd_pcm_hw_params_t *hw_params;
if (argc < 2) {
printf("ошибка: alsa_play_test [имя музыки] [второе имя]\n");
exit(1);
}
//1.
ret = snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (ret < 0) {
perror("snd_pcm_open");
exit(1);
}
.......
ret = pthread_create(&tid, NULL, play_func, argv[1]);
if (argc > 2)
ret = pthread_create(&tid2, NULL, play_func2, argv[2]);
pthread_join(tid, NULL);
if (argc > 2)
pthread_join(tid2, NULL);
snd_pcm_close(playback_handle);
return 0;
}
asound_threads 1.pcm 2.pcm
воспроизвести песню 1.pcm и 2.pcm
size = 320
size = 320
Но это звучит неясно, два PCM воспроизводятся попеременно, но скорость неверная и флинтует.
Так что в моем случае, как я могу воспроизводить несколько аудио (смешивание аудио)?
Я нашел правильный способ воспроизведения нескольких аудио в многопоточной системе.
Каждый поток должен инициализировать свой собственный PCM обработчик и использовать свой собственный обработчик для воспроизведения аудио.
Не следует использовать общий/shared PCM обработчик для многопоточного воспроизведения аудио.
Ответ или решение
Неудача при воспроизведении нескольких аудио с использованием ALSA DMIX
Введение
При работе с ALSA (Advanced Linux Sound Architecture) встраиваемых систем, нередко возникает необходимость воспроизводить несколько аудиофайлов одновременно из разных потоков. В данной статье мы рассмотрим, как правильно настроить систему вывода звука с использованием DMIX, а также основные ошибки, которые могут привести к проблемам с воспроизведением.
Проблема
В представленном вами коде, потоки пытаются использовать один и тот же PCM-объект для воспроизведения аудиофайлов. Это часто приводит к артефактам звука, как например, смешиванию аудио, искажению или потере качества.
Настройки DMIX
Ваш файл конфигурации /etc/asound.conf
выглядит корректно, и вы правильно используете dmix
для смешивания звуков. Ниже перечислены ключевые элементы вашей конфигурации:
pcm.dmixer
: определяет устройство для смешивания звуков.pcm.softvol
: позволяет регулировать громкость.pcm.play_chn0
: инициализирует устройство вывода, основанное наdmix
.ctl.dmixer
: контролирует смешивающее устройство.
Однако, несмотря на правильную конфигурацию, вы сталкиваетесь с проблемами воспроизведения в ваших потоках.
Решение
Для корректного воспроизведения нескольких аудиофайлов из разных потоков необходимо, чтобы каждый поток создавал свое собственное PCM-устройство. Это позволяет избежать конфликта между потоками, так как они будут обращаться к разным экземплярам PCM.
Изменение кода
Вот некоторые рекомендации по изменению вашего кода:
-
Создайте отдельные PCM-объекты для каждого потока. Измените
play_func
иplay_func2
, чтобы они инициализировали свои собственныеsnd_pcm_t
объекты. -
Закрывайте PCM-объекты после использования. Убедитесь, что вы освобождаете ресурсы, вызвав
snd_pcm_close
для каждого потока.
Пример обновленного кода
Вот как это может выглядеть:
void *play_func(void *arg) {
snd_pcm_t *playback_handle;
char *buffer;
int ret;
FILE *fp = fopen(arg, "rb");
if(fp == NULL)
return 0;
// Инициализация нового PCM-объекта
snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
buffer = (char *) malloc(size);
fprintf(stderr, "size = %d\n", size);
while (1) {
ret = fread(buffer, 1, size, fp);
if(ret == 0) {
fprintf(stderr, "end of file on input\n");
break;
}
while((ret = snd_pcm_writei(playback_handle, buffer, frames) < 0)) {
usleep(2000);
if (ret == -EPIPE) {
fprintf(stderr, "underrun occurred\n");
snd_pcm_prepare(playback_handle);
} else if (ret < 0) {
fprintf(stderr, "error from writei: %s\n", snd_strerror(ret));
}
}
}
snd_pcm_close(playback_handle); // закрытие PCM-объекта
free(buffer);
fclose(fp);
return NULL;
}
Заключение
Основная ошибка в вашем изначальном решении заключалась в том, что несколько потоков пытались использовать общий PCM-объект, что привело к проблемам с качеством звука. Создание отдельных PCM-объектов для каждого потока позволит вам без проблем воспроизводить несколько аудиофайлов одновременно с высокой стабильностью и качеством.
Следуйте приведенным рекомендациям, чтобы улучшить свою реализацию работы с ALSA на платформе Linux.