diff --git a/Software/components/core/src/EqProfile.cpp b/Software/components/core/src/EqProfile.cpp index dc4ad69..1328e5b 100644 --- a/Software/components/core/src/EqProfile.cpp +++ b/Software/components/core/src/EqProfile.cpp @@ -18,24 +18,27 @@ namespace core { namespace { constexpr std::array kDefaultCenters{ - 40U, 100U, 250U, 1000U, 4000U, 12000U}; + 20U, 100U, 400U, 1000U, 3000U, 8000U}; + +constexpr std::array kDefaultQ{ + 1.414F, 1.0F, 1.0F, 1.0F, 1.0F, 1.0F}; [[nodiscard]] std::array makeDefaultBands() noexcept { return std::array{ EqBandSettings{GainDb::zero(), *FrequencyHz::tryFromHz(kDefaultCenters[0]), - 1.414F}, + kDefaultQ[0]}, EqBandSettings{GainDb::zero(), *FrequencyHz::tryFromHz(kDefaultCenters[1]), - 1.414F}, + kDefaultQ[1]}, EqBandSettings{GainDb::zero(), *FrequencyHz::tryFromHz(kDefaultCenters[2]), - 1.414F}, + kDefaultQ[2]}, EqBandSettings{GainDb::zero(), *FrequencyHz::tryFromHz(kDefaultCenters[3]), - 1.414F}, + kDefaultQ[3]}, EqBandSettings{GainDb::zero(), *FrequencyHz::tryFromHz(kDefaultCenters[4]), - 1.414F}, + kDefaultQ[4]}, EqBandSettings{GainDb::zero(), *FrequencyHz::tryFromHz(kDefaultCenters[5]), - 1.414F}, + kDefaultQ[5]}, }; } diff --git a/Software/components/drivers/adau1701/include/adau1701/Adau1701Error.hpp b/Software/components/drivers/adau1701/include/adau1701/Adau1701Error.hpp index 4c5da0f..bba4c6c 100644 --- a/Software/components/drivers/adau1701/include/adau1701/Adau1701Error.hpp +++ b/Software/components/drivers/adau1701/include/adau1701/Adau1701Error.hpp @@ -30,6 +30,7 @@ enum class Adau1701Error { DownloadFailed, NotBooted, SafeloadFailed, + InvalidParameter, }; } // namespace adau1701 diff --git a/Software/components/drivers/adau1701/src/Adau1701Driver.cpp b/Software/components/drivers/adau1701/src/Adau1701Driver.cpp index 838745c..2ff7b35 100644 --- a/Software/components/drivers/adau1701/src/Adau1701Driver.cpp +++ b/Software/components/drivers/adau1701/src/Adau1701Driver.cpp @@ -38,6 +38,8 @@ namespace adau1701 { namespace { constexpr char kTag[] = "Adau1701"; constexpr int kI2cPort = 0; +/** Index 0 is the fixed high-pass band (SigmaStudio band 1); not safeloaded. */ +constexpr std::uint8_t kFixedHighPassBandIndex = 0U; } // namespace Adau1701Driver::Adau1701Driver(Adau1701Pins pins) @@ -197,6 +199,10 @@ std::expected Adau1701Driver::applyMixer( std::expected Adau1701Driver::setEqBand( core::EqBandIndex band, core::GainDb gain, core::FrequencyHz center, float q) { + if (band.value() == kFixedHighPassBandIndex) { + return std::unexpected(Adau1701Error::InvalidParameter); + } + if (auto ready = ensureBooted(); !ready) { return ready; } @@ -227,6 +233,9 @@ std::expected Adau1701Driver::applyEq( } for (std::uint8_t i = 0; i < core::EqBandIndex::kBandCount; ++i) { + if (i == kFixedHighPassBandIndex) { + continue; + } const auto index = core::EqBandIndex::tryFromIndex(i); if (!index) { return std::unexpected(Adau1701Error::SafeloadFailed); diff --git a/Software/components/drivers/adau1701/src/Adau1701Dsp.cpp b/Software/components/drivers/adau1701/src/Adau1701Dsp.cpp index 8a29a8d..1a0834b 100644 --- a/Software/components/drivers/adau1701/src/Adau1701Dsp.cpp +++ b/Software/components/drivers/adau1701/src/Adau1701Dsp.cpp @@ -25,6 +25,8 @@ core::DspError Adau1701Dsp::mapError(Adau1701Error error) noexcept switch (error) { case Adau1701Error::NotBooted: return core::DspError::NotBooted; + case Adau1701Error::InvalidParameter: + return core::DspError::InvalidParameter; case Adau1701Error::SafeloadFailed: return core::DspError::SafeloadFailed; default: diff --git a/Software/docs/manual/ch-classes.tex b/Software/docs/manual/ch-classes.tex index ab23ea7..b646137 100644 --- a/Software/docs/manual/ch-classes.tex +++ b/Software/docs/manual/ch-classes.tex @@ -162,9 +162,11 @@ Strong index into the six-band \texttt{Param EQ1} module (0--5). Maps to parameter RAM via \texttt{adau1701::paramAddrEqBandBase()}. \section{EqProfile}\label{cls:EqProfile} -Six-band parametric EQ snapshot with default centre frequencies -(40\,Hz--12\,kHz). Designed in the pure core; coefficients safeloaded by -\texttt{Adau1701Driver::applyEq()}. +Six-band parametric EQ snapshot aligned with the SigmaStudio map +(Chapter~\ref{ch:sigmastudio}): band index~0 is the fixed 20\,Hz +high-pass; bands 1--5 are peaking at 100\,Hz--8\,kHz. Designed in the +pure core; peaking bands are safeloaded by \texttt{Adau1701Driver::applyEq()} +(band~0 is skipped). \section{IDsp}\label{cls:IDsp} Abstract ADAU1701 control boundary. Defines \texttt{applyProfile()}, diff --git a/Software/docs/manual/ch-firmware.tex b/Software/docs/manual/ch-firmware.tex index ac43d3f..2366a09 100644 --- a/Software/docs/manual/ch-firmware.tex +++ b/Software/docs/manual/ch-firmware.tex @@ -134,6 +134,9 @@ The board carries no self-boot EEPROM. Instead, the ESP32 writes the SigmaStudio-exported program into the DSP's RAM at \emph{every} boot. Runtime changes to equalisation and mixing use the ADAU1701 safeload mechanism, so parameter updates are click-free while audio is playing. +Which blocks are runtime-controllable (source faders, EQ bands 2--6, +master volume) versus fixed at export (high-pass, limiters) is listed in +Chapter~\ref{ch:sigmastudio}, Section~\ref{sec:ss-runtime}. \begin{drcaution}[Safeload] Writing DSP parameter cells directly while audio is running produces diff --git a/Software/docs/manual/ch-hardware.tex b/Software/docs/manual/ch-hardware.tex index b8c6f15..e798db0 100644 --- a/Software/docs/manual/ch-hardware.tex +++ b/Software/docs/manual/ch-hardware.tex @@ -95,7 +95,9 @@ the signal reaches the Bluetooth stage. \subsection{Control and program loading} The DSP is controlled over I\textsuperscript{2}C. Its signal-processing program is designed in SigmaStudio and exported as a sequence of register -writes (control settings, program RAM, and parameter RAM). +writes (control settings, program RAM, and parameter RAM). The full +SigmaStudio schematic, signal chain, and runtime parameter map are +documented in Chapter~\ref{ch:sigmastudio}. \begin{drkey}[RAM boot, no EEPROM] The board deliberately omits the ADAU1701 self-boot EEPROM. Instead, the diff --git a/Software/docs/manual/ch-sigmastudio.tex b/Software/docs/manual/ch-sigmastudio.tex new file mode 100644 index 0000000..7775805 --- /dev/null +++ b/Software/docs/manual/ch-sigmastudio.tex @@ -0,0 +1,245 @@ +\chapter{DSP Configuration (SigmaStudio)} +\label{ch:sigmastudio} + +This chapter documents how the ADAU1701 is configured in SigmaStudio: the +hardware settings that must match the board, the audio signal-processing +chain, and the parameters exposed for runtime control by the ESP32. The +resulting SigmaStudio export is what the host writes into the DSP's RAM at +every boot (Section~\ref{sec:hw-adau}). + +\section{Overview} +\label{sec:ss-overview} + +The DSP program is built once in SigmaStudio and exported as a sequence of +register writes. Because the board carries no self-boot EEPROM, the ESP32 +replays that sequence over I\textsuperscript{2}C (address 0x34) on every +power-up. SigmaStudio is therefore used purely to \emph{design and export} +the program, not to drive the chip in operation. + +\begin{drkey}[No hardware required to export] +The whole program can be built and exported offline, without a physical +ADAU1701 or a USBi programmer connected. A USBi block is placed in the +Hardware Configuration only because SigmaStudio requires a communication +channel to compile; it is never used for a real download in this project. +\end{drkey} + +\section{Hardware Configuration} +\label{sec:ss-hwcfg} + +The Hardware Configuration tab must mirror the board exactly. The key +settings are collected in Table~\ref{tab:ss-hwcfg}. + +\begin{table}[htbp] + \centering + \begin{tabular}{@{}ll@{}} + \drhead Setting & Value \\ + \midrule + Control port (I\textsuperscript{2}C address) & 0x34 (ADDR0/ADDR1 = GND) \\ + Self-boot & Disabled (host RAM load) \\ + System sample rate & 48\,kHz \\ + MCLK & 12.288\,MHz active oscillator \\ + Serial Input format & I\textsuperscript{2}S, 24-bit \\ + Serial Output & \textbf{Master Mode} (ADAU generates clocks) \\ + Word length & 24 bits \\ + RAM Modulo / Program Length & 1x (1024 instructions) \\ + \bottomrule + \end{tabular} + \caption{ADAU1701 hardware settings in SigmaStudio.} + \label{tab:ss-hwcfg} +\end{table} + +\subsection{Multipurpose pin (MP) assignment} + +The ADAU1701 multipurpose pins are assigned to the serial audio signals +as wired on the board. Table~\ref{tab:ss-mp} lists the assignment; it +matches the board GPIO map (Section~\ref{sec:hw-gpio}). + +\begin{table}[htbp] + \centering + \begin{tabular}{@{}lll@{}} + \drhead MP pin & Direction / function & Signal \\ + \midrule + MP0 & Input Sdata\_in0 & audio from Si4684 (radio) \\ + MP1 & Input Sdata\_in1 & audio from ESP32 \\ + MP4 & Input Lrclk\_in & LRCLK (returned via board link) \\ + MP5 & Input Bclk\_in & BCLK (returned via board link) \\ + MP6 & Output Sdata\_out0 & audio to FSC-BT1035 \\ + MP10 & Lrclk\_out & LRCLK generated (master) \\ + MP11 & Bclk\_out & BCLK generated (master) \\ + \bottomrule + \end{tabular} + \caption{Multipurpose pin assignment. MP2, MP3, MP7--MP9 are unused + (GPIO input, default).} + \label{tab:ss-mp} +\end{table} + +\begin{drnote}[Master clock routing] +The ADAU1701 is the I\textsuperscript{2}S master: it generates LRCLK and +BCLK on MP10/MP11. On the board these are wired to MP4/MP5, so the same +master clocks time the serial inputs. This shared-clock scheme means the +serial inputs are configured as \emph{clock inputs} even though the ADAU +itself is master --- the clocks originate at MP10/MP11 and return through +the board link. All companion chips (Si4684, ESP32, BT1035) are +I\textsuperscript{2}S slaves. +\end{drnote} + +\section{Signal-processing chain} +\label{sec:ss-chain} + +The audio flows through a fully stereo chain, built in the Schematic tab +(Figure~\ref{fig:ss-chain}). Two stereo sources are level-trimmed, mixed, +equalised, volume-controlled, and peak-limited before reaching the two +digital outputs that feed the Bluetooth module. + +\begin{figure}[htbp] + \centering + \includegraphics[width=\linewidth]{sigma-chain.jpg} + \caption{The complete stereo signal-processing chain in SigmaStudio: + Input \(\rightarrow\) source faders \(\rightarrow\) stereo mixer + \(\rightarrow\) parametric EQ \(\rightarrow\) master volume + \(\rightarrow\) per-channel limiters \(\rightarrow\) outputs.} + \label{fig:ss-chain} +\end{figure} + +\subsection{Block-by-block} + +\begin{table}[htbp] + \centering + \small + \begin{tabular}{@{}lll@{}} + \drhead Stage & Block & Role \\ + \midrule + Input & Input (ch. 2--5) & radio L/R, ESP32 L/R \\ + Source trim & Si4674, ESP32 faders & per-source stereo level \\ + Mix & Stereo Mixer & combine sources, keep L/R \\ + EQ & Parametric EQ (2-ch) & high-pass + 5 peaking bands \\ + Volume & Multiple volume (2-ch) & master volume \\ + Protect & Limiter 1 / Limiter 2 & per-channel peak limiting \\ + Output & Output (DIG0, DIG1) & L/R to FSC-BT1035 \\ + \bottomrule + \end{tabular} + \caption{Signal-processing blocks and their roles. The chain is stereo + end to end; L and R never collapse to mono.} + \label{tab:ss-blocks} +\end{table} + +\subsection{Signal flow} + +\begin{drcode}[Signal flow (stereo)] +Input ch2/3 (radio L/R) -> Si4674 fader --. + |-> Stereo Mixer -> Param EQ +Input ch4/5 (ESP32 L/R) -> ESP32 fader --' + -> Master Volume -> Limiter L / Limiter R -> Output DIG0 / DIG1 -> BT1035 +\end{drcode} + +\section{Equaliser} +\label{sec:ss-eq} + +The parametric EQ is a two-channel (stereo) block; a single set of bands +applies identically to L and R. It is configured with one high-pass and +five peaking bands, all starting flat (0\,dB) so the default response is +neutral and any tone shaping is an explicit choice made at runtime by the +firmware. + +\begin{table}[htbp] + \centering + \begin{tabular}{@{}lllll@{}} + \drhead Band & Type & Frequency & Gain & Q \\ + \midrule + 1 & Butterworth High (high-pass) & 20\,Hz & --- & 1.41 \\ + 2 & Peaking & 100\,Hz & 0\,dB & 1.0 \\ + 3 & Peaking & 400\,Hz & 0\,dB & 1.0 \\ + 4 & Peaking & 1\,kHz & 0\,dB & 1.0 \\ + 5 & Peaking & 3\,kHz & 0\,dB & 1.0 \\ + 6 & Peaking & 8\,kHz & 0\,dB & 1.0 \\ + \bottomrule + \end{tabular} + \caption{Parametric EQ bands. Band~1 removes DC and subsonic content; + bands 2--6 are the tone controls the firmware adjusts.} + \label{tab:ss-eq} +\end{table} + +\begin{drnote}[Double precision for the high-pass] +The high-pass sits at 20\,Hz, very low relative to the 48\,kHz sample +rate, so it uses a double-precision second-order block to avoid +coefficient-quantisation noise in the bass. The peaking bands can use +single precision. +\end{drnote} + +\section{Master volume and limiters} +\label{sec:ss-vol} + +A two-channel volume block provides the master level (0\,dB default). Two +single-channel limiters --- one per channel --- protect the FSC-BT1035 +input from clipping on peaks. Suggested limiter settings are in +Table~\ref{tab:ss-lim}. + +\begin{table}[htbp] + \centering + \begin{tabular}{@{}ll@{}} + \drhead Parameter & Value \\ + \midrule + RMS TC & 50\,dB/s \\ + Decay & 12\,dB/s \\ + Threshold & $-1$\,dB (protective) \\ + \bottomrule + \end{tabular} + \caption{Per-channel limiter settings. The limiter is left fixed (not + runtime-adjustable).} + \label{tab:ss-lim} +\end{table} + +\section{Runtime-controllable parameters} +\label{sec:ss-runtime} + +The firmware controls a subset of the DSP at runtime via safeload writes. +These are the parameter cells whose addresses must be recorded from the +SigmaStudio export (Section~\ref{sec:ss-export}). + +\begin{table}[htbp] + \centering + \begin{tabular}{@{}lll@{}} + \drhead Control & Block & Runtime-adjustable \\ + \midrule + Source levels (2) & Si4674 / ESP32 faders & yes \\ + EQ bands 2--6 & Parametric EQ & yes (gain/freq/Q) \\ + Master volume & Multiple volume & yes \\ + High-pass (band 1) & Parametric EQ & fixed \\ + Limiters & Limiter 1 / 2 & fixed \\ + \bottomrule + \end{tabular} + \caption{Parameters exposed to the firmware. Fixed blocks are set once + in the program and never written at runtime.} + \label{tab:ss-runtime} +\end{table} + +\begin{drcaution}[Use safeload for live changes] +All runtime updates to the volume, source levels, and EQ bands must go +through the ADAU1701 safeload mechanism. Writing parameter cells directly +while audio is playing produces audible clicks. +\end{drcaution} + +\section{Exporting the program} +\label{sec:ss-export} + +With the schematic complete, the program is exported for the firmware: + +\begin{enumerate} + \item Enable \emph{Action \(\rightarrow\) Enable Short Parameter Name + for Export} so the exported cell names are concise. + \item \emph{Action \(\rightarrow\) Export System Files}, and choose an + output folder. +\end{enumerate} + +The export produces the program data (control registers, program RAM, and +parameter RAM) as byte sequences, plus the symbolic parameter addresses. +The firmware's ADAU1701 driver replays the program data over +I\textsuperscript{2}C at boot, and uses the parameter addresses for +safeload updates. + +\begin{drnote}[No download needed] +Since there is no ADAU1701 attached during development, do \emph{not} use +\emph{Link Compile Download} (it would fail trying to reach the chip). +\emph{Export System Files} compiles the schematic and writes the files +directly. +\end{drnote} diff --git a/Software/docs/manual/manual.tex b/Software/docs/manual/manual.tex index 832ae09..6996115 100644 --- a/Software/docs/manual/manual.tex +++ b/Software/docs/manual/manual.tex @@ -44,6 +44,7 @@ % ---------------- Chapters ---------------- \include{ch-intro} \include{ch-hardware} +\include{ch-sigmastudio} \include{ch-firmware} \include{ch-si4684} \include{ch-api} diff --git a/Software/docs/manual/sigma-chain.jpg b/Software/docs/manual/sigma-chain.jpg new file mode 100644 index 0000000..e3437ac Binary files /dev/null and b/Software/docs/manual/sigma-chain.jpg differ