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


实现:操作一个滑动器(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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 浅谈MacOS开发-NSSlider,它和iOS的UISlider相比,差别真的是太大了。 首先我们简单了解下 i...
    王科伟阅读 6,107评论 2 1
  • 以下原文转载于(https://docs.docker.com/docker-for-mac/)(想找中文版的最新...
    Veekend阅读 12,269评论 0 17
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 12,765评论 28 53
  • 信任包括信任自己和信任他人 很多时候,很多事情,失败、遗憾、错过,源于不自信,不信任他人 觉得自己做不成,别人做不...
    吴氵晃阅读 11,370评论 4 8
  • 怎么对待生活,它也会怎么对你 人都是哭着来到这个美丽的人间。每个人从来到尘寰到升入天堂,整个生命的历程都是一本书,...
    静静在等你阅读 10,471评论 1 6

友情链接更多精彩内容