Question

I am trying to make a musical instrument type of application. The problem I am having is that a new sound will only play if the old one is finished. I would like to be able to play them simultaneously.

This is how my code looks like:

First, the MyWave class which simply holds an audio buffer and some other info:

class MyWave
{
    public AudioBuffer Buffer { get; set; }
    public uint[] DecodedPacketsInfo { get; set; }
    public WaveFormat WaveFormat { get; set; }
}

In the SoundPlayer class:

    private XAudio2 xaudio;
    private MasteringVoice mvoice;
    Dictionary<string, MyWave> sounds;

    // Constructor
    public SoundPlayer()
    {
        xaudio = new XAudio2();
        xaudio.StartEngine();
        mvoice = new MasteringVoice(xaudio);
        sounds = new Dictionary<string, MyWave>();
    }

    // Reads a sound and puts it in the dictionary
    public void AddWave(string key, string filepath)
    {
        MyWave wave = new MyWave();

        var nativeFileStream = new NativeFileStream(filepath, NativeFileMode.Open, NativeFileAccess.Read, NativeFileShare.Read);
        var soundStream = new SoundStream(nativeFileStream);
        var buffer = new AudioBuffer() { Stream = soundStream, AudioBytes = (int)soundStream.Length, Flags = BufferFlags.EndOfStream };

        wave.Buffer = buffer;
        wave.DecodedPacketsInfo = soundStream.DecodedPacketsInfo;
        wave.WaveFormat = soundStream.Format;

        this.sounds.Add(key, wave);
    }

    // Plays the sound
    public void Play(string key)
    {
        if (!this.sounds.ContainsKey(key)) return;
        MyWave w = this.sounds[key];

        var sourceVoice = new SourceVoice(this.xaudio, w.WaveFormat);
        sourceVoice.SubmitSourceBuffer(w.Buffer, w.DecodedPacketsInfo);
        sourceVoice.Start();
    }
}

Google wasn't very helpful, I couldn't find anything useful. So how can I play multiple sounds simultaneously?

Was it helpful?

Solution

You'll have to create (and preferably pool) multiple SourceVoice instances and play them back simultaneously.

In fact, your current code should work, no? You may want to add a StreamEnd event listener to the SourceVoice to dispose of itself after playback is complete, and remember to enable callbacks when calling the constructor of the SourceVoice.

OTHER TIPS

Just use mciSendString with the open and play commands. This sample is playing note1.wav and note2.wav together at launch.

[System.Runtime.InteropServices.DllImport("winmm.dll")]
static extern Int32 mciSendString(string command,                                             
                                  StringBuilder buffer, 
                                  int bufferSize, 
                                  IntPtr hwndCallback);

        public frmGame()
        {
            InitializeComponent();
            DoubleBuffered = true;            
            mciSendString("open note1.wav type waveaudio  alias s1", null, 0, IntPtr.Zero);
            mciSendString("play s1", null, 0, IntPtr.Zero);
            mciSendString("open note2.wav type waveaudio  alias s2", null, 0, IntPtr.Zero);
            mciSendString("play s2", null, 0, IntPtr.Zero);
        }        
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top