Не удалось воспроизвести несколько аудио с помощью ALSA DMIX

Вопрос или проблема

Я использую 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.

Изменение кода

Вот некоторые рекомендации по изменению вашего кода:

  1. Создайте отдельные PCM-объекты для каждого потока. Измените play_func и play_func2, чтобы они инициализировали свои собственные snd_pcm_t объекты.

  2. Закрывайте 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.

Оцените материал
Добавить комментарий

Капча загружается...