From 784b23d05c2210d645d500adb766e9e011e6415a Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 19 Jan 2017 10:33:04 -0800 Subject: [PATCH 1/3] Partial support for floating-point audio output --- libraries/audio-client/src/AudioClient.cpp | 43 +++++++++++++--- libraries/audio/src/AudioRingBuffer.h | 59 ++++++++++++++-------- 2 files changed, 73 insertions(+), 29 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 1e3dc11338..e2d2dd499b 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -433,8 +433,21 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, for (int channelCount : (desiredAudioFormat.channelCount() == 1 ? inputChannels : outputChannels)) { for (int sampleRate : sampleRates) { + // int16_t samples adjustedAudioFormat.setChannelCount(channelCount); adjustedAudioFormat.setSampleRate(sampleRate); + adjustedAudioFormat.setSampleSize(16); + adjustedAudioFormat.setSampleType(QAudioFormat::SignedInt); + + if (audioDevice.isFormatSupported(adjustedAudioFormat)) { + return true; + } + + // float samples + adjustedAudioFormat.setChannelCount(channelCount); + adjustedAudioFormat.setSampleRate(sampleRate); + adjustedAudioFormat.setSampleSize(32); + adjustedAudioFormat.setSampleType(QAudioFormat::Float); if (audioDevice.isFormatSupported(adjustedAudioFormat)) { return true; @@ -1545,11 +1558,15 @@ float AudioClient::gainForSource(float distance, float volume) { return gain; } -qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { +qint64 AudioClient::AudioOutputIODevice::readData(char* data, qint64 maxSize) { + + // data can be int16_t* or float* + int sampleSize = _audio->_outputFormat.sampleSize() / 8; + bool isFloat = _audio->_outputFormat.sampleType() == QAudioFormat::SampleType::Float; // samples requested from OUTPUT_CHANNEL_COUNT int deviceChannelCount = _audio->_outputFormat.channelCount(); - int samplesRequested = (int)(maxSize / AudioConstants::SAMPLE_SIZE) * OUTPUT_CHANNEL_COUNT / deviceChannelCount; + int samplesRequested = (int)(maxSize / sampleSize) * OUTPUT_CHANNEL_COUNT / deviceChannelCount; int samplesPopped; int bytesWritten; @@ -1559,14 +1576,24 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput(); // if required, upmix or downmix to deviceChannelCount - if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) { - lastPopOutput.readSamples((int16_t*)data, samplesPopped); - } else if (deviceChannelCount > OUTPUT_CHANNEL_COUNT) { - lastPopOutput.readSamplesWithUpmix((int16_t*)data, samplesPopped, deviceChannelCount - OUTPUT_CHANNEL_COUNT); + if (isFloat) { + if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) { + lastPopOutput.readSamples((float*)data, samplesPopped); + } else if (deviceChannelCount > OUTPUT_CHANNEL_COUNT) { + lastPopOutput.readSamplesWithUpmix((float*)data, samplesPopped, deviceChannelCount - OUTPUT_CHANNEL_COUNT); + } else { + lastPopOutput.readSamplesWithDownmix((float*)data, samplesPopped); + } } else { - lastPopOutput.readSamplesWithDownmix((int16_t*)data, samplesPopped); + if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) { + lastPopOutput.readSamples((int16_t*)data, samplesPopped); + } else if (deviceChannelCount > OUTPUT_CHANNEL_COUNT) { + lastPopOutput.readSamplesWithUpmix((int16_t*)data, samplesPopped, deviceChannelCount - OUTPUT_CHANNEL_COUNT); + } else { + lastPopOutput.readSamplesWithDownmix((int16_t*)data, samplesPopped); + } } - bytesWritten = (samplesPopped * AudioConstants::SAMPLE_SIZE) * deviceChannelCount / OUTPUT_CHANNEL_COUNT; + bytesWritten = (samplesPopped * sampleSize) * deviceChannelCount / OUTPUT_CHANNEL_COUNT; } else { // nothing on network, don't grab anything from injectors, and just return 0s // this will flood the log: qCDebug(audioclient, "empty/partial network buffer"); diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 29e7a9e998..38ca319156 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -103,10 +103,11 @@ public: ConstIterator operator+(int i); ConstIterator operator-(int i); - void readSamples(int16_t* dest, int numSamples); - void readSamplesWithFade(int16_t* dest, int numSamples, float fade); - void readSamplesWithUpmix(int16_t* dest, int numSamples, int numExtraChannels); - void readSamplesWithDownmix(int16_t* dest, int numSamples); + // dest can be int16_t* or float* + template void readSamples(T* dest, int numSamples); + template void readSamplesWithFade(T* dest, int numSamples, float fade); + template void readSamplesWithUpmix(T* dest, int numSamples, int numExtraChannels); + template void readSamplesWithDownmix(T* dest, int numSamples); private: int16_t* atShiftedBy(int i); @@ -203,6 +204,28 @@ inline int16_t* AudioRingBuffer::ConstIterator::atShiftedBy(int i) { return _bufferFirst + i; } +inline AudioRingBuffer::ConstIterator AudioRingBuffer::nextOutput() const { + return ConstIterator(_buffer, _bufferLength, _nextOutput); +} + +inline AudioRingBuffer::ConstIterator AudioRingBuffer::lastFrameWritten() const { + return ConstIterator(_buffer, _bufferLength, _endOfLastWrite) - _numFrameSamples; +} + +// +// dest can be int16_t* or float* +// +template +inline void AudioRingBuffer::ConstIterator::readSamples(T* dest, int numSamples) { + int16_t* at = _at; + for (int i = 0; i < numSamples; i++) { + *dest++ = (T)*at; + at = (at == _bufferLast) ? _bufferFirst : at + 1; + } +} + +// fast path for int16_t* +template<> inline void AudioRingBuffer::ConstIterator::readSamples(int16_t* dest, int numSamples) { auto samplesToEnd = _bufferLast - _at + 1; @@ -218,16 +241,17 @@ inline void AudioRingBuffer::ConstIterator::readSamples(int16_t* dest, int numSa } } -inline void AudioRingBuffer::ConstIterator::readSamplesWithFade(int16_t* dest, int numSamples, float fade) { +template +inline void AudioRingBuffer::ConstIterator::readSamplesWithFade(T* dest, int numSamples, float fade) { int16_t* at = _at; for (int i = 0; i < numSamples; i++) { - *dest = (float)*at * fade; - ++dest; + *dest++ = (T)(*at * fade); at = (at == _bufferLast) ? _bufferFirst : at + 1; } } -inline void AudioRingBuffer::ConstIterator::readSamplesWithUpmix(int16_t* dest, int numSamples, int numExtraChannels) { +template +inline void AudioRingBuffer::ConstIterator::readSamplesWithUpmix(T* dest, int numSamples, int numExtraChannels) { int16_t* at = _at; for (int i = 0; i < numSamples/2; i++) { @@ -238,15 +262,16 @@ inline void AudioRingBuffer::ConstIterator::readSamplesWithUpmix(int16_t* dest, at = (at == _bufferLast) ? _bufferFirst : at + 1; // write 2 + N samples - *dest++ = left; - *dest++ = right; + *dest++ = (T)left; + *dest++ = (T)right; for (int n = 0; n < numExtraChannels; n++) { - *dest++ = 0; + *dest++ = (T)0; } } } -inline void AudioRingBuffer::ConstIterator::readSamplesWithDownmix(int16_t* dest, int numSamples) { +template +inline void AudioRingBuffer::ConstIterator::readSamplesWithDownmix(T* dest, int numSamples) { int16_t* at = _at; for (int i = 0; i < numSamples/2; i++) { @@ -257,16 +282,8 @@ inline void AudioRingBuffer::ConstIterator::readSamplesWithDownmix(int16_t* dest at = (at == _bufferLast) ? _bufferFirst : at + 1; // write 1 sample - *dest++ = (int16_t)((left + right) / 2); + *dest++ = (T)((left + right) / 2); } } -inline AudioRingBuffer::ConstIterator AudioRingBuffer::nextOutput() const { - return ConstIterator(_buffer, _bufferLength, _nextOutput); -} - -inline AudioRingBuffer::ConstIterator AudioRingBuffer::lastFrameWritten() const { - return ConstIterator(_buffer, _bufferLength, _endOfLastWrite) - _numFrameSamples; -} - #endif // hifi_AudioRingBuffer_h From 4d6eea9a63a6c447aade4d462e590ac8d679bec7 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 20 Jan 2017 16:40:32 -0800 Subject: [PATCH 2/3] Updated WASAPI plugins to force dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT --- cmake/externals/wasapi/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 7cfca4f3ba..b085eefb0c 100644 --- a/cmake/externals/wasapi/CMakeLists.txt +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -6,8 +6,8 @@ if (WIN32) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi5.zip - URL_MD5 0530753e855ffc00232cc969bf1c84a8 + URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi6.zip + URL_MD5 fcac808c1ba0b0f5b44ea06e2612ebab CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" From e1b313d9aa2ff05cf240d912223e540e03c757a4 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 20 Jan 2017 16:43:46 -0800 Subject: [PATCH 3/3] Remove support for floating-point audio (not needed) --- libraries/audio-client/src/AudioClient.cpp | 43 +++------------- libraries/audio/src/AudioRingBuffer.h | 59 ++++++++-------------- 2 files changed, 29 insertions(+), 73 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index e2d2dd499b..1e3dc11338 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -433,21 +433,8 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, for (int channelCount : (desiredAudioFormat.channelCount() == 1 ? inputChannels : outputChannels)) { for (int sampleRate : sampleRates) { - // int16_t samples adjustedAudioFormat.setChannelCount(channelCount); adjustedAudioFormat.setSampleRate(sampleRate); - adjustedAudioFormat.setSampleSize(16); - adjustedAudioFormat.setSampleType(QAudioFormat::SignedInt); - - if (audioDevice.isFormatSupported(adjustedAudioFormat)) { - return true; - } - - // float samples - adjustedAudioFormat.setChannelCount(channelCount); - adjustedAudioFormat.setSampleRate(sampleRate); - adjustedAudioFormat.setSampleSize(32); - adjustedAudioFormat.setSampleType(QAudioFormat::Float); if (audioDevice.isFormatSupported(adjustedAudioFormat)) { return true; @@ -1558,15 +1545,11 @@ float AudioClient::gainForSource(float distance, float volume) { return gain; } -qint64 AudioClient::AudioOutputIODevice::readData(char* data, qint64 maxSize) { - - // data can be int16_t* or float* - int sampleSize = _audio->_outputFormat.sampleSize() / 8; - bool isFloat = _audio->_outputFormat.sampleType() == QAudioFormat::SampleType::Float; +qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { // samples requested from OUTPUT_CHANNEL_COUNT int deviceChannelCount = _audio->_outputFormat.channelCount(); - int samplesRequested = (int)(maxSize / sampleSize) * OUTPUT_CHANNEL_COUNT / deviceChannelCount; + int samplesRequested = (int)(maxSize / AudioConstants::SAMPLE_SIZE) * OUTPUT_CHANNEL_COUNT / deviceChannelCount; int samplesPopped; int bytesWritten; @@ -1576,24 +1559,14 @@ qint64 AudioClient::AudioOutputIODevice::readData(char* data, qint64 maxSize) { AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput(); // if required, upmix or downmix to deviceChannelCount - if (isFloat) { - if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) { - lastPopOutput.readSamples((float*)data, samplesPopped); - } else if (deviceChannelCount > OUTPUT_CHANNEL_COUNT) { - lastPopOutput.readSamplesWithUpmix((float*)data, samplesPopped, deviceChannelCount - OUTPUT_CHANNEL_COUNT); - } else { - lastPopOutput.readSamplesWithDownmix((float*)data, samplesPopped); - } + if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) { + lastPopOutput.readSamples((int16_t*)data, samplesPopped); + } else if (deviceChannelCount > OUTPUT_CHANNEL_COUNT) { + lastPopOutput.readSamplesWithUpmix((int16_t*)data, samplesPopped, deviceChannelCount - OUTPUT_CHANNEL_COUNT); } else { - if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) { - lastPopOutput.readSamples((int16_t*)data, samplesPopped); - } else if (deviceChannelCount > OUTPUT_CHANNEL_COUNT) { - lastPopOutput.readSamplesWithUpmix((int16_t*)data, samplesPopped, deviceChannelCount - OUTPUT_CHANNEL_COUNT); - } else { - lastPopOutput.readSamplesWithDownmix((int16_t*)data, samplesPopped); - } + lastPopOutput.readSamplesWithDownmix((int16_t*)data, samplesPopped); } - bytesWritten = (samplesPopped * sampleSize) * deviceChannelCount / OUTPUT_CHANNEL_COUNT; + bytesWritten = (samplesPopped * AudioConstants::SAMPLE_SIZE) * deviceChannelCount / OUTPUT_CHANNEL_COUNT; } else { // nothing on network, don't grab anything from injectors, and just return 0s // this will flood the log: qCDebug(audioclient, "empty/partial network buffer"); diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 38ca319156..29e7a9e998 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -103,11 +103,10 @@ public: ConstIterator operator+(int i); ConstIterator operator-(int i); - // dest can be int16_t* or float* - template void readSamples(T* dest, int numSamples); - template void readSamplesWithFade(T* dest, int numSamples, float fade); - template void readSamplesWithUpmix(T* dest, int numSamples, int numExtraChannels); - template void readSamplesWithDownmix(T* dest, int numSamples); + void readSamples(int16_t* dest, int numSamples); + void readSamplesWithFade(int16_t* dest, int numSamples, float fade); + void readSamplesWithUpmix(int16_t* dest, int numSamples, int numExtraChannels); + void readSamplesWithDownmix(int16_t* dest, int numSamples); private: int16_t* atShiftedBy(int i); @@ -204,28 +203,6 @@ inline int16_t* AudioRingBuffer::ConstIterator::atShiftedBy(int i) { return _bufferFirst + i; } -inline AudioRingBuffer::ConstIterator AudioRingBuffer::nextOutput() const { - return ConstIterator(_buffer, _bufferLength, _nextOutput); -} - -inline AudioRingBuffer::ConstIterator AudioRingBuffer::lastFrameWritten() const { - return ConstIterator(_buffer, _bufferLength, _endOfLastWrite) - _numFrameSamples; -} - -// -// dest can be int16_t* or float* -// -template -inline void AudioRingBuffer::ConstIterator::readSamples(T* dest, int numSamples) { - int16_t* at = _at; - for (int i = 0; i < numSamples; i++) { - *dest++ = (T)*at; - at = (at == _bufferLast) ? _bufferFirst : at + 1; - } -} - -// fast path for int16_t* -template<> inline void AudioRingBuffer::ConstIterator::readSamples(int16_t* dest, int numSamples) { auto samplesToEnd = _bufferLast - _at + 1; @@ -241,17 +218,16 @@ inline void AudioRingBuffer::ConstIterator::readSamples(int16_t* dest, int numSa } } -template -inline void AudioRingBuffer::ConstIterator::readSamplesWithFade(T* dest, int numSamples, float fade) { +inline void AudioRingBuffer::ConstIterator::readSamplesWithFade(int16_t* dest, int numSamples, float fade) { int16_t* at = _at; for (int i = 0; i < numSamples; i++) { - *dest++ = (T)(*at * fade); + *dest = (float)*at * fade; + ++dest; at = (at == _bufferLast) ? _bufferFirst : at + 1; } } -template -inline void AudioRingBuffer::ConstIterator::readSamplesWithUpmix(T* dest, int numSamples, int numExtraChannels) { +inline void AudioRingBuffer::ConstIterator::readSamplesWithUpmix(int16_t* dest, int numSamples, int numExtraChannels) { int16_t* at = _at; for (int i = 0; i < numSamples/2; i++) { @@ -262,16 +238,15 @@ inline void AudioRingBuffer::ConstIterator::readSamplesWithUpmix(T* dest, int nu at = (at == _bufferLast) ? _bufferFirst : at + 1; // write 2 + N samples - *dest++ = (T)left; - *dest++ = (T)right; + *dest++ = left; + *dest++ = right; for (int n = 0; n < numExtraChannels; n++) { - *dest++ = (T)0; + *dest++ = 0; } } } -template -inline void AudioRingBuffer::ConstIterator::readSamplesWithDownmix(T* dest, int numSamples) { +inline void AudioRingBuffer::ConstIterator::readSamplesWithDownmix(int16_t* dest, int numSamples) { int16_t* at = _at; for (int i = 0; i < numSamples/2; i++) { @@ -282,8 +257,16 @@ inline void AudioRingBuffer::ConstIterator::readSamplesWithDownmix(T* dest, int at = (at == _bufferLast) ? _bufferFirst : at + 1; // write 1 sample - *dest++ = (T)((left + right) / 2); + *dest++ = (int16_t)((left + right) / 2); } } +inline AudioRingBuffer::ConstIterator AudioRingBuffer::nextOutput() const { + return ConstIterator(_buffer, _bufferLength, _nextOutput); +} + +inline AudioRingBuffer::ConstIterator AudioRingBuffer::lastFrameWritten() const { + return ConstIterator(_buffer, _bufferLength, _endOfLastWrite) - _numFrameSamples; +} + #endif // hifi_AudioRingBuffer_h