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:
@@ -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 3–5).
|
||||
*
|
||||
* @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 1–2).
|
||||
*
|
||||
* @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
|
||||
|
||||
Reference in New Issue
Block a user