mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-08 23:12:16 +02:00
Merge pull request #15260 from kencooke/audio-mixer-volume-control
Case 21902: System-wide independent volume controls
This commit is contained in:
commit
abb0a166b1
15 changed files with 422 additions and 74 deletions
|
@ -97,6 +97,7 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
|||
PacketType::RadiusIgnoreRequest,
|
||||
PacketType::RequestsDomainListData,
|
||||
PacketType::PerAvatarGainSet,
|
||||
PacketType::InjectorGainSet,
|
||||
PacketType::AudioSoloRequest },
|
||||
this, "queueAudioPacket");
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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") : "";
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -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") : "";
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
ICEServerQuery,
|
||||
OctreeStats,
|
||||
SetAvatarTraits,
|
||||
UNUSED_PACKET_TYPE,
|
||||
InjectorGainSet,
|
||||
AssignmentClientStatus,
|
||||
NoisyMute,
|
||||
AvatarIdentity,
|
||||
|
|
Loading…
Reference in a new issue