ifish/Ifish/controllers/IfishYooseeFile/IfishYooseeHelper/IfishYooseeP2PCore/PAIOUnit.m

495 lines
15 KiB
Objective-C

//
// PAIOUnit.m
// cuNewVersion
//
// Created by Jie on 13-11-15.
// Copyright (c) 2013年 Passol. All rights reserved.
//
#import "PAIOUnit.h"
#import <AudioUnit/AudioUnit.h>
#import "AppDelegate.h"
//什么含义
#define kOutputBus 0
#define kInputBus 1
#define pi 3.14159
@interface PAIOUnit ()
{
AudioUnit _audioUnit;
AudioBufferList *_m_inBufferList;
Float64 recordSampleRate;
AudioStreamBasicDescription audioFormat;
}
@property (readonly) AudioUnit audioUnit;
@property (readonly) AudioBufferList * m_inBufferList;
@end
@implementation PAIOUnit
@synthesize audioUnit = _audioUnit;
@synthesize m_inBufferList = _m_inBufferList;
#define checkstatus(x) checkStatusWithLineNumber(__LINE__, x)
void checkStatusWithLineNumber(int lineNumber, OSStatus status) {
if (status != 0) NSLog(@"An error occurred on line %d: %d", lineNumber, (int)status);
}
#pragma mark - IO CallBack
void interruptionListenerCallback (void *inUserData, UInt32 interruptionState)
{
//其实作用不大
if ([[PAIOUnit sharedUnit] isRunning]) {
if (interruptionState == kAudioSessionBeginInterruption) {
//hungup
}
}
}
void audioRouteChangeCallback(void*inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void*inData) {
CFDictionaryRef routeChangeDictionary = inData;
CFNumberRef routeChangeReasonRef = CFDictionaryGetValue(routeChangeDictionary, CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
SInt32 routeChangeReason;
CFNumberGetValue(routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason);
switch (routeChangeReason) {
case kAudioSessionRouteChangeReason_OldDeviceUnavailable:
NSLog(@"AudioSessionRouteChange : Old Audio Device Unavailable");
break;
case kAudioSessionRouteChangeReason_NewDeviceAvailable:
NSLog(@"AudioSessionRouteChange : New Audio Device Available");
break;
case kAudioSessionRouteChangeReason_Override:
NSLog(@"AudioSessionRouteChange : Audio Device Route Change Override");
break;
default:
if (routeChangeReason == kAudioSessionRouteChangeReason_NoSuitableRouteForCategory) {
NSLog(@"AudioSessionRouteChange : No Suitable Route For Category");
}
break;
}
[[PAIOUnit sharedUnit] checkSpeaker];
}
static OSStatus recordingCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
if ([[PAIOUnit sharedUnit] isRunning]) {
@autoreleasepool {
[PAIOUnit sharedUnit].m_inBufferList->mBuffers[0].mDataByteSize = inNumberFrames*sizeof(UInt16);
OSStatus status;
status = AudioUnitRender([PAIOUnit sharedUnit].audioUnit,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
[PAIOUnit sharedUnit].m_inBufferList);
//https://www.osstatus.com/search/results?platform=all&framework=all&search=-10867
//http://www.cnblogs.com/try2do-neo/p/3278459.html
checkstatus(status);
if (status != noErr) {
NSLog(@"卧槽发现AudioUnitRender error: %d", (int)status);
//AudioUnitInitialize([PAIOUnit sharedUnit].audioUnit);
return status;
}
[[PAIOUnit sharedUnit] processAudio:[[PAIOUnit sharedUnit] m_inBufferList]];
}
}
return noErr;
}
static OSStatus playbackCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
BOOL isGetAudioOk = NO;
if ([[P2PClient sharedClient]p2pCallState] == P2PCALL_STATUS_READY_P2P)
{
isGetAudioOk = fgGetAudioDataToPlay(ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize );
}
if ([[PAIOUnit sharedUnit] muteAudio] || !isGetAudioOk /*|| (NO == [[PAIOUnit sharedUnit] silentAudio])*/)
{
memset(ioData->mBuffers[0].mData, 0,ioData->mBuffers[0].mDataByteSize );
}
return noErr;
}
#pragma mark init method
- (BOOL)intialiseAudio
{
AudioComponentDescription audioDesc;
audioDesc.componentType = kAudioUnitType_Output;
audioDesc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
audioDesc.componentFlags = 0;
audioDesc.componentFlagsMask = 0;
audioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
OSStatus status = noErr;
AudioComponent inputComponent = AudioComponentFindNext(NULL, &audioDesc);
status = AudioComponentInstanceNew(inputComponent, &_audioUnit);
checkstatus(status);
if (status) {
return NO;
}
UInt32 flag = 1;
// Enable IO for recording
status = AudioUnitSetProperty(_audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
kInputBus,
&flag,
sizeof(flag));
checkstatus(status);
// Enable IO for playback
status = AudioUnitSetProperty(_audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
kOutputBus,
&flag,
sizeof(flag));
checkstatus(status);
// Describe format
audioFormat.mSampleRate = 8000;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
audioFormat.mBitsPerChannel = 16;//short
audioFormat.mBytesPerPacket = 2;
audioFormat.mBytesPerFrame = 2;
// Apply format
status = AudioUnitSetProperty(_audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
kInputBus,
&audioFormat,
sizeof(audioFormat));
checkstatus(status);
status = AudioUnitSetProperty(_audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kOutputBus,
&audioFormat,
sizeof(audioFormat));
checkstatus(status);
// Set input callback
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = recordingCallback;
callbackStruct.inputProcRefCon = (__bridge void *)(self);
status = AudioUnitSetProperty(_audioUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
kInputBus,
&callbackStruct,
sizeof(callbackStruct));
// Set output callback
callbackStruct.inputProc = playbackCallback;
callbackStruct.inputProcRefCon = (__bridge void *)(self);
status = AudioUnitSetProperty(_audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
kOutputBus,
&callbackStruct,
sizeof(callbackStruct));
checkstatus(status);
// Disable buffer allocation for the recorder (optional - do this if we want to pass in our own)
flag = 0;
status = AudioUnitSetProperty(_audioUnit,
kAudioUnitProperty_ShouldAllocateBuffer,
kAudioUnitScope_Output,
kInputBus,
&flag,
sizeof(flag));
checkstatus(status);
AudioUnitSetProperty(_audioUnit,
kAUVoiceIOProperty_VoiceProcessingEnableAGC,
kAudioUnitScope_Global,
0,
&flag,
sizeof(flag));
if (status) {
return NO;
}
_m_inBufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer));
_m_inBufferList->mNumberBuffers = 1;
_m_inBufferList->mBuffers[0].mNumberChannels = 1;
_m_inBufferList->mBuffers[0].mDataByteSize = 320 * 2;
_m_inBufferList->mBuffers[0].mData = malloc( 320 * 2 );
return YES;
}
#pragma mark AUDIOUNIT METHOD
+ (PAIOUnit *)sharedUnit
{
static PAIOUnit *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[PAIOUnit alloc] initWithAudioUnit];
sharedInstance.isRunning = NO;
sharedInstance.silentAudio = YES;
});
return sharedInstance;
}
- (id)initWithAudioUnit
{
if (self = [super init]) {
return self;
}
return nil;
}
- (BOOL)startAudioWithCallType:(P2PCallType )type
{
BOOL isOK = [self initAudioSession];
if (!isOK) {
return NO;
NSLog(@"initAudioSession=NO");
}
NSLog(@"initAudioSession=YES");
isOK = [self intialiseAudio];
if (!isOK) {
return NO;
NSLog(@"intialiseAudio=NO");
}
NSLog(@"intialiseAudio=YES");
OSStatus status;
self.isRunning = YES;
self.callType = type;
NSLog(@"*********");
status = AudioUnitInitialize(_audioUnit);
checkstatus(status);
NSLog(@"status%d***",(int)status);
if (status) {
return NO;
}
status = AudioOutputUnitStart(_audioUnit);
checkstatus(status);
NSLog(@"status%d***",(int)status);
if (status) {
return NO;
}
NSLog(@"*********");
return YES;
}
- (BOOL)stopAudio
{
OSStatus status = AudioOutputUnitStop(_audioUnit);
checkstatus(status);
if (status) {
return NO;
}
status = AudioUnitUninitialize(_audioUnit);
checkstatus(status);
if (status) {
return NO;
}
[self uninitWithAutioUnit];
self.isRunning = NO;
return YES;
}
- (BOOL)uninitWithAutioUnit
{
OSStatus status;
free(_m_inBufferList->mBuffers[0].mData);
free(_m_inBufferList);
status = AudioComponentInstanceDispose(_audioUnit);
checkstatus(status);
if (status) {
return NO;
}
[self exitAudioSession];
return YES;
}
#pragma mark Audiodata transit
- (void)processAudio:(AudioBufferList*)bufferList
{
if (self.isRunning)
{
if (self.silentAudio) {
short *myData = bufferList->mBuffers[0].mData;
for (int i = 0; i < (bufferList->mBuffers[0].mDataByteSize)/2 ; i++) {
myData[i] = 0;
}
}
else
{
if ([[P2PClient sharedClient]p2pCallState] == P2PCALL_STATUS_READY_P2P)
{
vFillAudioRawData(bufferList->mBuffers[0].mData, bufferList->mBuffers[0].mDataByteSize);
}
}
}
}
#pragma mark -
#pragma mark AudioSession Control
- (BOOL)initAudioSession
{
OSStatus error = AudioSessionInitialize(NULL, NULL, interruptionListenerCallback, NULL); // 初始化音频会话
if (error)
printf("ERROR INITIALIZING AUDIO SESSION! %d\n", (int)error);
// UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord;
UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory);
// 侦听耳机插拔事件
AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, audioRouteChangeCallback, (__bridge void *)(self));
// UInt32 routeSpeaker = kAudioSessionOverrideAudioRoute_Speaker;
// AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(routeSpeaker), &routeSpeaker);
UInt32 doSetProperty = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(doSetProperty), &doSetProperty);
Float32 duration = (float)320.0/8000.0;
double version = [[UIDevice currentDevice].systemVersion doubleValue];
if (version >= 7.0) {
if ([UIScreen mainScreen].bounds.size.height == 568.0) {
duration = (float)160.0/8000.0;
}
}
//Float32 duration = (float)kAudioBufferNumFrames / kDefaultSoundRate;
AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(duration), &duration);
Float64 hwSampleRate = 8000.0;
AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareSampleRate, sizeof(hwSampleRate), &hwSampleRate);
OSStatus status = AudioSessionSetActive(true); // 激活会话
if (status) {
DLog(@"error when open AudioSession!");
return NO;
}
return YES;
}
- (BOOL)exitAudioSession
{
AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioRouteChange, audioRouteChangeCallback, (__bridge void *)(self));
OSStatus status = AudioSessionSetActive(false); // 反激活会话
if (status) {
DLog(@"error when close AudioSession!");
return NO;
}
NSLog(@"exitAudioSession");
return YES;
}
#pragma mark - check AudioChange
- (BOOL)isHeadphone {
CFStringRef route;
UInt32 propertySize = sizeof(CFStringRef);
AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &route);
if((route == NULL) || (CFStringGetLength(route) == 0)){
// Silent Mode
DLog(@"AudioRoute: SILENT, do nothing!");
} else {
NSString* routeStr = (__bridge NSString *)route;
DLog(@"AudioRoute: %@", routeStr);
/* Known values of route:
* "Headset"
* "Headphone"
* "Speaker"
* "SpeakerAndMicrophone"
* "HeadphonesAndMicrophone"
* "HeadsetInOut"
* "ReceiverAndMicrophone"
* "Lineout"
*/
NSRange headphoneRange = [routeStr rangeOfString : @"Headphone"];
NSRange headsetRange = [routeStr rangeOfString : @"Headset"];
if (headphoneRange.location != NSNotFound) {
return YES;
} else if(headsetRange.location != NSNotFound) {
return YES;
}
}
return NO;
}
- (void)checkSpeaker
{
UInt32 audioRoute;
if (self.isHeadphone) {
audioRoute = kAudioSessionOverrideAudioRoute_None; // 默认输出
} else {
audioRoute = kAudioSessionOverrideAudioRoute_Speaker; // 扬声器输出
}
AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(UInt32), &audioRoute);
}
-(void)setSpeckState:(BOOL)state{
if ([[P2PClient sharedClient]p2pCallState] == P2PCALL_STATUS_READY_P2P) {
self.silentAudio = state;
if(self.callType==P2PCALL_TYPE_MONITOR){
if(self.silentAudio){
fgSendUserData(5, 0, NULL, 0);
}else{
fgSendUserData(5, 1, NULL, 0);
}
}
}
}
@end