Mac OS的系统音量控制 - NSSlider控件操作
实现:操作一个滑动器(NSSlider),来控制Mac电脑的系统音量控制!
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.0,maxValue默认为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.0,maxValue默认为100.0!操作滑动器会打印当前的值(范围:10.0 ~ 100.0)!
实现功能(系统音量大小控制)时,就使用滑动器的默认的最小值和最大值——minValue为0.0,maxValue为1.0!
Swift不太好实现。。(当然也不太想写,哈哈) 就用以前OC的代码书写了~(用个桥接来使用)
书写一个OC类(MacAudioVolumeObject
):
1.选择'Cocoa Class'!2.使用Objective-C语言,继承自NSObject并命名为‘MacAudioVolumeObject’!3.选择'Create Bridging Header'创建桥接头文件后可进行桥接—可实现OC、Swift混编!
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"
之后在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
以前写代码时就一直存在!到现在仍未找到解决方法!(已经解决的小伙伴可以交流一下~)
目前自己就只找到了这种方式,用了这么久一直不是很爽—使用了系统弃用API、运行代码还有警告⚠️
之前使用了AVCaptureAudioChannel、AVCaptureAudioPreviewOutput试试可不可实现,结果没有能实现!
(还有其他实现方式的小伙伴的话,希望可以交流一下~)