Запустить скрипт на основе уровня звука VU в vb.net - PullRequest
0 голосов
/ 03 февраля 2019

Я разрабатываю приложение для фарма для меня одной из моих игр.Однако подход, который я использую, заключается в том, чтобы прослушивать звук или пик в уровнях звука перед запуском желаемой комбинации макросов.Аудио, которое я просматриваю для мониторинга, является текущим аудиовыходом ПК, а не микрофоном или внешним устройством.

Эта тема может показаться весьма расплывчатой ​​в учебниках или информации, однако, насколько я знаю, я нашел сообщение здесь , в котором объясняется использование проекта BuckSoft.DirectSound ?

Итак, основываясь на найденной мной информации, я предполагаю, что вы делаете что-то вроде следующего?

If AnalogSignalMeter1.LeftLevel > 0 Or AnalogSignalMeter1.RightLevel > 0 Then

    ' Do Something

End If

С личной точки зрения, я хотел бы помочь прояснить решение для общественности, так как эта тема плохо документирована и ее избегают.Я открыт для всех решений или предложений, однако, я сосредоточен на 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

Данные IЯ собираюсь собрать:

Как показано ниже, я НЕ собираюсь собирать физические sound level %, а скорее хочу запускать действия, если уровни VU резко возрастают от 0. Это означает, что я играювидео или звуковой файл, приложение будет слышать звук, исходящий с текущей рабочей станции, и выполнять желаемое действие.

As Seen Here

Пыльник будет моим грубымпример того, как я планирую использовать или собирать данные из моих потенциальных клиентов.Используя таймер в vb.net, я могу иметь оператор, последовательно ищущий изменение в "VUSoundLevels" (не настоящий оператор), и запускать скрипт, когда происходит изменение / ввод.

Private Sub Timer1_Tick()

    If VUSoundLevels > 0 Then

        ' Run Code & Exit Loop

    End IF

End Sub

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

Вот решение C #, которое использует Windows Core Audio Library

Этот API имеет понятие Session (что в некоторой степени соответствует тому, что вы видите в микшере громкости).Итак, я предоставил класс-оболочку AudioSession, который может предоставить вам различную информацию обо всех текущих сеансах в Windows, включая такие детали, как идентификатор процесса сеанса и, возможно, его имя, отображаемое имя, значок и т. Д.

Этот класс также имеет GetChannelsPeakValues() методы, которые используют интерфейс IAudioMeterInformation для получения пикового значения для каждого аудиоканала.

Вот пример консольного приложения C # (но класс AudioSession поддерживает любой пользовательский интерфейс)технологии), которая при запуске будет отображать пиковые значения для каждого канала экземпляра chrome browser (запустите некоторое видео или звук из браузера, и цифры должны начать двигаться).Если у вас нет 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();
        }
    }
}

А вот код поддержки 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);
}
0 голосов
/ 05 февраля 2019

КЛЮЧ к этой (мое решение) работе заключается в том, что у вас отображается VOLUME MIXER во время работы этой формы.

Это может показаться немного сложным, но это может сработать и НАМНОГО проще, чемпытаясь получить информацию о звуковой карте.Я предлагаю использовать цветовую идентификацию и некоторые хорошие интерфейсы Win32.Во-первых, вам нужно будет показать свой микшер громкости (или настройки звука), как я сделал здесь (я собираюсь использовать микшер громкости для этого примера и сосредоточиться на элементе управления Amazon Music):

Volume Mixer

Sound Settings

Затем, используя следующий код (модуль и форма):

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  

А воткод формы (простой):

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

. Затем можно получить положение мыши, на которое нужно смотреть, а также цвета (примерно), как показано в результатах debug.print:

* 1022.*

Поэтому я выбрал наблюдение за координатами позиции мыши +/- (1) пиксель.Для (Красный <90), (Зеленый> 170) и (Синий <90) в качестве порогового значения. </p>

Как только это будет сделано, я могу просто установить таймер на каждые 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

И, вот снимок экрана (код выше не отражает это, только подход голых костей) конечного продукта (или, по крайней мере, пока я не решу добавить больше):

FinalProduct

...