Настройка аудио панорамирования на iPhone не работает

В своем коде я настроил график обработки аудио с двумя аудиоустройствами: блок ввода-вывода и блок многоканального микшера. Сначала блок ввода / вывода:

bool RtApiIos::setupAU(void *handle, AURenderCallbackStruct inRenderProc, AudioStreamBasicDescription &outFormat)
{

AudioUnitHandle *auHandle = (AudioUnitHandle *)handle;

AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;

AudioComponent comp = AudioComponentFindNext( NULL, &desc );

if (AudioComponentInstanceNew(comp, &auHandle->audioUnit))
    return false;

if (AudioUnitSetProperty(auHandle->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inRenderProc, sizeof(inRenderProc)))
    return false;

Затем многоканальный смеситель:

    AudioComponentDescription mixerDesc;
    mixerDesc.componentType = kAudioUnitType_Mixer;
    mixerDesc.componentSubType = kAudioUnitSubType_MultiChannelMixer;
    mixerDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
    mixerDesc.componentFlags = 0;
    mixerDesc.componentFlagsMask = 0;

    comp = AudioComponentFindNext( NULL, &mixerDesc );

    if (AudioComponentInstanceNew(comp, &auHandle->mixerUnit))
        return false;

    UInt32 busCount = 1;
    if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(busCount)) )
        return false;

    if (AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inRenderProc, sizeof(inRenderProc)))
        return false;

    size = sizeof(localFormat);
    if (AudioUnitGetProperty(auHandle->mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &localFormat, &size ))
        return false;

    if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &localFormat.mSampleRate, sizeof(localFormat.mSampleRate)))
        return false;

Я запускаю график обработки, и мы идем. Звук воспроизводится нормально, но последний вызов подпрограммы не оказывает слышимого эффекта на панорамирование (и "err" == 0).

    // Declare and instantiate an audio processing graph
    NewAUGraph(&auHandle->processingGraph);

    AUNode mixerNode;
    AUGraphAddNode(auHandle->processingGraph, &mixerDesc, &mixerNode);

    AUNode ioNode;
    AUGraphAddNode(auHandle->processingGraph, &desc, &ioNode);

    // Indirectly performs audio unit instantiation
    if (AUGraphOpen(auHandle->processingGraph))
        return false;

    if( AUGraphConnectNodeInput(auHandle->processingGraph, mixerNode,  0, ioNode, 0) )
        return false;

    if (AUGraphInitialize(auHandle->processingGraph))
        return false;

    AudioUnitParameterValue panValue = 0.9; // panned almost dead-right. possible values are between -1 and 1
    OSStatus err = AudioUnitSetParameter(auHandle->mixerUnit, kMultiChannelMixerParam_Pan, kAudioUnitScope_Output, 0, panValue, 0);
    if (err != noErr) {
        //error setting pan
    }
}

Что я делаю неправильно? Спасибо!!

1 ответ

Решение

Получил эту работу, с большой помощью из старого примера проекта Apple MixerHost.

Сначала создали структуру для объединения моих ресурсов:

struct AudioUnitHandle {
    AudioUnit audioUnit;
    AUGraph processingGraph;       
    AudioUnit mixerUnit;
};

Определил описания моего аудиоустройства, создал график обработки аудио и добавил два узла - один для моего узла ввода / вывода и один для многоканального микшера:

bool setupAU(void *handle, AURenderCallbackStruct inRenderProc, AudioStreamBasicDescription &outFormat)
{
AudioUnitHandle *auHandle = (AudioUnitHandle *)handle;

// I/O Unit
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;

// Multichannel mixer unit
AudioComponentDescription mixerDesc;
mixerDesc.componentType = kAudioUnitType_Mixer;
mixerDesc.componentSubType = kAudioUnitSubType_MultiChannelMixer;
mixerDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
mixerDesc.componentFlags = 0;
mixerDesc.componentFlagsMask = 0;

//............................................................................
// Create a new audio processing graph.
if( NewAUGraph(&auHandle->processingGraph) )
    return false;

//............................................................................
// Add nodes to the audio processing graph.
AUNode ioNode;
if( AUGraphAddNode(auHandle->processingGraph, &desc, &ioNode) )
    return false;

AUNode mixerNode;
if( AUGraphAddNode(auHandle->processingGraph, &mixerDesc, &mixerNode) )
    return false;

//............................................................................
// Open the audio processing graph
// Following this call, the audio units are instantiated but not initialized
//    (no resource allocation occurs and the audio units are not in a state to
//    process audio).
if( AUGraphOpen(auHandle->processingGraph) )
    return false;

Затем немного настроек самого многоканального микшера:

// Obtain the mixer unit instance from its corresponding node.
if( AUGraphNodeInfo(auHandle->processingGraph, mixerNode, NULL, &auHandle->mixerUnit) )
    return false;

//............................................................................
// Multichannel Mixer unit Setup
UInt32 busCount = 1;    // bus count for mixer unit input
UInt16 busNumber = 0;
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, busNumber, &busCount, sizeof(busCount)) )
    return false;

// Increase the maximum frames per slice allows the mixer unit to accommodate the
//    larger slice size used when the screen is locked.
UInt32 maximumFramesPerSlice = 4096;
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, busNumber, &maximumFramesPerSlice, sizeof(maximumFramesPerSlice)) )
    return false;

// Set a callback for the mixer node's specified input
if( AUGraphSetNodeInputCallback(auHandle->processingGraph, mixerNode, busNumber, &inRenderProc) )
    return false;

// Set the format of bus 0 of the mixer unit
AudioStreamBasicDescription localFormat = {0};
UInt32 size = sizeof(localFormat);

if (AudioUnitGetProperty(auHandle->mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, busNumber, &localFormat, &size ))
    return false;

localFormat.mSampleRate = outFormat.mSampleRate;
localFormat.mChannelsPerFrame = outFormat.mChannelsPerFrame;
localFormat.mFormatID = outFormat.mFormatID;
localFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved ;
localFormat.mBitsPerChannel = outFormat.mBitsPerChannel;
localFormat.mFramesPerPacket = outFormat.mFramesPerPacket;
localFormat.mBytesPerFrame = outFormat.mBytesPerFrame;
localFormat.mBytesPerPacket = outFormat.mBytesPerPacket;

OSStatus err = 0;
err = AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, busNumber, &localFormat, size);
if(noErr != err)            //kAudioUnitErr_FormatNotSupported        = -10868,
    return false;

// Set the mixer unit's output sample rate format. This is the only aspect of the output stream
//    format that must be explicitly set.
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &localFormat.mSampleRate, sizeof(localFormat.mSampleRate)) )
    return false;

Наконец, подключили узлы и инициализировали граф обработки:

// Connect the nodes of the audio processing graph
//  Connect the mixer output to the input of the I/O unit output element
if( AUGraphConnectNodeInput(auHandle->processingGraph,
                            mixerNode,                  //Source node
                            0,                          //Source node output bus number
                            ioNode,                     //Destination node
                            0) )                        //Destination node input bus number
    return false;

//............................................................................
// Initialize the audio processing graph, configure audio data stream formats for
//    each input and output, and validate the connections between audio units.
if (AUGraphInitialize(auHandle->processingGraph))
    return false;

return true;

}

Отсюда очень просто начать и остановить весь этот беспорядок:

//Start
OSStatus err = AUGraphStart( auHandle->processingGraph );

//Stop
OSStatus err = AUGraphStop( auHandle->processingGraph );

Уф!

Другие вопросы по тегам