A state-by-event read of audioecho.FeedbackDelay and audiodelays.MultiTapDelay at v0.6.3rc3, against every test that exercises them. Items marked measured were run on the CPython twin. The rest come from reading the code and are not reproduced. Nothing was run on a native build.
Could hang
- MultiTapDelay: a source that reports more data and hands back 0 bytes spins the pull loop with no exit (
MultiTapDelay.c:345-364; the twin's _Effect does the same, audiofilters.py:76-101). No test.
Audio lost or repeated
- FeedbackDelay:
reset_buffer drops the source frames the node holds. With 512-frame source buffers, 256 frames are lost (measured: the frame before the reset was 255, the first after was 512); a RawSample restarts from its top (FeedbackDelay.c:254-255). clear() keeps them.
- FeedbackDelay: a source that runs dry part-way through a block hands a SHORT block downstream (measured: 256, 44, 256 frames). The comment at
FeedbackDelay.c:235 says silence rather than a short block, which holds only when nothing was produced.
- FeedbackDelay ignores
GET_BUFFER_DONE, so a source that is done but still hands data is replayed for ever (measured: a 300-frame RawSample came out non-zero in 4 of 4 blocks). The node's own RawSample test fixtures depend on this.
A control taken to its stop leaves state behind
- FeedbackDelay
wow_hz to 0 freezes the oscillator mid-swing: the read head stays displaced (measured: a 20 ms echo at frame 148, not 160).
- FeedbackDelay
loop_semitones off and on resumes from a stale phase, with a half-window step.
- Not tested at all:
feedback 0 and back, delay_slew 0 mid-glide, Mix back from 0, two or three set() calls before one pull.
The C node and the CPython twin disagree
- MultiTapDelay
delay_ms: C zeroes the tail past the new length on every write (MultiTapDelay.c:134); the twin does not (audiodelays.py:188-195). Shorten then lengthen reads stale audio on CPython and silence on the boards.
- MultiTapDelay after
deinit(): C still reports its source from sources(); the twin clears it.
- MultiTapDelay with a stereo source that hands an odd number of samples swaps the lanes (
audiodsp_multitap.c:17).
Unlocked writes
The pattern
MultiTapDelay has no node-level lifecycle test at all: everything is three-interpreter identity against a golden, which pins what the node does and asserts nothing about whether it is right. FeedbackDelay is tested where a class audit found a defect and nowhere else.
Wanted: one fixed matrix of lifecycle events, run against every node, that asserts properties (no frame lost, no frame repeated, silence in gives silence out, a control back from its stop plays what a fresh node plays).
A state-by-event read of
audioecho.FeedbackDelayandaudiodelays.MultiTapDelayat v0.6.3rc3, against every test that exercises them. Items marked measured were run on the CPython twin. The rest come from reading the code and are not reproduced. Nothing was run on a native build.Could hang
MultiTapDelay.c:345-364; the twin's_Effectdoes the same,audiofilters.py:76-101). No test.Audio lost or repeated
reset_bufferdrops the source frames the node holds. With 512-frame source buffers, 256 frames are lost (measured: the frame before the reset was 255, the first after was 512); aRawSamplerestarts from its top (FeedbackDelay.c:254-255).clear()keeps them.FeedbackDelay.c:235says silence rather than a short block, which holds only when nothing was produced.GET_BUFFER_DONE, so a source that is done but still hands data is replayed for ever (measured: a 300-frameRawSamplecame out non-zero in 4 of 4 blocks). The node's ownRawSampletest fixtures depend on this.A control taken to its stop leaves state behind
wow_hzto 0 freezes the oscillator mid-swing: the read head stays displaced (measured: a 20 ms echo at frame 148, not 160).loop_semitonesoff and on resumes from a stale phase, with a half-window step.feedback0 and back,delay_slew0 mid-glide, Mix back from 0, two or threeset()calls before one pull.The C node and the CPython twin disagree
delay_ms: C zeroes the tail past the new length on every write (MultiTapDelay.c:134); the twin does not (audiodelays.py:188-195). Shorten then lengthen reads stale audio on CPython and silence on the boards.deinit(): C still reports its source fromsources(); the twin clears it.audiodsp_multitap.c:17).Unlocked writes
FeedbackDelay.set()takes no pump lock (FeedbackDelay.c:172-178), andMultiTapDelayreallocates its tap tables with no lock while a pull reads them (MultiTapDelay.c:234-246). On a board with the pump on its own thread, a write can tear. Same family as audioconvolve.Convolver: synthesize() and load() write the impulse without the pump lock, and load() still resets mid-stream #166.The pattern
MultiTapDelay has no node-level lifecycle test at all: everything is three-interpreter identity against a golden, which pins what the node does and asserts nothing about whether it is right. FeedbackDelay is tested where a class audit found a defect and nowhere else.
Wanted: one fixed matrix of lifecycle events, run against every node, that asserts properties (no frame lost, no frame repeated, silence in gives silence out, a control back from its stop plays what a fresh node plays).