因为用audioqueue的录音播放,或者用audioqueue录音,openal播放都有延迟。
然后用底层些的audio unit,果然延迟问题就好很多了,至少一边录一边播的问题可以很好的解决。。有不少audio unit的三方库,暂时没去细研究,查了点,自己修改了下。需要在进行录音的时候和播放单开线程。。之前有问题没明白,卡了一天突然明白了。。。直接上代码来得方便。。。多余的代码和变量也不在进行删除修改了,也为自己以后看吧
.h文件
#import
#import
#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