Jump to content
  • Advertisement
Sign in to follow this  
Grain

SlimDX Xaudio2

This topic is 3248 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

The documentation for this API is severely lacking. How are you supposed to use it effectively. It's just a bunch of guess work.[/endrant] This program used to work but then I got the newer version of SlimDX where AudioBuffer takes a stream instead of a byte array and I had to changes a couple things around.... now the whole thing is broken. What am I doing wrong? I get no sound, just pops an clicks occasionally.
using SlimDX.XAudio2;
using SlimDX.Multimedia;

namespace SimpleSpeaker
{
    abstract public class Speaker
    {
        abstract public void Dispose();
        abstract public void Play();
        abstract public void Stop();
        abstract public bool BufferSoundData(double[] data);
    }

    public class Speaker_XAudio2 : Speaker
    {
        XAudio2 device;
        MasteringVoice masteringVoice;
        SourceVoice sourceVoice;
        WaveFormat waveFormat;
        int bytesPerSample;
        AudioBuffer buffer;

        public Speaker_XAudio2(short BitsPerSample, short Channels, int SamplesPerSecond)
        {

            WaveFormat format = new SlimDX.Multimedia.WaveFormat();
            format.BitsPerSample = BitsPerSample;
            format.Channels = Channels;
            format.SamplesPerSecond = SamplesPerSecond;
            format.BlockAlignment = (short)(format.Channels * format.BitsPerSample / 8);
            format.AverageBytesPerSecond = format.SamplesPerSecond * format.BlockAlignment;
            // format.FormatTag = SlimDX.WaveFormatTag.IeeeFloat;  // <-- this is from the old version
            format.FormatTag = WaveFormatTag.IeeeFloat;  // <-- this is new
            device = new XAudio2();
            masteringVoice = new MasteringVoice(device);
            sourceVoice = new SourceVoice(device, format);
            buffer = new AudioBuffer();
            buffer.AudioData = new System.IO.MemoryStream(); // <-- this is new
            sourceVoice.SubmitSourceBuffer(buffer);
            waveFormat = format;
            bytesPerSample = waveFormat.BitsPerSample / 8;
        }

        override public void Dispose()
        {
            buffer.Dispose();
            sourceVoice.Dispose();
            masteringVoice.Dispose();
            device.Dispose();
        }

        override public void Play() { sourceVoice.Start(); }
        override public void Stop() { sourceVoice.Stop(); }

        override public bool BufferSoundData(double[] data)
        {
            if (sourceVoice.State.BuffersQueued > 1)
            {
                return false;
            }
            else
            {                
                
                    for (int i = 0, j = 0; i < bData.Length; i += 4, j++)
                    {
                        float flt = (float)data[j];
                        byte[] tmp = System.BitConverter.GetBytes(flt);
                        bData = tmp[0];
                        bData[i + 1] = tmp[1];
                        bData[i + 2] = tmp[2];
                        bData[i + 3] = tmp[3];
                    }

		//*****MOST OF THIS IS WHAT WAS RECONFIURED**********               
                buffer.AudioData.SetLength(0);
                buffer.AudioData.Write(bData,0,bData.Length);
                buffer.AudioData.Position = 0;
                buffer.AudioBytes = bData.Length;
                buffer.Flags = BufferFlags.None;
                sourceVoice.SubmitSourceBuffer(buffer);
                return true;
            }
        }
    }
}


This class is called by another program that feeds it programmaticly generated sine waves through the BufferSoundData method. That program has not changed at all.

Share this post


Link to post
Share on other sites
Advertisement
As far as I can tell form looking at the SlimDX code if you give the buffer a simple stream it tries to copy it into an array of bytes that it then pins using a handle inside of the buffer object itself. My first guess would be that you buffer is getting prematurely disposed or reused. Seems like you should keep a reference to the buffer (somehow prevent it from getting disposed) and not re-use it until the SourceVoice fires a BufferEnd for the buffer you submitted. (You can track this using the Context property of the buffer).

