This project has moved. For the latest updates, please go here.

Sound won't play in exclusive mode

Jun 27, 2012 at 2:21 PM

Hello, I'm trying to play a .wav file in a service in vb.net (2010/4.0).

When I change AudioClientShareMode.Exclusive to AudioClientShareMode.Shared the sound plays fine, but gets cut off sometimes (I'm assuming by other sounds that play in Exclusive mode).

I'm admittedly not that familiar with Playing Audio through vb.net and I'm just trying to piece together examples until it works.

What is the best way to make sure my sound plays?

Here is my code (wavPath is the relative path to my wav File).  It throws the error: "Can't find a supported format to use."

                    'Play sound
                    Dim waveoutdevice As IWavePlayer
                    Dim mainoutputstream As WaveStream
                    Dim inputStream As WaveChannel32
                    Dim wavreader As WaveStream

                    waveoutdevice = New WasapiOut(NAudio.CoreAudioApi.AudioClientShareMode.Exclusive, 100)

                    wavreader = New WaveFileReader(wavPath)
                    inputStream = New WaveChannel32(wavreader)

                    mainoutputstream = inputStream
                    waveoutdevice.Init(mainoutputstream)

                    waveoutdevice.Play()

                    'Wait for sound to play
                    Threading.Thread.Sleep(wavreader.TotalTime.Milliseconds * 10)

                    'Cleanup
                    waveoutdevice.Stop()
                    waveoutdevice.Dispose()
                    mainoutputstream.Close()
                    mainoutputstream.Dispose()
                    inputStream.Close()
                    inputStream.Dispose()
                    wavreader.Close()
                    wavreader.Dispose()

Thanks!

Coordinator
Jun 29, 2012 at 3:33 PM

wasapi out can be very picky about the exact waveformat you feed it. I would recommed using WaveOut if possible. If you want to use WasapiOut, find out what WaveFormats it supports in exclusive mode and check you pass it that.

Jun 29, 2012 at 6:56 PM

Hello markhealth,

Thank you for the suggestion.  I have tried using WaveOut, the only problem is it doesn't appear to work in my service, though it works fine in a normal form.  It is very important that the sound plays regardless of whether or not a user is logged into the computer, so a service is necessary.

I will try to look further into WasapiOut - would that be on Microsoft's website?

Currently I have implemented the "solution" in this thread: http://naudio.codeplex.com/discussions/356912/

I am playing my sound in shared mode and if it errors (detected by exactly what that thread describes) I am replaying my sound 1 second later, trying no more than 5 times to avoid infinite loops.

I don't believe this is the best solution, but I'm not sure if I can get anything else to work.

Is there a different format that would work better for wasapiout? Converting the sound to mp3 would not be too much of a hassle if that fixes the problem.

Thanks again for the reply!

Coordinator
Jun 29, 2012 at 7:01 PM

WaveOut won't work in a service because it uses window callbacks. Try WaveOutEvent instead, which uses a background thread.

Jun 29, 2012 at 8:00 PM

Hello markheath,

After playing around with WaveOutEvent I have it working!  So just for my own sanity, does waveoutevent play in exclusive mode? Is there such a thing as shared/exclusive modes with waveoutevent?  Will my sound ever be cut off by an exclusive mode sound?

Thanks for your help!

Coordinator
Jun 30, 2012 at 7:14 PM

No waveoutevent doesn't use exclusive mode. If another application uses exlusive mode (which should be rare) it will take over the soundcard.

Jul 2, 2012 at 2:17 PM

Thank you for your help markheath!

Jul 19, 2014 at 4:55 AM
Neither DirectSoundOut nor WaveOutEvent was an option for me. Both DirectSoundOut and WaveOutEvent made audible pops at the beginning and end of playback on all the machines I used for testing. Wasapi produces no pops with the same audio files on the same test machines.

Here is how I got around the five second cut off with Wasapi used in shared mode in a Windows service.

1) I installed NuGet. http://docs.nuget.org/docs/start-here/installing-nuget
2) I got the latest version of NAudio (1.7.1) using NuGet. https://www.nuget.org/packages/NAudio
3) I used the code from this blog: http://bresleveloper.blogspot.co.il/2012/06/c-service-play-sound-with-naudio.html.
4) I modified that code. The code is reproduced below with my modification, which checks the playstate every second and waits until the file is done playing before exiting the procedure.

