3

私の仕事は、ドキュメント ディレクトリにローカルに保存されているオーディオ ファイルを再生し、そのオーディオ ファイルに Effect Audio Unit を使用してオーディオ エフェクトを適用し、新しいオーディオ ファイルをドキュメント ディレクトリに保存することです。これまでに書いたコードは次のとおりですが、機能していません。オーディオにエフェクトが適用されていません。このコードのどこが間違っているのか教えてください?? 前もって感謝します..

- (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);
 }
4

1 に答える 1

4

初期化されたオーディオユニットのインスタンスがある場合、AudioUnitRenderAudioBufferList を提供することでサウンドに効果を適用できます。

まず、Audio Unit が受け付ける形式のサウンドがあることを確認してください。プロパティを取得することで、この形式を取得できkAudioUnitProperty_StreamFormatます。

オーディオ ファイルの形式がオーディオ ユニットから取得したものと異なる場合は、ExtAudioFile を使用してオーディオを「その場で」変換できます。これを実現するkExtAudioFileProperty_ClientDataFormatには、「kAudioUnitProperty_StreamFormat」から取得したフォーマットに ExtAudioFile のプロパティを設定する必要があります。ここで、オーディオ ファイルを読み取ると、必要な形式でオーディオが取得されます。

また、kAudioUnitProperty_ShouldAllocateBufferAudio Unit のプロパティが に設定されていることを確認します1

呼び出すには、AudioUnitRender有効なAudioTimeStamp, AudioUnitRenderActionFlags( に設定できます0) とを用意する必要がありAudioBufferListます。バッファーにメモリを割り当てる必要はありません。バッファーの数とそのサイズを指定するだけで済みます。

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

今すぐ呼び出すことができますAudioUnitRender:

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

オーディオ ユニットはコールバックにフィル バッファーを要求し、効果をサウンドに適用します。その後、有効なオーディオを含むバッファーが作成されます。この場合、kAudioUnitProperty_SetRenderCallbackプロパティを有効なコールバックに設定する必要があります。

于 2012-11-20T19:26:07.843 に答える