Add live VU-meter readback via ADAU1701 Data Capture Register

Implements GET /api/audio/levels, reading all six 1×RTA level-detector
cells on demand (no background polling, no caching -- only runs when
called, per explicit request this session).

The read mechanism is the ADAU1701's documented Data Capture Register
(address 2074/0x081A, datasheet Rev.0 pp.30,36: write a (program-step,
register-select) pair to configure what the register mirrors, then
read back a 3-byte 5.19 twos-complement value). The per-meter program-
step indices are taken verbatim from SigmaStudio's own compiler output
(Firmware/ADAU1701-Firmware/IC 1_DigiRadioFinale/net_list_out2/
trap.dat), not invented -- the datasheet explicitly says these indices
must come from that compiler-generated file.

Confirmed live: all six points return distinct, plausible dBFS values,
and the output meters track a master-volume change.

Also folds two pieces of live user feedback into the app brief: the
volume slider should reach +12 dB (the firmware's real ceiling), not
stop at 0 dB, and the new levels endpoint is what a "VU meter" UI
section should poll instead of faking one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 22:33:09 +02:00
co-authored by Claude Sonnet 5
parent 3e5cfe814d
commit 5d7d4919a0
14 changed files with 506 additions and 89 deletions
@@ -14,6 +14,7 @@
#include "adau1701/Adau1701Error.hpp"
#include "core/AudioLevels.hpp"
#include "core/AudioProfile.hpp"
#include "core/EnhanceLevel.hpp"
#include "core/EqBandIndex.hpp"
@@ -269,6 +270,21 @@ public:
[[nodiscard]] std::expected<void, Adau1701Error> writeRawParam(
unsigned address, float value);
/**
* @brief readLevels — on-demand read of all six 1×RTA level meters.
*
* @dname readLevels
* @return AudioLevels snapshot, or Adau1701Error.
* @pubstate Reads the ADAU1701 Data Capture Register (address 2074) six
* times in sequence, reconfiguring it for each meter's
* program-step/register-select pair before each read; no
* background polling, only runs when called.
*
* @author Michele Bigi
* @date 2026-08-25
*/
[[nodiscard]] std::expected<core::AudioLevels, Adau1701Error> readLevels();
private:
[[nodiscard]] std::expected<void, Adau1701Error> ensureBooted() const;
[[nodiscard]] std::expected<void, Adau1701Error> safeloadGain(
@@ -277,6 +293,8 @@ private:
unsigned paramAddr, std::int32_t fixpoint);
[[nodiscard]] std::expected<void, Adau1701Error> replayProgram(
const core::DspProgram& program);
[[nodiscard]] std::expected<float, Adau1701Error> readCaptureDb(
unsigned progCount, unsigned regSel);
Adau1701Pins pins_;
core::IDspProgramSource& programSource_;
@@ -70,6 +70,9 @@ public:
[[nodiscard]] std::expected<void, core::DspError> writeRawParam(
unsigned address, float value) override;
[[nodiscard]] std::expected<core::AudioLevels, core::DspError>
readLevels() override;
private:
[[nodiscard]] static core::DspError mapError(Adau1701Error error) noexcept;