macOS 开发 - 声音、声卡、输出


引言

每隔一段时间就会有人问,怎样获取音量,怎样切换声卡,这里写个方法总结吧。
偏好设置中的声音,在 Cocoa 中主要使用 CoreAudio 和 AudioToolbox 来控制,在使用下面方法后,不妨通读这个框架来加深了解。

常用方法

  • AudioHardwareGetProperty
  • AudioObjectGetPropertyData
  • AudioHardwareSetProperty

一、声卡、声音输入输出

1、获取当前声卡ID


- (void)test7{
    
    AudioDeviceID inID = kAudioDeviceUnknown;
    AudioDeviceID outID = kAudioDeviceUnknown;
    
    UInt32 propertySize0 = sizeof(inID);
    UInt32 propertySize1 = sizeof(outID);
    
    AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize0, &inID);
    AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice , &propertySize1, &outID);
    
    NSLog(@"outID : %d",outID);
    //    NSLog(@"inID : %d",inID);
    
}

2、根据 UID 获取声卡 id


#define KVirtualAudioUID "AudioDevice_UID"

- (AudioDeviceID)getAudioIDWithUID:(NSString *)UID{
    
    CFStringRef virtualDeviceAudioUID = (__bridge CFStringRef)UID;
    CFStringRef *inDeviceAudioUID = &virtualDeviceAudioUID;
    
    AudioDeviceID virtualAudioDeviceID = kAudioDeviceUnknown;
    
    AudioObjectPropertyAddress proprtyAddress = {};
    proprtyAddress.mSelector = kAudioHardwarePropertyTranslateUIDToDevice;
    proprtyAddress.mScope = kAudioObjectPropertyScopeGlobal;
    proprtyAddress.mElement = kAudioObjectPropertyElementMaster;
    
    
    UInt32 inSize = sizeof(inDeviceAudioUID);
    uint32 outSize = sizeof(virtualAudioDeviceID);
    
    
    OSStatus ret = AudioObjectGetPropertyData(kAudioObjectSystemObject,
                                              &proprtyAddress,
                                              inSize,
                                              inDeviceAudioUID,
                                              &outSize,
                                              &virtualAudioDeviceID);
    
    NSLog(@"ret : %d",ret);
    NSLog(@"virtualAudioDeviceID : %d",virtualAudioDeviceID);
//    NSLog(@"virtualDeviceAudioUID : %@",(__bridge NSString *)virtualDeviceAudioUID);
    
    if (virtualAudioDeviceID == kAudioDeviceUnknown) {
        
        NSLog(@"没有发现声卡");
        return kAudioDeviceUnknown;
    }
    
    return virtualAudioDeviceID;
}


3、根据 ID 获取声卡名字


+ (NSString *)deviceNameForID:(AudioDeviceID)deviceID {
    UInt32 propertySize = 256;
    char deviceName[256];
    AudioDeviceGetProperty(deviceID, 0, false, kAudioDevicePropertyDeviceName, &propertySize, deviceName);
    NSString *deviceNameForID = [NSString stringWithCString:deviceName encoding:NSUTF8StringEncoding];

    return deviceNameForID;
}

4、根据 声卡名字 获取 ID

+ (AudioDeviceID)deviceIDForName:(NSString *)requestedDeviceName {
    
    AudioDeviceID deviceIDForName = kAudioDeviceUnknown;
    NSArray *availableOutputDeviceIDs = [self availableOutputDeviceIDs];
    
    for(NSNumber *deviceIDNumber in availableOutputDeviceIDs) {
        UInt32 deviceID = [deviceIDNumber unsignedIntValue];
        NSString *deviceName = [self deviceNameForID:deviceID];
        
        if ([requestedDeviceName isEqualToString:deviceName]) {
            deviceIDForName = deviceID;
            break;
        }
    }
    
    return deviceIDForName;
}

5、获取当前所有有效的输出设备ID


