morphx666 / coreaudio Goto Github PK
View Code? Open in Web Editor NEWWindows CoreAudio wrapper for .NET
Home Page: https://whenimbored.xfx.net/2011/01/core-audio-for-net/
License: MIT License
Windows CoreAudio wrapper for .NET
Home Page: https://whenimbored.xfx.net/2011/01/core-audio-for-net/
License: MIT License
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();
Hi,
It would be useful to have this published as a NuGet package. Let me know if you want help setting up GitHub actions to auto-publish the NuGet package for every commit or something.
Hi,
This csproj has some nuget related information but no license info (and other nice to have properties).
it is good to add it so it will be shown in Visual Studio nuget management.
example:
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryType>git</RepositoryType>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
Hello,
Could you help me out?
I'm using these commands to be able to get the audio sessions for the active device
MMDeviceEnumerator deviceEnumerator = new MMDeviceEnumerator();
MMDevice device = deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
SessionCollection sessions = device.AudioSessionManager2.Sessions;
foreach (AudioSessionControl2 session in sessions)
{
Console.WriteLine($"Session Identifier: {session.GetSessionIdentifier}");
Console.WriteLine($"Session Name: {session.DisplayName}");
Console.WriteLine($"Session IconPath: {session.IconPath}");
Console.WriteLine("===================");
}
When the active device is my USB Headset then the DisplayName and IconPath are filled.
Session Identifier: {0.0.0.00000000}.{e4aa5a18-bf9b-470f-b5e2-f88a9d5c5998}|#%b{A9EF3FD9-4240-455E-A4D5-F2B3301887B2}
Session Name: @%SystemRoot%\System32\AudioSrv.Dll,-202
Session IconPath: @%SystemRoot%\System32\AudioSrv.Dll,-203
===================
Session Identifier: {0.0.0.00000000}.{e4aa5a18-bf9b-470f-b5e2-f88a9d5c5998}|\Device\HarddiskVolume4\Program Files\Mozilla Firefox\firefox.exe%b{00000000-0000-0000-0000-000000000000}
Session Name: Mozilla Firefox
Session IconPath: C:\Program Files\Mozilla Firefox\firefox.exe
===================
Session Identifier: {0.0.0.00000000}.{e4aa5a18-bf9b-470f-b5e2-f88a9d5c5998}|\Device\HarddiskVolume4\Program Files\VideoLAN\VLC\vlc.exe%b{4533F59D-59EE-00C6-ADB2-C68B501A6655}
Session Name: VLC media player
Session IconPath:
===================
But if I select my speakers as output then the DisplayName and IconPath are empty for some of the items.
Session Identifier: {0.0.0.00000000}.{e13e75ff-84a0-4dd6-bed0-ad11a8c3887b}|\Device\HarddiskVolume4\Program Files\VideoLAN\VLC\vlc.exe%b{4533F59D-59EE-00C6-ADB2-C68B501A6655}
Session Name: VLC media player
Session IconPath:
===================
Session Identifier: {0.0.0.00000000}.{e13e75ff-84a0-4dd6-bed0-ad11a8c3887b}|\Device\HarddiskVolume4\Program Files\Mozilla Firefox\firefox.exe%b{00000000-0000-0000-0000-000000000000}
Session Name:
Session IconPath:
===================
Session Identifier: {0.0.0.00000000}.{e13e75ff-84a0-4dd6-bed0-ad11a8c3887b}|#%b{A9EF3FD9-4240-455E-A4D5-F2B3301887B2}
Session Name: @%SystemRoot%\System32\AudioSrv.Dll,-202
Session IconPath: @%SystemRoot%\System32\AudioSrv.Dll,-203
===================
Maybe this is a specific problem for Mozilla Firefox? Why does the Name and IconPath of Mozilla disappear when I switch output devices?
With regards, Ben
Enumerating Active, Disabled and Unplugged devices work just fine but enumerating Not present devices produces this exception (which means also DEVICE_STATEMASK_ALL will cause this exception) :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CoreAudio;
using static System.Console;
namespace MyTests
{
class Program
{
static void Main(string[] args)
{
MMDeviceEnumerator DevEnum = new MMDeviceEnumerator();
{
MMDeviceCollection Devices = DevEnum.EnumerateAudioEndPoints(EDataFlow.eAll, DEVICE_STATE.DEVICE_STATE_NOTPRESENT);
List<MMDevice> DevicesList = Devices.ToList();
foreach (MMDevice Item in DevicesList)
WriteLine(Item.DeviceFriendlyName);
ReadLine();
}
}
}
}
System.Runtime.InteropServices.COMException: 'Exception from HRESULT: 0xE000020B'
Exception details:
System.Runtime.InteropServices.COMException
HResult=0xE000020B
Message=Exception from HRESULT: 0xE000020B
Source=mscorlib
StackTrace:
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at CoreAudio.PropertyStore.get_Item(PROPERTYKEY testKey)
at CoreAudio.MMDevice.get_DeviceFriendlyName()
at MyTests.Program.Main(String[] args) in A:\source\repos\ClassLibrary1-.NET_Framework\Class1.cs:line 21
Some of my users are reporting (radj307/volume-control#86, radj307/volume-control#128) a weird and difficult to reproduce problem where SimpleAudioVolume
doesn't affect some specific applications:
It works fine for everything else.
I believe the issue is due to this library because it affects Volume Control (which uses this library), but not EarTrumpet (which has its own C# implementation of the Core Audio APIs) or vccli
(which uses the C++ APIs directly).
I wrote a simple C# clone of vccli
using this library and had an affected user test it & that didn't work either (radj307/volume-control#160).
Especially weird is that if the affected user uses vccli
to change the volume/mute at least once, the problem stops happening entirely.
Please let me know if there's anything else I can provide or do that might help.
I am using playbackStoped Event to use as Rewind. To make clear, If audio file is complete then I wan to call palybackStopped event and inside it, I want to call outputDevice.Play() method. OutputDevice is object of WaveOutEvent. After calling outputDevice.Play() , playbackStoped fired multiple times and not able to again play.
Had this exception when using the Nuget package.
Downloaded the code from GitHub and ran the Forms.Framework sample and got the same error. Am running it in Framework 4.8.1, but read through another issue that just changing the targeting framework should work. Should mention that changing the selected device is working just fine. Only the volume has this problem.
System.AccessViolationException
HResult=0x80004003
Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Source=
StackTrace:
Stack trace
at CoreAudio.Interfaces.IAudioEndpointVolume.SetMasterVolumeLevelScalar(Single fLevel, Guid pguidEventContext)
at CoreAudio.AudioEndpointVolume.set_MasterVolumeLevelScalar(Single value) in C:\Code\ExampleCode\CoreAudio-master\CoreAudio\AudioEndpointVolume.cs:line 60
at CoreAudioForms.Framework.Sample.FormMain.Master_Scroll(Object sender, EventArgs e) in C:\Code\ExampleCode\CoreAudio-master\samples\CoreAudioForms.Framework.Sample\FormMain.cs:line 35
at System.Windows.Forms.TrackBar.OnScroll(EventArgs e)
at System.Windows.Forms.TrackBar.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.SendMessage(HandleRef hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at System.Windows.Forms.Control.SendMessage(Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.Control.ReflectMessageInternal(IntPtr hWnd, Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WmVScroll(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ContainerControl.WndProc(Message& m)
at System.Windows.Forms.Form.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
at System.Windows.Forms.Control.DefWndProc(Message& m)
at System.Windows.Forms.Control.WmMouseMove(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.TrackBar.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at CoreAudioForms.Framework.Sample.Program.Main() in C:\Code\ExampleCode\CoreAudio-master\samples\CoreAudioForms.Framework.Sample\Program.cs:line 13
Connector? connector;
...
public Connector? Connector
{
get
{
if (connector == null)
{
var pUnk = Marshal.GetIUnknownForObject(part);
var res = Marshal.QueryInterface(pUnk, ref RefIId.IIdIConnector, out var ppv);
if (ppv != IntPtr.Zero)
connector = new Connector((IConnector)Marshal.GetObjectForIUnknown(ppv));
else
connector = null;
}
return connector;
}
}
All connectors are parts, but not all parts are connectors, so I'm not sure this way, but anyway, I needed a way to make the sample code in the official document work.
void GetAudioVolumeLevel(AudioVolumeLevel? audioVolumeLevel) {
//private AudioVolumeLevel? GetAudioVolumeLevel() {
// return audioVolumeLevel;
//}
void GetAudioVolumeLevel() {
part.Activate(CLSCTX.ALL, ref RefIId.IIdIAudioVolumeLevel, out var result);
if(result is IAudioVolumeLevel level) {
audioVolumeLevel = new AudioVolumeLevel(level);
_AudioVolumeLevelChangeNotification = new ControlChangeNotify(this);
Marshal.ThrowExceptionForHR(part.RegisterControlChangeCallback(ref RefIId.IIdIAudioVolumeLevel,
_AudioVolumeLevelChangeNotification));
}
}
Hopefully these will be fixed in the next version.
Thanks
It's should work...
Hey @morphx666,
Maybe I've looked over it, but is there a way to get the peak volume of the default mic (input device)?
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?
Event never seems to properly fire, I've tried getting Count of sessions as according to MS documentation notifications are suppressed until user gets sessions to prevent race conditions but it doesn't seem to help either.
Code reproducing issue:
using CoreAudio;
var deviceEnumerator = new MMDeviceEnumerator(Guid.NewGuid());
var sessionManager = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Communications).AudioSessionManager2;
sessionManager.RefreshSessions();
var test = sessionManager.Sessions.Count;
foreach (var session in sessionManager.Sessions)
{
Console.WriteLine($"Existing session: {session.DisplayName}:{session.SessionIdentifier}");
}
sessionManager.OnSessionCreated += (sender, session) =>
{
Console.WriteLine("Session created");
};
await Task.Delay(TimeSpan.FromMinutes(1));
Hi
I'm probably just being an idiot, but I can't see a way to detect when a device is disconnected or changed.
For example, I would like to be able to get an event/notification when I turn on my Bluetooth headset. Normally I use my USB headset, but when I turn on my BT set then Windows will automatically switch to the BT set. I would like to be able to detect when the USB set is disconnected so I can react to it.
From what I can see on learn.microsoft.com, it should be possible with device events, but I can't see how to use these in this library.
https://learn.microsoft.com/en-us/windows/win32/coreaudio/device-events
Hi,
There is an error when running the netcore CoreAudioForms.Core.Sample
System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'
in:
v = (int)(device.AudioMeterInformation.PeakValues[1] * 100);
Hello,
I've been experimenting with various "CoreAudio" wrapper libraries and it looks like most of them, including this one, appear to be leaking memory - slowly but constantly (or it's me using the library wrong, not excluding that :D)
I have a memory dump created at the end of the tests however github allows attachments only up to 25MB - compressed dump is 30MB+. I can deliver it via preferred method if required.
Test project is a clean new one with CoreAudio installed via nuget.
Code used for testing (put directly into main function, please ignore the var name typo):
var enumerator = new MMDeviceEnumerator(Guid.Empty);
while (true)
{
foreach (var device in enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active))
{
var termpVar = device.DeviceFriendlyName;
device.Dispose();
}
}
@uxsoft, would it be possible to set up the dotnet.yml
so that we don't push a new NuGet package every time there's a new commit? Maybe set it up so that it needs to be run manually or perhaps when there's a new release?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.