//############################### debug.lib ############################################ // Debug library. Its official prefix is `db`. // // This library provides UI-based debug probes for metering, diagnostics, and // automated analysis. It includes level probes (RMS, peak, min/max), dynamics // probes (crest factor and envelope tracking), signal-quality probes (DC // offset, slew rate, zero-crossing rate, silence detection), control-signal // probes, spectral probes, decay/attack helpers, time stamps, and tap utilities. // // Each probe is a one-input/one-output processor that analyzes the passing // signal while leaving it unchanged. The measured value is exposed through // an output bargraph that can be regularly retrieved for metering purposes. // // Most probes take an `ID` argument and a `HIDE` argument. `ID` is embedded in // the `[probe:ID]` metadata tag so host applications can identify and collect // probe values reliably. `HIDE` controls the `[hidden:HIDE]` metadata tag and // can be used to keep probes out of the regular UI while still making their // values available to host-side tooling. // // Probes are intended to be inserted directly in a signal chain, or attached // with `probe_tap`/`probe_tap_n` when the measured signal should be branched // from mono or multichannel processing without changing the main arity. // // Debug probes can be compiled out by setting the `DEBUG` global to 0 via // explicit substitution (e.g. `db[DEBUG=0;].probe_rms_db(...)`). // CNE means constant numerical expression (known at compile time). // Some probes create several bargraphs from a base `ID`, such as // `probe_multiband`, which uses IDs `ID` through `ID+7`. // // The Debug library is organized into 11 sections: // // * [Level Probes](#level-probes) // * [Dynamics Probes](#dynamics-probes) // * [Signal Quality Probes](#signal-quality-probes) // * [Control Signal Probes](#control-signal-probes) // * [Spectral Probes](#spectral-probes) // * [Analysis Probes](#analysis-probes) // * [Decay Probes](#decay-probes) // * [Attack Probes](#attack-probes) // * [Analysis Signal Quality Probes](#analysis-signal-quality-probes) // * [Time Stamping](#time-stamping) // * [Tap Utilities](#tap-utilities) // // #### References // // * //######################################################################################## ma = library("maths.lib"); ba = library("basics.lib"); fi = library("filters.lib"); an = library("analyzers.lib"); si = library("signals.lib"); db = library("debug.lib"); // for compatible copy/paste out of this file declare name "Faust Debug Library"; declare version "0.3.1"; // Global parameter to enable/disable debug probes. DEBUG = 1; // 0: bypass probes (identity), 1: enable probes //==============================Level Probes=============================================== // Level meters for quick signal magnitude checks (RMS/peak). // Use for coarse gain staging and headroom validation. //======================================================================================== //------------------`(db.)probe_rms_lin`--------------------------------------- // RMS level probe (linear). // // #### Usage // // ``` // _ : probe_rms_lin(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_rms_lin_test = db.probe_rms_lin(1, 1, mono); // ``` //----------------------------------------------------------------------------- probe_rms_lin_impl(0, id, hide, x) = x; probe_rms_lin_impl(1, id, hide, x) = attach(x, an.rms_envelope_rect(0.1, x) : hbargraph("Probe RMS%2id [probe:%id][hidden:%hide]", 0, 1)); probe_rms_lin(id, hide, x) = probe_rms_lin_impl(DEBUG, id, hide, x); //------------------`(db.)probe_rms_db`---------------------------------------- // RMS level probe (dB). Bargraph includes `[unit:dB]`. // // #### Usage // // ``` // _ : probe_rms_db(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_rms_db_test = db.probe_rms_db(0, 1, mono); // ``` //----------------------------------------------------------------------------- probe_rms_db_impl(0, id, hide, x) = x; probe_rms_db_impl(1, id, hide, x) = attach(x, an.rms_envelope_rect(0.1, x) : max(0.00001) : ba.linear2db : hbargraph("Probe RMS dB%2id [probe:%id][unit:dB][hidden:%hide]", -60, 0)); probe_rms_db(id, hide, x) = probe_rms_db_impl(DEBUG, id, hide, x); //------------------`(db.)probe_peak_lin`-------------------------------------- // Peak level probe (linear). // // #### Usage // // ``` // _ : probe_peak_lin(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_peak_lin_test = db.probe_peak_lin(3, 1, mono); // ``` //----------------------------------------------------------------------------- probe_peak_lin_impl(0, id, hide, x) = x; probe_peak_lin_impl(1, id, hide, x) = attach(x, an.peak_envelope(0.1, x) : hbargraph("Probe Peak%2id [probe:%id][hidden:%hide]", 0, 1)); probe_peak_lin(id, hide, x) = probe_peak_lin_impl(DEBUG, id, hide, x); //------------------`(db.)probe_peak_db`--------------------------------------- // Peak level probe (dB). Bargraph includes `[unit:dB]`. // // #### Usage // // ``` // _ : probe_peak_db(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_peak_db_test = db.probe_peak_db(2, 1, mono); // ``` //----------------------------------------------------------------------------- probe_peak_db_impl(0, id, hide, x) = x; probe_peak_db_impl(1, id, hide, x) = attach(x, an.peak_envelope(0.1, x) : max(0.00001) : ba.linear2db : hbargraph("Probe Peak dB%2id [probe:%id][unit:dB][hidden:%hide]", -60, 0)); probe_peak_db(id, hide, x) = probe_peak_db_impl(DEBUG, id, hide, x); //=============================Dynamics Probes============================================ // Dynamics-related meters for crest factor and envelope tracking. // Useful for checking transient behavior and overall dynamics. //======================================================================================== //------------------`(db.)probe_crest_db`-------------------------------------- // Crest factor probe (peak/rms ratio in dB). // // #### Usage // // ``` // _ : probe_crest_db(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_crest_db_test = db.probe_crest_db(4, 1, mono); // ``` //----------------------------------------------------------------------------- probe_crest_db_impl(0, id, hide, x) = x; probe_crest_db_impl(1, id, hide, x) = attach(x, (an.peak_envelope(0.1, x) / max(0.00001, an.rms_envelope_rect(0.1, x))) : ba.linear2db : hbargraph("Probe Crest%2id [probe:%id][unit:dB][hidden:%hide]", 0, 20)); probe_crest_db(id, hide, x) = probe_crest_db_impl(DEBUG, id, hide, x); //------------------`(db.)probe_env`------------------------------------------- // Envelope follower probe (attack/release). // // #### Usage // // ``` // _ : probe_env(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_env_test = db.probe_env(5, 1, mono); // ``` //----------------------------------------------------------------------------- probe_env_impl(0, id, hide, x) = x; probe_env_impl(1, id, hide, x) = attach(x, an.amp_follower_ar(0.05, 0.2, x) : hbargraph("Probe Env%2id [probe:%id][hidden:%hide]", 0, 1)); probe_env(id, hide, x) = probe_env_impl(DEBUG, id, hide, x); //------------------`(db.)probe_min`------------------------------------------- // Minimum-hold probe (inverted peak envelope). // // #### Usage // // ``` // _ : probe_min(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_min_test = db.probe_min(6, 1, mono); // ``` //----------------------------------------------------------------------------- probe_min_impl(0, id, hide, x) = x; probe_min_impl(1, id, hide, x) = attach(x, (x * -1) : an.peak_envelope(0.5) : (_ * -1) : hbargraph("Probe Min%2id [probe:%id][hidden:%hide]", -2, 2)); probe_min(id, hide, x) = probe_min_impl(DEBUG, id, hide, x); //------------------`(db.)probe_max`------------------------------------------- // Maximum-hold probe (slow peak envelope). // // #### Usage // // ``` // _ : probe_max(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_max_test = db.probe_max(7, 1, mono); // ``` //----------------------------------------------------------------------------- probe_max_impl(0, id, hide, x) = x; probe_max_impl(1, id, hide, x) = attach(x, an.peak_envelope(0.5, x) : hbargraph("Probe Max%2id [probe:%id][hidden:%hide]", -2, 2)); probe_max(id, hide, x) = probe_max_impl(DEBUG, id, hide, x); //===========================Signal Quality Probes======================================== // Signal integrity checks such as DC offset, slew rate, and ZCR. // Helps detect bias, harsh transitions, or excessive high-frequency content. //======================================================================================== //------------------`(db.)probe_dc`-------------------------------------------- // DC offset probe (very lowpass). // // #### Usage // // ``` // _ : probe_dc(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_dc_test = db.probe_dc(8, 1, mono); // ``` //----------------------------------------------------------------------------- probe_dc_impl(0, id, hide, x) = x; probe_dc_impl(1, id, hide, x) = attach(x, fi.lowpass(1, 5, x) : hbargraph("Probe DC%2id [probe:%id][hidden:%hide]", -1, 1)); probe_dc(id, hide, x) = probe_dc_impl(DEBUG, id, hide, x); //------------------`(db.)probe_slew`------------------------------------------ // Slew rate probe (RMS of signal derivative). // // #### Usage // // ``` // _ : probe_slew(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_slew_test = db.probe_slew(9, 1, mono); // ``` //----------------------------------------------------------------------------- probe_slew_impl(0, id, hide, x) = x; probe_slew_impl(1, id, hide, x) = attach(x, (x - x') : abs : an.rms_envelope_rect(0.05) : hbargraph("Probe Slew%2id [probe:%id][hidden:%hide]", 0, 1)); probe_slew(id, hide, x) = probe_slew_impl(DEBUG, id, hide, x); //------------------`(db.)probe_zcr`------------------------------------------- // Zero-crossing rate probe (lowpassed). // // #### Usage // // ``` // _ : probe_zcr(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_zcr_test = db.probe_zcr(10, 1, mono); // ``` //----------------------------------------------------------------------------- probe_zcr_impl(0, id, hide, x) = x; probe_zcr_impl(1, id, hide, x) = attach(x, an.zcr(0.1, x) : hbargraph("Probe ZCR%2id [probe:%id][hidden:%hide]", 0, 1)); probe_zcr(id, hide, x) = probe_zcr_impl(DEBUG, id, hide, x); //===========================Control Signal Probes======================================== // Probes for control-rate or boolean signals. // Use to visualize modulators, gates, and internal state values. //======================================================================================== //------------------`(db.)probe_value`----------------------------------------- // Raw value probe (no smoothing). // // #### Usage // // ``` // _ : probe_value(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_value_test = db.probe_value(11, 1, mono); // ``` //----------------------------------------------------------------------------- probe_value_impl(0, id, hide, x) = x; probe_value_impl(1, id, hide, x) = attach(x, x : hbargraph("Probe Val%2id [probe:%id][hidden:%hide]", -1, 1)); probe_value(id, hide, x) = probe_value_impl(DEBUG, id, hide, x); //------------------`(db.)probe_bool`------------------------------------------ // Boolean probe (gate/trigger state). // // #### Usage // // ``` // _ : probe_bool(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_bool_test = db.probe_bool(12, 1, mono > 0); // ``` //----------------------------------------------------------------------------- probe_bool_impl(0, id, hide, x) = x; probe_bool_impl(1, id, hide, x) = attach(x, x : hbargraph("Probe Bool%2id [probe:%id][hidden:%hide]", 0, 1)); probe_bool(id, hide, x) = probe_bool_impl(DEBUG, id, hide, x); //==============================Spectral Probes=========================================== // Frequency-band and spectral analysis probes. // Includes simple band meters plus centroid and multiband analysis. //======================================================================================== //------------------`(db.)probe_band_lo`--------------------------------------- // Low-band energy probe (<300 Hz). // // #### Usage // // ``` // _ : probe_band_lo(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_band_lo_test = db.probe_band_lo(13, 1, mono); // ``` //----------------------------------------------------------------------------- probe_band_lo_impl(0, id, hide, x) = x; probe_band_lo_impl(1, id, hide, x) = attach(x, fi.lowpass(2, 300, x) : an.rms_envelope_rect(0.1) : hbargraph("Probe Lo%2id [probe:%id][hidden:%hide]", 0, 1)); probe_band_lo(id, hide, x) = probe_band_lo_impl(DEBUG, id, hide, x); //------------------`(db.)probe_band_mid`-------------------------------------- // Mid-band energy probe (300 Hz - 3 kHz). // // #### Usage // // ``` // _ : probe_band_mid(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_band_mid_test = db.probe_band_mid(14, 1, mono); // ``` //----------------------------------------------------------------------------- probe_band_mid_impl(0, id, hide, x) = x; probe_band_mid_impl(1, id, hide, x) = attach(x, fi.bandpass(2, 300, 3000, x) : an.rms_envelope_rect(0.1) : hbargraph("Probe Mid%2id [probe:%id][hidden:%hide]", 0, 1)); probe_band_mid(id, hide, x) = probe_band_mid_impl(DEBUG, id, hide, x); //------------------`(db.)probe_band_hi`--------------------------------------- // High-band energy probe (>3 kHz). // // #### Usage // // ``` // _ : probe_band_hi(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_band_hi_test = db.probe_band_hi(15, 1, mono); // ``` //----------------------------------------------------------------------------- probe_band_hi_impl(0, id, hide, x) = x; probe_band_hi_impl(1, id, hide, x) = attach(x, fi.highpass(2, 3000, x) : an.rms_envelope_rect(0.1) : hbargraph("Probe Hi%2id [probe:%id][hidden:%hide]", 0, 1)); probe_band_hi(id, hide, x) = probe_band_hi_impl(DEBUG, id, hide, x); //------------------`(db.)probe_spectral_centroid`------------------------------ // Spectral centroid estimate using multi-band analysis. // Outputs frequency estimate of spectral "center of mass". // // #### Usage // // ``` // _ : probe_spectral_centroid(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_spectral_centroid_test = db.probe_spectral_centroid(43, 1, mono); // ``` //----------------------------------------------------------------------------- probe_spectral_centroid_impl(0, id, hide, x) = x; probe_spectral_centroid_impl(1, id, hide, x) = attach(x, centroid) with { // 8-band analysis bands = ( fi.lowpass(2, 250, x), fi.bandpass(2, 250, 500, x), fi.bandpass(2, 500, 1000, x), fi.bandpass(2, 1000, 2000, x), fi.bandpass(2, 2000, 4000, x), fi.bandpass(2, 4000, 8000, x), fi.bandpass(2, 8000, 12000, x), fi.highpass(2, 12000, x) ); // Center frequencies freqs = (125, 375, 750, 1500, 3000, 6000, 10000, 16000); // RMS of each band rms(y) = an.rms_envelope_rect(0.05, y); energies = bands : par(i, 8, rms); // Weighted sum / total weighted_sum = energies : route(8, 8, par(i, 8, (i+1, i+1))) : par(i, 8, *(ba.take(i+1, freqs))) :> _; total_energy = energies :> _ : max(1e-6); centroid = weighted_sum / total_energy : hbargraph("Centroid%2id [probe:%id][unit:Hz][hidden:%hide]", 0, 10000); }; declare probe_spectral_centroid author "David Löwenfels"; probe_spectral_centroid(id, hide, x) = probe_spectral_centroid_impl(DEBUG, id, hide, x); //------------------`(db.)probe_multiband`-------------------------------------- // Multi-band analyzer - outputs energy in N frequency bands. // Designed for STFT-like time-frequency analysis via time-series capture. // // #### Usage // // ``` // _ : probe_multiband(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) base probe id used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // Creates 8 probes at IDs ID through ID+7. // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_multiband_test = db.probe_multiband(44, 1, mono); // ``` //----------------------------------------------------------------------------- probe_multiband_impl(0, id, hide, x) = x; probe_multiband_impl(1, id, hide, x) = attach(x, probes :> _) with { // 8-band analysis bands = ( fi.lowpass(2, 200, x), fi.bandpass(2, 200, 400, x), fi.bandpass(2, 400, 600, x), fi.bandpass(2, 600, 900, x), fi.bandpass(2, 900, 1400, x), fi.bandpass(2, 1400, 2200, x), fi.bandpass(2, 2200, 3500, x), fi.highpass(2, 3500, x) ); // RMS of each band energies = bands : par(i, 8, an.rms_envelope_rect(0.03)); // IDs (required for substitution) id0=id; id1=id+1; id2=id+2; id3=id+3; id4=id+4; id5=id+5; id6=id+6; id7=id+7; // Probes with unique IDs probes = energies : ( hbargraph("Band 0 [%id0][probe:%id0][band:0][hidden:%hide]", 0, 1), hbargraph("Band 1 [%id1][probe:%id1][band:1][hidden:%hide]", 0, 1), hbargraph("Band 2 [%id2][probe:%id2][band:2][hidden:%hide]", 0, 1), hbargraph("Band 3 [%id3][probe:%id3][band:3][hidden:%hide]", 0, 1), hbargraph("Band 4 [%id4][probe:%id4][band:4][hidden:%hide]", 0, 1), hbargraph("Band 5 [%id5][probe:%id5][band:5][hidden:%hide]", 0, 1), hbargraph("Band 6 [%id6][probe:%id6][band:6][hidden:%hide]", 0, 1), hbargraph("Band 7 [%id7][probe:%id7][band:7][hidden:%hide]", 0, 1) ); }; declare probe_multiband author "David Löwenfels"; probe_multiband(id, hide, x) = probe_multiband_impl(DEBUG, id, hide, x); //==============================Analysis Probes=========================================== // Targeted analysis probes for offline or diagnostic workflows. // Includes parametric band energy and frequency ratio checks. //======================================================================================== //------------------`(db.)probe_freq_lin`--------------------------------------- // Parametric bandpass energy probe at a specific frequency. // Use to verify presence of expected oscillator frequencies. // // #### Usage // // ``` // _ : probe_freq_lin(ID, HIDE, freq, q) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // * `freq`: center frequency in Hz // * `q`: filter Q (higher = narrower band, typically 5-20) // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_freq_lin_test = db.probe_freq_lin(34, 1, 440, 10, mono); // ``` //----------------------------------------------------------------------------- probe_freq_lin_impl(0, id, hide, freq, q, x) = x; probe_freq_lin_impl(1, id, hide, freq, q, x) = attach(x, fi.resonbp(freq, q, 1, x) : abs : an.rms_envelope_rect(0.05) : hbargraph("Freq %freq Hz%2id [probe:%id][freq:%freq][q:%q][hidden:%hide]", 0, 1)); declare probe_freq_lin author "David Löwenfels"; probe_freq_lin(id, hide, freq, q, x) = probe_freq_lin_impl(DEBUG, id, hide, freq, q, x); //------------------`(db.)probe_freq_db`---------------------------------------- // Parametric bandpass energy probe in dB at a specific frequency. // // #### Usage // // ``` // _ : probe_freq_db(ID, HIDE, freq, q) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // * `freq`: center frequency in Hz // * `q`: filter Q (higher = narrower band, typically 5-20) // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_freq_db_test = db.probe_freq_db(35, 1, 440, 10, mono); // ``` //----------------------------------------------------------------------------- probe_freq_db_impl(0, id, hide, freq, q, x) = x; probe_freq_db_impl(1, id, hide, freq, q, x) = attach(x, fi.resonbp(freq, q, 1, x) : abs : an.rms_envelope_rect(0.05) : max(1e-6) : ba.linear2db : hbargraph("Freq %freq Hz%2id [probe:%id][unit:dB][freq:%freq][q:%q][hidden:%hide]", -80, 0)); declare probe_freq_db author "David Löwenfels"; probe_freq_db(id, hide, freq, q, x) = probe_freq_db_impl(DEBUG, id, hide, freq, q, x); //------------------`(db.)probe_freq_ratio`------------------------------------- // Ratio between two frequency bands (useful for verifying oscillator balance). // // #### Usage // // ``` // _ : probe_freq_ratio(ID, HIDE, f1, f2, q) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // * `f1`: center frequency in Hz for the first band // * `f2`: center frequency in Hz for the second band // * `q`: filter Q (higher = narrower band, typically 5-20) // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_freq_ratio_test = db.probe_freq_ratio(36, 1, 440, 660, 12, mono); // ``` //----------------------------------------------------------------------------- probe_freq_ratio_impl(0, id, hide, f1, f2, q, x) = x; probe_freq_ratio_impl(1, id, hide, f1, f2, q, x) = attach(x, (band1 / max(1e-6, band2)) : hbargraph("Ratio %f1/%f2%2id [probe:%id][hidden:%hide]", 0, 10)) with { band1 = fi.resonbp(f1, q, 1, x) : abs : an.rms_envelope_rect(0.05); band2 = fi.resonbp(f2, q, 1, x) : abs : an.rms_envelope_rect(0.05); }; declare probe_freq_ratio author "David Löwenfels"; probe_freq_ratio(id, hide, f1, f2, q, x) = probe_freq_ratio_impl(DEBUG, id, hide, f1, f2, q, x); //==============================Decay Probes============================================= // Decay and release measurement helpers. // Use to track envelope level, peaks, and decay thresholds. //======================================================================================== //------------------`(db.)probe_env_lin`---------------------------------------- // Envelope state probe - outputs current envelope level for time-series capture. // // #### Usage // // ``` // _ : probe_env_lin(ID, HIDE, att_s, rel_s) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // * `att_s`: envelope attack time in seconds (e.g., 0.001) // * `rel_s`: envelope release time in seconds (e.g., 0.1) // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_env_lin_test = db.probe_env_lin(37, 1, 0.001, 0.1, mono); // ``` //----------------------------------------------------------------------------- probe_env_lin_impl(0, id, hide, att, rel, x) = x; probe_env_lin_impl(1, id, hide, att, rel, x) = attach(x, an.amp_follower_ar(att, rel, x) : hbargraph("Env%2id [probe:%id][attack:%att][release:%rel][hidden:%hide]", 0, 1)); declare probe_env_lin author "David Löwenfels"; probe_env_lin(id, hide, att, rel, x) = probe_env_lin_impl(DEBUG, id, hide, att, rel, x); //------------------`(db.)probe_env_db`----------------------------------------- // Envelope state probe in dB - for decay time analysis. // // #### Usage // // ``` // _ : probe_env_db(ID, HIDE, att_s, rel_s) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // * `att_s`: envelope attack time in seconds (e.g., 0.001) // * `rel_s`: envelope release time in seconds (e.g., 0.1) // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_env_db_test = db.probe_env_db(38, 1, 0.001, 0.1, mono); // ``` //----------------------------------------------------------------------------- probe_env_db_impl(0, id, hide, att, rel, x) = x; probe_env_db_impl(1, id, hide, att, rel, x) = attach(x, an.amp_follower_ar(att, rel, x) : max(1e-6) : ba.linear2db : hbargraph("Env dB%2id[probe:%id][unit:dB][attack:%att][release:%rel][hidden:%hide]", -80, 0)); declare probe_env_db author "David Löwenfels"; probe_env_db(id, hide, att, rel, x) = probe_env_db_impl(DEBUG, id, hide, att, rel, x); //------------------`(db.)probe_peak_hold`-------------------------------------- // Peak hold probe - captures and holds peak value until reset. // Useful for measuring maximum amplitude reached. // // #### Usage // // ``` // _ : probe_peak_hold(ID, HIDE, decay_s) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // * `decay_s`: time for held peak to decay (set high for true hold, e.g., 10.0) // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_peak_hold_test = db.probe_peak_hold(39, 1, 2.0, mono); // ``` //----------------------------------------------------------------------------- probe_peak_hold_impl(0, id, hide, decay_s, x) = x; probe_peak_hold_impl(1, id, hide, decay_s, x) = attach(x, held) with { env = abs(x); decay_coef = ba.tau2pole(decay_s); held = env : max ~ (_ * decay_coef) : hbargraph("Peak Hold%2id [probe:%id][hidden:%hide]", 0, 1); }; declare probe_peak_hold author "David Löwenfels"; probe_peak_hold(id, hide, decay_s, x) = probe_peak_hold_impl(DEBUG, id, hide, decay_s, x); //------------------`(db.)probe_below_threshold`-------------------------------- // Threshold crossing probe - outputs 1 when signal drops below threshold. // Useful for detecting when decay is "complete". // // #### Usage // // ``` // _ : probe_below_threshold(ID, HIDE, thresh_db) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // * `thresh_db`: threshold in dB for "below" detection // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_below_threshold_test = db.probe_below_threshold(40, 1, -40, mono); // ``` //----------------------------------------------------------------------------- probe_below_threshold_impl(0, id, hide, thresh_db, x) = x; probe_below_threshold_impl(1, id, hide, thresh_db, x) = attach(x, (env_db < thresh_db) : hbargraph("Below %thresh_db dB%2id[probe:%id][thresh:%thresh_db][hidden:%hide]", 0, 1)) with { env_db = an.amp_follower_ar(0.001, 0.05, x) : max(1e-6) : ba.linear2db; }; declare probe_below_threshold author "David Löwenfels"; probe_below_threshold(id, hide, thresh_db, x) = probe_below_threshold_impl(DEBUG, id, hide, thresh_db, x); //==============================Attack Probes============================================ // Attack/onset detection helpers. // Useful for timing, transient detection, and trigger validation. //======================================================================================== //------------------`(db.)probe_attack_state`----------------------------------- // Attack phase detector - outputs 1 during attack, 0 otherwise. // Attack is defined as: envelope rising AND above noise floor. // // #### Usage // // ``` // _ : probe_attack_state(ID, HIDE, floor_db) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // * `floor_db`: noise floor threshold in dB // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_attack_state_test = db.probe_attack_state(41, 1, -60, mono); // ``` //----------------------------------------------------------------------------- probe_attack_state_impl(0, id, hide, floor_db, x) = x; probe_attack_state_impl(1, id, hide, floor_db, x) = attach(x, (rising & above_floor) : hbargraph("Attacking%2id [probe:%id][hidden:%hide]", 0, 1)) with { env = an.amp_follower_ar(0.0005, 0.01, x); env_db = env : max(1e-6) : ba.linear2db; rising = env > env'; above_floor = env_db > floor_db; }; declare probe_attack_state author "David Löwenfels"; probe_attack_state(id, hide, floor_db, x) = probe_attack_state_impl(DEBUG, id, hide, floor_db, x); //------------------`(db.)probe_onset`------------------------------------------ // Onset detector - pulses 1 at signal onset (transition from silence to sound). // // #### Usage // // ``` // _ : probe_onset(ID, HIDE, thresh_db, holdoff_ms) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // * `thresh_db`: threshold for "sound present" (e.g., -40) // * `holdoff_ms`: minimum time between onsets in ms // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_onset_test = db.probe_onset(42, 1, -40, 50, mono); // ``` //----------------------------------------------------------------------------- probe_onset_impl(0, id, hide, thresh_db, holdoff_ms, x) = x; probe_onset_impl(1, id, hide, thresh_db, holdoff_ms, x) = attach(x, onset_pulse) with { env_db = an.amp_follower_ar(0.0005, 0.02, x) : max(1e-6) : ba.linear2db; above = env_db > thresh_db; was_below = above' == 0; holdoff_samples = holdoff_ms * ma.SR / 1000; // Simple onset with holdoff onset_raw = above & was_below; onset_pulse = onset_raw : ba.impulsify : hbargraph("Onset%2id[probe:%id][hidden:%hide]", 0, 1); }; declare probe_onset author "David Löwenfels"; probe_onset(id, hide, thresh_db, holdoff_ms, x) = probe_onset_impl(DEBUG, id, hide, thresh_db, holdoff_ms, x); //==============================Analysis Signal Quality Probes============================= // Additional signal-quality probes for analysis tasks. // Includes precise DC measurement and silence detection. //======================================================================================== //------------------`(db.)probe_dc_precise`------------------------------------- // Precise DC offset probe with very low cutoff for accurate measurement. // // #### Usage // // ``` // _ : probe_dc_precise(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_dc_precise_test = db.probe_dc_precise(56, 1, mono); // ``` //----------------------------------------------------------------------------- probe_dc_precise_impl(0, id, hide, x) = x; probe_dc_precise_impl(1, id, hide, x) = attach(x, fi.lowpass(2, 2, x) : hbargraph("DC Offset%2id [probe:%id][hidden:%hide]", -0.1, 0.1)); declare probe_dc_precise author "David Löwenfels"; probe_dc_precise(id, hide, x) = probe_dc_precise_impl(DEBUG, id, hide, x); //------------------`(db.)probe_silence`---------------------------------------- // Silence detector - outputs 1 when signal is effectively silent. // // #### Usage // // ``` // _ : probe_silence(ID, HIDE, thresh_db) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // * `thresh_db`: threshold in dB below which the signal is considered silent // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_silence_test = db.probe_silence(57, 1, -60, mono); // ``` //----------------------------------------------------------------------------- probe_silence_impl(0, id, hide, thresh_db, x) = x; probe_silence_impl(1, id, hide, thresh_db, x) = attach(x, (env_db < thresh_db) : hbargraph("Silent%2id [probe:%id][thresh:%thresh_db][hidden:%hide]", 0, 1)) with { env_db = an.rms_envelope_rect(0.05, x) : max(1e-6) : ba.linear2db; }; declare probe_silence author "David Löwenfels"; probe_silence(id, hide, thresh_db, x) = probe_silence_impl(DEBUG, id, hide, thresh_db, x); //==============================Time Stamping============================================= // Timebase probes for aligning analysis data. // Use sample count or milliseconds for synchronization. //======================================================================================== //------------------`(db.)probe_sample_count`----------------------------------- // Sample counter probe - outputs current sample number (wraps at 2^24). // Useful for synchronizing time-series data. // // #### Usage // // ``` // _ : probe_sample_count(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_sample_count_test = db.probe_sample_count(58, 1, mono); // ``` //----------------------------------------------------------------------------- probe_sample_count_impl(0, id, hide, x) = x; probe_sample_count_impl(1, id, hide, x) = attach(x, (+(1) : %(16777216)) ~ _ // 2^24 wrap : hbargraph("Sample%2id [probe:%id][hidden:%hide]", 0, 16777216)); declare probe_sample_count author "David Löwenfels"; probe_sample_count(id, hide, x) = probe_sample_count_impl(DEBUG, id, hide, x); //------------------`(db.)probe_time_ms`---------------------------------------- // Time probe in milliseconds (wraps at ~16 seconds at 44.1kHz). // // #### Usage // // ``` // _ : probe_time_ms(ID, HIDE) : _ // ``` // // Where: // // * `ID`: (CNE) probe id integer used in `[probe:ID]` // * `HIDE`: (CNE) 0 to show, 1 to hide in UI // // #### Test // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // mono = os.osc(220); // probe_time_ms_test = db.probe_time_ms(59, 1, mono); // ``` //----------------------------------------------------------------------------- probe_time_ms_impl(0, id, hide, x) = x; probe_time_ms_impl(1, id, hide, x) = attach(x, ((+(1) : %(int(ma.SR * 16))) ~ _) / ma.SR * 1000 : hbargraph("Time%2id [probe:%id][unit:ms][hidden:%hide]", 0, 16000)); declare probe_time_ms author "David Löwenfels"; probe_time_ms(id, hide, x) = probe_time_ms_impl(DEBUG, id, hide, x); //==============================Tap Utilities=============================================== // Utilities for tapping signals without changing arity. // Use to branch analysis meters while preserving main signal flow. //========================================================================================== //------------------`(db.)probe_tap`--------------------------------------------- ----------------- // Tap a signal for side-effect analysis without changing output arity. // // #### Usage // // ``` // _ : probe_tap(F) : _ // ``` // // Where: // // * `F`: signal processor for the tap (e.g., meter, analyser) // // #### Example test program // // Tap a pre-filter signal without changing arity: // // - The probe taps the signal before the filter. // - The main signal path remains 1-in/1-out, so the lowpass still sees a mono input. // - The RMS probe runs in parallel and is exposed through the UI/metrics. // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // fi = library("filters.lib"); // process = os.osc(220) // : db.probe_tap(db.probe_rms_db(0, 1)) // : fi.lowpass(4, 2000); // ``` // // #### Test // ``` // db = library("debug.lib"); // mono = os.osc(220); // os = library("oscillators.lib"); // probe_tap_test = db.probe_tap(db.probe_rms_db(16, 1), mono); // ``` //----------------------------------------------------------------------------- probe_tap_impl(0, f, x) = x; probe_tap_impl(1, f, x) = x <: attach(x, f); probe_tap(f, x) = probe_tap_impl(DEBUG, f, x); //------------------`(db.)probe_tap_n`-------------------------------------------- ----------------- // Tap an N-channel signal with a processor that takes N inputs and produces 1 output. // The original N-channel signal passes through unchanged. // // #### Usage // // ``` // probe_tap_n(N, F) // ``` // // Where: // // * `N`: (CNE) number of channels // * `F`: processor with N inputs and 1 output (e.g., sum or mix meter) // // #### Example test program // // Tap a stereo signal with a mixdown probe: // // - Two oscillators build a stereo pair with different filters per channel. // - After a stereo EQ stage, `probe_tap_n` mixes both channels for a single probe. // - The stereo signal continues unchanged after the tap (2-in/2-out). // ``` // db = library("debug.lib"); // os = library("oscillators.lib"); // fi = library("filters.lib"); // left = os.osc(220) : fi.lowpass(2, 1200); // right = os.osc(330) : fi.highpass(2, 400); // stereo = (left, right) // : (fi.lowpass(2, 2000), fi.highpass(2, 700)); // process = stereo // : db.probe_tap_n(2, + : db.probe_rms_db(100, 1)) // : (fi.lowpass(2, 8000), fi.lowpass(2, 8000)); // ``` // // #### Test // ``` // probe_tap_n_example = stereo // : db.probe_tap_n(2, + : db.probe_rms_db(100, 1)) // : (fi.lowpass(2, 8000), fi.lowpass(2, 8000)); // db = library("debug.lib"); // fi = library("filters.lib"); // stereo = (left, right) // : (fi.lowpass(2, 2000), fi.highpass(2, 700)); // left = os.osc(220) : fi.lowpass(2, 1200); // right = os.osc(330) : fi.highpass(2, 400); // os = library("oscillators.lib"); // probe_tap_n_test = probe_tap_n_example; // ``` //----------------------------------------------------------------------------- probe_tap_n_impl(0, n, f) = si.bus(n); probe_tap_n_impl(1, 1, f) = probe_tap(f); probe_tap_n_reorder(n) = route(n+1, n+1, (1, 1), (n+1, 2), par(i, n-1, (i+1, i+2))); probe_tap_n_impl(1, n, f) = si.bus(n) <: (si.bus(n), f) : probe_tap_n_reorder(n) : (attach, si.bus(n-1)); probe_tap_n(n, f) = probe_tap_n_impl(DEBUG, n, f);