2013-03-21 87 views
8

我正在尋找這個例子http://teragonaudio.com/article/How-to-do-realtime-recording-with-effect-processing-on-iOS.html無輸出實時音頻處理

我想關閉我的輸出。我嘗試更改:kAudioSessionCategory_PlayAndRecordkAudioSessionCategory_RecordAudio但這不起作用。我也試圖擺脫:

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

Becouse我想從麥克風得到聲音,但沒有播放它。但不管我的聲音到達renderCallback方法的時候,我都會做一個-50的錯誤。當音頻將自動輸出,一切工作正常玩......與代碼

更新:

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); 

回答

11

如果你只想記錄,沒有播放,只需註釋掉設置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; 
} 

看到代碼後更新:

正如我懷疑,你缺少輸入回調。添加這些行:

// 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 utility classes

+0

不,我需要渲染輸入。我不想馬上玩,但想要渲染方法。這裏的問題是從麥克風獲得聲音並實時修改它。這是renderCallback做的。 – Kuba 2013-03-25 09:11:56

+0

我不明白。我以爲你只需要麥克風輸入,沒有輸出。你想輸入/輸出,並希望實時修改輸入並將其發送到輸出? – Mar0ux 2013-03-25 09:14:02

+0

我希望實時修改輸入,但不要將其發送到輸出。所以我不需要輸出,但需要渲染方法。 – Kuba 2013-03-25 09:16:24

0

我在做一個類似的應用程序代碼相同的工作,我發現,你可以通過改變枚舉kAudioSessionCategory_PlayAndRecord結束播放RecordAudio

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

這阻止了我的硬件上麥克風和揚聲器之間的反饋。