美文网首页MAC OS
Mac OS的系统音量控制 - NSSlider控件操作

Mac OS的系统音量控制 - NSSlider控件操作

作者: goyohol | 来源:发表于2021-05-25 22:06 被阅读0次


    实现:操作一个滑动器(NSSlider),来控制Mac电脑的系统音量控制



    先简单讲讲NSSlider控件:

    let slider = NSSlider(frame: NSMakeRect(100, 100, 200, 50))
    self.view .addSubview(slider)
    //设置背景色
    slider.wantsLayer = true
    slider.layer?.backgroundColor = NSColor.cyan.cgColor
    //minValue默认为0.0,maxValue默认为1.0
    print("minValue:\(slider.minValue), maxValue:\(slider.maxValue)")
    //添加操作的响应事件
    slider.target = self;   slider.action = #selector(sliderValueChange)
    //slider.controlSize = NSControl.ControlSize.mini   //滑块设置为最小
    //if #available(macOS 11.0, *) {    //在macOS 11.0以上
    //slider.controlSize = NSControl.ControlSize.large  //滑块设置为最大
    //}
    

    操作滑动器的响应事件:

    @objc func sliderValueChange(slider: NSSlider) {//打印值(以各种类型)
        print("slider.objectValue = \(slider.objectValue as Any)")
        print("slider.intValue = \(slider.intValue)")
        print("slider.integerValue = \(slider.integerValue)")
        print("slider.floatValue = \(slider.floatValue)")
        print("slider.doubleValue = \(slider.doubleValue)")
    }
    

    效果:
    a.minValue默认为0.0maxValue默认为1.0操作滑动器会打印当前的(范围0.0 ~ 1.0)!
    b.在滑动器(青色背景色)范围内部即可对其进行操作

    设置最小值、最大值——当设置minValue为1.0,maxValue为100.0!

    //设置minValue为1.0,maxValue为100.0
    slider.minValue = 10.0;    slider.maxValue = 100.0
    print("after set! minValue:\(slider.minValue), maxValue:\(slider.maxValue)")
    

    效果:minValue默认为10.0maxValue默认为100.0操作滑动器会打印当前的(范围10.0 ~ 100.0)!






    实现功能(系统音量大小控制)时,就使用滑动器默认最小值最大值——minValue为0.0maxValue为1.0


    Swift不太好实现。。(当然也不太想写,哈哈) 就用以前OC的代码书写了~(用个桥接来使用)

    书写一个OC类(MacAudioVolumeObject):
    1.选择'Cocoa Class'!2.使用Objective-C语言,继承自NSObject并命名为‘MacAudioVolumeObject’!3.选择'Create Bridging Header'创建桥接头文件后可进行桥接—可实现OC、Swift混编

    1.选择'Cocoa Class' 2.使用Objective-C,继承自NSObject并命名为‘MacAudioVolumeObject’ 3.选择'Create Bridging Header'来桥接


    MacAudioVolumeObject类——系统音量增大减小的功能实现:

    .h文件

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface MacAudioVolumeObject : NSObject
    
    //获取系统音量
    +(float)getVolume;
    //设置系统音量
    +(void)setSystemVolumeValue:(Float32)newVolume;
    
    @end
    
    NS_ASSUME_NONNULL_END
    

    .m文件

    #import "MacAudioVolumeObject.h"
    
    //导入调节音量所需的库:
    @import AVFoundation;//必须
    //#import <IOKit/IOKitLib.h>❌❌
    //#import <IOKit/hidsystem/IOHIDLib.h>❌❌
    //#import <IOKit/hidsystem/ev_keymap.h>❌❌
    
    @implementation MacAudioVolumeObject
    
    +(AudioDeviceID)getDefaultOutputDeviceID {
        AudioDeviceID outputDeviceID = kAudioObjectUnknown;//get output device
        
        OSStatus status = noErr;
        AudioObjectPropertyAddress propertyAOPA;
        propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
        propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
        propertyAOPA.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
    
        if (!AudioHardwareServiceHasProperty(kAudioObjectSystemObject, &propertyAOPA))
        {
            printf("Cannot find default output device!");
            return outputDeviceID;
        }
    
        status = AudioHardwareServiceGetPropertyData(kAudioObjectSystemObject, &propertyAOPA, 0, NULL, (UInt32[]){sizeof(AudioDeviceID)}, &outputDeviceID);
    
        if (status != 0)
        {
            printf("Cannot find default output device!");
        }
        return outputDeviceID;
    }
    
    //获取系统音量
    +(float)getVolume {
        Float32 outputVolume;
        
        OSStatus status = noErr;
        AudioObjectPropertyAddress propertyAOPA;
        propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
        propertyAOPA.mSelector = kAudioHardwareServiceDeviceProperty_VirtualMasterVolume;
        propertyAOPA.mScope = kAudioDevicePropertyScopeOutput;
        
        AudioDeviceID outputDeviceID = [self getDefaultOutputDeviceID];
        
        if (outputDeviceID == kAudioObjectUnknown)
        {
            printf("Unknown device");
            return 0.0;
        }
        
        if (!AudioHardwareServiceHasProperty(outputDeviceID, &propertyAOPA))
        {
            printf("No volume returned for device 0x%0x", outputDeviceID);
            return 0.0;
        }
        
        status = AudioHardwareServiceGetPropertyData(outputDeviceID, &propertyAOPA, 0, NULL, (UInt32[]){sizeof(Float32)}, &outputVolume);
        
        if (status)
        {
            printf("No volume returned for device 0x%0x", outputDeviceID);
            return 0.0;
        }
        
        if (outputVolume < 0.0 || outputVolume > 1.0) return 0.0;
        
        return outputVolume;
    }
    //设置系统音量
    +(void)setSystemVolumeValue:(Float32)newVolume {
        if (newVolume < 0.0 || newVolume > 1.0) {
            NSLog(@"Requested volume out of range (%.2f)", newVolume);
            return;
        }
        
        
        UInt32 propertySize = 0;
        OSStatus status = noErr;
        AudioObjectPropertyAddress propertyAOPA;
        propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
        propertyAOPA.mScope = kAudioDevicePropertyScopeOutput;
        if (newVolume < 0.001) {
            NSLog(@"Requested mute");
            propertyAOPA.mSelector = kAudioDevicePropertyMute;
        }
        else {
            NSLog(@"Requested volume %.2f", newVolume);
            propertyAOPA.mSelector = kAudioHardwareServiceDeviceProperty_VirtualMasterVolume;
        }
        AudioDeviceID outputDeviceID = [self getDefaultOutputDeviceID];
        if (outputDeviceID == kAudioObjectUnknown) {
            NSLog(@"Unknown device"); return;
        }
        if (!AudioHardwareServiceHasProperty(outputDeviceID, &propertyAOPA)) {
            NSLog(@"Device 0x%0x does not support volume control", outputDeviceID);
            return;
        }
        Boolean canSetVolume = NO;
        status = AudioHardwareServiceIsPropertySettable(outputDeviceID, &propertyAOPA, &canSetVolume);
        if (status || canSetVolume == NO) {
            NSLog(@"Device 0x%0x does not support volume control", outputDeviceID);
            return;
        }
        if (propertyAOPA.mSelector == kAudioDevicePropertyMute) {
            propertySize = sizeof(UInt32);
            UInt32 mute = 1;
            status = AudioHardwareServiceSetPropertyData(outputDeviceID, &propertyAOPA, 0, NULL, propertySize, &mute);
        }
        else {
            propertySize = sizeof(Float32);
            status = AudioHardwareServiceSetPropertyData(outputDeviceID, &propertyAOPA, 0, NULL, propertySize, &newVolume);
            if (status) {
                NSLog(@"Unable to set volume for device 0x%0x", outputDeviceID);
            }
            // make sure we're not muted
            propertyAOPA.mSelector = kAudioDevicePropertyMute;
            propertySize = sizeof(UInt32);
            UInt32 mute = 0;
            if (!AudioHardwareServiceHasProperty(outputDeviceID, &propertyAOPA)) {
                NSLog(@"Device 0x%0x does not support muting", outputDeviceID);
                return;
            }
            Boolean canSetMute = NO;
            status = AudioHardwareServiceIsPropertySettable(outputDeviceID, &propertyAOPA, &canSetMute);
            if (status || !canSetMute) {
                NSLog(@"Device 0x%0x does not support muting", outputDeviceID);
                return;
            }
            status = AudioHardwareServiceSetPropertyData(outputDeviceID, &propertyAOPA, 0, NULL, propertySize, &mute);
        }
        if (status) {
            NSLog(@"Unable to set volume for device 0x%0x", outputDeviceID);
        }
    }
    
    @end
    

    注意:

    • a.书写“@import AVFoundation;”来引入AVFoundation库支持获取设置系统音量’是必须!

    • b.会有许多提示API要弃用的警告!(但是目前还未找到 替代的API其他实现方法

      其中“'AudioHardwareServiceHasProperty' is deprecated: first deprecated in macOS 10.11 - no longer supported”有4个、“'AudioHardwareServiceGetPropertyData' is deprecated: first deprecated in macOS 10.11 - no longer supported”有2个、“'AudioHardwareServiceIsPropertySettable' is deprecated: first deprecated in macOS 10.11 - no longer supported”有2个、“'AudioHardwareServiceSetPropertyData' is deprecated: first deprecated in macOS 10.11 - no longer supported”有3个!

      'AudioHardwareServiceHasProperty' is deprecated: first deprecated in macOS 10.11 - no longer supported
      'AudioHardwareServiceGetPropertyData' is deprecated: first deprecated in macOS 10.11 - no longer supported
      'AudioHardwareServiceHasProperty' is deprecated: first deprecated in macOS 10.11 - no longer supported
      'AudioHardwareServiceGetPropertyData' is deprecated: first deprecated in macOS 10.11 - no longer supported
      'AudioHardwareServiceHasProperty' is deprecated: first deprecated in macOS 10.11 - no longer supported
      'AudioHardwareServiceIsPropertySettable' is deprecated: first deprecated in macOS 10.11 - no longer supported
      'AudioHardwareServiceSetPropertyData' is deprecated: first deprecated in macOS 10.11 - no longer supported
      'AudioHardwareServiceSetPropertyData' is deprecated: first deprecated in macOS 10.11 - no longer supported
      'AudioHardwareServiceHasProperty' is deprecated: first deprecated in macOS 10.11 - no longer supported
      'AudioHardwareServiceIsPropertySettable' is deprecated: first deprecated in macOS 10.11 - no longer supported
      'AudioHardwareServiceSetPropertyData' is deprecated: first deprecated in macOS 10.11 - no longer supported
      
      出现的警告



    在Swift中操作OC类,需要进行桥接——引入头文件"MacAudioVolumeObject.h"

    在桥接头文件中添加上""——"MacAudioVolumeObject.h"

    之后在Swift代码中就可以使用操作该OC类(MacAudioVolumeObject)了:

    在Swift中使用“设置系统音量”的方法

    功能实现的代码书写:

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let slider = NSSlider(frame: NSMakeRect(100, 100, 200, 50))
        self.view .addSubview(slider)
        //设置背景色
        slider.wantsLayer = true
        slider.layer?.backgroundColor = NSColor.cyan.cgColor
        //minValue默认为0.0,maxValue默认为1.0
        print("minValue:\(slider.minValue), maxValue:\(slider.maxValue)")
        //添加操作的响应事件
        slider.target = self;   slider.action = #selector(sliderValueChange)
        
        //获取当前系统音量
        let sysVolume = MacAudioVolumeObject .getVolume()
        slider.floatValue = sysVolume//设置slider的初始值(音量)
        print("sysVolume:\(sysVolume),slider.floatValue:\(slider.floatValue)")
    }
    @objc func sliderValueChange(slider: NSSlider) {
        //设置当前系统音量
        MacAudioVolumeObject .setSystemVolumeValue(slider.floatValue)
        
    }
    

    效果
    a.刚打开App后,通过.getVolume()方法获取当前系统音量大小(0.0~1.0),并设置给滑动器
    (打印—sysVolume:0.23832849,slider.floatValue:0.23832849)
    b.拖动滑动器时会通过.setSystemVolumeValue(slider.floatValue)方法设置 系统音量大小,顶部状态栏音量提示也会改变


    注意:运行代码时,会有打印⚠️

    2021-05-24 10:06:16.890878+0800 ControlMacAudioVolume[2558:62317] [plugin] AddInstanceForFactory: No factory registered for id <CFUUID 0x600000242180> F8BB1C28-BAE8-11D6-9C31-00039315CD46
    2021-05-24 10:06:16.950842+0800 ControlMacAudioVolume[2558:62317]  HALC_ShellDriverPlugIn::Open: Can't get a pointer to the Open routine
    2021-05-24 10:06:16.951538+0800 ControlMacAudioVolume[2558:62317]  HALC_ShellDriverPlugIn::Open: Can't get a pointer to the Open routine
    

    以前写代码时就一直存在!到现在仍未找到解决方法!(已经解决的小伙伴可以交流一下~)


    至此,macOS上系统音量控制的功能就实现了~

    目前自己就只找到了这种方式,用了这么久一直不是很爽—使用了系统弃用API运行代码还有警告⚠️
    之前使用了AVCaptureAudioChannelAVCaptureAudioPreviewOutput试试可不可实现,结果没有能实现!
    (还有其他实现方式的小伙伴的话,希望可以交流一下~)








    goyohol's essay

    相关文章

      网友评论

        本文标题:Mac OS的系统音量控制 - NSSlider控件操作

        本文链接:https://www.haomeiwen.com/subject/qfillqtx.html