Merge pull request #15260 from kencooke/audio-mixer-volume-control

Case 21902: System-wide independent volume controls
This commit is contained in:
Ken Cooke 2019-03-27 13:19:30 -07:00 committed by GitHub
commit abb0a166b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 422 additions and 74 deletions

View file

@ -97,6 +97,7 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
PacketType::RadiusIgnoreRequest,
PacketType::RequestsDomainListData,
PacketType::PerAvatarGainSet,
PacketType::InjectorGainSet,
PacketType::AudioSoloRequest },
this, "queueAudioPacket");

View file

@ -92,6 +92,9 @@ int AudioMixerClientData::processPackets(ConcurrentAddedStreams& addedStreams) {
case PacketType::PerAvatarGainSet:
parsePerAvatarGainSet(*packet, node);
break;
case PacketType::InjectorGainSet:
parseInjectorGainSet(*packet, node);
break;
case PacketType::NodeIgnoreRequest:
parseNodeIgnoreRequest(packet, node);
break;
@ -197,14 +200,25 @@ void AudioMixerClientData::parsePerAvatarGainSet(ReceivedMessage& message, const
if (avatarUUID.isNull()) {
// set the MASTER avatar gain
setMasterAvatarGain(gain);
qCDebug(audio) << "Setting MASTER avatar gain for " << uuid << " to " << gain;
qCDebug(audio) << "Setting MASTER avatar gain for" << uuid << "to" << gain;
} else {
// set the per-source avatar gain
setGainForAvatar(avatarUUID, gain);
qCDebug(audio) << "Setting avatar gain adjustment for hrtf[" << uuid << "][" << avatarUUID << "] to " << gain;
qCDebug(audio) << "Setting avatar gain adjustment for hrtf[" << uuid << "][" << avatarUUID << "] to" << gain;
}
}
void AudioMixerClientData::parseInjectorGainSet(ReceivedMessage& message, const SharedNodePointer& node) {
QUuid uuid = node->getUUID();
uint8_t packedGain;
message.readPrimitive(&packedGain);
float gain = unpackFloatGainFromByte(packedGain);
setMasterInjectorGain(gain);
qCDebug(audio) << "Setting MASTER injector gain for" << uuid << "to" << gain;
}
void AudioMixerClientData::setGainForAvatar(QUuid nodeID, float gain) {
auto it = std::find_if(_streams.active.cbegin(), _streams.active.cend(), [nodeID](const MixableStream& mixableStream){
return mixableStream.nodeStreamID.nodeID == nodeID && mixableStream.nodeStreamID.streamID.isNull();

View file

@ -63,6 +63,7 @@ public:
void negotiateAudioFormat(ReceivedMessage& message, const SharedNodePointer& node);
void parseRequestsDomainListData(ReceivedMessage& message);
void parsePerAvatarGainSet(ReceivedMessage& message, const SharedNodePointer& node);
void parseInjectorGainSet(ReceivedMessage& message, const SharedNodePointer& node);
void parseNodeIgnoreRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node);
void parseRadiusIgnoreRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node);
void parseSoloRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node);
@ -84,6 +85,8 @@ public:
float getMasterAvatarGain() const { return _masterAvatarGain; }
void setMasterAvatarGain(float gain) { _masterAvatarGain = gain; }
float getMasterInjectorGain() const { return _masterInjectorGain; }
void setMasterInjectorGain(float gain) { _masterInjectorGain = gain; }
AudioLimiter audioLimiter;
@ -189,6 +192,7 @@ private:
int _frameToSendStats { 0 };
float _masterAvatarGain { 1.0f }; // per-listener mixing gain, applied only to avatars
float _masterInjectorGain { 1.0f }; // per-listener mixing gain, applied only to injectors
CodecPluginPointer _codec;
QString _selectedCodecName;

View file

@ -50,7 +50,7 @@ void sendEnvironmentPacket(const SharedNodePointer& node, AudioMixerClientData&
// mix helpers
inline float approximateGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd);
inline float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNodeStream,
inline float computeGain(float masterAvatarGain, float masterInjectorGain, const AvatarAudioStream& listeningNodeStream,
const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, float distance, bool isEcho);
inline float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition);
@ -338,8 +338,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
}
if (!isThrottling) {
updateHRTFParameters(stream, *listenerAudioStream,
listenerData->getMasterAvatarGain());
updateHRTFParameters(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(),
listenerData->getMasterInjectorGain());
}
return false;
});
@ -363,8 +363,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
}
if (!isThrottling) {
updateHRTFParameters(stream, *listenerAudioStream,
listenerData->getMasterAvatarGain());
updateHRTFParameters(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(),
listenerData->getMasterInjectorGain());
}
return false;
});
@ -381,13 +381,13 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
stream.approximateVolume = approximateVolume(stream, listenerAudioStream);
} else {
if (shouldBeSkipped(stream, *listener, *listenerAudioStream, *listenerData)) {
addStream(stream, *listenerAudioStream, 0.0f, isSoloing);
addStream(stream, *listenerAudioStream, 0.0f, 0.0f, isSoloing);
streams.skipped.push_back(move(stream));
++stats.activeToSkipped;
return true;
}
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(),
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), listenerData->getMasterInjectorGain(),
isSoloing);
if (shouldBeInactive(stream)) {
@ -423,7 +423,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
return true;
}
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(),
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), listenerData->getMasterInjectorGain(),
isSoloing);
if (shouldBeInactive(stream)) {
@ -491,7 +491,9 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStream,
AvatarAudioStream& listeningNodeStream,
float masterListenerGain, bool isSoloing) {
float masterAvatarGain,
float masterInjectorGain,
bool isSoloing) {
++stats.totalMixes;
auto streamToAdd = mixableStream.positionalStream;
@ -504,9 +506,10 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre
float distance = glm::max(glm::length(relativePosition), EPSILON);
float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition);
float gain = masterListenerGain;
float gain = masterAvatarGain;
if (!isSoloing) {
gain = computeGain(masterListenerGain, listeningNodeStream, *streamToAdd, relativePosition, distance, isEcho);
gain = computeGain(masterAvatarGain, masterInjectorGain, listeningNodeStream, *streamToAdd, relativePosition,
distance, isEcho);
}
const int HRTF_DATASET_INDEX = 1;
@ -585,8 +588,9 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre
}
void AudioMixerSlave::updateHRTFParameters(AudioMixerClientData::MixableStream& mixableStream,
AvatarAudioStream& listeningNodeStream,
float masterListenerGain) {
AvatarAudioStream& listeningNodeStream,
float masterAvatarGain,
float masterInjectorGain) {
auto streamToAdd = mixableStream.positionalStream;
// check if this is a server echo of a source back to itself
@ -595,7 +599,8 @@ void AudioMixerSlave::updateHRTFParameters(AudioMixerClientData::MixableStream&
glm::vec3 relativePosition = streamToAdd->getPosition() - listeningNodeStream.getPosition();
float distance = glm::max(glm::length(relativePosition), EPSILON);
float gain = computeGain(masterListenerGain, listeningNodeStream, *streamToAdd, relativePosition, distance, isEcho);
float gain = computeGain(masterAvatarGain, masterInjectorGain, listeningNodeStream, *streamToAdd, relativePosition,
distance, isEcho);
float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition);
mixableStream.hrtf->setParameterHistory(azimuth, distance, gain);
@ -720,6 +725,7 @@ float approximateGain(const AvatarAudioStream& listeningNodeStream, const Positi
// injector: apply attenuation
if (streamToAdd.getType() == PositionalAudioStream::Injector) {
gain *= reinterpret_cast<const InjectedAudioStream*>(&streamToAdd)->getAttenuationRatio();
// injector: skip master gain
}
// avatar: skip attenuation - it is too costly to approximate
@ -729,16 +735,23 @@ float approximateGain(const AvatarAudioStream& listeningNodeStream, const Positi
float distance = glm::length(relativePosition);
return gain / distance;
// avatar: skip master gain - it is constant for all streams
// avatar: skip master gain
}
float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNodeStream,
const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, float distance, bool isEcho) {
float computeGain(float masterAvatarGain,
float masterInjectorGain,
const AvatarAudioStream& listeningNodeStream,
const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition,
float distance,
bool isEcho) {
float gain = 1.0f;
// injector: apply attenuation
if (streamToAdd.getType() == PositionalAudioStream::Injector) {
gain *= reinterpret_cast<const InjectedAudioStream*>(&streamToAdd)->getAttenuationRatio();
// apply master gain
gain *= masterInjectorGain;
// avatar: apply fixed off-axis attenuation to make them quieter as they turn away
} else if (!isEcho && (streamToAdd.getType() == PositionalAudioStream::Microphone)) {
@ -754,8 +767,8 @@ float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNo
gain *= offAxisCoefficient;
// apply master gain, only to avatars
gain *= masterListenerGain;
// apply master gain
gain *= masterAvatarGain;
}
auto& audioZones = AudioMixer::getAudioZones();
@ -797,8 +810,9 @@ float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNo
return gain;
}
float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition) {
float computeAzimuth(const AvatarAudioStream& listeningNodeStream,
const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition) {
glm::quat inverseOrientation = glm::inverse(listeningNodeStream.getOrientation());
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;

View file

@ -57,10 +57,13 @@ private:
bool prepareMix(const SharedNodePointer& listener);
void addStream(AudioMixerClientData::MixableStream& mixableStream,
AvatarAudioStream& listeningNodeStream,
float masterListenerGain, bool isSoloing);
float masterAvatarGain,
float masterInjectorGain,
bool isSoloing);
void updateHRTFParameters(AudioMixerClientData::MixableStream& mixableStream,
AvatarAudioStream& listeningNodeStream,
float masterListenerGain);
float masterAvatarGain,
float masterInjectorGain);
void resetHRTFState(AudioMixerClientData::MixableStream& mixableStream);
void addStreams(Node& listener, AudioMixerClientData& listenerData);

View file

@ -87,8 +87,19 @@ Rectangle {
}
function updateMyAvatarGainFromQML(sliderValue, isReleased) {
if (Users.getAvatarGain(myAvatarUuid) != sliderValue) {
Users.setAvatarGain(myAvatarUuid, sliderValue);
if (AudioScriptingInterface.getAvatarGain() != sliderValue) {
AudioScriptingInterface.setAvatarGain(sliderValue);
}
}
function updateInjectorGainFromQML(sliderValue, isReleased) {
if (AudioScriptingInterface.getInjectorGain() != sliderValue) {
AudioScriptingInterface.setInjectorGain(sliderValue); // server side
AudioScriptingInterface.setLocalInjectorGain(sliderValue); // client side
}
}
function updateSystemInjectorGainFromQML(sliderValue, isReleased) {
if (AudioScriptingInterface.getSystemInjectorGain() != sliderValue) {
AudioScriptingInterface.setSystemInjectorGain(sliderValue);
}
}
@ -382,6 +393,7 @@ Rectangle {
}
}
}
AudioControls.LoopbackAudio {
id: loopbackAudio
x: margins.paddings
@ -462,22 +474,22 @@ Rectangle {
}
Item {
id: gainContainer
id: avatarGainContainer
x: margins.paddings;
anchors.top: outputView.bottom;
anchors.topMargin: 10;
width: parent.width - margins.paddings*2
height: gainSliderTextMetrics.height
height: avatarGainSliderTextMetrics.height
HifiControlsUit.Slider {
id: gainSlider
id: avatarGainSlider
anchors.right: parent.right
height: parent.height
width: 200
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
value: Users.getAvatarGain(myAvatarUuid)
value: AudioScriptingInterface.getAvatarGain()
onValueChanged: {
updateMyAvatarGainFromQML(value, false);
}
@ -493,7 +505,7 @@ Rectangle {
// Do nothing.
}
onDoubleClicked: {
gainSlider.value = 0.0
avatarGainSlider.value = 0.0
}
onPressed: {
// Pass through to Slider
@ -507,13 +519,13 @@ Rectangle {
}
}
TextMetrics {
id: gainSliderTextMetrics
text: gainSliderText.text
font: gainSliderText.font
id: avatarGainSliderTextMetrics
text: avatarGainSliderText.text
font: avatarGainSliderText.font
}
RalewayRegular {
// The slider for my card is special, it controls the master gain
id: gainSliderText;
id: avatarGainSliderText;
text: "Avatar volume";
size: 16;
anchors.left: parent.left;
@ -523,15 +535,133 @@ Rectangle {
}
}
Item {
id: injectorGainContainer
x: margins.paddings;
width: parent.width - margins.paddings*2
height: injectorGainSliderTextMetrics.height
anchors.top: avatarGainContainer.bottom;
anchors.topMargin: 10;
HifiControlsUit.Slider {
id: injectorGainSlider
anchors.right: parent.right
height: parent.height
width: 200
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
value: AudioScriptingInterface.getInjectorGain()
onValueChanged: {
updateInjectorGainFromQML(value, false);
}
onPressedChanged: {
if (!pressed) {
updateInjectorGainFromQML(value, false);
}
}
MouseArea {
anchors.fill: parent
onWheel: {
// Do nothing.
}
onDoubleClicked: {
injectorGainSlider.value = 0.0
}
onPressed: {
// Pass through to Slider
mouse.accepted = false
}
onReleased: {
// the above mouse.accepted seems to make this
// never get called, nonetheless...
mouse.accepted = false
}
}
}
TextMetrics {
id: injectorGainSliderTextMetrics
text: injectorGainSliderText.text
font: injectorGainSliderText.font
}
RalewayRegular {
id: injectorGainSliderText;
text: "Environment volume";
size: 16;
anchors.left: parent.left;
color: hifi.colors.white;
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignTop;
}
}
Item {
id: systemInjectorGainContainer
x: margins.paddings;
width: parent.width - margins.paddings*2
height: systemInjectorGainSliderTextMetrics.height
anchors.top: injectorGainContainer.bottom;
anchors.topMargin: 10;
HifiControlsUit.Slider {
id: systemInjectorGainSlider
anchors.right: parent.right
height: parent.height
width: 200
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
value: AudioScriptingInterface.getSystemInjectorGain()
onValueChanged: {
updateSystemInjectorGainFromQML(value, false);
}
onPressedChanged: {
if (!pressed) {
updateSystemInjectorGainFromQML(value, false);
}
}
MouseArea {
anchors.fill: parent
onWheel: {
// Do nothing.
}
onDoubleClicked: {
systemInjectorGainSlider.value = 0.0
}
onPressed: {
// Pass through to Slider
mouse.accepted = false
}
onReleased: {
// the above mouse.accepted seems to make this
// never get called, nonetheless...
mouse.accepted = false
}
}
}
TextMetrics {
id: systemInjectorGainSliderTextMetrics
text: systemInjectorGainSliderText.text
font: systemInjectorGainSliderText.font
}
RalewayRegular {
id: systemInjectorGainSliderText;
text: "System Sound volume";
size: 16;
anchors.left: parent.left;
color: hifi.colors.white;
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignTop;
}
}
AudioControls.PlaySampleSound {
id: playSampleSound
x: margins.paddings
anchors.top: gainContainer.bottom;
anchors.top: systemInjectorGainContainer.bottom;
anchors.topMargin: 10;
visible: (bar.currentIndex === 1 && isVR) ||
(bar.currentIndex === 0 && !isVR);
anchors { left: parent.left; leftMargin: margins.paddings }
}
}
}

View file

@ -44,8 +44,11 @@ RowLayout {
}
HifiControlsUit.Button {
text: audioLoopedBack ? qsTr("STOP TESTING YOUR VOICE") : qsTr("TEST YOUR VOICE");
text: audioLoopedBack ? qsTr("STOP TESTING VOICE") : qsTr("TEST YOUR VOICE");
color: audioLoopedBack ? hifi.buttons.red : hifi.buttons.blue;
fontSize: 15;
width: 200;
height: 32;
onClicked: {
if (audioLoopedBack) {
loopbackTimer.stop();
@ -57,11 +60,11 @@ RowLayout {
}
}
RalewayRegular {
Layout.leftMargin: 2;
size: 14;
color: "white";
font.italic: true
text: audioLoopedBack ? qsTr("Speak in your input") : "";
}
// RalewayRegular {
// Layout.leftMargin: 2;
// size: 14;
// color: "white";
// font.italic: true
// text: audioLoopedBack ? qsTr("Speak in your input") : "";
// }
}

View file

@ -56,16 +56,19 @@ RowLayout {
HifiConstants { id: hifi; }
HifiControlsUit.Button {
text: isPlaying ? qsTr("STOP TESTING YOUR SOUND") : qsTr("TEST YOUR SOUND");
text: isPlaying ? qsTr("STOP TESTING") : qsTr("TEST YOUR SOUND");
color: isPlaying ? hifi.buttons.red : hifi.buttons.blue;
onClicked: isPlaying ? stopSound() : playSound();
fontSize: 15;
width: 200;
height: 32;
}
RalewayRegular {
Layout.leftMargin: 2;
size: 14;
color: "white";
font.italic: true
text: isPlaying ? qsTr("Listen to your output") : "";
}
// RalewayRegular {
// Layout.leftMargin: 2;
// size: 14;
// color: "white";
// font.italic: true
// text: isPlaying ? qsTr("Listen to your output") : "";
// }
}

View file

@ -377,6 +377,18 @@ void Audio::handlePushedToTalk(bool enabled) {
}
}
void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) {
withWriteLock([&] {
_devices.chooseInputDevice(device, isHMD);
});
}
void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) {
withWriteLock([&] {
_devices.chooseOutputDevice(device, isHMD);
});
}
void Audio::setReverb(bool enable) {
withWriteLock([&] {
DependencyManager::get<AudioClient>()->setReverb(enable);
@ -389,14 +401,66 @@ void Audio::setReverbOptions(const AudioEffectOptions* options) {
});
}
void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) {
void Audio::setAvatarGain(float gain) {
withWriteLock([&] {
_devices.chooseInputDevice(device, isHMD);
// ask the NodeList to set the master avatar gain
DependencyManager::get<NodeList>()->setAvatarGain(QUuid(), gain);
});
}
void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) {
withWriteLock([&] {
_devices.chooseOutputDevice(device, isHMD);
float Audio::getAvatarGain() {
return resultWithReadLock<float>([&] {
return DependencyManager::get<NodeList>()->getAvatarGain(QUuid());
});
}
void Audio::setInjectorGain(float gain) {
withWriteLock([&] {
// ask the NodeList to set the audio injector gain
DependencyManager::get<NodeList>()->setInjectorGain(gain);
});
}
float Audio::getInjectorGain() {
return resultWithReadLock<float>([&] {
return DependencyManager::get<NodeList>()->getInjectorGain();
});
}
void Audio::setLocalInjectorGain(float gain) {
withWriteLock([&] {
if (_localInjectorGain != gain) {
_localInjectorGain = gain;
// convert dB to amplitude
gain = fastExp2f(gain / 6.02059991f);
// quantize and limit to match NodeList::setInjectorGain()
gain = unpackFloatGainFromByte(packFloatGainToByte(gain));
DependencyManager::get<AudioClient>()->setLocalInjectorGain(gain);
}
});
}
float Audio::getLocalInjectorGain() {
return resultWithReadLock<float>([&] {
return _localInjectorGain;
});
}
void Audio::setSystemInjectorGain(float gain) {
withWriteLock([&] {
if (_systemInjectorGain != gain) {
_systemInjectorGain = gain;
// convert dB to amplitude
gain = fastExp2f(gain / 6.02059991f);
// quantize and limit to match NodeList::setInjectorGain()
gain = unpackFloatGainFromByte(packFloatGainToByte(gain));
DependencyManager::get<AudioClient>()->setSystemInjectorGain(gain);
}
});
}
float Audio::getSystemInjectorGain() {
return resultWithReadLock<float>([&] {
return _systemInjectorGain;
});
}

View file

@ -170,6 +170,66 @@ public:
*/
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
/**jsdoc
* Sets the avatar gain at the server.
* Units are Decibels (dB)
* @function Audio.setAvatarGain
* @param {number} gain (in dB)
*/
Q_INVOKABLE void setAvatarGain(float gain);
/**jsdoc
* Gets the avatar gain at the server.
* @function Audio.getAvatarGain
* @returns {number} gain (in dB)
*/
Q_INVOKABLE float getAvatarGain();
/**jsdoc
* Sets the injector gain at the server.
* Units are Decibels (dB)
* @function Audio.setInjectorGain
* @param {number} gain (in dB)
*/
Q_INVOKABLE void setInjectorGain(float gain);
/**jsdoc
* Gets the injector gain at the server.
* @function Audio.getInjectorGain
* @returns {number} gain (in dB)
*/
Q_INVOKABLE float getInjectorGain();
/**jsdoc
* Sets the local injector gain in the client.
* Units are Decibels (dB)
* @function Audio.setLocalInjectorGain
* @param {number} gain (in dB)
*/
Q_INVOKABLE void setLocalInjectorGain(float gain);
/**jsdoc
* Gets the local injector gain in the client.
* @function Audio.getLocalInjectorGain
* @returns {number} gain (in dB)
*/
Q_INVOKABLE float getLocalInjectorGain();
/**jsdoc
* Sets the injector gain for system sounds.
* Units are Decibels (dB)
* @function Audio.setSystemInjectorGain
* @param {number} gain (in dB)
*/
Q_INVOKABLE void setSystemInjectorGain(float gain);
/**jsdoc
* Gets the injector gain for system sounds.
* @function Audio.getSystemInjectorGain
* @returns {number} gain (in dB)
*/
Q_INVOKABLE float getSystemInjectorGain();
/**jsdoc
* Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format.
* @function Audio.startRecording
@ -350,6 +410,8 @@ private:
float _inputVolume { 1.0f };
float _inputLevel { 0.0f };
float _localInjectorGain { 0.0f }; // in dB
float _systemInjectorGain { 0.0f }; // in dB
bool _isClipping { false };
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
bool _enableWarnWhenMuted { true };

View file

@ -1368,7 +1368,9 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
memset(_localScratchBuffer, 0, bytesToRead);
if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) {
float gain = options.volume;
bool isSystemSound = !options.positionSet && !options.ambisonic;
float gain = options.volume * (isSystemSound ? _systemInjectorGain : _localInjectorGain);
if (options.ambisonic) {

View file

@ -241,6 +241,8 @@ public slots:
void setInputVolume(float volume, bool emitSignal = true);
void setReverb(bool reverb);
void setReverbOptions(const AudioEffectOptions* options);
void setLocalInjectorGain(float gain) { _localInjectorGain = gain; };
void setSystemInjectorGain(float gain) { _systemInjectorGain = gain; };
void outputNotify();
@ -395,6 +397,8 @@ private:
int16_t* _outputScratchBuffer { NULL };
// for local audio (used by audio injectors thread)
std::atomic<float> _localInjectorGain { 1.0f };
std::atomic<float> _systemInjectorGain { 1.0f };
float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO];
int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
float* _localOutputMixBuffer { NULL };

View file

@ -1016,6 +1016,14 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) {
// also send them the current ignore radius state.
sendIgnoreRadiusStateToNode(newNode);
// also send the current avatar and injector gains
if (_avatarGain != 0.0f) {
setAvatarGain(QUuid(), _avatarGain);
}
if (_injectorGain != 0.0f) {
setInjectorGain(_injectorGain);
}
}
if (newNode->getType() == NodeType::AvatarMixer) {
// this is a mixer that we just added - it's unlikely it knows who we were previously ignoring in this session,
@ -1062,13 +1070,17 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
if (nodeID.isNull()) {
qCDebug(networking) << "Sending Set MASTER Avatar Gain packet with Gain:" << gain;
} else {
qCDebug(networking) << "Sending Set Avatar Gain packet with UUID: " << uuidStringWithoutCurlyBraces(nodeID) << "Gain:" << gain;
}
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
QWriteLocker lock{ &_avatarGainMapLock };
_avatarGainMap[nodeID] = gain;
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
_avatarGain = gain;
} else {
qCDebug(networking) << "Sending Set Avatar Gain packet with UUID:" << uuidStringWithoutCurlyBraces(nodeID) << "Gain:" << gain;
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
QWriteLocker lock{ &_avatarGainMapLock };
_avatarGainMap[nodeID] = gain;
}
} else {
qWarning() << "Couldn't find audio mixer to send set gain request";
@ -1079,14 +1091,41 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
}
float NodeList::getAvatarGain(const QUuid& nodeID) {
QReadLocker lock{ &_avatarGainMapLock };
auto it = _avatarGainMap.find(nodeID);
if (it != _avatarGainMap.cend()) {
return it->second;
if (nodeID.isNull()) {
return _avatarGain;
} else {
QReadLocker lock{ &_avatarGainMapLock };
auto it = _avatarGainMap.find(nodeID);
if (it != _avatarGainMap.cend()) {
return it->second;
}
}
return 0.0f;
}
void NodeList::setInjectorGain(float gain) {
auto audioMixer = soloNodeOfType(NodeType::AudioMixer);
if (audioMixer) {
// setup the packet
auto setInjectorGainPacket = NLPacket::create(PacketType::InjectorGainSet, sizeof(float), true);
// We need to convert the gain in dB (from the script) to an amplitude before packing it.
setInjectorGainPacket->writePrimitive(packFloatGainToByte(fastExp2f(gain / 6.02059991f)));
qCDebug(networking) << "Sending Set Injector Gain packet with Gain:" << gain;
sendPacket(std::move(setInjectorGainPacket), *audioMixer);
_injectorGain = gain;
} else {
qWarning() << "Couldn't find audio mixer to send set gain request";
}
}
float NodeList::getInjectorGain() {
return _injectorGain;
}
void NodeList::kickNodeBySessionID(const QUuid& nodeID) {
// send a request to domain-server to kick the node with the given session ID
// the domain-server will handle the persistence of the kick (via username or IP)

View file

@ -83,6 +83,8 @@ public:
bool isPersonalMutingNode(const QUuid& nodeID) const;
void setAvatarGain(const QUuid& nodeID, float gain);
float getAvatarGain(const QUuid& nodeID);
void setInjectorGain(float gain);
float getInjectorGain();
void kickNodeBySessionID(const QUuid& nodeID);
void muteNodeBySessionID(const QUuid& nodeID);
@ -181,6 +183,9 @@ private:
mutable QReadWriteLock _avatarGainMapLock;
tbb::concurrent_unordered_map<QUuid, float, UUIDHasher> _avatarGainMap;
std::atomic<float> _avatarGain { 0.0f }; // in dB
std::atomic<float> _injectorGain { 0.0f }; // in dB
void sendIgnoreRadiusStateToNode(const SharedNodePointer& destinationNode);
#if defined(Q_OS_ANDROID)
Setting::Handle<bool> _ignoreRadiusEnabled { "IgnoreRadiusEnabled", false };

View file

@ -57,7 +57,7 @@ public:
ICEServerQuery,
OctreeStats,
SetAvatarTraits,
UNUSED_PACKET_TYPE,
InjectorGainSet,
AssignmentClientStatus,
NoisyMute,
AvatarIdentity,