Skip to content

[FIX] peak_time with a negative peak, and regularization of short signals - #131

Open
aguirreSL wants to merge 3 commits into
PyTTaMaster:developmentfrom
aguirreSL:dev-fix_peak_crossfade
Open

aguirreSL wants to merge 3 commits into
PyTTaMaster:developmentfrom
aguirreSL:dev-fix_peak_crossfade

Conversation

@aguirreSL

Copy link
Copy Markdown

Summary

Two crashes that a smoke test of the exported functions found on development, each with a test that fails without the fix:

  1. peak_time raised IndexError whenever the largest magnitude of a channel is a negative sample, which is common in impulse responses.
  2. The regularized deconvolution of ImpulsiveResponse raised ValueError: Window length M must be a non-negative integer for short signals.

peak_time

The function looked for the sample equal to the absolute value of the peak, np.where(x == abs(max|x|)), which finds nothing when that peak is negative. It now takes np.argmax(np.abs(x)), the sample of the largest magnitude whatever its sign.

Short signals in the regularization

_calculate_regu_spk crossfades the regularization from freqMin / √2 to freqMin with a Hann window over the bins between them. For a short signal those two frequencies can fall in neighbouring bins, or in the same one: 4096 samples at 48 kHz give bins of 11.7 Hz, and the default freqMin of 17.8 Hz leaves 5 Hz for the crossfade. The window length f1idx - f0idx was then negative. A crossfade narrower than one bin now becomes a switch at the first bin from the lower frequency, which is what the window tends to as its length goes to zero. Signals long enough to resolve the crossfade are not affected.

Tests

tests/test_peak_and_short_regularization.py:

  1. Two channels with a negative peak at 0.3 s and a positive one at 0.7 s give [0.3, 0.7].
  2. White noise of 4096 samples at 48 kHz through a known decaying response, deconvolved with the default regularization, gives that response back between 100 Hz and 10 kHz within 4.1e-5, which the float32 samples allow.

This pull request builds on #52, whose fix of scipy.signal.windows.hann the regularization needs, and shows its commit until #52 is merged. On top of #52 the 3 failures in test_pyttasave.py that #60 fixes are the same with and without this change. Checked with Python 3.12, numpy 1.26 and scipy 1.17.

Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com

aguirreSL and others added 3 commits October 31, 2026 19:44
The name given to generated objects was read by a block copied in 20
places that walked the stack with traceback.walk_stack(None). Since
Python 3.12 it starts four frames up instead of two, so in a plain
script it walks past the end of the stack and pytta.generate.sweep()
raises AttributeError at module level and UnboundLocalError inside a
function. One helper, _instanceinfo.caller_name, replaces the copies: it
walks from the caller explicitly and returns an empty name when the
source line is not available (python -c).

Tests run real scripts in a subprocess, since under pytest the stack is
deep enough to hide the failure.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
scipy.signal.hann and scipy.signal.tukey were removed in SciPy 1.13. The
five calls now use scipy.signal.windows, the same functions with the
same defaults.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nals

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant