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

Get AudioSessionControl2 object from OnSessionCreated event #24

Closed valentimarco closed 11 months ago

valentimarco commented 11 months ago

Hi, first of all thanks for this piece of art! I am writing simple VR mixer and i had been blocked in this part of code, where i need to take the AudioSessionControl2 object connected to a specific Process and insert it in a list. My first idea was to modified the code, by changing the access modifier from internal to public. (But if was written as internal, is there a reason??) Is there a way to get this object without modify the code?

Here the code

var list = new ObservableCollection<AudioSessionControl2>();

var changestate = new AudioSessionControl2.StateChangedDelegate(delegate (object o, AudioSessionState reason) {
    if (reason == AudioSessionState.AudioSessionStateExpired) 
        list.Remove((AudioSessionControl2) o);
});

var eventContext = Guid.NewGuid();
    MMDeviceEnumerator DevEnum = new MMDeviceEnumerator(eventContext);
    MMDevice device = DevEnum.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);

    //evento per individuare nuove instance audio. Da effettuare dopo la scansione delle sessioni premilimari!
    device.AudioSessionManager2.OnSessionCreated += (sender, session) => {
        Console.WriteLine("Ho creato una nuova instanza Audio!");
        uint pid;
        session.GetProcessId(out pid);
        Console.WriteLine(Process.GetProcessById((int)pid).ProcessName);
        /*
          Here i Need the AudioSessionControl2 object.
        */
        // my first idea works, but required the change to access modifier "public"
        var audio = new AudioSessionControl2(session, typeof(IAudioSessionControl2).GUID);
        audio.OnStateChanged += changestate;
        list.Add(audio);
    };

    foreach (var endpoint in device.AudioSessionManager2.Sessions) {
        endpoint.OnStateChanged += changestate;
        list.Add(endpoint);
    }

    list.CollectionChanged += (sender, eventArgs) => {
        if(eventArgs.Action is not (NotifyCollectionChangedAction.Add or NotifyCollectionChangedAction.Remove)) return;
        UpdateGui();

        void UpdateGui() {
            Console.WriteLine("---------------inizio---------------");
            foreach (var VARIABLE in list) {
                Console.WriteLine(Process.GetProcessById((int)VARIABLE.ProcessID).ProcessName);
                Console.WriteLine("-------------------------------");
            }
            Console.WriteLine("---------------fine---------------");
        }
    };

foreach (var VARIABLE in list) {
    Console.WriteLine(Process.GetProcessById((int)VARIABLE.ProcessID).ProcessName);
    Console.WriteLine("-------------------------------");
} 

//stay open the process....
Console.ReadLine();
morphx666 commented 11 months ago

Hi @valentimarco,

I'm not sure I understand, but the AudioSessionControl2 is passed as a parameter in the OnSessionCreated event handler. It's the second parameter (session).

I guess you could write something like this:

var audio = (AudioSessionControl2)session;
audio.OnStateChanged += changestate;
valentimarco commented 11 months ago

Hi, thx for the response I can confirm that casting do not work, the program breaks (without crashing) in that line. In the code as you can see, i call the constructor of AudioSessionControl2 bc i modify the project by putting public the constructor... but i don't think is a good idea, so i asked with this issue if there are some other method to do the same thing...

morphx666 commented 11 months ago

My bad... it's been a while since I personally used CoreAudio ;)

Looking at the sample code from CoreAudioForms.Framework.Sessions I see that the handler for the OnSessionCreated does not attach to the OnStateChange event for that session; instead, it queries the AudioSessionManager2 from the sender and gathers all the sessions from that object and that's where the OnStateChanged event handler is attached.

valentimarco commented 11 months ago

Ok, i will try tomorrow! thx

morphx666 commented 11 months ago

Here's a simplified version of the code from the sample. Hope this helps.

private void HandleSessionCreated(object sender, IAudioSessionControl2 newSession) {
    AudioSessionManager2 asm = (AudioSessionManager2)sender;
    newSession.GetProcessId(out uint newSessionId);

    asm.RefreshSessions();
    foreach(var session in asm.Sessions) {
        if(session.ProcessID == newSessionId) {
            session.OnStateChanged += (s, e) => {
                // Your code here
            };
            break;
        }
    }
}
valentimarco commented 11 months ago

Thank you for the Code Snippet, all works flawlessly! i have another question: When AudioSessionManager2 object is (potentially) null in MMDevice object?

morphx666 commented 11 months ago

Awesome! Glad I could help...

I don't think I have ever encountered AudioSessionManager2 being null and I did test CoreAudio on many, many devices when I was actively developing it.