Joh*_*ens 8 .net c# vb.net audio visual-studio
我正在设计一个应用程序来为我提供一个游戏.不过,我有去的办法是听声音或钉在噪音水平运行所需的宏组合之前.我想要监听的音频是当前PC的音频输出,而不是麦克风或外部设备.
这个主题看起来的要改变的教程或信息模糊,但是从我的知识,我发现了一个帖子在这里,说明使用BuckSoft.DirectSound项目?
所以根据我发现的信息,我假设你做了类似下面的事情?
If AnalogSignalMeter1.LeftLevel > 0 Or AnalogSignalMeter1.RightLevel > 0 Then
' Do Something
End If
Run Code Online (Sandbox Code Playgroud)
从个人的角度来看,我很乐意帮助澄清公众的解决方案,因为这个主题没有得到很好的记录和避免.我对所有解决方案或建议持开放态度,但是,我的重点是vb.net,如果需要,我会考虑使用C#.
我见过的另一个选项是CoreAudio API.我已经看到这个API在多个帖子上用于提取当前声级的能力,但是我还没有看到用于读取当前Master VU meter and fader数据/级别的示例.
Private Function GetVol() As Integer 'Function to read current volume setting
Dim MasterMinimum As Integer = 0
Dim DevEnum As New MMDeviceEnumerator()
Dim device As MMDevice = DevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia)
Dim Vol As Integer = 0
With device.AudioEndpointVolume
Vol = CInt(.MasterVolumeLevelScalar * 100)
If Vol < MasterMinimum Then
Vol = MasterMinimum / 100.0F
End If
End With
Return Vol
End Function
Run Code Online (Sandbox Code Playgroud)
我想收集的数据:
如下所示,sound level %如果VU级别从0开始飙升,我不打算收集物理,而是寻找运行动作.这意味着如果我播放视频或声音文件,应用程序将听到来自当前工作站的声音并执行所需的操作.
贝娄将是我计划如何使用或收集我的预期数据的粗略示例.在vb.net中使用计时器我可以在声明中一致地查找"VUSoundLevels"(不是真实语句)中的更改并在发生更改/输入时运行脚本.
Private Sub Timer1_Tick()
If VUSoundLevels > 0 Then
' Run Code & Exit Loop
End IF
End Sub
Run Code Online (Sandbox Code Playgroud)
这是一个使用Windows Core Audio Library的C#解决方案
此API具有Session(与您在Volume Mixer中看到的内容相对应的概念).所以,我提供了一个AudioSession包装器类,它可以为您提供有关Windows中所有当前会话的各种信息,其中包括会话进程ID以及可能的名称,显示名称,图标等详细信息.
该类还有一个GetChannelsPeakValues()方法,它使用IAudioMeterInformation接口获取每个音频通道的峰值.
这是一个C#控制台应用程序示例(但AudioSession类支持任何UI技术),当运行时,将显示chrome浏览器实例的每个通道的峰值(从浏览器运行一些视频或声音,数字应该开始移动).如果您没有镀铬,请使用您选择的其他工艺.
class Program
{
static void Main(string[] args)
{
// Here, I'm just monitoring chrome, one of the possible sessions.
// If you want the whole speakers peak values, use the AudioSession.GetSpeakersChannelsPeakValues() method
foreach (var session in AudioSession.EnumerateAll())
{
if (session.Process?.ProcessName == "chrome")
{
do
{
var values = session.GetChannelsPeakValues();
if (values.Length == 0)
continue;
Console.WriteLine(string.Join(" ", values.Select(v => v.ToString("00%"))));
}
while (true);
}
session.Dispose();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是支持C#代码:
public class AudioSession : IDisposable
{
private readonly Lazy<Icon> _icon;
private readonly Lazy<Process> _process;
private readonly IAudioSessionControl2 _control;
public AudioSession(IAudioSessionControl2 control)
{
_control = control;
control.GetState(out var state);
State = state;
control.GetGroupingParam(out var guid);
GroupingParam = guid;
IconPath = GetString(control.GetIconPath);
DisplayName = GetString(control.GetDisplayName);
_icon = new Lazy<Icon>(GetIcon, true);
_process = new Lazy<Process>(() => Process.GetProcessById(ProcessId), true);
Id = GetString(control.GetSessionIdentifier);
InstanceId = GetString(control.GetSessionInstanceIdentifier);
control.GetProcessId(out var pid);
ProcessId = pid;
IsSystemSounds = control.IsSystemSoundsSession() == 0;
}
public AudioSessionState State { get; }
public string IconPath { get; }
public string DisplayName { get; }
public Guid GroupingParam { get; }
public Icon Icon => _icon.Value;
public string Id { get; }
public string InstanceId { get; }
public int ProcessId { get; }
public Process Process => _process.Value;
public bool IsSystemSounds { get; }
public float[] GetChannelsPeakValues()
{
var meter = (IAudioMeterInformation)_control;
meter.GetMeteringChannelCount(out var channelCount);
var values = new float[channelCount];
meter.GetChannelsPeakValues(channelCount, values);
return values;
}
private delegate int GetStringFn(out IntPtr ptr);
private static string GetString(GetStringFn fn)
{
fn(out var ptr);
if (ptr == IntPtr.Zero)
return null;
try
{
var s = Marshal.PtrToStringUni(ptr);
if (!string.IsNullOrWhiteSpace(s) && s.StartsWith("@"))
{
var sb = new StringBuilder(256);
if (SHLoadIndirectString(s, sb, sb.Capacity, IntPtr.Zero) == 0)
{
s = sb.ToString();
}
}
return s;
}
finally
{
Marshal.FreeCoTaskMem(ptr);
}
}
private Icon GetIcon()
{
if (string.IsNullOrWhiteSpace(IconPath))
return null;
var index = ParseIconLocationPath(IconPath, out var path);
// note this may only work if the OS bitness is the same as this process bitness
var hIcon = ExtractIcon(IntPtr.Zero, path, index);
return hIcon == IntPtr.Zero ? null : Icon.FromHandle(hIcon);
}
public override string ToString() => DisplayName;
public void Dispose() => _icon.Value?.Dispose();
public static float[] GetSpeakersChannelsPeakValues()
{
// get the speakers (1st render + multimedia) device
var deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out IMMDevice speakers);
if (speakers == null)
return new float[0];
// get meter information
speakers.Activate(typeof(IAudioMeterInformation).GUID, 0, IntPtr.Zero, out object o);
var meter = (IAudioMeterInformation)o;
if (meter == null)
return new float[0];
meter.GetMeteringChannelCount(out var count);
if (count == 0)
return new float[0];
var values = new float[count];
meter.GetChannelsPeakValues(count, values);
return values;
}
public static IEnumerable<AudioSession> EnumerateAll()
{
// get the speakers (1st render + multimedia) device
var deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out IMMDevice speakers);
if (speakers == null)
yield break;
// activate the session manager, we need the enumerator
speakers.Activate(typeof(IAudioSessionManager2).GUID, 0, IntPtr.Zero, out object o);
var sessionManager = (IAudioSessionManager2)o;
if (sessionManager == null)
yield break;
// enumerate sessions for on this device
sessionManager.GetSessionEnumerator(out IAudioSessionEnumerator sessionEnumerator);
sessionEnumerator.GetCount(out int count);
for (int i = 0; i < count; i++)
{
sessionEnumerator.GetSession(i, out var sessionControl);
if (sessionControl != null)
{
var meter = sessionControl as IAudioMeterInformation;
yield return new AudioSession(sessionControl);
}
}
}
[DllImport("shlwapi", CharSet = CharSet.Unicode)]
private extern static int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved);
[DllImport("shlwapi", CharSet = CharSet.Unicode)]
private static extern int PathParseIconLocation(string pszIconFile);
[DllImport("shell32", CharSet = CharSet.Unicode)]
private static extern IntPtr ExtractIcon(IntPtr ptr, string pszExeFileName, int nIconIndex);
private static int ParseIconLocationPath(string location, out string path)
{
if (location == null)
throw new ArgumentNullException(nameof(location));
path = string.Copy(location);
int index = PathParseIconLocation(path);
int pos = path.LastIndexOf('\0');
if (pos >= 0)
{
path = path.Substring(0, pos);
}
if (path.StartsWith("@"))
{
path = path.Substring(1);
}
return index;
}
}
[ComImport]
[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
public class MMDeviceEnumerator
{
}
public enum EDataFlow
{
eRender,
eCapture,
eAll,
EDataFlow_enum_count
}
public enum ERole
{
eConsole,
eMultimedia,
eCommunications,
ERole_enum_count
}
[Guid("a95664d2-9614-4f35-a746-de8db63617e6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public partial interface IMMDeviceEnumerator
{
[PreserveSig]
int EnumAudioEndpoints(EDataFlow dataFlow, uint dwStateMask, out IntPtr ppDevices);
[PreserveSig]
int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice ppEndpoint);
[PreserveSig]
int GetDevice([MarshalAs(UnmanagedType.LPWStr)] string pwstrId, out IMMDevice ppDevice);
[PreserveSig]
int RegisterEndpointNotificationCallback(IntPtr pClient);
[PreserveSig]
int UnregisterEndpointNotificationCallback(IntPtr pClient);
}
[Guid("d666063f-1587-4e43-81f1-b948e807363f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public partial interface IMMDevice
{
[PreserveSig]
int Activate([MarshalAs(UnmanagedType.LPStruct)] Guid iid, uint dwClsCtx, [In, Out] IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
[PreserveSig]
int OpenPropertyStore(uint stgmAccess, out IntPtr ppProperties);
[PreserveSig]
int GetId(out IntPtr ppstrId);
[PreserveSig]
int GetState(out uint pdwState);
}
[Guid("C02216F6-8C67-4B5B-9D00-D008E73E0064"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAudioMeterInformation
{
[PreserveSig]
int GetPeakValue(out float pfPeak);
[PreserveSig]
int GetMeteringChannelCount(out int pnChannelCount);
[PreserveSig]
int GetChannelsPeakValues(int u32ChannelCount, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] float[] afPeakValues);
[PreserveSig]
int QueryHardwareSupport(out int pdwHardwareSupportMask);
}
[Guid("77aa99a0-1bd6-484f-8bc7-2c654c9a9b6f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public partial interface IAudioSessionManager2
{
// IAudioSessionManager
[PreserveSig]
int GetAudioSessionControl(IntPtr AudioSessionGuid, uint StreamFlags, out IAudioSessionControl2 SessionControl);
[PreserveSig]
int GetSimpleAudioVolume(IntPtr AudioSessionGuid, uint StreamFlags, out IntPtr AudioVolume);
// IAudioSessionManager2
[PreserveSig]
int GetSessionEnumerator(out IAudioSessionEnumerator SessionEnum);
[PreserveSig]
int RegisterSessionNotification(IntPtr SessionNotification);
[PreserveSig]
int UnregisterSessionNotification(IntPtr SessionNotification);
[PreserveSig]
int RegisterDuckNotification([MarshalAs(UnmanagedType.LPWStr)] string sessionID, IntPtr duckNotification);
[PreserveSig]
int UnregisterDuckNotification(IntPtr duckNotification);
}
[Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAudioSessionEnumerator
{
[PreserveSig]
int GetCount(out int SessionCount);
[PreserveSig]
int GetSession(int SessionCount, out IAudioSessionControl2 Session);
}
public enum AudioSessionState
{
AudioSessionStateInactive = 0,
AudioSessionStateActive = 1,
AudioSessionStateExpired = 2,
}
[Guid("bfb7ff88-7239-4fc9-8fa2-07c950be9c6d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public partial interface IAudioSessionControl2
{
// IAudioSessionControl
[PreserveSig]
int GetState(out AudioSessionState pRetVal);
[PreserveSig]
int GetDisplayName(out IntPtr pRetVal);
[PreserveSig]
int SetDisplayName([MarshalAs(UnmanagedType.LPWStr)] string Value, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext);
[PreserveSig]
int GetIconPath(out IntPtr pRetVal);
[PreserveSig]
int SetIconPath([MarshalAs(UnmanagedType.LPWStr)] string Value, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext);
[PreserveSig]
int GetGroupingParam(out Guid pRetVal);
[PreserveSig]
int SetGroupingParam([MarshalAs(UnmanagedType.LPStruct)] Guid Override, [MarshalAs(UnmanagedType.LPStruct)] Guid EventContext);
[PreserveSig]
int RegisterAudioSessionNotification(IntPtr NewNotifications);
[PreserveSig]
int UnregisterAudioSessionNotification(IntPtr NewNotifications);
// IAudioSessionControl2
[PreserveSig]
int GetSessionIdentifier(out IntPtr pRetVal);
[PreserveSig]
int GetSessionInstanceIdentifier(out IntPtr pRetVal);
[PreserveSig]
int GetProcessId(out int pRetVal);
[PreserveSig]
int IsSystemSoundsSession();
[PreserveSig]
int SetDuckingPreference(bool optOut);
}
Run Code Online (Sandbox Code Playgroud)
对此(我的解决方案)工作的关键是在此表单运行时显示您的VOLUME MIXER.
这可能看起来有点像,但它可能工作,并且比尝试利用声卡信息更容易.我建议使用颜色识别和一些好的win win32 api.首先,您需要像我在这里一样展示您的音量混音器(或声音设置)(我将在本示例中使用音量混音器并专注于Amazon Music控件):
然后,使用以下代码(模块和表单):
Imports System.Runtime.InteropServices
Module Module1
<DllImport("user32.dll")>
Private Function GetDC(ByVal hwnd As IntPtr) As IntPtr
End Function
<DllImport("user32.dll")>
Private Function ReleaseDC(ByVal hwnd As IntPtr, ByVal hdc As IntPtr) As Int32
End Function
<DllImport("gdi32.dll")>
Private Function GetPixel(ByVal hdc As IntPtr, ByVal nXPos As Integer, ByVal nYPos As Integer) As UInteger
End Function
Public Function GetPixelColor(ByVal x As Integer, ByVal y As Integer) As System.Drawing.Color
Dim hdc As IntPtr = GetDC(IntPtr.Zero)
Dim pixel As UInteger = GetPixel(hdc, x, y)
Dim clr As Color
ReleaseDC(IntPtr.Zero, hdc)
clr = Color.FromArgb(255, (pixel And &HFF), (pixel And &HFF00) >> 8,
(pixel And &HFF0000) >> 16)
Return clr
End Function
End Module
Run Code Online (Sandbox Code Playgroud)
这是表单代码(简单):
Public Class Form1
Const c_blnDebug as Boolean = True
'Make false to run your program with settings
'Make true to get location and colors
Const c_intRedThresh As Integer = 90
'Threshold color must be less than or equal to this
Const c_intGreenThresh As Integer = 170
'Threshold color must be greater than or equal to this
Const c_intBlueThresh As Integer = 90
'Threshold color must be less than or equal to this
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim ptArr() As Point
Dim intI As Integer
Dim clrTemp As Color
Dim intRed As Integer
Dim intGreen As Integer
Dim intBlue As Integer
'Set the pixel locations to watch if NOT DEBUGGING
ReDim ptArr(0 To 2)
'at source level
ptArr(0).X = 1762
ptArr(0).Y = 870
'-1 pixel
ptArr(1).X = 1762
ptArr(1).Y = 869
'+1 pixel
ptArr(2).X = 1762
ptArr(2).Y = 871
If c_blnDebug Then
Debug.Print(GetPixelColor(MousePosition.X, MousePosition.Y).ToString & vbCrLf &
"X: " & MousePosition.X & vbCrLf & "Y: " & MousePosition.Y)
Else
For intI = 0 To 2
clrTemp = GetPixelColor(ptArr(intI).X, ptArr(intI).Y)
intRed = clrTemp.R
intGreen = clrTemp.G
intBlue = clrTemp.B
If ((intRed < c_intRedThresh) And
(intGreen >= c_intGreenThresh) And
(intBlue <= c_intBlueThresh)) Then
'Sound Spike do your business
Debug.Print("Found Spike @ " & Now)
Exit For
End If
Next intI
End If
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If c_blnDebug Then
Timer1.Interval = 1000 '1 second
Else
Timer1.Interval = 250 '0.25 seconds
End If
Timer1.Enabled = True
End Sub
End Class
Run Code Online (Sandbox Code Playgroud)
然后可以获得您需要观察的鼠标位置以及我的debug.print结果中显示的颜色(粗略):
Color [A=255, R=51, G=190, B=51]
X: 1762
Y: 870
Color [A=255, R=51, G=190, B=51]
X: 1762
Y: 870
Color [A=255, R=51, G=191, B=51]
X: 1762
Y: 870
Color [A=255, R=51, G=188, B=51]
X: 1762
Y: 870
Color [A=255, R=51, G=195, B=51]
X: 1762
Y: 870
Color [A=255, R=232, G=17, B=35]
X: 1491
Y: 646
Run Code Online (Sandbox Code Playgroud)
因此,我选择的是观察鼠标位置坐标+/-(1)像素.对于(红色<90),(绿色> 170)和(蓝色<90)作为阈值.
一旦完成,我可以简单地将计时器设置为每250毫秒并检查颜色.这就像一个魅力,非常简单!
我希望这有帮助,我得到以下结果!
Found Spike @ 2/5/2019 10:16:14 AM
Found Spike @ 2/5/2019 10:16:17 AM
Found Spike @ 2/5/2019 10:16:17 AM
Found Spike @ 2/5/2019 10:16:18 AM
Found Spike @ 2/5/2019 10:16:19 AM
Found Spike @ 2/5/2019 10:16:19 AM
Found Spike @ 2/5/2019 10:16:21 AM
Found Spike @ 2/5/2019 10:16:21 AM
Found Spike @ 2/5/2019 10:16:21 AM
Found Spike @ 2/5/2019 10:16:21 AM
Found Spike @ 2/5/2019 10:16:23 AM
Run Code Online (Sandbox Code Playgroud)
而且,这是最终产品的屏幕截图(上面的代码并不反映这一点,只有裸骨的方法)(或者至少在我决定添加更多产品之前):