0
mirror of https://github.com/torvalds/GuitarPedal.git synced 2026-08-14 12:44:08 +00:00
Files
Linus Torvalds 52032bfe45 Validation: run the pedal's audio core on the host
The effects have never been measured.  The plumbing around them has -
boot, link gain, ring latency, the MIDI parser - but nothing has ever
asked an effect what it does to a signal, and the only DSP ever executed
off-target is the FFT in test-fft.c.

bench/ closes that.  It builds audio/effect.h, effects/*.h and
single_sample() for the host and replaces the only two things the audio
core touches - the DMA read and write pointers, and the free-running
timer - with ordinary memory.  So what runs is the signal path rather
than a model of it, which matters more than it sounds: a harness that
re-implemented the middle of single_sample() would be a second opinion
about what the pedal does, and a second opinion is exactly what you
cannot check an effect against.

It needed no change to anything under Software/.  That is worth
recording as a fact about the architecture: apart from __not_in_flash()
in reverb.h, the effect headers have no hardware in them at all.

Measured on a transparent chain, the bench's own floor is a gain error
of -0.000265 dB and noise 144.7 dB down.  The gain error is the
firmware's, not the bench's - it is eps(1.0)/2 * 512, where a 1/512 slew
stalls against float32 rounding.

bench.py drives it.  The stimulus is 440 Hz because 48000/440 is 1200/11
exactly, so 12000 samples hold 110 whole cycles and every harmonic lands
on a bin with no leakage - and because 48000/440 is *not* an integer,
which is what lets aliased harmonics fall off the harmonic grid where
they can be told from honest distortion.  A frequency that divides the
sample rate hides its own aliasing inside its harmonic series.

--map asks a single function out of audio/util.h directly, with no audio
path around it, so the fast approximations can be characterised against
double precision instead of being inferred through an effect that is
already distorting.

Three targets:

  check-effects   the two controls that say whether the instrument
                  works - a transparent chain has to come out
                  transparent, and boost's fold(), which is sharp on
                  purpose, has to read as sharp.  Host only.

  check-bench     whether the bench agrees with a real pedal.  Test
                  tone into boost's wavefolder, captured over USB, so
                  the path is digital end to end and a disagreement is
                  a real one.  Every harmonic agrees to 0.00 dB and the
                  aliasing to 0.04 dB.

  check-analog    the other half: a patch cable from the output back to
                  the input, one board and one codec so there is no
                  second clock to chase.  The converter pair is a gain
                  constant to 0.001 dB over 58 dB of level, 1.08 ms of
                  delay, and a residual 84.9 dB down once those two are
                  removed.

All three skip rather than fail when what they need is not plugged in.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2026-08-10 08:44:48 -07:00

45 lines
1.3 KiB
C

//
// A DMA controller made of ordinary memory.
//
// audio/effect.h reads exactly two fields - the read address of the
// transmit channel and the write address of the receive channel - and
// turns each into a pointer into i2s_dma_buf[]. That is the whole of
// the audio core's contact with the hardware, so a struct with those two
// fields in it is a complete substitute, and single_sample() runs
// unmodified against it.
//
// The bench moves these two "registers" between samples the way the DMA
// engine would: rx one slot ahead of the cpu, so the spin exits at once,
// and tx well behind it, so the deadline check stays false. See
// bench.c.
//
// uintptr_t rather than the SDK's uint32_t. The firmware stores a
// 32-bit address in a 32-bit register; here the addresses are the host's
// and a uint32_t would truncate every one of them. The '& ~7' in
// effect.h still does the right thing, because ~7 widens to all-ones
// with the low three bits clear.
//
#ifndef _BENCH_HARDWARE_DMA_H
#define _BENCH_HARDWARE_DMA_H
#include <stdint.h>
#define BENCH_DMA_CHANNELS 16
struct bench_dma_channel {
uintptr_t read_addr;
uintptr_t write_addr;
uint32_t transfer_count;
uint32_t ctrl_trig;
};
struct bench_dma {
struct bench_dma_channel ch[BENCH_DMA_CHANNELS];
};
extern struct bench_dma bench_dma_regs;
#define dma_hw (&bench_dma_regs)
#endif