When I do this I allocate a block of memory with Marshal.AllocHGlobal, Copy my buffer data into that block and then create a DataStream on the pointer before assigning it to the buffer's AudioData property. It appears that the conversion has special behavior if you pass it a DataStream and if I read it right will use a direct pointer rather than create another copy. I monitor the BufferEnd and only delete/reuse the buffer when I know the source voice is done with it.

Also review all your counts and indicates to make sure you are using samples when you need samples and bytes when you need bytes, this has messed me up before.

Hope this helps.

Share this post


Link to post
Share on other sites
Quote:
Original post by turnpast
As far as I can tell form looking at the SlimDX code if you give the buffer a simple stream it tries to copy it into an array of bytes that it then pins using a handle inside of the buffer object itself. My first guess would be that you buffer is getting prematurely disposed or reused. Seems like you should keep a reference to the buffer (somehow prevent it from getting disposed) and not re-use it until the SourceVoice fires a BufferEnd for the buffer you submitted. (You can track this using the Context property of the buffer).

When I do this I allocate a block of memory with Marshal.AllocHGlobal, Copy my buffer data into that block and then create a DataStream on the pointer before assigning it to the buffer's AudioData property. It appears that the conversion has special behavior if you pass it a DataStream and if I read it right will use a direct pointer rather than create another copy. I monitor the BufferEnd and only delete/reuse the buffer when I know the source voice is done with it.
I highly doubt it's getting disposed. It's created in the constructor and the reference to it is a member of the class. It should live as long as the Speaker object does.

Though perhaps it could be that it's getting reused by the sourcevoice. I should perhaps have an array of 4 or so buffers and using a sliding slot technique to feed them to the sorucevoice?

Quote:
Original post by turnpast
Also review all your counts and indicates to make sure you are using samples when you need samples and bytes when you need bytes, this has messed me up before.

Hope this helps.
Hmm... That may be it, the question is where does it want samples and where bytes. I'm assuming buffer.AudioBytes is actually bytes and not samples. Originally when I wrote this class I had issues along these lines, but I resolved them. Could the SlimDX implementation have changed in that regard?

Share this post


Link to post
Share on other sites
It looks like every time you call BufferSoundData it re-uses the buffer, so if you call this before the SourceVoice is done with the buffer it could cause problems. I use 3 rotating buffers and have an alternate thread (not the thread from the callback) fill them in response to a BufferEnd. I don't seen anything obviously wrong with your counts, I use this the following to create a mono floating point output:

XAudio2 device = new XAudio2(XAudio2Flags.DebugEngine, ProcessorSpecifier.DefaultProcessor);
device.StartEngine();
WaveFormat wf = new WaveFormat{
FormatTag = WaveFormatTag.IeeeFloat,
Channels = 1,
SamplesPerSecond = 44100,
BitsPerSample = 32,
BlockAlignment = 4,
AverageBytesPerSecond = 44100 * 4,
};
MasteringVoice mv = new MasteringVoice(device, 2, 44100, 0);
SourceVoice sv = new SourceVoice(device, wf, VoiceFlags.UseFilter);

Share this post


Link to post
Share on other sites
I tried using multiple buffers and cycling between them.

Same thing.

Going with samples instead of bytes gets me bad static instead of silence, however the is a discernible oscillation to the static that is at the same freq as the intended sound. Not sure what that means.

Would you mind showing me how you load the stream with floats and also how you pass it to the source voice? Thanks.

Share this post


Link to post
Share on other sites
Oh for fucks sake!

I'm a complete idiot.

Turns out it was working perfectly to begin with. SlimDX wasn't the only thing that had changed. I'm now developing on my cheep netbook instead of my desktop(which I now use for testing and want to keep clean of development tools). The thing is, the netbook's crappy speakers don't really reproduce sounds in the 50Hz to 200Hz range all that well.

Using my pair of $300 Etymotic ER4 Earphones,(which is more than my net book cost BTW) I could hear the sound booming loud and crystal clear.

Changing to frequency to 500Hz and listening on the built-in speakers also worked.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!