iOS - AudioUnitRender在设备上返回错误-10876,但在模拟器中运行正常

时间:2011-03-31 15:16:33

标签: iphone audiounit

我遇到了一个问题,导致我无法从设备(iPhone4)上的麦克风捕获输入信号。但是,代码在模拟器中运行良好。 该代码最初是从Apple的MixerHostAudio类中采用的,来自MixerHost示例代码。在我开始添加用于捕获麦克风输入的代码之前,它在设备和模拟器中运行良好。 想知道是否有人可以帮助我。提前谢谢!

这是我的inputRenderCallback函数,它将信号输入到混音器输入:

static OSStatus inputRenderCallback (

void                        *inRefCon,
AudioUnitRenderActionFlags  *ioActionFlags,
const AudioTimeStamp        *inTimeStamp, 
UInt32                      inBusNumber,
UInt32                      inNumberFrames,
AudioBufferList             *ioData) {
recorderStructPtr recorderStructPointer     = (recorderStructPtr) inRefCon;
    // ....
        AudioUnitRenderActionFlags renderActionFlags;
        err = AudioUnitRender(recorderStructPointer->iOUnit, 
                              &renderActionFlags, 
                              inTimeStamp, 
                              1, // bus number for input
                              inNumberFrames, 
                              recorderStructPointer->fInputAudioBuffer
                              );
                    // error returned is -10876
    // ....
}

这是我的相关初始化代码: 现在我在混音器中只保留了1个输入,因此混音器似乎是多余的,但在添加输入捕获代码之前工作正常。

// Convenience function to allocate our audio buffers
- (AudioBufferList *) allocateAudioBufferListByNumChannels:(UInt32)numChannels withSize:(UInt32)size {
    AudioBufferList*            list;
    UInt32                      i;

    list = (AudioBufferList*)calloc(1, sizeof(AudioBufferList) + numChannels * sizeof(AudioBuffer));
    if(list == NULL)
        return nil;

    list->mNumberBuffers = numChannels;
    for(i = 0; i < numChannels; ++i) {
        list->mBuffers[i].mNumberChannels = 1;
        list->mBuffers[i].mDataByteSize = size;
        list->mBuffers[i].mData = malloc(size);
        if(list->mBuffers[i].mData == NULL) {
            [self destroyAudioBufferList:list];
            return nil;
        }
    }
    return list;
}

// initialize audio buffer list for input capture
recorderStructInstance.fInputAudioBuffer = [self allocateAudioBufferListByNumChannels:1 withSize:4096];

// I/O unit description
AudioComponentDescription iOUnitDescription;
iOUnitDescription.componentType          = kAudioUnitType_Output;
iOUnitDescription.componentSubType       = kAudioUnitSubType_RemoteIO;
iOUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
iOUnitDescription.componentFlags         = 0;
iOUnitDescription.componentFlagsMask     = 0;

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

AUNode   iONode;         // node for I/O unit
AUNode   mixerNode;      // node for Multichannel Mixer unit

// Add the nodes to the audio processing graph
result =    AUGraphAddNode (
                processingGraph,
                &iOUnitDescription,
                &iONode);

result =    AUGraphAddNode (
                processingGraph,
                &MixerUnitDescription,
                &mixerNode
            );

result = AUGraphOpen (processingGraph);

// fetch mixer AudioUnit instance
result =    AUGraphNodeInfo (
                processingGraph,
                mixerNode,
                NULL,
                &mixerUnit
            );

// fetch RemoteIO AudioUnit instance
result =    AUGraphNodeInfo (
                             processingGraph,
                             iONode,
                             NULL,
                             &(recorderStructInstance.iOUnit)
                             );


    // enable input of RemoteIO unit
UInt32 enableInput = 1;
AudioUnitElement inputBus = 1;
result = AudioUnitSetProperty(recorderStructInstance.iOUnit, 
                              kAudioOutputUnitProperty_EnableIO, 
                              kAudioUnitScope_Input, 
                              inputBus, 
                              &enableInput, 
                              sizeof(enableInput)
                              );
// setup mixer inputs
UInt32 busCount   = 1;

result = AudioUnitSetProperty (
             mixerUnit,
             kAudioUnitProperty_ElementCount,
             kAudioUnitScope_Input,
             0,
             &busCount,
             sizeof (busCount)
         );


UInt32 maximumFramesPerSlice = 4096;

result = AudioUnitSetProperty (
             mixerUnit,
             kAudioUnitProperty_MaximumFramesPerSlice,
             kAudioUnitScope_Global,
             0,
             &maximumFramesPerSlice,
             sizeof (maximumFramesPerSlice)
         );


