-
Notifications
You must be signed in to change notification settings - Fork 14
10 ‐ Pulse, Cycles and Alignment
Plain numpy/scipy functions for pulse-level analysis of any 1-D signal or event-time array (onset-detection functions, quantity-of-motion curves, accelerometer speed, audio envelopes, ...). No MgVideo/MgAudio object is required. This page covers two closely related toolkits: peak-picking and cycle analysis (segmenting a stream of onsets into rhythmic cycles) and audio–motion alignment (lead/lag and event-matching between two independently-clocked streams).
pick_peaks() is the toolbox's one canonical peak-picker, shared by the pulse, alignment, quantity-of-motion and audio-feature modules. _pulse builds on it to segment a stream of stroke onsets into rhythmic cycles and fit an accelerando model — developed for the ro ritual's accelerating double-drum-stroke pattern, but applicable to any cyclic, accelerating onset sequence.
-
pick_peaks(x, fs=1.0, smooth=3, rel_threshold=0.5, min_interval=0.3, rel_prominence=0.2, threshold=None, prominence=None)— adaptive peak-picker: optional moving-average smoothing, a relative/absolute amplitude threshold, a minimum inter-peak interval, and an optional prominence gate. Returns integer sample indices. -
group_strokes(onset_times, max_strokes=4, ...)— segment stroke onsets into per-cycle stroke groups by dynamic-programming over candidate segmentations (size, within-gap, ordering and accelerando priors). Returns a list of onset-time groups. -
segment_cycles(onset_times, event_times=None, **kwargs)— likegroup_strokes()but returnsCycleobjects, each optionally carrying a secondary event (e.g. a shout) that falls inside the cycle. -
Cycle— dataclass for one rhythmic cycle (index,strokes,event); propertiest_start,n_strokes,stroke_gap. -
cycle_table(cycles, clip_id="", context="")— tabulate per-cycle metrics (t,ioi,n_strokes,stroke_gap,event,stroke_event_ioi) into apandas.DataFrame. -
fit_accelerando(times, iois)— least-squares fit ofIOI(t) = ioi0 * 2**(-t / t_double); returns(ioi0, t_double, r2). -
motion_onsets(motion, fs, min_interval=0.25, smooth_cutoff=8.0)— onset times of the steepest sustained rises in a motion signal (low-pass, differentiate, peak-pick).
import musicalgestures as mg
import numpy as np
# Stroke onsets from an accelerating double-stroke pattern (e.g. drum strokes)
onset_times = np.array([0.0, 0.30, 2.0, 2.28, 3.7, 3.95, 5.0, 5.20])
cycles = mg.segment_cycles(onset_times)
table = mg.cycle_table(cycles, clip_id="take01", context="ro")
ioi0, t_double, r2 = mg.fit_accelerando(table["t"].to_numpy(), table["ioi"].to_numpy())
# Peak-pick a quantity-of-motion curve
idx = mg.pick_peaks(qom_curve, fs=30.0, rel_threshold=0.4, min_interval=0.2)
onset_times_motion = mg.motion_onsets(qom_curve, fs=30.0)Lead/lag estimation and event alignment between two independently-clocked streams (e.g. audio onsets vs. motion onsets, or two camera angles). Pass in numpy arrays of envelopes or event times.
-
xcorr_lag(x, y, fs, max_lag=1.5)— canonical lead/lag estimate between two signals by cross-correlation, searched within ±max_lagseconds; returns(lag, r), positive lag =yhappens afterx. -
envelope_lag(x, y, rate, max_lag_s=1.5)— thin wrapper aroundxcorr_lag(), kept as the ro study's interface for envelope-to-envelope lags (e.g. voice vs. motion envelope). -
per_cycle_motion_delta(cycle_starts, motion_times, lookback=0.3)— for each rhythmic cycle (see Peak picking and cycle analysis above), the time of its assigned motion onset minus the cycle start. -
anchor_and_match(times_a, times_b, anchor_a=None, anchor_b=None, weights_a=None, weights_b=None, window=0.15)— per-take relative event alignment between two streams with independent clocks: both streams are anchored at their strongest event, then non-anchor events are matched one-to-one within ±windowseconds. Returns signed offsets (bminusa). -
offset_stats(offsets)— summary statistics (n,median,mean,std,iqr,abs_median,min,max) of a signed-offset distribution, e.g. fromanchor_and_match(). -
sliding_correlation(x, y, fs, window=10.0, step=2.0, min_std=1e-6)— local (windowed) Pearson correlation profile between two signals, for testing whether globally-uncorrelated streams couple locally. -
envelope_agreement(signals, fs, smooth=1.0)— agreement (correlation matrix + mean off-diagonal) among N parallel envelopes, e.g. per-camera-angle motion curves of the same performance.
import musicalgestures as mg
# Lead/lag between an audio onset envelope and a motion envelope
lag, r = mg.xcorr_lag(audio_envelope, motion_envelope, fs=100.0, max_lag=1.5)
# Relative timing agreement between kinematic impacts and audio onsets
offsets = mg.anchor_and_match(impact_times, audio_onset_times, window=0.15)
stats = mg.offset_stats(offsets)
# Cross-view agreement of five camera angles' motion envelopes
C, mean_r = mg.envelope_agreement([env_cam1, env_cam2, env_cam3, env_cam4, env_cam5], fs=25.0)pick_peaks() reimplements the cymbal-comparison study's (Jensenius) peak-picking convention and also subsumes the peak-picking conventions used in the Westney-comparisons and ro studies; its defaults are PROVISIONAL (the source study's own reported values vary by signal type — see the function docstring). The rest of _pulse — stroke grouping, cycle tables, the accelerando fit and motion onsets — implements the ro study's (Jensenius) analysis of the accelerating ro ritual's drum-stroke cycles and their coupling to body motion.
_alignment unifies the alignment methods of three studies (Jensenius): the ro study's envelope-lag and per-cycle motion-onset delta (rowing-gesture onsets vs. drum cycles); the cymbal-comparison study's anchor-and-match relative event alignment and offset statistics (audio-onset vs. kinematic-impact timing agreement without a common clock); and the Westney-comparisons study's cross-correlation sync, sliding-window coupling, and N-source envelope agreement (cross-view motion-envelope agreement, local motion-loudness coupling).
A project from the fourMs Lab, RITMO Centre for Interdisciplinary Studies in Rhythm, Time and Motion, Department of Musicology, University of Oslo.