smoosic
Version:
<sub>[Github site](https://github.com/Smoosic/smoosic) | [source documentation](https://smoosic.github.io/Smoosic/release/docs/modules.html) | [change notes](https://aarondavidnewman.github.io/Smoosic/changes.html) | [application](https://smoosic.github.i
88 lines (85 loc) • 3.51 kB
text/typescript
import { SmoSelector, SmoSelection } from '../../smo/xform/selections';
import { SuiScoreView } from '../sui/scoreView';
import { SvgHelpers } from '../sui/svgHelpers';
import { SmoMeasure } from '../../smo/data/measure';
import { layoutDebug } from '../sui/layoutDebug';
/**
* A generic function that can be sent used to animate playback
*/
export type AudioAnimationHandler = (view: SuiScoreView, selector: SmoSelector, offsetPct: number, durationPct: number) => void;
/**
* A generic function that can be sent used to clean up playback animation
*/
export type ClearAudioAnimationHandler = (delay: number) => void;
/**
* Allow users to specify their own music playback animations.
* @category SuiAudio
*/
export interface SuiAudioAnimationParams {
audioAnimationHandler: AudioAnimationHandler,
clearAudioAnimationHandler: ClearAudioAnimationHandler
}
export const defaultClearAudioAnimationHandler = (delay: number) => {
if (delay < 1) {
const ell = document.getElementById('vf-music-cursor');
if (ell) {
ell.remove();
}
} else {
setTimeout(() => {
defaultClearAudioAnimationHandler(0);
}, delay);
}
}
/**
* default implementation of playback animation.
* @category SuiAudio
* @returns
*/
export const defaultAudioAnimationHandler = (view: SuiScoreView, selector: SmoSelector, offsetPct: number, durationPct: number) => {
const score = view.renderer.score;
if (!score) {
return;
}
const scroller = view.scroller;
const renderer = view.renderer;
// Get note from 0th staff if we can
const measureSel = SmoSelection.measureSelection(score,
score.staves.length - 1, selector.measure);
const zmeasureSel = SmoSelection.measureSelection(score,
0, selector.measure);
const measure = measureSel?.measure as SmoMeasure;
if (measure.svg.logicalBox && zmeasureSel?.measure?.svg?.logicalBox) {
const context = renderer.pageMap.getRenderer(measure.svg.logicalBox);
const topBox = SvgHelpers.smoBox(zmeasureSel.measure.svg.logicalBox);
topBox.y -= context.box.y;
const botBox = SvgHelpers.smoBox(measure.svg.logicalBox);
botBox.y -= context.box.y;
const height = (botBox.y + botBox.height) - topBox.y;
const measureWidth = botBox.width - measure.svg.adjX;
const nhWidth = 10 / score.layoutManager!.getGlobalLayout().svgScale;
let width = measureWidth * durationPct - 10 / score.layoutManager!.getGlobalLayout().svgScale;
width = Math.max(nhWidth, width);
const y = topBox.y;
let x = topBox.x + measure.svg.adjX + offsetPct * measureWidth;
const noteBox = score.staves[selector.staff].measures[selector.measure].voices[selector.voice].notes[selector.tick];
if (noteBox && noteBox.logicalBox) {
x = noteBox.logicalBox.x;
}
const screenBox = SvgHelpers.boxPoints(x, y, width, height);
const fillParams: Record<string, string> = {};
fillParams['fill-opacity'] = '0.5';
fillParams['fill'] = '#4444ff';
const ctx = context.getContext();
defaultClearAudioAnimationHandler(0);
ctx.save();
ctx.openGroup('music-cursor', 'music-cursor');
ctx.rect(x, screenBox.y, width, screenBox.height, fillParams);
ctx.closeGroup();
ctx.restore();
layoutDebug.updatePlayDebug(selector, measure.svg.logicalBox);
if (view.score.preferences.autoScrollPlayback) {
scroller.scrollVisibleBox(zmeasureSel.measure.svg.logicalBox);
}
}
}