1
0
mirror of https://github.com/danog/libtgvoip.git synced 2024-12-02 09:37:52 +01:00
libtgvoip/os/darwin/AudioUnitIO.cpp

239 lines
7.4 KiB
C++
Raw Normal View History

2017-02-02 17:24:40 +01:00
//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//
#include <stdio.h>
#include <AudioToolbox/AudioToolbox.h>
#include "AudioUnitIO.h"
#include "AudioInputAudioUnit.h"
#include "AudioOutputAudioUnit.h"
#include "../../logging.h"
2017-03-30 16:06:59 +02:00
#include "../../VoIPController.h"
2017-02-02 17:24:40 +01:00
#define CHECK_AU_ERROR(res, msg) if(res!=noErr){ LOGE(msg": OSStatus=%d", (int)res); return; }
#define BUFFER_SIZE 960 // 20 ms
#define kOutputBus 0
#define kInputBus 1
using namespace tgvoip;
2017-02-02 17:24:40 +01:00
int CAudioUnitIO::refCount=0;
CAudioUnitIO* CAudioUnitIO::sharedInstance=NULL;
2017-03-30 16:06:59 +02:00
bool CAudioUnitIO::haveAudioSession=false;
2017-02-02 17:24:40 +01:00
CAudioUnitIO::CAudioUnitIO(){
2017-03-30 16:06:59 +02:00
input=NULL;
output=NULL;
configured=false;
inputEnabled=false;
outputEnabled=false;
inBufferList.mBuffers[0].mData=malloc(10240);
inBufferList.mBuffers[0].mDataByteSize=10240;
inBufferList.mNumberBuffers=1;
if(haveAudioSession)
ProcessAudioSessionAcquired();
}
CAudioUnitIO::~CAudioUnitIO(){
if(runFakeIO){
runFakeIO=false;
join_thread(fakeIOThread);
}
AudioOutputUnitStop(unit);
AudioUnitUninitialize(unit);
AudioComponentInstanceDispose(unit);
free(inBufferList.mBuffers[0].mData);
haveAudioSession=false;
}
void CAudioUnitIO::ProcessAudioSessionAcquired(){
2017-02-02 17:24:40 +01:00
OSStatus status;
AudioComponentDescription desc;
AudioComponent inputComponent;
desc.componentType = kAudioUnitType_Output;
#if TARGET_OS_IPHONE
//desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
#else
desc.componentSubType = kAudioUnitSubType_HALOutput;
#endif
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
inputComponent = AudioComponentFindNext(NULL, &desc);
status = AudioComponentInstanceNew(inputComponent, &unit);
2017-03-30 16:06:59 +02:00
if(configured)
ActuallyConfigure(cfgSampleRate, cfgBitsPerSample, cfgChannels);
2017-02-02 17:24:40 +01:00
}
CAudioUnitIO* CAudioUnitIO::Get(){
if(refCount==0){
sharedInstance=new CAudioUnitIO();
}
refCount++;
assert(refCount>0);
return sharedInstance;
}
void CAudioUnitIO::Release(){
refCount--;
assert(refCount>=0);
if(refCount==0){
delete sharedInstance;
sharedInstance=NULL;
}
}
2017-03-30 16:06:59 +02:00
void CAudioUnitIO::AudioSessionAcquired(){
haveAudioSession=true;
if(sharedInstance)
sharedInstance->ProcessAudioSessionAcquired();
}
2017-02-02 17:24:40 +01:00
void CAudioUnitIO::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
if(configured)
return;
2017-03-30 16:06:59 +02:00
runFakeIO=true;
start_thread(fakeIOThread, CAudioUnitIO::StartFakeIOThread, this);
set_thread_priority(fakeIOThread, get_thread_max_priority());
if(haveAudioSession){
ActuallyConfigure(sampleRate, bitsPerSample, channels);
}else{
cfgSampleRate=sampleRate;
cfgBitsPerSample=bitsPerSample;
cfgChannels=channels;
}
configured=true;
}
void CAudioUnitIO::ActuallyConfigure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
2017-02-02 17:24:40 +01:00
UInt32 flag=1;
OSStatus status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag));
CHECK_AU_ERROR(status, "Error enabling AudioUnit output");
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
CHECK_AU_ERROR(status, "Error enabling AudioUnit input");
2017-03-30 16:06:59 +02:00
flag=0;
2017-02-02 17:24:40 +01:00
status=AudioUnitSetProperty(unit, kAUVoiceIOProperty_VoiceProcessingEnableAGC, kAudioUnitScope_Global, kInputBus, &flag, sizeof(flag));
CHECK_AU_ERROR(status, "Error disabling AGC");
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = sampleRate;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = channels;
audioFormat.mBitsPerChannel = bitsPerSample*channels;
audioFormat.mBytesPerPacket = bitsPerSample/8*channels;
audioFormat.mBytesPerFrame = bitsPerSample/8*channels;
status = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &audioFormat, sizeof(audioFormat));
CHECK_AU_ERROR(status, "Error setting output format");
status = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &audioFormat, sizeof(audioFormat));
CHECK_AU_ERROR(status, "Error setting input format");
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = CAudioUnitIO::BufferCallback;
callbackStruct.inputProcRefCon = this;
status = AudioUnitSetProperty(unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct));
CHECK_AU_ERROR(status, "Error setting output buffer callback");
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callbackStruct, sizeof(callbackStruct));
CHECK_AU_ERROR(status, "Error setting input buffer callback");
status = AudioUnitInitialize(unit);
CHECK_AU_ERROR(status, "Error initializing AudioUnit");
status=AudioOutputUnitStart(unit);
CHECK_AU_ERROR(status, "Error starting AudioUnit");
}
OSStatus CAudioUnitIO::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
((CAudioUnitIO*)inRefCon)->BufferCallback(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
return noErr;
}
void CAudioUnitIO::BufferCallback(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 bus, UInt32 numFrames, AudioBufferList *ioData){
2017-03-30 16:06:59 +02:00
runFakeIO=false;
2017-02-02 17:24:40 +01:00
if(bus==kOutputBus){
if(output && outputEnabled){
output->HandleBufferCallback(ioData);
}else{
memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
}
}else if(bus==kInputBus){
if(input && inputEnabled){
inBufferList.mBuffers[0].mDataByteSize=10240;
AudioUnitRender(unit, ioActionFlags, inTimeStamp, bus, numFrames, &inBufferList);
input->HandleBufferCallback(&inBufferList);
}
}
}
void CAudioUnitIO::AttachInput(CAudioInputAudioUnit *i){
assert(input==NULL);
input=i;
}
void CAudioUnitIO::AttachOutput(CAudioOutputAudioUnit *o){
assert(output==NULL);
output=o;
}
void CAudioUnitIO::DetachInput(){
assert(input!=NULL);
input=NULL;
inputEnabled=false;
}
void CAudioUnitIO::DetachOutput(){
assert(output!=NULL);
output=NULL;
outputEnabled=false;
}
void CAudioUnitIO::EnableInput(bool enabled){
inputEnabled=enabled;
}
void CAudioUnitIO::EnableOutput(bool enabled){
outputEnabled=enabled;
}
2017-03-30 16:06:59 +02:00
void* CAudioUnitIO::StartFakeIOThread(void *arg){
((CAudioUnitIO*)arg)->RunFakeIOThread();
return NULL;
}
void CAudioUnitIO::RunFakeIOThread(){
double neededDataDuration=0;
double prevTime=VoIPController::GetCurrentTime();
2017-03-30 16:06:59 +02:00
while(runFakeIO){
double t=VoIPController::GetCurrentTime();
2017-03-30 16:06:59 +02:00
neededDataDuration+=t-prevTime;
prevTime=t;
while(neededDataDuration>=0.020){
if(output && outputEnabled){
inBufferList.mBuffers[0].mDataByteSize=960*2;
output->HandleBufferCallback(&inBufferList);
}
if((output && outputEnabled) || (input && inputEnabled)){
memset(inBufferList.mBuffers[0].mData, 0, 960*2);
}
if(input && inputEnabled){
inBufferList.mBuffers[0].mDataByteSize=960*2;
input->HandleBufferCallback(&inBufferList);
}
neededDataDuration-=0.020;
}
usleep(5000);
}
LOGD("FakeIO thread exiting");
}