Conversation
Bumps [node-web-audio-api](https://github.com/ircam-ismm/node-web-audio-api) from 1.0.9 to 2.0.0. - [Changelog](https://github.com/ircam-ismm/node-web-audio-api/blob/main/CHANGELOG.md) - [Commits](ircam-ismm/node-web-audio-api@v1.0.9...v2.0.0) --- updated-dependencies: - dependency-name: node-web-audio-api dependency-version: 2.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) from 1.67.0 to 1.69.0. - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.69.0/npm/oxlint) --- updated-dependencies: - dependency-name: oxlint dependency-version: 1.69.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [@typescript/native-preview](https://github.com/microsoft/typescript-go) from 7.0.0-dev.20260607.1 to 7.0.0-dev.20260611.2. - [Changelog](https://github.com/microsoft/typescript-go/blob/main/CHANGES.md) - [Commits](https://github.com/microsoft/typescript-go/commits) --- updated-dependencies: - dependency-name: "@typescript/native-preview" dependency-version: 7.0.0-dev.20260611.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [@cloudflare/workers-types](https://github.com/cloudflare/workerd) from 4.20260608.1 to 4.20260611.1. - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 4.20260611.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Extract decodeBmsText / decodeUtf8Text from parser.ts into core/bms-text-decode.ts as the single bytes-to-text pipeline for every runtime, and switch the web player's collection loader to it instead of its local duplicate. Remove the unreachable iconv-lite based bms-text-decoder.ts (never imported) together with the iconv-lite dependency. Export decodeUtf8Text with a bench case and test.
…pture Move the object-line interpretation rule (channel 02 measure length + non-zero token stream) into event-utils buildBmsObjectLineEntry so the strict parse path and the #RANDOM / #IF / #SWITCH capture path can no longer drift apart. Output shapes are unchanged.
Replace the per-backend sourceFormat === 'bms' checks that encode the LR2-compatible one-voice-per-slot retrigger policy with a single usesMonophonicWavPlayback predicate in @be-music/chart. The Node mixer, debug active-audio estimator, WebAudio session, offline renderer, and web preview now consult the same definition.
Add bmsExRankValueToJudgeRankPercent (the RANK 2 = 100 unit conversion shared by #DEFEXRANK and #EXRANKxx) and route the dynamic channel-A0 path through a dedicated resolveBmsJudgeWindowsMsForExRankValue entry point in judge-window.ts. Behavior is unchanged: the dynamic path still skips the conversion (spec audit A-2); the deviation is now documented and pinned by tests so the future fix is a single-line change in one module.
Export DEFAULT_POOR_BGA_DISPLAY_SECONDS from core/bga-timeline.ts and consume it from the TUI compositor and the web LR2 scene instead of their local 2 s / 2000 ms constants. Replace the web scene's hardcoded 'gauge >= 80' clear check with core isGrooveGaugeCleared so the clear threshold has a single owner; GROOVE behavior is identical (80 %), HARD / EASY skin variants now follow the core per-variant thresholds.
The Vite build emits worker and lazy chunks with a .js extension (WASMAudioDecoderWorker-*.js, opus-ml-*.js) that the inliner skipped, leaving top-level relative requires in the embedded entry. The SEA injected require only resolves builtins, so every launch (including --help) crashed with ERR_UNKNOWN_BUILTIN_MODULE.
vite build() runs the client environment, whose default conditions and main fields prefer browser entries. The SEA bundle therefore shipped pino/browser (flush is a noop, so the logger close() hung forever, swallowing playback errors into a silent exit 0, writing zero bytes to the NDJSON log file, and dumping log objects to the console) and isoworker's Web Worker implementation. Resolve with the server conditions and main fields instead.
… SPEED head Resolve the first wave of BMS spec-audit findings: - HARD / DEATH gauges now FAIL when they bottom out at 0 %; isGrooveGaugeCleared previously read 0 >= 0 as cleared, so survival gauges could never fail. Propagates to the web scene via the shared helper. - Dynamic #EXRANKxx (channel A0) values now pass through the same RANK 2 = 100 unit conversion as #DEFEXRANK, so #EXRANK 100 restores exactly the NORMAL windows instead of widening them by 4/3. - bmson key_channels per-mine damage is applied as the gauge damage, taking precedence over the BMS value/2 rule; damage 0 is honored as a no-damage decoration mine. - #SPEEDxx now holds the first keyframe's value before its beat (Bemuse reference semantics) instead of ramping linearly from 1.0. #SCROLLxx keeps its implicit 1.0 head. Update player-spec / bmson-spec docs (both languages) to state the EXRANK unit, the pre-first-keyframe SPEED rule, and the spec-correct negative-total handling.
A SEA binary cannot load the gameplay/UI/BGA-video worker scripts: the worker .js files are not shipped next to the executable, the injected require only resolves builtins, and the CJS bundle compiles import.meta.url to undefined, so resolving the worker URL threw before the Worker was even constructed. Playback therefore never started. Embed the bundle itself as a SEA asset and spawn eval workers that re-run it; the new sea-main entry dispatches on a workerData role marker to the matching worker module before the CLI entry runs. The CLI entry additionally refuses to start from a worker thread, since inside SEA workers process.argv[1] still equals process.execPath.
An undefined / missing / undecodable #WAVxx reference must be silent (LR2 / beatoraja both skip it); the synthesized sine fallback used to play unconditionally on the Node / TUI path while the web runtime was already silent. The tone is now opt-in: fallbackToneSeconds in the audio-renderer APIs, missingSampleToneSeconds in PlayerOptions.
…eBmsText Implement the documented detection pipeline in the shared decoder: BOM (UTF-8 / UTF-16LE / UTF-16BE) -> #CHARSET -> ASCII fast path -> strict UTF-8 validation -> scored candidates (shift_jis / utf-8 / euc-jp / iso-8859-1), all on the WHATWG TextDecoder so CLI / TUI / web behave identically. Previously UTF-16 charts decoded as garbled shift_jis (losing every event) and BOM-less UTF-8 titles were mojibake.
The channel stream of #mmmcc:data is one contiguous token run in de-facto BMS implementations. The tokenizer used to skip whitespace and keep pairing, so trailing text fabricated note events (junk words -> JU/NK/WO/RD) and inflated the denominator of legitimate tokens. The shared tokenizer change covers both the strict parse path and control-flow captured lines.
Inside a SEA, import() of a bare specifier always fails with ERR_UNKNOWN_BUILTIN_MODULE regardless of any node_modules on disk, so node-web-audio-api (audio playback) and @uwx/libav.js-fat (video BGA) could never load and the player stayed permanently silent. Add a loadOptionalNodeModule helper that falls back to createRequire resolution anchored next to the executable and the working directory, letting a SEA binary load these modules from a node_modules directory shipped alongside it.
node --build-sea invalidates the executable's signature, and macOS kills unsigned binaries with SIGKILL at launch. The signing step was best-effort and silent, which turned a codesign failure into a "build succeeded but the binary dies with Killed: 9" mystery.
…ed voices docs/bms-spec.md specifies that a #xxx97 / #xxx98 volume change applies as the initial gain of voices triggered from that point on and must not touch voices already playing. The WebAudio session wrote the level onto the shared bus mixers via setValueAtTime, retroactively rescaling in-flight voices and diverging from the Node engine. The session now tracks the current BGM / key dynamic gain and splices it into each new source's per-voice gain alongside the #WAVCMD multiplier.
These misspellings appear in released charts. Without the aliases the enclosing #IF block never closed, so on a non-matching #RANDOM roll everything after the directive vanished to EOF (and on a matching roll a junk END extras header leaked in). The variants normalize onto the canonical #ENDIF / #ELSEIF commands at parse time; #END with any other value stays an unknown header.
Allow BE_MUSIC_OPTIONAL_NODE_MODULE_DIR to name an extra base directory whose node_modules is consulted between the executable directory and the working directory. SEA binaries will point it at the extraction location of their embedded native modules; worker threads pick it up through the inherited environment.
Embed the package and its production dependency closure (filtered to the build host's native addon) as SEA assets. At startup the player extracts them once into a content-addressed cache directory under ~/.be-music/sea-embedded-modules and points loadOptionalNodeModule at it via BE_MUSIC_OPTIONAL_NODE_MODULE_DIR, so audio playback works out of the box without shipping a node_modules directory next to the executable. A node_modules beside the binary still takes precedence.
Embed @uwx/libav.js-fat into the player SEA binary with per-package exclude patterns: Node always selects the .wasm build and the player runs libav with noworker, so the asm.js fallback (~122MB), the worker-threaded build (~35MB), and the LGPL source tarballs (~30MB) are dropped, keeping the addition to ~31MB.
The inliner re-indented every line of an inlined chunk and rewrote its require calls with a regex. Both transformations can mutate the inside of multi-line template literals, and the 4-space indent corrupted the yEnc-encoded decoder wasm of @wasm-audio-decoders/* — in the player bundle every decoder copy lives inside a chunk, so ogg/opus/mp3 keysounds failed crc32 validation and played silently (the select preview fell back to dummy tones). Redirect chunk requires by shadowing the require parameter instead and wrap the entry in an IIFE, leaving both sources byte-for-byte intact.
import() wraps CJS exports in a namespace whose default holds them, but the createRequire fallback used inside a SEA returns the exports object directly. createLibAvInstance assumed the namespace shape, so under SEA it threw 'Cannot read properties of undefined (reading LibAV)' — the error was swallowed by the video loader and video BGA never played. Accept both module shapes and validate the factory before use.
Preserve the beatoraja bmson extensions info.ln_type and per-note t (1: LN, 2: CN, 3: HCN) in the IR, round-trip them through JSON and bmson stringifier output, and resolve the player's long-note mode from them (per-note t > info.ln_type > default). Unspecified charts now default to LN -- no tail release judgment, matching the LR2-aligned BMS default -- instead of always being judged as CN.
Replace the IIDX-baseline windows with linear judgerank scaling by the LR2 measurements (hitkey diary 2015-01-19, lr2oraja JudgeProperty): RANK 0-3 -> PG 8/15/18/21ms, GR 24/30/40/60ms, GD 40/60/100/120ms, RANK 4 treated as NORMAL, BAD fixed at 200ms for every rank, scratch sharing the key windows. #DEFEXRANK / #EXRANKxx / bmson judge_rank interpolate piecewise-linearly between the rank anchors (the JudgeWindowRule.LR2 model), extrapolate past EASY, and clamp to the fixed BAD gate.
Adopt beatoraja's LR2-compat gauge constants: HARD recovery is the fixed +0.1/+0.1/+0.05 with damage scaled by the LR2 #TOTAL multiplier table and softened x0.6 under 30%; EASY uses -3.2/-4.8/-1.6 damage and clears at 80% like GROOVE; DEATH follows HAZARD_LR2 (+0.15/+0.06/0, instant death on BAD/POOR, -10 on empty POOR). Survival gauges collapse to 0% once below 2% and never recover, and raw deltas (mine damage, HCN drain) route through applyGrooveGaugeRawDelta which bypasses guts / TOTAL scaling while keeping the life rules.
A phantom press now charges an empty POOR only when the lane has a
note within the next second (lr2oraja JudgeProperty LR2 miss window
{0, 1000000}us, early side only, rank-independent). Presses after a
note or in note-free stretches play the fallback keysound and nothing
else -- previously every phantom press drained the gauge and flashed
the POOR BGA, which made HARD / DEATH runs lose gauge for harmless
taps between phrases.
A mine now explodes while the lane's key is ON and the mine sits within the GOOD window of the judge line (losak's LR2 writeup; beatoraja's JudgeManager) -- both pressing with a mine in range and holding through a passing mine detonate; an un-pressed mine passes harmlessly. The old press-time matching (which detonated mines up to the BAD window early and let mines swallow presses aimed at nearby notes) is gone, regular note judgment runs independently, and explosions no longer emit a BAD or break combo. Damage is the raw base36 value as a percentage (LR2 / beatoraja; the nanasi value/2 rule is not what LR2 does), bypasses HARD guts / #TOTAL scaling via the raw-delta gauge path, and ZZ instantly fails survival gauges. Held state uses the kitty press/release set with a short press-grace approximation for release-less fallback input.
…1.69.0 chore(deps-dev): bump oxlint from 1.67.0 to 1.69.0
…are/workers-types-4.20260611.1 chore(deps-dev): bump @cloudflare/workers-types from 4.20260608.1 to 4.20260611.1
…ipt/native-preview-7.0.0-dev.20260611.2 chore(deps-dev): bump @typescript/native-preview from 7.0.0-dev.20260607.1 to 7.0.0-dev.20260611.2
The demo's Vite config enumerates @be-music/utils subpath exports as explicit aliases, and the new optional-node-module subpath (used by player/src/audio-sink.ts since the SEA embedded-modules work) was missing -- the bare '@be-music/utils' file alias swallowed it and rolldown tried to load 'index.ts/optional-node-module' (Not a directory, os error 20), breaking 'vite build'. The module is browser-safe by design (Node built-ins load lazily in the fallback path only), so a direct alias to the source file fixes the bundle.
Two skin-side divergences found by reverse-engineering the actual LR2 default 7K skin (LR2files/.../7keys/7_LL0.csv) and its #SRC_GROOVEGAUGE definition: - Remove the peak-hold / afterimage bead. LR2's groove gauge has no peak indicator -- the single #SRC/#DST_GROOVEGAUGE draws the bar purely from the live value. Drops the whole gaugePeak subsystem (fields, decay constants, updateGaugePeak tick). - Suppress the green clear-zone split for survival gauges. LR2 reuses one 4-cell SRC (表赤/表緑/裏赤/裏緑) and only paints the >=80% clear zone green for gauges that have a clear border (GROOVE / EASY). HARD / DEATH have no border, so LR2 renders the whole bar red; we now pass the gauge type through and suppress the green zone for them. Extract the per-bead cell selection into the pure, unit-tested resolveGrooveGaugeBeads helper.
The mine tests placed the mine on measure 0 (chart 0 s) and pressed the key immediately at speed 240. After A-3 switched mines to LR2's passage-based detonation (key held while the mine crosses the GOOD window), a single sped-up poll tick could advance chart time past the mine's window before the input was processed, so detonation became timing-dependent -- passing locally but flaking on CI (the bmson per-mine damage test failed there). Move the mine to measure 1 (chart 2.0 s) and hold the lane key through its passage via a shared kitty-state input schedule at speed 1, the same proven timing as the hold-through test. Detonation now lands on a frame tick inside the GOOD window every run.
Bumps [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler) from 4.98.0 to 4.100.0. - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.100.0/packages/wrangler) --- updated-dependencies: - dependency-name: wrangler dependency-version: 4.99.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) from 0.52.0 to 0.54.0. - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.54.0/npm/oxfmt) --- updated-dependencies: - dependency-name: oxfmt dependency-version: 0.54.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
…r-4.99.0 chore(deps-dev): bump wrangler from 4.98.0 to 4.100.0
….54.0 chore(deps-dev): bump oxfmt from 0.52.0 to 0.54.0
…b-audio-api-2.0.0 chore(deps): bump node-web-audio-api from 1.0.9 to 2.0.0
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
be-music-player-demo | d9df3f6 | Jun 14 2026, 04:57 AM |
Exports Benchmark
Summary
Top Regressions
Top Improvements
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release PR (
devel→main). Merging this triggers the Release workflow, which publishes a GitHub Release per bumped package and attaches the player / audio-renderer SEA executables.Released packages
Minor
@be-music/player0.4.3 → 0.5.0 — LR2 judge windows / HARD・EASY・DEATH gauges / mine detonation model@be-music/player-tui0.2.6 → 0.3.0 — Node SEA (single executable) support, embedded node-web-audio-api / libav.js@be-music/utils0.2.1 → 0.3.0 — new@be-music/utils/optional-node-modulesubpath (loadOptionalNodeModule)Patch
@be-music/parser0.2.2 → 0.2.3 — UTF-16 BOM detection / encoding scoring, control-flow spelling variants, object-data whitespace truncation, bmson ln_type@be-music/audio-renderer0.2.2 → 0.2.3 — missing keysounds default to silence@be-music/player-web0.6.1 → 0.6.2 — LR2 groove gauge bar rendering, dynamic volume scoping@be-music/json0.2.1 → 0.2.2,@be-music/stringifier0.3.0 → 0.3.1,@be-music/chart0.3.1 → 0.3.2 — bmson ln_type round-trip / internal dependency bumps@be-music/lr2-skin0.1.3 → 0.1.4,@be-music/beatoraja-skin0.1.1 → 0.1.2,@be-music/editor0.2.2 → 0.2.3 — internal dependency bumps@be-music/player-web-demo(private) is bumped 0.3.0 → 0.3.1 by the internal-dependency cascade but is not published.Per-package details are in each
packages/*/CHANGELOG.md.🤖 Generated with Claude Code