0

I'm using C#, WPF, and NAudio to play a wav file.

I have sound_1.wav in the Resources folder and included in project. Once compiled, it exports the exe and resources to a folder and plays the wav from a path on the hard drive.

string sound1 = "Resources\\sound_1.wav";

NAudio.Wave.WaveFileReader wav = new NAudio.Wave.WaveFileReader(sound1);
WaveOutEvent output = new WaveOutEvent();
output.Init(wav);
output.Play();

But I would like to embed the wav file in the exe and use something like:

UnmanagedMemoryStream sound1 = Properties.Resources.sound_1; //embedded resource
NAudio.Wave.WaveFileReader wav = new NAudio.Wave.WaveFileReader(sound1);

Embedded Resources

How can I get that to play through WaveFileReader? It only accepts string path.


Solutions

This works, but the sound plays in slow motion and sounds like reverb.

UnmanagedMemoryStream sound1 = Properties.Resources.sound_1;

WaveIn wavin = new WaveIn();
NAudio.Wave.RawSourceWaveStream wav = new NAudio.Wave.RawSourceWaveStream(sound1, wavin.WaveFormat);         
WaveOutEvent output = new WaveOutEvent();
output.Init(wav);
output.Play();

This works with loud pop at the end of sound.

Convert Stream to Byte Array
https://stackoverflow.com/a/1080445/6806643

byte[] b = ReadToEnd(sound1);

WaveStream wav = new RawSourceWaveStream(new MemoryStream(b), new WaveFormat(44100, 16, 2));
WaveOutEvent output = new WaveOutEvent();
output.Init(wav);
output.Play();
Matt McManis
  • 3,987
  • 3
  • 30
  • 74
  • 3
    Have you tried [extracting the embedded resource to a byte array](https://stackoverflow.com/questions/10412401/how-to-read-an-embedded-resource-as-array-of-bytes-without-writing-it-to-disk) & then [creating a WaveFileReader using a MemoryStream](https://stackoverflow.com/questions/33954614/playing-byte-using-naudio) – PaulF Mar 08 '18 at 16:53
  • @PaulF It still seems to require a string path. – Matt McManis Mar 08 '18 at 17:18
  • I am not sure what you mean - WaveFileReader accepts a Stream as a constructor - this can be a MemoryStream created from a byte array. You create the byte array by extracting the embedded resource. – PaulF Mar 08 '18 at 17:23
  • @PaulF They use `String filename`. When I change it to `UnmanagedMemoryStream filename` to extract the embedded resource it still requires `String` for `a.GetManifestResourceStream(filename)`. – Matt McManis Mar 08 '18 at 17:30
  • @MattMcManis: doesn't it work to pass sound1 to GetManifestResourceStream like in the first link PaulF (accepted answer by Rotem) has provided? – oliver Mar 08 '18 at 17:39
  • Have you tried accessing the embedded file through Properties.Resources.sound_1 --- _"NAudio.Wave.WaveFileReader wav = new NAudio.Wave.WaveFileReader(new MemoryStream(Properties.Resources.sound_1), true);"_ – PaulF Mar 08 '18 at 17:42
  • @PaulF Error: `cannot convert from System.IO.UnmanagedMemoryStream to init` – Matt McManis Mar 08 '18 at 17:45
  • @PaulF I updated my post, at the bottom is some code that almost works. – Matt McManis Mar 08 '18 at 17:52
  • What do you mean by "almost works"? – PaulF Mar 09 '18 at 10:24
  • @PaulF It had some audio popping and artifacts. I managed to figure it out, but it has really high RAM usage. https://stackoverflow.com/q/49186932/6806643 – Matt McManis Mar 09 '18 at 10:29
  • @PaulF Here is the solution I came up with, still with problems. https://stackoverflow.com/a/49191890/6806643 – Matt McManis Mar 09 '18 at 10:40

1 Answers1

1

I figured out how to make it work.

The problem with this solution is high memory usage.

WAV

// embedded resource sound.wav

UnmanagedMemoryStream sound = Properties.Resources.sound;

MemoryStream ms = new MemoryStream(StreamToBytes(sound));

WaveStream ws = new WaveFileReader(ms);

WaveOutEvent output = new WaveOutEvent();

output.PlaybackStopped += new EventHandler<StoppedEventArgs>(Media_Ended);
output.Init(ws);
output.Play();

MP3

// embedded resource sound.mp3

MemoryStream sound = new MemoryStream(Properties.Resources.sound);

MemoryStream ms = new MemoryStream(StreamToBytes(sound));

WaveStream ws = new Mp3FileReader(ms);

WaveOutEvent output = new WaveOutEvent();

output.PlaybackStopped += new EventHandler<StoppedEventArgs>(Media_Ended);
output.Init(ws);
output.Play();

Stream to Byte Array

https://stackoverflow.com/a/1080445/6806643

I used this to convert MemoryStream to byte[], or else it will crash if 2 sounds play at the same time with Exception "Not a WAVE file - no RIFF header".


Dispose

Doesn't seem to have any affect on reducing RAM.

public static void Media_Ended(object sender, EventArgs e)
{
    if (output.PlaybackState == PlaybackState.Stopped)
    {
        if (ms != null)
        {
            ms.Close();
            ms.Flush();
        }
        if (ws != null)
        {
            ws.Close();
        }
        if (output != null)
        {
            output.Dispose();
        }
    }
}
Community
  • 1
  • 1
Matt McManis
  • 3,987
  • 3
  • 30
  • 74
  • You may need to call Dispose for each of the stream instances as they all implement IDisposable. – PaulF Mar 09 '18 at 10:48
  • @PaulF I tried calling `Close()` and `Dispose()` in `Media_Ended()`. That way it doesn't close a sound as it's playing. But the memory usage never goes down. – Matt McManis Mar 09 '18 at 10:50
  • Yeah, I can put it in `Media_Ended()` after every key press, but it's overkill. Made no difference. Here the yellow line show when GC is called. https://i.stack.imgur.com/Mat1a.jpg – Matt McManis Mar 09 '18 at 11:20
  • also try setting output to null and clearing the stopped event handler which could be keeping alive the wave out, which in turn is keeping alive the input stream – Mark Heath Mar 19 '18 at 10:03
  • @MarkHeath How can I clear the stopped event handler? It says it's ReadOnly if I do `output.PlaybackState = null`. – Matt McManis Mar 22 '18 at 01:18
  • you can do `-= new EventHandler(Media_Ended);` – Mark Heath Mar 22 '18 at 15:39
  • @MarkHeath Where should I put that, Before `output.Init()`, after `output.Play()` or in `Media_Ended()`? – Matt McManis Mar 22 '18 at 22:05
  • @MarkHeath Or just replace the `+=` one with `-=`? – Matt McManis Mar 23 '18 at 00:30