使用NAudio在C#中播放ohLibSpotify pcm数据流

时间:2014-01-23 11:59:05

标签: c# .net-4.0 naudio libspotify

我正在尝试播放来自ohLibSpotify c#library(https://github.com/openhome/ohLibSpotify)的原始pcm数据。

我在以下回调中获取数据:

public void MusicDeliveryCallback(SpotifySession session, AudioFormat format, IntPtr frames, int num_frames)
{
    //EXAMPLE DATA
    //format.channels = 2, format.samplerate = 44100, format.sample_type = Int16NativeEndian
    //frames = ?
    //num_frames = 2048
}

现在我想用NAudio(http://naudio.codeplex.com/)直接播放收到的数据。使用以下代码片段,我可以从磁盘播放mp3文件。是否可以直接将从spotify接收的数据传递给NAudio并实时播放?

using (var ms = File.OpenRead("test.pcm"))
using (var rdr = new Mp3FileReader(ms))
using (var wavStream = WaveFormatConversionStream.CreatePcmStream(rdr))
using (var baStream = new BlockAlignReductionStream(wavStream))
using (var waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
{
    waveOut.Init(baStream);
    waveOut.Play();
    while (waveOut.PlaybackState == PlaybackState.Playing)
    {
        Thread.Sleep(100);
    }
}

修改 我更新了我的代码。该程序不会抛出任何错误,但我也听不到音乐。我的代码有什么问题吗?

这是音乐传递回调:

public void MusicDeliveryCallback(SpotifySession session, AudioFormat format, IntPtr frames, int num_frames)
{
    //format.channels = 2, format.samplerate = 44100, format.sample_type = Int16NativeEndian
    //frames = ?
    //num_frames = 2048

    byte[] frames_copy = new byte[num_frames];
    Marshal.Copy(frames, frames_copy, 0, num_frames);

    bufferedWaveProvider = new BufferedWaveProvider(new WaveFormat(format.sample_rate, format.channels));
    bufferedWaveProvider.BufferDuration = TimeSpan.FromSeconds(40);            
    bufferedWaveProvider.AddSamples(frames_copy, 0, num_frames);
    bufferedWaveProvider.Read(frames_copy, 0, num_frames);

    if (_waveOutDeviceInitialized == false)
    {
        IWavePlayer waveOutDevice = new WaveOut();
        waveOutDevice.Init(bufferedWaveProvider);
        waveOutDevice.Play();
        _waveOutDeviceInitialized = true;
    }
}

这些是SessionListener中的覆盖回调:

public override int MusicDelivery(SpotifySession session, AudioFormat format, IntPtr frames, int num_frames)
{
    _sessionManager.MusicDeliveryCallback(session, format, frames, num_frames);
    return base.MusicDelivery(session, format, frames, num_frames);
}

public override void GetAudioBufferStats(SpotifySession session, out AudioBufferStats stats)
{
    stats.samples = 2048 / 2;   //???
    stats.stutter = 0;          //???
}

2 个答案:

答案 0 :(得分:4)

我认为你可以这样做:

  1. 创建BufferedWaveProvider。
  2. 将此传递给waveOut.Init。
  3. 在MusicDeliveryCallback中,使用Marshal.Copy从本机缓冲区复制到托管字节数组。
  4. 将此托管字节数组传递给BufferedWaveProvider上的AddSamples。
  5. 在GetAudioBufferStats回调中,使用bufferedWaveProvider.BufferedBytes / 2作为“samples”,并将“stutters”保留为0。
  6. 认为会起作用。它涉及一些不必要的复制,并没有准确地记录口吃,但它是一个很好的起点。我认为实现IWaveProvider并自行管理缓冲可能是更好(更有效和可靠)的解决方案。

    我写了ohLibSpotify包装库,但我不再为同一家公司工作了,所以我不再参与其开发了。您可以在此论坛上获得更多人的帮助:http://forum.openhome.org/forumdisplay.php?fid=6就音乐传递而言,ohLibSpotify的目标是尽可能减少开销。它不会复制音乐数据 ,它只是传递了libspotify库本身提供的相同的本机指针,因此您可以将其自己复制到其最终目的地并避免不必要的层复制。尽管如此,它确实让它变得有点笨拙。

    祝你好运!

答案 1 :(得分:3)

首先,上面显示的代码段比它需要的更复杂。您只需要五个,而不是两个using个参数。 Mp3FileReader为您解码为PCM。其次,使用WaveOutEvent优先于WaveOut进行函数回调。它更可靠。

using (var rdr = new Mp3FileReader("test.pcm"))
using (var waveOut = new WaveOutEvent())
{
   //...
}

要回答实际问题,您需要使用BufferedWaveProvider。您可以创建其中一个并将其传递给Init方法中的输出设备。现在,当您收到音频时,将其解压缩到PCM(如果它被压缩)并将其放入BufferedWaveProvider。 NAudioDemo应用程序包含如何执行此操作的示例,因此请查看NAudio源代码以了解其完成情况。