Skip to content

[FIX] zero-mean colored noise with random phase in every channel - #54

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

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

Conversation

@aguirreSL

@aguirreSL aguirreSL commented Oct 31, 2026 •

Copy link
Copy Markdown

This PR builds on #52. Its first two commits are those of #52, which colored_noise needs to run with SciPy 1.13 or later, and the change proposed here is the last commit.

_powerlaw_noise, which generates every color of generate.colored_noise, had two bugs.

  1. The DC bin received the largest amplitude of the spectrum, since the power law was evaluated at 1/nsamples Hz for it. The noise came out with an offset of about 7 times its rms. With the silent margins of colored_noise, the offset becomes a rectangular pulse whose leakage tilts the spectrum: pink noise measured -3.96 dB per octave, where -3.01 is expected. The DC bin is now set to zero.
  2. imag[-1] = 0 was meant for the Nyquist bin. The array is channels by frequencies, so the line zeroed the imaginary part of the whole last channel, and that channel came out as its own mirror image in time. With a single channel, every noise was affected. The line is now imag[:, -1] = 0.

Measured with a fixed seed:

Before After
Pink slope, 0.1 s margins -3.96 dB/octave -2.98 dB/octave
DC offset / rms 7.2 0.02
corr(x[n], x[N-n]) in the last channel 1.00 -0.09

The tests in tests/test_colored_noise.py check the slope of the five colors, from purple to red, within 0.2 dB per octave of the power law, the absence of DC offset, and a random phase in every channel.

This also answers #12, since pink noise is available as pytta.generate.colored_noise('pink').

Checked with Python 3.12, numpy 1.26 and scipy 1.17. The 3 failures left in test_pyttasave.py are those that remain after #52.

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>
_powerlaw_noise gave the DC bin the largest amplitude of the spectrum
(frequency 1/nsamples in the power law), so the noise came out as mostly
offset: 7 times its rms. With the silent margins of colored_noise the
offset is a rectangular pulse whose 1/f**2 leakage tilts the spectrum:
pink measured -3.96 dB per octave instead of -3.01. The DC bin is now 0.

imag[-1] = 0 was meant for the Nyquist bin but zeroed the last channel,
whose spectrum became real and its waveform its own mirror image in
time; with one channel every noise did. It is now imag[:, -1].

Tests pin the power law of the five colors, the absence of offset and a
random phase in every channel.

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