如何获得计算机的当前音量?

时间:2012-01-21 04:45:25

标签: objective-c macos cocoa

如何从Cocoa API访问Mac的当前音量级别?

例如:当我在OS X 10.7上使用Spotify.app并且出现声音广告时,我关闭了Mac的音量,应用程序将暂停广告,直到我将其恢复到平均水平。我发现这令人讨厌,并且违反了用户隐私,但Spotify已经找到了一种方法来实现这一目标。

有什么办法可以用Cocoa做到这一点吗?我正在制作一个应用程序,如果音量很低,可能会对用户发出警告。

2 个答案:

答案 0 :(得分:9)

有两种选择。第一步是确定您想要的设备并获取其ID。假设默认输出设备,代码将类似于:

AudioObjectPropertyAddress propertyAddress = { 
    kAudioHardwarePropertyDefaultOutputDevice, 
    kAudioObjectPropertyScopeGlobal, 
    kAudioObjectPropertyElementMaster 
};

AudioDeviceID deviceID;
UInt32 dataSize = sizeof(deviceID);
OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, &deviceID);

if(kAudioHardwareNoError != result)
    // Handle the error

接下来,您可以使用kAudioHardwareServiceDeviceProperty_VirtualMasterVolume属性来获取设备的虚拟主卷:

AudioObjectPropertyAddress propertyAddress = { 
    kAudioHardwareServiceDeviceProperty_VirtualMasterVolume, 
    kAudioDevicePropertyScopeOutput,
    kAudioObjectPropertyElementMaster 
};

if(!AudioHardwareServiceHasProperty(deviceID, &propertyAddress))
    // An error occurred

Float32 volume;
UInt32 dataSize = sizeof(volume);
OSStatus result = AudioHardwareServiceGetPropertyData(deviceID, &propertyAddress, 0, NULL, &dataSize, &volume);

if(kAudioHardwareNoError != result)
    // An error occurred

或者,您可以使用kAudioDevicePropertyVolumeScalar获取特定频道的音量:

UInt32 channel = 1; // Channel 0  is master, if available
AudioObjectPropertyAddress propertyAddress = { 
    kAudioDevicePropertyVolumeScalar, 
    kAudioDevicePropertyScopeOutput,
    channel 
};

if(!AudioObjectHasProperty(deviceID, &propertyAddress))
    // An error occurred

Float32 volume;
UInt32 dataSize = sizeof(volume);
OSStatus result = AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, NULL, &dataSize, &volume);

if(kAudioHardwareNoError != result)
    // An error occurred

Apple的文档中解释了两者之间的区别:

  

kAudioHardwareServiceDeviceProperty_VirtualMasterVolume

     

表示音量控件值的Float32值。该   此属性的值范围是0.0(静音)到1.0(完整   水平)。此属性的效果取决于硬件设备   与HAL音频对象相关联。如果设备有主设备   音量控制,此属性控制它。如果设备有   单个通道音量控制,此属性适用于那些   通过设备首选的多通道布局识别,或者   如果设备仅是立体声,则首选立体声对。这个控制   保持它影响的渠道之间的相对平衡。

因此,准确定义设备的音量可能会非常棘手,特别是对于具有非标准频道映射的多声道设备。

答案 1 :(得分:2)

CocoaDev开始,这些类方法看起来应该可行,尽管它不像Cocoa那样:

#import <AudioToolbox/AudioServices.h>

+(AudioDeviceID)defaultOutputDeviceID
{
    AudioDeviceID   outputDeviceID = kAudioObjectUnknown;

    // get output device device
    UInt32 propertySize = 0;
    OSStatus status = noErr;
    AudioObjectPropertyAddress propertyAOPA;
    propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
    propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
    propertyAOPA.mSelector = kAudioHardwarePropertyDefaultOutputDevice;

    if (!AudioHardwareServiceHasProperty(kAudioObjectSystemObject, &propertyAOPA))
    {
        NSLog(@"Cannot find default output device!");
        return outputDeviceID;
    }

    propertySize = sizeof(AudioDeviceID);

    status = AudioHardwareServiceGetPropertyData(kAudioObjectSystemObject, &propertyAOPA, 0, NULL, &propertySize, &outputDeviceID);

    if(status) 
    {
        NSLog(@"Cannot find default output device!");
    }
    return outputDeviceID;
}

// getting system volume

+(float)volume 
{
    Float32         outputVolume;

    UInt32 propertySize = 0;
    OSStatus status = noErr;
    AudioObjectPropertyAddress propertyAOPA;
    propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
    propertyAOPA.mSelector = kAudioHardwareServiceDeviceProperty_VirtualMasterVolume;
    propertyAOPA.mScope = kAudioDevicePropertyScopeOutput;

    AudioDeviceID outputDeviceID = [[self class] defaultOutputDeviceID];

    if (outputDeviceID == kAudioObjectUnknown)
    {
        NSLog(@"Unknown device");
        return 0.0;
    }

    if (!AudioHardwareServiceHasProperty(outputDeviceID, &propertyAOPA))
    {
        NSLog(@"No volume returned for device 0x%0x", outputDeviceID);
        return 0.0;
    }

    propertySize = sizeof(Float32);

    status = AudioHardwareServiceGetPropertyData(outputDeviceID, &propertyAOPA, 0, NULL, &propertySize, &outputVolume);

    if (status)
    {
        NSLog(@"No volume returned for device 0x%0x", outputDeviceID);
        return 0.0;
    }

    if (outputVolume < 0.0 || outputVolume > 1.0) return 0.0;

    return outputVolume;
}