@real-music-packages/web-core 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/scene/index.d.ts +2 -1
- package/dist/scene/index.js +94 -4
- package/dist/scene/index.js.map +1 -1
- package/package.json +1 -1
package/dist/scene/index.d.ts
CHANGED
|
@@ -834,6 +834,7 @@ declare function distinctOnsets(notes: {
|
|
|
834
834
|
* (first→last onset), not on musicMs — so the fade tracks the notes too.
|
|
835
835
|
*/
|
|
836
836
|
declare function audioPlayheadLine(layout: NotationLayout, onsetsMs: number[], tMs: number): PlayheadLine | null;
|
|
837
|
+
declare function vstackAudioPlayheadLine(layout: NotationLayout, onsetsMs: number[], tMs: number, nBars: number): PlayheadLine | null;
|
|
837
838
|
|
|
838
839
|
/** Map a canvas-space box through a base notation layout into world/screen coords. */
|
|
839
840
|
declare function mapBoxThroughLayout(base: NotationLayout, b: Box): Box;
|
|
@@ -1619,4 +1620,4 @@ interface SectionMinimapProps {
|
|
|
1619
1620
|
}
|
|
1620
1621
|
declare const sectionMinimapFactory: LayerFactory<SectionMinimapProps>;
|
|
1621
1622
|
|
|
1622
|
-
export { type Affine, type AudioClock, type AudioEvent, type BackgroundProps, type BeatGridOpts, type BeatTick, type BrandingProps, type BufferFactory, type BuildSceneOpts, type BuiltScene, type CameraState, type CaptionCue, type CaptionProps, type CaptionScript, type CaptionStyle, type ChordSpan, type CircleOfFifthsProps, type CirclePoint, type ClickTrackOpts, type ColorBy, type ContourPlot, type ContourPoint, type CountInOpts, type CountingTrackProps, type CtaProps, DEFAULT_FUNCTION_COLORS, type DegreeLabel, type DegreeLabelsProps, type DroneOpts, type DuckWindow, type Easing, type ExtendedDemoOpts, FIFTHS_MAJOR, FIFTHS_MINOR, FOLLOW_BARS, FOLLOW_PAD, type FallingKeyboardDemoOpts, type FallingNotesProps, type FunctionColors, type FunctionalHarmonyProps, type GateError, type GateInput, type HandColors, type HarmonicFunction, type HarmonyMode, type HighlightRegion, type HookProps, type KeyRect, type KeyboardLayout, type KeyboardLayoutOpts, type KeyboardProps, type LabelMode, type Layer, type LayerFactory, type McqCardProps, type NotationEngraving, type NotationLayout, type NotationLayoutOpts, type NotationProps, type NotationRect, type OutputProbe, PIANO_HIGH, PIANO_LOW, type PitchContourProps, type Placement, type PlayheadLine, type PortraitProps, type PromoCardsDemoOpts, type Quiz, type QuizOption, type QuizPhase, type RayEndpoints, type RecordSceneSpecOpts, type Rect, type RenderCtx, type ResolvedSegment, type RevealProps, type SafeGuidesProps, type SceneSpec, type ScheduleTarget, type Score, type ScoreFromMusicXMLOpts, type ScoreNote, type ScrollCursorProps, type Section, type SectionMinimapProps, type SegmentAudio, type SegmentAudioCtx, type SegmentTransition, type SpecLayer, type SpectrumInput, type SpectrumProps, type StaffKeyboardRayProps, type TempoMap, type TimeAnchor, type TimelineSegment, activeChord, activeCue, activeSection, animatedSlot, applySchedule, applyToContext, assertGate, audioPlayheadLine, ballArc, ballX, beatGrid, beatPhase, blackKeys, bpmOf, brandingFactory, buildScene, cameraForFollow, cameraTransform, circleOfFifthsDemoSpec, circleOfFifthsFactory, clamp, clickTrackSchedule, contourMinimapDemoSpec, contourPoints, contourPolyline, countInLeadSec, countInSchedule, countdownRemaining, countdownSeconds, countingDegreeDemoSpec, countingTrackFactory, cropAroundBox, ctaFactory, cubicEaseInOut, cueOpacity, degreeLabel, degreeLabelsFactory, distinctOnsets, dotAt, drawCaption, drawHighlight, droneSchedule, duckGainAt, easeIn, easeInOut, easeOut, fallingKeyboardDemoScore, fallingKeyboardDemoSpec, fallingNotesFactory, firstMeasureBox, followBoxAt, followSrcBox, followWindowStart, fracSlotPoint, frameRect, functionColor, functionalHarmonyFactory, getKeyboardLayout, getLayerFactory, getNotationEngraving, harmonyDemoSpec, highlightIntensity, hookFactory, identityCamera, inRange, invLerp, isBlackKey, kenBurns, keyCenterX, keyColumnWidth, keyRect, keySlot, keyboardFactory, keyboardLayout, lerp, lerpBox, lerpCamera, linear, mapBoxThroughLayout, mcqCardFactory, mcqDemoSpec, measureColumnsFromLayout, measureCount, measureSpanBox, measureSpans, measureSystemMap, timeToX as minimapTimeToX, msPerBeat, notationFactory, notationLayout, noteColor, noteSetXRange, parseKey, parseTimeSig, pcToSlot, pitchAt, pitchContourFactory, pitchRange, playheadLine, portraitFactory, progress01, projectPoint, promoCardsDemoSpec, quizPhase, rayEndpoints, rayPointAt, recordSceneSpec, registerLayer, registeredKeys, resolveAnchor, resolveKeyboardLayout, resolveTimeline, revealFactory, revealProgress, runGate, safeGuidesFactory, scoreFromMusicXML, scorePitchSpan, scrollCursorFactory, sectionMinimapFactory, setFollowLayoutProvider, setKeyboardLayout, setNotationEngraving, slotAngle, slotPc, slotPoint, spectrumFactory, staffAnchor, staffKeyboardRayFactory, staffRayDemoSpec, systemBox, transitionProgress, validateChordTrack, validateQuiz, validateSections, visualTimelineMs, vstackFollowBox, whiteKeys, worldToViewport };
|
|
1623
|
+
export { type Affine, type AudioClock, type AudioEvent, type BackgroundProps, type BeatGridOpts, type BeatTick, type BrandingProps, type BufferFactory, type BuildSceneOpts, type BuiltScene, type CameraState, type CaptionCue, type CaptionProps, type CaptionScript, type CaptionStyle, type ChordSpan, type CircleOfFifthsProps, type CirclePoint, type ClickTrackOpts, type ColorBy, type ContourPlot, type ContourPoint, type CountInOpts, type CountingTrackProps, type CtaProps, DEFAULT_FUNCTION_COLORS, type DegreeLabel, type DegreeLabelsProps, type DroneOpts, type DuckWindow, type Easing, type ExtendedDemoOpts, FIFTHS_MAJOR, FIFTHS_MINOR, FOLLOW_BARS, FOLLOW_PAD, type FallingKeyboardDemoOpts, type FallingNotesProps, type FunctionColors, type FunctionalHarmonyProps, type GateError, type GateInput, type HandColors, type HarmonicFunction, type HarmonyMode, type HighlightRegion, type HookProps, type KeyRect, type KeyboardLayout, type KeyboardLayoutOpts, type KeyboardProps, type LabelMode, type Layer, type LayerFactory, type McqCardProps, type NotationEngraving, type NotationLayout, type NotationLayoutOpts, type NotationProps, type NotationRect, type OutputProbe, PIANO_HIGH, PIANO_LOW, type PitchContourProps, type Placement, type PlayheadLine, type PortraitProps, type PromoCardsDemoOpts, type Quiz, type QuizOption, type QuizPhase, type RayEndpoints, type RecordSceneSpecOpts, type Rect, type RenderCtx, type ResolvedSegment, type RevealProps, type SafeGuidesProps, type SceneSpec, type ScheduleTarget, type Score, type ScoreFromMusicXMLOpts, type ScoreNote, type ScrollCursorProps, type Section, type SectionMinimapProps, type SegmentAudio, type SegmentAudioCtx, type SegmentTransition, type SpecLayer, type SpectrumInput, type SpectrumProps, type StaffKeyboardRayProps, type TempoMap, type TimeAnchor, type TimelineSegment, activeChord, activeCue, activeSection, animatedSlot, applySchedule, applyToContext, assertGate, audioPlayheadLine, ballArc, ballX, beatGrid, beatPhase, blackKeys, bpmOf, brandingFactory, buildScene, cameraForFollow, cameraTransform, circleOfFifthsDemoSpec, circleOfFifthsFactory, clamp, clickTrackSchedule, contourMinimapDemoSpec, contourPoints, contourPolyline, countInLeadSec, countInSchedule, countdownRemaining, countdownSeconds, countingDegreeDemoSpec, countingTrackFactory, cropAroundBox, ctaFactory, cubicEaseInOut, cueOpacity, degreeLabel, degreeLabelsFactory, distinctOnsets, dotAt, drawCaption, drawHighlight, droneSchedule, duckGainAt, easeIn, easeInOut, easeOut, fallingKeyboardDemoScore, fallingKeyboardDemoSpec, fallingNotesFactory, firstMeasureBox, followBoxAt, followSrcBox, followWindowStart, fracSlotPoint, frameRect, functionColor, functionalHarmonyFactory, getKeyboardLayout, getLayerFactory, getNotationEngraving, harmonyDemoSpec, highlightIntensity, hookFactory, identityCamera, inRange, invLerp, isBlackKey, kenBurns, keyCenterX, keyColumnWidth, keyRect, keySlot, keyboardFactory, keyboardLayout, lerp, lerpBox, lerpCamera, linear, mapBoxThroughLayout, mcqCardFactory, mcqDemoSpec, measureColumnsFromLayout, measureCount, measureSpanBox, measureSpans, measureSystemMap, timeToX as minimapTimeToX, msPerBeat, notationFactory, notationLayout, noteColor, noteSetXRange, parseKey, parseTimeSig, pcToSlot, pitchAt, pitchContourFactory, pitchRange, playheadLine, portraitFactory, progress01, projectPoint, promoCardsDemoSpec, quizPhase, rayEndpoints, rayPointAt, recordSceneSpec, registerLayer, registeredKeys, resolveAnchor, resolveKeyboardLayout, resolveTimeline, revealFactory, revealProgress, runGate, safeGuidesFactory, scoreFromMusicXML, scorePitchSpan, scrollCursorFactory, sectionMinimapFactory, setFollowLayoutProvider, setKeyboardLayout, setNotationEngraving, slotAngle, slotPc, slotPoint, spectrumFactory, staffAnchor, staffKeyboardRayFactory, staffRayDemoSpec, systemBox, transitionProgress, validateChordTrack, validateQuiz, validateSections, visualTimelineMs, vstackAudioPlayheadLine, vstackFollowBox, whiteKeys, worldToViewport };
|
package/dist/scene/index.js
CHANGED
|
@@ -323,6 +323,28 @@ function cubicEaseInOut(t) {
|
|
|
323
323
|
const f = 2 * t - 2;
|
|
324
324
|
return 0.5 * f * f * f + 1;
|
|
325
325
|
}
|
|
326
|
+
function staffSpacePx(rn) {
|
|
327
|
+
const hs = (rn.measures ?? []).map((m) => m.box.h).filter((h) => h > 1).sort((a, b) => a - b);
|
|
328
|
+
if (!hs.length) return 14;
|
|
329
|
+
const med = hs[Math.floor(hs.length / 2)];
|
|
330
|
+
return Math.max(6, med / 4);
|
|
331
|
+
}
|
|
332
|
+
function withLedgerHeadroom(rn, box, spaces, unionInk) {
|
|
333
|
+
const pad = staffSpacePx(rn) * spaces;
|
|
334
|
+
const ch = rn.canvas.height || box.y + box.h;
|
|
335
|
+
let y0 = box.y - pad;
|
|
336
|
+
let y1 = box.y + box.h + pad;
|
|
337
|
+
if (unionInk) {
|
|
338
|
+
const c = rn.content;
|
|
339
|
+
if (c && c.h > 0) {
|
|
340
|
+
y0 = Math.min(y0, c.y);
|
|
341
|
+
y1 = Math.max(y1, c.y + c.h);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
y0 = Math.max(0, y0);
|
|
345
|
+
y1 = Math.min(ch, y1);
|
|
346
|
+
return { x: box.x, y: y0, w: box.w, h: Math.max(1, y1 - y0) };
|
|
347
|
+
}
|
|
326
348
|
function lerpBox(a, b, e) {
|
|
327
349
|
return {
|
|
328
350
|
x: a.x + (b.x - a.x) * e,
|
|
@@ -353,7 +375,7 @@ function measureSpanBox(rn, lo, hi) {
|
|
|
353
375
|
const y0 = Math.min(...ms.map((b) => b.y));
|
|
354
376
|
const x1 = Math.max(...ms.map((b) => b.x + b.w));
|
|
355
377
|
const y1 = Math.max(...ms.map((b) => b.y + b.h));
|
|
356
|
-
return { x: x0, y: y0, w: x1 - x0, h: y1 - y0 };
|
|
378
|
+
return withLedgerHeadroom(rn, { x: x0, y: y0, w: x1 - x0, h: y1 - y0 }, 6, true);
|
|
357
379
|
}
|
|
358
380
|
function firstMeasureBox(rn) {
|
|
359
381
|
const first = (rn.measures ?? []).filter((m) => m.index === 0).map((m) => m.box);
|
|
@@ -362,7 +384,7 @@ function firstMeasureBox(rn) {
|
|
|
362
384
|
const y0 = Math.min(...first.map((b) => b.y));
|
|
363
385
|
const x1 = Math.max(...first.map((b) => b.x + b.w));
|
|
364
386
|
const y1 = Math.max(...first.map((b) => b.y + b.h));
|
|
365
|
-
return { x: x0, y: y0, w: x1 - x0, h: y1 - y0 };
|
|
387
|
+
return withLedgerHeadroom(rn, { x: x0, y: y0, w: x1 - x0, h: y1 - y0 }, 6, false);
|
|
366
388
|
}
|
|
367
389
|
function measureCount(rn) {
|
|
368
390
|
const idx = (rn.measures ?? []).map((m) => m.index);
|
|
@@ -421,7 +443,7 @@ function systemBox(rn, sys, sysMap) {
|
|
|
421
443
|
const y0 = Math.min(...boxes.map((b) => b.y));
|
|
422
444
|
const x1 = Math.max(...boxes.map((b) => b.x + b.w));
|
|
423
445
|
const y1 = Math.max(...boxes.map((b) => b.y + b.h));
|
|
424
|
-
return { x: x0, y: y0, w: x1 - x0, h: y1 - y0 };
|
|
446
|
+
return withLedgerHeadroom(rn, { x: x0, y: y0, w: x1 - x0, h: y1 - y0 }, 8, false);
|
|
425
447
|
}
|
|
426
448
|
function vstackFollowBox(rn, camProgress01) {
|
|
427
449
|
const sysMap = measureSystemMap(rn);
|
|
@@ -617,6 +639,69 @@ function audioPlayheadLine(layout, onsetsMs, tMs) {
|
|
|
617
639
|
if (alpha <= 0.02) return null;
|
|
618
640
|
return { x, y0, y1, alpha };
|
|
619
641
|
}
|
|
642
|
+
function vstackAudioPlayheadLine(layout, onsetsMs, tMs, nBars) {
|
|
643
|
+
const padV = 10;
|
|
644
|
+
const cols = measureColumnsFromLayout(layout.measures);
|
|
645
|
+
if (!cols.length || !onsetsMs.length || nBars <= 0) return null;
|
|
646
|
+
const n = onsetsMs.length;
|
|
647
|
+
const first = onsetsMs[0];
|
|
648
|
+
const last = onsetsMs[n - 1];
|
|
649
|
+
const span = last - first;
|
|
650
|
+
let progress;
|
|
651
|
+
if (tMs <= first || n === 1) progress = 0;
|
|
652
|
+
else if (tMs >= last || last <= first) progress = 1;
|
|
653
|
+
else {
|
|
654
|
+
let lo = 0, hi = n - 1;
|
|
655
|
+
while (lo < hi) {
|
|
656
|
+
const mid = lo + hi + 1 >> 1;
|
|
657
|
+
if (onsetsMs[mid] <= tMs) lo = mid;
|
|
658
|
+
else hi = mid - 1;
|
|
659
|
+
}
|
|
660
|
+
const segFrac = cubicEaseInOut((tMs - onsetsMs[lo]) / (onsetsMs[lo + 1] - onsetsMs[lo]));
|
|
661
|
+
progress = (lo + segFrac) / (n - 1);
|
|
662
|
+
}
|
|
663
|
+
const colX = (m2, f) => {
|
|
664
|
+
const sx = Math.min(m2.noteStartX, m2.x + m2.w);
|
|
665
|
+
return sx + Math.min(1, Math.max(0, f)) * (m2.x + m2.w - sx);
|
|
666
|
+
};
|
|
667
|
+
const posBars = Math.min(nBars - 1e-6, Math.max(0, progress * nBars));
|
|
668
|
+
const i = Math.min(cols.length - 1, Math.floor(posBars));
|
|
669
|
+
const barFrac = posBars - i;
|
|
670
|
+
const m = cols[i];
|
|
671
|
+
let x = colX(m, barFrac);
|
|
672
|
+
let y0 = m.y - padV;
|
|
673
|
+
let y1 = m.y + m.h + padV;
|
|
674
|
+
if (i + 1 < cols.length) {
|
|
675
|
+
const nxt = cols[i + 1];
|
|
676
|
+
const crossesSystem = Math.abs(nxt.y - m.y) > m.h * 1.5;
|
|
677
|
+
if (crossesSystem) {
|
|
678
|
+
const s = cubicEaseInOut(Math.min(1, Math.max(0, (barFrac - 0.66) / 0.34)));
|
|
679
|
+
x = x + (colX(nxt, 0) - x) * s;
|
|
680
|
+
y0 = y0 + (nxt.y - padV - y0) * s;
|
|
681
|
+
y1 = y1 + (nxt.y + nxt.h + padV - y1) * s;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
const bandY0 = layout.rect.dy;
|
|
685
|
+
const bandY1 = layout.rect.dy + layout.rect.dh;
|
|
686
|
+
const h = Math.max(1, y1 - y0);
|
|
687
|
+
if (y0 < bandY0) {
|
|
688
|
+
y0 = bandY0;
|
|
689
|
+
y1 = bandY0 + h;
|
|
690
|
+
}
|
|
691
|
+
if (y1 > bandY1) {
|
|
692
|
+
y1 = bandY1;
|
|
693
|
+
y0 = bandY1 - h;
|
|
694
|
+
}
|
|
695
|
+
let alpha = 0.85;
|
|
696
|
+
if (span > 0) {
|
|
697
|
+
const fadeIn = Math.min(250, span * 0.5);
|
|
698
|
+
const fadeOut = Math.min(400, span * 0.5);
|
|
699
|
+
if (tMs < first + fadeIn) alpha *= clamp01((tMs - (first - fadeIn)) / (2 * fadeIn));
|
|
700
|
+
if (tMs > last - fadeOut) alpha *= clamp01((last + fadeOut - tMs) / (2 * fadeOut));
|
|
701
|
+
}
|
|
702
|
+
if (alpha <= 0.02) return null;
|
|
703
|
+
return { x, y0, y1, alpha };
|
|
704
|
+
}
|
|
620
705
|
|
|
621
706
|
// src/scene/engravingStore.ts
|
|
622
707
|
var STORE = /* @__PURE__ */ new WeakMap();
|
|
@@ -783,7 +868,11 @@ function scrollCursorLayer() {
|
|
|
783
868
|
let line;
|
|
784
869
|
const onsets = mode === "audio" ? onsetsFor(ctx) : [];
|
|
785
870
|
if (mode === "audio" && onsets.length) {
|
|
786
|
-
|
|
871
|
+
if (scrollMode === "vstack") {
|
|
872
|
+
line = vstackAudioPlayheadLine(layout, onsets, tMs, measureCount(eng.rendered));
|
|
873
|
+
} else {
|
|
874
|
+
line = audioPlayheadLine(layout, onsets, tMs);
|
|
875
|
+
}
|
|
787
876
|
} else {
|
|
788
877
|
line = playheadLine(layout, camProgress(tMs));
|
|
789
878
|
}
|
|
@@ -3822,6 +3911,7 @@ export {
|
|
|
3822
3911
|
validateQuiz,
|
|
3823
3912
|
validateSections,
|
|
3824
3913
|
visualTimelineMs,
|
|
3914
|
+
vstackAudioPlayheadLine,
|
|
3825
3915
|
vstackFollowBox,
|
|
3826
3916
|
whiteKeys,
|
|
3827
3917
|
worldToViewport
|