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
@@ -1,6 +1,6 @@
/**
* @file Adau1701Driver.hpp
* @brief ADAU1701 SigmaDSP driver — RAM boot on every power-up.
* @brief ADAU1701 SigmaDSP driver — RAM boot and runtime safeload control.
*
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
*
@@ -14,6 +14,15 @@
#include "adau1701/Adau1701Error.hpp"
#include "core/AudioProfile.hpp"
#include "core/EqBandIndex.hpp"
#include "core/EqProfile.hpp"
#include "core/FrequencyHz.hpp"
#include "core/GainDb.hpp"
#include "core/MixSource.hpp"
#include "core/MixerState.hpp"
#include <cstdint>
#include <expected>
namespace adau1701 {
@@ -36,7 +45,7 @@ struct Adau1701Pins {
};
/**
* @brief Adau1701Driver — owns I2C + reset and loads SigmaStudio RAM.
* @brief Adau1701Driver — owns I2C + reset, boot, and safeload runtime.
*
* @dname Adau1701Driver
* @param pins Board wiring for I2C and RESET#.
@@ -45,7 +54,8 @@ struct Adau1701Pins {
* successful default_download replay.
*
* Writes the SigmaStudio export from Firmware/ADAU1701-Firmware on every
* boot (no EEPROM self-boot on DigiRadio).
* boot (no EEPROM self-boot on DigiRadio). Runtime EQ and mixer changes
* use the ADAU1701 safeload mechanism.
*
* @author Michele Bigi
* @date 2026-07-06
@@ -102,7 +112,104 @@ public:
*/
[[nodiscard]] bool isBooted() const noexcept;
/**
* @brief applyProfile — safeload mixer, EQ, and master from snapshot.
*
* @dname applyProfile
* @param profile User audio configuration.
* @return Ok on success, or Adau1701Error.
* @pubstate writes parameter RAM via safeload.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::expected<void, Adau1701Error> applyProfile(
const core::AudioProfile& profile);
/**
* @brief applyMixer — safeload input and stereo-mixer gains.
*
* @dname applyMixer
* @param mixer Per-source and St Mixer1 levels.
* @return Ok on success, or Adau1701Error.
* @pubstate writes parameter RAM via safeload.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::expected<void, Adau1701Error> applyMixer(
const core::MixerState& mixer);
/**
* @brief applyEq — safeload all six PEQ bands.
*
* @dname applyEq
* @param eq Six-band parametric EQ settings.
* @return Ok on success, or Adau1701Error.
* @pubstate writes parameter RAM via safeload.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::expected<void, Adau1701Error> applyEq(
const core::EqProfile& eq);
/**
* @brief setInputVolume — safeload one input path gain.
*
* @dname setInputVolume
* @param source Si4684 or ESP32 I2S path.
* @param left Left channel gain.
* @param right Right channel gain.
* @return Ok on success, or Adau1701Error.
* @pubstate writes parameter RAM via safeload.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::expected<void, Adau1701Error> setInputVolume(
core::MixSource source, core::GainDb left, core::GainDb right);
/**
* @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 Adau1701Error.
* @pubstate writes parameter RAM via safeload.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::expected<void, Adau1701Error> setMasterVolume(
core::GainDb left, core::GainDb right);
/**
* @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 Adau1701Error.
* @pubstate writes five coefficients in one safeload transfer.
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] std::expected<void, Adau1701Error> setEqBand(
core::EqBandIndex band, core::GainDb gain, core::FrequencyHz center,
float q);
private:
[[nodiscard]] std::expected<void, Adau1701Error> ensureBooted() const;
[[nodiscard]] std::expected<void, Adau1701Error> safeloadGain(
unsigned paramAddr, core::GainDb gain);
[[nodiscard]] std::expected<void, Adau1701Error> safeloadFixpoint(
unsigned paramAddr, std::int32_t fixpoint);
Adau1701Pins pins_;
bool booted_;
void* i2cBus_;
@@ -0,0 +1,70 @@
/**
* @file Adau1701Dsp.hpp
* @brief core::IDsp adapter over Adau1701Driver.
*
* 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 "adau1701/Adau1701Driver.hpp"
#include "core/IDsp.hpp"
namespace adau1701 {
/**
* @brief Adau1701Dsp — maps core::IDsp to Adau1701Driver safeload API.
*
* @dname Adau1701Dsp
* @return n/a (type)
* @pubstate Borrows Adau1701Driver for the process lifetime.
*
* @author Michele Bigi
* @date 2026-07-06
*/
class Adau1701Dsp final : public core::IDsp {
public:
/**
* @brief Adau1701Dsp — bind to the board driver instance.
*
* @dname Adau1701Dsp
* @param driver Booted ADAU1701 driver (must outlive this adapter).
* @pubstate stores driver reference.
*
* @author Michele Bigi
* @date 2026-07-06
*/
explicit Adau1701Dsp(Adau1701Driver& driver);
[[nodiscard]] std::expected<void, core::DspError> applyProfile(
const core::AudioProfile& profile) override;
[[nodiscard]] std::expected<void, core::DspError> applyMixer(
const core::MixerState& mixer) override;
[[nodiscard]] std::expected<void, core::DspError> applyEq(
const core::EqProfile& eq) override;
[[nodiscard]] std::expected<void, core::DspError> setInputVolume(
core::MixSource source, core::GainDb left, core::GainDb right) override;
[[nodiscard]] std::expected<void, core::DspError> setMasterVolume(
core::GainDb left, core::GainDb right) override;
[[nodiscard]] std::expected<void, core::DspError> setEqBand(
core::EqBandIndex band, core::GainDb gain, core::FrequencyHz center,
float q) override;
private:
[[nodiscard]] static core::DspError mapError(Adau1701Error error) noexcept;
Adau1701Driver& driver_;
};
} // namespace adau1701
@@ -28,6 +28,8 @@ enum class Adau1701Error {
I2cInitFailed,
ResetFailed,
DownloadFailed,
NotBooted,
SafeloadFailed,
};
} // namespace adau1701
@@ -0,0 +1,77 @@
/**
* @file Adau1701ParamMap.hpp
* @brief SigmaStudio parameter RAM addresses for DigiRadio runtime 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
#include "DigiRadio_IC_1_PARAM.h"
#include "core/EqBandIndex.hpp"
#include "core/MixSource.hpp"
#include <cstdint>
namespace adau1701 {
/**
* @brief paramAddrEqBandBase — first coefficient address for a PEQ band.
*
* @dname paramAddrEqBandBase
* @param band Band index 0..5.
* @return ADDR_PARAMEQ1_STn_B0 from the SigmaStudio export.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] inline unsigned paramAddrEqBandBase(std::uint8_t band) noexcept
{
return static_cast<unsigned>(ADDR_PARAMEQ1_ST0_B0)
+ static_cast<unsigned>(band) * 5U;
}
/**
* @brief paramAddrInputLeft — left volume address for an input source.
*
* @dname paramAddrInputLeft
* @param source Si4684 or ESP32 path.
* @return Parameter RAM address for the left channel gain cell.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] inline unsigned paramAddrInputLeft(core::MixSource source) noexcept
{
return source == core::MixSource::Si4684
? static_cast<unsigned>(ADDR_SI4674)
: static_cast<unsigned>(ADDR_ESP32);
}
/**
* @brief paramAddrInputRight — right volume address for an input source.
*
* @dname paramAddrInputRight
* @param source Si4684 or ESP32 path.
* @return Parameter RAM address for the right channel gain cell.
* @pubstate none
*
* @author Michele Bigi
* @date 2026-07-06
*/
[[nodiscard]] inline unsigned paramAddrInputRight(core::MixSource source) noexcept
{
return source == core::MixSource::Si4684
? static_cast<unsigned>(ADDR_SI4674_1)
: static_cast<unsigned>(ADDR_ESP32_1);
}
} // namespace adau1701