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:
2026-07-06 17:00:12 +02:00
co-authored by Cursor
parent 6c350f5c48
commit fa532e068f
22 changed files with 826 additions and 19 deletions
@@ -14,6 +14,8 @@
#include "core/AudioProfile.hpp"
#include "core/DspError.hpp"
#include "core/EnhanceLevel.hpp"
#include "core/EnhancementsDesign.hpp"
#include "core/EqBandIndex.hpp"
#include "core/FrequencyHz.hpp"
#include "core/GainDb.hpp"
@@ -147,9 +149,45 @@ public:
core::EqBandIndex band, core::GainDb gain, core::FrequencyHz center,
float q, bool persist);
/**
* @brief setStereoEnhance — adjust stereo depth overlay (PEQ bands 35).
*
* @dname setStereoEnhance
* @param level Intensity 0..100 (0 = off).
* @param persist When true and store is set, write NVS.
* @return Ok on success, or StoreError.
* @pubstate updates profile_.enhancements.stereo and safeloads effective EQ.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::expected<void, core::StoreError> setStereoEnhance(
core::EnhanceLevel level, bool persist);
/**
* @brief setBassEnhance — adjust bass emphasis overlay (PEQ bands 12).
*
* @dname setBassEnhance
* @param level Intensity 0..100 (0 = off).
* @param persist When true and store is set, write NVS.
* @return Ok on success, or StoreError.
* @pubstate updates profile_.enhancements.bass and safeloads effective EQ.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::expected<void, core::StoreError> setBassEnhance(
core::EnhanceLevel level, bool persist);
private:
[[nodiscard]] std::expected<void, core::StoreError> persistProfile() const;
[[nodiscard]] std::expected<void, core::StoreError> applyProfileToDsp(
const core::AudioProfile& profile);
[[nodiscard]] std::expected<void, core::StoreError> applyEffectiveEq(
bool persist);
core::IDsp& dsp_;
core::IAudioProfileStore* store_;
core::AudioProfile profile_;
@@ -15,6 +15,19 @@
namespace audio {
namespace {
[[nodiscard]] core::AudioProfile profileForHardware(
const core::AudioProfile& profile) noexcept
{
core::AudioProfile hardware = profile;
hardware.eq =
core::applyEnhancementsToEq(profile.eq, profile.enhancements);
return hardware;
}
} // namespace
AudioService::AudioService(core::IDsp& dsp, core::IAudioProfileStore* store)
: dsp_(dsp)
, store_(store)
@@ -30,8 +43,8 @@ std::expected<void, core::DspError> AudioService::loadAndApply()
}
}
if (auto applied = dsp_.applyProfile(profile_); !applied) {
return applied;
if (auto applied = applyProfileToDsp(profile_); !applied) {
return std::unexpected(core::DspError::SafeloadFailed);
}
return {};
}
@@ -49,11 +62,35 @@ std::expected<void, core::StoreError> AudioService::persistProfile() const
return store_->saveProfile(profile_);
}
std::expected<void, core::StoreError> AudioService::applyProfileToDsp(
const core::AudioProfile& profile)
{
const core::AudioProfile hardware = profileForHardware(profile);
if (auto applied = dsp_.applyProfile(hardware); !applied) {
return std::unexpected(core::StoreError::IoFailed);
}
return {};
}
std::expected<void, core::StoreError> AudioService::applyEffectiveEq(
bool persist)
{
const core::EqProfile effective = core::applyEnhancementsToEq(
profile_.eq, profile_.enhancements);
if (auto applied = dsp_.applyEq(effective); !applied) {
return std::unexpected(core::StoreError::IoFailed);
}
if (persist) {
return persistProfile();
}
return {};
}
std::expected<void, core::StoreError> AudioService::applyProfile(
const core::AudioProfile& profile, bool persist)
{
if (auto applied = dsp_.applyProfile(profile); !applied) {
return std::unexpected(core::StoreError::IoFailed);
if (auto applied = applyProfileToDsp(profile); !applied) {
return applied;
}
profile_ = profile;
if (persist) {
@@ -101,18 +138,26 @@ std::expected<void, core::StoreError> AudioService::setEqBand(
core::EqBandIndex band, core::GainDb gain, core::FrequencyHz center, float q,
bool persist)
{
if (auto applied = dsp_.setEqBand(band, gain, center, q); !applied) {
return std::unexpected(core::StoreError::IoFailed);
}
profile_.eq.setBand(band, core::EqBandSettings{
.gain = gain,
.center = center,
.q = q,
});
if (persist) {
return persistProfile();
}
return {};
return applyEffectiveEq(persist);
}
std::expected<void, core::StoreError> AudioService::setStereoEnhance(
core::EnhanceLevel level, bool persist)
{
profile_.enhancements.stereo = level;
return applyEffectiveEq(persist);
}
std::expected<void, core::StoreError> AudioService::setBassEnhance(
core::EnhanceLevel level, bool persist)
{
profile_.enhancements.bass = level;
return applyEffectiveEq(persist);
}
} // namespace audio