wave-roll
Version:
JavaScript Library for Comparative MIDI Piano-Roll Visualization
202 lines • 7.14 kB
TypeScript
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