Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@
},
{
"path": "dist/exo.full.iife.min.js",
"limit": "643 KB",
"limit": "647 KB",
"gzip": true
},
{
Expand Down
12 changes: 12 additions & 0 deletions packages/exojs-audio-fx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ Effects, analyser nodes, detectors, and playing voices have distinct lifetimes.

Beat detection estimates tempo and phase. Polling windows such as `justBeat` are not one-shot events, and asynchronous messages are not hard real-time delivery. A rhythm-game scoring timeline needs an explicit authoritative timing design. Analysis values are not a calibrated acoustic loudness measurement.

Assigning a different `BeatDetector.source` clears analysis history and pending predictions. Call `detector.reset()` after a seek or another playback discontinuity on the same source; reassigning that same source preserves continuity. Disconnected inputs and gaps in the context-frame stream also reset analysis. Recorded silence is processed as audio and expires predictions without assuming a source change. Use `notifyDiscontinuity('eof')` or `notifyDiscontinuity('explicitPause')` to stop analysis explicitly; `resume`, `reset()` or another source identity starts a new epoch. Seek, loop and starvation notifications clear history while preserving an explicit stop. Changing manual context also preserves that stop.

`predictionValidUntil` is the audio-context time at which a retained prediction schedule must be discarded, or 0 when unavailable. Without renewed onset support, validity lasts two tracked beat periods plus one FFT window. `lookahead` becomes empty after expiry, and unavailable `nextBeatTime` and `nextDownbeatTime` return 0. Later times in the eight-beat extrapolation remain conditional; retaining an array does not extend its validity. `onBeatPredicted` announces each received revision at most once and does not coalesce queued revisions. The immutable `schedule` view adds epoch/revision, generation/evidence times, conditional horizon, pulse period and the trust status at generation. It is null when expired. Its `nextBeat` and `nextDownbeat` are future entries or null, including when a manual boundary lies beyond the horizon. A retained snapshot containing downbeats expires with its meter interpretation; a fresh view may retain beat-only predictions until their separate deadline.

`meterEstimate` is an immutable view with `selected`, at most two `alternatives`, an uncalibrated `score`, `reason`, `origin` and audio-context validity times. `selected: null` means no supported cycle; `selected.phasePulses: null` means a cycle without an anchored downbeat. The selected model contains `cyclePulses`, `pulsePeriodSeconds`, `phasePulses` and `denominator`. Automatic detection considers three/four pulses and never infers a notated denominator. `meterKnown` requires both a valid cycle and phase. Unknown or expired phase suppresses automatic downbeats, bar-start events and lookahead downbeat flags; beat effects can continue.

Use `manualMeter: { cyclePulses: 3, phasePulses: 0, denominator: 4 }` to provide counting context, anchored at the first tracked beat after reset (`origin: 'manual'`). Omit phase to keep the cycle unanchored. The runtime `manualMeter` setter snapshots and validates its input, resets analysis when changed and accepts `null` to resume automatic detection. Configuration survives reset/source changes; the phase anchor is established anew. `enableTimeSignatureDetection: false` instead supplies a fixed four-beat convention (`origin: 'convention'`). Manual cycles support 1..32 pulses; an upcoming boundary outside the eight-entry lookahead remains unavailable rather than becoming the next beat.

`barPosition`, `barLength` and `timeSignature` are legacy display values when meter is unknown. Their denominator defaults to the convention 4 unless provided manually. `beatConfidence` separates the pulse score from the legacy meter factor; `confidence` and its lock behavior retain that factor, and `gridStability` remains its legacy alias. None is a calibrated probability. `BeatInfo.energy` retains raw novelty from the emission hop; the separate `onsetEnergy` and `onsetAudioTime` describe the associated onset or are null without a match. Cached rhythm getters expire against the audio-context clock even without another worklet message.

The [meter model and interpretation contract](docs/meter-estimation.md) describes the compact candidate and its limits. Automatic cycle/downbeat quality is experimental; a locked tempo does not establish musical bar accuracy.

## Documentation

[Audio basics](https://exoridus.github.io/ExoJS/en/guide/audio/audio-basics/) · [Effects and routing](https://exoridus.github.io/ExoJS/en/guide/audio/audio-effects/) · [Beat detection](https://exoridus.github.io/ExoJS/en/guide/audio/beat-detection/) · [Audio-reactive visuals](https://exoridus.github.io/ExoJS/en/guide/audio/audio-reactive-visualization/) · [AudioAnalyser API](https://exoridus.github.io/ExoJS/en/api/audio-analyser/)
Expand Down
19 changes: 19 additions & 0 deletions packages/exojs-audio-fx/docs/meter-estimation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Meter interpretation

The live detector keeps pulse tracking and meter interpretation separate. FFT, mel novelty, two tempo windows and the PLL still determine beats. A compact experimental consumer evaluates three/four-pulse cycles on the tracked grid. It is not a joint tempo/bar-pointer decoder or a neural downbeat model, and it does not identify notated denominators, arbitrary groupings or simultaneous meters.

Each matched onset retains raw novelty and normalized strength with its identity and sample position. Its accent is the maximum PCM power over the following FFT window. Three positive-novelty band sums are accumulated from the existing mel pass over the same window. A later unrelated emission hop cannot supply that onset's accent. Missing onsets contribute no fabricated feature vector. New meter fitting and band accumulation are disabled for manual/fixed counting; the legacy tracker still contributes to legacy confidence and lock behavior.

The consumer retains at most 32 beat observations. It transforms each available channel with `log1p(value / windowMean)` and standardizes channels with sufficient contrast. Three/four-position repeating means are compared against a common two-position null, so uniform accents or a two-pulse alias do not establish a bar. Both cycle models use the same observations and channel priors; an extra-mean penalty limits the advantage of the larger model. Channel evidence is marginalized so a noisy band cannot veto a consistent attack channel. The highest supported cycle and its phase anchor are evaluated separately: two equally plausible anchor positions leave phase unknown even when repetition supports a cycle.

This is an untrained periodicity/accent reference with explicit abstention. Its scores are heuristic, and its thresholds do not establish calibrated confidence or representative real-music accuracy. Synthetic correctness tests prove the tested associations, phase rules, transitions and event timing; they do not qualify the automatic method for a musical application. A release-quality automatic-method decision also requires independent annotated recordings and native runtime measurements. No model is selected merely because it ranks first among unsuccessful candidates.

Support lasts two tracked pulse periods plus one FFT finalization window after the latest supporting observation. This covers one missing pulse and its successor's complete attack without a tempo-independent timeout. Expiry removes model/phase availability, including in cached main-thread getters. It does not backdate a newly inferred downbeat event. Lookahead revisions can update after feature finalization without emitting another beat.

`MeterEstimate.selected` is null without a supported cycle. A selected model's `phasePulses` can independently be null. Alternatives describe competing cycle models rather than simultaneous meters. Reasons distinguish acquisition, insufficient evidence, low contrast, ambiguous cycle, ambiguous phase, availability and expiry. `meterKnown` requires a current selection and non-null phase; only then may downbeats and bar starts be emitted. Legacy display counters remain readable while meter is unknown.

Manual context is caller-owned musical/counting knowledge, copied at assignment. Phase refers to the first tracked beat of the new analysis epoch, not an automatically found acoustic bar. Configuration persists across reset/source changes; observations and anchors do not. Fixed four-pulse counting has origin `convention`; manual context has origin `manual`; audio-derived hypotheses have origin `estimated`. A score of 1 for a chosen rule describes that rule, not certainty about the music.

A forecast revision captures generation time, consumed-evidence time, grid period and provisional/locked status when generated. Later state delivery does not rewrite that metadata. The public immutable `schedule` view bounds the lifetime of all included interpretations; a snapshot containing downbeat flags cannot outlive meter support. A fresh beat-only view can remain available until pulse support expires. Regular state transport runs no faster than 20 Hz, rounded to whole analysis hops; beat, tempo and immediate reset notices remain separate.

Explicit EOF/pause notifications invalidate the epoch and stop audio consumption by the detector. Resume, reset or another source identity starts fresh. Notifications for seek, loop or starvation preserve an existing stop, as does manual reconfiguration. The tap does not infer these owner-level playback events from recorded silence.
10 changes: 10 additions & 0 deletions packages/exojs-audio-fx/docs/mir-evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,13 @@ The existing provisional path improves first delivery by 1.440 seconds on 120 BP
A separate low-latency onset path is deferred. This evaluation establishes an existing startup benefit but does not demonstrate an incremental benefit from another detector, and provisional beats still have substantial posting delay and false positives on some inputs. No onset API, detector algorithm, threshold, or worklet option is changed by the harness. Any future candidate should be compared against these unchanged fixtures and a broader independently annotated music corpus before expanding the public API.

The sandbox measures deterministic algorithm behavior, not main-thread message delivery, speaker latency, animation-frame alignment, browser scheduling, CPU cost, or real-time deadline reliability. It covers one sample rate and synthetic mono signals. The kit is a simplified synthesis, swing annotations reflect an explicit beat convention, and ramp beat positions use the existing generator's forward-step approximation. Results do not establish music-genre generalization, calibrated confidence, downbeat accuracy, or rhythm-game scoring suitability.

## Pulse-cycle evidence

Run the named `test/worklets/meter-evidence.test.ts` and `test/worklets/beat-detector-meter.test.ts` files in the same package project for the automatic cycle model. The first tests finalized onset power, every initial three-/four-pulse phase across gains, missing observations, skipped grid pulses, validity through slow pulses, two-pulse ambiguity, equal accents and seeded random accent noise. The second passes authored PCM through the production FFT/onset/tempo/PLL pipeline without preparing a meter state: 50/60 BPM event regressions and 90/120/180 BPM controls, three-/four-pulse accents, quiet attacks with another spectrum, equal accents, both cycle-switch directions and explicit four-beat counting. Authored downbeat times are independent of the detector's output. Cycle and phase checks after ten seconds describe these controls; they do not establish zero startup delay or general music accuracy.

The named `test/browser/beat-detector.audio.test.ts` file in `browser-audio-chromium` additionally exercises three-/four-pulse and equal-accent audio at 44.1 and 48 kHz, reset acknowledgement and expiry during silence in a native AudioWorklet. These functional tests do not measure render deadlines, allocations or performance under graphics contention.

Unknown is a normal result, including when a beat grid is locked. The model requires at least twelve finalized matched onset observations, sufficient contrast against equal accents and separation between three- and four-pulse explanations. Its bounded history can delay recognition after a cycle change. The score is uncalibrated, and a pulse cycle does not infer a notated denominator. Representative independently annotated real-music meter/downbeat evaluation and controlled native timing/allocation qualification remain required before claiming general reliability.

Prediction retention follows the tracked pulse interval: two periods plus one FFT window after the last supporting observation. The finalization allowance keeps an established cycle available while the next attack is being finalized, including at 50 BPM. One missing pulse can be bridged; without renewed support, predictions still expire. Explicit reset and source/input discontinuities invalidate immediately.
1 change: 1 addition & 0 deletions packages/exojs-audio-fx/src/BeatAnalysisDiscontinuity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type BeatAnalysisDiscontinuity = 'sourceChange' | 'seek' | 'loop' | 'starvation' | 'explicitPause' | 'resume' | 'eof' | 'reset';
Loading
Loading