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