9

我有一个使用音频单元记录音频数据的 OSX 应用程序。音频单元的输入可以设置为任何可用的输入源,包括内置输入。问题是,我从内置输入中获得的音频经常被削波,而在 Audacity(甚至 Quicktime)等程序中,我可以调低输入电平而不会削波。

当然,将样本帧乘以分数是行不通的,因为我得到的音量较低,但样本本身在输入时仍会被剪裁。

如何设置该内置输入的输入电平或增益以避免削波问题?

4

1 回答 1

15

这对我来说可以在我的 MacBook Pro(2011 型号)上设置输入音量。这有点古怪,我不得不尝试设置主通道音量,然后设置每个独立的立体声通道音量,直到找到一个有效的。查看我的代码中的注释,我怀疑判断您的代码是否有效的最佳方法是找到一个有效的 get/set-property 组合,然后执行 get/set(其他)/get 之类的操作来验证您的二传手正在工作。

哦,我当然会指出,我不会依赖地址中的值在 getProperty 调用中保持不变,就像我在这里所做的那样。它似乎有效,但是当您通过引用传递一个函数时,依赖结构值相同绝对是一种不好的做法。这当然是示例代码,所以请原谅我的懒惰。;)

//
//  main.c
//  testInputVolumeSetter
//

#include <CoreFoundation/CoreFoundation.h>
#include <CoreAudio/CoreAudio.h>

OSStatus setDefaultInputDeviceVolume( Float32 toVolume );

int main(int argc, const char * argv[]) {
    OSStatus                        err;

    // Load the Sound system preference, select a default
    // input device, set its volume to max.  Now set
    // breakpoints at each of these lines.  As you step over
    // them you'll see the input volume change in the Sound
    // preference panel.
    //
    // On my MacBook Pro setting the channel[ 1 ] volume
    // on the default microphone input device seems to do
    // the trick.  channel[ 0 ] reports that it works but
    // seems to have no effect and the master channel is
    // unsettable.
    //
    // I do not know how to tell which one will work so
    // probably the best thing to do is write your code
    // to call getProperty after you call setProperty to
    // determine which channel(s) work.
    err = setDefaultInputDeviceVolume( 0.0 );
    err = setDefaultInputDeviceVolume( 0.5 );
    err = setDefaultInputDeviceVolume( 1.0 );
}

// 0.0 == no volume, 1.0 == max volume
OSStatus setDefaultInputDeviceVolume( Float32 toVolume ) {
    AudioObjectPropertyAddress      address;
    AudioDeviceID                   deviceID;
    OSStatus                        err;
    UInt32                          size;
    UInt32                          channels[ 2 ];
    Float32                         volume;

    // get the default input device id
    address.mSelector = kAudioHardwarePropertyDefaultInputDevice;
    address.mScope = kAudioObjectPropertyScopeGlobal;
    address.mElement = kAudioObjectPropertyElementMaster;

    size = sizeof(deviceID);
    err = AudioObjectGetPropertyData( kAudioObjectSystemObject, &address, 0, nil, &size, &deviceID );

    // get the input device stereo channels
    if ( ! err ) {
        address.mSelector = kAudioDevicePropertyPreferredChannelsForStereo;
        address.mScope = kAudioDevicePropertyScopeInput;
        address.mElement = kAudioObjectPropertyElementWildcard;
        size = sizeof(channels);
        err = AudioObjectGetPropertyData( deviceID, &address, 0, nil, &size, &channels );
    }

    // run some tests to see what channels might respond to volume changes
    if ( ! err ) {
        Boolean                     hasProperty;

        address.mSelector = kAudioDevicePropertyVolumeScalar;
        address.mScope = kAudioDevicePropertyScopeInput;

        // On my MacBook Pro using the default microphone input:

        address.mElement = kAudioObjectPropertyElementMaster;
        // returns false, no VolumeScalar property for the master channel
        hasProperty = AudioObjectHasProperty( deviceID, &address );

        address.mElement = channels[ 0 ];
        // returns true, channel 0 has a VolumeScalar property
        hasProperty = AudioObjectHasProperty( deviceID, &address );

        address.mElement = channels[ 1 ];
        // returns true, channel 1 has a VolumeScalar property
        hasProperty = AudioObjectHasProperty( deviceID, &address );
    }

    // try to get the input volume
    if ( ! err ) {
        address.mSelector = kAudioDevicePropertyVolumeScalar;
        address.mScope = kAudioDevicePropertyScopeInput;

        size = sizeof(volume);
        address.mElement = kAudioObjectPropertyElementMaster;
        // returns an error which we expect since it reported not having the property
        err = AudioObjectGetPropertyData( deviceID, &address, 0, nil, &size, &volume );

        size = sizeof(volume);
        address.mElement = channels[ 0 ];
        // returns noErr, but says the volume is always zero (weird)
        err = AudioObjectGetPropertyData( deviceID, &address, 0, nil, &size, &volume );

        size = sizeof(volume);
        address.mElement = channels[ 1 ];
        // returns noErr, but returns the correct volume!
        err = AudioObjectGetPropertyData( deviceID, &address, 0, nil, &size, &volume );
    }

    // try to set the input volume
    if ( ! err ) {
        address.mSelector = kAudioDevicePropertyVolumeScalar;
        address.mScope = kAudioDevicePropertyScopeInput;

        size = sizeof(volume);

        if ( toVolume < 0.0 ) volume = 0.0;
        else if ( toVolume > 1.0 ) volume = 1.0;
        else volume = toVolume;

        address.mElement = kAudioObjectPropertyElementMaster;
        // returns an error which we expect since it reported not having the property
        err = AudioObjectSetPropertyData( deviceID, &address, 0, nil, size, &volume );

        address.mElement = channels[ 0 ];
        // returns noErr, but doesn't affect my input gain
        err = AudioObjectSetPropertyData( deviceID, &address, 0, nil, size, &volume );

        address.mElement = channels[ 1 ];
        // success! correctly sets the input device volume.
        err = AudioObjectSetPropertyData( deviceID, &address, 0, nil, size, &volume );
    }

    return err;
}

