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
@@ -22,6 +22,15 @@ add_library(digiradio_core STATIC
"${CORE_SRC_DIR}/EmbeddedBlobReader.cpp"
"${CORE_SRC_DIR}/TunerJson.cpp"
"${CORE_SRC_DIR}/FrequencyKHz.cpp"
"${CORE_SRC_DIR}/GainDb.cpp"
"${CORE_SRC_DIR}/FrequencyHz.cpp"
"${CORE_SRC_DIR}/EqBandIndex.cpp"
"${CORE_SRC_DIR}/BiquadCoefficients.cpp"
"${CORE_SRC_DIR}/BiquadDesign.cpp"
"${CORE_SRC_DIR}/MixerState.cpp"
"${CORE_SRC_DIR}/EqProfile.cpp"
"${CORE_SRC_DIR}/AudioProfile.cpp"
"${CORE_SRC_DIR}/AudioProfileJson.cpp"
)
target_include_directories(digiradio_core PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/../include"
@@ -46,3 +55,11 @@ add_test(NAME tuner_json_test COMMAND tuner_json_test)
add_executable(frequency_khz_test frequency_khz_test.cpp)
target_link_libraries(frequency_khz_test PRIVATE digiradio_core)
add_test(NAME frequency_khz_test COMMAND frequency_khz_test)
add_executable(biquad_design_test biquad_design_test.cpp)
target_link_libraries(biquad_design_test PRIVATE digiradio_core)
add_test(NAME biquad_design_test COMMAND biquad_design_test)
add_executable(audio_profile_json_test audio_profile_json_test.cpp)
target_link_libraries(audio_profile_json_test PRIVATE digiradio_core)
add_test(NAME audio_profile_json_test COMMAND audio_profile_json_test)
@@ -0,0 +1,66 @@
/**
* @file audio_profile_json_test.cpp
* @brief Host tests for AudioProfile JSON round-trip.
*
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
*
* Copyright 2026 Michele Bigi
* SPDX-License-Identifier: Apache-2.0
*
* @author Michele Bigi
* @date 2026-07-06
*/
#include "core/AudioProfile.hpp"
#include "core/AudioProfileJson.hpp"
#include "core/EqBandIndex.hpp"
#include "core/FrequencyHz.hpp"
#include "core/GainDb.hpp"
#include <cstdlib>
#include <iostream>
namespace {
[[nodiscard]] int runRoundTripTest()
{
core::AudioProfile profile = core::AudioProfile::factoryDefault();
const auto band = core::EqBandIndex::tryFromIndex(2U);
const auto gain = core::GainDb::tryFromDb(3.0F);
const auto center = core::FrequencyHz::tryFromHz(500U);
if (!band || !gain || !center) {
std::cerr << "test setup failed\n";
return EXIT_FAILURE;
}
profile.eq.setBand(*band,
core::EqBandSettings{
.gain = *gain,
.center = *center,
.q = 2.0F,
});
const std::string json = core::serializeAudioProfileJson(profile);
const auto parsed = core::parseAudioProfileJson(json);
if (!parsed) {
std::cerr << "parse failed\n";
return EXIT_FAILURE;
}
const auto& b = parsed->eq.band(*band);
if (b.gain.value() != 3.0F || b.center.value() != 500U
|| b.q != 2.0F) {
std::cerr << "round-trip mismatch\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
} // namespace
int main()
{
if (runRoundTripTest() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
@@ -0,0 +1,68 @@
/**
* @file biquad_design_test.cpp
* @brief Host tests for ADAU biquad design and fixpoint conversion.
*
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
*
* Copyright 2026 Michele Bigi
* SPDX-License-Identifier: Apache-2.0
*
* @author Michele Bigi
* @date 2026-07-06
*/
#include "core/BiquadDesign.hpp"
#include "core/FrequencyHz.hpp"
#include "core/GainDb.hpp"
#include <cmath>
#include <cstdlib>
#include <iostream>
namespace {
[[nodiscard]] int runUnityGainFixpointTest()
{
const auto gain = core::GainDb::zero();
const std::int32_t fix = core::gainDbToLinearFixpoint(gain);
if (fix != 0x00800000) {
std::cerr << "expected 0 dB fixpoint 0x00800000, got 0x"
<< std::hex << fix << std::dec << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
[[nodiscard]] int runFlatBiquadTest()
{
const core::BiquadCoefficients flat = core::designFlatEq();
if (std::fabs(flat.b0 - 1.0F) > 0.001F || std::fabs(flat.b1) > 0.001F
|| std::fabs(flat.b2) > 0.001F || std::fabs(flat.a0) > 0.001F
|| std::fabs(flat.a1) > 0.001F) {
std::cerr << "flat biquad coefficients unexpected\n";
return EXIT_FAILURE;
}
const auto center = core::FrequencyHz::tryFromHz(1000U);
const auto zero = core::GainDb::zero();
const core::BiquadCoefficients peaking =
core::designPeakingEq(*center, zero, 1.414F);
if (std::fabs(peaking.b0 - 1.0F) > 0.001F) {
std::cerr << "0 dB peaking should match flat b0\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
} // namespace
int main()
{
if (runUnityGainFixpointTest() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
if (runFlatBiquadTest() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}