3

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

这是我的 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

4 回答 4

1

I solved the issue on my own. It is due to a bug in my code causing 10876 error on AudioUnitRender().

I set the category of my AudioSession as AVAudioSessionCategoryPlayback instead of AVAudioSessionCategoryPlayAndRecord. When I fixed the category to AVAudioSessionCategoryPlayAndRecord, I can finally capture microphone input successfully by calling A*udioUnitRender()* on the device.

using AVAudioSessionCategoryPlayback doesn't result to any error upon calling AudioUnitRender() to capture microphone input and is working well in the simulator. I think this should be an issue for iOS simulator (though not critical).

于 2011-04-04T08:01:20.120 回答
1

首先需要注意的是错误码-10876对应的符号名为kAudioUnitErr_NoConnection. 您通常可以通过谷歌搜索错误代码编号以及术语 CoreAudio 来找到这些。这应该是您要求系统渲染到未正确连接的 AudioUnit 的提示。

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

于 2011-04-01T08:44:04.203 回答
0

I have also seen this issue occur when the values in the I/O Unit's stream format property are inconsistent. Make sure that your AudioStreamBasicDescription's bits per channel, channels per frame, bytes per frame, frames per packet, and bytes per packet all make sense.

Specifically I got the NoConnection error when I changed a stream format from stereo to mono by changing the channels per frame, but forgot to change the bytes per frame and bytes per packet to match the fact that there is half as much data in a mono frame as a stereo frame.

于 2014-06-26T21:54:50.193 回答
0

如果你初始化一个AudioUnit并且不设置它的kAudioUnitProperty_SetRenderCallback属性,如果你调用AudioUnitRender它你会得到这个错误。

AudioUnitProcess改为调用它。

于 2019-12-10T11:44:33.157 回答