What's wrong
DelayLine.Process(float input, int delaySamples) (Containers/DelayLine.cs ~line 152) is implemented as:
Write(input);
return Read(delaySamples);
The range check on delaySamples (ThrowIfNegative / ThrowIfGreaterThan(delaySamples, Capacity)) lives in Read, so it only runs after Write has stored the sample and advanced the write cursor.
Repro (verified by running against the current main)
var line = new DelayLine(4);
try { line.Process(99f, 5); } catch (ArgumentOutOfRangeException) { }
Console.WriteLine(line.Read(0)); // expected 0 (the call was rejected), actual 99
Why it matters
The call throws, so the caller treats it as rejected, but the delay line's state has still changed. The write cursor has moved and a sample the caller thinks was discarded is now in the buffer. Every later read is off by one sample relative to the caller's own sample count. In an audio callback that catches and carries on, this puts a click into the output and shifts the timing of every tap.
Suggested fix
Validate before mutating. Either:
- add
ArgumentOutOfRangeException.ThrowIfNegative(delaySamples); ArgumentOutOfRangeException.ThrowIfGreaterThan(delaySamples, Capacity); at the top of Process, or
- move the check into a small shared helper that both
Read and Process call before doing anything.
Acceptance criteria
Process with a negative delay, or a delay above Capacity, throws ArgumentOutOfRangeException and leaves the buffer contents and write position unchanged.
- A test that covers the repro above.
What's wrong
DelayLine.Process(float input, int delaySamples)(Containers/DelayLine.cs~line 152) is implemented as:The range check on
delaySamples(ThrowIfNegative/ThrowIfGreaterThan(delaySamples, Capacity)) lives inRead, so it only runs afterWritehas stored the sample and advanced the write cursor.Repro (verified by running against the current main)
Why it matters
The call throws, so the caller treats it as rejected, but the delay line's state has still changed. The write cursor has moved and a sample the caller thinks was discarded is now in the buffer. Every later read is off by one sample relative to the caller's own sample count. In an audio callback that catches and carries on, this puts a click into the output and shifts the timing of every tap.
Suggested fix
Validate before mutating. Either:
ArgumentOutOfRangeException.ThrowIfNegative(delaySamples); ArgumentOutOfRangeException.ThrowIfGreaterThan(delaySamples, Capacity);at the top ofProcess, orReadandProcesscall before doing anything.Acceptance criteria
Processwith a negative delay, or a delay aboveCapacity, throwsArgumentOutOfRangeExceptionand leaves the buffer contents and write position unchanged.