+ (NSArray *)availableOutputDeviceIDs {
    UInt32 propertySize;
    AudioDeviceID devices[64];
    int devicesCount = 0;
    
    AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
    AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propertySize, devices);
    devicesCount = (propertySize / sizeof(AudioDeviceID));
    
    NSMutableArray *availableOutputDeviceIDs = [[NSMutableArray alloc] initWithCapacity:devicesCount];
    
    for(int i = 0; i < devicesCount; ++i) {
        if ([self isOutputDevice:devices[i]]) {
            NSNumber *outputDeviceID = [NSNumber numberWithUnsignedInt:devices[i]];
            [availableOutputDeviceIDs addObject:outputDeviceID];
        }
    }
    
    return [NSArray arrayWithArray:availableOutputDeviceIDs];
}

+ (BOOL)isOutputDevice:(AudioDeviceID)deviceID {
    UInt32 propertySize = 256;
    
    AudioDeviceGetPropertyInfo(deviceID, 0, false, kAudioDevicePropertyStreams, &propertySize, NULL);
    BOOL isOutputDevice = (propertySize > 0);
    
    return isOutputDevice;
}


6、切换输出声卡

#pragma mark -- 切换输出声卡
+ (void)setOutputDeviceByID:(AudioDeviceID)newDeviceID {
    UInt32 propertySize = sizeof(UInt32);
    AudioHardwareSetProperty(kAudioHardwarePropertyDefaultOutputDevice, propertySize, &newDeviceID);
    
}

7、切换输入声卡

#pragma mark -- 切换输入声卡
+ (void)setInputDeviceByID:(AudioDeviceID)newDeviceID {
    UInt32 propertySize = sizeof(UInt32);
    AudioHardwareSetProperty(kAudioHardwarePropertyDefaultInputDevice, propertySize, &newDeviceID);
    
}

8、显示/隐藏声卡

在系统偏好设置中 显示/隐藏声卡

- (void)shouldHide:(BOOL)shouldHide audioID:(AudioDeviceID)audioID{
    
    AudioDeviceID virtualAudioDeviceID = audioID;
    
    int32_t hidden = 0;
    if (shouldHide) {
        hidden = 1;
    }
    
    AudioObjectPropertyAddress proprtyAddress = {};
    
    proprtyAddress.mSelector = 'hide';
    proprtyAddress.mScope = kAudioObjectPropertyScopeGlobal;
    proprtyAddress.mElement = kAudioObjectPropertyElementMaster;
    
    int32_t hidden_value = hidden;
    CFNumberRef number = CFNumberCreate(NULL, kCFNumberSInt32Type, &hidden_value);
    
    AudioObjectSetPropertyData(virtualAudioDeviceID, &proprtyAddress, 0, NULL, sizeof(CFNumberRef), &number);
    CFRelease(number);
}

二、音量


1、设置音量


+ (void)setSystemVolume:(float)theVolume
{
	float						newValue = theVolume;
	AudioObjectPropertyAddress	theAddress;
	AudioDeviceID				defaultDevID;
	OSStatus					theError = noErr;
	UInt32						muted;
	Boolean						canSetVol = YES, muteValue;
	Boolean						hasMute = YES, canMute = YES;
	
	defaultDevID = obtainDefaultOutputDevice();
	if (defaultDevID == kAudioObjectUnknown) {			//device not found: return without trying to set
		NSLog(@"Device unknown");
		return;
	}
	
		//check if the new value is in the correct range - normalize it if not
	newValue = theVolume > 1.0 ? 1.0 : (theVolume < 0.0 ? 0.0 : theVolume);
	if (newValue != theVolume) {
		NSLog(@"Tentative volume (%5.2f) was out of range; reset to %5.2f", theVolume, newValue);
	}
	
	theAddress.mElement = kAudioObjectPropertyElementMaster;
	theAddress.mScope = kAudioDevicePropertyScopeOutput;
	
		//set the selector to mute or not by checking if under threshold and check if a mute command is available
	if ( (muteValue = (newValue < THRESHOLD)) )
	{
		theAddress.mSelector = kAudioDevicePropertyMute;
		hasMute = AudioObjectHasProperty(defaultDevID, &theAddress);
		if (hasMute)
		{
			theError = AudioObjectIsPropertySettable(defaultDevID, &theAddress, &canMute);
			if (theError != noErr || !canMute)
			{
				canMute = NO;
				NSLog(@"Should mute device 0x%0x but did not success",defaultDevID);
			}
		}
		else canMute = NO;
	}
	else
	{
		theAddress.mSelector = kAudioHardwareServiceDeviceProperty_VirtualMasterVolume;
	}
	
// **** now manage the volume following the what we found ****
	
		//be sure the device has a volume command
	if (! AudioObjectHasProperty(defaultDevID, &theAddress))
	{
		NSLog(@"The device 0x%0x does not have a volume to set", defaultDevID);
		return;
	}
	
		//be sure the device can set the volume
	theError = AudioObjectIsPropertySettable(defaultDevID, &theAddress, &canSetVol);
	if ( theError!=noErr || !canSetVol )
	{
		NSLog(@"The volume of device 0x%0x cannot be set", defaultDevID);
		return;
	}
	
		//if under the threshold then mute it, only if possible - done/exit
	if (muteValue && hasMute && canMute)
	{
		muted = 1;
		theError = AudioObjectSetPropertyData(defaultDevID, &theAddress, 0, NULL, sizeof(muted), &muted);
		if (theError != noErr)
		{
			NSLog(@"The device 0x%0x was not muted",defaultDevID);
			return;
		}
	}
	else		//else set it
	{
		theError = AudioObjectSetPropertyData(defaultDevID, &theAddress, 0, NULL, sizeof(newValue), &newValue);
		if (theError != noErr)
		{
			NSLog(@"The device 0x%0x was unable to set volume", defaultDevID);
		}
			//if device is able to handle muting, maybe it was muted, so unlock it
		if (hasMute && canMute)
		{
			theAddress.mSelector = kAudioDevicePropertyMute;
			muted = 0;
			theError = AudioObjectSetPropertyData(defaultDevID, &theAddress, 0, NULL, sizeof(muted), &muted);
		}
	}
	if (theError != noErr) {
		NSLog(@"Unable to set volume for device 0x%0x", defaultDevID);
	}
}


