因为用audioqueue的录音播放,或者用audioqueue录音,openal播放都有延迟。
然后用底层些的audio unit,果然延迟问题就好很多了,至少一边录一边播的问题可以很好的解决。。有不少audio unit的三方库,暂时没去细研究,查了点,自己修改了下。需要在进行录音的时候和播放单开线程。。之前有问题没明白,卡了一天突然明白了。。。直接上代码来得方便。。。多余的代码和变量也不在进行删除修改了,也为自己以后看吧
.h文件
#import <Foundation/Foundation.h> #import <AudioToolbox/AudioToolbox.h> #define kOutputBus 0 #define kInputBus 1 #define kSampleRate 8000 #define kFramesPerPacket 1 #define kChannelsPerFrame 1 #define kBitsPerChannel 16 #define BUFFER_SIZE 1024 @interface AudioController : NSObject @property (readonly) AudioComponentInstance audioUnit; @property (readonly) AudioBuffer audioBuffer; @property (strong, readwrite) NSMutableData *mIn; @property (strong, readwrite) NSMutableData *mOut; @property (strong, readwrite) NSMutableData *mAllAudioData; - (void)hasError:(int)statusCode file:(char*)file line:(int)line; - (void)processBuffer: (AudioBufferList* )audioBufferList; + (AudioController *) sharedAudioManager; -(void)clearDataArray; -(void)startAudio; @end.m文件的内容
#import "AudioController.h" static NSMutableData *mIn; static NSMutableData *mOut; static NSMutableData *mAllAudioData; static bool mIsStarted; // audio unit start static bool mSendServerStart; // send server continue loop static bool mRecServerStart; // rec server continue loop static bool mIsTele; // telephone call static OSStatus recordingCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { // the data gets rendered here AudioBuffer buffer; // a variable where we check the status OSStatus status; //This is the reference to the object who owns the callback. AudioController *audioProcessor = (__bridge AudioController* )inRefCon; /** on this point we define the number of channels, which is mono for the iphone. the number of frames is usally 512 or 1024. */ buffer.mDataByteSize = inNumberFrames * 2; // sample size buffer.mNumberChannels = 1; // one channel buffer.mData = malloc( inNumberFrames * 2 ); // buffer size // we put our buffer into a bufferlist array for rendering AudioBufferList bufferList; bufferList.mNumberBuffers = 1; bufferList.mBuffers[0] = buffer; // render input and check for error status = AudioUnitRender([audioProcessor audioUnit], ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList); // [audioProcessor hasError:status file:__FILE__ line:__LINE__]; // process the bufferlist in the audio processor [audioProcessor processBuffer: &bufferList]; // clean up the buffer free(bufferList.mBuffers[0].mData); dispatch_async(dispatch_get_global_queue(0, 0), ^{ [audioProcessor clearDataArray]; }); return noErr; } #pragma mark Playback callback static OSStatus playbackCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { long len = [mIn length]; len = len > 1024 ? 1024 : len; if (len <= 0) { return noErr; } for (int i = 0; i < ioData -> mNumberBuffers; i++) { // NSLog( @"len:%ld", len); AudioBuffer buffer = ioData -> mBuffers[i]; NSData *pcmBlock = [mIn subdataWithRange: NSMakeRange(0, len)]; UInt32 size = (UInt32)MIN(buffer.mDataByteSize, [pcmBlock length]); memcpy(buffer.mData, [pcmBlock bytes], size); [mIn replaceBytesInRange: NSMakeRange(0, size) withBytes: NULL length: 0]; buffer.mDataByteSize = size; } return noErr; } @implementation AudioController @synthesize audioUnit; @synthesize audioBuffer; /* * It's Singleton pattern * the flow is init(if there isn't existed self) -> initializeAudioConfig(set audio format, io pipe and callback functions) * -> recordingCallback -> processBuffer * -> playbackCallback */ // 封装一个单例 + (AudioController *) sharedAudioManager{ static AudioController *sharedAudioManager; @synchronized(self) { if (!sharedAudioManager) { sharedAudioManager = [[AudioController alloc] init]; } return sharedAudioManager; } } - (AudioController* )init { self = [super init]; if (self) { [self initializeAudioConfig]; mIn = [[NSMutableData alloc] init]; mOut = [[NSMutableData alloc] init]; mAllAudioData = [[NSMutableData alloc] init]; mIsStarted = false; mSendServerStart = false; mRecServerStart = false; mIsTele = false; } return self; } - (void)initializeAudioConfig { OSStatus status; AudioComponentDescription desc; desc.componentType = kAudioUnitType_Output; // we want to ouput desc.componentSubType = kAudioUnitSubType_RemoteIO; // we want in and ouput desc.componentFlags = 0; // must be zero desc.componentFlagsMask = 0; // must be zero desc.componentManufacturer = kAudioUnitManufacturer_Apple; // select provider AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc); status = AudioComponentInstanceNew(inputComponent, &audioUnit); [self hasError:status file:__FILE__ line:__LINE__]; // define that we want record io on the input bus UInt32 flag = 1; status = AudioUnitSetProperty(audioUnit,kAudioOutputUnitProperty_EnableIO, // use io kAudioUnitScope_Input, // scope to input kInputBus, // select input bus (1) &flag, // set flag sizeof(flag)); [self hasError:status file:__FILE__ line:__LINE__]; // define that we want play on io on the output bus status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, // use io kAudioUnitScope_Output, // scope to output kOutputBus, // select output bus (0) &flag, // set flag sizeof(flag)); [self hasError:status file:__FILE__ line:__LINE__]; // specifie our format on which we want to work. AudioStreamBasicDescription audioFormat; audioFormat.mSampleRate = kSampleRate; audioFormat.mFormatID = kAudioFormatLinearPCM; audioFormat.mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; audioFormat.mFramesPerPacket = kFramesPerPacket; audioFormat.mChannelsPerFrame = kChannelsPerFrame; audioFormat.mBitsPerChannel = kBitsPerChannel; audioFormat.mBytesPerPacket = kBitsPerChannel * kChannelsPerFrame * kFramesPerPacket / 8; audioFormat.mBytesPerFrame = kBitsPerChannel * kChannelsPerFrame / 8; // set the format on the output stream status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &audioFormat, sizeof(audioFormat)); [self hasError:status file:__FILE__ line:__LINE__]; // set the format on the input stream status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &audioFormat, sizeof(audioFormat)); [self hasError:status file:__FILE__ line:__LINE__]; /** We need to define a callback structure which holds a pointer to the recordingCallback and a reference to the audio processor object */ AURenderCallbackStruct callbackStruct; // set recording callback struct callbackStruct.inputProc = recordingCallback; // recordingCallback pointer callbackStruct.inputProcRefCon = (__bridge void * _Nullable)(self); // set input callback to recording callback on the input bus status = AudioUnitSetProperty(audioUnit,kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callbackStruct, sizeof(callbackStruct)); [self hasError:status file:__FILE__ line:__LINE__]; // set playback callback struct callbackStruct.inputProc = playbackCallback; callbackStruct.inputProcRefCon = (__bridge void * _Nullable)(self); // set playbackCallback as callback on our renderer for the output bus status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct)); [self hasError:status file:__FILE__ line:__LINE__]; // reset flag to 0 flag = 0; /* we need to tell the audio unit to allocate the render buffer, that we can directly write into it. */ status = AudioUnitSetProperty(audioUnit,kAudioUnitProperty_ShouldAllocateBuffer, kAudioUnitScope_Output, kInputBus, &flag, sizeof(flag)); /* we set the number of channels to mono and allocate our block size to 1024 bytes. kiki: I don't know where the size 1024 bytes comes from... */ audioBuffer.mNumberChannels = kChannelsPerFrame; audioBuffer.mDataByteSize = 512 * 2; audioBuffer.mData = malloc( 512 * 2 ); // Initialize the Audio Unit and cross fingers =) status = AudioUnitInitialize(audioUnit); [self hasError:status file:__FILE__ line:__LINE__]; } - (void)processBuffer: (AudioBufferList* )audioBufferList { AudioBuffer sourceBuffer = audioBufferList -> mBuffers[0]; // we check here if the input data byte size has changed if (audioBuffer.mDataByteSize != sourceBuffer.mDataByteSize) { // clear old buffer free(audioBuffer.mData); // assing new byte size and allocate them on mData audioBuffer.mDataByteSize = sourceBuffer.mDataByteSize; audioBuffer.mData = malloc(sourceBuffer.mDataByteSize); } // copy incoming audio data to the audio buffer memcpy(audioBuffer.mData, audioBufferList -> mBuffers[0].mData, audioBufferList -> mBuffers[0].mDataByteSize); NSData *pcmBlock = [NSData dataWithBytes:sourceBuffer.mData length:sourceBuffer.mDataByteSize]; [mOut appendData: pcmBlock]; } -(void)clearDataArray { if ([mOut length] <= 0) { return; } // [mAllAudioData appendBytes:mOut.bytes length:mOut.length]; [mIn appendBytes:mOut.bytes length:mOut.length]; [mOut replaceBytesInRange: NSMakeRange(0, mOut.length) withBytes: NULL length: 0]; } - (void)start { if (mIsStarted) { // NSLog( @"-- already start --"); return; } // NSLog( @"-- start --"); mIsStarted = true; [mIn replaceBytesInRange: NSMakeRange(0, [mIn length]) withBytes: NULL length: 0]; [mOut replaceBytesInRange: NSMakeRange(0, [mOut length]) withBytes: NULL length: 0]; OSStatus status = AudioOutputUnitStart(audioUnit); [self hasError:status file:__FILE__ line:__LINE__]; } - (void)stop { NSLog( @"-- stop --"); OSStatus status = AudioOutputUnitStop(audioUnit); [self hasError:status file:__FILE__ line:__LINE__]; mIsStarted = false; [mIn replaceBytesInRange: NSMakeRange(0, [mIn length]) withBytes: NULL length: 0]; [mOut replaceBytesInRange: NSMakeRange(0, [mOut length]) withBytes: NULL length: 0]; } #pragma mark Error handling - (void)hasError:(int)statusCode file:(char*)file line:(int)line { if (statusCode) { NSLog(@"Error Code responded %d in file %s on line %d", statusCode, file, line); exit(-1); } } -(void)startAudio { [self stop]; [self start]; } @end