在vb.net中基于声音VU级别运行脚本

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)

Sim*_*ier 7

这是一个使用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)


IAm*_*000 5

对此(我的解决方案)工作的关键是在此表单运行时显示您的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)

而且,这是最终产品的屏幕截图(上面的代码并不反映这一点,只有裸骨的方法)(或者至少在我决定添加更多产品之前):

最终产品