2、设置静音

+ (void)applyMute:(Boolean)m
{
	AudioDeviceID				defaultDevID = kAudioObjectUnknown;
	AudioObjectPropertyAddress	theAddress;
	Boolean						hasMute, canMute = YES;
	OSStatus					theError = noErr;
	UInt32						muted = 0;
	
	defaultDevID = obtainDefaultOutputDevice();
	if (defaultDevID == kAudioObjectUnknown) {			//device not found
		NSLog(@"Device unknown");
		return;
	}
	
	theAddress.mElement = kAudioObjectPropertyElementMaster;
	theAddress.mScope = kAudioDevicePropertyScopeOutput;
	theAddress.mSelector = kAudioDevicePropertyMute;
	muted = m ? 1 : 0;
	
	hasMute = AudioObjectHasProperty(defaultDevID, &theAddress);
	
	if (hasMute)
	{
		theError = AudioObjectIsPropertySettable(defaultDevID, &theAddress, &canMute);
		if (theError == noErr && canMute)
		{
			theError = AudioObjectSetPropertyData(defaultDevID, &theAddress, 0, NULL, sizeof(muted), &muted);
			if (theError != noErr) NSLog(@"Cannot change mute status of device 0x%0x", defaultDevID);
		}
	}
}



3、判断是否静音


+ (Boolean)isMuted
{
    AudioDeviceID				defaultDevID = kAudioObjectUnknown;
    AudioObjectPropertyAddress	theAddress;
    Boolean						hasMute, canMute = YES;
    OSStatus					theError = noErr;
    UInt32						muted = 0;
    UInt32                      mutedSize = 4;
    
    defaultDevID = obtainDefaultOutputDevice();
    if (defaultDevID == kAudioObjectUnknown) {			//device not found
        NSLog(@"Device unknown");
        return false;           // works, but not the best return code for this
    }
    
    theAddress.mElement = kAudioObjectPropertyElementMaster;
    theAddress.mScope = kAudioDevicePropertyScopeOutput;
    theAddress.mSelector = kAudioDevicePropertyMute;
    
    hasMute = AudioObjectHasProperty(defaultDevID, &theAddress);
    
    if (hasMute)
    {
        theError = AudioObjectIsPropertySettable(defaultDevID, &theAddress, &canMute);
        if (theError == noErr && canMute)
        {
            theError = AudioObjectGetPropertyData(defaultDevID, &theAddress, 0, NULL, &mutedSize, &muted);
            if (muted) {
                return true;
            }
        }
    }
    return false;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
### 回答1: macOS是苹果公司开发的一款操作系统,而Arm-Linux是一款基于Arm架构的Linux操作系统。MacOS Arm-Linux指的是在Mac电脑上运行基于Arm架构的Linux系统。 苹果公司宣布将在不久的将来改变Mac电脑的芯片架构,从之前使用的Intel处理器转变为自家研发的Arm架构处理器。这意味着未来的Mac电脑将可以运行使用Arm架构编译的软件。 在这种背景下,Mac电脑将具备运行Arm-Linux操作系统的能力。Arm-Linux是Linux操作系统的一个分支,被广泛应用于嵌入式设备和移动设备等领域。将Arm-Linux移植到Mac电脑上,用户可以通过在Mac上运行Linux,获得更多的软件和应用选择。 MacOS Arm-Linux的引入将为用户提供更多的自由和灵活性。Arm-Linux操作系统具有良好的兼容性和可定制性,用户可以根据个人需求进行自定义设置,进行系统优化和调试等操作。同时,对于开发者来说,Arm-Linux也提供了更多的开发工具和环境,使他们能够更方便地进行软件开发和调试。 总之,MacOS Arm-Linux的结合将为用户带来更广阔的选择和更多的可能性,使Mac电脑成为一款更加强大和多功能的设备。用户可以享受到更多的软件和应用,同时也能更灵活地进行系统设置和软件开发等操作。这对于个人和开发者来说都是一种积极的变化,有助于提升用户体验和系统的可定制性。 ### 回答2: macOS是苹果公司自主开发的操作系统,主要运行于苹果的个人电脑和服务器上。而arm-linux是一种基于ARM架构的Linux操作系统。 macOS使用的是x86架构,而arm-linux则是基于ARM架构的操作系统。这两种架构在硬件和指令集上存在差异,因此软件在不同架构上运行时需要进行编译和适配。 最近,苹果公司宣布将在将来的Mac电脑上使用自家研发的ARM架构芯片。这一决定的背后有很多原因,其中包括更好的性能、能效和统一化等。换句话说,未来的Mac电脑将会运行基于ARM架构的芯片,并适配macOS操作系统。 对于软件开发者和用户来说,这个转变意味着一些软件可能需要重新编写或适配以在新的硬件和操作系统上运行。macOS上原本运行良好的x86架构软件需要重新编译为ARM架构版本,以确保兼容性和性能。 然而,这个转变也给开发者带来了新的机会。ARM架构在移动设备上已经得到广泛应用,移植到Mac电脑上有望带来更好的性能和能效。此外,将来的Mac电脑和iOS设备将共享更多的软件和生态系统,有助于提升跨设备的一体化体验。 总之,macOS将转向ARM架构的决定对于苹果公司和用户来说都是一个重要的里程碑。它将带来更好的性能、能效和统一化,并为开发者带来新的机遇。也许,未来的Mac电脑将更加强大和多样化。 ### 回答3: macOS是苹果公司的操作系统,它最初是基于PowerPC架构的,后来转向了x86架构。而ARM架构是一种使用更低功耗的处理器架构,被广泛应用于手机、平板电脑和嵌入式系统等领域。 近年来,ARM架构的处理器性能不断提升,开始逐渐应用于个人电脑领域。所以有人开始猜测苹果是否会将ARM架构引入其Mac电脑产品线。 然而,直到2020年底,苹果才正式发布了基于ARM架构的Mac电脑。这代表着苹果公司将逐渐适应ARM架构,并且未来的Mac电脑将会更加高效、节能。 对于开发者和用户来说,macOS转向ARM架构意味着一些程序和应用可能需要重新编译和优化,以适应新架构的要求。同时,用户也将享受到更长续航时间、更流畅的系统操作和更低的功耗。 对于整个行业来说,macOS转向ARM架构也体现了ARM架构的不断演进和扩大应用领域。它有望推动ARM架构在电脑领域的发展,进一步加强ARM与x86架构的竞争。特别是在移动办公、云计算和物联网等领域,ARM架构有望发挥更大的优势。 总之,macOS转向ARM架构是苹果公司顺应时代发展趋势的举措。它将为用户带来更好的系统体验,也为整个行业的发展带来新的机遇和挑战。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伊织Scope

请我喝杯伯爵奶茶~!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值