2012-08-30 11 views
5

Ich schreibe eine iOS-App, die Eingaben vom Mikrofon erhält, durch eine Hochpassfilter-Audioeinheit führt und über die Lautsprecher wiedergibt. Ich konnte dies erfolgreich mit der AUGraph-API durchführen. Darin habe ich zwei Knoten platziert: eine Remote I/O-Einheit und eine Effektaudioeinheit (kAudioUnitType_Effect, kAudioUnitSubType_HighPassFilter) und den Ausgabeumfang des Eingabeelements der io-Einheit mit dem Eingang der Effekteinheit und den Ausgang des Effektknotens mit der io-Einheit verbunden Eingabebereich des Ausgabeelements Aber jetzt muss ich eine Analyse basierend auf den verarbeiteten Audio-Samples durchführen, also brauche ich direkten Zugriff auf den Puffer. Das bedeutet (und korrigiere mich bitte, wenn ich falsch liege) Ich kann AUGraphConnectNodeInput nicht mehr verwenden, um die Verbindung zwischen dem Ausgang des Effektknotens und dem Ausgabeelement der io-Einheit herzustellen, und muss eine Render-Callback-Funktion für das Ausgabeelement der io-Einheit anhängen. damit ich auf den Puffer zugreifen kann, wenn die Lautsprecher neue Samples benötigen. Ich habe es getan, aber ich bekomme einen -50 Fehler, wenn ich die Funktion im Render-Callback aufrufen. Ich glaube, ich habe einen Fall von ASBDs Mismatch zwischen den beiden Audio-Einheiten, da ich im Render-Callback nichts dagegen tun (und die AUGraph hat sich schon darum gekümmert). Hier ist der Code:Fehler beim Schreiben einer Remote-I/O-Render-Callback-Funktion

AudioController.h:

@interface AudioController : NSObject 
{ 
    AUGraph mGraph; 
    AudioUnit mEffects; 
    AudioUnit ioUnit; 
} 

@property (readonly, nonatomic) AudioUnit mEffects; 
@property (readonly, nonatomic) AudioUnit ioUnit; 

-(void)initializeAUGraph; 
-(void)startAUGraph; 
-(void)stopAUGraph; 

@end 

AudioController.mm:

@implementation AudioController 

… 

static OSStatus renderInput(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) 
{ 
    AudioController *THIS = (__bridge AudioController*)inRefCon; 

    AudioBuffer buffer; 

    AudioStreamBasicDescription fxOutputASBD; 
    UInt32 fxOutputASBDSize = sizeof(fxOutputASBD); 
    AudioUnitGetProperty([THIS mEffects], kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &fxOutputASBD, &fxOutputASBDSize); 

    buffer.mDataByteSize = inNumberFrames * fxOutputASBD.mBytesPerFrame; 
    buffer.mNumberChannels = fxOutputASBD.mChannelsPerFrame; 
    buffer.mData = malloc(buffer.mDataByteSize); 

    AudioBufferList bufferList; 
    bufferList.mNumberBuffers = 1; 
    bufferList.mBuffers[0] = buffer; 

    //TODO prender ARM y solucionar problema de memoria 

    OSStatus result = AudioUnitRender([THIS mEffects], ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList); 
    [THIS hasError:result:__FILE__:__LINE__]; 

    memcpy(ioData, buffer.mData, buffer.mDataByteSize); 

    return noErr; 
} 


