mirror of https://github.com/naudio/NAudio.git
Browse Source
Implemented classes for some missing interfaces needed to navigate device topology
pull/1016/head
Implemented classes for some missing interfaces needed to navigate device topology
pull/1016/head

19 changed files with 893 additions and 19 deletions
-
31NAudio.Wasapi/CoreAudioApi/AudioMute.cs
-
58NAudio.Wasapi/CoreAudioApi/AudioVolumeLevel.cs
-
8NAudio.Wasapi/CoreAudioApi/Connector.cs
-
21NAudio.Wasapi/CoreAudioApi/Interfaces/IAudioAutoGainControl.cs
-
22NAudio.Wasapi/CoreAudioApi/Interfaces/IAudioMute.cs
-
15NAudio.Wasapi/CoreAudioApi/Interfaces/IAudioVolumeLevel.cs
-
18NAudio.Wasapi/CoreAudioApi/Interfaces/IControlChangeNotify.cs
-
14NAudio.Wasapi/CoreAudioApi/Interfaces/IControlInterface.cs
-
17NAudio.Wasapi/CoreAudioApi/Interfaces/IKsJackDescription.cs
-
83NAudio.Wasapi/CoreAudioApi/Interfaces/IPart.cs
-
20NAudio.Wasapi/CoreAudioApi/Interfaces/IPerChannelDbLevel.cs
-
17NAudio.Wasapi/CoreAudioApi/Interfaces/PartType.cs
-
35NAudio.Wasapi/CoreAudioApi/KsJackDescription.cs
-
150NAudio.Wasapi/CoreAudioApi/Part.cs
-
45NAudio.Wasapi/CoreAudioApi/PartsList.cs
-
87NAudioDemo/DeviceTopology/DeviceTopologyPanel.Designer.cs
-
189NAudioDemo/DeviceTopology/DeviceTopologyPanel.cs
-
60NAudioDemo/DeviceTopology/DeviceTopologyPanel.resx
-
22NAudioDemo/DeviceTopology/DeviceTopologyPlugin.cs
@ -0,0 +1,31 @@ |
|||
using NAudio.CoreAudioApi.Interfaces; |
|||
using NAudio.Wasapi.CoreAudioApi.Interfaces; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi |
|||
{ |
|||
public class AudioMute |
|||
{ |
|||
private IAudioMute audioMuteInterface; |
|||
internal AudioMute(IAudioMute audioMute) |
|||
{ |
|||
audioMuteInterface = audioMute; |
|||
} |
|||
|
|||
public bool IsMuted |
|||
{ |
|||
get |
|||
{ |
|||
audioMuteInterface.GetMute(out var result); |
|||
return result; |
|||
} |
|||
set |
|||
{ |
|||
var guid = Guid.Empty; |
|||
audioMuteInterface.SetMute(value, guid); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,58 @@ |
|||
using NAudio.CoreAudioApi.Interfaces; |
|||
using NAudio.Wasapi.CoreAudioApi.Interfaces; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.Wasapi.CoreAudioApi |
|||
{ |
|||
public class AudioVolumeLevel |
|||
{ |
|||
private readonly IAudioVolumeLevel audioVolumeLevelInterface; |
|||
private bool isLevelRangeRead = false; |
|||
private float minLevelDb, maxLevelDb, stepping; |
|||
|
|||
internal AudioVolumeLevel(IAudioVolumeLevel audioVolumeLevel) |
|||
{ |
|||
audioVolumeLevelInterface = audioVolumeLevel; |
|||
} |
|||
|
|||
public uint ChannelCount |
|||
{ |
|||
get |
|||
{ |
|||
audioVolumeLevelInterface.GetChannelCount(out uint result); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public void GetLevelRange(uint channel, out float minLevelDb, out float maxLevelDb, out float stepping) |
|||
{ |
|||
audioVolumeLevelInterface.GetLevelRange(channel, out minLevelDb, out maxLevelDb, out stepping); |
|||
} |
|||
|
|||
public float GetLevel(uint channel) |
|||
{ |
|||
audioVolumeLevelInterface.GetLevel(channel, out float result); |
|||
return result; |
|||
} |
|||
|
|||
public void SetLevel(uint channel, float value) |
|||
{ |
|||
var guid = Guid.Empty; |
|||
audioVolumeLevelInterface.SetLevel(channel, value, ref guid); |
|||
} |
|||
|
|||
public void SetLevelUniform(float value) |
|||
{ |
|||
var guid = Guid.Empty; |
|||
audioVolumeLevelInterface.SetLevelUniform(value, ref guid); |
|||
} |
|||
|
|||
public void SetLevelAllChannel(float[] values, uint channels) |
|||
{ |
|||
var guid = Guid.Empty; |
|||
audioVolumeLevelInterface.SetLevelAllChannel(values, channels, ref guid); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi.Interfaces |
|||
{ |
|||
[Guid("85401FD4-6DE4-4b9d-9869-2D6753A82F3C"), |
|||
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), |
|||
ComImport] |
|||
internal interface IAudioAutoGainControl |
|||
{ |
|||
[PreserveSig] |
|||
int GetEnabled( |
|||
[Out, MarshalAs(UnmanagedType.Bool)] out bool enabled); |
|||
|
|||
[PreserveSig] |
|||
int SetEnabled( |
|||
[In, MarshalAs(UnmanagedType.Bool)] bool enabled); |
|||
} |
|||
} |
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi.Interfaces |
|||
{ |
|||
[Guid("DF45AEEA-B74A-4B6B-AFAD-2366B6AA012E"), |
|||
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), |
|||
ComImport] |
|||
internal interface IAudioMute |
|||
{ |
|||
[PreserveSig] |
|||
int GetMute( |
|||
[Out, MarshalAs(UnmanagedType.Bool)] out bool mute); |
|||
|
|||
[PreserveSig] |
|||
int SetMute( |
|||
[In, MarshalAs(UnmanagedType.Bool)] bool mute, |
|||
[In] ref Guid eventContext); |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi.Interfaces |
|||
{ |
|||
[Guid("7FB7B48F-531D-44A2-BCB3-5AD5A134B3DC"), |
|||
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), |
|||
ComImport] |
|||
internal interface IAudioVolumeLevel : IPerChannelDbLevel |
|||
{ |
|||
|
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.Wasapi.CoreAudioApi.Interfaces |
|||
{ |
|||
[Guid("9c2c4058-23f5-41de-877a-df3af236a09e"), |
|||
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), |
|||
ComImport] |
|||
interface IControlChangeNotify |
|||
{ |
|||
[PreserveSig] |
|||
int OnNotify( |
|||
[In] uint controlId, |
|||
[In] IntPtr context); |
|||
} |
|||
} |
@ -0,0 +1,14 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi.Interfaces |
|||
{ |
|||
[Guid("45d37c3f-5140-444a-ae24-400789f3cbf3"), |
|||
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), |
|||
ComImport] |
|||
public interface IControlInterface |
|||
{ |
|||
} |
|||
} |
@ -0,0 +1,17 @@ |
|||
using NAudio.Utils; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi.Interfaces |
|||
{ |
|||
[Guid("4509F757-2D46-4637-8E62-CE7DB944F57B"), |
|||
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), |
|||
ComImport] |
|||
internal interface IKsJackDescription |
|||
{ |
|||
int GetJackCount([Out] out uint jacks); |
|||
int GetJackDescription([In] uint jack, [Out, MarshalAs(UnmanagedType.LPWStr)] out string description); |
|||
}; |
|||
} |
@ -1,19 +1,64 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi.Interfaces |
|||
{ |
|||
/// <summary>
|
|||
/// Windows CoreAudio IPart interface
|
|||
/// Defined in devicetopology.h
|
|||
/// </summary>
|
|||
[Guid("AE2DE0E4-5BCA-4F2D-AA46-5D13F8FDB3A9"), |
|||
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), |
|||
ComImport] |
|||
internal interface IPart |
|||
{ |
|||
// Stub, Not implemented
|
|||
} |
|||
} |
|||
using NAudio.Wasapi.CoreAudioApi.Interfaces; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi.Interfaces |
|||
{ |
|||
/// <summary>
|
|||
/// Windows CoreAudio IPart interface
|
|||
/// Defined in devicetopology.h
|
|||
/// </summary>
|
|||
[Guid("AE2DE0E4-5BCA-4F2D-AA46-5D13F8FDB3A9"), |
|||
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), |
|||
ComImport] |
|||
internal interface IPart |
|||
{ |
|||
int GetName( |
|||
[Out, MarshalAs(UnmanagedType.LPWStr)] out string name); |
|||
|
|||
int GetLocalId( |
|||
[Out] out uint id); |
|||
|
|||
int GetGlobalId( |
|||
[Out, MarshalAs(UnmanagedType.LPWStr)] out string id); |
|||
|
|||
int GetPartType( |
|||
[Out] out PartTypeEnum partType); |
|||
|
|||
int GetSubType( |
|||
out Guid subType); |
|||
|
|||
int GetControlInterfaceCount( |
|||
[Out] out uint count); |
|||
|
|||
int GetControlInterface( |
|||
[In] uint index, |
|||
[Out, MarshalAs(UnmanagedType.IUnknown)] out IControlInterface controlInterface); |
|||
|
|||
[PreserveSig] |
|||
int EnumPartsIncoming( |
|||
[Out] out IPartsList parts); |
|||
|
|||
[PreserveSig] |
|||
int EnumPartsOutgoing( |
|||
[Out] out IPartsList parts); |
|||
|
|||
int GetTopologyObject( |
|||
[Out] out object topologyObject); |
|||
|
|||
[PreserveSig] |
|||
int Activate( |
|||
[In] ClsCtx dwClsContext, |
|||
[In] ref Guid refiid, |
|||
[MarshalAs(UnmanagedType.IUnknown)] out object interfacePointer); |
|||
|
|||
int RegisterControlChangeCallback( |
|||
[In] ref Guid refiid, |
|||
[In] IControlChangeNotify notify); |
|||
|
|||
int UnregisterControlChangeCallback( |
|||
[In] IControlChangeNotify notify); |
|||
} |
|||
} |
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi.Interfaces |
|||
{ |
|||
[Guid("7FB7B48F-531D-44A2-BCB3-5AD5A134B3DC"), |
|||
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), |
|||
ComImport] |
|||
internal interface IPerChannelDbLevel |
|||
{ |
|||
int GetChannelCount(out uint channels); |
|||
int GetLevelRange(uint channel, out float minLevelDb, out float maxLevelDb, out float stepping); |
|||
int GetLevel(uint channel, out float levelDb); |
|||
int SetLevel(uint channel, float levelDb, ref Guid eventGuidContext); |
|||
int SetLevelUniform(float levelDb, ref Guid eventGuidContext); |
|||
int SetLevelAllChannel(float[] levelsDb, uint channels, ref Guid eventGuidContext); |
|||
} |
|||
} |
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi.Interfaces |
|||
{ |
|||
public enum PartTypeEnum |
|||
{ |
|||
Connector = 0, |
|||
Subunit = 1, |
|||
HardwarePeriphery = 2, |
|||
SoftwareDriver = 3, |
|||
Splitter = 4, |
|||
Category = 5, |
|||
Other = 6 |
|||
} |
|||
} |
@ -0,0 +1,35 @@ |
|||
using NAudio.CoreAudioApi.Interfaces; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi |
|||
{ |
|||
public class KsJackDescription |
|||
{ |
|||
private readonly IKsJackDescription ksJackDescriptionInterface; |
|||
|
|||
internal KsJackDescription(IKsJackDescription ksJackDescription) |
|||
{ |
|||
ksJackDescriptionInterface = ksJackDescription; |
|||
} |
|||
|
|||
public uint Count |
|||
{ |
|||
get |
|||
{ |
|||
ksJackDescriptionInterface.GetJackCount(out var result); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public string this[uint index] |
|||
{ |
|||
get |
|||
{ |
|||
ksJackDescriptionInterface.GetJackDescription(index, out var result); |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,150 @@ |
|||
using NAudio.CoreAudioApi.Interfaces; |
|||
using NAudio.Wasapi.CoreAudioApi; |
|||
using NAudio.Wasapi.CoreAudioApi.Interfaces; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi |
|||
{ |
|||
public class Part |
|||
{ |
|||
private const int E_NOTFOUND = unchecked((int)0x80070490); |
|||
private readonly IPart partInterface; |
|||
private DeviceTopology deviceTopology; |
|||
private static Guid IID_IAudioVolumeLevel = new Guid("7FB7B48F-531D-44A2-BCB3-5AD5A134B3DC"); |
|||
private static Guid IID_IAudioMute = new Guid("DF45AEEA-B74A-4B6B-AFAD-2366B6AA012E"); |
|||
private static Guid IID_IAudioEndpointVolume = new Guid("5CDF2C82-841E-4546-9722-0CF74078229A"); |
|||
private static Guid IID_IKsJackDescription = new Guid("4509F757-2D46-4637-8E62-CE7DB944F57B"); |
|||
|
|||
internal Part(IPart part) |
|||
{ |
|||
partInterface = part; |
|||
} |
|||
|
|||
public string Name |
|||
{ |
|||
get |
|||
{ |
|||
partInterface.GetName(out var result); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public uint LocalId |
|||
{ |
|||
get |
|||
{ |
|||
partInterface.GetLocalId(out var result); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public string GlobalId |
|||
{ |
|||
get |
|||
{ |
|||
partInterface.GetGlobalId(out var result); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public PartTypeEnum PartType |
|||
{ |
|||
get |
|||
{ |
|||
partInterface.GetPartType(out var result); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public Guid GetSubType |
|||
{ |
|||
get |
|||
{ |
|||
partInterface.GetSubType(out var result); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public uint ControlInterfaceCount |
|||
{ |
|||
get |
|||
{ |
|||
partInterface.GetControlInterfaceCount(out var result); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public IControlInterface GetControlInterface(uint index) |
|||
{ |
|||
partInterface.GetControlInterface(index, out var result); |
|||
return result; |
|||
} |
|||
|
|||
public PartsList PartsIncoming |
|||
{ |
|||
get |
|||
{ |
|||
var hr = partInterface.EnumPartsIncoming(out var result); |
|||
return hr == 0 ? new PartsList(result) : hr == E_NOTFOUND ? new PartsList(null) : throw new COMException(nameof(IPart.EnumPartsIncoming), hr); |
|||
} |
|||
} |
|||
|
|||
public PartsList PartsOutgoing |
|||
{ |
|||
get |
|||
{ |
|||
var hr = partInterface.EnumPartsOutgoing(out var result); |
|||
return hr == 0 ? new PartsList(result) : hr == E_NOTFOUND ? new PartsList(null) : throw new COMException(nameof(IPart.EnumPartsOutgoing), hr); |
|||
} |
|||
} |
|||
|
|||
public DeviceTopology DeviceTopology |
|||
{ |
|||
get |
|||
{ |
|||
if (deviceTopology == null) |
|||
{ |
|||
GetDeviceTopology(); |
|||
} |
|||
|
|||
return deviceTopology; |
|||
} |
|||
} |
|||
|
|||
public AudioVolumeLevel AudioVolumeLevel |
|||
{ |
|||
get |
|||
{ |
|||
var hr = partInterface.Activate(ClsCtx.ALL, ref IID_IAudioVolumeLevel, out var result); |
|||
return hr == 0 ? new AudioVolumeLevel(result as IAudioVolumeLevel) : null; |
|||
} |
|||
} |
|||
|
|||
public AudioMute AudioMute |
|||
{ |
|||
get |
|||
{ |
|||
var hr = partInterface.Activate(ClsCtx.ALL, ref IID_IAudioMute, out var result); |
|||
return hr == 0 ? new AudioMute(result as IAudioMute) : null; |
|||
} |
|||
} |
|||
|
|||
public KsJackDescription JackDescription |
|||
{ |
|||
get |
|||
{ |
|||
var hr = partInterface.Activate(ClsCtx.ALL, ref IID_IKsJackDescription, out var result); |
|||
return hr == 0 ? new KsJackDescription(result as IKsJackDescription) : null; |
|||
} |
|||
} |
|||
|
|||
private void GetDeviceTopology() |
|||
{ |
|||
Marshal.ThrowExceptionForHR(partInterface.GetTopologyObject(out var result)); |
|||
deviceTopology = new DeviceTopology(result as IDeviceTopology); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,45 @@ |
|||
using NAudio.CoreAudioApi.Interfaces; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace NAudio.CoreAudioApi |
|||
{ |
|||
public class PartsList |
|||
{ |
|||
private IPartsList partsListInterface; |
|||
|
|||
internal PartsList(IPartsList partsList) |
|||
{ |
|||
partsListInterface = partsList; |
|||
} |
|||
|
|||
public uint Count |
|||
{ |
|||
get |
|||
{ |
|||
uint result = 0; |
|||
if (partsListInterface != null) |
|||
{ |
|||
partsListInterface.GetCount(out result); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public Part this[uint index] |
|||
{ |
|||
get |
|||
{ |
|||
if (partsListInterface == null) |
|||
{ |
|||
throw new IndexOutOfRangeException(); |
|||
} |
|||
|
|||
partsListInterface.GetPart(index, out IPart part); |
|||
return new Part(part); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,87 @@ |
|||
namespace NAudioDemo.DeviceTopology |
|||
{ |
|||
partial class DeviceTopologyPanel |
|||
{ |
|||
/// <summary>
|
|||
/// Required designer variable.
|
|||
/// </summary>
|
|||
private System.ComponentModel.IContainer components = null; |
|||
|
|||
/// <summary>
|
|||
/// Clean up any resources being used.
|
|||
/// </summary>
|
|||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
if (disposing && (components != null)) |
|||
{ |
|||
components.Dispose(); |
|||
} |
|||
base.Dispose(disposing); |
|||
} |
|||
|
|||
#region Component Designer generated code
|
|||
|
|||
/// <summary>
|
|||
/// Required method for Designer support - do not modify
|
|||
/// the contents of this method with the code editor.
|
|||
/// </summary>
|
|||
private void InitializeComponent() |
|||
{ |
|||
this.lblSelectDevice = new System.Windows.Forms.Label(); |
|||
this.cbDevices = new System.Windows.Forms.ComboBox(); |
|||
this.tbTopology = new System.Windows.Forms.TextBox(); |
|||
this.SuspendLayout(); |
|||
//
|
|||
// lblSelectDevice
|
|||
//
|
|||
this.lblSelectDevice.AutoSize = true; |
|||
this.lblSelectDevice.Dock = System.Windows.Forms.DockStyle.Top; |
|||
this.lblSelectDevice.Location = new System.Drawing.Point(0, 0); |
|||
this.lblSelectDevice.Name = "lblSelectDevice"; |
|||
this.lblSelectDevice.Size = new System.Drawing.Size(111, 15); |
|||
this.lblSelectDevice.TabIndex = 0; |
|||
this.lblSelectDevice.Text = "Select audio device:"; |
|||
//
|
|||
// cbDevices
|
|||
//
|
|||
this.cbDevices.Dock = System.Windows.Forms.DockStyle.Top; |
|||
this.cbDevices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; |
|||
this.cbDevices.FormattingEnabled = true; |
|||
this.cbDevices.Location = new System.Drawing.Point(0, 15); |
|||
this.cbDevices.Name = "cbDevices"; |
|||
this.cbDevices.Size = new System.Drawing.Size(1013, 23); |
|||
this.cbDevices.TabIndex = 1; |
|||
this.cbDevices.SelectedValueChanged += new System.EventHandler(this.cbDevices_SelectedValueChanged); |
|||
//
|
|||
// tbTopology
|
|||
//
|
|||
this.tbTopology.Dock = System.Windows.Forms.DockStyle.Fill; |
|||
this.tbTopology.Location = new System.Drawing.Point(0, 38); |
|||
this.tbTopology.Multiline = true; |
|||
this.tbTopology.Name = "tbTopology"; |
|||
this.tbTopology.ScrollBars = System.Windows.Forms.ScrollBars.Both; |
|||
this.tbTopology.Size = new System.Drawing.Size(1013, 483); |
|||
this.tbTopology.TabIndex = 2; |
|||
//
|
|||
// DeviceTopologyPanel
|
|||
//
|
|||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); |
|||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |
|||
this.Controls.Add(this.tbTopology); |
|||
this.Controls.Add(this.cbDevices); |
|||
this.Controls.Add(this.lblSelectDevice); |
|||
this.Name = "DeviceTopologyPanel"; |
|||
this.Size = new System.Drawing.Size(1013, 521); |
|||
this.ResumeLayout(false); |
|||
this.PerformLayout(); |
|||
|
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
private System.Windows.Forms.Label lblSelectDevice; |
|||
private System.Windows.Forms.ComboBox cbDevices; |
|||
private System.Windows.Forms.TextBox tbTopology; |
|||
} |
|||
} |
@ -0,0 +1,189 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Data; |
|||
using System.Diagnostics; |
|||
using System.Drawing; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using System.Windows.Forms; |
|||
|
|||
using NAudio.CoreAudioApi; |
|||
using NAudio.CoreAudioApi.Interfaces; |
|||
using NAudio.Wasapi.CoreAudioApi; |
|||
|
|||
namespace NAudioDemo.DeviceTopology |
|||
{ |
|||
public partial class DeviceTopologyPanel : UserControl |
|||
{ |
|||
class AudioVolumeWrapper |
|||
{ |
|||
public uint Channel { get; set; } |
|||
public AudioVolumeLevel AudioVolumeLevel { get; set; } |
|||
} |
|||
|
|||
public DeviceTopologyPanel() |
|||
{ |
|||
InitializeComponent(); |
|||
|
|||
foreach (var device in GetCaptureDevices()) |
|||
{ |
|||
cbDevices.Items.Add(device); |
|||
} |
|||
|
|||
foreach (var device in GetRenderDevices()) |
|||
{ |
|||
cbDevices.Items.Add(device); |
|||
} |
|||
|
|||
if (cbDevices.Items.Count > 0) |
|||
{ |
|||
cbDevices.SelectedIndex = 0; |
|||
} |
|||
} |
|||
|
|||
public IEnumerable<MMDevice> GetCaptureDevices() |
|||
{ |
|||
using (var enumerator = new MMDeviceEnumerator()) |
|||
{ |
|||
var audioEndPoints = enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active); |
|||
|
|||
foreach (var device in audioEndPoints) |
|||
{ |
|||
yield return device; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public IEnumerable<MMDevice> GetRenderDevices() |
|||
{ |
|||
using (var enumerator = new MMDeviceEnumerator()) |
|||
{ |
|||
var audioEndPoints = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active); |
|||
|
|||
foreach (var device in audioEndPoints) |
|||
{ |
|||
yield return device; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void cbDevices_SelectedValueChanged(object sender, EventArgs e) |
|||
{ |
|||
var device = (MMDevice)cbDevices.SelectedItem; |
|||
var topology = device.DeviceTopology; |
|||
var connector = topology.GetConnector(0); |
|||
|
|||
var deviceConnector = connector.ConnectedTo; |
|||
tbTopology.Text = ""; |
|||
_superMixBox = null; |
|||
WalkParts(deviceConnector.Part, device.DataFlow == DataFlow.Render, ""); |
|||
} |
|||
|
|||
private bool HasVolumeOrMuteParts(Part part) |
|||
{ |
|||
if (part.Name == "Volume" || part.Name == "Mute") |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
for (uint i = 0; i < part.PartsIncoming.Count; i++) |
|||
{ |
|||
if (HasVolumeOrMuteParts(part.PartsIncoming[i])) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private void WalkParts(Part part, bool isRenderDevice, string indent) |
|||
{ |
|||
var audioVolumeLevel = part.AudioVolumeLevel; |
|||
var audioMute = part.AudioMute; |
|||
var jackDescription = part.JackDescription; |
|||
|
|||
var s = "\r\n"; |
|||
if (audioVolumeLevel != null) |
|||
{ |
|||
s = $" (audio volume level)\r\n"; |
|||
for (uint i = 0; i < audioVolumeLevel.ChannelCount; i++) |
|||
{ |
|||
audioVolumeLevel.GetLevelRange(i, out var minLevelDb, out var maxLevelDb, out var stepping); |
|||
var volume = audioVolumeLevel.GetLevel(i); |
|||
s += $"{indent}Channel: {i}, Min {minLevelDb:0.##} dB, Max {maxLevelDb:0.##} dB, stepping {stepping:0.##} dB: {volume:0.##} dB\r\n"; |
|||
} |
|||
} |
|||
else if (audioMute != null) |
|||
{ |
|||
s = $" (audio mute. Muted: {audioMute.IsMuted})\r\n"; |
|||
} if (jackDescription != null) |
|||
{ |
|||
s = $" (Jacks: {jackDescription.Count})\r\n"; |
|||
} |
|||
|
|||
tbTopology.AppendText($"{indent}{part.PartType}: {part.Name}{s}"); |
|||
if (part.Name.ToLowerInvariant() == "supermix") |
|||
{ |
|||
if (isRenderDevice && HasVolumeOrMuteParts(part)) |
|||
{ |
|||
_superMixBox = new GroupBox |
|||
{ |
|||
Text = $"Supermix", |
|||
Dock = DockStyle.Top |
|||
}; |
|||
} |
|||
} |
|||
|
|||
var parts = isRenderDevice ? part.PartsIncoming : part.PartsOutgoing; |
|||
for (uint i = 0; i < parts.Count; i++) |
|||
{ |
|||
var outgoingPart = parts[i]; |
|||
WalkParts(outgoingPart, isRenderDevice, indent + " "); |
|||
} |
|||
} |
|||
|
|||
private void Tb_ValueChanged(object sender, EventArgs e) |
|||
{ |
|||
var trackBar = (TrackBar)sender; |
|||
var wrapper = (AudioVolumeWrapper)trackBar.Tag; |
|||
|
|||
wrapper.AudioVolumeLevel.GetLevelRange(wrapper.Channel, out var minLevelDb, out float maxLevelDb, out float _); |
|||
wrapper.AudioVolumeLevel.SetLevel(wrapper.Channel, (float)LinearToDecibels(trackBar.Value, minLevelDb, maxLevelDb)); |
|||
} |
|||
|
|||
private static double LinearToDecibels(double linearValue, double minDb, double maxDb) |
|||
{ |
|||
// Calculate the minimum and maximum values in linear scale
|
|||
double minLinearValue = Math.Pow(10, minDb / 10.0); |
|||
double maxLinearValue = Math.Pow(10, maxDb / 10.0); |
|||
|
|||
// Convert the percentage to a linear value within the given range
|
|||
double linearRangeValue = (linearValue / 100.0) * (maxLinearValue - minLinearValue) + minLinearValue; |
|||
|
|||
// Convert the linear value to decibels
|
|||
return 10.0 * Math.Log10(linearRangeValue); |
|||
} |
|||
|
|||
private static double DecibelsToLinear(double valueInDb, double minDb, double maxDb) |
|||
{ |
|||
// Convert the value from decibels to linear scale
|
|||
double linearValue = Math.Pow(10, valueInDb / 10.0); |
|||
|
|||
// Calculate the minimum and maximum values in linear scale
|
|||
double minLinearValue = Math.Pow(10, minDb / 10.0); |
|||
double maxLinearValue = Math.Pow(10, maxDb / 10.0); |
|||
|
|||
// Convert the linear value to a percentage within the given range
|
|||
double percentage = (linearValue - minLinearValue) / (maxLinearValue - minLinearValue) * 100.0; |
|||
|
|||
// Ensure the percentage is within the valid range from 0 to 100
|
|||
return Math.Max(Math.Min(percentage, 100.0), 0.0); |
|||
|
|||
} |
|||
|
|||
private GroupBox _superMixBox; |
|||
} |
|||
} |
@ -0,0 +1,60 @@ |
|||
<root> |
|||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> |
|||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> |
|||
<xsd:element name="root" msdata:IsDataSet="true"> |
|||
<xsd:complexType> |
|||
<xsd:choice maxOccurs="unbounded"> |
|||
<xsd:element name="metadata"> |
|||
<xsd:complexType> |
|||
<xsd:sequence> |
|||
<xsd:element name="value" type="xsd:string" minOccurs="0" /> |
|||
</xsd:sequence> |
|||
<xsd:attribute name="name" use="required" type="xsd:string" /> |
|||
<xsd:attribute name="type" type="xsd:string" /> |
|||
<xsd:attribute name="mimetype" type="xsd:string" /> |
|||
<xsd:attribute ref="xml:space" /> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
<xsd:element name="assembly"> |
|||
<xsd:complexType> |
|||
<xsd:attribute name="alias" type="xsd:string" /> |
|||
<xsd:attribute name="name" type="xsd:string" /> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
<xsd:element name="data"> |
|||
<xsd:complexType> |
|||
<xsd:sequence> |
|||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> |
|||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> |
|||
</xsd:sequence> |
|||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> |
|||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> |
|||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> |
|||
<xsd:attribute ref="xml:space" /> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
<xsd:element name="resheader"> |
|||
<xsd:complexType> |
|||
<xsd:sequence> |
|||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> |
|||
</xsd:sequence> |
|||
<xsd:attribute name="name" type="xsd:string" use="required" /> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
</xsd:choice> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
</xsd:schema> |
|||
<resheader name="resmimetype"> |
|||
<value>text/microsoft-resx</value> |
|||
</resheader> |
|||
<resheader name="version"> |
|||
<value>2.0</value> |
|||
</resheader> |
|||
<resheader name="reader"> |
|||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> |
|||
</resheader> |
|||
<resheader name="writer"> |
|||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> |
|||
</resheader> |
|||
</root> |
@ -0,0 +1,22 @@ |
|||
using NAudioDemo.FadeInOutDemo; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace NAudioDemo.DeviceTopology |
|||
{ |
|||
class DeviceTopologyPlugin : INAudioDemoPlugin |
|||
{ |
|||
public string Name |
|||
{ |
|||
get { return "Device Topology"; } |
|||
} |
|||
|
|||
public System.Windows.Forms.Control CreatePanel() |
|||
{ |
|||
return new DeviceTopologyPanel(); |
|||
} |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue