在后台更改 Windows 10 应用程序音频混合(最好使用 Python)

Posted

技术标签:

【中文标题】在后台更改 Windows 10 应用程序音频混合(最好使用 Python)【英文标题】:Change Windows 10 Application Audio Mix in Background (preferrably with Python) 【发布时间】:2016-12-21 04:32:53 【问题描述】:

我正在寻找一种方法来更改 Windows 中不同应用程序的音频混合(即 Firefox 音频级别与 Foobar 音频级别),而不会将焦点从当前应用程序上移开。 Python 是我最熟悉的语言,但如果这能让我更轻松的话,我会使用另一种语言。该代码将用于连接带有一些音量旋钮和按钮的外部 HID 设备。焦点需要停留在当前窗口上,因为将有一个 VR 游戏在混音器之上运行,如果脚本选项卡消失,我将无法重新关注它。

我已经成功地将一些使用 comtypes 模块的older code 一起进行了 frankensteining,但我只能从那里更改左/右平衡,而不能更改特定于应用程序的音频电平。

我试图通过 MSDN 上的相关 Windows 文档(尤其是 WASAPI)来削减自己的方式,但它通常最终让我陷入微软的兔子洞,而我方式在我的头上(我充其量还是个新手程序员)。

我是不是完全走错了路?

【问题讨论】:

【参考方案1】:

因此,您提供的“旧代码”仍然与有关 Windows 10 声音 API 工作原理的 API 相关。

这种类型的代码的一个很好的例子目前在 C# 中,可以在这里找到:

*** - c# - Controling Volume Mixer:

using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;

namespace SetAppVolumne

    class Program
    
        static void Main(string[] args)
        
            const string app = "Mozilla Firefox";

            foreach (string name in EnumerateApplications())
            
                Console.WriteLine("name:" + name);
                if (name == app)
                
                    // display mute state & volume level (% of master)
                    Console.WriteLine("Mute:" + GetApplicationMute(app));
                    Console.WriteLine("Volume:" + GetApplicationVolume(app));

                    // mute the application
                    SetApplicationMute(app, true);

                    // set the volume to half of master volume (50%)
                    SetApplicationVolume(app, 50);
                
            
        

        public static float? GetApplicationVolume(string name)
        
            ISimpleAudioVolume volume = GetVolumeObject(name);
            if (volume == null)
                return null;

            float level;
            volume.GetMasterVolume(out level);
            return level * 100;
        

        public static bool? GetApplicationMute(string name)
        
            ISimpleAudioVolume volume = GetVolumeObject(name);
            if (volume == null)
                return null;

            bool mute;
            volume.GetMute(out mute);
            return mute;
        

        public static void SetApplicationVolume(string name, float level)
        
            ISimpleAudioVolume volume = GetVolumeObject(name);
            if (volume == null)
                return;

            Guid guid = Guid.Empty;
            volume.SetMasterVolume(level / 100, ref guid);
        

        public static void SetApplicationMute(string name, bool mute)
        
            ISimpleAudioVolume volume = GetVolumeObject(name);
            if (volume == null)
                return;

            Guid guid = Guid.Empty;
            volume.SetMute(mute, ref guid);
        

        public static IEnumerable<string> EnumerateApplications()
        
            // get the speakers (1st render + multimedia) device
            IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
            IMMDevice speakers;
            deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out speakers);

            // activate the session manager. we need the enumerator
            Guid IID_IAudiosessionManager2 = typeof(IAudioSessionManager2).GUID;
            object o;
            speakers.Activate(ref IID_IAudioSessionManager2, 0, IntPtr.Zero, out o);
            IAudioSessionManager2 mgr = (IAudioSessionManager2)o;

            // enumerate sessions for on this device
            IAudioSessionEnumerator sessionEnumerator;
            mgr.GetSessionEnumerator(out sessionEnumerator);
            int count;
            sessionEnumerator.GetCount(out count);

            for (int i = 0; i < count; i++)
            
                IAudioSessionControl ctl;
                sessionEnumerator.GetSession(i, out ctl);
                string dn;
                ctl.GetDisplayName(out dn);
                yield return dn;
                Marshal.ReleaseComObject(ctl);
            
            Marshal.ReleaseComObject(sessionEnumerator);
            Marshal.ReleaseComObject(mgr);
            Marshal.ReleaseComObject(speakers);
            Marshal.ReleaseComObject(deviceEnumerator);
        

        private static ISimpleAudioVolume GetVolumeObject(string name)
        
            // get the speakers (1st render + multimedia) device
            IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
            IMMDevice speakers;
            deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out speakers);

            // activate the session manager. we need the enumerator
            Guid IID_IAudioSessionManager2 = typeof(IAudioSessionManager2).GUID;
            object o;
            speakers.Activate(ref IID_IAudioSessionManager2, 0, IntPtr.Zero, out o);
            IAudioSessionManager2 mgr = (IAudioSessionManager2)o;

            // enumerate sessions for on this device
            IAudioSessionEnumerator sessionEnumerator;
            mgr.GetSessionEnumerator(out sessionEnumerator);
            int count;
            sessionEnumerator.GetCount(out count);

            // search for an audio session with the required name
            // NOTE: we could also use the process id instead of the app name (with IAudioSessionControl2)
            ISimpleAudioVolume volumeControl = null;
            for (int i = 0; i < count; i++)
            
                IAudioSessionControl ctl;
                sessionEnumerator.GetSession(i, out ctl);
                string dn;
                ctl.GetDisplayName(out dn);
                if (string.Compare(name, dn, StringComparison.OrdinalIgnoreCase) == 0)
                
                    volumeControl = ctl as ISimpleAudioVolume;
                    break;
                
                Marshal.ReleaseComObject(ctl);
            
            Marshal.ReleaseComObject(sessionEnumerator);
            Marshal.ReleaseComObject(mgr);
            Marshal.ReleaseComObject(speakers);
            Marshal.ReleaseComObject(deviceEnumerator);
            return volumeControl;
        
    

    [ComImport]
    [Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
    internal class MMDeviceEnumerator
    
    

    internal enum EDataFlow
    
        eRender,
        eCapture,
        eAll,
        EDataFlow_enum_count
    

    internal enum ERole
    
        eConsole,
        eMultimedia,
        eCommunications,
        ERole_enum_count
    

    [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IMMDeviceEnumerator
    
        int NotImpl1();

        [PreserveSig]
        int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice ppDevice);

        // the rest is not implemented
    

    [Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IMMDevice
    
        [PreserveSig]
        int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);

        // the rest is not implemented
    

    [Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IAudioSessionManager2
    
        int NotImpl1();
        int NotImpl2();

        [PreserveSig]
        int GetSessionEnumerator(out IAudioSessionEnumerator SessionEnum);

        // the rest is not implemented
    

    [Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IAudioSessionEnumerator
    
        [PreserveSig]
        int GetCount(out int SessionCount);

        [PreserveSig]
        int GetSession(int SessionCount, out IAudioSessionControl Session);
    

    [Guid("F4B1A599-7266-4319-A8CA-E70ACB11E8CD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IAudioSessionControl
    
        int NotImpl1();

        [PreserveSig]
        int GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);

        // the rest is not implemented
    

    [Guid("87CE5498-68D6-44E5-9215-6DA47EF883D8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface ISimpleAudioVolume
    
        [PreserveSig]
        int SetMasterVolume(float fLevel, ref Guid EventContext);

        [PreserveSig]
        int GetMasterVolume(out float pfLevel);

        [PreserveSig]
        int SetMute(bool bMute, ref Guid EventContext);

        [PreserveSig]
        int GetMute(out bool pbMute);
    

我发现一个将所有 Windows 核心音频 API COMTypes 导入 Python 的库是 pycaw,如果您想了解如何将 Windows API 移植到 Python 中,这将是一个好的开始。

【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review 感谢您的回复,我将尝试将此示例合并到我现有的脚本中并报告。 @Mack 修改为包含代码,感谢您的提醒。

以上是关于在后台更改 Windows 10 应用程序音频混合(最好使用 Python)的主要内容,如果未能解决你的问题,请参考以下文章

获取音频混合器中可视化的单个 Windows 应用程序当前音量输出级别

在 ios 上混合音频

如何更改录制音频的音高并在后台保存?

Windows 10 音频输入设备设置存储在哪里?

Windows Vista/7:如何对输出音频混合进行采样?

科尔多瓦录制音频不起作用 Windows 10 移动版