Frage

I'm trying to play raw pcm data delivered from ohLibSpotify c# library (https://github.com/openhome/ohLibSpotify).

I get the data in the following callback:

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
}

Now i want to directly play the received data with NAudio (http://naudio.codeplex.com/). With the following code snippet i can play a mp3 file from disk. Is it possible to directly pass the data received from spotify to NAudio and play it in realtime?

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

EDIT: I updated my code. The program doesn't throw any errors, but i also can't hear music. Is anything wrong in my code?

This is the music delivery callback:

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

And these are the overwritten callbacks in the 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;          //???
}
War es hilfreich?

Lösung

I think you can do this:

  1. Create a BufferedWaveProvider.
  2. Pass this to waveOut.Init.
  3. In your MusicDeliveryCallback, use Marshal.Copy to copy from the native buffer into a managed byte array.
  4. Pass this managed byte array to AddSamples on your BufferedWaveProvider.
  5. In your GetAudioBufferStats callback, use bufferedWaveProvider.BufferedBytes / 2 for "samples" and leave "stutters" as 0.

I think that will work. It involves some unnecessary copying and doesn't accurately keep track of stutters, but it's a good starting point. I think it might be a better (more efficient and reliable) solution to implement IWaveProvider and manage the buffering yourself.

I wrote the ohLibSpotify wrapper-library, but I don't work for the same company anymore, so I'm not involved in its development anymore. You might be able to get more help from someone on this forum: http://forum.openhome.org/forumdisplay.php?fid=6 So far as music delivery goes, ohLibSpotify aims to have as little overhead as possible. It doesn't copy the music data at all, it just passes you the same native pointer that the libspotify library itself provided, so that you can copy it yourself to its final destination and avoid an unnecessary layer of copying. It does make it a bit clunky for simple usage, though.

Good luck!

Andere Tipps

First, your code snippet shown above is more complicated than it needs to be. You only need five, instead of two using statments. Mp3FileReader decodes to PCM for you. Second, use WaveOutEvent in preference to WaveOut with function callbacks. It is much more reliable.

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

To answer you actual question, you need to use a BufferedWaveProvider. You create one of these and pass it to your output device in the Init method. Now, as you receive audio, decompress it to PCM (if it is compressed) and put it into the BufferedWaveProvider. The NAudioDemo application includes examples of how to do this, so look at the NAudio source code to see how its done.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top