c# - 使用回调播放带有端口音频的音频文件?

标签 c# audio portaudio

我正在尝试使用 portaudio C 库的 portaudiosharp 绑定(bind)来播放带有 C# 的波形文件,并且在设想正确的方法来做这件事时遇到了麻烦。我将粘贴我目前正在使用的代码。它有点工作,但我认为这不是正确的做事方式。

这是我的回调函数:

public PortAudio.PaStreamCallbackResult myPaStreamCallback(
            IntPtr input,
            IntPtr output,
            uint frameCount,
            ref PortAudio.PaStreamCallbackTimeInfo timeInfo,
            PortAudio.PaStreamCallbackFlags statusFlags,
            IntPtr userData)
            {
                short[] mybuffer = (short[])myQ.Dequeue();
                Marshal.Copy(mybuffer, 0, output, (int)frameCount * 2);
                return PortAudio.PaStreamCallbackResult.paContinue;
            }

然后我有一个“主循环”:
    PortAudio.Pa_Initialize();

    IntPtr stream;
    IntPtr userdata = IntPtr.Zero;
    PortAudio.Pa_OpenDefaultStream(out stream, 1, 2, 8,
            48000, NUM_SAMPLES/2, new PortAudio.PaStreamCallbackDelegate(myPaStreamCallback), userdata);

    PortAudio.Pa_StartStream(stream);

    while (readerPosition < reader.Length)
    {
            short[] qBuffer = new short[NUM_SAMPLES];
            read = reader.Read(buffer, 0, NUM_SAMPLES * 2); //read a block out from my wave file
            Buffer.BlockCopy(buffer, 0, qBuffer, 0, read); //copy them to the short buffer
            myQ.Enqueue(qBuffer);
            readerPosition += read;
    }

    while(PortAudio.Pa_IsStreamActive(stream) == 0)
    {
           //this while loop never gets entered -- why??
           Console.WriteLine("waiting");
    }

   System.Threading.Thread.Sleep(5000); //need this so that the callback function fires
   PortAudio.Pa_StopStream(stream);

我试图实现一个 FIFO 缓冲区,但我认为我可能以一种愚蠢的方式完成了它,因为基本上发生的情况是队列被填满,直到没有更多的样本可以放入其中,然后 PA 回调才开始触发.

这样做的更好方法是什么?如何使我的主循环屈服,以便回调函数可以在不休眠的情况下触发?

我正在使用 NAudio wavreader 从波形文件中读取,但我认为这并不重要。如果是这样,我可以发布更多详细信息。

最佳答案

一些显而易见的事情:

  • 你应该在调用 StartStream()
  • 之前填充你的环形缓冲区。
  • 您希望主循环通过在缓冲区未满时向其写入数据来保持缓冲区已满。您可以通过轮询和休眠以错误的方式执行此操作。如果队列足够大,您可以一次睡一秒钟,并且开销不会那么大。
  • 做到这一点的“正确”方法是使用 Event 对象,并在每次队列“未满”时从回调中发出信号。 main() 循环使用 WFSO 阻塞该事件,并在可以将数据写入队列时唤醒。 (提示:使用自动重置事件)。
  • 如果您只想播放声音文件,您可以使用 PA 的 WriteStream() API,它在内部完成所有这些操作。

  • 其他注意事项:
  • 编写正确的原子 FIFO 队列并非易事。你还没有为此显示你的代码。
  • 您的回调不处理队列为空的情况。在这种情况下,它可能应该输出静音。
  • 您可能不想为每个 block 都更新一个新缓冲区。考虑通过第二个队列将使用过的 block 返回到主线程并重用它们。
  • 您可能想要限制队列的大小(对于大多数情况来说,3-5 秒的音频已经足够了)——这就是我上面所说的“未满”的意思。另一种考虑这个问题的方法是根据高水印:PA 回调在缓冲区非空时耗尽缓冲区,main() 在缓冲区未满(例如 5 秒持续时间)水印时填充缓冲区。只要缓冲区低于水印,回调就会唤醒 main。
  • 关于c# - 使用回调播放带有端口音频的音频文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19987670/

    相关文章:

    c# - 为用户分配角色 DSharpPlus

    java - 如何修改Java声音文件的音高?

    c - C 中的 Windows 虚拟麦克风

    java - 我想在 .wav 中以 8000hz 的采样率在 android 手机中录制 6 秒的声音

    c++ - Portaudio C++ 绑定(bind) : symbol not found in MemFunCallbackStream

    c++ - 端口音频导致 50% 的测试发出响亮的嗡嗡声

    c# - 结合适用于本地和 Azure DevOps 的 C++ 和 C# 项目的最佳方式?

    c# - RGB 到 HSL 和返回,计算问题

    c# - Wpf ICollectionView 绑定(bind)项无法解析类型对象的属性

    algorithm - 检测音高