Add Slice 5 ADAU1701 runtime audio control (firmware 0.5.0).

Safeload mixer/EQ/master on the ADAU1701, persist AudioProfile in NVS,
expose /api/audio routes and web UI controls, with host tests and manual sync.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 16:47:58 +02:00
co-authored by Cursor
parent 4b931b0aad
commit ab3651e678
58 changed files with 3191 additions and 41 deletions
@@ -0,0 +1,51 @@
/**
* @file AudioProfile.hpp
* @brief Combined mixer, EQ, and master volume snapshot.
*
* 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/EqProfile.hpp"
#include "core/GainDb.hpp"
#include "core/MixerState.hpp"
namespace core {
/**
* @brief AudioProfile — full ADAU1701 user configuration snapshot.
*
* @dname AudioProfile
* @return n/a (type)
* @pubstate Plain aggregate persisted by IAudioProfileStore and applied by
* AudioService after boot.
*
* @author Michele Bigi
* @date 2026-07-06
*/
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.
/**
* @brief factoryDefault — factory-flat audio path (0 dB everywhere).
*
* @dname factoryDefault
* @return AudioProfile suitable for first boot and reset.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] static AudioProfile factoryDefault() noexcept;
};
} // namespace core
@@ -0,0 +1,77 @@
/**
* @file AudioProfileJson.hpp
* @brief JSON parse/serialise for audio profile API (pure core).
*
* 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/AudioProfile.hpp"
#include "core/ParseError.hpp"
#include <expected>
#include <string>
#include <string_view>
namespace core {
/**
* @brief serializeAudioProfileJson — serialise profile for GET /api/audio.
*
* @dname serializeAudioProfileJson
* @param profile Domain snapshot from AudioService.
* @return JSON object string for the HTTP response body.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::string serializeAudioProfileJson(
const AudioProfile& profile);
/**
* @brief parseAudioProfileJson — parse PUT /api/audio/profile body.
*
* @dname parseAudioProfileJson
* @param json Untrusted request body.
* @return AudioProfile on success, or ParseError.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::expected<AudioProfile, ParseError> parseAudioProfileJson(
std::string_view json);
/**
* @brief serializeAudioSavedJson — success response after profile apply.
*
* @dname serializeAudioSavedJson
* @return JSON object \texttt{\{"status":"saved"\}}.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::string serializeAudioSavedJson();
/**
* @brief serializeAudioErrorJson — error response for audio routes.
*
* @dname serializeAudioErrorJson
* @param reason Safe token (never includes secrets).
* @return JSON object with status and reason fields.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::string serializeAudioErrorJson(std::string_view reason);
} // namespace core
@@ -0,0 +1,50 @@
/**
* @file BiquadCoefficients.hpp
* @brief Float and fixpoint biquad coefficients for ADAU1701 PEQ.
*
* 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 <array>
#include <cstdint>
namespace core {
/**
* @brief BiquadCoefficients — five PEQ coefficients (b0..a1).
*
* @dname BiquadCoefficients
* @return n/a (type)
* @pubstate Plain value type matching SigmaStudio Param EQ cell order.
*
* @author Michele Bigi
* @date 2026-07-06
*/
struct BiquadCoefficients {
float b0; ///< Numerator b0.
float b1; ///< Numerator b1.
float b2; ///< Numerator b2.
float a0; ///< Denominator a0 (ADI stores feedback terms).
float a1; ///< Denominator a1.
/**
* @brief toFixpoint823 — convert each coefficient to 8.23 fixpoint.
*
* @dname toFixpoint823
* @return Array of five 32-bit fixpoint words for safeload.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::array<std::int32_t, 5> toFixpoint823() const noexcept;
};
} // namespace core
@@ -0,0 +1,80 @@
/**
* @file BiquadDesign.hpp
* @brief Host-testable biquad coefficient design for ADAU1701 PEQ.
*
* 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/BiquadCoefficients.hpp"
#include "core/FrequencyHz.hpp"
#include "core/GainDb.hpp"
namespace core {
/** Sample rate of the DigiRadio SigmaStudio project (Hz). */
inline constexpr std::uint32_t kAdauSampleRateHz = 48000U;
/**
* @brief designPeakingEq — compute PEQ coefficients for one band.
*
* @dname designPeakingEq
* @param center Validated centre frequency.
* @param gain Band gain in dB (0 dB yields flat response).
* @param q Quality factor (typically 0.5..10).
* @return BiquadCoefficients in SigmaStudio Param EQ order.
* @pubstate none
*
* Uses the Robert Bristow-Johnson peaking EQ formulae at kAdauSampleRateHz.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] BiquadCoefficients designPeakingEq(FrequencyHz center, GainDb gain,
float q) noexcept;
/**
* @brief designFlatEq — unity-gain bypass coefficients for a PEQ band.
*
* @dname designFlatEq
* @return Identity biquad (b0=1, others 0) as in SigmaStudio defaults.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] BiquadCoefficients designFlatEq() noexcept;
/**
* @brief gainDbToLinearFixpoint — map dB to ADAU 8.23 linear gain word.
*
* @dname gainDbToLinearFixpoint
* @param gain Validated gain in dB.
* @return 32-bit fixpoint suitable for safeload (0 dB = 0x00800000).
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::int32_t gainDbToLinearFixpoint(GainDb gain) noexcept;
/**
* @brief floatToFixpoint823 — convert a float to ADAU 8.23 fixpoint.
*
* @dname floatToFixpoint823
* @param value Floating value (typically -16..+16 for filter coeffs).
* @return Rounded 32-bit fixpoint word.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::int32_t floatToFixpoint823(float value) noexcept;
} // namespace core
@@ -0,0 +1,33 @@
/**
* @file DspError.hpp
* @brief Typed errors for DSP control operations.
*
* 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
namespace core {
/**
* @brief DspError — failure causes for IDsp operations.
*
* @dname DspError
* @return n/a (type)
* @pubstate n/a
*
* @author Michele Bigi
* @date 2026-07-06
*/
enum class DspError {
NotBooted,
SafeloadFailed,
InvalidParameter,
};
} // namespace core
@@ -0,0 +1,69 @@
/**
* @file EqBandIndex.hpp
* @brief Strong index for ADAU1701 Param EQ1 bands (05).
*
* 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 EqBandIndex — validated index into the 6-band Param EQ1 module.
*
* @dname EqBandIndex
* @return n/a (type)
* @pubstate Owns index_ in 0..kBandCount-1 after construction.
*
* @author Michele Bigi
* @date 2026-07-06
*/
class EqBandIndex {
public:
/** Number of parametric EQ bands in the SigmaStudio export. */
static constexpr std::uint8_t kBandCount = 6U;
/**
* @brief tryFromIndex — validate a band index at the boundary.
*
* @dname tryFromIndex
* @param index Untrusted band index from JSON input.
* @return EqBandIndex on success, or ParseError::MissingField.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] static std::expected<EqBandIndex, ParseError> tryFromIndex(
std::uint32_t index) noexcept;
/**
* @brief value — read the stored band index.
*
* @dname value
* @return Band index 0..5.
* @pubstate reads index_.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::uint8_t value() const noexcept;
private:
explicit EqBandIndex(std::uint8_t index) noexcept;
std::uint8_t index_;
};
} // namespace core
@@ -0,0 +1,36 @@
/**
* @file EqBandSettings.hpp
* @brief Per-band EQ settings for EqProfile.
*
* 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/FrequencyHz.hpp"
#include "core/GainDb.hpp"
namespace core {
/**
* @brief EqBandSettings — gain, centre frequency, and Q for one PEQ band.
*
* @dname EqBandSettings
* @return n/a (type)
* @pubstate Plain value type; q in 0.2..10.0 when validated via EqProfile.
*
* @author Michele Bigi
* @date 2026-07-06
*/
struct EqBandSettings {
GainDb gain; ///< Band gain in dB.
FrequencyHz center; ///< Centre frequency in Hz.
float q; ///< Quality factor.
};
} // namespace core
@@ -0,0 +1,94 @@
/**
* @file EqProfile.hpp
* @brief Six-band parametric EQ profile for the ADAU1701.
*
* 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/EqBandIndex.hpp"
#include "core/EqBandSettings.hpp"
#include "core/FrequencyHz.hpp"
#include "core/GainDb.hpp"
#include <array>
namespace core {
/**
* @brief EqProfile — six PEQ bands persisted and applied via AudioService.
*
* @dname EqProfile
* @return n/a (type)
* @pubstate Owns bands_ array; default centres match typical listening curve.
*
* @author Michele Bigi
* @date 2026-07-06
*/
class EqProfile {
public:
/**
* @brief factoryDefault — flat EQ at standard centre frequencies.
*
* @dname factoryDefault
* @return EqProfile with 0 dB on all bands.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] static EqProfile factoryDefault() noexcept;
/**
* @brief band — read settings for one PEQ band.
*
* @dname band
* @param index Band index 0..5.
* @return Const reference to band settings.
* @pubstate reads bands_.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] const EqBandSettings& band(EqBandIndex index) const noexcept;
/**
* @brief setBand — replace settings for one PEQ band.
*
* @dname setBand
* @param index Band index 0..5.
* @param settings New gain, centre, and Q.
* @pubstate writes bands_.
*
* @author Michele Bigi
* @date 2026-07-06
*/
void setBand(EqBandIndex index, EqBandSettings settings) noexcept;
/**
* @brief bands — access the full band array.
*
* @dname bands
* @return Const reference to all six band settings.
* @pubstate reads bands_.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] const std::array<EqBandSettings, EqBandIndex::kBandCount>&
bands() const noexcept;
private:
explicit EqProfile(std::array<EqBandSettings, EqBandIndex::kBandCount> bands)
noexcept;
std::array<EqBandSettings, EqBandIndex::kBandCount> bands_;
};
} // namespace core
@@ -0,0 +1,71 @@
/**
* @file FrequencyHz.hpp
* @brief Strong type for audio centre frequencies (EQ bands).
*
* 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 FrequencyHz — validated centre frequency for parametric EQ.
*
* @dname FrequencyHz
* @return n/a (type)
* @pubstate Owns hz_ within 20..20\,000 Hz (48 kHz SigmaStudio project).
*
* @author Michele Bigi
* @date 2026-07-06
*/
class FrequencyHz {
public:
/** Minimum EQ centre frequency (Hz). */
static constexpr std::uint32_t kMinHz = 20U;
/** Maximum EQ centre frequency (Hz). */
static constexpr std::uint32_t kMaxHz = 20000U;
/**
* @brief tryFromHz — validate a centre frequency at the boundary.
*
* @dname tryFromHz
* @param hz Untrusted frequency in hertz.
* @return FrequencyHz on success, or ParseError::MissingField.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] static std::expected<FrequencyHz, ParseError> tryFromHz(
std::uint32_t hz) noexcept;
/**
* @brief value — read the stored frequency in hertz.
*
* @dname value
* @return Validated centre frequency in Hz.
* @pubstate reads hz_.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::uint32_t value() const noexcept;
private:
explicit FrequencyHz(std::uint32_t hz) noexcept;
std::uint32_t hz_;
};
} // namespace core
@@ -0,0 +1,95 @@
/**
* @file GainDb.hpp
* @brief Strong type for decibel gain/attenuation values.
*
* 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 <cmath>
#include <expected>
namespace core {
/**
* @brief GainDb — validated gain in decibels for DSP volume and EQ.
*
* @dname GainDb
* @return n/a (type)
* @pubstate Owns db_ within kMinDb..kMaxDb after construction.
*
* @author Michele Bigi
* @date 2026-07-06
*/
class GainDb {
public:
/** Minimum attenuation (effectively muted on the linear path). */
static constexpr float kMinDb = -96.0F;
/** Maximum boost allowed on the DSP path. */
static constexpr float kMaxDb = 12.0F;
/**
* @brief tryFromDb — validate a gain value at the HTTP boundary.
*
* @dname tryFromDb
* @param db Untrusted gain in decibels.
* @return GainDb on success, or ParseError::MissingField.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] static std::expected<GainDb, ParseError> tryFromDb(
float db) noexcept;
/**
* @brief zero — unity gain (0 dB).
*
* @dname zero
* @return GainDb at 0 dB.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] static GainDb zero() noexcept;
/**
* @brief value — read the stored gain in decibels.
*
* @dname value
* @return Validated gain in dB.
* @pubstate reads db_.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] float value() const noexcept;
/**
* @brief linear — convert to linear amplitude (not dB).
*
* @dname linear
* @return Linear gain factor (1.0 at 0 dB).
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] float linear() const noexcept;
private:
explicit GainDb(float db) noexcept;
float db_;
};
} // namespace core
@@ -0,0 +1,88 @@
/**
* @file IAudioProfileStore.hpp
* @brief Persistence boundary for user audio configuration.
*
* 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/AudioProfile.hpp"
#include "core/StoreError.hpp"
#include <expected>
namespace core {
/**
* @brief IAudioProfileStore — load/save AudioProfile in NVS (non-secret).
*
* @dname IAudioProfileStore
* @return n/a (type)
* @pubstate Shell implementations own NVS handles; core uses fakes in tests.
*
* @author Michele Bigi
* @date 2026-07-06
*/
class IAudioProfileStore {
public:
virtual ~IAudioProfileStore() = default;
/**
* @brief hasProfile — check whether a saved profile exists.
*
* @dname hasProfile
* @return true when loadProfile would succeed.
* @pubstate reads backing storage via implementation.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] virtual bool hasProfile() const = 0;
/**
* @brief saveProfile — persist a validated audio profile.
*
* @dname saveProfile
* @param profile User mixer/EQ/master snapshot.
* @return Ok on success, or StoreError::IoFailed.
* @pubstate writes backing storage via implementation.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] virtual std::expected<void, StoreError> saveProfile(
const AudioProfile& profile) = 0;
/**
* @brief loadProfile — read the stored audio profile.
*
* @dname loadProfile
* @return AudioProfile on success, or StoreError::NotFound / IoFailed.
* @pubstate reads backing storage via implementation.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] virtual std::expected<AudioProfile, StoreError> loadProfile()
const = 0;
/**
* @brief clearProfile — erase the stored audio profile.
*
* @dname clearProfile
* @return Ok on success, or StoreError::IoFailed.
* @pubstate clears backing storage via implementation.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] virtual std::expected<void, StoreError> clearProfile() = 0;
};
} // namespace core
@@ -0,0 +1,135 @@
/**
* @file IDsp.hpp
* @brief Abstract ADAU1701 DSP control boundary (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/AudioProfile.hpp"
#include "core/DspError.hpp"
#include "core/EqBandIndex.hpp"
#include "core/FrequencyHz.hpp"
#include "core/GainDb.hpp"
#include "core/MixSource.hpp"
#include "core/MixerState.hpp"
#include "core/EqProfile.hpp"
#include <expected>
namespace core {
/**
* @brief IDsp — hardware abstraction for ADAU1701 runtime control.
*
* @dname IDsp
* @return n/a (type)
* @pubstate Implemented by adau1701::Adau1701Dsp on device; fakes in tests.
*
* All parameter updates use safeload on the implementation side.
*
* @author Michele Bigi
* @date 2026-07-06
*/
class IDsp {
public:
virtual ~IDsp() = default;
/**
* @brief applyProfile — safeload mixer, EQ, and master from a snapshot.
*
* @dname applyProfile
* @param profile Validated user configuration.
* @return Ok on success, or DspError.
* @pubstate writes ADAU1701 parameter RAM via safeload.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] virtual std::expected<void, DspError> applyProfile(
const AudioProfile& profile) = 0;
/**
* @brief applyMixer — safeload input and stereo-mixer gains.
*
* @dname applyMixer
* @param mixer Per-source and St Mixer1 levels.
* @return Ok on success, or DspError.
* @pubstate writes ADAU1701 parameter RAM via safeload.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] virtual std::expected<void, DspError> applyMixer(
const MixerState& mixer) = 0;
/**
* @brief applyEq — safeload all six PEQ bands.
*
* @dname applyEq
* @param eq Six-band parametric EQ settings.
* @return Ok on success, or DspError.
* @pubstate writes ADAU1701 parameter RAM via safeload.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] virtual std::expected<void, DspError> applyEq(
const EqProfile& eq) = 0;
/**
* @brief setInputVolume — safeload one input path (Si4684 or ESP32).
*
* @dname setInputVolume
* @param source Tuner or ESP32 I2S path.
* @param left Left channel gain.
* @param right Right channel gain.
* @return Ok on success, or DspError.
* @pubstate writes ADAU1701 parameter RAM via safeload.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] virtual std::expected<void, DspError> setInputVolume(
MixSource source, GainDb left, GainDb right) = 0;
/**
* @brief setMasterVolume — safeload Multiple 1 master output gain.
*
* @dname setMasterVolume
* @param left Left master gain.
* @param right Right master gain.
* @return Ok on success, or DspError.
* @pubstate writes ADAU1701 parameter RAM via safeload.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] virtual std::expected<void, DspError> setMasterVolume(
GainDb left, GainDb right) = 0;
/**
* @brief setEqBand — design and safeload one PEQ band.
*
* @dname setEqBand
* @param band Band index 0..5.
* @param gain Band gain in dB.
* @param center Centre frequency.
* @param q Quality factor.
* @return Ok on success, or DspError.
* @pubstate writes five coefficients via a single safeload transfer.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] virtual std::expected<void, DspError> setEqBand(
EqBandIndex band, GainDb gain, FrequencyHz center, float q) = 0;
};
} // namespace core
@@ -0,0 +1,32 @@
/**
* @file MixSource.hpp
* @brief ADAU1701 input path selector for per-source volume control.
*
* 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
namespace core {
/**
* @brief MixSource — Si4684 tuner or ESP32 I2S input on the ADAU1701.
*
* @dname MixSource
* @return n/a (type)
* @pubstate n/a
*
* @author Michele Bigi
* @date 2026-07-06
*/
enum class MixSource {
Si4684,
Esp32,
};
} // namespace core
@@ -0,0 +1,52 @@
/**
* @file MixerState.hpp
* @brief ADAU1701 input and stereo-mixer gain snapshot.
*
* 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/GainDb.hpp"
namespace core {
/**
* @brief MixerState — per-source and St Mixer1 levels for the ADAU1701.
*
* @dname MixerState
* @return n/a (type)
* @pubstate Immutable snapshot applied by AudioService via IDsp.
*
* Maps to SigmaStudio modules Si4674, ESP32, and St Mixer1.
*
* @author Michele Bigi
* @date 2026-07-06
*/
struct MixerState {
GainDb si4684Left; ///< Si4674 volume control, left channel.
GainDb si4684Right; ///< Si4674 volume control, right channel.
GainDb esp32Left; ///< ESP32 volume control, left channel.
GainDb esp32Right; ///< ESP32 volume control, right channel.
GainDb mixLeft; ///< St Mixer1 left blend level.
GainDb mixRight; ///< St Mixer1 right blend level.
/**
* @brief factoryDefault — unity gains on all paths (radio-first mix).
*
* @dname factoryDefault
* @return MixerState with 0 dB on every control.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] static MixerState factoryDefault() noexcept;
};
} // namespace core