Cleanup of XAudio2 device.

Started by
5 comments, last by trinith_006 12 years ago
Hey all,

So I've got a question about object cleanup. I've created some sound playback functionality using SlimDX for a project I'm working on and it's working great; however I've got one hitch.

Right now I'm creating only one, static instance of my XAudio2 device that all my sounds will use to play. This is for the entire application. A while back I was having some problems with this and the advice I got was to only have once device (http://www.gamedev.n...stream-xaudio2/). This worked out well and to manage it, I created a static class called SoundEngine that would control instantiating and disposing of that XAudio2 object with the startup and shut down of my program so that everything would dispose nicely.

Now for the hitch. The product this is integrated with allows scripting and this scripting is allowed access to any DLL. This is intentional and actually fits with the goals I had in mind for this functionality. I want scripts to be able to create and play sounds. However, what I do not want is for the scripts to have access to SoundEngine, as I only want the hosting application to be able to start up and shut down the sound engine (ie, create or dispose of the XAudio2 object).

So I ran through a bunch of ideas and the simplest/easiest I could think of was to just not allow the creation/destruction of the XAudio2 object. Just create it at start up and let it get destroyed with all the other objects when the application is turned off. So I've been experimenting with this and now I'm getting the following output...

Object of type SlimDX.XAudio2.XAudio2 was not disposed. Stack trace of object creation:
Total of 1 objects still alive.[/quote]

... which makes sense. I've purposely not disposed of the XAudio2 device. So after all that rambling and backstory, my question is this: Is this a problem?

I don't intend for my device to ever get destroyed except when the application shuts down, and all my sound objects will properly dispose themselves so, under ideal circumstances, we should never see this message with more than that single object still alive. But I figured it might be a good idea to check and see if anyone had any thoughts on this and if, should any SlimDX developers be watching this forum, whether or not there could be any unintended side effects of this behaviour.

Thanks for reading, and thanks for any insight provided smile.png
Advertisement
The operating system will clean up after your process exits, so it's probably not a big deal if you leave just the one object instead of disposing of it.

With that being said, I'm wondering why you can't expose a reduced sound playing API for scripts to access.
Mike Popoloski | Journal | SlimDX
Cool, that's what I was thinking too.

And the reason is that our scripting allows the use of any .NET assembly. We don't directly control what they are able to use, so there would be no way to stop them from using this functionality. Whether or not that's good, this is the way that functionality got implemented so I'll just have to work around it I guess :)

Thanks for your feedback, much appreciated!
So a bit of a follow up to this... everything works great in the C# world but unfortunately there appear to be some problems in the unmanaged/managed mixed C++ world. When I run in the application I'm integrating with, I'm getting a crash message that says "string binding is invalid" along with some other dump info. I went back to disposing of my device when the program exits and still got it. Turns out I have to dispose of both the device and the mastering voice, then no crash.

I was able to boil this down to a separate project, outside the main application, that can reproduce the issue. Create a C# class library with the following code:

using System;
using System.Collections.Generic;
using System.Text;

using SlimDX;
using SlimDX.XAudio2;
using SlimDX.Multimedia;

namespace TestingSlimDX
{
public class SomeTest
{
public static XAudio2 device = null;
public static MasteringVoice masteringVoice = null;

public static void PlayWarning()
{
device = new XAudio2();
masteringVoice = new MasteringVoice(device);

WaveStream waveStream = new WaveStream("c:\\temp\\warn2.wav"); // Whatever sound you want here
AudioBuffer buffer = new AudioBuffer();
buffer.AudioData = waveStream;
buffer.AudioBytes = (int)waveStream.Length;
buffer.Flags = BufferFlags.EndOfStream;

SourceVoice sourceVoice = new SourceVoice(device, waveStream.Format);

buffer.AudioData.Position = 0;
sourceVoice.SubmitSourceBuffer(buffer);
sourceVoice.Start();

while (sourceVoice.State.BuffersQueued > 0) ;

sourceVoice.Dispose();
buffer.Dispose();
//waveStream.Dispose();

//Console.Write("Disposing of mastering voice... ");
//masteringVoice.Dispose();
//Console.WriteLine("Finished.");
//Console.WriteLine();

//Console.Write("Disposing of device... ");
//device.Dispose();
//Console.WriteLine("Finished.");
//Console.WriteLine();
}
}
}


Build this, then create a new C++ console application. Use the following code:
#include <iostream>
#include <windows.h>

using namespace std;
using namespace System;

using namespace TestingSlimDX;

int StartPoint(void);

//#pragma unmanaged
//int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int iCmdShow)
//{
// int retVal = StartPoint();
//
// return retVal;
//}

#pragma unmanaged
int main(void)
{
int retVal = StartPoint();

return retVal;
}

#pragma managed
int StartPoint(void)
{

Console::WriteLine("Starting execution of main...");
try
{
Console::WriteLine("Starting sound playback...");
SomeTest::PlayWarning();
Console::WriteLine("Sound playback complete!");
}
catch (Exception^ e)
{
Console::WriteLine("Exception occurred!" + Environment::NewLine + e->ToString());
}
Console::WriteLine("Execution of main complete.");

return 0;
}


So build the c++ application and run it. For me, I had to run it twice for some strange reason. The first run wouldn't crash, but the second would... then every run after that. If an undetermined period of time passed, it would then succeed on first run again. I have no idea how long that is... it just seems to work like that. The crash also happens after main exits, so I'm not getting any debug information out of this. I'm not sure how our application gets it either. All I get is a windows dialog saying "blahbityblah.exe has stopped working".

Now, if I change the C# code so to uncomment the disposes for device and masteringVoice, build it and then rebuild the C++ project to update the dll, no crashes at all. Everything works fine.

I've downloaded the SlimDX source and I don't see anything that would be funny... the destructors both do NULL checks before trying to dispose. I'm attempting to get it building on my machine so that I can set some breakpoints and see if I can figure it out that way but I figured I'd also post here to see if anybody else had encountered this and/or had any insights.

Thanks!
I have no idea what's causing this, but I can make a few guesses that might be useful to you when trying to track it down.

My guess is that the unmanaged entry point causes the CLR to spool up in slightly different way with regards to the COM state of the thread. XAudio2 requires a CoInitialize call and when everything gets torn down at the end something might be going wrong with that.

Hope that ends up being helpful.
Mike Popoloski | Journal | SlimDX
Hmmm, perhaps... though I'm not sure why disposing of the items would cause the crash to go away.

Is SlimDX doing the CoInitialize call for XAudio2 somewhere? I searched through XAudio2.cpp and MasteringVoice.cpp and didn't get any hits. Our main application does have a CoInitialize call (and subsequent CoUninitialize at the end) and exhibits the same behvaiour as the test app, which doesn't have it's own CoInitialize calls.

(Also, I should mention, I know very little about these... the bulk of my experience is on the C# end of things but I find myself getting mixed into unmanaged C++ code :D)

I appreciate the help and if I can get the debug working maybe I'll have a bit more information, which is taking a while to set up. Yay slow networks.
Just to update this...

I ran out of time and had to go with a work around solution that I'm not entirely happy with, but I guess them's the breaks. I'll try to revisit the problem at a later date and if I make any headway on it, I'll post back here.

Thanks for your help though, Mike. I appreciate the comments!

This topic is closed to new replies.

Advertisement