实时audio处理,无输出

我正在寻找这个例子http://teragonaudio.com/article/How-to-do-do-realtime-recording-with-effect-processing-on-iOS.html

我想closures我的输出。 我尝试将kAudioSessionCategory_PlayAndRecord更改为kAudioSessionCategory_RecordAudio但这不起作用。 我也试图摆脱:

  if(AudioUnitSetProperty(*audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &streamDescription, sizeof(streamDescription)) != noErr) { return 1; } 

以前我想从麦克风听到声音,但没有播放它。 但是,不pipe我的声音到达renderCallback方法的时候,都会有一个-50的错误。 当audio自动播放输出时,一切工作正常…

用代码更新:

 using namespace std; AudioUnit *audioUnit = NULL; float *convertedSampleBuffer = NULL; int initAudioSession() { audioUnit = (AudioUnit*)malloc(sizeof(AudioUnit)); if(AudioSessionInitialize(NULL, NULL, NULL, NULL) != noErr) { return 1; } if(AudioSessionSetActive(true) != noErr) { return 1; } UInt32 sessionCategory = kAudioSessionCategory_PlayAndRecord; if(AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(UInt32), &sessionCategory) != noErr) { return 1; } Float32 bufferSizeInSec = 0.02f; if(AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(Float32), &bufferSizeInSec) != noErr) { return 1; } UInt32 overrideCategory = 1; if(AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(UInt32), &overrideCategory) != noErr) { return 1; } // There are many properties you might want to provide callback functions for: // kAudioSessionProperty_AudioRouteChange // kAudioSessionProperty_OverrideCategoryEnableBluetoothInput // etc. return 0; } OSStatus renderCallback(void *userData, AudioUnitRenderActionFlags *actionFlags, const AudioTimeStamp *audioTimeStamp, UInt32 busNumber, UInt32 numFrames, AudioBufferList *buffers) { OSStatus status = AudioUnitRender(*audioUnit, actionFlags, audioTimeStamp, 1, numFrames, buffers); int doOutput = 0; if(status != noErr) { return status; } if(convertedSampleBuffer == NULL) { // Lazy initialization of this buffer is necessary because we don't // know the frame count until the first callback convertedSampleBuffer = (float*)malloc(sizeof(float) * numFrames); baseTime = (float)QRealTimer::getUptimeInMilliseconds(); } SInt16 *inputFrames = (SInt16*)(buffers->mBuffers->mData); // If your DSP code can use integers, then don't bother converting to // floats here, as it just wastes CPU. However, most DSP algorithms rely // on floating point, and this is especially true if you are porting a // VST/AU to iOS. int i; for( i = numFrames; i < fftlength; i++ ) // Shifting buffer x_inbuf[i - numFrames] = x_inbuf[i]; for( i = 0; i < numFrames; i++) { x_inbuf[i + x_phase] = (float)inputFrames[i] / (float)32768; } if( x_phase + numFrames == fftlength ) { x_alignment.SigProc_frontend(x_inbuf); // Signal processing front-end (FFT!) doOutput = x_alignment.Align(); /// Output as text! In the real-time version, // this is where we update visualisation callbacks and launch other services if ((doOutput) & (x_netscore.isEvent(x_alignment.Position())) &(x_alignment.lastAction()<x_alignment.Position()) ) { // here i want to do something with my input! } } else x_phase += numFrames; return noErr; } int initAudioStreams(AudioUnit *audioUnit) { UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord; if(AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(UInt32), &audioCategory) != noErr) { return 1; } UInt32 overrideCategory = 1; if(AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(UInt32), &overrideCategory) != noErr) { // Less serious error, but you may want to handle it and bail here } AudioComponentDescription componentDescription; componentDescription.componentType = kAudioUnitType_Output; componentDescription.componentSubType = kAudioUnitSubType_RemoteIO; componentDescription.componentManufacturer = kAudioUnitManufacturer_Apple; componentDescription.componentFlags = 0; componentDescription.componentFlagsMask = 0; AudioComponent component = AudioComponentFindNext(NULL, &componentDescription); if(AudioComponentInstanceNew(component, audioUnit) != noErr) { return 1; } UInt32 enable = 1; if(AudioUnitSetProperty(*audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enable, sizeof(UInt32)) != noErr) { return 1; } AURenderCallbackStruct callbackStruct; callbackStruct.inputProc = renderCallback; // Render function callbackStruct.inputProcRefCon = NULL; if(AudioUnitSetProperty(*audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callbackStruct, sizeof(AURenderCallbackStruct)) != noErr) { return 1; } AudioStreamBasicDescription streamDescription; // You might want to replace this with a different value, but keep in mind that the // iPhone does not support all sample rates. 8kHz, 22kHz, and 44.1kHz should all work. streamDescription.mSampleRate = 44100; // Yes, I know you probably want floating point samples, but the iPhone isn't going // to give you floating point data. You'll need to make the conversion by hand from // linear PCM <-> float. streamDescription.mFormatID = kAudioFormatLinearPCM; // This part is important! streamDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; streamDescription.mBitsPerChannel = 16; // 1 sample per frame, will always be 2 as long as 16-bit samples are being used streamDescription.mBytesPerFrame = 2; streamDescription.mChannelsPerFrame = 1; streamDescription.mBytesPerPacket = streamDescription.mBytesPerFrame * streamDescription.mChannelsPerFrame; // Always should be set to 1 streamDescription.mFramesPerPacket = 1; // Always set to 0, just to be sure streamDescription.mReserved = 0; // Set up input stream with above properties if(AudioUnitSetProperty(*audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamDescription, sizeof(streamDescription)) != noErr) { return 1; } // Ditto for the output stream, which we will be sending the processed audio to if(AudioUnitSetProperty(*audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &streamDescription, sizeof(streamDescription)) != noErr) { return 1; } return 0; } int startAudioUnit(AudioUnit *audioUnit) { if(AudioUnitInitialize(*audioUnit) != noErr) { return 1; } if(AudioOutputUnitStart(*audioUnit) != noErr) { return 1; } return 0; } 