编辑以回答您的问题,“[我] 怎么解决这个问题?”

在过去五年左右的时间里,我花了很多时间使用 Apple 的音频代码,并且在涉及到哪里以及如何寻找解决方案时,我已经开发了一些直觉/过程。我和我的商业伙伴共同为第一代 iPhone 和其他一些设备编写了原始的 iHeartRadio 应用程序,我在该项目中的职责之一是音频部分,专门为 iOS 编写 AAC Shoutcast 流解码器/播放器。当时没有任何文档或开源示例,所以它涉及到大量的反复试验,我学到了很多东西。

无论如何,当我读到你的问题并看到赏金时,我认为这只是唾手可得的果实(即你没有 RTFM ;-)。我写了几行代码来设置音量属性,当这不起作用时,我真的很感兴趣。

过程方面,也许你会发现这很有用:

一旦我知道这不是一个直截了当的答案,我就开始考虑如何解决这个问题。我知道 Sound System Preference 可以让你设置输入增益,所以我开始用 otool 拆解它,看看 Apple 是在使用旧的还是新的 Audio Toolbox 例程(新的,因为它发生了):

尝试使用:

otool -tV /System/Library/PreferencePanes/Sound.prefPane/Contents/MacOS/Sound | bbedit

然后搜索Audio以查看调用了哪些方法(如果您没有 bbedit,每个 Mac 开发人员都应该 IMO,将其转储到文件并在其他文本编辑器中打开)。

我最熟悉旧的、已弃用的音频工具箱例程(在这个行业中过时三年),所以我查看了一些来自 Apple 的技术说明。他们有一个显示如何使用最新的 CoreAudio 方法获取默认输入设备并设置其音量的方法,但您无疑看到他们的代码无法正常工作(至少在我的 MBP 上)。

一旦我达到了这一点,我就回到了久经考验的方法:开始在谷歌上搜索可能涉及的关键字(例如AudioObjectSetPropertyDatakAudioDevicePropertyVolumeScalar等),以寻找示例用法。

我发现关于 CoreAudio 和使用 Apple Toolbox 的一件有趣的事情是,有很多开源代码可供人们尝试各种事情(大量的 pastebin 和 GoogleCode 项目等)。如果您愿意深入研究这些代码,您通常会直接找到答案或获得一些非常好的想法。

在我的搜索中,我发现最相关的东西是 Apple 技术说明,它展示了如何使用新的 Toolbox 例程获取默认输入设备并设置主输入增益(即使它在我的硬件上不起作用),我找到了一些代码显示在输出设备上按通道设置增益。由于输入设备可以是多通道的,我认为这是下一个合乎逻辑的尝试。

您的问题非常好,因为至少现在 Apple 没有正确的文档显示如何执行您所要求的操作。这也很愚蠢,因为两个通道都报告说他们设置了音量,但显然只有一个设置了(输入麦克风是单声道源,所以这并不奇怪,但我认为有一个无操作通道并且没有关于它的文档苹果上的一个错误)。

当您开始使用 Apple 的尖端技术时,这种情况会始终如一地发生。你可以用他们的工具箱做一些了不起的事情,它把我在水中工作过的所有其他操作系统都搞砸了,但不需要太长时间就能领先于他们的文档,特别是如果你正在尝试做任何适度复杂的事情。

例如,如果您决定编写内核驱动程序,您会发现 IOKit 上的文档严重不足。最终,您必须上网并挖掘源代码,无论是其他人的项目还是 OS X 源代码或两者兼而有之,很快您就会得出结论,正如我所拥有的那样,源代码确实是获得答案的最佳位置(即使 StackOverflow非常棒)。

感谢您的积分并祝您项目好运:)

于 2012-06-17T07:17:56.923 回答