Cómo configurar la unidad de audio de efectos en iOS

Mi tarea es reproducir un file de audio que se guarda localmente en el directory de documentos, aplicar el efecto de audio en ese file de audio usando la unidad de efectos de audio y save un nuevo file de audio en el directory de documentos con ese efecto. Aquí está mi código que he escrito hasta ahora, pero no funciona. Los efectos no se aplican en el audio. Por favor, sugerirme lo que está mal en este código? Gracias por adelantado..

- (void) setUpAudioUnits { OSStatus setupErr = noErr; // describe unit AudioComponentDescription audioCompDesc; audioCompDesc.componentType = kAudioUnitType_Output; audioCompDesc.componentSubType = kAudioUnitSubType_RemoteIO; audioCompDesc.componentManufacturer = kAudioUnitManufacturer_Apple; audioCompDesc.componentFlags = 0; audioCompDesc.componentFlagsMask = 0; // get rio unit from audio component manager AudioComponent rioComponent = AudioComponentFindNext(NULL, &audioCompDesc); setupErr = AudioComponentInstanceNew(rioComponent, &remoteIOUnit); NSAssert (setupErr == noErr, @"Couldn't get RIO unit instance"); // set up the rio unit for playback UInt32 oneFlag = 1; AudioUnitElement outputElement = 0; setupErr = AudioUnitSetProperty (remoteIOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, outputElement, &oneFlag, sizeof(oneFlag)); NSAssert (setupErr == noErr, @"Couldn't enable RIO output"); // enable rio input AudioUnitElement inputElement = 1; // setup an asbd in the iphone canonical format AudioStreamBasicDescription myASBD; memset (&myASBD, 0, sizeof (myASBD)); // myASBD.mSampleRate = 44100; myASBD.mSampleRate = hardwareSampleRate; myASBD.mFormatID = kAudioFormatLinearPCM; myASBD.mFormatFlags = kAudioFormatFlagsCanonical; myASBD.mBytesPerPacket = 4; myASBD.mFramesPerPacket = 1; myASBD.mBytesPerFrame = 4; myASBD.mChannelsPerFrame = 2; myASBD.mBitsPerChannel = 16; /* // set format for output (bus 0) on rio's input scope */ setupErr = AudioUnitSetProperty (remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, outputElement, &myASBD, sizeof (myASBD)); NSAssert (setupErr == noErr, @"Couldn't set ASBD for RIO on input scope / bus 0"); // song must be an LPCM file, preferably in caf container // to convert, use /usr/bin/afconvert, like this: // /usr/bin/afconvert --data LEI16 Girlfriend.m4a song.caf // read in the entire audio file (NOT recommended) // better to use a ring buffer: thread or timer fills, render callback drains NSURL *songURL = [NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource: @"song" ofType: @"caf"]]; AudioFileID songFile; setupErr = AudioFileOpenURL((CFURLRef) songURL, kAudioFileReadPermission, 0, &songFile); NSAssert (setupErr == noErr, @"Couldn't open audio file"); UInt64 audioDataByteCount; UInt32 audioDataByteCountSize = sizeof (audioDataByteCount); setupErr = AudioFileGetProperty(songFile, kAudioFilePropertyAudioDataByteCount, &audioDataByteCountSize, &audioDataByteCount); NSAssert (setupErr == noErr, @"Couldn't get size property"); musicPlaybackState.audioData = malloc (audioDataByteCount); musicPlaybackState.audioDataByteCount = audioDataByteCount; musicPlaybackState.samplePtr = musicPlaybackState.audioData; NSLog (@"reading %qu bytes from file", audioDataByteCount); UInt32 bytesRead = audioDataByteCount; setupErr = AudioFileReadBytes(songFile, false, 0, &bytesRead, musicPlaybackState.audioData); NSAssert (setupErr == noErr, @"Couldn't read audio data"); NSLog (@"read %d bytes from file", bytesRead); AudioStreamBasicDescription fileASBD; UInt32 asbdSize = sizeof (fileASBD); setupErr = AudioFileGetProperty(songFile, kAudioFilePropertyDataFormat, &asbdSize, &fileASBD); NSAssert (setupErr == noErr, @"Couldn't get file asbd"); ExtAudioFileCreateWithURL(outputFileURL, kAudioFileCAFType, &fileASBD, nil, kAudioFileFlags_EraseFile, &musicPlaybackState.extAudioFile); // get the mixer unit AudioComponentDescription mixerDesc; mixerDesc.componentType = kAudioUnitType_Effect; mixerDesc.componentSubType = kAudioUnitSubType_Delay; mixerDesc.componentManufacturer = kAudioUnitManufacturer_Apple; mixerDesc.componentFlags = 0; mixerDesc.componentFlagsMask = 0; // get mixer unit from audio component manager AudioComponent mixerComponent = AudioComponentFindNext(NULL, &mixerDesc); setupErr = AudioComponentInstanceNew(mixerComponent, &mixerUnit); NSAssert (setupErr == noErr, @"Couldn't get mixer unit instance"); // set up connections and callbacks // connect mixer bus 0 input to robot voice render callback effectState.rioUnit = remoteIOUnit; effectState.sineFrequency = 23; effectState.sinePhase = 0; effectState.asbd = myASBD; // connect mixer bus 1 input to music player callback AURenderCallbackStruct musicPlayerCallbackStruct; musicPlayerCallbackStruct.inputProc = MusicPlayerCallback; // callback function musicPlayerCallbackStruct.inputProcRefCon = &musicPlaybackState; setupErr = AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, outputElement, &musicPlayerCallbackStruct, sizeof (musicPlayerCallbackStruct)); NSAssert (setupErr == noErr, @"Couldn't set mixer render callback on bus 1"); // direct connect mixer to output AudioUnitConnection connection; connection.sourceAudioUnit = mixerUnit; connection.sourceOutputNumber = outputElement; connection.destInputNumber = outputElement; setupErr = AudioUnitSetProperty(remoteIOUnit, kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, outputElement, &connection, sizeof (connection)); NSAssert (setupErr == noErr, @"Couldn't set mixer-to-RIO connection"); setupErr = AudioUnitInitialize(mixerUnit); NSAssert (setupErr == noErr, @"Couldn't initialize mixer unit"); setupErr = AudioUnitInitialize(remoteIOUnit); NSAssert (setupErr == noErr, @"Couldn't initialize RIO unit"); setupErr = AudioOutputUnitStart (remoteIOUnit); } 

Cuando tenga una instancia de unidad de audio inicializada, puede aplicar el efecto al sonido utilizando AudioUnitRender al proporcionarle AudioBufferList.

En primer lugar, asegúrese de tener un sonido en el formatting que aceptó mediante la unidad de audio. Puede get este formatting obteniendo la propiedad kAudioUnitProperty_StreamFormat .

Si su file de audio tiene un formatting diferente al que obtuvo de la unidad de audio, puede convertir el audio "sobre la marcha" utilizando ExtAudioFile. Para lograr esto, debe establecer la propiedad kExtAudioFileProperty_ClientDataFormat en ExtAudioFile al formatting que obtuvo de 'kAudioUnitProperty_StreamFormat'. Ahora, cuando leerá el file de audio, obtendrá el audio en el formatting necesario.

Además, asegúrese de que la propiedad kAudioUnitProperty_ShouldAllocateBuffer de la unidad de audio esté configurada en 1 .

Para llamar a AudioUnitRender debe preparar un AudioTimeStamp válido, AudioUnitRenderActionFlags (se puede configurar en 0 ) y AudioBufferList . No necesita asignar memory para los búferes, solo tiene que proporcionar el número de búferes y su tamaño.

 AudioBufferList *buffer = malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer)); buffer->mNumberBuffers = 2; // at least 2 buffers buffer->mBuffers[0].mDataByteSize = ...; // size of one buffer buffer->mBuffers[1].mDataByteSize = ...; AudioUnitRenderActionFlags flags = 0; AudioTimeStamp timeStamp; memset(&timeStamp, 0, sizeof(AudioTimeStamp)); timeStamp.mFlags = kAudioTimeStampSampleTimeValid; UInt32 frames = ...; // number of frames in buffer AudioUnit unit = ...; // your Delay unit 

Ahora puedes llamar a AudioUnitRender :

 AudioUnitRender(unit, &flags, &timeStamp, 0, frames, buffer); 

La unidad de audio le pedirá a la callback que rellene los búferes y aplique el efecto al sonido, luego tendrá búferes con audio válido. En este caso, debe establecer la propiedad kAudioUnitProperty_SetRenderCallback a una callback válida.