From 2a9d8d923831d733975b02c5dcaf3ce644c86aa1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Sep 2014 07:04:16 -0700 Subject: [PATCH 1/3] flip default for enable filter to be disable filter to work around settings bug --- assignment-client/src/audio/AudioMixer.cpp | 6 ++++-- domain-server/resources/web/settings/describe.json | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index c872906d63..0477dfed84 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -690,10 +690,12 @@ void AudioMixer::run() { qDebug() << "Stream stats will be printed to stdout"; } - const QString FILTER_KEY = "J-enable-filter"; - _enableFilter = audioGroupObject[FILTER_KEY].toBool(); + const QString FILTER_KEY = "J-disable-filter"; + _enableFilter = !audioGroupObject[FILTER_KEY].toBool(); if (_enableFilter) { qDebug() << "Filter enabled"; + } else { + qDebug() << "Filter disabled"; } const QString UNATTENUATED_ZONE_KEY = "Z-unattenuated-zone"; diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index 2ea0aec0c7..c68d42d3e8 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -57,17 +57,17 @@ "help": "If enabled, audio upstream and downstream stats of each agent will be printed each second to stdout", "default": false }, + "J-disable-filter": { + "type": "checkbox", + "label": "Disable Positional Filter", + "help": "If checked the positional audio stream will not use a lowpass filter", + "default": false + }, "Z-unattenuated-zone": { "label": "Unattenuated Zone", "help": "Boxes for source and listener (corner x, corner y, corner z, size x, size y, size z, corner x, corner y, corner z, size x, size y, size z)", "placeholder": "no zone", "default": "" - }, - "J-enable-filter": { - "type": "checkbox", - "label": "Enable Positional Filter", - "help": "If enabled, positional audio stream uses lowpass filter", - "default": true } } } From c30ef9c331e0e0587d7c62f1334a93af98773970 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Sep 2014 08:52:23 -0700 Subject: [PATCH 2/3] Revert "flip default for enable filter to be disable filter to work around settings bug" This reverts commit 2a9d8d923831d733975b02c5dcaf3ce644c86aa1. --- assignment-client/src/audio/AudioMixer.cpp | 6 ++---- domain-server/resources/web/settings/describe.json | 12 ++++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 0477dfed84..c872906d63 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -690,12 +690,10 @@ void AudioMixer::run() { qDebug() << "Stream stats will be printed to stdout"; } - const QString FILTER_KEY = "J-disable-filter"; - _enableFilter = !audioGroupObject[FILTER_KEY].toBool(); + const QString FILTER_KEY = "J-enable-filter"; + _enableFilter = audioGroupObject[FILTER_KEY].toBool(); if (_enableFilter) { qDebug() << "Filter enabled"; - } else { - qDebug() << "Filter disabled"; } const QString UNATTENUATED_ZONE_KEY = "Z-unattenuated-zone"; diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index c68d42d3e8..2ea0aec0c7 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -57,17 +57,17 @@ "help": "If enabled, audio upstream and downstream stats of each agent will be printed each second to stdout", "default": false }, - "J-disable-filter": { - "type": "checkbox", - "label": "Disable Positional Filter", - "help": "If checked the positional audio stream will not use a lowpass filter", - "default": false - }, "Z-unattenuated-zone": { "label": "Unattenuated Zone", "help": "Boxes for source and listener (corner x, corner y, corner z, size x, size y, size z, corner x, corner y, corner z, size x, size y, size z)", "placeholder": "no zone", "default": "" + }, + "J-enable-filter": { + "type": "checkbox", + "label": "Enable Positional Filter", + "help": "If enabled, positional audio stream uses lowpass filter", + "default": true } } } From ab447e20ee2da070f5bd5bc22736a1a6006a2248 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Sep 2014 12:48:47 -0700 Subject: [PATCH 3/3] move filter to single instance in mixer, and do filtering on pre-mix buffer not the mixed buffer --- assignment-client/src/audio/AudioMixer.cpp | 35 ++++++++++++------- assignment-client/src/audio/AudioMixer.h | 13 +++++-- libraries/audio/src/PositionalAudioStream.cpp | 4 --- libraries/audio/src/PositionalAudioStream.h | 8 ----- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index c872906d63..9d56e02a67 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -57,6 +57,8 @@ #include "AvatarAudioStream.h" #include "InjectedAudioStream.h" + + #include "AudioMixer.h" const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f; @@ -92,7 +94,9 @@ AudioMixer::AudioMixer(const QByteArray& packet) : _timeSpentPerHashMatchCallStats(0, READ_DATAGRAMS_STATS_WINDOW_SECONDS), _readPendingCallsPerSecondStats(1, READ_DATAGRAMS_STATS_WINDOW_SECONDS) { - + // constant defined in AudioMixer.h. However, we don't want to include this here + // we will soon find a better common home for these audio-related constants + _penumbraFilter.initialize(SAMPLE_RATE, (NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)) / 2); } AudioMixer::~AudioMixer() { @@ -257,6 +261,7 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* // we need to do several things in this process: // 1) convert from mono to stereo by copying each input sample into the left and right output samples + // 2) // 2) apply an attenuation AND fade to all samples (left and right) // 3) based on the bearing relative angle to the source we will weaken and delay either the left or // right channel of the input into the output @@ -314,7 +319,7 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* for (int i = 0; i < numSamplesDelay; i++) { int16_t originalHistoricalSample = *delayStreamSourceSamples; - _clientSamples[delayedChannelHistoricalAudioOutputIndex] += originalHistoricalSample + _preMixSamples[delayedChannelHistoricalAudioOutputIndex] += originalHistoricalSample * attenuationAndWeakChannelRatioAndFade; ++delayStreamSourceSamples; // move our input pointer delayedChannelHistoricalAudioOutputIndex += OUTPUT_SAMPLES_PER_INPUT_SAMPLE; // move our output sample @@ -329,10 +334,10 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* // since we might be delayed, don't write beyond our maxOutputIndex if (leftDestinationIndex <= maxOutputIndex) { - _clientSamples[leftDestinationIndex] += leftSideSample; + _preMixSamples[leftDestinationIndex] += leftSideSample; } if (rightDestinationIndex <= maxOutputIndex) { - _clientSamples[rightDestinationIndex] += rightSideSample; + _preMixSamples[rightDestinationIndex] += rightSideSample; } leftDestinationIndex += OUTPUT_SAMPLES_PER_INPUT_SAMPLE; @@ -345,7 +350,7 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* float attenuationAndFade = attenuationCoefficient * repeatedFrameFadeFactor; for (int s = 0; s < NETWORK_BUFFER_LENGTH_SAMPLES_STEREO; s++) { - _clientSamples[s] = glm::clamp(_clientSamples[s] + (int)(streamPopOutput[s / stereoDivider] * attenuationAndFade), + _preMixSamples[s] = glm::clamp(_preMixSamples[s] + (int)(streamPopOutput[s / stereoDivider] * attenuationAndFade), MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE); } } @@ -409,12 +414,15 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* #endif // set the gain on both filter channels - AudioFilterHSF1s& penumbraFilter = streamToAdd->getFilter(); - - penumbraFilter.setParameters(0, 0, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainL, penumbraFilterSlope); - penumbraFilter.setParameters(0, 1, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainR, penumbraFilterSlope); - - penumbraFilter.render(_clientSamples, _clientSamples, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO / 2); + _penumbraFilter.reset(); + _penumbraFilter.setParameters(0, 0, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainL, penumbraFilterSlope); + _penumbraFilter.setParameters(0, 1, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainR, penumbraFilterSlope); + _penumbraFilter.render(_preMixSamples, _preMixSamples, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO / 2); + } + + // Actually mix the _preMixSamples into the _mixSamples here. + for (int s = 0; s < NETWORK_BUFFER_LENGTH_SAMPLES_STEREO; s++) { + _mixSamples[s] = glm::clamp(_mixSamples[s] + _preMixSamples[s], MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE); } return 1; @@ -424,7 +432,8 @@ int AudioMixer::prepareMixForListeningNode(Node* node) { AvatarAudioStream* nodeAudioStream = ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioStream(); // zero out the client mix for this node - memset(_clientSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_STEREO); + memset(_preMixSamples, 0, sizeof(_preMixSamples)); + memset(_mixSamples, 0, sizeof(_mixSamples)); // loop through all other nodes that have sufficient audio to mix int streamsMixed = 0; @@ -817,7 +826,7 @@ void AudioMixer::run() { dataAt += sizeof(quint16); // pack mixed audio samples - memcpy(dataAt, _clientSamples, NETWORK_BUFFER_LENGTH_BYTES_STEREO); + memcpy(dataAt, _mixSamples, NETWORK_BUFFER_LENGTH_BYTES_STEREO); dataAt += NETWORK_BUFFER_LENGTH_BYTES_STEREO; } else { // pack header diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 83ce6195cc..afc59d3641 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -13,6 +13,10 @@ #define hifi_AudioMixer_h #include +#include // For AudioFilterHSF1s and _penumbraFilter +#include // For AudioFilterHSF1s and _penumbraFilter +#include // For AudioFilterHSF1s and _penumbraFilter +#include // For AudioFilterHSF1s and _penumbraFilter #include #include @@ -23,7 +27,6 @@ const int SAMPLE_PHASE_DELAY_AT_90 = 20; const int READ_DATAGRAMS_STATS_WINDOW_SECONDS = 30; - /// Handles assignments of type AudioMixer - mixing streams of audio and re-distributing to various clients. class AudioMixer : public ThreadedAssignment { Q_OBJECT @@ -48,11 +51,17 @@ private: /// prepares and sends a mix to one Node int prepareMixForListeningNode(Node* node); + + // used on a per stream basis to run the filter on before mixing, large enough to handle the historical + // data from a phase delay as well as an entire network buffer + int16_t _preMixSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)]; // client samples capacity is larger than what will be sent to optimize mixing // we are MMX adding 4 samples at a time so we need client samples to have an extra 4 - int16_t _clientSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)]; + int16_t _mixSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)]; + AudioFilterHSF1s _penumbraFilter; + void perSecondActions(); QString getReadPendingDatagramsCallsPerSecondsStatsString() const; diff --git a/libraries/audio/src/PositionalAudioStream.cpp b/libraries/audio/src/PositionalAudioStream.cpp index c8d0f66c4d..ae30022268 100644 --- a/libraries/audio/src/PositionalAudioStream.cpp +++ b/libraries/audio/src/PositionalAudioStream.cpp @@ -33,10 +33,6 @@ PositionalAudioStream::PositionalAudioStream(PositionalAudioStream::Type type, b _lastPopOutputLoudness(0.0f), _listenerUnattenuatedZone(NULL) { - // constant defined in AudioMixer.h. However, we don't want to include this here - // we will soon find a better common home for these audio-related constants - const int SAMPLE_PHASE_DELAY_AT_90 = 20; - _filter.initialize(SAMPLE_RATE, (NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)) / 2); } void PositionalAudioStream::resetStats() { diff --git a/libraries/audio/src/PositionalAudioStream.h b/libraries/audio/src/PositionalAudioStream.h index f5f1b9027a..2b615a575b 100644 --- a/libraries/audio/src/PositionalAudioStream.h +++ b/libraries/audio/src/PositionalAudioStream.h @@ -16,10 +16,6 @@ #include #include "InboundAudioStream.h" -#include "AudioFormat.h" -#include "AudioBuffer.h" -#include "AudioFilter.h" -#include "AudioFilterBank.h" const int AUDIOMIXER_INBOUND_RING_BUFFER_FRAME_CAPACITY = 100; @@ -50,8 +46,6 @@ public: void setListenerUnattenuatedZone(AABox* listenerUnattenuatedZone) { _listenerUnattenuatedZone = listenerUnattenuatedZone; } - AudioFilterHSF1s& getFilter() { return _filter; } - protected: // disallow copying of PositionalAudioStream objects PositionalAudioStream(const PositionalAudioStream&); @@ -70,8 +64,6 @@ protected: float _lastPopOutputTrailingLoudness; float _lastPopOutputLoudness; AABox* _listenerUnattenuatedZone; - - AudioFilterHSF1s _filter; }; #endif // hifi_PositionalAudioStream_h