mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-05-19 14:15:02 -04:00
[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.
This commit is contained in:
committed by
Bartosz Cichocki
parent
6aa54d4ad5
commit
81052bbcb1
46
image/user/data/equalizer/earspeaker_routing.json
Normal file
46
image/user/data/equalizer/earspeaker_routing.json
Normal file
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
46
image/user/data/equalizer/headphones_playback.json
Normal file
46
image/user/data/equalizer/headphones_playback.json
Normal file
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
46
image/user/data/equalizer/headphones_routing.json
Normal file
46
image/user/data/equalizer/headphones_routing.json
Normal file
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
46
image/user/data/equalizer/loudspeaker_playback.json
Normal file
46
image/user/data/equalizer/loudspeaker_playback.json
Normal file
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
46
image/user/data/equalizer/loudspeaker_routing.json
Normal file
46
image/user/data/equalizer/loudspeaker_routing.json
Normal file
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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 <Utils.hpp>
|
||||
|
||||
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
|
||||
|
||||
@@ -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 <memory>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <module-vfs/paths/include/purefs/filesystem_paths.hpp>
|
||||
|
||||
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;
|
||||
|
||||
|
||||
108
module-audio/Audio/Profiles/ProfileConfigUtils.cpp
Normal file
108
module-audio/Audio/Profiles/ProfileConfigUtils.cpp
Normal file
@@ -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 <fstream>
|
||||
#include <log/log.hpp>
|
||||
#include <json11.hpp>
|
||||
#include <magic_enum.hpp>
|
||||
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 <typename E, typename T>
|
||||
constexpr inline typename std::enable_if<std::is_enum<E>::value && std::is_integral<T>::value, E>::type toEnum(
|
||||
T value) noexcept
|
||||
{
|
||||
return static_cast<E>(value);
|
||||
}
|
||||
|
||||
equalizer::FilterType toFilterType(const std::string &filterName)
|
||||
{
|
||||
auto filterType = magic_enum::enum_cast<equalizer::FilterType>(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<codec::InputPath>(configJson[strings::inputPath].int_value());
|
||||
config.outputPath = utils::toEnum<codec::OutputPath>(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
|
||||
13
module-audio/Audio/Profiles/ProfileConfigUtils.hpp
Normal file
13
module-audio/Audio/Profiles/ProfileConfigUtils.hpp
Normal file
@@ -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 <filesystem>
|
||||
#include <Audio/codec.hpp>
|
||||
|
||||
namespace audio
|
||||
{
|
||||
[[nodiscard]] const audio::codec::Configuration loadConfigurationFromFile(std::filesystem::path filePath);
|
||||
const std::string readFile(std::filesystem::path filePath);
|
||||
|
||||
} // namespace audio
|
||||
@@ -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
|
||||
|
||||
@@ -10,30 +10,30 @@ namespace audio
|
||||
class ProfilePlaybackHeadphones : public Profile
|
||||
{
|
||||
public:
|
||||
ProfilePlaybackHeadphones(Volume volume)
|
||||
explicit ProfilePlaybackHeadphones(Volume volume)
|
||||
: Profile(
|
||||
"Playback Headphones",
|
||||
Type::PlaybackHeadphones,
|
||||
purefs::dir::getUserDiskPath() / "data/equalizer/headphones_playback.json",
|
||||
audio::codec::Configuration{
|
||||
.sampleRate_Hz = 0,
|
||||
.bitWidth = 16,
|
||||
.flags = 0,
|
||||
.outputVolume = static_cast<float>(volume),
|
||||
.outputVolume = 0,
|
||||
.inputGain = 0,
|
||||
.inputPath = audio::codec::InputPath::None,
|
||||
.outputPath = audio::codec::OutputPath::Headphones,
|
||||
.filterCoefficients =
|
||||
{qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterHighPass, 100.2f, 44100, 0.701f, 0),
|
||||
{qfilter_CalculateCoeffs(audio::equalizer::FilterType::HighPass, 100.2f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::LowPass, 17996.2f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterLowPass, 17996.2f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterHighShelf, 13984.7f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterLowShelf, 200.4f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::FilterNone, 0, 44100, 0.701f, -4)}},
|
||||
audio::equalizer::FilterType::HighShelf, 13984.7f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::LowShelf, 200.4f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::None, 0, 44100, 0.701f, -4)}},
|
||||
AudioDevice::Type::Audiocodec)
|
||||
{}
|
||||
{
|
||||
audioConfiguration.outputVolume = static_cast<float>(volume);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
|
||||
@@ -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
|
||||
@@ -11,30 +11,31 @@ namespace audio
|
||||
class ProfilePlaybackLoudspeaker : public Profile
|
||||
{
|
||||
public:
|
||||
ProfilePlaybackLoudspeaker(Volume volume)
|
||||
: Profile("Playback Loudspeaker",
|
||||
Type::PlaybackLoudspeaker,
|
||||
audio::codec::Configuration{
|
||||
.sampleRate_Hz = 0,
|
||||
.bitWidth = 16,
|
||||
.flags = 0,
|
||||
.outputVolume = static_cast<float>(volume),
|
||||
.inputGain = 0,
|
||||
.inputPath = audio::codec::InputPath::None,
|
||||
.outputPath = audio::codec::OutputPath::Loudspeaker,
|
||||
.filterCoefficients =
|
||||
{qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterHighPass, 501.8f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterLowPass, 14999.5f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterHighShelf, 15975.7f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterLowShelf, 401.f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterParametric, 1496.7f, 44100, 0.701f, -4)}},
|
||||
AudioDevice::Type::Audiocodec)
|
||||
{}
|
||||
explicit ProfilePlaybackLoudspeaker(Volume volume)
|
||||
: Profile(
|
||||
"Playback Loudspeaker",
|
||||
Type::PlaybackLoudspeaker,
|
||||
purefs::dir::getUserDiskPath() / "data/equalizer/loudspeaker_playback.json",
|
||||
audio::codec::Configuration{
|
||||
.sampleRate_Hz = 0,
|
||||
.bitWidth = 16,
|
||||
.flags = 0,
|
||||
.outputVolume = 0,
|
||||
.inputGain = 0,
|
||||
.inputPath = audio::codec::InputPath::None,
|
||||
.outputPath = audio::codec::OutputPath::Loudspeaker,
|
||||
.filterCoefficients =
|
||||
{qfilter_CalculateCoeffs(audio::equalizer::FilterType::HighPass, 501.8f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::LowPass, 14999.5f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::HighShelf, 15975.7f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::LowShelf, 401.f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::Parametric, 1496.7f, 44100, 0.701f, -4)}},
|
||||
AudioDevice::Type::Audiocodec)
|
||||
{
|
||||
audioConfiguration.outputVolume = static_cast<float>(volume);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
|
||||
@@ -11,31 +11,31 @@ namespace audio
|
||||
{
|
||||
public:
|
||||
ProfileRoutingEarspeaker(Volume volume, Gain gain)
|
||||
: Profile("Routing Earspeaker",
|
||||
Type::RoutingEarspeaker,
|
||||
audio::codec::Configuration{
|
||||
.sampleRate_Hz = 16000,
|
||||
.bitWidth = 16,
|
||||
.flags = static_cast<uint32_t>(
|
||||
audio::codec::Flags::InputLeft) | // microphone use left audio channel
|
||||
static_cast<uint32_t>(audio::codec::Flags::OutputMono),
|
||||
.outputVolume = static_cast<float>(volume),
|
||||
.inputGain = static_cast<float>(gain),
|
||||
.inputPath = audio::codec::InputPath::Microphone,
|
||||
.outputPath = audio::codec::OutputPath::Earspeaker,
|
||||
.filterCoefficients =
|
||||
{qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterHighPass, 700.f, 44100, 0.701f, 10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterLowPass, 4993.7f, 44100, 0.701f, -5),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterLowPass, 6000.7f, 44100, 0.701f, 10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterHighPass, 100.4f, 44100, 0.701f, 10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterNotch, 1500.7f, 44100, 0.701f, -3)}},
|
||||
AudioDevice::Type::Audiocodec)
|
||||
{}
|
||||
: Profile(
|
||||
"Routing Earspeaker",
|
||||
Type::RoutingEarspeaker,
|
||||
purefs::dir::getUserDiskPath() / "data/equalizer/earspeaker_routing.json",
|
||||
audio::codec::Configuration{
|
||||
.sampleRate_Hz = 16000,
|
||||
.bitWidth = 16,
|
||||
.flags =
|
||||
static_cast<uint32_t>(audio::codec::Flags::InputLeft) | // microphone use left audio channel
|
||||
static_cast<uint32_t>(audio::codec::Flags::OutputMono),
|
||||
.outputVolume = 0,
|
||||
.inputGain = 0,
|
||||
.inputPath = audio::codec::InputPath::Microphone,
|
||||
.outputPath = audio::codec::OutputPath::Earspeaker,
|
||||
.filterCoefficients =
|
||||
{qfilter_CalculateCoeffs(audio::equalizer::FilterType::HighPass, 700.f, 44100, 0.701f, 10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::LowPass, 4993.7f, 44100, 0.701f, -5),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::LowPass, 6000.7f, 44100, 0.701f, 10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::HighPass, 100.4f, 44100, 0.701f, 10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::Notch, 1500.7f, 44100, 0.701f, -3)}},
|
||||
AudioDevice::Type::Audiocodec)
|
||||
{
|
||||
audioConfiguration.outputVolume = static_cast<float>(volume);
|
||||
audioConfiguration.inputGain = static_cast<float>(gain);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,30 +11,27 @@ namespace audio
|
||||
{
|
||||
public:
|
||||
ProfileRoutingHeadphones(Volume volume, Gain gain)
|
||||
: Profile("Routing Headset",
|
||||
Type::RoutingHeadphones,
|
||||
audio::codec::Configuration{
|
||||
.sampleRate_Hz = 16000,
|
||||
.bitWidth = 16,
|
||||
.flags = static_cast<uint32_t>(
|
||||
audio::codec::Flags::InputLeft) | // microphone use left audio channel
|
||||
static_cast<uint32_t>(audio::codec::Flags::OutputMono),
|
||||
.outputVolume = static_cast<float>(volume),
|
||||
.inputGain = static_cast<float>(gain),
|
||||
.inputPath = audio::codec::InputPath::Headphones,
|
||||
.outputPath = audio::codec::OutputPath::Headphones,
|
||||
.filterCoefficients =
|
||||
{qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterHighPass, 997.f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterLowPass, 4993.7f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterNone, 15975.7f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterNone, 200.4f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterNone, 1496.7f, 44100, 0.701f, -4)}},
|
||||
AudioDevice::Type::Audiocodec)
|
||||
: Profile(
|
||||
"Routing Headset",
|
||||
Type::RoutingHeadphones,
|
||||
purefs::dir::getUserDiskPath() / "data/equalizer/headphones_routing.json",
|
||||
audio::codec::Configuration{
|
||||
.sampleRate_Hz = 16000,
|
||||
.bitWidth = 16,
|
||||
.flags =
|
||||
static_cast<uint32_t>(audio::codec::Flags::InputLeft) | // microphone use left audio channel
|
||||
static_cast<uint32_t>(audio::codec::Flags::OutputMono),
|
||||
.outputVolume = static_cast<float>(volume),
|
||||
.inputGain = static_cast<float>(gain),
|
||||
.inputPath = audio::codec::InputPath::Headphones,
|
||||
.outputPath = audio::codec::OutputPath::Headphones,
|
||||
.filterCoefficients =
|
||||
{qfilter_CalculateCoeffs(audio::equalizer::FilterType::HighPass, 997.f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::LowPass, 4993.7f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::None, 15975.7f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::None, 200.4f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::None, 1496.7f, 44100, 0.701f, -4)}},
|
||||
AudioDevice::Type::Audiocodec)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -13,31 +13,36 @@ namespace audio
|
||||
|
||||
public:
|
||||
ProfileRoutingLoudspeaker(Volume volume, Gain gain)
|
||||
: Profile("Routing Speakerphone",
|
||||
Type::RoutingLoudspeaker,
|
||||
audio::codec::Configuration{
|
||||
.sampleRate_Hz = sampleRate,
|
||||
.bitWidth = 16,
|
||||
.flags = static_cast<uint32_t>(
|
||||
audio::codec::Flags::InputLeft) | // microphone use left audio channel
|
||||
static_cast<uint32_t>(audio::codec::Flags::OutputMono),
|
||||
.outputVolume = static_cast<float>(volume),
|
||||
.inputGain = static_cast<float>(gain),
|
||||
.inputPath = audio::codec::InputPath::Microphone,
|
||||
.outputPath = audio::codec::OutputPath::Loudspeaker,
|
||||
.filterCoefficients =
|
||||
{qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterHighPass, 307.3f, sampleRate, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterLowPass, 5080.1f, sampleRate, 0.847f, 0),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterNone, 15975.7f, sampleRate, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterNone, 200.4f, sampleRate, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::FilterNone, 1496.7f, sampleRate, 0.701f, -4)}},
|
||||
AudioDevice::Type::Audiocodec)
|
||||
{}
|
||||
: Profile(
|
||||
"Routing Speakerphone",
|
||||
Type::RoutingLoudspeaker,
|
||||
purefs::dir::getUserDiskPath() / "data/equalizer/loudspeaker_routing.json",
|
||||
audio::codec::Configuration{
|
||||
.sampleRate_Hz = sampleRate,
|
||||
.bitWidth = 16,
|
||||
.flags =
|
||||
static_cast<uint32_t>(audio::codec::Flags::InputLeft) | // microphone use left audio channel
|
||||
static_cast<uint32_t>(audio::codec::Flags::OutputMono),
|
||||
.outputVolume = 0,
|
||||
.inputGain = 0,
|
||||
.inputPath = audio::codec::InputPath::Microphone,
|
||||
.outputPath = audio::codec::OutputPath::Loudspeaker,
|
||||
.filterCoefficients = {qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::HighPass, 307.3f, sampleRate, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::LowPass, 5080.1f, sampleRate, 0.847f, 0),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::None, 15975.7f, sampleRate, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::None, 200.4f, sampleRate, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(
|
||||
audio::equalizer::FilterType::None, 1496.7f, sampleRate, 0.701f, -4)}},
|
||||
AudioDevice::Type::Audiocodec)
|
||||
{
|
||||
audioConfiguration.sampleRate_Hz = sampleRate;
|
||||
audioConfiguration.outputVolume = static_cast<float>(volume);
|
||||
audioConfiguration.inputGain = static_cast<float>(gain);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
|
||||
51
module-audio/Audio/Profiles/README.md
Normal file
51
module-audio/Audio/Profiles/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Profile JSON file
|
||||
This document aims to describe the profile file and how to tweak those values.
|
||||
|
||||
## First things first
|
||||
The JSON files describing the profile parameters are used to configure the audio profile with proper input/output and filter settings.
|
||||
It is **NOT** recommended to change anything than the filter settings as it might make your device speechless.
|
||||
|
||||
According to the [MAX98090 datasheet](https://datasheets.maximintegrated.com/en/ds/MAX98090.pdf), it is possible to have 7-band-EQ, although we're
|
||||
using only 5-band-EQ right now. It can be easily changed via proper register setup. All filters are realized using the [biquad filters](https://en.wikipedia.org/wiki/Digital_biquad_filter) (second order IIR filter).
|
||||
In order to prevent audio issues, when no file is present or the JSON structure is damaged, the fallback config is being loaded.
|
||||
|
||||
##File structure
|
||||
|
||||
| Field | Value type | Description |
|
||||
|--------------|------------|-------------------------------------------------------------|
|
||||
| samplerate | integer | Defines the sample rate of the profile |
|
||||
| bitWidth | integer | Defines the bit width of the audio stream |
|
||||
| flags | integer | Defines the input/output channels (see codec.hpp) |
|
||||
| outputVolume | float | Defines the output volume for the particular profile |
|
||||
| inputGain | float | Defines the input gain for the particular profile |
|
||||
| inputPath | integer | Defines the audio path for profile's input (see codec.hpp) |
|
||||
| outputPath | integer | Defines the audio path for profile's output (see codec.hpp) |
|
||||
| filterParams | array | Array of filter parameters (one per band) |
|
||||
|
||||
###Filter parameters array
|
||||
|
||||
The filter parameters array consists of a one JSON struct per filter in the codec. It means that adding next element in the array
|
||||
does not give any effect, but removing one of them will lead to wrong audio configuration! If you want to disable unused filter, select
|
||||
appropriate filter type - `None`.
|
||||
|
||||
###Array element structure
|
||||
|
||||
| Field | Value type | Description |
|
||||
|------------|------------|------------------------------------------------------------|
|
||||
| filterType | string | Defines the type of the filter |
|
||||
| frequency | float | Defines the cutoff frequency for the filter |
|
||||
| samplerate | integer | Defines the samplerate used to calculate the filter coeffs |
|
||||
| Q | float | Defines Q-factor for the filter |
|
||||
| gain | float | Defines gain for shelf-like filters |
|
||||
|
||||
Filter type can be selected from following types:
|
||||
|
||||
* `LowPass` - simple low pass filter
|
||||
* `HighPass` - simple high pass filter
|
||||
* `HighShelf` - shelving high pass filter
|
||||
* `LowShelf` - shelving low pass filter
|
||||
* `Notch` - notch filter
|
||||
* `Parametric` - parametric filter
|
||||
* `Flat` - flat transfer function filter
|
||||
* `None` - filter is disabled
|
||||
|
||||
@@ -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
|
||||
@@ -42,11 +42,11 @@ namespace audio::codec
|
||||
InputPath inputPath = InputPath::None;
|
||||
OutputPath outputPath = OutputPath::None;
|
||||
audio::equalizer::Equalizer filterCoefficients = {
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::FilterNone, 100.2f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::FilterNone, 17996.2f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::FilterNone, 13984.7f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::FilterNone, 200.4f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::FilterNone, 0, 44100, 0.701f, -4)};
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::None, 100.2f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::None, 17996.2f, 44100, 0.701f, 0),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::None, 13984.7f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::None, 200.4f, 44100, 0.701f, -10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::None, 0, 44100, 0.701f, -4)};
|
||||
};
|
||||
|
||||
} // namespace audio::codec
|
||||
|
||||
@@ -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 "Equalizer.hpp"
|
||||
@@ -15,10 +15,10 @@ namespace audio::equalizer
|
||||
constexpr auto qMinValue = .1f;
|
||||
constexpr auto qMaxValue = 10.f;
|
||||
constexpr auto frequencyMinValue = 0.f;
|
||||
if (frequency < frequencyMinValue && filter != FilterType::FilterNone) {
|
||||
if (frequency < frequencyMinValue && filter != FilterType::None) {
|
||||
throw std::invalid_argument("Negative frequency provided");
|
||||
}
|
||||
if ((Q < qMinValue || Q > qMaxValue) && filter != FilterType::FilterNone) {
|
||||
if ((Q < qMinValue || Q > qMaxValue) && filter != FilterType::None) {
|
||||
throw std::invalid_argument("Q out of range");
|
||||
}
|
||||
QFilterCoefficients filter_coeff;
|
||||
@@ -30,7 +30,7 @@ namespace audio::equalizer
|
||||
float gain_abs = pow(10, gain / 40);
|
||||
|
||||
switch (filter) {
|
||||
case FilterType::FilterBandPass:
|
||||
case FilterType::BandPass:
|
||||
filter_coeff.b0 = alpha;
|
||||
filter_coeff.b1 = 0;
|
||||
filter_coeff.b2 = -alpha;
|
||||
@@ -39,7 +39,7 @@ namespace audio::equalizer
|
||||
a0 = 1 + alpha;
|
||||
break;
|
||||
|
||||
case FilterType::FilterHighPass:
|
||||
case FilterType::HighPass:
|
||||
filter_coeff.b0 = (1 + cs) / 2;
|
||||
filter_coeff.b1 = -(1 + cs);
|
||||
filter_coeff.b2 = (1 + cs) / 2;
|
||||
@@ -48,7 +48,7 @@ namespace audio::equalizer
|
||||
a0 = 1 + alpha;
|
||||
break;
|
||||
|
||||
case FilterType::FilterLowPass:
|
||||
case FilterType::LowPass:
|
||||
filter_coeff.b0 = (1 - cs) / 2;
|
||||
filter_coeff.b1 = 1 - cs;
|
||||
filter_coeff.b2 = (1 - cs) / 2;
|
||||
@@ -56,7 +56,7 @@ namespace audio::equalizer
|
||||
filter_coeff.a2 = 1 - alpha;
|
||||
a0 = 1 + alpha;
|
||||
break;
|
||||
case FilterType::FilterFlat:
|
||||
case FilterType::Flat:
|
||||
filter_coeff.b0 = 0.0;
|
||||
filter_coeff.b1 = 0.0;
|
||||
filter_coeff.b2 = 1;
|
||||
@@ -64,7 +64,7 @@ namespace audio::equalizer
|
||||
filter_coeff.a2 = 0.0;
|
||||
a0 = 1;
|
||||
break;
|
||||
case FilterType::FilterNotch:
|
||||
case FilterType::Notch:
|
||||
filter_coeff.b0 = 1;
|
||||
filter_coeff.b1 = -2 * cs;
|
||||
filter_coeff.b2 = 1;
|
||||
@@ -72,7 +72,7 @@ namespace audio::equalizer
|
||||
filter_coeff.a2 = 1 - alpha;
|
||||
a0 = 1 + alpha;
|
||||
break;
|
||||
case FilterType::FilterHighShelf:
|
||||
case FilterType::HighShelf:
|
||||
filter_coeff.b0 = gain_abs * ((gain_abs + 1) + (gain_abs - 1) * cs + 2 * sqrt(gain_abs) * alpha);
|
||||
filter_coeff.b1 = -2 * gain_abs * ((gain_abs - 1.0) + (gain_abs + 1) * cs);
|
||||
filter_coeff.b2 = gain_abs * ((gain_abs + 1) + (gain_abs - 1) * cs - 2 * sqrt(gain_abs) * alpha);
|
||||
@@ -80,7 +80,7 @@ namespace audio::equalizer
|
||||
filter_coeff.a1 = 2 * ((gain_abs - 1) - (gain_abs + 1) * cs);
|
||||
filter_coeff.a2 = (gain_abs + 1) - (gain_abs - 1) * cs - 2 * sqrt(gain_abs) * alpha;
|
||||
break;
|
||||
case FilterType::FilterLowShelf:
|
||||
case FilterType::LowShelf:
|
||||
filter_coeff.b0 = gain_abs * ((gain_abs + 1) - (gain_abs - 1) * cs + 2 * sqrt(gain_abs) * alpha);
|
||||
filter_coeff.b1 = 2 * gain_abs * ((gain_abs - 1.0) - (gain_abs + 1) * cs);
|
||||
filter_coeff.b2 = gain_abs * ((gain_abs + 1) - (gain_abs - 1) * cs - 2 * sqrt(gain_abs) * alpha);
|
||||
@@ -88,7 +88,7 @@ namespace audio::equalizer
|
||||
filter_coeff.a1 = -2 * ((gain_abs - 1) + (gain_abs + 1) * cs);
|
||||
filter_coeff.a2 = (gain_abs + 1) + (gain_abs - 1) * cs - 2 * sqrt(gain_abs) * alpha;
|
||||
break;
|
||||
case FilterType::FilterParametric:
|
||||
case FilterType::Parametric:
|
||||
filter_coeff.b0 = 1.0 + alpha * gain_abs;
|
||||
filter_coeff.b1 = -2.0 * cs;
|
||||
filter_coeff.b2 = 1.0 - alpha * gain_abs;
|
||||
@@ -96,7 +96,7 @@ namespace audio::equalizer
|
||||
filter_coeff.a1 = -2.0 * cs;
|
||||
filter_coeff.a2 = 1.0 - alpha / gain_abs;
|
||||
break;
|
||||
case FilterType::FilterNone:
|
||||
case FilterType::None:
|
||||
filter_coeff.b0 = 1;
|
||||
filter_coeff.b1 = 0;
|
||||
filter_coeff.b2 = 0;
|
||||
|
||||
@@ -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
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
namespace audio::equalizer
|
||||
{
|
||||
/*
|
||||
* Currently we have 5 band EQ configured in the codec chip, although it is possible
|
||||
* to use up to 7 band EQ - it is configurable via internal chip registers.
|
||||
*/
|
||||
constexpr inline auto bands = 5;
|
||||
struct QFilterCoefficients
|
||||
{
|
||||
float b0;
|
||||
@@ -15,21 +20,59 @@ namespace audio::equalizer
|
||||
float b2;
|
||||
float a1;
|
||||
float a2;
|
||||
inline bool operator==(const QFilterCoefficients &rhs) const
|
||||
{
|
||||
if (b0 != rhs.b0) {
|
||||
return false;
|
||||
}
|
||||
if (b1 != rhs.b1) {
|
||||
return false;
|
||||
}
|
||||
if (b2 != rhs.b2) {
|
||||
return false;
|
||||
}
|
||||
if (a1 != rhs.a1) {
|
||||
return false;
|
||||
}
|
||||
if (a2 != rhs.a2) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
inline bool operator!=(const QFilterCoefficients &rhs) const
|
||||
{
|
||||
if (b0 != rhs.b0) {
|
||||
return true;
|
||||
}
|
||||
if (b1 != rhs.b1) {
|
||||
return true;
|
||||
}
|
||||
if (b2 != rhs.b2) {
|
||||
return true;
|
||||
}
|
||||
if (a1 != rhs.a1) {
|
||||
return true;
|
||||
}
|
||||
if (a2 != rhs.a2) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
using Equalizer = std::array<QFilterCoefficients, 5>;
|
||||
using Equalizer = std::array<QFilterCoefficients, bands>;
|
||||
|
||||
enum class FilterType
|
||||
{
|
||||
FilterBandPass,
|
||||
FilterHighPass,
|
||||
FilterLowPass,
|
||||
FilterFlat,
|
||||
FilterNotch,
|
||||
FilterLowShelf,
|
||||
FilterHighShelf,
|
||||
FilterParametric,
|
||||
FilterNone
|
||||
BandPass,
|
||||
HighPass,
|
||||
LowPass,
|
||||
Flat,
|
||||
Notch,
|
||||
LowShelf,
|
||||
HighShelf,
|
||||
Parametric,
|
||||
None
|
||||
};
|
||||
|
||||
QFilterCoefficients qfilter_CalculateCoeffs(
|
||||
|
||||
@@ -51,4 +51,14 @@ add_catch2_executable(
|
||||
module-utils
|
||||
)
|
||||
|
||||
add_catch2_executable(
|
||||
NAME
|
||||
audio-config-utils
|
||||
SRCS
|
||||
unittest_config_utils.cpp
|
||||
LIBS
|
||||
module-audio
|
||||
module-utils
|
||||
)
|
||||
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/testfiles" DESTINATION "${CMAKE_BINARY_DIR}")
|
||||
|
||||
46
module-audio/Audio/test/testfiles/testProfile.json
Normal file
46
module-audio/Audio/test/testfiles/testProfile.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"samplerate": 44100,
|
||||
"bitWidth": 8,
|
||||
"flags": 1,
|
||||
"outputVolume": 1,
|
||||
"inputGain": 2,
|
||||
"inputPath": 2,
|
||||
"outputPath": 3,
|
||||
"filterParams": [
|
||||
{
|
||||
"filterType": "None",
|
||||
"frequency": 1000.2,
|
||||
"samplerate": 44100,
|
||||
"Q": 0.7,
|
||||
"gain": 10
|
||||
},
|
||||
{
|
||||
"filterType": "HighPass",
|
||||
"frequency": 2000.0,
|
||||
"samplerate": 8000,
|
||||
"Q": 1.7,
|
||||
"gain": -10
|
||||
},
|
||||
{
|
||||
"filterType": "LowPass",
|
||||
"frequency": 10000,
|
||||
"samplerate": 44100,
|
||||
"Q": 0.75,
|
||||
"gain": 2.5
|
||||
},
|
||||
{
|
||||
"filterType": "Notch",
|
||||
"frequency": 2500,
|
||||
"samplerate": 44100,
|
||||
"Q": 4.4,
|
||||
"gain": 5.3
|
||||
},
|
||||
{
|
||||
"filterType": "BandPass",
|
||||
"frequency": 1000,
|
||||
"samplerate": 44100,
|
||||
"Q": 0.7,
|
||||
"gain": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
75
module-audio/Audio/test/unittest_config_utils.cpp
Normal file
75
module-audio/Audio/test/unittest_config_utils.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
|
||||
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
#include <Audio/Profiles/ProfileConfigUtils.hpp>
|
||||
#include <purefs/filesystem_paths.hpp>
|
||||
|
||||
TEST_CASE("QFilterCoefficients - overloaded operator test")
|
||||
{
|
||||
audio::equalizer::QFilterCoefficients coeffs1{1, 1, 1, 1, 1}, coeffs2{1, 1, 1, 1, 1};
|
||||
|
||||
SECTION("Equals")
|
||||
{
|
||||
REQUIRE(coeffs1 == coeffs2);
|
||||
}
|
||||
|
||||
SECTION("Differs 1/5")
|
||||
{
|
||||
coeffs1.b0 = 2;
|
||||
REQUIRE(coeffs1 != coeffs2);
|
||||
}
|
||||
|
||||
SECTION("Differs 2/5")
|
||||
{
|
||||
coeffs2.b1 = 2;
|
||||
REQUIRE(coeffs1 != coeffs2);
|
||||
}
|
||||
|
||||
SECTION("Differs 3/5")
|
||||
{
|
||||
coeffs1.a1 = 2;
|
||||
REQUIRE(coeffs1 != coeffs2);
|
||||
}
|
||||
|
||||
SECTION("Differs 4/5")
|
||||
{
|
||||
coeffs2.a2 = 2;
|
||||
REQUIRE(coeffs1 != coeffs2);
|
||||
}
|
||||
|
||||
SECTION("Differs 5/5")
|
||||
{
|
||||
coeffs2.b2 = 2;
|
||||
REQUIRE(coeffs1 != coeffs2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Audio profile config utils")
|
||||
{
|
||||
SECTION("Loading config from json file")
|
||||
{
|
||||
auto config = audio::loadConfigurationFromFile("testfiles/testProfile.json");
|
||||
|
||||
REQUIRE(config.sampleRate_Hz == 44100);
|
||||
REQUIRE(config.bitWidth == 8);
|
||||
REQUIRE(config.flags == 1);
|
||||
REQUIRE(config.inputGain == 2.0);
|
||||
REQUIRE(config.outputVolume == 1.0);
|
||||
REQUIRE(config.inputPath == audio::codec::InputPath::None);
|
||||
REQUIRE(config.outputPath == audio::codec::OutputPath::None);
|
||||
|
||||
audio::equalizer::Equalizer filterCoefficients = {
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::None, 1000.2f, 44100, 0.7f, 10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::HighPass, 2000.0f, 8000, 1.7f, -10),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::LowPass, 10000.0f, 44100, 0.75f, 2.5),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::Notch, 2500.0f, 44100, 4.4f, 5.3),
|
||||
qfilter_CalculateCoeffs(audio::equalizer::FilterType::BandPass, 1000, 44100, 0.7f, 10)};
|
||||
|
||||
for (size_t i = 0; i < audio::equalizer::bands; i++) {
|
||||
REQUIRE(config.filterCoefficients.at(i) == filterCoefficients.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
@@ -14,7 +14,7 @@ SCENARIO("Calculate filter coeff")
|
||||
|
||||
GIVEN("High pass filter")
|
||||
{
|
||||
const auto filterHighPass = qfilter_CalculateCoeffs(FilterType::FilterHighPass, 300.9f, 44100, 0.701f, 0);
|
||||
const auto filterHighPass = qfilter_CalculateCoeffs(FilterType::HighPass, 300.9f, 44100, 0.701f, 0);
|
||||
THEN("Registers 1,2,3 should match b0 setup")
|
||||
{
|
||||
const auto [byte1, byte2, byte3] = utils::floatingPointConverter(filterHighPass.b0);
|
||||
@@ -53,7 +53,7 @@ SCENARIO("Calculate filter coeff")
|
||||
}
|
||||
GIVEN("High shelf filter")
|
||||
{
|
||||
const auto filterHighShelf = qfilter_CalculateCoeffs(FilterType::FilterLowShelf, 401.f, 44100, 0.701f, -10);
|
||||
const auto filterHighShelf = qfilter_CalculateCoeffs(FilterType::LowShelf, 401.f, 44100, 0.701f, -10);
|
||||
THEN("Registers 1,2 should match b0 setup")
|
||||
{
|
||||
const auto [byte1, byte2, _] = utils::floatingPointConverter(filterHighShelf.b0);
|
||||
@@ -88,7 +88,7 @@ SCENARIO("Calculate filter coeff")
|
||||
|
||||
GIVEN("Filter none")
|
||||
{
|
||||
const auto filterNone = qfilter_CalculateCoeffs(FilterType::FilterNone, 0, 0, 0, 0);
|
||||
const auto filterNone = qfilter_CalculateCoeffs(FilterType::None, 0, 0, 0, 0);
|
||||
THEN("Register 1 should be equal to 16. Registers 2,3 should be equal to 0")
|
||||
{
|
||||
const auto [byte1, byte2, byte3] = utils::floatingPointConverter(filterNone.b0);
|
||||
@@ -132,7 +132,7 @@ SCENARIO("Calculate filter coeff")
|
||||
{
|
||||
THEN("Calculation of coefficients should throw")
|
||||
{
|
||||
REQUIRE_THROWS_AS(qfilter_CalculateCoeffs(FilterType::FilterHighPass, 300.9f, 44100, -1.f, 0),
|
||||
REQUIRE_THROWS_AS(qfilter_CalculateCoeffs(FilterType::HighPass, 300.9f, 44100, -1.f, 0),
|
||||
std::invalid_argument);
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ SCENARIO("Calculate filter coeff")
|
||||
{
|
||||
THEN("Calculation of coefficients should throw")
|
||||
{
|
||||
REQUIRE_THROWS_AS(qfilter_CalculateCoeffs(FilterType::FilterHighPass, 300.9f, 44100, 100.f, 0),
|
||||
REQUIRE_THROWS_AS(qfilter_CalculateCoeffs(FilterType::HighPass, 300.9f, 44100, 100.f, 0),
|
||||
std::invalid_argument);
|
||||
}
|
||||
}
|
||||
@@ -150,7 +150,7 @@ SCENARIO("Calculate filter coeff")
|
||||
{
|
||||
THEN("Calculation of coefficients should throw")
|
||||
{
|
||||
REQUIRE_THROWS_AS(qfilter_CalculateCoeffs(FilterType::FilterHighPass, -300.9f, 44100, 0.2f, 0),
|
||||
REQUIRE_THROWS_AS(qfilter_CalculateCoeffs(FilterType::HighPass, -300.9f, 44100, 0.2f, 0),
|
||||
std::invalid_argument);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ target_sources(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/RecorderOperation.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/RouterOperation.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Audio/Profiles/Profile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Audio/Profiles/ProfileConfigUtils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Audio/ServiceObserver.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Audio/Stream.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Audio/StreamFactory.cpp
|
||||
|
||||
@@ -117,7 +117,9 @@ In order to store `Operation` configuration a concept of `Audio Profile` has bee
|
||||
Profiles configurations can be found in directory: [Profiles](./Audio/Profiles)
|
||||
`Operations` may not implement support for all possible `Profile` parameters i.e. `inputGain` and `inputPath` will be ignored in playback `Operation`.
|
||||
|
||||
**IMPORTANT:** For the time being profiles are not loaded and stored into database. This should be fixed.
|
||||
~~**IMPORTANT:** For the time being profiles are not loaded and stored into database. This should be fixed.~~
|
||||
**NOTE:** Currently some of the profiles are configurable via json files located [here](../image/user/data/equalizer). The json format explanation can be found [here](./Audio/Profiles/README.md).
|
||||
|
||||
**IMPORTANT:** Callbacks mechanism is only experimental and should be considered as incomplete.
|
||||
|
||||
# Audio class
|
||||
|
||||
Reference in New Issue
Block a user