objective-c – 如何在内置输入(OSX Core Audio / Audio Unit)上设置输入电平(增益)?

我有一个OSX应用程序,使用音频单元记录音频数据.音频单元的输入可以设置为带输入的任何可用信号源,包括内置输入.问题是,我从内置输入获得的音频经常被剪裁,而在诸如Audacity(甚至是Quicktime)之类的程序中,我可以调低输入电平而不会剪辑.

当然,将样本帧乘以一小部分是行不通的,因为我的体积较小,但样本本身在输入时仍然被剪裁.

如何设置内置输入的输入电平或增益以避免剪切问题?

这对我来说可以在我的MacBook Pro(2011型号)上设置输入音量.这有点时髦,我不得不尝试设置主通道音量,然后是每个独立的立体声通道音量,直到我发现一个有效.查看我的代码中的注释,我怀疑最好的方法来判断你的代码是否正常工作是找到一个有效的get / set-property组合,然后做一些像get / set(别的东西)/ get来验证你的代码setter正在工作.

哦,我当然会指出,我不会依赖地址中的值来保持getProperty调用的相同,就像我在这里做的那样.它似乎工作但是当你通过引用传递一个函数时依赖于struct值是绝对不好的做法.这当然是示例代码所以请原谅我的懒惰. 😉

//
//  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,将其转储到文件并在其他文本编辑器中打开).

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

一旦我达到这一点,我又回到了经过验证的尝试:开始谷歌搜索可能涉及的关键字(例如AudioObjectSetPropertyData,kAudioDevicePropertyVolumeScalar等),寻找示例用法.

我发现有关CoreAudio和使用Apple Toolbox的一个有趣的事情是,有许多开源代码,人们尝试各种各样的事情(大量的粘贴和GoogleCode项目等).如果你愿意挖掘一堆这样的代码,你通常要么直接找到答案,要么得到一些非常好的想法.

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

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

当您开始处理Apple的尖端技术时,这种情况非常一致.你可以使用他们的工具箱做出令人惊奇的事情,它会打击我在水面上工作过的所有其他操作系统,但是不需要太长时间才能获得他们的文档,特别是如果你想要做任何适度复杂的事情.

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

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

相关文章
相关标签/搜索