UNPKG

wave-roll

Version:

JavaScript Library for Comparative MIDI Piano-Roll Visualization

202 lines 7.14 kB
import * as PIXI from "pixi.js"; import { ControlChangeEvent, NoteData } from "@/lib/midi/types"; import { PianoRollConfig, PianoRollViewState } from "./types"; import { ScaleLinear } from "d3-scale"; import { NoteInterval } from "@/lib/core/controls/utils/overlap"; import type { FileInfoMap } from "./types-internal"; export declare class PianoRoll { app: PIXI.Application; container: PIXI.Container; domContainer: HTMLElement; notesContainer: PIXI.Container; sustainContainer: PIXI.Container; playheadLine: PIXI.Graphics; backgroundGrid: PIXI.Graphics; /** Piano key horizontal lines (panY applied via container) */ pianoKeyLines: PIXI.Graphics; /** Container for octave labels on piano keys (panY applied via container) */ pianoKeyLabelContainer: PIXI.Container; /** Waveform overlay layer (rendered below the grid) */ waveformLayer: PIXI.Graphics; /** Waveform overlay drawn above the piano-keys area so it shows left of playhead */ waveformKeysLayer: PIXI.Graphics; loopOverlay: PIXI.Graphics; loopLines: { start: PIXI.Graphics; end: PIXI.Graphics; } | null; /** Semi-transparent overlay that visualizes sustain-pedal (CC64) regions */ sustainOverlay: PIXI.Graphics; overlapOverlay: PIXI.Graphics; /** Mask to clip notes/sustains so they never overlap the waveform band */ notesMask: PIXI.Graphics; overlapIntervals: NoteInterval[]; private tooltipDiv; private helpButtonEl; private helpPanelEl; private pitchHoverDiv; pitchHoverHighlight: PIXI.Graphics; playheadX: number; notes: NoteData[]; noteGraphics: PIXI.Graphics[]; controlChanges: ControlChangeEvent[]; /** * Sprite-based note objects used by the default renderer (Sprite batching). * We keep the original `noteGraphics` array for compatibility with the * legacy Graphics renderer that can still be enabled manually. Only one of * the two arrays is populated at any given time. */ noteSprites: PIXI.Sprite[]; state: PianoRollViewState; options: Required<PianoRollConfig>; /** * Indicates whether the note layer needs a full geometry redraw. We set this * flag to `true` whenever the underlying note data, zoom level, or canvas * dimensions change. During regular playback the timeline scrolls by * translating the `notesContainer` instead of erasing and re-drawing every * individual note each frame, so we can skip heavy redraw work when this * flag is `false`. */ private needsNotesRedraw; timeScale: ScaleLinear<number, number>; pitchScale: ScaleLinear<number, number>; private lastRenderTime; private renderThrottleMs; private rafId; private performanceMetrics; loopStart: number | null; loopEnd: number | null; pxPerSecond: number | null; onTimeChangeCallback: ((time: number) => void) | null; backgroundLabelContainer: PIXI.Container; loopLabelContainer: PIXI.Container; patternSprites?: PIXI.TilingSprite[]; hatchSprites?: PIXI.TilingSprite[]; onsetSprites?: PIXI.Sprite[]; fileColors?: Record<string, number>; highlightMode?: string; originalOnsetMap?: Record<string, number>; onlyOriginalOnsets?: boolean; fileInfoMap?: FileInfoMap; private constructor(); private initializeScales; /** * Static factory method to create PianoRoll instance */ static create(canvas: HTMLCanvasElement, domContainer: HTMLElement, options?: PianoRollConfig): Promise<PianoRoll>; /** * Initialize PixiJS application with canvas */ private initializeApp; /** * Initialize container hierarchy for organized rendering */ private initializeContainers; private initializeTooltip; /** Create a top-right help button with hover panel explaining interactions */ private initializeHelpButton; /** Initialize pitch hover indicator for showing current pitch row */ private initializePitchHover; /** * Find all notes at the given time and pitch position */ private findNotesAtPosition; /** * Show tooltip populated with the given note information. */ showNoteTooltip(note: NoteData, event: PIXI.FederatedPointerEvent): void; /** Update tooltip position to follow the pointer */ moveTooltip(event: PIXI.FederatedPointerEvent): void; /** Hide the tooltip */ hideTooltip(): void; /** * Update pitch hover indicator based on mouse Y position * @param clientY - Mouse Y position relative to viewport */ updatePitchHover(clientY: number): void; /** Hide pitch hover indicator */ hidePitchHover(): void; /** * Set up mouse/touch interaction for panning and zooming */ private setupInteraction; /** * Request render with throttling for performance */ requestRender(): void; /** * Full render of all components */ render(): void; /** * Set note data and trigger re-render */ setNotes(notes: NoteData[]): void; /** * Set current playback time and update playhead */ setTime(time: number): void; /** * Zoom in/out on X axis (time) */ zoomX(factor: number, anchorX?: number): void; /** * Zoom in/out on Y axis (pitch) */ zoomY(factor: number): void; /** * Pan the view by specified pixels */ pan(deltaX: number, deltaY: number): void; /** * Reset zoom and pan to default values */ resetView(): void; /** * Resize the PixiJS renderer and recompute scales/render. * @param width New canvas width in pixels * @param height New canvas height in pixels (defaults to existing height) */ resize(width: number, height?: number): void; /** * Update timeStep (grid spacing in seconds) and re-render background */ setTimeStep(step: number): void; /** * Update minor grid step and re-render */ setMinorTimeStep(step: number): void; /** * Get current timeStep */ getTimeStep(): number; /** * Get current minor timeStep */ getMinorTimeStep(): number; /** * Destroy the piano roll and clean up resources */ destroy(): void; /** * Get current state for debugging */ getState(): PianoRollViewState; /** * Clamp panX so that the playhead (fixed at pianoKeysOffset) always lies within * the timeline content. Prevents scrolling past the beginning or end. */ /** * Update loop window markers (A-B). Pass nulls to clear. */ setLoopWindow(start: number | null, end: number | null): void; /** * Register a callback that fires whenever the time under the fixed playhead changes. * This happens when the visual timeline is panned or zoomed. */ onTimeChange(callback: (time: number) => void): void; computeTimeAtPlayhead(): number; setOverlapRegions(overlaps: NoteInterval[]): void; setControlChanges(controlChanges: ControlChangeEvent[]): void; } //# sourceMappingURL=piano-roll.d.ts.map