#define kOutputBus 0
#define kInputBus 1
#define kSampleRate 8000
#define kFramesPerPacket 1
#define kChannelsPerFrame 1
#define kBitsPerChannel 16
#define SEND_PORT 9533
#define REC_PORT 9534
#define MAX_CLIENT_COUNT 20
#define BUFFER_SIZE 1024
@interface RemoteAudioProcessor : NSObject
@property (readonly) AudioComponentInstance audioUnit;
@property (readonly) AudioBuffer audioBuffer;
@property (strong, readwrite) NSMutableData *mIn;
@property (strong, readwrite) NSMutableData *mOut;
- (void)hasError:(int)statusCode file:(char*)file line:(int)line;
- (void)processBuffer: (AudioBufferList* )audioBufferList;
@end
// RemoteAudioProcessor.m
// AOIAudioSimulator
#import "RemoteAudioProcessor.h"
static NSMutableData *mIn;
static NSMutableData *mOut;
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.
RemoteAudioProcessor *audioProcessor = (__bridge RemoteAudioProcessor* )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);
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;
}
// to be changed
// RemoteAudioProcessor *audioProcessor = (__bridge RemoteAudioProcessor* )inRefCon;
for (int i = 0; i < ioData -> mNumberBuffers; i++) {
ASLog( @"len:%ld", len);
AudioBuffer buffer = ioData -> mBuffers[i];
NSData *pcmBlock = [mIn subdataWithRange: NSMakeRange(0, len)];
UInt32 size = (UInt32)MIN(buffer.mDataByteSize, [pcmBlock length]);// ? 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 RemoteAudioProcessor
@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
*/
- (RemoteAudioProcessor* )init {
self = [super init];
if (self) {
[self initializeAudioConfig];
mIn = [[NSMutableData alloc] init];
mOut = [[NSMutableData alloc] init];
mIsStarted = false;
mSendServerStart = false;
mRecServerStart = false;
mIsTele = false;
[NSThread detachNewThreadSelector:@selector(initSendSocketServer)
toTarget:self
withObject:nil];
[NSThread detachNewThreadSelector:@selector(initRecSocketServer)
toTarget:self
withObject:nil];
}
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)start {
if (mIsStarted) {
ASLog( @"-- already start --");
return;
}
ASLog( @"-- 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 {
ASLog( @"-- 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) {
ASLog(@"Error Code responded %d in file %s on line %d", statusCode, file, line);
exit(-1);
}
}
#pragma mark Socket Implementation
- (int)initSendSocketServer {
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(SEND_PORT);
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
ASLog( @"Create send socket failed");
return -1;
}
int yes = 1;
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
[self closeSocket:server_socket error:@"Send Server bind failed"];
}
if (listen(server_socket, MAX_CLIENT_COUNT)) {
[self closeSocket:server_socket error:@"Send Server listen failed"];
}
mSendServerStart = true;
while (mSendServerStart) {
ASLog( @"wait send client");
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);
if (client_socket < 0) {
ASLog( @"Create send client socket failed");
break;
}
[NSThread detachNewThreadSelector:@selector(sendClientRun:)
toTarget:self
withObject:[NSNumber numberWithInt:client_socket]];
}
close(server_socket);
return 0;
}
- (int)initRecSocketServer {
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(REC_PORT);
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
ASLog( @"Create rec socket failed");
return -1;
}
int yes = 1;
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
[self closeSocket:server_socket error:@"Rec Server bind failed"];
}
if (listen(server_socket, MAX_CLIENT_COUNT)) {
[self closeSocket:server_socket error:@"Rec Server listen failed"];
}
mSendServerStart = true;
while (mSendServerStart) {
ASLog( @"wait rec client");
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);
if (client_socket < 0) {
ASLog( @"Create rec client socket failed");
break;
}
[NSThread detachNewThreadSelector:@selector(recClientRun:)
toTarget:self
withObject:[NSNumber numberWithInt:client_socket]];
}
close(server_socket);
return 0;
}
- (int)closeSocket: (int)socket
error: (NSString *)errorMsg {
if (errorMsg) {
ASLog(@"%@", errorMsg);
close(socket);
return -1;
} else {
close(socket);
return 0;
}
}
- (void)stopSocketServer {
mSendServerStart = false;
mRecServerStart = false;
}
- (void)sendClientRun: (NSNumber *)socket {
int client_socket = [socket intValue];
[self stop];
[self start];
ASLog( @"send client connection with %d", client_socket);
bool isClientAlive = true;
while (isClientAlive) {
if ([mOut length] <= 0) {
continue;
}
ssize_t res = send(client_socket, [mOut bytes], [mOut length], 0);
if (res > 0) {
[mOut replaceBytesInRange: NSMakeRange(0, res) withBytes: NULL length: 0];
} else {
isClientAlive = false;
[self closeSocket: client_socket error: @"send socket died"];
}
}
}
- (void)recClientRun: (NSNumber *)socket {
char buffer[BUFFER_SIZE];
int client_socket = [socket intValue];
ASLog( @"rec client connection with %d", client_socket);
bool isClientAlive = true;
while (isClientAlive) {
ssize_t res = recv(client_socket, buffer, BUFFER_SIZE, 0);
if (res > 0) {
NSData* pcmBlock = [[NSData alloc] initWithBytes: buffer length: res];
[mIn appendBytes: (__bridge const void * _Nonnull)(pcmBlock) length: res];
ASLog( @"rec:%zd", res);
} else {
isClientAlive = false;
[self closeSocket: client_socket error: @"rec socket died"];
}
}
}
@end
网友评论