Как настроить уровень входного сигнала (усиление) на встроенном входе (OSX Core Audio / Audio Unit)?

У меня есть приложение OSX, которое записывает аудиоданные с помощью аудиоустройства. Вход аудиоустройства может быть установлен на любой доступный источник с входами, включая встроенный вход. Проблема заключается в том, что звук, который я получаю со встроенного входа, часто обрезается, тогда как в такой программе, как Audacity (или даже Quicktime), я могу уменьшить уровень входного сигнала, и у меня нет ограничения.

Конечно, умножение выборочных кадров на долю не работает, потому что я получаю меньшую громкость, но сами сэмплы все еще обрезаются во время ввода.

Как установить уровень входного сигнала или усиление для этого встроенного входа, чтобы избежать проблемы ограничения?

Ответы на вопрос(1)

Решение Вопроса

чтобы установить громкость ввода на моем MacBook Pro (модель 2011 года). Это немного забавно, мне пришлось попробовать установить громкость основного канала, затем громкость каждого независимого стереоканала, пока я не нашел тот, который работал. Посмотрите комментарии в моем коде, я подозреваю, что лучший способ узнать, работает ли ваш код, - это найти комбинацию get / set-property, которая работает, а затем сделать что-то вроде get / set (что-то еще) / get, чтобы убедиться, что ваш код работает. сеттер работает.

О, и я, конечно, укажу, что я не буду полагаться на значения в адресе, оставаясь неизменными при вызовах 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;
}

EDIT in response to your question, "How'd [I] figure this out?"

За последние пять лет я потратил много времени, используя аудиокод Apple, и разработал некоторую интуицию / процесс, когда речь заходит о том, где и как искать решения. Мой бизнес-партнер и я совместно написали оригинальные приложения iHeartRadio для iPhone первого поколения и нескольких других устройств, и одной из моих обязанностей в этом проекте была аудиозапись, в частности написание потокового декодера / проигрывателя AAC Shoutcast для iOS. В то время не было никаких документов или примеров с открытым исходным кодом, поэтому в нем было много проб и ошибок, и я многому научился.

В любом случае, когда я прочитал ваш вопрос и увидел награду, я подумал, что это просто низко висящий фрукт (то есть вы не имели RTFM ;-). Я написал несколько строк кода, чтобы установить свойство тома, и когда это не сработало, я действительно заинтересовался.

С точки зрения процесса, возможно, вы найдете это полезным:

Как только я понял, что это был не простой ответ, я начал думать оhow решить проблему. Я знал, что 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 в целом, заключается в том, что существуетlot с открытым исходным кодом, где люди пробуют разные вещи (тонны пастбинов, проектов GoogleCode и т. д.). Если вы хотите покопаться в куче этого кода, вы, как правило, либо найдете прямой ответ, либо получите очень хорошие идеи.

В моем поиске наиболее релевантными вещами, которые я обнаружил, были технические замечания Apple, показывающие, как получить устройство ввода по умолчанию и установить усиление основного ввода с помощью новых подпрограмм Toolbox (даже если оно не работает на моем оборудовании), и я нашел некоторый код это показало настройку усиления по каналу на устройстве вывода. Поскольку устройства ввода могут быть многоканальными, я решил, что это следующая логичная вещь, которую стоит попробовать.

Ваш вопрос действительно хорош, потому что по крайней мере сейчас нет правильной документации от Apple, которая показывает, как делать то, что вы просили. Это также глупо, потому что оба канала сообщают, что они устанавливают громкость, но, разумеется, только один из них (входной микрофон является монофоническим источником, так что это не удивительно, но я думаю, что у меня есть неактивный канал и нет документации об этом). небольшая ошибка на Apple).

Это происходит довольно последовательно, когда вы начинаете работать с передовыми технологиями Apple. Вы можете делать удивительные вещи с их набором инструментов, и это уносит с ног все остальные ОС, над которыми я работал, но это не займет много времени, чтобы опередить их документацию, особенно если вы пытаетесь сделать что-то достаточно сложное.

Если вы когда-нибудь решите написать драйвер ядра, например, вы найдете документацию по IOKit крайне неудачной. В конечном итоге вам нужно подключиться к Интернету и покопаться в исходном коде, в проектах других людей, в исходном коде OS X или в обоих случаях, и довольно скоро вы придете к выводу, что, как я знаю, источник - действительно лучшее место для ответов (даже хотя StackOverflow довольно крутой).

Спасибо за очки и удачи в вашем проекте :)

 jn_pdx19 июн. 2012 г., 21:32
Спасибо за отличный ответ. Из любопытства и потенциальной будущей полезности, как получилось, что вы либо столкнулись с этим решением, либо имели знания для написания кода?
 jn_pdx20 июн. 2012 г., 01:49
Отличное описание - спасибо, что нашли время!
 20 июн. 2012 г., 01:32
Получается, что правильный ответ не помещается в комментарии, поэтому я отредактировал свой ответ. Еще раз спасибо за щедрость!
 15 дек. 2014 г., 15:29
Я использую Audiounit и ищу те же решения, будет ли это работать для меня?
 25 янв. 2015 г., 19:14
Я проголосовал за это, но я хотел бы добавить, что, хотя код постера отлично работал на моем новом MacBook Pro (купленном в июле 2014 года), он не работал на моем старом MacBook Pro (январь 2008 года). Похоже, что если после попытки установить основной канал, которого нет у более старого устройства, настройка других каналов также не удастся (вероятно, какой-то асинхронный процесс не завершился). Мое решение состояло в том, чтобы только попытаться установить каналы, которые фактически доступны (hasProperty == YES).

Ваш ответ на вопрос