播放两个重叠的独立波形文件所需的 API 配置/调用是什么?我试图这样做,我收到资源繁忙错误。解决问题的一些指示将非常有帮助。
snd_pcm_prepare()
以下是来自第二个波形文件的错误消息
"Device or resource busy"
您可以配置 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
duplex
设备作为默认设备hw:0
控制默认设备(alsamixer 等)坚持这个,或者~/.asoundrc
你/etc/asound.conf
应该很高兴。
有关更多信息,请参阅http://www.alsa-project.org/main/index.php/Asoundrc#Software_mixing。
ALSA 不提供混音器。如果您需要同时播放多个音频流,则需要自己将它们混合在一起。
实现这一点的最简单方法是将 WAV 文件解码为float
样本,添加它们,然后在将它们转换回整数样本时对其进行剪辑。
或者,您可以尝试多次打开默认音频设备(而不是像“hw:0”这样的硬件设备),对于您希望播放的每个流一次,并希望加载 dmix ALSA 插件并提供混音功能。
由于 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();
}
您也可以使用此配置
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 “格式” “文件名”
根据您的音频文件输入通道、速率、格式和文件名的值
下面是一个非常简化的多线程播放方案(假设两个文件是相同的样本格式,相同的通道号和相同的频率):
每个文件解码都启动基于缓冲区的线程(必须将此代码制作 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()
文件解码线程本身(也必须定义两次 - 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
启动合并线程 (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()
合并和播放(又名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)