6

播放两个重叠的独立波形文件所需的 API 配置/调用是什么?我试图这样做,我收到资源繁忙错误。解决问题的一些指示将非常有帮助。

snd_pcm_prepare()以下是来自第二个波形文件的错误消息

"Device or resource busy"
4

5 回答 5

22

您可以配置 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,允许多个应用共享输出流
  • 创建另一个 usingdsnoop对输入流执行相同的操作
  • 将这些合并到一个新设备中,该设备将使用插件duplex支持输入和输出asym
  • 告诉 ALSA 使用新duplex设备作为默认设备
  • 告诉 ALSA 用来hw:0控制默认设备(alsamixer 等)

坚持这个,或者~/.asoundrc/etc/asound.conf应该很高兴。

有关更多信息,请参阅http://www.alsa-project.org/main/index.php/Asoundrc#Software_mixing

于 2013-01-18T12:25:32.400 回答
5

ALSA 不提供混音器。如果您需要同时播放多个音频流,则需要自己将它们混合在一起。

实现这一点的最简单方法是将 WAV 文件解码为float样本,添加它们,然后在将它们转换回整数样本时对其进行剪辑。

或者,您可以尝试多次打开默认音频设备(而不是像“hw:0”这样的硬件设备),对于您希望播放的每个流一次,并希望加载 dmix ALSA 插件并提供混音功能。

于 2013-01-18T12:10:10.300 回答
2

由于 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();
  }
于 2019-12-03T01:03:05.970 回答
0

您也可以使用此配置

   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 “格式” “文件名”

根据您的音频文件输入通道、速率、格式和文件名的值

于 2016-09-02T07:18:47.057 回答
0

下面是一个非常简化的多线程播放方案(假设两个文件是相同的样本格式,相同的通道号和相同的频率):

  1. 每个文件解码都启动基于缓冲区的线程(必须将此代码制作 2 次 - forfile1和 for 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. 文件解码线程本身(也必须定义两次 - forfile1和 for 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. 启动合并线程 (aka 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)
    
于 2018-04-13T06:27:35.330 回答