并从我的VC调用:

  initAudioSession(); initAudioStreams( audioUnit); startAudioUnit( audioUnit); 

如果您只想录制,不播放,只需将设置renderCallback的行注释掉即可:

 AURenderCallbackStruct callbackStruct; callbackStruct.inputProc = renderCallback; // Render function callbackStruct.inputProcRefCon = NULL; if(AudioUnitSetProperty(*audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callbackStruct, sizeof(AURenderCallbackStruct)) != noErr) { return 1; } 

看到代码后更新:

正如我怀疑,你缺lessinputcallback。 添加这些行:

 // at top: #define kInputBus 1 AURenderCallbackStruct callbackStruct; /**/ callbackStruct.inputProc = &ALAudioUnit::recordingCallback; callbackStruct.inputProcRefCon = this; status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callbackStruct, sizeof(callbackStruct)); 

现在在你的recordingCallback中:

 OSStatus ALAudioUnit::recordingCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { // TODO: Use inRefCon to access our interface object to do stuff // Then, use inNumberFrames to figure out how much data is available, and make // that much space available in buffers in an AudioBufferList. // Then: // Obtain recorded samples OSStatus status; ALAudioUnit *pThis = reinterpret_cast<ALAudioUnit*>(inRefCon); if (!pThis) return noErr; //assert (pThis->m_nMaxSliceFrames >= inNumberFrames); pThis->recorderBufferList->GetBufferList().mBuffers[0].mDataByteSize = inNumberFrames * pThis->m_recorderSBD.mBytesPerFrame; status = AudioUnitRender(pThis->audioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &pThis->recorderBufferList->GetBufferList()); THROW_EXCEPTION_IF_ERROR(status, "error rendering audio unit"); // If we're not playing, I don't care about the data, simply discard it if (!pThis->playbackState || pThis->isSeeking) return noErr; // Now, we have the samples we just read sitting in buffers in bufferList pThis->DoStuffWithTheRecordedAudio(inNumberFrames, pThis->recorderBufferList, inTimeStamp); return noErr; } 

顺便说一句,我分配我自己的缓冲区,而不是使用AudioUnit提供的缓冲区。 如果您想使用AudioUnit分配的缓冲区,您可能需要更改这些部分。

更新:

如何分配自己的缓冲区:

 recorderBufferList = new AUBufferList(); recorderBufferList->Allocate(m_recorderSBD, m_nMaxSliceFrames); recorderBufferList->PrepareBuffer(m_recorderSBD, m_nMaxSliceFrames); 

此外,如果你这样做,告诉AudioUnit不分配缓冲区:

 // Disable buffer allocation for the recorder (optional - do this if we want to pass in our own) flag = 0; status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_ShouldAllocateBuffer, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); 

您需要包含CoreAudio实用程序类

感谢@ Mar0ux的回答。 谁来这里寻找完整的示例代码可以看看这里:

https://code.google.com/p/ios-coreaudio-example/

我正在做一个类似的应用程序使用相同的代码,我发现你可以通过改变枚举kAudioSessionCategory_PlayAndRecordRecordAudio来结束播放

 int initAudioStreams(AudioUnit *audioUnit) { UInt32 audioCategory = kAudioSessionCategory_RecordAudio; if(AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(UInt32), &audioCategory) != noErr) { return 1; } 

这阻止了我的硬件上麦克风和扬声器之间的反馈。