Add generic ADAU1701 parameter access (GET/PUT /api/dsp/param)
New core::IDsp::writeRawParam(address, value) safeloads any named
Parameter RAM cell in the compiled SigmaStudio program (74 cells: mixer/
Beep1/PEQ coefficients/limiter thresholds/etc — the same set SigmaStudio's
own Remote Connection, components/net/SigmaStudioTcpServer, already has
full access to). Value is a plain SigmaStudio floating coefficient,
converted to ADAU 8.23 fixpoint via the existing core::floatToFixpoint823.
Adau1701ParamTable.hpp holds the name->address table extracted from
Firmware/ADAU1701-Firmware/DigiRadio_IC_1_PARAM.h; core::DspParamJson
handles the wire format without core/ depending on the adau1701 driver
(net/SetupWebServer.cpp bridges the two, consistent with how other
JSON DTOs are supplied their data by the net layer).
GET /api/dsp/params lists every cell (name + address) for discovery.
PUT /api/dsp/param {"name":...,"value":...} writes one. Deliberately no
domain validation, matching the trust level already implied by the
existing SigmaStudio TCP bridge being reachable on the same network.
HTTP routes were registered in the SetupWebServer.cpp change committed
alongside the FM band scan feature (3a10ed7).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178rASQ6ZETPMUamvpoR2KR
This commit is contained in:
@@ -12,6 +12,7 @@ idf_component_register(
|
|||||||
"src/WifiScanJson.cpp"
|
"src/WifiScanJson.cpp"
|
||||||
"src/EmbeddedBlobReader.cpp"
|
"src/EmbeddedBlobReader.cpp"
|
||||||
"src/TunerJson.cpp"
|
"src/TunerJson.cpp"
|
||||||
|
"src/DspParamJson.cpp"
|
||||||
"src/FrequencyKHz.cpp"
|
"src/FrequencyKHz.cpp"
|
||||||
"src/GainDb.cpp"
|
"src/GainDb.cpp"
|
||||||
"src/FrequencyHz.cpp"
|
"src/FrequencyHz.cpp"
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/**
|
||||||
|
* @file DspParamJson.hpp
|
||||||
|
* @brief JSON parse/serialise for generic ADAU1701 parameter access (pure core).
|
||||||
|
*
|
||||||
|
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||||
|
*
|
||||||
|
* Copyright 2026 Michele Bigi
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/ParseError.hpp"
|
||||||
|
|
||||||
|
#include <expected>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief DspParamWriteRequest — parsed PUT /api/dsp/param body.
|
||||||
|
*
|
||||||
|
* @dname DspParamWriteRequest
|
||||||
|
* @return n/a (type)
|
||||||
|
* @pubstate Plain DTO filled by parseDspParamWriteJson at the HTTP boundary.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
struct DspParamWriteRequest {
|
||||||
|
std::string name; ///< Cell name, looked up against the driver's table.
|
||||||
|
float value; ///< SigmaStudio floating coefficient to write.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief DspParamInfo — one discoverable Parameter RAM cell.
|
||||||
|
*
|
||||||
|
* @dname DspParamInfo
|
||||||
|
* @return n/a (type)
|
||||||
|
* @pubstate Plain DTO; caller supplies the table (core stays hardware-agnostic).
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
struct DspParamInfo {
|
||||||
|
std::string_view name; ///< Cell name as exported by SigmaStudio.
|
||||||
|
unsigned address; ///< Parameter RAM address for safeload writes.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief parseDspParamWriteJson — validate PUT /api/dsp/param body.
|
||||||
|
*
|
||||||
|
* @dname parseDspParamWriteJson
|
||||||
|
* @param json Untrusted request body from the HTTP handler.
|
||||||
|
* @return DspParamWriteRequest on success, or a ParseError.
|
||||||
|
* @pubstate none
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::expected<DspParamWriteRequest, ParseError>
|
||||||
|
parseDspParamWriteJson(std::string_view json);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief serializeDspParamListJson — serialise the discoverable cell list.
|
||||||
|
*
|
||||||
|
* @dname serializeDspParamListJson
|
||||||
|
* @param params Every named cell in the compiled DSP program.
|
||||||
|
* @return JSON object with a `params` array for the HTTP response body.
|
||||||
|
* @pubstate none
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::string serializeDspParamListJson(
|
||||||
|
const std::vector<DspParamInfo>& params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief serializeDspParamErrorJson — serialise a DSP param API error.
|
||||||
|
*
|
||||||
|
* @dname serializeDspParamErrorJson
|
||||||
|
* @param reason Short machine-readable cause (never a secret).
|
||||||
|
* @return JSON object string with status error and reason fields.
|
||||||
|
* @pubstate none
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::string serializeDspParamErrorJson(const char* reason);
|
||||||
|
|
||||||
|
} // namespace core
|
||||||
@@ -145,6 +145,24 @@ public:
|
|||||||
*/
|
*/
|
||||||
[[nodiscard]] virtual std::expected<void, DspError> setBeepEnabled(
|
[[nodiscard]] virtual std::expected<void, DspError> setBeepEnabled(
|
||||||
bool enabled) = 0;
|
bool enabled) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief writeRawParam — safeload any named Parameter RAM cell.
|
||||||
|
*
|
||||||
|
* @dname writeRawParam
|
||||||
|
* @param address Parameter RAM address.
|
||||||
|
* @param value Coefficient in the SigmaStudio floating
|
||||||
|
* convention; converted to ADAU 8.23 fixpoint.
|
||||||
|
* @return Ok on success, or DspError.
|
||||||
|
* @pubstate writes ADAU1701 parameter RAM via safeload. Not part of
|
||||||
|
* AudioProfile — live-only, never persisted. No domain
|
||||||
|
* validation; see Adau1701Driver::writeRawParam().
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
[[nodiscard]] virtual std::expected<void, DspError> writeRawParam(
|
||||||
|
unsigned address, float value) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* @file DspParamJson.cpp
|
||||||
|
* @brief DspParamJson implementation.
|
||||||
|
*
|
||||||
|
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||||
|
*
|
||||||
|
* Copyright 2026 Michele Bigi
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/DspParamJson.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] std::string_view extractJsonString(std::string_view json,
|
||||||
|
std::string_view key)
|
||||||
|
{
|
||||||
|
const std::string needle =
|
||||||
|
std::string("\"") + std::string(key) + "\":\"";
|
||||||
|
const std::size_t start = json.find(needle);
|
||||||
|
if (start == std::string_view::npos) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const std::size_t valueStart = start + needle.size();
|
||||||
|
const std::size_t valueEnd = json.find('"', valueStart);
|
||||||
|
if (valueEnd == std::string_view::npos) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return json.substr(valueStart, valueEnd - valueStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool extractJsonFloat(std::string_view json,
|
||||||
|
std::string_view key, float& out)
|
||||||
|
{
|
||||||
|
const std::string needle = std::string("\"") + std::string(key) + "\":";
|
||||||
|
const std::size_t start = json.find(needle);
|
||||||
|
if (start == std::string_view::npos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const std::size_t valueStart = start + needle.size();
|
||||||
|
char* end = nullptr;
|
||||||
|
out = std::strtof(json.data() + valueStart, &end);
|
||||||
|
return end != json.data() + valueStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::expected<DspParamWriteRequest, ParseError> parseDspParamWriteJson(
|
||||||
|
std::string_view json)
|
||||||
|
{
|
||||||
|
if (json.find('{') == std::string_view::npos) {
|
||||||
|
return std::unexpected(ParseError::InvalidJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string_view name = extractJsonString(json, "name");
|
||||||
|
if (name.empty()) {
|
||||||
|
return std::unexpected(ParseError::MissingField);
|
||||||
|
}
|
||||||
|
|
||||||
|
float value = 0.0F;
|
||||||
|
if (!extractJsonFloat(json, "value", value)) {
|
||||||
|
return std::unexpected(ParseError::MissingField);
|
||||||
|
}
|
||||||
|
|
||||||
|
DspParamWriteRequest req;
|
||||||
|
req.name.assign(name.begin(), name.end());
|
||||||
|
req.value = value;
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string serializeDspParamListJson(const std::vector<DspParamInfo>& params)
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "{\"params\":[";
|
||||||
|
for (std::size_t i = 0; i < params.size(); ++i) {
|
||||||
|
if (i > 0U) {
|
||||||
|
out << ',';
|
||||||
|
}
|
||||||
|
out << "{\"name\":\"" << params[i].name << "\",\"address\":"
|
||||||
|
<< params[i].address << "}";
|
||||||
|
}
|
||||||
|
out << "]}";
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string serializeDspParamErrorJson(const char* reason)
|
||||||
|
{
|
||||||
|
return std::string("{\"status\":\"error\",\"reason\":\"") + reason
|
||||||
|
+ "\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace core
|
||||||
@@ -24,6 +24,7 @@ add_library(digiradio_core STATIC
|
|||||||
"${CORE_SRC_DIR}/WifiScanJson.cpp"
|
"${CORE_SRC_DIR}/WifiScanJson.cpp"
|
||||||
"${CORE_SRC_DIR}/EmbeddedBlobReader.cpp"
|
"${CORE_SRC_DIR}/EmbeddedBlobReader.cpp"
|
||||||
"${CORE_SRC_DIR}/TunerJson.cpp"
|
"${CORE_SRC_DIR}/TunerJson.cpp"
|
||||||
|
"${CORE_SRC_DIR}/DspParamJson.cpp"
|
||||||
"${CORE_SRC_DIR}/FrequencyKHz.cpp"
|
"${CORE_SRC_DIR}/FrequencyKHz.cpp"
|
||||||
"${CORE_SRC_DIR}/GainDb.cpp"
|
"${CORE_SRC_DIR}/GainDb.cpp"
|
||||||
"${CORE_SRC_DIR}/FrequencyHz.cpp"
|
"${CORE_SRC_DIR}/FrequencyHz.cpp"
|
||||||
@@ -89,6 +90,10 @@ add_executable(tuner_json_test tuner_json_test.cpp)
|
|||||||
target_link_libraries(tuner_json_test PRIVATE digiradio_core)
|
target_link_libraries(tuner_json_test PRIVATE digiradio_core)
|
||||||
add_test(NAME tuner_json_test COMMAND tuner_json_test)
|
add_test(NAME tuner_json_test COMMAND tuner_json_test)
|
||||||
|
|
||||||
|
add_executable(dsp_param_json_test dsp_param_json_test.cpp)
|
||||||
|
target_link_libraries(dsp_param_json_test PRIVATE digiradio_core)
|
||||||
|
add_test(NAME dsp_param_json_test COMMAND dsp_param_json_test)
|
||||||
|
|
||||||
add_executable(frequency_khz_test frequency_khz_test.cpp)
|
add_executable(frequency_khz_test frequency_khz_test.cpp)
|
||||||
target_link_libraries(frequency_khz_test PRIVATE digiradio_core)
|
target_link_libraries(frequency_khz_test PRIVATE digiradio_core)
|
||||||
add_test(NAME frequency_khz_test COMMAND frequency_khz_test)
|
add_test(NAME frequency_khz_test COMMAND frequency_khz_test)
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
/**
|
||||||
|
* @file dsp_param_json_test.cpp
|
||||||
|
* @brief Host tests for DspParamJson parse/serialise.
|
||||||
|
*
|
||||||
|
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||||
|
*
|
||||||
|
* Copyright 2026 Michele Bigi
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/DspParamJson.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool expectEqual(const std::string& actual, const std::string& expected)
|
||||||
|
{
|
||||||
|
if (actual != expected) {
|
||||||
|
std::cerr << "expected: " << expected << "\n actual: " << actual
|
||||||
|
<< "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int runParseWriteTest()
|
||||||
|
{
|
||||||
|
const auto parsed = core::parseDspParamWriteJson(
|
||||||
|
R"({"name":"BEEP1_KICK","value":1.0})");
|
||||||
|
if (!parsed) {
|
||||||
|
std::cerr << "expected parse success\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (parsed->name != "BEEP1_KICK" || parsed->value != 1.0F) {
|
||||||
|
std::cerr << "unexpected parsed fields\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int runParseNegativeValueTest()
|
||||||
|
{
|
||||||
|
const auto parsed = core::parseDspParamWriteJson(
|
||||||
|
R"({"name":"LIMITER1_THRESHOLD","value":-0.5})");
|
||||||
|
if (!parsed || parsed->value != -0.5F) {
|
||||||
|
std::cerr << "expected negative value to parse\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int runParseMissingFieldTest()
|
||||||
|
{
|
||||||
|
const auto parsed = core::parseDspParamWriteJson(R"({"name":"X"})");
|
||||||
|
if (parsed) {
|
||||||
|
std::cerr << "expected missing value to fail\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int runParseInvalidJsonTest()
|
||||||
|
{
|
||||||
|
const auto parsed = core::parseDspParamWriteJson("not json");
|
||||||
|
if (parsed) {
|
||||||
|
std::cerr << "expected invalid json to fail\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int runSerialiseListTest()
|
||||||
|
{
|
||||||
|
const std::vector<core::DspParamInfo> params = {
|
||||||
|
{"BEEP1_ENABLE", 0},
|
||||||
|
{"BEEP1_KICK", 1},
|
||||||
|
};
|
||||||
|
const std::string json = core::serializeDspParamListJson(params);
|
||||||
|
if (!expectEqual(
|
||||||
|
json,
|
||||||
|
R"({"params":[{"name":"BEEP1_ENABLE","address":0},{"name":"BEEP1_KICK","address":1}]})")) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string emptyJson = core::serializeDspParamListJson({});
|
||||||
|
if (!expectEqual(emptyJson, R"({"params":[]})")) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int runSerialiseErrorTest()
|
||||||
|
{
|
||||||
|
const std::string json = core::serializeDspParamErrorJson("not_found");
|
||||||
|
if (!expectEqual(json, R"({"status":"error","reason":"not_found"})")) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
if (runParseWriteTest() != EXIT_SUCCESS) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (runParseNegativeValueTest() != EXIT_SUCCESS) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (runParseMissingFieldTest() != EXIT_SUCCESS) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (runParseInvalidJsonTest() != EXIT_SUCCESS) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (runSerialiseListTest() != EXIT_SUCCESS) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (runSerialiseErrorTest() != EXIT_SUCCESS) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -75,6 +75,12 @@ public:
|
|||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<void, core::DspError> writeRawParam(
|
||||||
|
unsigned, float) override
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class TrackingTuner final : public core::ITuner {
|
class TrackingTuner final : public core::ITuner {
|
||||||
|
|||||||
@@ -234,6 +234,28 @@ public:
|
|||||||
[[nodiscard]] std::expected<void, Adau1701Error> setBeepEnabled(
|
[[nodiscard]] std::expected<void, Adau1701Error> setBeepEnabled(
|
||||||
bool enabled);
|
bool enabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief writeRawParam — safeload any named Parameter RAM cell.
|
||||||
|
*
|
||||||
|
* @dname writeRawParam
|
||||||
|
* @param address Parameter RAM address (see Adau1701ParamTable.hpp).
|
||||||
|
* @param value Coefficient in the SigmaStudio floating convention
|
||||||
|
* (e.g. -1.0..1.0 for volume/coefficient cells);
|
||||||
|
* converted to ADAU 8.23 fixpoint before the write.
|
||||||
|
* @return Ok on success, or Adau1701Error.
|
||||||
|
* @pubstate writes parameter RAM via safeload. No domain validation —
|
||||||
|
* this is deliberately as permissive as SigmaStudio's own
|
||||||
|
* Remote Connection (see SigmaStudioTcpServer), for callers
|
||||||
|
* that need cells the curated applyMixer/applyEq/setEqBand
|
||||||
|
* API doesn't expose (Beep1 frequency, limiter thresholds,
|
||||||
|
* raw PEQ coefficients, etc.).
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::expected<void, Adau1701Error> writeRawParam(
|
||||||
|
unsigned address, float value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] std::expected<void, Adau1701Error> ensureBooted() const;
|
[[nodiscard]] std::expected<void, Adau1701Error> ensureBooted() const;
|
||||||
[[nodiscard]] std::expected<void, Adau1701Error> safeloadGain(
|
[[nodiscard]] std::expected<void, Adau1701Error> safeloadGain(
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ public:
|
|||||||
[[nodiscard]] std::expected<void, core::DspError> setBeepEnabled(
|
[[nodiscard]] std::expected<void, core::DspError> setBeepEnabled(
|
||||||
bool enabled) override;
|
bool enabled) override;
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<void, core::DspError> writeRawParam(
|
||||||
|
unsigned address, float value) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] static core::DspError mapError(Adau1701Error error) noexcept;
|
[[nodiscard]] static core::DspError mapError(Adau1701Error error) noexcept;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
/**
|
||||||
|
* @file Adau1701ParamTable.hpp
|
||||||
|
* @brief Name -> parameter RAM address table for the whole DSP program.
|
||||||
|
*
|
||||||
|
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||||
|
*
|
||||||
|
* Copyright 2026 Michele Bigi
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Generated from Firmware/ADAU1701-Firmware/DigiRadio_IC_1_PARAM.h (the
|
||||||
|
* SigmaStudio export). Regenerate by re-running the extraction over that
|
||||||
|
* file if the SigmaStudio project's cell list changes; entries here must
|
||||||
|
* stay in sync with the ADDR_* constants used by
|
||||||
|
* components/drivers/adau1701/src/Adau1701Driver.cpp's replayProgram().
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <optional>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace adau1701 {
|
||||||
|
|
||||||
|
/** One addressable Parameter RAM cell from the compiled SigmaStudio graph. */
|
||||||
|
struct Adau1701ParamEntry {
|
||||||
|
std::string_view name; ///< Cell name as exported by SigmaStudio.
|
||||||
|
unsigned address; ///< Parameter RAM address for safeload writes.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief kAdau1701ParamTable — every named Parameter RAM cell.
|
||||||
|
*
|
||||||
|
* @dname kAdau1701ParamTable
|
||||||
|
* @return n/a (data)
|
||||||
|
* @pubstate Read-only; see file header for regeneration instructions.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
inline constexpr std::array<Adau1701ParamEntry, 74> kAdau1701ParamTable = {{
|
||||||
|
{"BEEP1_ENABLE", 0},
|
||||||
|
{"BEEP1_KICK", 1},
|
||||||
|
{"BEEP1_BEEP_FREQ", 2},
|
||||||
|
{"SI4674", 3},
|
||||||
|
{"SI4674_1", 4},
|
||||||
|
{"ESP32", 5},
|
||||||
|
{"ESP32_1", 6},
|
||||||
|
{"SINGLE1", 7},
|
||||||
|
{"SSPLITTER1", 8},
|
||||||
|
{"STMIXER1_ST0_VOLUME", 9},
|
||||||
|
{"STMIXER1_ST1_VOLUME", 10},
|
||||||
|
{"STMIXER1_ST2_VOLUME", 11},
|
||||||
|
{"PARAMEQ1_ST0_B0", 12},
|
||||||
|
{"PARAMEQ1_ST0_B1", 13},
|
||||||
|
{"PARAMEQ1_ST0_B2", 14},
|
||||||
|
{"PARAMEQ1_ST0_A0", 15},
|
||||||
|
{"PARAMEQ1_ST0_A1", 16},
|
||||||
|
{"PARAMEQ1_ST1_B0", 17},
|
||||||
|
{"PARAMEQ1_ST1_B1", 18},
|
||||||
|
{"PARAMEQ1_ST1_B2", 19},
|
||||||
|
{"PARAMEQ1_ST1_A0", 20},
|
||||||
|
{"PARAMEQ1_ST1_A1", 21},
|
||||||
|
{"PARAMEQ1_ST2_B0", 22},
|
||||||
|
{"PARAMEQ1_ST2_B1", 23},
|
||||||
|
{"PARAMEQ1_ST2_B2", 24},
|
||||||
|
{"PARAMEQ1_ST2_A0", 25},
|
||||||
|
{"PARAMEQ1_ST2_A1", 26},
|
||||||
|
{"PARAMEQ1_ST3_B0", 27},
|
||||||
|
{"PARAMEQ1_ST3_B1", 28},
|
||||||
|
{"PARAMEQ1_ST3_B2", 29},
|
||||||
|
{"PARAMEQ1_ST3_A0", 30},
|
||||||
|
{"PARAMEQ1_ST3_A1", 31},
|
||||||
|
{"PARAMEQ1_ST4_B0", 32},
|
||||||
|
{"PARAMEQ1_ST4_B1", 33},
|
||||||
|
{"PARAMEQ1_ST4_B2", 34},
|
||||||
|
{"PARAMEQ1_ST4_A0", 35},
|
||||||
|
{"PARAMEQ1_ST4_A1", 36},
|
||||||
|
{"PARAMEQ1_ST5_B0", 37},
|
||||||
|
{"PARAMEQ1_ST5_B1", 38},
|
||||||
|
{"PARAMEQ1_ST5_B2", 39},
|
||||||
|
{"PARAMEQ1_ST5_A0", 40},
|
||||||
|
{"PARAMEQ1_ST5_A1", 41},
|
||||||
|
{"MULTIPLE1", 42},
|
||||||
|
{"MULTIPLE1_1", 43},
|
||||||
|
{"LIMITER1_S1", 44},
|
||||||
|
{"LIMITER1_INT1", 45},
|
||||||
|
{"LIMITER1_S2", 46},
|
||||||
|
{"LIMITER1_INT2", 47},
|
||||||
|
{"LIMITER1_S3", 48},
|
||||||
|
{"LIMITER1_INT3", 49},
|
||||||
|
{"LIMITER1_S4", 50},
|
||||||
|
{"LIMITER1_INT4", 51},
|
||||||
|
{"LIMITER1_C1", 52},
|
||||||
|
{"LIMITER1_C2", 53},
|
||||||
|
{"LIMITER1_C3", 54},
|
||||||
|
{"LIMITER1_THRESHOLD", 55},
|
||||||
|
{"LIMITER1_RMS", 56},
|
||||||
|
{"LIMITER1_DECAY", 57},
|
||||||
|
{"LIMITER1_DECAYCOMPLEMENT", 58},
|
||||||
|
{"LIMITER2_S1", 59},
|
||||||
|
{"LIMITER2_INT1", 60},
|
||||||
|
{"LIMITER2_S2", 61},
|
||||||
|
{"LIMITER2_INT2", 62},
|
||||||
|
{"LIMITER2_S3", 63},
|
||||||
|
{"LIMITER2_INT3", 64},
|
||||||
|
{"LIMITER2_S4", 65},
|
||||||
|
{"LIMITER2_INT4", 66},
|
||||||
|
{"LIMITER2_C1", 67},
|
||||||
|
{"LIMITER2_C2", 68},
|
||||||
|
{"LIMITER2_C3", 69},
|
||||||
|
{"LIMITER2_THRESHOLD", 70},
|
||||||
|
{"LIMITER2_RMS", 71},
|
||||||
|
{"LIMITER2_DECAY", 72},
|
||||||
|
{"LIMITER2_DECAYCOMPLEMENT", 73},
|
||||||
|
}};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief findAdau1701ParamAddress — look up a cell's RAM address by name.
|
||||||
|
*
|
||||||
|
* @dname findAdau1701ParamAddress
|
||||||
|
* @param name Cell name, case-sensitive, matching kAdau1701ParamTable.
|
||||||
|
* @return The address if found, or std::nullopt.
|
||||||
|
* @pubstate none
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline std::optional<unsigned> findAdau1701ParamAddress(
|
||||||
|
std::string_view name) noexcept
|
||||||
|
{
|
||||||
|
for (const auto& entry : kAdau1701ParamTable) {
|
||||||
|
if (entry.name == name) {
|
||||||
|
return entry.address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace adau1701
|
||||||
@@ -345,6 +345,16 @@ namespace adau1701
|
|||||||
return safeloadFixpoint(static_cast<unsigned>(ADDR_BEEP1_KICK), value);
|
return safeloadFixpoint(static_cast<unsigned>(ADDR_BEEP1_KICK), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::expected<void, Adau1701Error> Adau1701Driver::writeRawParam(
|
||||||
|
unsigned address, float value)
|
||||||
|
{
|
||||||
|
if (auto ready = ensureBooted(); !ready)
|
||||||
|
{
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
return safeloadFixpoint(address, core::floatToFixpoint823(value));
|
||||||
|
}
|
||||||
|
|
||||||
std::expected<void, Adau1701Error> Adau1701Driver::applyEq(
|
std::expected<void, Adau1701Error> Adau1701Driver::applyEq(
|
||||||
const core::EqProfile &eq)
|
const core::EqProfile &eq)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -97,4 +97,13 @@ std::expected<void, core::DspError> Adau1701Dsp::setBeepEnabled(bool enabled)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::expected<void, core::DspError> Adau1701Dsp::writeRawParam(
|
||||||
|
unsigned address, float value)
|
||||||
|
{
|
||||||
|
if (auto result = driver_.writeRawParam(address, value); !result) {
|
||||||
|
return std::unexpected(mapError(result.error()));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace adau1701
|
} // namespace adau1701
|
||||||
|
|||||||
@@ -208,6 +208,22 @@ public:
|
|||||||
[[nodiscard]] std::expected<void, core::DspError> setBeepEnabled(
|
[[nodiscard]] std::expected<void, core::DspError> setBeepEnabled(
|
||||||
bool enabled);
|
bool enabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief writeRawParam — safeload any named ADAU1701 Parameter RAM cell.
|
||||||
|
*
|
||||||
|
* @dname writeRawParam
|
||||||
|
* @param address Parameter RAM address.
|
||||||
|
* @param value Coefficient in the SigmaStudio floating convention.
|
||||||
|
* @return Ok on success, or DspError.
|
||||||
|
* @pubstate live-only: not part of AudioProfile, never persisted, does
|
||||||
|
* not touch profile_. No domain validation.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-18
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::expected<void, core::DspError> writeRawParam(
|
||||||
|
unsigned address, float value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] std::expected<void, core::StoreError> persistProfile() const;
|
[[nodiscard]] std::expected<void, core::StoreError> persistProfile() const;
|
||||||
|
|
||||||
|
|||||||
@@ -193,4 +193,10 @@ std::expected<void, core::DspError> AudioService::setBeepEnabled(bool enabled)
|
|||||||
return dsp_.setBeepEnabled(enabled);
|
return dsp_.setBeepEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::expected<void, core::DspError> AudioService::writeRawParam(
|
||||||
|
unsigned address, float value)
|
||||||
|
{
|
||||||
|
return dsp_.writeRawParam(address, value);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace audio
|
} // namespace audio
|
||||||
|
|||||||
Reference in New Issue
Block a user