From 85f68662b08a3ec3e0a4067d4e8a6c4b8261b206 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Sun, 14 Sep 2014 12:22:29 -0700 Subject: [PATCH 01/10] implement Gordon-Smith 2-pole quadrature oscillator --- libraries/audio/src/AudioSourceTone.h | 32 ++++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/libraries/audio/src/AudioSourceTone.h b/libraries/audio/src/AudioSourceTone.h index 5ebe1ba2a9..a21806460f 100644 --- a/libraries/audio/src/AudioSourceTone.h +++ b/libraries/audio/src/AudioSourceTone.h @@ -12,6 +12,7 @@ #ifndef hifi_AudioSourceTone_h #define hifi_AudioSourceTone_h +// Implemenbts a Gordon-Smith oscillator class AudioSourceTone { static uint32_t _frameOffset; @@ -19,6 +20,16 @@ class AudioSourceTone float32_t _amplitude; float32_t _sampleRate; float32_t _omega; + float32_t _epsilon; + float32_t _yq1; + float32_t _y1; + + void updateCoefficients() { + _omega = _frequency / _sampleRate * TWO_PI; + _epsilon = 2.0f * sinf(_omega / 2.0f); + _yq1 = cosf(-1.0f * _omega); + _y1 = sinf(+1.04 * _omega); + } public: AudioSourceTone() { @@ -30,22 +41,20 @@ public: } void initialize() { - _frameOffset = 0; - setParameters(SAMPLE_RATE, 220.0f, 0.9f); + setParameters(SAMPLE_RATE, 220.0f, 0.708f); } void finalize() { } void reset() { - _frameOffset = 0; } void setParameters(const float32_t sampleRate, const float32_t frequency, const float32_t amplitude) { _sampleRate = std::max(sampleRate, 1.0f); _frequency = std::max(frequency, 1.0f); _amplitude = std::max(amplitude, 1.0f); - _omega = _frequency / _sampleRate * TWO_PI; + updateCoefficients(); } void getParameters(float32_t& sampleRate, float32_t& frequency, float32_t& amplitude) { @@ -56,15 +65,22 @@ public: void render(AudioBufferFloat32& frameBuffer) { - // note: this is a placeholder implementation. final version will not include any transcendental ops in our render loop - float32_t** samples = frameBuffer.getFrameData(); + float32_t yq; + float32_t y; for (uint16_t i = 0; i < frameBuffer.getFrameCount(); ++i) { + + yq = _yq1 - (_epsilon * _y1); + y = _y1 + (_epsilon * yq); + + // update delays + _yq1 = yq; + _y1 = y; + for (uint16_t j = 0; j < frameBuffer.getChannelCount(); ++j) { - samples[j][i] = sinf((i + _frameOffset) * _omega); + samples[j][i] = _amplitude * y; } } - _frameOffset += frameBuffer.getFrameCount(); } }; From 4a63f9f764a0aaebb2f048e66eca8fa1af024b3b Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Sun, 14 Sep 2014 12:22:32 -0700 Subject: [PATCH 02/10] implement Gordon-Smith 2-pole quadrature oscillator --- libraries/audio/src/AudioSourceTone.cpp | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 libraries/audio/src/AudioSourceTone.cpp diff --git a/libraries/audio/src/AudioSourceTone.cpp b/libraries/audio/src/AudioSourceTone.cpp deleted file mode 100644 index 80a6aed48e..0000000000 --- a/libraries/audio/src/AudioSourceTone.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// -// AudioSourceTone.cpp -// hifi -// -// Created by Craig Hansen-Sturm on 8/10/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include "AudioRingBuffer.h" -#include "AudioFormat.h" -#include "AudioBuffer.h" -#include "AudioSourceTone.h" - -uint32_t AudioSourceTone::_frameOffset = 0; From 089267a937e2adadf153bfedf22a3804f1742f32 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Sun, 14 Sep 2014 12:26:25 -0700 Subject: [PATCH 03/10] completely remove new audio objects from playback rendering path when they are unused --- interface/src/Audio.cpp | 42 ++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 80500d392d..71b64d7c6e 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -506,30 +506,34 @@ void Audio::handleAudioInput() { QByteArray inputByteArray = _inputDevice->readAll(); - int16_t* inputFrameData = (int16_t*)inputByteArray.data(); - const int inputFrameCount = inputByteArray.size() / sizeof(int16_t); + if (!_muted && (_audioSourceInjectEnabled || _peqEnabled)) { + + int16_t* inputFrameData = (int16_t*)inputByteArray.data(); + const int inputFrameCount = inputByteArray.size() / sizeof(int16_t); - _inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, false /*copy in*/); + _inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, false /*copy in*/); - // _inputGain.render(_inputFrameBuffer); // input/mic gain+mute - - // Add audio source injection if enabled - if (_audioSourceInjectEnabled && !_muted) { - - if (_toneSourceEnabled) { // sine generator - _toneSource.render(_inputFrameBuffer); +#if ENABLE_INPUT_GAIN + _inputGain.render(_inputFrameBuffer); // input/mic gain+mute +#endif + // Add audio source injection if enabled + if (_audioSourceInjectEnabled) { + + if (_toneSourceEnabled) { // sine generator + _toneSource.render(_inputFrameBuffer); + } + else if(_noiseSourceEnabled) { // pink noise generator + _noiseSource.render(_inputFrameBuffer); + } + _sourceGain.render(_inputFrameBuffer); // post gain } - else if(_noiseSourceEnabled) { // pink noise generator - _noiseSource.render(_inputFrameBuffer); + if (_peqEnabled) { + _peq.render(_inputFrameBuffer); // 3-band parametric eq } - _sourceGain.render(_inputFrameBuffer); // post gain - } - if (_peqEnabled && !_muted) { - _peq.render(_inputFrameBuffer); // 3-band parametric eq - } - _inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/); - + _inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/); + } + if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted && _audioOutput) { // if this person wants local loopback add that to the locally injected audio From 0ba952b2f85949ff4223278894e46faa77113b3b Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Sun, 14 Sep 2014 12:32:48 -0700 Subject: [PATCH 04/10] cleanup/remove stray static _frameOffset --- libraries/audio/src/AudioSourceTone.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioSourceTone.h b/libraries/audio/src/AudioSourceTone.h index a21806460f..812e9fe2c2 100644 --- a/libraries/audio/src/AudioSourceTone.h +++ b/libraries/audio/src/AudioSourceTone.h @@ -12,10 +12,9 @@ #ifndef hifi_AudioSourceTone_h #define hifi_AudioSourceTone_h -// Implemenbts a Gordon-Smith oscillator +// Implements a two-pole Gordon-Smith oscillator class AudioSourceTone { - static uint32_t _frameOffset; float32_t _frequency; float32_t _amplitude; float32_t _sampleRate; From 1c955384fbdf52d96f73799ace892d073dc4d29d Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Mon, 15 Sep 2014 09:56:11 -0700 Subject: [PATCH 05/10] coding standard/typo --- libraries/audio/src/AudioSourceTone.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioSourceTone.h b/libraries/audio/src/AudioSourceTone.h index 812e9fe2c2..4a9046a6b2 100644 --- a/libraries/audio/src/AudioSourceTone.h +++ b/libraries/audio/src/AudioSourceTone.h @@ -27,7 +27,7 @@ class AudioSourceTone _omega = _frequency / _sampleRate * TWO_PI; _epsilon = 2.0f * sinf(_omega / 2.0f); _yq1 = cosf(-1.0f * _omega); - _y1 = sinf(+1.04 * _omega); + _y1 = sinf(+1.0f * _omega); } public: @@ -40,7 +40,9 @@ public: } void initialize() { - setParameters(SAMPLE_RATE, 220.0f, 0.708f); + const float32_t FREQUENCY_220_HZ = 220.0f; + const float32_t GAIN_MINUS_3DB = 0.708f; + setParameters(SAMPLE_RATE, FREQUENCY_220_HZ, GAIN_MINUS_3DB); } void finalize() { From 6feb7324671572c2608b383a087607a506060311 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 15 Sep 2014 21:30:19 +0200 Subject: [PATCH 06/10] load a javascript from a message when the user agrees --- examples/loadScriptFromMessage.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/loadScriptFromMessage.js diff --git a/examples/loadScriptFromMessage.js b/examples/loadScriptFromMessage.js new file mode 100644 index 0000000000..c9d067cc3b --- /dev/null +++ b/examples/loadScriptFromMessage.js @@ -0,0 +1,27 @@ +// +// loadScriptFromMessage.js +// examples +// +// Created by Thijs Wenker on 9/15/14. +// Copyright 2014 High Fidelity, Inc. +// +// Filters script links out of incomming messages and prompts you to run the script. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +//Javascript link RegEX +const JS_LINK_REGEX = /https?:\/\/[^ ]+\.js/i; + +function onIncomingMessage(user, message) { + var script_link = JS_LINK_REGEX.exec(message); + if (script_link == null) { + return; + } + if (Window.confirm("@" + user + " sent the following script:\n" + script_link + "\nwould you like to run it?")) { + Script.load(script_link); + } +} + +GlobalServices.incomingMessage.connect(onIncomingMessage); \ No newline at end of file From d8a4d71b08853ecc3e456507cecf71b75962e4c3 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Mon, 15 Sep 2014 18:38:32 -0700 Subject: [PATCH 07/10] out-of-line inline's for performance critical member functions --- libraries/audio/src/AudioSourceTone.cpp | 59 +++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 libraries/audio/src/AudioSourceTone.cpp diff --git a/libraries/audio/src/AudioSourceTone.cpp b/libraries/audio/src/AudioSourceTone.cpp new file mode 100644 index 0000000000..e9b6e103c4 --- /dev/null +++ b/libraries/audio/src/AudioSourceTone.cpp @@ -0,0 +1,59 @@ +// +// AudioSourceTone.cpp +// hifi +// +// Created by Craig Hansen-Sturm on 8/10/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include "AudioRingBuffer.h" +#include "AudioFormat.h" +#include "AudioBuffer.h" +#include "AudioSourceTone.h" + +AudioSourceTone::AudioSourceTone() { + initialize(); +} + +AudioSourceTone::~AudioSourceTone() { + finalize(); +} + +void AudioSourceTone::finalize() { +} + +void AudioSourceTone::reset() { +} + +inline void AudioSourceTone::updateCoefficients() { + _omega = _frequency / _sampleRate * TWO_PI; + _epsilon = 2.0f * sinf(_omega / 2.0f); + _yq1 = cosf(-1.0f * _omega); + _y1 = sinf(+1.0f * _omega); +} + +inline void AudioSourceTone::initialize() { + const float32_t FREQUENCY_220_HZ = 220.0f; + const float32_t GAIN_MINUS_3DB = 0.708f; + setParameters(SAMPLE_RATE, FREQUENCY_220_HZ, GAIN_MINUS_3DB); +} + +inline void AudioSourceTone::setParameters(const float32_t sampleRate, const float32_t frequency, const float32_t amplitude) { + _sampleRate = std::max(sampleRate, 1.0f); + _frequency = std::max(frequency, 1.0f); + _amplitude = std::max(amplitude, 1.0f); + updateCoefficients(); +} + +inline void AudioSourceTone::getParameters(float32_t& sampleRate, float32_t& frequency, float32_t& amplitude) { + sampleRate = _sampleRate; + frequency = _frequency; + amplitude = _amplitude; +} + From 8de32f11705b9563e9b24e3ad4589e241f8d416c Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Mon, 15 Sep 2014 18:38:36 -0700 Subject: [PATCH 08/10] out-of-line inline's for performance critical member functions --- libraries/audio/src/AudioSourceTone.h | 78 +++++++++------------------ 1 file changed, 26 insertions(+), 52 deletions(-) diff --git a/libraries/audio/src/AudioSourceTone.h b/libraries/audio/src/AudioSourceTone.h index 4a9046a6b2..22ec95496f 100644 --- a/libraries/audio/src/AudioSourceTone.h +++ b/libraries/audio/src/AudioSourceTone.h @@ -23,67 +23,41 @@ class AudioSourceTone float32_t _yq1; float32_t _y1; - void updateCoefficients() { - _omega = _frequency / _sampleRate * TWO_PI; - _epsilon = 2.0f * sinf(_omega / 2.0f); - _yq1 = cosf(-1.0f * _omega); - _y1 = sinf(+1.0f * _omega); - } + void updateCoefficients(); public: - AudioSourceTone() { - initialize(); - } + AudioSourceTone(); + ~AudioSourceTone(); - ~AudioSourceTone() { - finalize(); - } - - void initialize() { - const float32_t FREQUENCY_220_HZ = 220.0f; - const float32_t GAIN_MINUS_3DB = 0.708f; - setParameters(SAMPLE_RATE, FREQUENCY_220_HZ, GAIN_MINUS_3DB); - } - - void finalize() { - } - - void reset() { - } + void initialize(); + void finalize(); + void reset(); - void setParameters(const float32_t sampleRate, const float32_t frequency, const float32_t amplitude) { - _sampleRate = std::max(sampleRate, 1.0f); - _frequency = std::max(frequency, 1.0f); - _amplitude = std::max(amplitude, 1.0f); - updateCoefficients(); - } + void setParameters(const float32_t sampleRate, const float32_t frequency, const float32_t amplitude); + void getParameters(float32_t& sampleRate, float32_t& frequency, float32_t& amplitude); - void getParameters(float32_t& sampleRate, float32_t& frequency, float32_t& amplitude) { - sampleRate = _sampleRate; - frequency = _frequency; - amplitude = _amplitude; - } + void render(AudioBufferFloat32& frameBuffer); +}; - void render(AudioBufferFloat32& frameBuffer) { + +inline void AudioSourceTone::render(AudioBufferFloat32& frameBuffer) { + float32_t** samples = frameBuffer.getFrameData(); + float32_t yq; + float32_t y; + for (uint16_t i = 0; i < frameBuffer.getFrameCount(); ++i) { - float32_t** samples = frameBuffer.getFrameData(); - float32_t yq; - float32_t y; - for (uint16_t i = 0; i < frameBuffer.getFrameCount(); ++i) { - - yq = _yq1 - (_epsilon * _y1); - y = _y1 + (_epsilon * yq); - - // update delays - _yq1 = yq; - _y1 = y; - - for (uint16_t j = 0; j < frameBuffer.getChannelCount(); ++j) { - samples[j][i] = _amplitude * y; - } + yq = _yq1 - (_epsilon * _y1); + y = _y1 + (_epsilon * yq); + + // update delays + _yq1 = yq; + _y1 = y; + + for (uint16_t j = 0; j < frameBuffer.getChannelCount(); ++j) { + samples[j][i] = _amplitude * y; } } -}; +} #endif From b6c38b82dcd48a439cf282875c7edfa4dcb32eee Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Mon, 15 Sep 2014 18:41:41 -0700 Subject: [PATCH 09/10] stray inline keyword --- libraries/audio/src/AudioSourceTone.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/audio/src/AudioSourceTone.cpp b/libraries/audio/src/AudioSourceTone.cpp index e9b6e103c4..da6eea6a9e 100644 --- a/libraries/audio/src/AudioSourceTone.cpp +++ b/libraries/audio/src/AudioSourceTone.cpp @@ -31,27 +31,27 @@ void AudioSourceTone::finalize() { void AudioSourceTone::reset() { } -inline void AudioSourceTone::updateCoefficients() { +void AudioSourceTone::updateCoefficients() { _omega = _frequency / _sampleRate * TWO_PI; _epsilon = 2.0f * sinf(_omega / 2.0f); _yq1 = cosf(-1.0f * _omega); _y1 = sinf(+1.0f * _omega); } -inline void AudioSourceTone::initialize() { +void AudioSourceTone::initialize() { const float32_t FREQUENCY_220_HZ = 220.0f; const float32_t GAIN_MINUS_3DB = 0.708f; setParameters(SAMPLE_RATE, FREQUENCY_220_HZ, GAIN_MINUS_3DB); } -inline void AudioSourceTone::setParameters(const float32_t sampleRate, const float32_t frequency, const float32_t amplitude) { +void AudioSourceTone::setParameters(const float32_t sampleRate, const float32_t frequency, const float32_t amplitude) { _sampleRate = std::max(sampleRate, 1.0f); _frequency = std::max(frequency, 1.0f); _amplitude = std::max(amplitude, 1.0f); updateCoefficients(); } -inline void AudioSourceTone::getParameters(float32_t& sampleRate, float32_t& frequency, float32_t& amplitude) { +void AudioSourceTone::getParameters(float32_t& sampleRate, float32_t& frequency, float32_t& amplitude) { sampleRate = _sampleRate; frequency = _frequency; amplitude = _amplitude; From ea6f89c9f489567ed51eb2460dd4601d236e8b52 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Mon, 15 Sep 2014 18:46:10 -0700 Subject: [PATCH 10/10] head penumbra filter tuning / head penumbra filter now turned on by default --- assignment-client/src/audio/AudioMixer.cpp | 34 +++++++++---------- .../resources/web/settings/describe.json | 2 +- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index bfe2f7e249..714cd0e3f3 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -321,9 +321,14 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* const float TWO_OVER_PI = 2.0f / PI; const float ZERO_DB = 1.0f; - const float NEGATIVE_ONE_DB = 0.891f; +// const float NEGATIVE_ONE_DB = 0.891f; const float NEGATIVE_THREE_DB = 0.708f; - + const float NEGATIVE_SIX_DB = 0.501f; + + const float FILTER_GAIN_AT_0 = ZERO_DB; // source is in front + const float FILTER_GAIN_AT_90 = NEGATIVE_SIX_DB; // source is incident to left or right ear + const float FILTER_GAIN_AT_180 = NEGATIVE_SIX_DB; // source is behind + const float FILTER_CUTOFF_FREQUENCY_HZ = 1000.0f; const float penumbraFilterFrequency = FILTER_CUTOFF_FREQUENCY_HZ; // constant frequency @@ -334,32 +339,25 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* // variable gain calculation broken down by quadrent if (bearingAngleToSource < -PI_OVER_TWO && bearingAngleToSource > -PI) { - // gainL(-pi/2,0b)->(-pi,-1db) penumbraFilterGainL = TWO_OVER_PI * - (ZERO_DB - NEGATIVE_ONE_DB) * (bearingAngleToSource + PI_OVER_TWO) + ZERO_DB; - // gainR(-pi/2,-3db)->(-pi,-1db) + (FILTER_GAIN_AT_0 - FILTER_GAIN_AT_180) * (bearingAngleToSource + PI_OVER_TWO) + FILTER_GAIN_AT_0; penumbraFilterGainR = TWO_OVER_PI * - (NEGATIVE_THREE_DB - NEGATIVE_ONE_DB) * (bearingAngleToSource + PI_OVER_TWO) + NEGATIVE_THREE_DB; + (FILTER_GAIN_AT_90 - FILTER_GAIN_AT_180) * (bearingAngleToSource + PI_OVER_TWO) + FILTER_GAIN_AT_90; } else if (bearingAngleToSource <= PI && bearingAngleToSource > PI_OVER_TWO) { - // gainL(+pi,-1db)->(pi/2,-3db) penumbraFilterGainL = TWO_OVER_PI * - (NEGATIVE_ONE_DB - NEGATIVE_THREE_DB) * (bearingAngleToSource - PI) + NEGATIVE_ONE_DB; - // gainR(+pi,-1db)->(pi/2,0db) + (FILTER_GAIN_AT_180 - FILTER_GAIN_AT_90) * (bearingAngleToSource - PI) + FILTER_GAIN_AT_180; penumbraFilterGainR = TWO_OVER_PI * - (NEGATIVE_ONE_DB - ZERO_DB) * (bearingAngleToSource - PI) + NEGATIVE_ONE_DB; + (FILTER_GAIN_AT_180 - FILTER_GAIN_AT_0) * (bearingAngleToSource - PI) + FILTER_GAIN_AT_180; } else if (bearingAngleToSource <= PI_OVER_TWO && bearingAngleToSource > 0) { - // gainL(+pi/2,-3db)->(0,0db) penumbraFilterGainL = TWO_OVER_PI * - (NEGATIVE_THREE_DB - ZERO_DB) * (bearingAngleToSource - PI_OVER_TWO) + NEGATIVE_THREE_DB; - // gainR(+p1/2,0db)->(0,0db) - penumbraFilterGainR = ZERO_DB; + (FILTER_GAIN_AT_90 - FILTER_GAIN_AT_0) * (bearingAngleToSource - PI_OVER_TWO) + FILTER_GAIN_AT_90; + penumbraFilterGainR = FILTER_GAIN_AT_0; } else { - // gainL(0,0db)->(-pi/2,0db) - penumbraFilterGainL = ZERO_DB; - // gainR(0,0db)->(-pi/2,-3db) + penumbraFilterGainL = FILTER_GAIN_AT_0; penumbraFilterGainR = TWO_OVER_PI * - (ZERO_DB - NEGATIVE_THREE_DB) * (bearingAngleToSource) + ZERO_DB; + (FILTER_GAIN_AT_0 - FILTER_GAIN_AT_90) * (bearingAngleToSource) + FILTER_GAIN_AT_0; } + #if 0 qDebug() << "avatar=" << listeningNodeStream diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index fee7ff21fc..2ea0aec0c7 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -67,7 +67,7 @@ "type": "checkbox", "label": "Enable Positional Filter", "help": "If enabled, positional audio stream uses lowpass filter", - "default": false + "default": true } } }