ALSA Api: Как одновременно воспроизводить два волновых файла?

Какова необходимая конфигурация/вызов API для воспроизведения двух независимых волновых файлов с перекрытием? Я пытался сделать это, я получаю ошибку занятости ресурсов. Некоторые указатели для решения проблемы будут очень полезны.

Ниже приведено сообщение об ошибке из snd_pcm_prepare() второго волнового файла.

"Device or resource busy"

person Lunar Mushrooms    schedule 18.01.2013    source источник


Ответы (5)


ALSA не предоставляет микшер. Если вам нужно воспроизвести несколько аудиопотоков одновременно, вам нужно смешать их вместе самостоятельно.

Самый простой способ добиться этого — декодировать файлы WAV в float выборки, добавить их и обрезать при преобразовании обратно в целочисленные выборки.

В качестве альтернативы вы можете попытаться открыть аудиоустройство по умолчаниюне аппаратное устройство, такое как «hw:0») несколько раз, по одному разу для каждого потока, который вы хотите воспроизвести, и надеемся, что плагин dmix ALSA загружен и обеспечит функциональность микширования.

person Nikos C.    schedule 18.01.2013
comment
как насчет утилиты микшера alsa? Могу ли я использовать его для микширования аудио? - person Lunar Mushrooms; 18.01.2013
comment
@LunarMushrooms Я говорил о микшировании нескольких аудиопотоков, а не об утилите управления микшером для управления уровнями громкости. - person Nikos C.; 18.01.2013

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

Пример конфигурации для этого приведен ниже:

pcm.dmixed {
    type dmix
    ipc_key 1024
    ipc_key_add_uid 0
    slave.pcm "hw:0,0"
}
pcm.dsnooped {
    type dsnoop
    ipc_key 1025
    slave.pcm "hw:0,0"
}

pcm.duplex {
    type asym
    playback.pcm "dmixed"
    capture.pcm "dsnooped"
}

# Instruct ALSA to use pcm.duplex as the default device
pcm.!default {
    type plug
    slave.pcm "duplex"
}
ctl.!default {
    type hw
    card 0
}

Это делает следующее:

  • создает новое устройство с помощью плагина dmix, который позволяет нескольким приложениям совместно использовать выходной поток
  • создает другой, используя dsnoop, который делает то же самое для входного потока
  • объединяет их в новое duplex устройство, которое будет поддерживать ввод и вывод с помощью плагина asym
  • скажите ALSA использовать новое устройство duplex в качестве устройства по умолчанию
  • скажите ALSA использовать hw:0 для управления устройством по умолчанию (alsamixer и т. д.)

Вставьте это в ~/.asoundrc или /etc/asound.conf, и все будет хорошо.

Для получения дополнительной информации см. http://www.alsa-project.org/main/index.php/Asoundrc#Software_mixing.

person Jonny    schedule 18.01.2013
comment
Гений!!!! Джонни, ты ГЕРОЙ, да. Большое спасибо, нет миллионов. Отличная работа, это здорово, я ненавижу PulseAudio, позор им, его крах 2013 года и PA сделал нас инвалидами. Где Alsa лучший навсегда, и только у меня была эта проблема с несколькими выходами с вашим скриптом, он работает на 500%. Спасибо, так держать лучшие ответы. - person ; 01.10.2013

Поскольку ALSA по умолчанию предоставляет микшер (dmix), вы можете просто использовать aplay, например:

aplay song1.wav &
aplay -Dplug:dmix song2.wav

Если ваши аудиофайлы имеют одинаковую скорость и формат, вам не нужно использовать штекер. Это становится :

aplay song1.wav &
aplay -Ddmix song2.wav

Однако, если вы хотите запрограммировать этот метод, есть несколько учебных пособий по программированию аудио на C++ здесь. В этих руководствах показано, как загружать аудиофайлы и работать с различными аудиоподсистемами, такими как jackd и ALSA.

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

Sox<short int> sox2;
res=sox2.openRead(argv[2]);
if (res<0 && res!=SOX_READ_MAXSCALE_ERROR)
  return SoxDebug().evaluateError(res);

Затем измените цикл while следующим образом:

  Eigen::Array<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> buffer, buffer2;
  size_t totalWritten=0;
  while (sox.read(buffer, pSize)>=0 && sox2.read(buffer2, pSize)>=0){
    if (buffer.rows()==0 || buffer.rows()==0) // end of the file.
      break;
    // as the original files were opened as short int, summing will not overload the int buffer.
    buffer+=buffer2; // sum the two waveforms together
    playBack<<buffer; // play the audio data
    totalWritten+=buffer.rows();
  }
person Matt    schedule 03.12.2019

Вы также можете использовать эту конфигурацию

   pcm.dmix_stream
   {
       type dmix
        ipc_key 321456 
        ipc_key_add_uid true
        slave.pcm "hw:0,0"
   }


   pcm.mix_stream
   {
      type plug
      slave.pcm dmix_stream
   }

Обновите его в ~/.asoundrc или /etc/asound.conf

Вы можете использовать команду

Для wav-файла

aplay -D mix_stream "имя файла"

Для raw или pcmfile

aplay -D mix_stream -c "каналы" -r "скорость" -f "формат" "имя файла"

Введите значение для каналов, скорости, формата и имени файла в соответствии с вашим аудиофайлом.

person Varun    schedule 02.09.2016

Ниже приведено очень упрощенное решение для многопоточного воспроизведения (при условии, что оба файла имеют один и тот же формат образца, один и тот же номер канала и одинаковую частоту):

  1. начальный поток на основе буфера для каждого декодирования файла (нужно сделать этот код 2 раза - для file1 и для file2):

    import wave
    import threading
    
    periodsize = 160
    f = wave.open(file1Wave, 'rb')
    file1Alive = True
    file1Thread = threading.Thread(target=_playFile1)
    file1Thread.daemon = True
    file1Thread.start()
    
  2. сам поток декодирования файла (также должен быть определен дважды - для file1 и для file2):

    def _playFile1():
        # Read data from RIFF
        while file1Alive:
            if file1dataReady:
                time.sleep(.001)
            else:
                data1 = f.readframes(periodsize)
                if not data1:
                    file1Alive = False
                    f.close()
                else:
                    file1dataReady == True
    
  3. запуск потока слияния (он же funnel) для слияния расшифровок файлов

    import alsaaudio
    import threading
    
    sink = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, device="hw:CARD=default")
    sinkformat = 2
    funnelalive = True
    funnelThread = threading.Thread(target=self._funnelLoop)
    funnelThread.daemon = True
    funnelThread.start()
    
  4. объединить и воспроизвести (также известный как funnel) поток

    def _funnelLoop():
        # Reading all Inputs
        while funnelalive:
            # if nothing to play - time to selfdestruct
            if not file1Alive and not file2Alive:
                funnelalive = False
                sink.close()
            else:
                if file1dataReady and file2dataReady: 
                    # merging data from others but first
                    datamerged = audioop.add(data2, data2, sinkformat)
                    file1dataReady = False
                    file2dataReady = False
                    sink.write(datamerged)
                    time.sleep(.001)
    
person Oleg Kokorin    schedule 13.04.2018