1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

feat(audio): make echo cancellation work and improve some minor stuff

This commit is contained in:
sudden6 2017-04-27 00:06:26 +02:00
parent 9c603e8654
commit 5d60f09df4
No known key found for this signature in database
GPG Key ID: 279509B499E032B9
3 changed files with 82 additions and 68 deletions

View File

@ -100,7 +100,7 @@ public:
static constexpr uint32_t AUDIO_FRAME_DURATION = 20; static constexpr uint32_t AUDIO_FRAME_DURATION = 20;
static constexpr uint32_t AUDIO_FRAME_SAMPLE_COUNT = static constexpr uint32_t AUDIO_FRAME_SAMPLE_COUNT =
AUDIO_FRAME_DURATION * AUDIO_SAMPLE_RATE / 1000; AUDIO_FRAME_DURATION * AUDIO_SAMPLE_RATE / 1000;
static constexpr uint32_t AUDIO_CHANNELS = 2; static constexpr uint32_t AUDIO_CHANNELS = 1;
signals: signals:
void frameAvailable(const int16_t* pcm, size_t sample_count, uint8_t channels, void frameAvailable(const int16_t* pcm, size_t sample_count, uint8_t channels,

View File

@ -316,7 +316,7 @@ bool OpenAL2::initOutput(const QString& deviceName)
} }
// check for the needed extensions for echo cancelation // check for the needed extensions for echo cancelation
if (echoCancelSupported = (alcIsExtensionPresent(NULL, "ALC_SOFT_loopback") == AL_TRUE)) { if (echoCancelSupported = (alcIsExtensionPresent(alOutDev, "ALC_SOFT_loopback") == AL_TRUE)) {
qDebug() << "Device supports loopback"; qDebug() << "Device supports loopback";
} }
if (alIsExtensionPresent("AL_SOFT_source_latency") == AL_TRUE) { if (alIsExtensionPresent("AL_SOFT_source_latency") == AL_TRUE) {
@ -540,98 +540,89 @@ void OpenAL2::playMono16SoundCleanup()
} }
/** /**
* @brief Called on the captureTimer events to capture audio * @brief Handle audio output
*/ */
void OpenAL2::doAudio() void OpenAL2::doOutput()
{ {
QMutexLocker lock(&audioLock); alcMakeContextCurrent(alOutContext);
ALuint bufids[PROXY_BUFFER_COUNT];
ALint processed = 0, queued = 0;
alGetSourcei(alProxySource, AL_BUFFERS_PROCESSED, &processed);
alGetSourcei(alProxySource, AL_BUFFERS_QUEUED, &queued);
static int outBufcnt = 0; //qDebug() << "Speedtest processed: " << processed << " queued: " << queued;
static double outLatency = 0;
// output section if (processed > 0) {
if(echoCancelSupported && outputInitialized) { // unqueue all processed buffers
alcMakeContextCurrent(alOutContext); alSourceUnqueueBuffers(alProxySource, 1, bufids);
ALuint bufids[PROXY_BUFFER_COUNT]; } else if (queued < PROXY_BUFFER_COUNT) {
ALint processed = 0, queued = 0; // create new buffer until the maximum is reached
alGetSourcei(alProxySource, AL_BUFFERS_PROCESSED, &processed); alGenBuffers(1, bufids);
alGetSourcei(alProxySource, AL_BUFFERS_QUEUED, &queued); } else {
return;
}
//qDebug() << "Speedtest processed: " << processed << " queued: " << queued; LPALGETSOURCEDVSOFT alGetSourcedvSOFT =
reinterpret_cast<LPALGETSOURCEDVSOFT> (alcGetProcAddress(alOutDev, "alGetSourcedvSOFT"));
if (processed > 0) { ALdouble latency[2] = {0};
// unqueue all processed buffers if(alGetSourcedvSOFT) {
alSourceUnqueueBuffers(alProxySource, 1, bufids);
} else if (queued < PROXY_BUFFER_COUNT) {
// create new buffer until the maximum is reached
alGenBuffers(1, bufids);
} else {
return;
}
LPALGETSOURCEDVSOFT alGetSourcedvSOFT =
reinterpret_cast<LPALGETSOURCEDVSOFT> (alcGetProcAddress(alOutDev, "alGetSourcedvSOFT"));
ALdouble latency[2] = {0};
alGetSourcedvSOFT(alProxySource, AL_SEC_OFFSET_LATENCY_SOFT, latency); alGetSourcedvSOFT(alProxySource, AL_SEC_OFFSET_LATENCY_SOFT, latency);
checkAlError(); checkAlError();
//qDebug() << "Playback latency: " << latency[1] << "offset: " << latency[0]; }
outLatency = latency[1]; //qDebug() << "Playback latency: " << latency[1] << "offset: " << latency[0];
ALshort outBuf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS] = {0}; ALshort outBuf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS] = {0};
alcMakeContextCurrent(alProxyContext); alcMakeContextCurrent(alProxyContext);
LPALCRENDERSAMPLESSOFT alcRenderSamplesSOFT = LPALCRENDERSAMPLESSOFT alcRenderSamplesSOFT =
reinterpret_cast<LPALCRENDERSAMPLESSOFT> (alcGetProcAddress(alOutDev, "alcRenderSamplesSOFT")); reinterpret_cast<LPALCRENDERSAMPLESSOFT> (alcGetProcAddress(alOutDev, "alcRenderSamplesSOFT"));
alcRenderSamplesSOFT(alProxyDev, outBuf, AUDIO_FRAME_SAMPLE_COUNT); alcRenderSamplesSOFT(alProxyDev, outBuf, AUDIO_FRAME_SAMPLE_COUNT);
alcMakeContextCurrent(alOutContext); alcMakeContextCurrent(alOutContext);
alBufferData(bufids[0], (AUDIO_CHANNELS == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, outBuf, alBufferData(bufids[0], (AUDIO_CHANNELS == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, outBuf,
AUDIO_FRAME_SAMPLE_COUNT * 2 * AUDIO_CHANNELS, AUDIO_SAMPLE_RATE); AUDIO_FRAME_SAMPLE_COUNT * 2 * AUDIO_CHANNELS, AUDIO_SAMPLE_RATE);
alSourceQueueBuffers(alProxySource, 1, bufids); alSourceQueueBuffers(alProxySource, 1, bufids);
++outBufcnt;
// initialize echo canceler if supported // initialize echo canceler if supported
if(!filterer) { if(!filterer) {
filterer = new_filter_audio(AUDIO_SAMPLE_RATE); filterer = new_filter_audio(AUDIO_SAMPLE_RATE);
int16_t filterLatency = outLatency*1000 + AUDIO_FRAME_DURATION; int16_t filterLatency = latency[1]*1000*2 + AUDIO_FRAME_DURATION;
qDebug() << "Setting filter delay to: " << filterLatency << "ms"; qDebug() << "Setting filter delay to: " << filterLatency << "ms";
set_echo_delay_ms(filterer, filterLatency); set_echo_delay_ms(filterer, filterLatency);
} enable_disable_filters(filterer, 1, 1, 1, 0);
// do echo cancel
pass_audio_output(filterer, outBuf, AUDIO_FRAME_SAMPLE_COUNT);
ALint state;
alGetSourcei(alProxySource, AL_SOURCE_STATE, &state);
if (state != AL_PLAYING) {
qDebug() << "Proxy source underflow detected";
alSourcePlay(alProxySource);
}
} }
// input section // do echo cancel
int retVal = pass_audio_output(filterer, outBuf, AUDIO_FRAME_SAMPLE_COUNT);
if (!alInDev || !inSubscriptions) ALint state;
return; alGetSourcei(alProxySource, AL_SOURCE_STATE, &state);
if (state != AL_PLAYING) {
qDebug() << "Proxy source underflow detected";
alSourcePlay(alProxySource);
}
}
/**
* @brief handles recording of audio frames
*/
void OpenAL2::doInput()
{
ALint curSamples = 0; ALint curSamples = 0;
alcGetIntegerv(alInDev, ALC_CAPTURE_SAMPLES, sizeof(curSamples), &curSamples); alcGetIntegerv(alInDev, ALC_CAPTURE_SAMPLES, sizeof(curSamples), &curSamples);
if (curSamples < AUDIO_FRAME_SAMPLE_COUNT) if (curSamples < AUDIO_FRAME_SAMPLE_COUNT) {
return; return;
// check for dropped buffers in echo cancel
if(outBufcnt > 1) {
qDebug() << "Echo cancel frame dropped";
} }
outBufcnt = 0;
int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS]; int16_t buf[AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS];
alcCaptureSamples(alInDev, buf, AUDIO_FRAME_SAMPLE_COUNT); alcCaptureSamples(alInDev, buf, AUDIO_FRAME_SAMPLE_COUNT);
int retVal = 0;
if(echoCancelSupported && filterer) { if(echoCancelSupported && filterer) {
filter_audio(filterer, buf, AUDIO_FRAME_SAMPLE_COUNT); retVal = filter_audio(filterer, buf, AUDIO_FRAME_SAMPLE_COUNT);
} }
// gain amplification with clipping to 16-bit boundaries
for (quint32 i = 0; i < AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS; ++i) { for (quint32 i = 0; i < AUDIO_FRAME_SAMPLE_COUNT * AUDIO_CHANNELS; ++i) {
// gain amplification with clipping to 16-bit boundaries
int ampPCM = int ampPCM =
qBound<int>(std::numeric_limits<int16_t>::min(), qRound(buf[i] * inputGainFactor()), qBound<int>(std::numeric_limits<int16_t>::min(), qRound(buf[i] * inputGainFactor()),
std::numeric_limits<int16_t>::max()); std::numeric_limits<int16_t>::max());
@ -642,6 +633,27 @@ void OpenAL2::doAudio()
emit Audio::frameAvailable(buf, AUDIO_FRAME_SAMPLE_COUNT, AUDIO_CHANNELS, AUDIO_SAMPLE_RATE); emit Audio::frameAvailable(buf, AUDIO_FRAME_SAMPLE_COUNT, AUDIO_CHANNELS, AUDIO_SAMPLE_RATE);
} }
/**
* @brief Called on the captureTimer events to capture audio
*/
void OpenAL2::doAudio()
{
QMutexLocker lock(&audioLock);
// output section
if(echoCancelSupported && outputInitialized && !peerSources.isEmpty()) {
doOutput();
} else {
kill_filter_audio(filterer);
filterer = nullptr;
}
// input section
if (alInDev && inSubscriptions) {
doInput();
}
}
/** /**
* @brief Returns true if the output device is open * @brief Returns true if the output device is open
*/ */

View File

@ -106,6 +106,8 @@ private:
void playMono16SoundCleanup(); void playMono16SoundCleanup();
void doAudio(); void doAudio();
qreal inputGainFactor() const; qreal inputGainFactor() const;
void doInput();
void doOutput();
private: private:
QThread* audioThread; QThread* audioThread;