- Use VoiceProcessingIO on OS X 10.7 and higher

- Apply AGC after AEC/NS to work around loud echo on desktops
- Fixed (?) mic muting on Windows
This commit is contained in:
Grishka
2017-08-01 20:17:44 +03:00
parent 01f27014b2
commit bfd5cfecb3
16 changed files with 360 additions and 243 deletions

View File

@@ -86,7 +86,7 @@ EchoCanceller::EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC){
//#ifndef TGVOIP_USE_DESKTOP_DSP
ns=WebRtcNsx_Create();
WebRtcNsx_Init((NsxHandle*)ns, 48000);
WebRtcNsx_set_policy((NsxHandle*)ns, 2);
WebRtcNsx_set_policy((NsxHandle*)ns, 1);
/*#else
ns=WebRtcNs_Create();
WebRtcNs_Init((NsHandle*)ns, 48000);
@@ -99,19 +99,11 @@ EchoCanceller::EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC){
WebRtcAgcConfig agcConfig;
agcConfig.compressionGaindB = 9;
agcConfig.limiterEnable = 1;
#ifndef TGVOIP_USE_DESKTOP_DSP
agcConfig.targetLevelDbfs = 3;
#else
agcConfig.targetLevelDbfs = 9;
#endif
WebRtcAgc_Init(agc, 0, 255, kAgcModeAdaptiveAnalog, 48000);
WebRtcAgc_set_config(agc, agcConfig);
agcMicLevel=128;
}
/*state=webrtc::WebRtcAec_Create();
webrtc::WebRtcAec_Init(state, 16000, 16000);
webrtc::WebRtcAec_enable_delay_agnostic(webrtc::WebRtcAec_aec_core(state), 1);*/
#endif
}
@@ -224,29 +216,6 @@ void EchoCanceller::ProcessInput(unsigned char* data, unsigned char* out, size_t
memcpy(bufIn->ibuf()->bands(0)[0], samplesIn, 960*2);
((webrtc::SplittingFilter*)splittingFilter)->Analysis(bufIn, bufOut);
if(enableAGC){
int16_t _agcOut[3][320];
int16_t* agcIn[3];
int16_t* agcOut[3];
for(i=0;i<3;i++){
agcIn[i]=(int16_t*)bufOut->ibuf_const()->bands(0)[i];
agcOut[i]=_agcOut[i];
}
uint8_t saturation;
WebRtcAgc_AddMic(agc, agcIn, 3, 160);
WebRtcAgc_Process(agc, (const int16_t *const *) agcIn, 3, 160, agcOut, agcMicLevel, &agcMicLevel, 0, &saturation);
for(i=0;i<3;i++){
agcOut[i]+=160;
agcIn[i]+=160;
}
WebRtcAgc_AddMic(agc, agcIn, 3, 160);
WebRtcAgc_Process(agc, (const int16_t *const *) agcIn, 3, 160, agcOut, agcMicLevel, &agcMicLevel, 0, &saturation);
//LOGV("AGC mic level %d", agcMicLevel);
memcpy(bufOut->ibuf()->bands(0)[0], _agcOut[0], 320*2);
memcpy(bufOut->ibuf()->bands(0)[1], _agcOut[1], 320*2);
memcpy(bufOut->ibuf()->bands(0)[2], _agcOut[2], 320*2);
}
#ifndef TGVOIP_USE_DESKTOP_DSP
if(enableAEC && enableNS){
@@ -356,6 +325,29 @@ void EchoCanceller::ProcessInput(unsigned char* data, unsigned char* out, size_t
memcpy(bufOut->fbuf()->bands(0)[2], _aecOut[2], 320*4);
}
#endif
if(enableAGC){
int16_t _agcOut[3][320];
int16_t* agcIn[3];
int16_t* agcOut[3];
for(i=0;i<3;i++){
agcIn[i]=(int16_t*)bufOut->ibuf_const()->bands(0)[i];
agcOut[i]=_agcOut[i];
}
uint8_t saturation;
WebRtcAgc_AddMic(agc, agcIn, 3, 160);
WebRtcAgc_Process(agc, (const int16_t *const *) agcIn, 3, 160, agcOut, agcMicLevel, &agcMicLevel, 0, &saturation);
for(i=0;i<3;i++){
agcOut[i]+=160;
agcIn[i]+=160;
}
WebRtcAgc_AddMic(agc, agcIn, 3, 160);
WebRtcAgc_Process(agc, (const int16_t *const *) agcIn, 3, 160, agcOut, agcMicLevel, &agcMicLevel, 0, &saturation);
//LOGV("AGC mic level %d", agcMicLevel);
memcpy(bufOut->ibuf()->bands(0)[0], _agcOut[0], 320*2);
memcpy(bufOut->ibuf()->bands(0)[1], _agcOut[1], 320*2);
memcpy(bufOut->ibuf()->bands(0)[2], _agcOut[2], 320*2);
}
((webrtc::SplittingFilter*)splittingFilter)->Synthesis(bufOut, bufIn);

View File

@@ -10,9 +10,8 @@
#include "../os/android/AudioInputAndroid.h"
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#include "../os/darwin/AudioInputAudioUnit.h"
#else
#if TARGET_OS_OSX
#include "../os/darwin/AudioInputAudioUnitOSX.h"
#endif
#elif defined(_WIN32)
@@ -45,10 +44,10 @@ AudioInput *AudioInput::Create(std::string deviceID){
return new AudioInputAndroid();
#elif defined(__APPLE__)
#if TARGET_OS_OSX
return new AudioInputAudioUnit(deviceID);
#else
return new AudioInputAudioUnit();
if(kCFCoreFoundationVersionNumber<kCFCoreFoundationVersionNumber10_7)
return new AudioInputAudioUnitLegacy(deviceID);
#endif
return new AudioInputAudioUnit(deviceID);
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
if(LOBYTE(LOWORD(GetVersion()))<6)
@@ -79,7 +78,7 @@ bool AudioInput::IsInitialized(){
void AudioInput::EnumerateDevices(std::vector<AudioInputDevice>& devs){
#if defined(__APPLE__) && TARGET_OS_OSX
AudioInputAudioUnit::EnumerateDevices(devs);
AudioInputAudioUnitLegacy::EnumerateDevices(devs);
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
if(LOBYTE(LOWORD(GetVersion()))<6){

View File

@@ -11,9 +11,8 @@
#include "../os/android/AudioOutputAndroid.h"
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#include "../os/darwin/AudioOutputAudioUnit.h"
#else
#if TARGET_OS_OSX
#include "../os/darwin/AudioOutputAudioUnitOSX.h"
#endif
#elif defined(_WIN32)
@@ -43,10 +42,10 @@ AudioOutput *AudioOutput::Create(std::string deviceID){
return new AudioOutputOpenSLES();
#elif defined(__APPLE__)
#if TARGET_OS_OSX
return new AudioOutputAudioUnit(deviceID);
#else
return new AudioOutputAudioUnit();
if(kCFCoreFoundationVersionNumber<kCFCoreFoundationVersionNumber10_7)
return new AudioOutputAudioUnitLegacy(deviceID);
#endif
return new AudioOutputAudioUnit(deviceID);
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
if(LOBYTE(LOWORD(GetVersion()))<6)
@@ -93,7 +92,7 @@ float AudioOutput::GetLevel(){
void AudioOutput::EnumerateDevices(std::vector<AudioOutputDevice>& devs){
#if defined(__APPLE__) && TARGET_OS_OSX
AudioOutputAudioUnit::EnumerateDevices(devs);
AudioOutputAudioUnitLegacy::EnumerateDevices(devs);
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
if(LOBYTE(LOWORD(GetVersion()))<6){

View File

@@ -75,10 +75,16 @@
'<(tgvoip_src_loc)/os/windows/AudioInputWASAPI.h',
# macOS
'<(tgvoip_src_loc)/os/darwin/AudioInputAudioUnit.cpp',
'<(tgvoip_src_loc)/os/darwin/AudioInputAudioUnit.h',
'<(tgvoip_src_loc)/os/darwin/AudioOutputAudioUnit.cpp',
'<(tgvoip_src_loc)/os/darwin/AudioOutputAudioUnit.h',
'<(tgvoip_src_loc)/os/darwin/AudioInputAudioUnitOSX.cpp',
'<(tgvoip_src_loc)/os/darwin/AudioInputAudioUnitOSX.h',
'<(tgvoip_src_loc)/os/darwin/AudioOutputAudioUnitOSX.cpp',
'<(tgvoip_src_loc)/os/darwin/AudioOutputAudioUnitOSX.h',
'<(tgvoip_src_loc)/os/darwin/AudioUnitIO.cpp',
'<(tgvoip_src_loc)/os/darwin/AudioUnitIO.h',
'<(tgvoip_src_loc)/os/darwin/DarwinSpecific.mm',
'<(tgvoip_src_loc)/os/darwin/DarwinSpecific.h',

View File

@@ -16,11 +16,15 @@
using namespace tgvoip;
using namespace tgvoip::audio;
AudioInputAudioUnit::AudioInputAudioUnit(){
AudioInputAudioUnit::AudioInputAudioUnit(std::string deviceID){
remainingDataSize=0;
isRecording=false;
this->io=AudioUnitIO::Get();
#if TARGET_OS_OSX
io->SetCurrentDevice(true, deviceID);
#endif
io->AttachInput(this);
failed=io->IsFailed();
}
AudioInputAudioUnit::~AudioInputAudioUnit(){
@@ -35,6 +39,7 @@ void AudioInputAudioUnit::Configure(uint32_t sampleRate, uint32_t bitsPerSample,
void AudioInputAudioUnit::Start(){
isRecording=true;
io->EnableInput(true);
failed=io->IsFailed();
}
void AudioInputAudioUnit::Stop(){
@@ -44,11 +49,22 @@ void AudioInputAudioUnit::Stop(){
void AudioInputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
int i;
int j;
for(i=0;i<ioData->mNumberBuffers;i++){
AudioBuffer buf=ioData->mBuffers[i];
#if TARGET_OS_OSX
assert(remainingDataSize+buf.mDataByteSize/2<10240);
float* src=reinterpret_cast<float*>(buf.mData);
int16_t* dst=reinterpret_cast<int16_t*>(remainingData+remainingDataSize);
for(j=0;j<buf.mDataByteSize/4;j++){
dst[j]=(int16_t)(src[j]*INT16_MAX);
}
remainingDataSize+=buf.mDataByteSize/2;
#else
assert(remainingDataSize+buf.mDataByteSize<10240);
memcpy(remainingData+remainingDataSize, buf.mData, buf.mDataByteSize);
remainingDataSize+=buf.mDataByteSize;
#endif
while(remainingDataSize>=BUFFER_SIZE*2){
InvokeCallback((unsigned char*)remainingData, BUFFER_SIZE*2);
remainingDataSize-=BUFFER_SIZE*2;
@@ -58,3 +74,9 @@ void AudioInputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
}
}
}
#if TARGET_OS_OSX
void AudioInputAudioUnit::SetCurrentDevice(std::string deviceID){
io->SetCurrentDevice(true, deviceID);
}
#endif

View File

@@ -16,12 +16,15 @@ class AudioUnitIO;
class AudioInputAudioUnit : public AudioInput{
public:
AudioInputAudioUnit();
AudioInputAudioUnit(std::string deviceID);
virtual ~AudioInputAudioUnit();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
void HandleBufferCallback(AudioBufferList* ioData);
#if TARGET_OS_OSX
virtual void SetCurrentDevice(std::string deviceID);
#endif
private:
unsigned char remainingData[10240];

View File

@@ -20,13 +20,13 @@
using namespace tgvoip;
using namespace tgvoip::audio;
AudioInputAudioUnit::AudioInputAudioUnit(std::string deviceID) : AudioInput(deviceID){
AudioInputAudioUnitLegacy::AudioInputAudioUnitLegacy(std::string deviceID) : AudioInput(deviceID){
remainingDataSize=0;
isRecording=false;
OSStatus status;
AudioComponentDescription inputDesc={
.componentType = kAudioUnitType_Output, .componentSubType = kAudioUnitSubType_HALOutput, .componentFlags = 0, .componentFlagsMask = 0,
.componentType = kAudioUnitType_Output, .componentSubType = /*kAudioUnitSubType_HALOutput*/kAudioUnitSubType_VoiceProcessingIO, .componentFlags = 0, .componentFlagsMask = 0,
.componentManufacturer = kAudioUnitManufacturer_Apple
};
AudioComponent component=AudioComponentFindNext(NULL, &inputDesc);
@@ -51,10 +51,10 @@ AudioInputAudioUnit::AudioInputAudioUnit(std::string deviceID) : AudioInput(devi
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioInputAudioUnit::DefaultDeviceChangedCallback, this);
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioInputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = AudioInputAudioUnit::BufferCallback;
callbackStruct.inputProc = AudioInputAudioUnitLegacy::BufferCallback;
callbackStruct.inputProcRefCon=this;
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callbackStruct, sizeof(callbackStruct));
CHECK_AU_ERROR(status, "Error setting input buffer callback");
@@ -66,42 +66,42 @@ AudioInputAudioUnit::AudioInputAudioUnit(std::string deviceID) : AudioInput(devi
inBufferList.mNumberBuffers=1;
}
AudioInputAudioUnit::~AudioInputAudioUnit(){
AudioInputAudioUnitLegacy::~AudioInputAudioUnitLegacy(){
AudioObjectPropertyAddress propertyAddress;
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioInputAudioUnit::DefaultDeviceChangedCallback, this);
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioInputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
AudioUnitUninitialize(unit);
AudioComponentInstanceDispose(unit);
free(inBufferList.mBuffers[0].mData);
}
void AudioInputAudioUnit::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
void AudioInputAudioUnitLegacy::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}
void AudioInputAudioUnit::Start(){
void AudioInputAudioUnitLegacy::Start(){
isRecording=true;
OSStatus status=AudioOutputUnitStart(unit);
CHECK_AU_ERROR(status, "Error starting AudioUnit");
}
void AudioInputAudioUnit::Stop(){
void AudioInputAudioUnitLegacy::Stop(){
isRecording=false;
OSStatus status=AudioOutputUnitStart(unit);
CHECK_AU_ERROR(status, "Error stopping AudioUnit");
}
OSStatus AudioInputAudioUnit::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
AudioInputAudioUnit* input=(AudioInputAudioUnit*) inRefCon;
OSStatus AudioInputAudioUnitLegacy::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
AudioInputAudioUnitLegacy* input=(AudioInputAudioUnitLegacy*) inRefCon;
input->inBufferList.mBuffers[0].mDataByteSize=10240;
OSStatus res=AudioUnitRender(input->unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &input->inBufferList);
input->HandleBufferCallback(&input->inBufferList);
return noErr;
}
void AudioInputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
void AudioInputAudioUnitLegacy::HandleBufferCallback(AudioBufferList *ioData){
int i;
for(i=0;i<ioData->mNumberBuffers;i++){
AudioBuffer buf=ioData->mBuffers[i];
@@ -124,7 +124,7 @@ void AudioInputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
}
void AudioInputAudioUnit::EnumerateDevices(std::vector<AudioInputDevice>& devs){
void AudioInputAudioUnitLegacy::EnumerateDevices(std::vector<AudioInputDevice>& devs){
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
@@ -211,7 +211,7 @@ void AudioInputAudioUnit::EnumerateDevices(std::vector<AudioInputDevice>& devs){
audioDevices = NULL;
}
void AudioInputAudioUnit::SetCurrentDevice(std::string deviceID){
void AudioInputAudioUnitLegacy::SetCurrentDevice(std::string deviceID){
UInt32 size=sizeof(AudioDeviceID);
AudioDeviceID inputDevice=NULL;
OSStatus status;
@@ -299,9 +299,9 @@ void AudioInputAudioUnit::SetCurrentDevice(std::string deviceID){
}
}
OSStatus AudioInputAudioUnit::DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData){
OSStatus AudioInputAudioUnitLegacy::DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData){
LOGV("System default input device changed");
AudioInputAudioUnit* self=(AudioInputAudioUnit*)inClientData;
AudioInputAudioUnitLegacy* self=(AudioInputAudioUnitLegacy*)inClientData;
if(self->currentDevice=="default"){
self->SetCurrentDevice(self->currentDevice);
}

View File

@@ -4,8 +4,8 @@
// you should have received with this source code distribution.
//
#ifndef LIBTGVOIP_AUDIOINPUTAUDIOUNIT_H
#define LIBTGVOIP_AUDIOINPUTAUDIOUNIT_H
#ifndef LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H
#define LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H
#include <AudioUnit/AudioUnit.h>
#import <AudioToolbox/AudioToolbox.h>
@@ -13,11 +13,11 @@
#include "../../audio/AudioInput.h"
namespace tgvoip{ namespace audio{
class AudioInputAudioUnit : public AudioInput{
class AudioInputAudioUnitLegacy : public AudioInput{
public:
AudioInputAudioUnit(std::string deviceID);
virtual ~AudioInputAudioUnit();
AudioInputAudioUnitLegacy(std::string deviceID);
virtual ~AudioInputAudioUnitLegacy();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
@@ -37,4 +37,4 @@ private:
};
}}
#endif //LIBTGVOIP_AUDIOINPUTAUDIOUNIT_H
#endif //LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H

View File

@@ -17,12 +17,16 @@ const int8_t permutation[33]={0,1,2,3,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,
using namespace tgvoip;
using namespace tgvoip::audio;
AudioOutputAudioUnit::AudioOutputAudioUnit(){
AudioOutputAudioUnit::AudioOutputAudioUnit(std::string deviceID){
isPlaying=false;
remainingDataSize=0;
level=0.0;
this->io=AudioUnitIO::Get();
#if TARGET_OS_OSX
io->SetCurrentDevice(false, deviceID);
#endif
io->AttachOutput(this);
failed=io->IsFailed();
}
AudioOutputAudioUnit::~AudioOutputAudioUnit(){
@@ -45,6 +49,7 @@ void AudioOutputAudioUnit::EnableLoudspeaker(bool enabled){
void AudioOutputAudioUnit::Start(){
isPlaying=true;
io->EnableOutput(true);
failed=io->IsFailed();
}
void AudioOutputAudioUnit::Stop(){
@@ -75,11 +80,21 @@ void AudioOutputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
InvokeCallback(remainingData+remainingDataSize, BUFFER_SIZE*2);
remainingDataSize+=BUFFER_SIZE*2;
}
#if TARGET_OS_OSX
float* dst=reinterpret_cast<float*>(buf.mData);
int16_t* src=reinterpret_cast<int16_t*>(remainingData);
for(k=0;k<buf.mDataByteSize/4;k++){
dst[k]=src[k]/(float)INT16_MAX;
}
remainingDataSize-=buf.mDataByteSize/2;
memmove(remainingData, remainingData+buf.mDataByteSize/2, remainingDataSize);
#else
memcpy(buf.mData, remainingData, buf.mDataByteSize);
remainingDataSize-=buf.mDataByteSize;
memmove(remainingData, remainingData+buf.mDataByteSize, remainingDataSize);
unsigned int samples=buf.mDataByteSize/sizeof(int16_t);
#endif
/*unsigned int samples=buf.mDataByteSize/sizeof(int16_t);
for (k=0;k<samples;k++){
int16_t absolute=(int16_t)abs(*((int16_t *)buf.mData+k));
if (absolute>absVal)
@@ -99,7 +114,12 @@ void AudioOutputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
}
level=permutation[position];
absMax>>=2;
}
}*/
}
}
#if TARGET_OS_OSX
void AudioOutputAudioUnit::SetCurrentDevice(std::string deviceID){
io->SetCurrentDevice(false, deviceID);
}
#endif

View File

@@ -15,7 +15,7 @@ class AudioUnitIO;
class AudioOutputAudioUnit : public AudioOutput{
public:
AudioOutputAudioUnit();
AudioOutputAudioUnit(std::string deviceID);
virtual ~AudioOutputAudioUnit();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual bool IsPhone();
@@ -25,6 +25,9 @@ public:
virtual bool IsPlaying();
virtual float GetLevel();
void HandleBufferCallback(AudioBufferList* ioData);
#if TARGET_OS_OSX
virtual void SetCurrentDevice(std::string deviceID);
#endif
private:
bool isPlaying;

View File

@@ -20,7 +20,7 @@
using namespace tgvoip;
using namespace tgvoip::audio;
AudioOutputAudioUnit::AudioOutputAudioUnit(std::string deviceID){
AudioOutputAudioUnitLegacy::AudioOutputAudioUnitLegacy(std::string deviceID){
remainingDataSize=0;
isPlaying=false;
sysDevID=NULL;
@@ -61,7 +61,7 @@ AudioOutputAudioUnit::AudioOutputAudioUnit(std::string deviceID){
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioOutputAudioUnit::DefaultDeviceChangedCallback, this);
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
AudioStreamBasicDescription desiredFormat={
.mSampleRate=/*hardwareFormat.mSampleRate*/48000, .mFormatID=kAudioFormatLinearPCM, .mFormatFlags=kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian,
@@ -72,7 +72,7 @@ AudioOutputAudioUnit::AudioOutputAudioUnit(std::string deviceID){
CHECK_AU_ERROR(status, "Error setting format");
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = AudioOutputAudioUnit::BufferCallback;
callbackStruct.inputProc = AudioOutputAudioUnitLegacy::BufferCallback;
callbackStruct.inputProcRefCon=this;
status = AudioUnitSetProperty(unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct));
CHECK_AU_ERROR(status, "Error setting input buffer callback");
@@ -80,12 +80,12 @@ AudioOutputAudioUnit::AudioOutputAudioUnit(std::string deviceID){
CHECK_AU_ERROR(status, "Error initializing unit");
}
AudioOutputAudioUnit::~AudioOutputAudioUnit(){
AudioOutputAudioUnitLegacy::~AudioOutputAudioUnitLegacy(){
AudioObjectPropertyAddress propertyAddress;
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioOutputAudioUnit::DefaultDeviceChangedCallback, this);
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
AudioObjectPropertyAddress dataSourceProp={
kAudioDevicePropertyDataSource,
@@ -93,39 +93,39 @@ AudioOutputAudioUnit::~AudioOutputAudioUnit(){
kAudioObjectPropertyElementMaster
};
if(isMacBookPro && sysDevID && AudioObjectHasProperty(sysDevID, &dataSourceProp)){
AudioObjectRemovePropertyListener(sysDevID, &dataSourceProp, AudioOutputAudioUnit::DefaultDeviceChangedCallback, this);
AudioObjectRemovePropertyListener(sysDevID, &dataSourceProp, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
}
AudioUnitUninitialize(unit);
AudioComponentInstanceDispose(unit);
}
void AudioOutputAudioUnit::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
void AudioOutputAudioUnitLegacy::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}
void AudioOutputAudioUnit::Start(){
void AudioOutputAudioUnitLegacy::Start(){
isPlaying=true;
OSStatus status=AudioOutputUnitStart(unit);
CHECK_AU_ERROR(status, "Error starting AudioUnit");
}
void AudioOutputAudioUnit::Stop(){
void AudioOutputAudioUnitLegacy::Stop(){
isPlaying=false;
OSStatus status=AudioOutputUnitStart(unit);
CHECK_AU_ERROR(status, "Error stopping AudioUnit");
}
OSStatus AudioOutputAudioUnit::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
AudioOutputAudioUnit* input=(AudioOutputAudioUnit*) inRefCon;
OSStatus AudioOutputAudioUnitLegacy::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
AudioOutputAudioUnitLegacy* input=(AudioOutputAudioUnitLegacy*) inRefCon;
input->HandleBufferCallback(ioData);
return noErr;
}
bool AudioOutputAudioUnit::IsPlaying(){
bool AudioOutputAudioUnitLegacy::IsPlaying(){
return isPlaying;
}
void AudioOutputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
void AudioOutputAudioUnitLegacy::HandleBufferCallback(AudioBufferList *ioData){
int i;
unsigned int k;
int16_t absVal=0;
@@ -147,7 +147,7 @@ void AudioOutputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
}
void AudioOutputAudioUnit::EnumerateDevices(std::vector<AudioOutputDevice>& devs){
void AudioOutputAudioUnitLegacy::EnumerateDevices(std::vector<AudioOutputDevice>& devs){
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
@@ -234,7 +234,7 @@ void AudioOutputAudioUnit::EnumerateDevices(std::vector<AudioOutputDevice>& devs
audioDevices = NULL;
}
void AudioOutputAudioUnit::SetCurrentDevice(std::string deviceID){
void AudioOutputAudioUnitLegacy::SetCurrentDevice(std::string deviceID){
UInt32 size=sizeof(AudioDeviceID);
AudioDeviceID outputDevice=NULL;
OSStatus status;
@@ -245,7 +245,7 @@ void AudioOutputAudioUnit::SetCurrentDevice(std::string deviceID){
};
if(isMacBookPro && sysDevID && AudioObjectHasProperty(sysDevID, &dataSourceProp)){
AudioObjectRemovePropertyListener(sysDevID, &dataSourceProp, AudioOutputAudioUnit::DefaultDeviceChangedCallback, this);
AudioObjectRemovePropertyListener(sysDevID, &dataSourceProp, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
}
if(deviceID=="default"){
@@ -337,22 +337,22 @@ void AudioOutputAudioUnit::SetCurrentDevice(std::string deviceID){
size=4;
AudioObjectGetPropertyData(outputDevice, &dataSourceProp, 0, NULL, &size, &dataSource);
SetPanRight(dataSource=='ispk');
AudioObjectAddPropertyListener(outputDevice, &dataSourceProp, AudioOutputAudioUnit::DefaultDeviceChangedCallback, this);
AudioObjectAddPropertyListener(outputDevice, &dataSourceProp, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
}else{
SetPanRight(false);
}
}
}
void AudioOutputAudioUnit::SetPanRight(bool panRight){
void AudioOutputAudioUnitLegacy::SetPanRight(bool panRight){
LOGI("%sabling pan right on macbook pro", panRight ? "En" : "Dis");
int32_t channelMap[]={panRight ? -1 : 0, 0};
OSStatus status=AudioUnitSetProperty(unit, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Global, kOutputBus, channelMap, sizeof(channelMap));
CHECK_AU_ERROR(status, "Error setting channel map");
}
OSStatus AudioOutputAudioUnit::DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData){
AudioOutputAudioUnit* self=(AudioOutputAudioUnit*)inClientData;
OSStatus AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData){
AudioOutputAudioUnitLegacy* self=(AudioOutputAudioUnitLegacy*)inClientData;
if(inAddresses[0].mSelector==kAudioHardwarePropertyDefaultOutputDevice){
LOGV("System default input device changed");
if(self->currentDevice=="default"){

View File

@@ -4,8 +4,8 @@
// you should have received with this source code distribution.
//
#ifndef LIBTGVOIP_AUDIOINPUTAUDIOUNIT_H
#define LIBTGVOIP_AUDIOINPUTAUDIOUNIT_H
#ifndef LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H
#define LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H
#include <AudioUnit/AudioUnit.h>
#import <AudioToolbox/AudioToolbox.h>
@@ -13,11 +13,11 @@
#include "../../audio/AudioOutput.h"
namespace tgvoip{ namespace audio{
class AudioOutputAudioUnit : public AudioOutput{
class AudioOutputAudioUnitLegacy : public AudioOutput{
public:
AudioOutputAudioUnit(std::string deviceID);
virtual ~AudioOutputAudioUnit();
AudioOutputAudioUnitLegacy(std::string deviceID);
virtual ~AudioOutputAudioUnitLegacy();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
@@ -40,4 +40,4 @@ private:
};
}}
#endif //LIBTGVOIP_AUDIOINPUTAUDIOUNIT_H
#endif //LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H

View File

@@ -4,7 +4,6 @@
// 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"
@@ -12,7 +11,7 @@
#include "../../VoIPController.h"
#include "../../VoIPServerConfig.h"
#define CHECK_AU_ERROR(res, msg) if(res!=noErr){ LOGE(msg": OSStatus=%d", (int)res); return; }
#define CHECK_AU_ERROR(res, msg) if(res!=noErr){ LOGE(msg": OSStatus=%d", (int)res); failed=true; return; }
#define BUFFER_SIZE 960 // 20 ms
#define kOutputBus 0
@@ -23,57 +22,106 @@ using namespace tgvoip::audio;
int AudioUnitIO::refCount=0;
AudioUnitIO* AudioUnitIO::sharedInstance=NULL;
bool AudioUnitIO::haveAudioSession=false;
AudioUnitIO::AudioUnitIO(){
input=NULL;
output=NULL;
configured=false;
inputEnabled=false;
outputEnabled=false;
failed=false;
started=false;
inBufferList.mBuffers[0].mData=malloc(10240);
inBufferList.mBuffers[0].mDataByteSize=10240;
inBufferList.mNumberBuffers=1;
#ifdef TGVOIP_USE_AUDIO_SESSION
if(haveAudioSession)
ProcessAudioSessionAcquired();
#else
haveAudioSession=true;
ProcessAudioSessionAcquired();
#endif
}
AudioUnitIO::~AudioUnitIO(){
if(runFakeIO){
runFakeIO=false;
join_thread(fakeIOThread);
}
AudioOutputUnitStop(unit);
AudioUnitUninitialize(unit);
AudioComponentInstanceDispose(unit);
free(inBufferList.mBuffers[0].mData);
haveAudioSession=false;
}
void AudioUnitIO::ProcessAudioSessionAcquired(){
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);
if(configured)
ActuallyConfigure(cfgSampleRate, cfgBitsPerSample, cfgChannels);
UInt32 flag=1;
#if TARGET_OS_IPHONE
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");
#endif
#if TARGET_OS_IPHONE
flag=ServerConfig::GetSharedInstance()->GetBoolean("use_ios_vpio_agc", true) ? 1 : 0;
#else
flag=ServerConfig::GetSharedInstance()->GetBoolean("use_osx_vpio_agc", true) ? 1 : 0;
#endif
status=AudioUnitSetProperty(unit, kAUVoiceIOProperty_VoiceProcessingEnableAGC, kAudioUnitScope_Global, kInputBus, &flag, sizeof(flag));
CHECK_AU_ERROR(status, "Error disabling AGC");
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 48000;
audioFormat.mFormatID = kAudioFormatLinearPCM;
#if TARGET_OS_IPHONE
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 2;
audioFormat.mBytesPerFrame = 2;
#else // OS X
audioFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian;
audioFormat.mBitsPerChannel = 32;
audioFormat.mBytesPerPacket = 4;
audioFormat.mBytesPerFrame = 4;
#endif
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
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 = AudioUnitIO::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");
#if TARGET_OS_OSX
CFRunLoopRef theRunLoop = NULL;
AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
status = AudioObjectSetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioUnitIO::DefaultDeviceChangedCallback, this);
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioUnitIO::DefaultDeviceChangedCallback, this);
#endif
}
AudioUnitIO::~AudioUnitIO(){
AudioOutputUnitStop(unit);
AudioUnitUninitialize(unit);
AudioComponentInstanceDispose(unit);
free(inBufferList.mBuffers[0].mData);
#if TARGET_OS_OSX
AudioObjectPropertyAddress propertyAddress;
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioUnitIO::DefaultDeviceChangedCallback, this);
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioUnitIO::DefaultDeviceChangedCallback, this);
#endif
}
AudioUnitIO* AudioUnitIO::Get(){
@@ -94,72 +142,8 @@ void AudioUnitIO::Release(){
}
}
void AudioUnitIO::AudioSessionAcquired(){
haveAudioSession=true;
if(sharedInstance)
sharedInstance->ProcessAudioSessionAcquired();
}
void AudioUnitIO::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
if(configured)
return;
#ifdef TGVOIP_USE_AUDIO_SESSION
runFakeIO=true;
start_thread(fakeIOThread, AudioUnitIO::StartFakeIOThread, this);
set_thread_priority(fakeIOThread, get_thread_max_priority());
#endif
if(haveAudioSession){
ActuallyConfigure(sampleRate, bitsPerSample, channels);
}else{
cfgSampleRate=sampleRate;
cfgBitsPerSample=bitsPerSample;
cfgChannels=channels;
}
configured=true;
}
void AudioUnitIO::ActuallyConfigure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
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");
flag=ServerConfig::GetSharedInstance()->GetBoolean("use_ios_vpio_agc", true) ? 1 : 0;
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 = AudioUnitIO::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 AudioUnitIO::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
@@ -168,7 +152,6 @@ OSStatus AudioUnitIO::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags
}
void AudioUnitIO::BufferCallback(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 bus, UInt32 numFrames, AudioBufferList *ioData){
runFakeIO=false;
if(bus==kOutputBus){
if(output && outputEnabled){
output->HandleBufferCallback(ioData);
@@ -176,9 +159,9 @@ void AudioUnitIO::BufferCallback(AudioUnitRenderActionFlags *ioActionFlags, cons
memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
}
}else if(bus==kInputBus){
inBufferList.mBuffers[0].mDataByteSize=10240;
AudioUnitRender(unit, ioActionFlags, inTimeStamp, bus, numFrames, &inBufferList);
if(input && inputEnabled){
inBufferList.mBuffers[0].mDataByteSize=10240;
AudioUnitRender(unit, ioActionFlags, inTimeStamp, bus, numFrames, &inBufferList);
input->HandleBufferCallback(&inBufferList);
}
}
@@ -208,40 +191,127 @@ void AudioUnitIO::DetachOutput(){
void AudioUnitIO::EnableInput(bool enabled){
inputEnabled=enabled;
StartIfNeeded();
}
void AudioUnitIO::EnableOutput(bool enabled){
outputEnabled=enabled;
StartIfNeeded();
}
void* AudioUnitIO::StartFakeIOThread(void *arg){
((AudioUnitIO*)arg)->RunFakeIOThread();
return NULL;
void AudioUnitIO::StartIfNeeded(){
if(started)
return;
started=true;
OSStatus status = AudioUnitInitialize(unit);
CHECK_AU_ERROR(status, "Error initializing AudioUnit");
status=AudioOutputUnitStart(unit);
CHECK_AU_ERROR(status, "Error starting AudioUnit");
}
void AudioUnitIO::RunFakeIOThread(){
double neededDataDuration=0;
double prevTime=VoIPController::GetCurrentTime();
while(runFakeIO){
double t=VoIPController::GetCurrentTime();
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;
bool AudioUnitIO::IsFailed(){
return failed;
}
#if TARGET_OS_OSX
OSStatus AudioUnitIO::DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData){
AudioUnitIO* self=(AudioUnitIO*)inClientData;
if(inAddresses[0].mSelector==kAudioHardwarePropertyDefaultOutputDevice){
LOGV("System default output device changed");
if(self->currentOutputDevice=="default"){
self->SetCurrentDevice(false, self->currentOutputDevice);
}
}else if(inAddresses[0].mSelector==kAudioHardwarePropertyDefaultInputDevice){
LOGV("System default input device changed");
if(self->currentInputDevice=="default"){
self->SetCurrentDevice(true, self->currentInputDevice);
}
usleep(5000);
}
LOGD("FakeIO thread exiting");
return noErr;
}
void AudioUnitIO::SetCurrentDevice(bool input, std::string deviceID){
if(started){
AudioOutputUnitStop(unit);
AudioUnitUninitialize(unit);
}
UInt32 size=sizeof(AudioDeviceID);
AudioDeviceID device=NULL;
OSStatus status;
if(deviceID=="default"){
AudioObjectPropertyAddress propertyAddress;
propertyAddress.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
UInt32 propsize = sizeof(AudioDeviceID);
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propsize, &device);
CHECK_AU_ERROR(status, "Error getting default device");
}else{
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
UInt32 dataSize = 0;
status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
CHECK_AU_ERROR(status, "Error getting devices size");
UInt32 deviceCount = (UInt32)(dataSize / sizeof(AudioDeviceID));
AudioDeviceID audioDevices[deviceCount];
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
CHECK_AU_ERROR(status, "Error getting device list");
for(UInt32 i = 0; i < deviceCount; ++i) {
// Query device UID
CFStringRef deviceUID = NULL;
dataSize = sizeof(deviceUID);
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
CHECK_AU_ERROR(status, "Error getting device uid");
char buf[1024];
CFStringGetCString(deviceUID, buf, 1024, kCFStringEncodingUTF8);
if(deviceID==buf){
LOGV("Found device for id %s", buf);
device=audioDevices[i];
break;
}
}
if(!device){
LOGW("Requested device not found, using default");
SetCurrentDevice(input, "default");
return;
}
}
status=AudioUnitSetProperty(unit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
input ? kInputBus : kOutputBus,
&device,
size);
CHECK_AU_ERROR(status, "Error setting input device");
if(input)
currentInputDevice=deviceID;
else
currentOutputDevice=deviceID;
/*AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyBufferFrameSize,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
size=4;
UInt32 bufferFrameSize;
status=AudioObjectGetPropertyData(device, &propertyAddress, 0, NULL, &size, &bufferFrameSize);
if(status==noErr){
estimatedDelay=bufferFrameSize/48;
LOGD("CoreAudio buffer size for device is %u frames (%u ms)", bufferFrameSize, estimatedDelay);
}*/
if(started){
started=false;
StartIfNeeded();
}
}
#endif

View File

@@ -8,7 +8,9 @@
#define LIBTGVOIP_AUDIOUNITIO_H
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
#include "../../threading.h"
#include <string>
namespace tgvoip{ namespace audio{
class AudioInputAudioUnit;
@@ -25,33 +27,32 @@ public:
void DetachOutput();
void EnableInput(bool enabled);
void EnableOutput(bool enabled);
bool IsFailed();
static AudioUnitIO* Get();
static void Release();
static void* StartFakeIOThread(void* arg);
static void AudioSessionAcquired();
#if TARGET_OS_OSX
void SetCurrentDevice(bool input, std::string deviceID);
#endif
private:
static OSStatus BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
void BufferCallback(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 bus, UInt32 numFrames, AudioBufferList* ioData);
void RunFakeIOThread();
void Init();
void ActuallyConfigure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
void ProcessAudioSessionAcquired();
void StartIfNeeded();
#if TARGET_OS_OSX
static OSStatus DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData);
std::string currentInputDevice;
std::string currentOutputDevice;
#endif
AudioComponentInstance unit;
AudioInputAudioUnit* input;
AudioOutputAudioUnit* output;
AudioBufferList inBufferList;
bool configured;
bool inputEnabled;
bool outputEnabled;
bool runFakeIO;
uint32_t cfgSampleRate;
uint32_t cfgBitsPerSample;
uint32_t cfgChannels;
tgvoip_thread_t fakeIOThread;
bool failed;
bool started;
static int refCount;
static AudioUnitIO* sharedInstance;
static bool haveAudioSession;
};
}}

View File

@@ -61,12 +61,13 @@ AudioInputWASAPI::AudioInputWASAPI(std::string deviceID){
audioClient=NULL;
captureClient=NULL;
thread=NULL;
started=false;
SetCurrentDevice(deviceID);
}
AudioInputWASAPI::~AudioInputWASAPI(){
if(audioClient && isRecording){
if(audioClient && started){
audioClient->Stop();
}
@@ -109,15 +110,14 @@ void AudioInputWASAPI::Start(){
thread=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AudioInputWASAPI::StartThread, this, 0, NULL);
}
if(audioClient)
started=true;
if(audioClient){
audioClient->Start();
}
}
void AudioInputWASAPI::Stop(){
isRecording=false;
if(audioClient)
audioClient->Stop();
}
bool AudioInputWASAPI::IsRecording(){
@@ -368,7 +368,8 @@ void AudioInputWASAPI::RunThread() {
memcpy(remainingData+remainingDataLen, data, dataLen);
remainingDataLen+=dataLen;
while(remainingDataLen>960*2){
InvokeCallback(remainingData, 960*2);
if(isRecording)
InvokeCallback(remainingData, 960*2);
memmove(remainingData, remainingData+(960*2), remainingDataLen-960*2);
remainingDataLen-=960*2;
}

View File

@@ -77,6 +77,7 @@ private:
bool isDefaultDevice;
ULONG refCount;
std::string streamChangeToDevice;
bool started;
#ifdef TGVOIP_WINDOWS_DESKTOP
STDMETHOD(OnDisplayNameChanged) (LPCWSTR /*NewDisplayName*/, LPCGUID /*EventContext*/) { return S_OK; };