- (void)initializeAUGraph 
{ 
    OSStatus result = noErr; 

    // create a new AUGraph 
    result = NewAUGraph(&mGraph); 

    AUNode outputNode; 
    AUNode effectsNode; 

    AudioComponentDescription effects_desc; 
    effects_desc.componentType = kAudioUnitType_Effect; 
    effects_desc.componentSubType = kAudioUnitSubType_LowPassFilter; 
    effects_desc.componentFlags = 0; 
    effects_desc.componentFlagsMask = 0; 
    effects_desc.componentManufacturer = kAudioUnitManufacturer_Apple; 

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

    // Add nodes to the graph to hold the AudioUnits 
    result = AUGraphAddNode(mGraph, &output_desc, &outputNode); 
    [self hasError:result:__FILE__:__LINE__]; 
    result = AUGraphAddNode(mGraph, &effects_desc, &effectsNode); 
    [self hasError:result:__FILE__:__LINE__]; 

    // Connect the effect node's output to the output node's input 
    // This is no longer the case, as I need to access the buffer 
    // result = AUGraphConnectNodeInput(mGraph, effectsNode, 0, outputNode, 0); 
    [self hasError:result:__FILE__:__LINE__]; 

    // Connect the output node's input scope's output to the effectsNode input 
    result = AUGraphConnectNodeInput(mGraph, outputNode, 1, effectsNode, 0); 
    [self hasError:result:__FILE__:__LINE__]; 

    // open the graph AudioUnits 
    result = AUGraphOpen(mGraph); 
    [self hasError:result:__FILE__:__LINE__]; 

    // Get a link to the effect AU 
    result = AUGraphNodeInfo(mGraph, effectsNode, NULL, &mEffects); 
    [self hasError:result:__FILE__:__LINE__]; 

    // Same for io unit 
    result = AUGraphNodeInfo(mGraph, outputNode, NULL, &ioUnit); 
    [self hasError:result:__FILE__:__LINE__]; 

    // Enable input on io unit 
    UInt32 flag = 1; 
    result = AudioUnitSetProperty(ioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &flag, sizeof(flag)); 
    [self hasError:result:__FILE__:__LINE__]; 

    // Setup render callback struct 
    AURenderCallbackStruct renderCallbackStruct; 
    renderCallbackStruct.inputProc = &renderInput; 
    renderCallbackStruct.inputProcRefCon = (__bridge void*)self; 

    // Set a callback for the specified node's specified input 
    result = AUGraphSetNodeInputCallback(mGraph, outputNode, 0, &renderCallbackStruct); 
    [self hasError:result:__FILE__:__LINE__]; 

    // Get fx unit's input current stream format... 
    AudioStreamBasicDescription fxInputASBD; 
    UInt32 sizeOfASBD = sizeof(AudioStreamBasicDescription); 

    result = AudioUnitGetProperty(mEffects, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &fxInputASBD, &sizeOfASBD); 
    [self hasError:result:__FILE__:__LINE__]; 

    // ...and set it on the io unit's input scope's output 
    result = AudioUnitSetProperty(ioUnit, 
            kAudioUnitProperty_StreamFormat, 
            kAudioUnitScope_Output, 
            1, 
            &fxInputASBD, 
            sizeof(fxInputASBD)); 
    [self hasError:result:__FILE__:__LINE__]; 

    // Set fx unit's output sample rate, just in case 
    Float64 sampleRate = 44100.0; 

    result = AudioUnitSetProperty(mEffects, 
            kAudioUnitProperty_SampleRate, 
            kAudioUnitScope_Output, 
            0, 
            &sampleRate, 
            sizeof(sampleRate)); 
    [self hasError:result:__FILE__:__LINE__]; 

    // Once everything is set up call initialize to validate connections 
    result = AUGraphInitialize(mGraph); 
    [self hasError:result:__FILE__:__LINE__]; 
} 

@end 

Wie ich schon sagte, ich bin auf dem AudioUnitRender Anruf einen -50 Fehler bekommen, und Ich finde wenig bis gar keine Dokumentation darüber.

Jede Hilfe wird sehr geschätzt.

Danke an Tim Bolstad (http://timbolstad.com/2010/03/14/core-audio-getting-started/) für ein hervorragendes Startpunkt-Tutorial.

Antwort

0

Es gibt einfachere Arbeitsbeispiele für die Verwendung von RemoteIO für die Wiedergabe von Audiopuffern. Beginnen Sie vielleicht mit einem der ersten und nicht mit einem Graphen.

0

Überprüfen Sie, ob Sie tatsächlich alle erforderlichen Verbindungen herstellen. Es sieht so aus, als würden Sie fast alles Notwendige initialisieren, aber wenn Sie nur Audio durchreichen möchten, brauchen Sie die Render-Callback-Funktion nicht.

Nun, wenn Sie den Filter machen wollen, brauchen Sie vielleicht einen, aber stellen Sie sicher, dass Sie die Komponenten tatsächlich richtig miteinander verbinden.

Hier ist ein Ausschnitt aus einer App arbeite ich an:

AUGraphConnectNodeInput(graph, outputNode, kInputBus, mixerNode, kInputBus); 
AUGraphConnectNodeInput(graph, mixerNode, kOutputBus, outputNode, kOutputBus); 

Diese die Eingabe von der RemoteIO Einheit zu einer Mehrkanal-Mixer-Einheit, dann verbindet den Ausgang aus dem Mischer auf den RemoteIO Ausgabe an die verbinden Redner.

0

Es sieht für mich so aus, als würden Sie das falsche Audiogerät an übergeben. Ich denke, Sie müssen ioUnit statt mEffects übergeben. Überprüfen Sie in jedem Fall alle Parameter, die Sie übergeben, an . Wenn ich -50 zurücksehe, liegt das daran, dass ich einen von ihnen verpfuscht habe.

Verwandte Themen