ALSA Api:如何同时播放两个波形文件?

时间:2013-01-18 12:04:02

标签: c linux audio alsa

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

以下是来自第二个wavefile的snd_pcm_prepare()的错误消息

"Device or resource busy"

4 个答案:

答案 0 :(得分:5)

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

最简单的方法是将WAV文件解码为float样本,添加它们,并在将它们转换回整数样本时剪切它们。

或者,您可以尝试多次打开默认音频设备(以及硬件设备,如“hw:0”),对于您希望的每个流,玩,并希望加载dmix ALSA插件并提供混音功能。

答案 1 :(得分:1)

由于ALSA默认提供混音设备(dmix),因此您可以简单地使用aplay,就像这样:

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

如果您的音频文件具有相同的速率和格式,则无需使用插头。变成:

aplay song1.wav &
aplay -Ddmix song2.wav

但是,如果要编程此方法,则可以进行一些C ++音频编程tutorials here。这些教程向您展示了如何加载音频文件以及如何操作不同的音频子系统,例如jackd和ALSA。

this example中,它演示了使用ALSA播放一个音频文件的过程。可以这样打开第二个音频文件来修改它:

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

然后像这样修改the while loop

  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();
  }

答案 2 :(得分:0)

您也可以使用此配置

private void CreateExpression(Type interfaceType, Type methodReturnType, MethodInfo methodName)
    {
    ParameterExpression parameter = Expression.Parameter(interfaceType, "srv");
    Expression property = Expression.Call(methodReturnType, methodName.Name, new Type[]{ } ,parameter);       
    Type expressionType = typeof(Expression<>);
    Type lambdaType = typeof(LambdaExpression);
    Type funcType = typeof(Func<,>);
    Type delegateType = funcType.MakeGenericType(interfaceType, methodReturnType);
    Type expression = expressionType.MakeGenericType(delegateType);

    MethodInfo mI = typeof(Expression).GetMethod("Lambda");
    MethodInfo lambda = mI.MakeGenericMethod(delegateType);
    var ex = lambda.Invoke(this, new object[] { delegateType, property, parameter });
}

在〜/ .asoundrc或/etc/asound.conf

中更新它

您可以使用命令

对于wav文件

aplay -D mix_stream&#34; filename&#34;

对于raw或pcmfile

aplay -D mix_stream -c&#34;频道&#34; -r&#34;率&#34; -f&#34;格式&#34; &#34;文件名&#34;

根据音频文件输入频道,比率,格式和文件名的值

答案 3 :(得分:0)

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

  1. 每个文件解码启动基于缓冲区的线程(必须为file1file2生成此代码2次:

    import wave
    import threading
    
    periodsize = 160
    f = wave.open(file1Wave, 'rb')
    file1Alive = True
    file1Thread = threading.Thread(target=_playFile1)
    file1Thread.daemon = True
    file1Thread.start()
    
  2. 文件解码线程本身(也必须定义两次 - file1file2):

    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)