mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-01-10 06:41:11 +00:00
- 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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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',
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"){
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; };
|
||||
|
||||
Reference in New Issue
Block a user