I hope this helps someone because getting audio to work nicely in a windows service is a pain.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;

using NAudio.Wave;
using NAudio.CoreAudioApi;
using NAudio.Wave.SampleProviders;

namespace CallAndRead
{
    public interface IInputFileFormatPlugin
    {
        string Name { get; }
        string Extension { get; }
        WaveStream CreateWaveStream(string fileName);
    }

    [Export(typeof(IInputFileFormatPlugin))]
    class WaveInputFilePlugin : IInputFileFormatPlugin
    {
        public string Name
        { get { return "WAV file"; } }
        public string Extension
        { get { return ".wav"; } }

        public WaveStream CreateWaveStream(string fileName)
        {
            WaveStream readerStream = new WaveFileReader(fileName);
            if (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm
                  && readerStream.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
            {
                readerStream = WaveFormatConversionStream.CreatePcmStream(readerStream);
                readerStream = new BlockAlignReductionStream(readerStream);
            }
            return readerStream;
        }
    }

    class WASAPI
    {
        public static void Concatenate(string outputFile, IEnumerable<string> sourceFiles)
        {
            byte[] buffer = new byte[1024];
            WaveFileWriter waveFileWriter = null;
            try
            {
                foreach (string sourceFile in sourceFiles)
                {
                    using (WaveFileReader reader = new WaveFileReader(sourceFile))
                    {
                        if (waveFileWriter == null)
                            waveFileWriter = new WaveFileWriter(outputFile, reader.WaveFormat);
                        else
                        {
                            if (!reader.WaveFormat.Equals(waveFileWriter.WaveFormat))
                                throw new InvalidOperationException(
                                         "Can't concatenate WAV Files that don't share the same format");
                        }
                        int read;
                        while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            waveFileWriter.Write(buffer, 0, read);
                        }
                    }
                }
            }
            finally
            {
                if (waveFileWriter != null)
                    waveFileWriter.Dispose();
            }
        }

        private IWavePlayer waveOut = new WasapiOut(AudioClientShareMode.Shared, 300);
        WaveStream fileWaveStream;
        Action<float> setVolumeDelegate;

        [ImportMany(typeof(IInputFileFormatPlugin))]
        public IEnumerable<IInputFileFormatPlugin> InputFileFormats { get; set; }

        void OnPreVolumeMeter(object sender, StreamVolumeEventArgs e)
        {
            // we know it is stereo
            //w aveformPainter1.AddMax(e.MaxSampleValues[0]);
            //waveformPainter2.AddMax(e.MaxSampleValues[1]);
        }

        public ISampleProvider CreateInputStream(string fileName)
        {
            var plugin = new WaveInputFilePlugin();
            if (plugin == null)
                throw new InvalidOperationException("Unsupported file extension");
            fileWaveStream = plugin.CreateWaveStream(fileName);
            var waveChannel = new SampleChannel(fileWaveStream);
            setVolumeDelegate = (vol) => waveChannel.Volume = vol;
            waveChannel.PreVolumeMeter += OnPreVolumeMeter;
            var postVolumeMeter = new MeteringSampleProvider(waveChannel);
            postVolumeMeter.StreamVolume += OnPostVolumeMeter;
            return postVolumeMeter;
        }

        void OnPostVolumeMeter(object sender, StreamVolumeEventArgs e)
        {
            // we know it is stereo
            //volumeMeter1.Amplitude = e.MaxSampleValues[0];
            //volumeMeter2.Amplitude = e.MaxSampleValues[1];
        }

        public WASAPI(string fileName)
        {
            ISampleProvider sampleProvider = null;
            sampleProvider = CreateInputStream(fileName);
            waveOut.Init(new SampleToWaveProvider(sampleProvider));
            waveOut.Play();
            while (waveOut.PlaybackState == PlaybackState.Playing)
            {
                Thread.Sleep(1000);
            }
            return;
        }
    }
}
To play a file just call this:
CallAndRead.WASAPI Player = new CallAndRead.WASAPI(<string path to audio file>);
Yeah, I know, NUTS! but it works.

-Tom