for (UInt16 busNumber = 0; busNumber < busCount; ++busNumber) {

    // set up input callback
    AURenderCallbackStruct inputCallbackStruct;
    inputCallbackStruct.inputProc        = &inputRenderCallback;
inputCallbackStruct.inputProcRefCon  = &recorderStructInstance;

    result = AUGraphSetNodeInputCallback (
                 processingGraph,
                 mixerNode,
                 busNumber,
                 &inputCallbackStruct
             );

            // set up stream format
    AudioStreamBasicDescription mixerBusStreamFormat;
    size_t bytesPerSample = sizeof (AudioUnitSampleType);

    mixerBusStreamFormat.mFormatID          = kAudioFormatLinearPCM;
    mixerBusStreamFormat.mFormatFlags       = kAudioFormatFlagsAudioUnitCanonical;
    mixerBusStreamFormat.mBytesPerPacket    = bytesPerSample;
    mixerBusStreamFormat.mFramesPerPacket   = 1;
    mixerBusStreamFormat.mBytesPerFrame     = bytesPerSample;
    mixerBusStreamFormat.mChannelsPerFrame  = 2;
    mixerBusStreamFormat.mBitsPerChannel    = 8 * bytesPerSample;
    mixerBusStreamFormat.mSampleRate        = graphSampleRate;

    result = AudioUnitSetProperty (
                                   mixerUnit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   busNumber,
                                   &mixerBusStreamFormat,
                                   sizeof (mixerBusStreamFormat)
                                   );


}

// set sample rate of mixer output
result = AudioUnitSetProperty (
             mixerUnit,
             kAudioUnitProperty_SampleRate,
             kAudioUnitScope_Output,
             0,
             &graphSampleRate,
             sizeof (graphSampleRate)
         );


// connect mixer output to RemoteIO
result = AUGraphConnectNodeInput (
             processingGraph,
             mixerNode,         // source node
             0,                 // source node output bus number
             iONode,            // destination node
             0                  // desintation node input bus number
         );


// initialize AudioGraph
result = AUGraphInitialize (processingGraph);

// start AudioGraph
result = AUGraphStart (processingGraph);

// enable mixer input
result = AudioUnitSetParameter (
                     mixerUnit,
                     kMultiChannelMixerParam_Enable,
                     kAudioUnitScope_Input,
                     0, // bus number
                     1, // on
                     0
                  );

4 个答案:

答案 0 :(得分:1)

首先,应该注意,错误代码-10876对应于名为kAudioUnitErr_NoConnection的符号。您通常可以通过Google搜索错误代码编号以及术语CoreAudio来查找这些内容。这应该是一个暗示,你要求系统渲染到一个没有正确连接的AudioUnit。

在渲染回调中,您将void*用户数据转换为recorderStructPtr。我将假设当您调试此代码时,此强制转换返回了一个非空结构,其中包含您的实际音频单元的地址。但是,您应该使用传递给渲染回调的AudioBufferList渲染它(即inputRenderCallback函数)。其中包含您需要处理的系统样本列表。

答案 1 :(得分:1)

我自己解决了这个问题。这是由于我的代码中的错误导致 AudioUnitRender()导致10876错误。

我将AudioSession的类别设置为 AVAudioSessionCategoryPlayback ,而不是 AVAudioSessionCategoryPlayAndRecord 。当我将类别修改为 AVAudioSessionCategoryPlayAndRecord 时,我终于可以通过在设备上调用A * udioUnitRender() *来成功捕获麦克风输入。

使用AVAudioSessionCategoryPlayback在调用 AudioUnitRender()来捕获麦克风输入并且在模拟器中运行良好时不会导致任何错误。我认为这应该是一个问题 iOS模拟器(虽然不是关键)。

答案 2 :(得分:0)

我还看到当I / O单元的流格式属性中的值不一致时会出现此问题。确保每个通道的AudioStreamBasicDescription位,每帧通道,每帧字节数,每个数据包的帧数和每个数据包的字节数都有意义。

具体来说,当我通过改变每帧的通道将流格式从立体声改为单声道时,我得到了NoConnection错误,但是忘了改变每帧的字节数和每个数据包的字节数以匹配数据的一半。单声道帧作为立体声帧。

答案 3 :(得分:0)

如果初始化AudioUnit且未设置其kAudioUnitProperty_SetRenderCallback属性,则在其上调用AudioUnitRender时将收到此错误。

改为致电AudioUnitProcess