mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 19:57:11 +02:00
In AudioClient, replace all SOXR with AudioSRC. Used for input-to-network, network-to-output, and loopback resampling.
This commit is contained in:
parent
4b9c2ed6b6
commit
c4bc5233b1
2 changed files with 39 additions and 104 deletions
|
@ -47,8 +47,6 @@ extern "C" {
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <soxr.h>
|
|
||||||
|
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
#include <PositionalAudioStream.h>
|
#include <PositionalAudioStream.h>
|
||||||
|
@ -189,18 +187,6 @@ QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& de
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
soxr_datatype_t soxrDataTypeFromQAudioFormat(const QAudioFormat& audioFormat) {
|
|
||||||
if (audioFormat.sampleType() == QAudioFormat::Float) {
|
|
||||||
return SOXR_FLOAT32_I;
|
|
||||||
} else {
|
|
||||||
if (audioFormat.sampleSize() == 16) {
|
|
||||||
return SOXR_INT16_I;
|
|
||||||
} else {
|
|
||||||
return SOXR_INT32_I;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int numDestinationSamplesRequired(const QAudioFormat& sourceFormat, const QAudioFormat& destinationFormat,
|
int numDestinationSamplesRequired(const QAudioFormat& sourceFormat, const QAudioFormat& destinationFormat,
|
||||||
int numSourceSamples) {
|
int numSourceSamples) {
|
||||||
float ratio = (float) destinationFormat.channelCount() / sourceFormat.channelCount();
|
float ratio = (float) destinationFormat.channelCount() / sourceFormat.channelCount();
|
||||||
|
@ -350,7 +336,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
||||||
// use 22050, resample but closer to 24
|
// use 22050, resample but closer to 24
|
||||||
adjustedAudioFormat.setSampleRate(HALF_FORTY_FOUR);
|
adjustedAudioFormat.setSampleRate(HALF_FORTY_FOUR);
|
||||||
} else if (audioDevice.supportedSampleRates().contains(FORTY_FOUR)) {
|
} else if (audioDevice.supportedSampleRates().contains(FORTY_FOUR)) {
|
||||||
// use 48000, libsoxr will resample
|
// use 48000, resample
|
||||||
adjustedAudioFormat.setSampleRate(FORTY_FOUR);
|
adjustedAudioFormat.setSampleRate(FORTY_FOUR);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -391,10 +377,10 @@ bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationS
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
soxr_error_t possibleResampling(soxr_t resampler,
|
void possibleResampling(AudioSRC* resampler,
|
||||||
const int16_t* sourceSamples, int16_t* destinationSamples,
|
const int16_t* sourceSamples, int16_t* destinationSamples,
|
||||||
unsigned int numSourceSamples, unsigned int numDestinationSamples,
|
unsigned int numSourceSamples, unsigned int numDestinationSamples,
|
||||||
const QAudioFormat& sourceAudioFormat, const QAudioFormat& destinationAudioFormat) {
|
const QAudioFormat& sourceAudioFormat, const QAudioFormat& destinationAudioFormat) {
|
||||||
|
|
||||||
if (numSourceSamples > 0) {
|
if (numSourceSamples > 0) {
|
||||||
if (!resampler) {
|
if (!resampler) {
|
||||||
|
@ -403,32 +389,19 @@ soxr_error_t possibleResampling(soxr_t resampler,
|
||||||
// no conversion, we can copy the samples directly across
|
// no conversion, we can copy the samples directly across
|
||||||
memcpy(destinationSamples, sourceSamples, numSourceSamples * sizeof(int16_t));
|
memcpy(destinationSamples, sourceSamples, numSourceSamples * sizeof(int16_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
} else {
|
} else {
|
||||||
soxr_error_t resampleError = 0;
|
|
||||||
|
|
||||||
if (sourceAudioFormat.channelCount() != destinationAudioFormat.channelCount()) {
|
if (sourceAudioFormat.channelCount() != destinationAudioFormat.channelCount()) {
|
||||||
float channelCountRatio = (float) destinationAudioFormat.channelCount() / sourceAudioFormat.channelCount();
|
float channelCountRatio = (float)destinationAudioFormat.channelCount() / sourceAudioFormat.channelCount();
|
||||||
|
|
||||||
int numChannelCoversionSamples = (int) (numSourceSamples * channelCountRatio);
|
int numChannelCoversionSamples = (int)(numSourceSamples * channelCountRatio);
|
||||||
int16_t* channelConversionSamples = new int16_t[numChannelCoversionSamples];
|
int16_t* channelConversionSamples = new int16_t[numChannelCoversionSamples];
|
||||||
|
|
||||||
sampleChannelConversion(sourceSamples, channelConversionSamples,
|
sampleChannelConversion(sourceSamples, channelConversionSamples,
|
||||||
numSourceSamples,
|
numSourceSamples,
|
||||||
sourceAudioFormat, destinationAudioFormat);
|
sourceAudioFormat, destinationAudioFormat);
|
||||||
|
|
||||||
size_t numDestinationSamplesActual = 0;
|
resampler->render(channelConversionSamples, destinationSamples, numChannelCoversionSamples);
|
||||||
resampleError = soxr_process(resampler,
|
|
||||||
channelConversionSamples, numChannelCoversionSamples, NULL,
|
|
||||||
destinationSamples, numDestinationSamples, &numDestinationSamplesActual);
|
|
||||||
|
|
||||||
// return silence instead of playing garbage samples
|
|
||||||
if (numDestinationSamplesActual < numDestinationSamples) {
|
|
||||||
unsigned int nBytes = (numDestinationSamples - numDestinationSamplesActual) * destinationAudioFormat.channelCount() * sizeof(int16_t);
|
|
||||||
memset(&destinationSamples[numDestinationSamplesActual * destinationAudioFormat.channelCount()], 0, nBytes);
|
|
||||||
qCDebug(audioclient) << "SOXR: padded with" << nBytes << "bytes of silence";
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] channelConversionSamples;
|
delete[] channelConversionSamples;
|
||||||
} else {
|
} else {
|
||||||
|
@ -441,56 +414,12 @@ soxr_error_t possibleResampling(soxr_t resampler,
|
||||||
numAdjustedDestinationSamples /= 2;
|
numAdjustedDestinationSamples /= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t numAdjustedDestinationSamplesActual = 0;
|
resampler->render(sourceSamples, destinationSamples, numAdjustedSourceSamples);
|
||||||
resampleError = soxr_process(resampler,
|
|
||||||
sourceSamples, numAdjustedSourceSamples, NULL,
|
|
||||||
destinationSamples, numAdjustedDestinationSamples, &numAdjustedDestinationSamplesActual);
|
|
||||||
|
|
||||||
// return silence instead of playing garbage samples
|
|
||||||
if (numAdjustedDestinationSamplesActual < numAdjustedDestinationSamples) {
|
|
||||||
unsigned int nBytes = (numAdjustedDestinationSamples - numAdjustedDestinationSamplesActual) * destinationAudioFormat.channelCount() * sizeof(int16_t);
|
|
||||||
memset(&destinationSamples[numAdjustedDestinationSamplesActual * destinationAudioFormat.channelCount()], 0, nBytes);
|
|
||||||
qCDebug(audioclient) << "SOXR: padded with" << nBytes << "bytes of silence";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resampleError;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
soxr_t soxrResamplerFromInputFormatToOutputFormat(const QAudioFormat& sourceAudioFormat,
|
|
||||||
const QAudioFormat& destinationAudioFormat) {
|
|
||||||
soxr_error_t soxrError;
|
|
||||||
|
|
||||||
// setup soxr_io_spec_t for input and output
|
|
||||||
soxr_io_spec_t inputToNetworkSpec = soxr_io_spec(soxrDataTypeFromQAudioFormat(sourceAudioFormat),
|
|
||||||
soxrDataTypeFromQAudioFormat(destinationAudioFormat));
|
|
||||||
|
|
||||||
// setup soxr_quality_spec_t for quality options
|
|
||||||
soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0);
|
|
||||||
|
|
||||||
int channelCount = (sourceAudioFormat.channelCount() == 2 && destinationAudioFormat.channelCount() == 2)
|
|
||||||
? 2 : 1;
|
|
||||||
|
|
||||||
soxr_t newResampler = soxr_create(sourceAudioFormat.sampleRate(),
|
|
||||||
destinationAudioFormat.sampleRate(),
|
|
||||||
channelCount,
|
|
||||||
&soxrError, &inputToNetworkSpec, &qualitySpec, 0);
|
|
||||||
|
|
||||||
if (soxrError) {
|
|
||||||
qCDebug(audioclient) << "There was an error setting up the soxr resampler -" << "soxr error code was " << soxrError;
|
|
||||||
|
|
||||||
soxr_delete(newResampler);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newResampler;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioClient::start() {
|
void AudioClient::start() {
|
||||||
|
|
||||||
// set up the desired audio format
|
// set up the desired audio format
|
||||||
|
@ -546,7 +475,7 @@ void AudioClient::stop() {
|
||||||
switchOutputToAudioDevice(QAudioDeviceInfo());
|
switchOutputToAudioDevice(QAudioDeviceInfo());
|
||||||
|
|
||||||
if (_loopbackResampler) {
|
if (_loopbackResampler) {
|
||||||
soxr_delete(_loopbackResampler);
|
delete _loopbackResampler;
|
||||||
_loopbackResampler = NULL;
|
_loopbackResampler = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -767,11 +696,12 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
||||||
// do we need to setup a resampler?
|
// do we need to setup a resampler?
|
||||||
if (_inputFormat.sampleRate() != _outputFormat.sampleRate() && !_loopbackResampler) {
|
if (_inputFormat.sampleRate() != _outputFormat.sampleRate() && !_loopbackResampler) {
|
||||||
qCDebug(audioclient) << "Attemping to create a resampler for input format to output format for audio loopback.";
|
qCDebug(audioclient) << "Attemping to create a resampler for input format to output format for audio loopback.";
|
||||||
_loopbackResampler = soxrResamplerFromInputFormatToOutputFormat(_inputFormat, _outputFormat);
|
|
||||||
|
|
||||||
if (!_loopbackResampler) {
|
assert(_inputFormat.sampleSize() == 16);
|
||||||
return;
|
assert(_outputFormat.sampleSize() == 16);
|
||||||
}
|
int channelCount = (_inputFormat.channelCount() == 2 && _outputFormat.channelCount() == 2) ? 2 : 1;
|
||||||
|
|
||||||
|
_loopbackResampler = new AudioSRC(_inputFormat.sampleRate(), _outputFormat.sampleRate(), channelCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QByteArray reverbAlone; // Intermediary for local reverb with no echo
|
static QByteArray reverbAlone; // Intermediary for local reverb with no echo
|
||||||
|
@ -1099,7 +1029,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
|
||||||
|
|
||||||
if (_inputToNetworkResampler) {
|
if (_inputToNetworkResampler) {
|
||||||
// if we were using an input to network resampler, delete it here
|
// if we were using an input to network resampler, delete it here
|
||||||
soxr_delete(_inputToNetworkResampler);
|
delete _inputToNetworkResampler;
|
||||||
_inputToNetworkResampler = NULL;
|
_inputToNetworkResampler = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1111,15 +1041,17 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
|
||||||
qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat;
|
qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat;
|
||||||
|
|
||||||
// we've got the best we can get for input
|
// we've got the best we can get for input
|
||||||
// if required, setup a soxr resampler for this input to our desired network format
|
// if required, setup a resampler for this input to our desired network format
|
||||||
if (_inputFormat != _desiredInputFormat
|
if (_inputFormat != _desiredInputFormat
|
||||||
&& _inputFormat.sampleRate() != _desiredInputFormat.sampleRate()) {
|
&& _inputFormat.sampleRate() != _desiredInputFormat.sampleRate()) {
|
||||||
qCDebug(audioclient) << "Attemping to create a soxr resampler for input format to network format.";
|
qCDebug(audioclient) << "Attemping to create a resampler for input format to network format.";
|
||||||
_inputToNetworkResampler = soxrResamplerFromInputFormatToOutputFormat(_inputFormat, _desiredInputFormat);
|
|
||||||
|
assert(_inputFormat.sampleSize() == 16);
|
||||||
|
assert(_desiredInputFormat.sampleSize() == 16);
|
||||||
|
int channelCount = (_inputFormat.channelCount() == 2 && _desiredInputFormat.channelCount() == 2) ? 2 : 1;
|
||||||
|
|
||||||
|
_inputToNetworkResampler = new AudioSRC(_inputFormat.sampleRate(), _desiredInputFormat.sampleRate(), channelCount);
|
||||||
|
|
||||||
if (!_inputToNetworkResampler) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
qCDebug(audioclient) << "No resampling required for audio input to match desired network format.";
|
qCDebug(audioclient) << "No resampling required for audio input to match desired network format.";
|
||||||
}
|
}
|
||||||
|
@ -1194,13 +1126,13 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
||||||
|
|
||||||
if (_networkToOutputResampler) {
|
if (_networkToOutputResampler) {
|
||||||
// if we were using an input to network resampler, delete it here
|
// if we were using an input to network resampler, delete it here
|
||||||
soxr_delete(_networkToOutputResampler);
|
delete _networkToOutputResampler;
|
||||||
_networkToOutputResampler = NULL;
|
_networkToOutputResampler = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_loopbackResampler) {
|
if (_loopbackResampler) {
|
||||||
// if we were using an input to output resample, delete it here
|
// if we were using an input to output resample, delete it here
|
||||||
soxr_delete(_loopbackResampler);
|
delete _loopbackResampler;
|
||||||
_loopbackResampler = NULL;
|
_loopbackResampler = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1212,15 +1144,17 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
||||||
qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat;
|
qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat;
|
||||||
|
|
||||||
// we've got the best we can get for input
|
// we've got the best we can get for input
|
||||||
// if required, setup a soxr resampler for this input to our desired network format
|
// if required, setup a resampler for this input to our desired network format
|
||||||
if (_desiredOutputFormat != _outputFormat
|
if (_desiredOutputFormat != _outputFormat
|
||||||
&& _desiredOutputFormat.sampleRate() != _outputFormat.sampleRate()) {
|
&& _desiredOutputFormat.sampleRate() != _outputFormat.sampleRate()) {
|
||||||
qCDebug(audioclient) << "Attemping to create a resampler for network format to output format.";
|
qCDebug(audioclient) << "Attemping to create a resampler for network format to output format.";
|
||||||
_networkToOutputResampler = soxrResamplerFromInputFormatToOutputFormat(_desiredOutputFormat, _outputFormat);
|
|
||||||
|
|
||||||
if (!_networkToOutputResampler) {
|
assert(_desiredOutputFormat.sampleSize() == 16);
|
||||||
return false;
|
assert(_outputFormat.sampleSize() == 16);
|
||||||
}
|
int channelCount = (_desiredOutputFormat.channelCount() == 2 && _outputFormat.channelCount() == 2) ? 2 : 1;
|
||||||
|
|
||||||
|
_networkToOutputResampler = new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), channelCount);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
qCDebug(audioclient) << "No resampling required for network output to match actual output format.";
|
qCDebug(audioclient) << "No resampling required for network output to match actual output format.";
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
|
|
||||||
#include "AudioIOStats.h"
|
#include "AudioIOStats.h"
|
||||||
#include "AudioNoiseGate.h"
|
#include "AudioNoiseGate.h"
|
||||||
|
#include "AudioSRC.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#pragma warning( push )
|
#pragma warning( push )
|
||||||
|
@ -72,7 +73,7 @@ static const quint64 DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_PERIOD = 10 * 1000; /
|
||||||
class QAudioInput;
|
class QAudioInput;
|
||||||
class QAudioOutput;
|
class QAudioOutput;
|
||||||
class QIODevice;
|
class QIODevice;
|
||||||
struct soxr;
|
|
||||||
typedef struct ty_gverb ty_gverb;
|
typedef struct ty_gverb ty_gverb;
|
||||||
|
|
||||||
typedef glm::vec3 (*AudioPositionGetter)();
|
typedef glm::vec3 (*AudioPositionGetter)();
|
||||||
|
@ -262,10 +263,10 @@ private:
|
||||||
AudioEffectOptions* _reverbOptions;
|
AudioEffectOptions* _reverbOptions;
|
||||||
ty_gverb* _gverb;
|
ty_gverb* _gverb;
|
||||||
|
|
||||||
// possible soxr streams needed for resample
|
// possible streams needed for resample
|
||||||
soxr* _inputToNetworkResampler;
|
AudioSRC* _inputToNetworkResampler;
|
||||||
soxr* _networkToOutputResampler;
|
AudioSRC* _networkToOutputResampler;
|
||||||
soxr* _loopbackResampler;
|
AudioSRC* _loopbackResampler;
|
||||||
|
|
||||||
// Adds Reverb
|
// Adds Reverb
|
||||||
ty_gverb* createGverbFilter();
|
ty_gverb* createGverbFilter();
|
||||||
|
|
Loading…
Reference in a new issue