Signal core
Oscillators, filters, noise and WAV I/O in Rust, compiled to WebAssembly and Python.
- Repository
signal-coreon GitHub- Cite
- doi:10.5281/zenodo.24038472 (all versions, via Zenodo)
- Layer
- Layer 0
- Licence
- Apache-2.0
- Status
- released
Claims
| Claim | Level | Statement | Evidence |
|---|---|---|---|
signal:BOX-RESPONSE v1.0.0 | P | Averaging the last L samples of the sinusoid e^{iωn} gives the same sinusoid multiplied by H(ω) = (1/L)·Σ_{k<L} e^{-iωk}, so H is the moving average's frequency response. | |
signal:BOX-DC-GAIN v1.0.0 | P | The moving average has DC gain 1: H(0) = 1 for every length L ≥ 1, so a constant passes unchanged. |
|
signal:BOX-NULLS v1.0.0 | P | For every integer m not divisible by L, H(2πm/L) = 0: a moving average of L samples removes the frequencies m·sr/L completely (the terms are powers of an L-th root of unity ζ ≠ 1, and their geometric sum is 0). |
|
signal:BOX-GAIN-LE-ONE v1.0.0 | P | The moving average never amplifies: |H(ω)| ≤ 1 for every length L and every real ω. |
|
signal:ALIASING v1.0.0 | P | Sampled at t = n/sr (sr ≠ 0), sin(2πft + φ) and sin(2π(f + k·sr)t + φ) give identical samples for all integers n and k. |
|
signal:SAMPLING-THEOREM v1.0.0 | R | A signal that contains no frequencies above B hertz is completely determined by its samples taken 2B times a second, so audio sampled at sr can represent every frequency below sr/2. | |
signal:N-SAMPLES v1.0.0 | T | n_samples(dur, sr) is int(round(dur·sr)) with halves rounded to even, as lifesound.py (Python's round) computes it. | |
signal:OSC-MATCHES-REFERENCE v1.0.0 | T | osc, osc_sweep and additive accumulate phase as lifesound.py does (sample i has phase 2π·(f₀ + … + fᵢ)/sr + phase) and match it within 1e-12. | |
signal:OSC-ALIASING-IN-CODE v1.0.0 | T | osc at f and at f + k·sr (0 ≤ f < 4000 Hz, |k| ≤ 3, sr = 8000) gives the same samples to within 1e-9 over 0.05 s (400 samples). The residue is floating-point rounding in the running phase sum, so it grows with duration and depends on f (whole-hertz frequencies, whose running sum is exact, show almost none): for the tested pairs (f, k) = (440 Hz, 1), (1234.567 Hz, 3), (3999.9 Hz, 3) and (3999.9 Hz, −3) it stays below 1e-7 over 1 s and below 1e-5 over 10 s (1.0e-8 and 2.4e-6 at f = 1234.567 Hz, k = 3). |
|
signal:ENVELOPES-MATCH-REFERENCE v1.0.0 | T | adsr and fade match lifesound.py within 1e-12: numpy.linspace segments of int(t·sr) samples, an ADSR exactly n samples long, and fades applied to every channel of a frame. | |
signal:EXP-DECAY-60DB v1.0.0 | T | exp_decay falls by a factor of 1000 (60 dB) every t60 seconds, within 1e-12 relative; it uses ln 1000 where lifesound.py used the rounded 6.907755. | |
signal:BOX-LOWPASS-MATCHES-REFERENCE v1.0.0 | T | box_lowpass and highpass match lifesound.py's cumulative-sum algorithm within 1e-12 (L = 2h + 1 with h = max(⌊⌊0.44·sr/cutoff⌋/2⌋, 1), edges padded with the end samples, channels filtered separately), and highpass + box_lowpass(3 passes) reconstructs the input. | |
signal:BOX-NULLS-IN-CODE v1.0.0 | T | In the floating-point code, one pass of box_lowpass removes sinusoids at every whole multiple of box_first_null_hz = sr/L below sr to under 1e-9 away from the edges, keeps constants to 1e-12, and never raises the peak. |
|
signal:RNG-REPRODUCIBLE v1.0.0 | T | SplitMix64 reproduces the reference stream (seed 0 starts 0xE220A8397B1DCDAF, 0x6E789E6AA1B965F4, 0x06C45D188009454F); white_noise and tpdf_noise match an independent Python port bit for bit and gaussian_noise within 1e-12, in Rust, Python and WebAssembly. | |
signal:PINK-NOISE-MATCHES-REFERENCE v1.0.0 | T | pink_noise is lifesound.py's Voss–McCartney method driven by SplitMix64 and matches it within 1e-12; the result has zero mean, a peak just below 1, and more low-frequency power than white noise. |
|
signal:REVERB-MATCHES-REFERENCE v1.0.0 | T | reverb matches the oracle's FFT convolution reverb (lifesound.py's, with the impulse response built from the crate's noise and ln 1000 in exp_decay) within 1e-12; its overlap-add FFT convolution equals direct convolution within 1e-12 for mono and stereo input, in one block and across several (blocks of 8 and 5 frames), a click through a fully wet reverb returns the impulse response, and each impulse-response channel has unit energy. |
|
signal:PAN-EQUAL-POWER v1.0.0 | T | pan matches lifesound.py's equal-power pan and keeps power: left² + right² = x² within 1e-15 for every position in [−1, 1]. | |
signal:MIX-INTO-MATCHES-REFERENCE v1.0.0 | T | mix_into adds gain·sig into a buffer from frame n_samples(start_s, sr), cut off at the buffer's end, as lifesound.py does, for mono and interleaved stereo. | |
signal:WAV-BYTES-MATCH-REFERENCE v1.0.0 | T | write_wav writes the same 44-byte header (Python's wave module), TPDF dither formula, half-to-even rounding and clipping as lifesound.py's write_wav, with the dither drawn from SplitMix64 instead of numpy's PCG64: its bytes equal those of lifesound.py's write_wav with that generator swapped (the oracle), not those of files lifesound.py wrote before. |
|
signal:WAV-ROUND-TRIP v1.0.0 | T | read_wav(write_wav(x)) returns the channels and sample rate exactly and every sample in [−1, 1] within 1.5/32767; read_wav agrees with Python's wave module and skips unknown chunks. | |
signal:INPUT-VALIDATION v1.0.0 | T | Every function rejects a zero or negative sample rate, negative or non-finite durations and parameters, buffers that are not whole frames, requests above MAX_SAMPLES samples and malformed WAV files; Rust returns SignalError, Python raises ValueError (OverflowError for a negative or too-large integer argument such as a count, sample rate or seed, TypeError for a non-integer one) and JavaScript throws an Error (also for a count, sample rate or seed that is not a whole number up to 2^53 − 1). | |
signal:IMPLEMENTATIONS-AGREE v1.0.0 | T | The Rust crate, the Python bindings and the WebAssembly bindings all reproduce every golden vector. Every expected output comes from the oracle (lifesound.py's code run with numpy, with SplitMix64 in place of numpy's generator and ln 1000 in exp_decay), never from the Rust output; the error cases in errors.json are the crate's own input rules, some of which lifesound.py accepted. | |
Depends on
rhythm-unitsreleased
Used by
synthesis-theoryreleasedaudio-safety-mathreleasedegg-activation-modelsreleasedsonification-coreplannedweb-audio-kitplannedpsychoacousticsplanned
Posts about this repo
No posts about this repo yet.