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;
}

发布了164 篇原创文章 · 获赞 162 · 访问量 65万+

猜你喜欢

转载自blog.csdn.net/lovechris00/article/details/94554313