Preprocessing API¶
Working out how an experiment's recordings line up, before the pipeline runs over them.
Alignment¶
body_eye_sync.preprocessing.alignment
¶
Calculate the time offsets that put every input on one shared timeline, based on doi.org/10.1007/s12193-015-0196-1
Alignment
dataclass
¶
Offsets putting every input on one clock, and how much to trust them.
Source code in src/body_eye_sync/preprocessing/alignment.py
ok
property
¶
Whether every input is connected and the locked pairs agree.
PairOffset
dataclass
¶
One measurement: how far b sits from a, and how sure we are.
Source code in src/body_eye_sync/preprocessing/alignment.py
align(envelopes, *, hop=LANDMARK_HOP, min_quality=LANDMARK_MIN_VOTES, reference=None, pairwise=landmark_offset)
¶
Solve every input's offset from the envelopes, using all pairs at once.
reference is the input left at offset zero, defaulting to the first;
which one is chosen only shifts the whole timeline, it does not change the
inputs' positions relative to each other.
Source code in src/body_eye_sync/preprocessing/alignment.py
align_media(paths, *, reference=None, progress=None)
¶
Offsets for a set of recordings, keyed the way the inputs are.
Source code in src/body_eye_sync/preprocessing/alignment.py
landmark_features(media_path, sample_rate=LANDMARK_SAMPLE_RATE)
¶
Sparse (frame, hash) fingerprints for blind alignment.
Source code in src/body_eye_sync/preprocessing/alignment.py
landmark_offset(a, b, hop=LANDMARK_HOP)
¶
How many seconds to add to b's clock to align with a.
Confidence is the number of independent hash matches agreeing within four landmark frames. Hashes occurring very often are discarded because they describe repetitive tones rather than distinctive acoustic events.
Source code in src/body_eye_sync/preprocessing/alignment.py
measure_pairs(envelopes, hop=LANDMARK_HOP, pairwise=landmark_offset)
¶
Measure every pair of envelopes against each other.
Source code in src/body_eye_sync/preprocessing/alignment.py
solve_offsets(ids, pairs, *, min_quality=LANDMARK_MIN_VOTES, reference=None)
¶
Least-squares offsets from pair measurements, ignoring ones that failed.
Each locked pair contributes offset(b) - offset(a) = lag, weighted by
its quality, and the reference is pinned to zero. With more pairs than
unknowns the leftover disagreement becomes :attr:Alignment.residual.
Source code in src/body_eye_sync/preprocessing/alignment.py
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | |
Clock Rate¶
body_eye_sync.preprocessing.clock_rate
¶
Determine offset and rate to make recordings align in time.
ClockRateAnalysis
dataclass
¶
Measured lag curves and the timelines fitted to them.
Source code in src/body_eye_sync/preprocessing/clock_rate.py
ClockRateAnalysisCancelled
¶
analyse_clock_rates(paths, offsets, *, window=DEFAULT_WINDOW, search=DEFAULT_SEARCH, min_quality=SPECTRAL_MIN_QUALITY, min_drift_ppm=MIN_DRIFT_PPM, progress=None)
¶
Measure local lags and fit a timeline for every usable input.
The reference is selected automatically as the recording with the greatest total overlap with the others on the existing alignment timeline. Its clock is the one the others are measured against, so it keeps a rate of one; all returned fits are expressed on the existing experiment clock and can therefore be applied directly to the inputs.
A clock difference smaller than min_drift_ppm is left uncorrected.
Source code in src/body_eye_sync/preprocessing/clock_rate.py
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | |
fit_timeline(points, *, min_drift_ppm=MIN_DRIFT_PPM, min_span=MIN_DRIFT_SPAN, confidence=DRIFT_CONFIDENCE)
¶
Fit where a drifting recording starts and how fast its clock runs.
The fit is a straight line through the measured offsets, so its slope is the difference between the two devices' clocks. It is a Theil-Sen line — the median of the slopes between every pair of points — because a window that locks onto the wrong lag misses by a second where the others agree to a few milliseconds, and least squares would follow it.
None unless the slope clears three separate bars: measured over at least
min_span of recording, so it is not extrapolated across a session from
a moment of it; a confidence interval that excludes no difference at
all, so it is not noise; and at least min_drift_ppm, so it is worth
correcting.
Source code in src/body_eye_sync/preprocessing/clock_rate.py
offset_curve(reference, other, offset, *, window=10.0, search=12.0, min_quality=SPECTRAL_MIN_QUALITY, progress=None)
¶
Measure other's offset repeatedly across the experiment.
reference and other are arrays returned by :func:spectral_features.
offset seeds the search and only has to be close enough that the true
lag falls within search of it.
window is how much audio each measurement correlates. Windows do not
overlap, so their errors are independent and the spread of the points is
an honest measure of how well the lag is known.
min_quality is the lock threshold for each window.
Source code in src/body_eye_sync/preprocessing/clock_rate.py
pairwise_offset(a, b)
¶
Seconds to add to b's clock to reach a's, and the lock quality.
Each spectral feature is mean-subtracted and correlated independently, then the correlations are combined by the length of their vector.
Quality is how many standard deviations the best lag stands above the rest of the curve, so it says whether one lag is singled out rather than how strongly the recordings resemble each other.
Source code in src/body_eye_sync/preprocessing/clock_rate.py
spectral_features(media_path)
¶
Cepstral features of one recording, several values per frame.
The log-mel spectrogram comes from the faster-whisper FeatureExtractor.
Source code in src/body_eye_sync/preprocessing/clock_rate.py
Audio¶
body_eye_sync.preprocessing.audio
¶
audio_samples(path, sample_rate)
¶
Decode audio when possible, returning an empty array otherwise.
Source code in src/body_eye_sync/preprocessing/audio.py
load_audio(audio_path, sample_rate=SAMPLE_RATE)
¶
Decode a recording onto its own timeline, as mono samples.
Every decoded frame is placed at the position its timestamp gives it, so stretches a recorder never wrote are represented as silence.