From 81052bbcb1cf9f5b2d6433f790fa1d01a110ec6e Mon Sep 17 00:00:00 2001 From: Bartosz Cichocki Date: Tue, 25 Jan 2022 14:44:32 +0100 Subject: [PATCH] [EGD-8162] Allow importing audio profile settings from json file This allows for imporing the audio profile setitngs, including EQ settings from the json file, thus allowing modification of those setitngs without the need of recompiling the source code. --- .../data/equalizer/earspeaker_routing.json | 46 ++++++++ .../data/equalizer/headphones_playback.json | 46 ++++++++ .../data/equalizer/headphones_routing.json | 46 ++++++++ .../data/equalizer/loudspeaker_playback.json | 46 ++++++++ .../data/equalizer/loudspeaker_routing.json | 46 ++++++++ module-audio/Audio/Profiles/Profile.cpp | 20 +++- module-audio/Audio/Profiles/Profile.hpp | 10 +- .../Audio/Profiles/ProfileConfigUtils.cpp | 108 ++++++++++++++++++ .../Audio/Profiles/ProfileConfigUtils.hpp | 13 +++ .../Profiles/ProfilePlaybackHeadphones.hpp | 24 ++-- .../Profiles/ProfilePlaybackLoudspeaker.hpp | 51 +++++---- .../Profiles/ProfileRoutingEarspeaker.hpp | 50 ++++---- .../Profiles/ProfileRoutingHeadphones.hpp | 47 ++++---- .../Profiles/ProfileRoutingLoudspeaker.hpp | 57 ++++----- module-audio/Audio/Profiles/README.md | 51 +++++++++ module-audio/Audio/codec.hpp | 12 +- module-audio/Audio/equalizer/Equalizer.cpp | 24 ++-- module-audio/Audio/equalizer/Equalizer.hpp | 65 +++++++++-- module-audio/Audio/test/CMakeLists.txt | 10 ++ .../Audio/test/testfiles/testProfile.json | 46 ++++++++ .../Audio/test/unittest_config_utils.cpp | 75 ++++++++++++ .../Audio/test/unittest_equalizer.cpp | 14 +-- module-audio/CMakeLists.txt | 1 + module-audio/README.md | 4 +- 24 files changed, 760 insertions(+), 152 deletions(-) create mode 100644 image/user/data/equalizer/earspeaker_routing.json create mode 100644 image/user/data/equalizer/headphones_playback.json create mode 100644 image/user/data/equalizer/headphones_routing.json create mode 100644 image/user/data/equalizer/loudspeaker_playback.json create mode 100644 image/user/data/equalizer/loudspeaker_routing.json create mode 100644 module-audio/Audio/Profiles/ProfileConfigUtils.cpp create mode 100644 module-audio/Audio/Profiles/ProfileConfigUtils.hpp create mode 100644 module-audio/Audio/Profiles/README.md create mode 100644 module-audio/Audio/test/testfiles/testProfile.json create mode 100644 module-audio/Audio/test/unittest_config_utils.cpp diff --git a/image/user/data/equalizer/earspeaker_routing.json b/image/user/data/equalizer/earspeaker_routing.json new file mode 100644 index 000000000..9acd11053 --- /dev/null +++ b/image/user/data/equalizer/earspeaker_routing.json @@ -0,0 +1,46 @@ +{ + "samplerate": 16000, + "bitWidth": 16, + "flags": 5, + "outputVolume": 1, + "inputGain": 0, + "inputPath": 1, + "outputPath": 1, + "filterParams": [ + { + "filterType": "HighPass", + "frequency": 700, + "samplerate": 44100, + "Q": 0.701, + "gain": 10 + }, + { + "filterType": "LowPass", + "frequency": 4993.7, + "samplerate": 44100, + "Q": 0.701, + "gain": -5 + }, + { + "filterType": "LowPass", + "frequency": 6000, + "samplerate": 44100, + "Q": 0.701, + "gain": 10 + }, + { + "filterType": "HighPass", + "frequency": 100.4, + "samplerate": 44100, + "Q": 0.701, + "gain": 10 + }, + { + "filterType": "Notch", + "frequency": 1500.7, + "samplerate": 44100, + "Q": 0.701, + "gain": -3 + } + ] +} diff --git a/image/user/data/equalizer/headphones_playback.json b/image/user/data/equalizer/headphones_playback.json new file mode 100644 index 000000000..d46ddee06 --- /dev/null +++ b/image/user/data/equalizer/headphones_playback.json @@ -0,0 +1,46 @@ +{ + "samplerate": 0, + "bitWidth": 16, + "flags": 0, + "outputVolume": 0, + "inputGain": 0, + "inputPath": 2, + "outputPath": 0, + "filterParams": [ + { + "filterType": "HighPass", + "frequency": 100.2, + "samplerate": 44100, + "Q": 0.701, + "gain": 0 + }, + { + "filterType": "LowPass", + "frequency": 17996.2, + "samplerate": 44100, + "Q": 0.701, + "gain": 0 + }, + { + "filterType": "HighShelf", + "frequency": 13984.7, + "samplerate": 44100, + "Q": 0.701, + "gain": -10 + }, + { + "filterType": "LowShelf", + "frequency": 200.4, + "samplerate": 44100, + "Q": 0.701, + "gain": -10 + }, + { + "filterType": "None", + "frequency": 1496.7, + "samplerate": 44100, + "Q": 0.701, + "gain": -4 + } + ] +} diff --git a/image/user/data/equalizer/headphones_routing.json b/image/user/data/equalizer/headphones_routing.json new file mode 100644 index 000000000..660b00a31 --- /dev/null +++ b/image/user/data/equalizer/headphones_routing.json @@ -0,0 +1,46 @@ +{ + "samplerate": 0, + "bitWidth": 16, + "flags": 0, + "outputVolume": 0, + "inputGain": 0, + "inputPath": 2, + "outputPath": 0, + "filterParams": [ + { + "filterType": "HighPass", + "frequency": 997, + "samplerate": 44100, + "Q": 0.701, + "gain": 0 + }, + { + "filterType": "LowPass", + "frequency": 4993.7, + "samplerate": 44100, + "Q": 0.701, + "gain": 0 + }, + { + "filterType": "None", + "frequency": 13984.7, + "samplerate": 44100, + "Q": 0.701, + "gain": -10 + }, + { + "filterType": "None", + "frequency": 200.4, + "samplerate": 44100, + "Q": 0.701, + "gain": -10 + }, + { + "filterType": "None", + "frequency": 1496.7, + "samplerate": 44100, + "Q": 0.701, + "gain": -4 + } + ] +} diff --git a/image/user/data/equalizer/loudspeaker_playback.json b/image/user/data/equalizer/loudspeaker_playback.json new file mode 100644 index 000000000..6a1d84cb4 --- /dev/null +++ b/image/user/data/equalizer/loudspeaker_playback.json @@ -0,0 +1,46 @@ +{ + "samplerate": 0, + "bitWidth": 16, + "flags": 0, + "outputVolume": 1, + "inputGain": 0, + "inputPath": 2, + "outputPath": 2, + "filterParams": [ + { + "filterType": "HighPass", + "frequency": 501.8, + "samplerate": 44100, + "Q": 0.701, + "gain": 0 + }, + { + "filterType": "LowPass", + "frequency": 14999.5, + "samplerate": 44100, + "Q": 0.701, + "gain": 0 + }, + { + "filterType": "HighShelf", + "frequency": 15975.7, + "samplerate": 44100, + "Q": 0.701, + "gain": -10 + }, + { + "filterType": "LowShelf", + "frequency": 401, + "samplerate": 44100, + "Q": 0.701, + "gain": -10 + }, + { + "filterType": "Parametric", + "frequency": 1496.7, + "samplerate": 44100, + "Q": 0.701, + "gain": -4 + } + ] +} diff --git a/image/user/data/equalizer/loudspeaker_routing.json b/image/user/data/equalizer/loudspeaker_routing.json new file mode 100644 index 000000000..c7c1eb845 --- /dev/null +++ b/image/user/data/equalizer/loudspeaker_routing.json @@ -0,0 +1,46 @@ +{ + "samplerate": 16000, + "bitWidth": 16, + "flags": 5, + "outputVolume": 1, + "inputGain": 0, + "inputPath": 1, + "outputPath": 2, + "filterParams": [ + { + "filterType": "HighPass", + "frequency": 307.3, + "samplerate": 16000, + "Q": 0.701, + "gain": 0 + }, + { + "filterType": "LowPass", + "frequency": 5080.1, + "samplerate": 16000, + "Q": 0.847, + "gain": 0 + }, + { + "filterType": "None", + "frequency": 15975.7, + "samplerate": 16000, + "Q": 0.701, + "gain": -10 + }, + { + "filterType": "None", + "frequency": 200.4, + "samplerate": 16000, + "Q": 0.701, + "gain": -10 + }, + { + "filterType": "None", + "frequency": 1496.7, + "samplerate": 44100, + "Q": 0.701, + "gain": -4 + } + ] +} diff --git a/module-audio/Audio/Profiles/Profile.cpp b/module-audio/Audio/Profiles/Profile.cpp index 6dfb40ecc..bf47339b7 100644 --- a/module-audio/Audio/Profiles/Profile.cpp +++ b/module-audio/Audio/Profiles/Profile.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "Profile.hpp" @@ -21,6 +21,7 @@ #include "ProfileRoutingBluetoothHFP.hpp" #include "ProfileRecordingBluetoothHFP.hpp" +#include "ProfileConfigUtils.hpp" #include namespace audio @@ -94,6 +95,22 @@ namespace audio : audioConfiguration(fmt), audioDeviceType(devType), name(name), type(type) {} + Profile::Profile(const std::string &name, + const Type type, + const std::filesystem::path &configurationPath, + const audio::codec::Configuration &fallbackConfig, + AudioDevice::Type devType) + : audioDeviceType(devType), name(name), type(type) + { + try { + audioConfiguration = loadConfigurationFromFile(configurationPath); + } + catch (std::invalid_argument &e) { + LOG_ERROR("Failed loading the profile configuration from file, using fallback! Cause: %s", e.what()); + audioConfiguration = fallbackConfig; + } + } + void Profile::SetInputGain(Gain gain) { audioConfiguration.inputGain = gain; @@ -131,4 +148,5 @@ namespace audio } return utils::enumToString(profileType); } + } // namespace audio diff --git a/module-audio/Audio/Profiles/Profile.hpp b/module-audio/Audio/Profiles/Profile.hpp index 4bc5352c6..2ecdb0a9e 100644 --- a/module-audio/Audio/Profiles/Profile.hpp +++ b/module-audio/Audio/Profiles/Profile.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #pragma once @@ -9,6 +9,8 @@ #include #include #include +#include +#include namespace audio { @@ -128,6 +130,12 @@ namespace audio const audio::codec::Configuration &fmt, AudioDevice::Type devType); + Profile(const std::string &name, + const Type type, + const std::filesystem::path &configurationPath, + const audio::codec::Configuration &fallbackConfig, + AudioDevice::Type devType); + audio::codec::Configuration audioConfiguration; AudioDevice::Type audioDeviceType = AudioDevice::Type::Audiocodec; diff --git a/module-audio/Audio/Profiles/ProfileConfigUtils.cpp b/module-audio/Audio/Profiles/ProfileConfigUtils.cpp new file mode 100644 index 000000000..c9f3fb70d --- /dev/null +++ b/module-audio/Audio/Profiles/ProfileConfigUtils.cpp @@ -0,0 +1,108 @@ +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "ProfileConfigUtils.hpp" +#include +#include +#include +#include +namespace audio +{ + namespace strings + { + constexpr inline auto samplerate = "samplerate"; + constexpr inline auto bitWidth = "bitWidth"; + constexpr inline auto flags = "flags"; + constexpr inline auto outputVolume = "outputVolume"; + constexpr inline auto outputPath = "outputPath"; + constexpr inline auto inputGain = "inputGain"; + constexpr inline auto inputPath = "inputPath"; + constexpr inline auto filterParams = "filterParams"; + constexpr inline auto filterType = "filterType"; + constexpr inline auto frequency = "frequency"; + constexpr inline auto Q = "Q"; + constexpr inline auto gain = "gain"; + } // namespace strings + + namespace utils + { + + template + constexpr inline typename std::enable_if::value && std::is_integral::value, E>::type toEnum( + T value) noexcept + { + return static_cast(value); + } + + equalizer::FilterType toFilterType(const std::string &filterName) + { + auto filterType = magic_enum::enum_cast(filterName); + if (filterType.has_value()) { + return filterType.value(); + } + else { + LOG_ERROR("Unknown filter type, using none"); + return equalizer::FilterType::None; + } + } + + const std::string readFileToString(std::filesystem::path filePath) + { + std::ifstream file; + std::string configString; + LOG_DEBUG("Reading %s ...", filePath.c_str()); + file.open(filePath); + if (not file.is_open()) { + LOG_ERROR("Can't open profile configuration file, using defaults!"); + throw std::invalid_argument("Can't open file!"); + } + while (file) { + std::string line; + std::getline(file, line); + configString += line; + } + file.close(); + return configString; + } + } // namespace utils + + const audio::codec::Configuration loadConfigurationFromFile(std::filesystem::path filePath) + { + + auto configString = utils::readFileToString(filePath); + audio::codec::Configuration config; + + json11::Json configJson; + std::string err; + configJson = json11::Json::parse(configString.c_str(), err); + if (!err.empty()) { + LOG_ERROR("Failed parsing device string!"); + throw std::invalid_argument("Can't parse the file!"); + } + config.sampleRate_Hz = configJson[strings::samplerate].int_value(); + config.bitWidth = configJson[strings::bitWidth].int_value(); + config.flags = configJson[strings::flags].int_value(); + config.outputVolume = configJson[strings::outputVolume].number_value(); + config.inputGain = configJson[strings::inputGain].number_value(); + config.inputPath = utils::toEnum(configJson[strings::inputPath].int_value()); + config.outputPath = utils::toEnum(configJson[strings::outputPath].int_value()); + + json11::Json::array paramsArray; + audio::equalizer::Equalizer filterParams; + paramsArray = configJson[strings::filterParams].array_items(); + + for (size_t i = 0; i < equalizer::bands; i++) { + auto filterType = utils::toFilterType(paramsArray[i][strings::filterType].string_value()); + auto frequency = paramsArray[i][strings::frequency].number_value(); + auto samplerate = paramsArray[i][strings::samplerate].int_value(); + auto Q = paramsArray[i][strings::Q].number_value(); + auto gain = paramsArray[i][strings::gain].number_value(); + + filterParams.at(i) = qfilter_CalculateCoeffs(filterType, frequency, samplerate, Q, gain); + } + + config.filterCoefficients = filterParams; + return config; + } + +} // namespace audio diff --git a/module-audio/Audio/Profiles/ProfileConfigUtils.hpp b/module-audio/Audio/Profiles/ProfileConfigUtils.hpp new file mode 100644 index 000000000..b9dece03d --- /dev/null +++ b/module-audio/Audio/Profiles/ProfileConfigUtils.hpp @@ -0,0 +1,13 @@ +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once +#include +#include