morphx666 / CoreAudio

Windows CoreAudio wrapper for .NET
https://whenimbored.xfx.net/2011/01/core-audio-for-net/
MIT License
95 stars 20 forks source link

Unhandled exception when disposing AudioSessionManager2 #12

Closed LAB02-Admin closed 2 years ago

LAB02-Admin commented 2 years ago

hi @morphx666, I'm trying to use your (great!) library to continuously monitor volume level, sessions and default audio device.

It all works great, apart from when I want to dispose an AudioSessionManager2 because the default device changed. It then crashes here:

void UnregisterNotifications()
{
    _Sessions = null;

    if (_AudioSessionNotification != null)
        Marshal.ThrowExceptionForHR(_AudioSessionManager2.UnregisterSessionNotification(_AudioSessionNotification));
}

I'm simply calling Dispose() on my object. What am I doing wrong?

morphx666 commented 2 years ago

Hi @LAB02-Admin, if I understand correctly the error occurs when the default audio output device changes?

If that's the case, I will need to run some tests to see if I can reproduce the problem.

LAB02-Admin commented 2 years ago

No that works fine. When the device changes, I dispose the AudioSessionManager2 (that's where it crashes) and fetch a new one. But I just realised that's probably not the right way.

What do you recommend? Just keep using the same MMDevice & AudioSessionManager2, even when the device changes, and only dispose when closing the application?

LAB02-Admin commented 2 years ago

If I don't renew the AudioSessionManager2, it doesn't pick up sessions that are bound to the new device, so I can't keep using the same one. You probably already knew that though, just reporting what I tested :)

morphx666 commented 2 years ago

Each MMDevice has its own AudioSessionManager2 so when changing the device, the new device will contain its own set of sessions via its own AudioSessionManager2.

If you run this sample you will see that every time you select a different output device, the sessions change accordingly.

Regarding that sample, I will be soon updating it to support the creation and expiration of sessions.

LAB02-Admin commented 2 years ago

Alright, thanks for the info! I'll be processing volume/session changes non stop, so what do you recommend regarding disposals? Memory leaks seem to be hard to avoid when working with audio devices.

LAB02-Admin commented 2 years ago

The odd thing is, that when I'm just periodically looping through the devices and their sessions, the number of objects (and amount of unmanaged memory) just keep rising:

image

This is whether I dispose where objects can be disposed, or not.

These objects just keep rising in count:

image

Any idea what I'm doing wrong or what's happening?

morphx666 commented 2 years ago

I'm sorry for taking so long to reply.

Something that occurs to me would be to call RefreshSessions() on the AudioSessionManager2 every time you "re-connect" to an MMDevice. This will force a dispose on the object and will then re-create it with all sessions attached to that device.

LAB02-Admin commented 2 years ago

Don't worry about it :)

I'm already doing that:

image

I've published the source here: https://github.com/LAB02-Admin/AudioDetectionTest

The relevant file is AudioManager.cs.

Maybe you could have a quick look when you have some spare time?

morphx666 commented 2 years ago

Absolutely. Give me time until this weekend as I'm swamped!

LAB02-Admin commented 2 years ago

Of course, good luck!

morphx666 commented 2 years ago

So now it happens that I cannot run the profiler on VS 2022 because of some weird bug.

I will check the code (manually) and I'll let you know if I can find anything...

morphx666 commented 2 years ago

Good news. I have just updated the library (1.12.0) to include a Dispose() method for MMDevice. This appears to solve the problem.

In your sample, I'd recommend doing the following changes:

Please let me know if you notice any improvements.

If it works, it'd be interesting to test if removing all the manual Dispose's also works, as the GC should take care of that automatically.

LAB02-Admin commented 2 years ago

Wow @morphx666, this is some grade-A support :)

Been running for a while and no more memory leak, very happy with this, thank you!

I'll remove the manual disposes, test again later and let you know

LAB02-Admin commented 2 years ago

After removing all disposes, the obj count keeps rising:

image

LAB02-Admin commented 2 years ago

Another weird thing; the objects are going great, but the amount of unmanaged mem keeps rising (like 40MB with just a few MB for the app itself).

I noticed a drop in the unmanaged mem when the profiler called the GC, so I added this to run every 5 min:

GC.Collect();
GC.WaitForPendingFinalizers();

And now the memory stays 100% stable.

Weird though, never had to use that before.

morphx666 commented 2 years ago

hmmm... that doesn´t sound right. I guess I'll have to run some more tests and see if I can figure out what's going on.

LAB02-Admin commented 2 years ago

Could be that I'm just too focussed on it now, and that GC will collect it eventually. I'll keep it running for a few days to see what happens.

LAB02-Admin commented 2 years ago

It's all good, I was indeed just too focussed. GC just keeps the memory around to save resources, but it stays around 30-50MB. Its released when I run out of memory, but that normally doesn't happen.

Thanks for all your support @morphx666, really great :)