Add stereo and bass enhance APIs via PEQ overlay.
Expose POST /api/audio/stereo-enhance and bass-enhance with 0–100 levels, persist enhancements in AudioProfile, and document the virtual EQ mapping in the manual and SigmaStudio chapter. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -17,6 +17,9 @@ idf_component_register(
|
||||
"src/BiquadDesign.cpp"
|
||||
"src/MixerState.cpp"
|
||||
"src/EqProfile.cpp"
|
||||
"src/EnhanceLevel.cpp"
|
||||
"src/AudioEnhancements.cpp"
|
||||
"src/EnhancementsDesign.cpp"
|
||||
"src/AudioProfile.cpp"
|
||||
"src/AudioProfileJson.cpp"
|
||||
INCLUDE_DIRS "include"
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file AudioEnhancements.hpp
|
||||
* @brief Stereo width and bass boost enhancement levels.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/EnhanceLevel.hpp"
|
||||
|
||||
namespace core {
|
||||
|
||||
/**
|
||||
* @brief AudioEnhancements — psychoacoustic EQ overlays (0–100 each).
|
||||
*
|
||||
* @dname AudioEnhancements
|
||||
* @return n/a (type)
|
||||
* @pubstate Mapped to PEQ bands at runtime (Chapter~\ref{ch:sigmastudio}).
|
||||
* No dedicated SigmaStudio blocks; see \texttt{applyEnhancementsToEq}.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
struct AudioEnhancements {
|
||||
EnhanceLevel stereo; ///< Stereo depth / presence (PEQ bands 3–5).
|
||||
EnhanceLevel bass; ///< Bass emphasis (PEQ bands 1–2).
|
||||
|
||||
/**
|
||||
* @brief factoryDefault — both enhancements off.
|
||||
*
|
||||
* @dname factoryDefault
|
||||
* @return AudioEnhancements at level 0.
|
||||
* @pubstate none
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] static AudioEnhancements factoryDefault() noexcept;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/AudioEnhancements.hpp"
|
||||
#include "core/EqProfile.hpp"
|
||||
#include "core/GainDb.hpp"
|
||||
#include "core/MixerState.hpp"
|
||||
@@ -32,8 +33,9 @@ namespace core {
|
||||
struct AudioProfile {
|
||||
MixerState mixer; ///< Input and stereo-mixer gains.
|
||||
EqProfile eq; ///< Six-band parametric EQ.
|
||||
GainDb masterLeft; ///< Multiple 1 master volume, left.
|
||||
GainDb masterRight; ///< Multiple 1 master volume, right.
|
||||
GainDb masterLeft; ///< Multiple 1 master volume, left.
|
||||
GainDb masterRight; ///< Multiple 1 master volume, right.
|
||||
AudioEnhancements enhancements; ///< Stereo depth and bass boost (PEQ overlay).
|
||||
|
||||
/**
|
||||
* @brief factoryDefault — factory-flat audio path (0 dB everywhere).
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "core/AudioProfile.hpp"
|
||||
#include "core/ParseError.hpp"
|
||||
|
||||
#include "core/EnhanceLevel.hpp"
|
||||
|
||||
#include <expected>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -74,4 +76,18 @@ namespace core {
|
||||
*/
|
||||
[[nodiscard]] std::string serializeAudioErrorJson(std::string_view reason);
|
||||
|
||||
/**
|
||||
* @brief parseEnhanceLevelJson — parse POST body \texttt{\{"level":0..100\}}.
|
||||
*
|
||||
* @dname parseEnhanceLevelJson
|
||||
* @param json Untrusted request body.
|
||||
* @return EnhanceLevel on success, or ParseError.
|
||||
* @pubstate none
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<EnhanceLevel, ParseError> parseEnhanceLevelJson(
|
||||
std::string_view json);
|
||||
|
||||
} // namespace core
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @file EnhanceLevel.hpp
|
||||
* @brief Strong type for 0–100 enhancement intensity (stereo / bass).
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/ParseError.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
|
||||
namespace core {
|
||||
|
||||
/**
|
||||
* @brief EnhanceLevel — validated enhancement intensity (0 = off, 100 = max).
|
||||
*
|
||||
* @dname EnhanceLevel
|
||||
* @return n/a (type)
|
||||
* @pubstate Owns level_ in 0..100 after construction.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
class EnhanceLevel {
|
||||
public:
|
||||
/** Maximum enhancement intensity. */
|
||||
static constexpr std::uint8_t kMax = 100U;
|
||||
|
||||
/**
|
||||
* @brief tryFromLevel — validate an enhancement level at the boundary.
|
||||
*
|
||||
* @dname tryFromLevel
|
||||
* @param level Untrusted 0..100 value from JSON.
|
||||
* @return EnhanceLevel on success, or ParseError::MissingField.
|
||||
* @pubstate none
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] static std::expected<EnhanceLevel, ParseError> tryFromLevel(
|
||||
std::uint32_t level) noexcept;
|
||||
|
||||
/**
|
||||
* @brief zero — enhancement disabled.
|
||||
*
|
||||
* @dname zero
|
||||
* @return EnhanceLevel at 0.
|
||||
* @pubstate none
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] static EnhanceLevel zero() noexcept;
|
||||
|
||||
/**
|
||||
* @brief value — read the stored level.
|
||||
*
|
||||
* @dname value
|
||||
* @return Level 0..100.
|
||||
* @pubstate reads level_.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::uint8_t value() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief fraction — normalised intensity in 0.0..1.0.
|
||||
*
|
||||
* @dname fraction
|
||||
* @return level / 100 as float.
|
||||
* @pubstate none
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] float fraction() const noexcept;
|
||||
|
||||
private:
|
||||
explicit EnhanceLevel(std::uint8_t level) noexcept;
|
||||
|
||||
std::uint8_t level_;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @file EnhancementsDesign.hpp
|
||||
* @brief Map enhancement levels onto Param EQ1 bands (host-testable).
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/AudioEnhancements.hpp"
|
||||
#include "core/EqProfile.hpp"
|
||||
|
||||
namespace core {
|
||||
|
||||
/**
|
||||
* @brief applyEnhancementsToEq — merge enhancement overlays into an EQ profile.
|
||||
*
|
||||
* @dname applyEnhancementsToEq
|
||||
* @param base User EQ settings (bands not touched stay unchanged).
|
||||
* @param enhancements Stereo and bass levels (0 = use base band only).
|
||||
* @return EqProfile with affected PEQ bands updated for safeload.
|
||||
* @pubstate none
|
||||
*
|
||||
* Stereo (bands 3–5): slight 1\,kHz cut plus 3/8\,kHz lift for depth.
|
||||
* Bass (bands 1–2): 100\,Hz and 400\,Hz peaking boost.
|
||||
* Band 0 (high-pass) is never modified.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] EqProfile applyEnhancementsToEq(
|
||||
const EqProfile& base, const AudioEnhancements& enhancements) noexcept;
|
||||
|
||||
} // namespace core
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @file AudioEnhancements.cpp
|
||||
* @brief AudioEnhancements factory defaults.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
|
||||
#include "core/AudioEnhancements.hpp"
|
||||
|
||||
namespace core {
|
||||
|
||||
AudioEnhancements AudioEnhancements::factoryDefault() noexcept
|
||||
{
|
||||
return AudioEnhancements{
|
||||
.stereo = EnhanceLevel::zero(),
|
||||
.bass = EnhanceLevel::zero(),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
@@ -23,6 +23,7 @@ AudioProfile AudioProfile::factoryDefault() noexcept
|
||||
.eq = EqProfile::factoryDefault(),
|
||||
.masterLeft = unity,
|
||||
.masterRight = unity,
|
||||
.enhancements = AudioEnhancements::factoryDefault(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
#include "core/AudioProfileJson.hpp"
|
||||
|
||||
#include "core/EnhanceLevel.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
@@ -144,6 +146,34 @@ namespace {
|
||||
return profile;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::expected<AudioEnhancements, ParseError> parseEnhancementsJson(
|
||||
std::string_view json)
|
||||
{
|
||||
const std::size_t enhStart = json.find("\"enhancements\"");
|
||||
if (enhStart == std::string_view::npos) {
|
||||
return AudioEnhancements::factoryDefault();
|
||||
}
|
||||
|
||||
const std::string_view enh = json.substr(enhStart);
|
||||
unsigned long stereoLevel = 0U;
|
||||
unsigned long bassLevel = 0U;
|
||||
if (!extractJsonUint(enh, "stereo_level", stereoLevel)
|
||||
|| !extractJsonUint(enh, "bass_level", bassLevel)) {
|
||||
return std::unexpected(ParseError::MissingField);
|
||||
}
|
||||
|
||||
const auto stereo = EnhanceLevel::tryFromLevel(stereoLevel);
|
||||
const auto bass = EnhanceLevel::tryFromLevel(bassLevel);
|
||||
if (!stereo || !bass) {
|
||||
return std::unexpected(ParseError::MissingField);
|
||||
}
|
||||
|
||||
return AudioEnhancements{
|
||||
.stereo = *stereo,
|
||||
.bass = *bass,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string serializeAudioProfileJson(const AudioProfile& profile)
|
||||
@@ -172,7 +202,10 @@ std::string serializeAudioProfileJson(const AudioProfile& profile)
|
||||
<< ",\"center_hz\":" << b.center.value() << ",\"q\":" << b.q
|
||||
<< '}';
|
||||
}
|
||||
out << "]}";
|
||||
out << "],\"enhancements\":{"
|
||||
<< "\"stereo_level\":" << static_cast<unsigned>(profile.enhancements.stereo.value())
|
||||
<< ",\"bass_level\":" << static_cast<unsigned>(profile.enhancements.bass.value())
|
||||
<< "}}";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
@@ -204,11 +237,17 @@ std::expected<AudioProfile, ParseError> parseAudioProfileJson(
|
||||
return std::unexpected(ParseError::MissingField);
|
||||
}
|
||||
|
||||
const auto enhancements = parseEnhancementsJson(json);
|
||||
if (!enhancements) {
|
||||
return std::unexpected(enhancements.error());
|
||||
}
|
||||
|
||||
return AudioProfile{
|
||||
.mixer = *mixer,
|
||||
.eq = *eq,
|
||||
.masterLeft = *masterLeft,
|
||||
.masterRight = *masterRight,
|
||||
.enhancements = *enhancements,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -224,4 +263,18 @@ std::string serializeAudioErrorJson(std::string_view reason)
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::expected<EnhanceLevel, ParseError> parseEnhanceLevelJson(
|
||||
std::string_view json)
|
||||
{
|
||||
if (json.find('{') == std::string_view::npos) {
|
||||
return std::unexpected(ParseError::InvalidJson);
|
||||
}
|
||||
|
||||
unsigned long level = 0U;
|
||||
if (!extractJsonUint(json, "level", level)) {
|
||||
return std::unexpected(ParseError::MissingField);
|
||||
}
|
||||
return EnhanceLevel::tryFromLevel(level);
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file EnhanceLevel.cpp
|
||||
* @brief EnhanceLevel implementation.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
|
||||
#include "core/EnhanceLevel.hpp"
|
||||
|
||||
namespace core {
|
||||
|
||||
EnhanceLevel::EnhanceLevel(std::uint8_t level) noexcept
|
||||
: level_(level)
|
||||
{
|
||||
}
|
||||
|
||||
std::expected<EnhanceLevel, ParseError> EnhanceLevel::tryFromLevel(
|
||||
std::uint32_t level) noexcept
|
||||
{
|
||||
if (level > kMax) {
|
||||
return std::unexpected(ParseError::MissingField);
|
||||
}
|
||||
return EnhanceLevel(static_cast<std::uint8_t>(level));
|
||||
}
|
||||
|
||||
EnhanceLevel EnhanceLevel::zero() noexcept
|
||||
{
|
||||
return EnhanceLevel(0U);
|
||||
}
|
||||
|
||||
std::uint8_t EnhanceLevel::value() const noexcept
|
||||
{
|
||||
return level_;
|
||||
}
|
||||
|
||||
float EnhanceLevel::fraction() const noexcept
|
||||
{
|
||||
return static_cast<float>(level_) / static_cast<float>(kMax);
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @file EnhancementsDesign.cpp
|
||||
* @brief Enhancement-to-EQ mapping implementation.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
#include "core/EnhancementsDesign.hpp"
|
||||
|
||||
#include "core/EqBandIndex.hpp"
|
||||
#include "core/FrequencyHz.hpp"
|
||||
#include "core/GainDb.hpp"
|
||||
|
||||
namespace core {
|
||||
|
||||
namespace {
|
||||
|
||||
void setBand(EqProfile& profile, std::uint8_t index, GainDb gain,
|
||||
FrequencyHz center, float q) noexcept
|
||||
{
|
||||
const auto band = EqBandIndex::tryFromIndex(index);
|
||||
if (!band) {
|
||||
return;
|
||||
}
|
||||
profile.setBand(*band, EqBandSettings{
|
||||
.gain = gain,
|
||||
.center = center,
|
||||
.q = q,
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] GainDb gainFromDb(float db) noexcept
|
||||
{
|
||||
return *GainDb::tryFromDb(db);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EqProfile applyEnhancementsToEq(const EqProfile& base,
|
||||
const AudioEnhancements& enhancements) noexcept
|
||||
{
|
||||
EqProfile profile = base;
|
||||
|
||||
if (enhancements.bass.value() > 0U) {
|
||||
const float t = enhancements.bass.fraction();
|
||||
setBand(profile, 1U, gainFromDb(9.0F * t),
|
||||
*FrequencyHz::tryFromHz(100U), 0.9F);
|
||||
setBand(profile, 2U, gainFromDb(3.0F * t),
|
||||
*FrequencyHz::tryFromHz(400U), 1.0F);
|
||||
}
|
||||
|
||||
if (enhancements.stereo.value() > 0U) {
|
||||
const float t = enhancements.stereo.fraction();
|
||||
setBand(profile, 3U, gainFromDb(-1.5F * t),
|
||||
*FrequencyHz::tryFromHz(1000U), 1.0F);
|
||||
setBand(profile, 4U, gainFromDb(2.0F * t),
|
||||
*FrequencyHz::tryFromHz(3000U), 1.0F);
|
||||
setBand(profile, 5U, gainFromDb(4.0F * t),
|
||||
*FrequencyHz::tryFromHz(8000U), 1.0F);
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
@@ -29,6 +29,9 @@ add_library(digiradio_core STATIC
|
||||
"${CORE_SRC_DIR}/BiquadDesign.cpp"
|
||||
"${CORE_SRC_DIR}/MixerState.cpp"
|
||||
"${CORE_SRC_DIR}/EqProfile.cpp"
|
||||
"${CORE_SRC_DIR}/EnhanceLevel.cpp"
|
||||
"${CORE_SRC_DIR}/AudioEnhancements.cpp"
|
||||
"${CORE_SRC_DIR}/EnhancementsDesign.cpp"
|
||||
"${CORE_SRC_DIR}/AudioProfile.cpp"
|
||||
"${CORE_SRC_DIR}/AudioProfileJson.cpp"
|
||||
)
|
||||
@@ -63,3 +66,7 @@ add_test(NAME biquad_design_test COMMAND biquad_design_test)
|
||||
add_executable(audio_profile_json_test audio_profile_json_test.cpp)
|
||||
target_link_libraries(audio_profile_json_test PRIVATE digiradio_core)
|
||||
add_test(NAME audio_profile_json_test COMMAND audio_profile_json_test)
|
||||
|
||||
add_executable(enhancements_design_test enhancements_design_test.cpp)
|
||||
target_link_libraries(enhancements_design_test PRIVATE digiradio_core)
|
||||
add_test(NAME enhancements_design_test COMMAND enhancements_design_test)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "core/AudioProfile.hpp"
|
||||
#include "core/AudioProfileJson.hpp"
|
||||
#include "core/EnhanceLevel.hpp"
|
||||
#include "core/EqBandIndex.hpp"
|
||||
#include "core/FrequencyHz.hpp"
|
||||
#include "core/GainDb.hpp"
|
||||
@@ -52,6 +53,22 @@ namespace {
|
||||
std::cerr << "round-trip mismatch\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (parsed->enhancements.stereo.value() != 0U
|
||||
|| parsed->enhancements.bass.value() != 0U) {
|
||||
std::cerr << "default enhancements mismatch\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
profile.enhancements.stereo = *core::EnhanceLevel::tryFromLevel(40U);
|
||||
profile.enhancements.bass = *core::EnhanceLevel::tryFromLevel(75U);
|
||||
const std::string json2 = core::serializeAudioProfileJson(profile);
|
||||
const auto parsed2 = core::parseAudioProfileJson(json2);
|
||||
if (!parsed2 || parsed2->enhancements.stereo.value() != 40U
|
||||
|| parsed2->enhancements.bass.value() != 75U) {
|
||||
std::cerr << "enhancements round-trip mismatch\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* @file enhancements_design_test.cpp
|
||||
* @brief Host tests for enhancement-to-EQ mapping.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
|
||||
#include "core/AudioEnhancements.hpp"
|
||||
#include "core/EnhanceLevel.hpp"
|
||||
#include "core/EnhancementsDesign.hpp"
|
||||
#include "core/EqBandIndex.hpp"
|
||||
#include "core/EqProfile.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] bool nearlyEqual(float a, float b) noexcept
|
||||
{
|
||||
return std::fabs(a - b) < 0.01F;
|
||||
}
|
||||
|
||||
[[nodiscard]] int runBassMappingTest()
|
||||
{
|
||||
const auto level = core::EnhanceLevel::tryFromLevel(100U);
|
||||
if (!level) {
|
||||
std::cerr << "level setup failed\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
core::AudioEnhancements enhancements = core::AudioEnhancements::factoryDefault();
|
||||
enhancements.bass = *level;
|
||||
|
||||
const core::EqProfile base = core::EqProfile::factoryDefault();
|
||||
const core::EqProfile effective =
|
||||
core::applyEnhancementsToEq(base, enhancements);
|
||||
|
||||
const auto band1 = core::EqBandIndex::tryFromIndex(1U);
|
||||
const auto band2 = core::EqBandIndex::tryFromIndex(2U);
|
||||
if (!band1 || !band2) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!nearlyEqual(effective.band(*band1).gain.value(), 9.0F)
|
||||
|| !nearlyEqual(effective.band(*band2).gain.value(), 3.0F)) {
|
||||
std::cerr << "bass mapping mismatch\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
[[nodiscard]] int runStereoMappingTest()
|
||||
{
|
||||
const auto level = core::EnhanceLevel::tryFromLevel(50U);
|
||||
if (!level) {
|
||||
std::cerr << "level setup failed\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
core::AudioEnhancements enhancements = core::AudioEnhancements::factoryDefault();
|
||||
enhancements.stereo = *level;
|
||||
|
||||
const core::EqProfile base = core::EqProfile::factoryDefault();
|
||||
const core::EqProfile effective =
|
||||
core::applyEnhancementsToEq(base, enhancements);
|
||||
|
||||
const auto band3 = core::EqBandIndex::tryFromIndex(3U);
|
||||
const auto band4 = core::EqBandIndex::tryFromIndex(4U);
|
||||
const auto band5 = core::EqBandIndex::tryFromIndex(5U);
|
||||
if (!band3 || !band4 || !band5) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!nearlyEqual(effective.band(*band3).gain.value(), -0.75F)
|
||||
|| !nearlyEqual(effective.band(*band4).gain.value(), 1.0F)
|
||||
|| !nearlyEqual(effective.band(*band5).gain.value(), 2.0F)) {
|
||||
std::cerr << "stereo mapping mismatch\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
[[nodiscard]] int runOffLeavesBaseTest()
|
||||
{
|
||||
const core::EqProfile base = core::EqProfile::factoryDefault();
|
||||
const core::AudioEnhancements off = core::AudioEnhancements::factoryDefault();
|
||||
const core::EqProfile effective = core::applyEnhancementsToEq(base, off);
|
||||
|
||||
const auto band1 = core::EqBandIndex::tryFromIndex(1U);
|
||||
if (!band1) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!nearlyEqual(effective.band(*band1).gain.value(),
|
||||
base.band(*band1).gain.value())) {
|
||||
std::cerr << "off should leave base unchanged\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
if (runBassMappingTest() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (runStereoMappingTest() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (runOffLeavesBaseTest() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user