UNPKG

@perry-rylance/midi-to-milliseconds

Version:

TypeScript library for resolving MIDI event times to milliseconds. Useful for syncing things like graphics to MIDI.

107 lines (75 loc) 2.52 kB
import { Event, Track } from "@perry-rylance/midi"; import TimeResolvedEvent, { AbsoluteTime } from "./TimeResolvedEvent"; import InjectedSetTempoEvent from "./InjectedSetTempoEvent"; // @ts-ignore import binarytree from "../lib/d3-binarytree/binarytree"; import { TimeResolverOptions } from "./TimeResolver"; export default class TimeResolvedTrack { events: TimeResolvedEvent[]; private millisecondsBinaryTree: any; private ticksBinaryTree: any; private options?: TimeResolverOptions; constructor(track: Track, options?: TimeResolverOptions) { let ticks = 0; this.options = options; this.events = track.events.map(event => { ticks += event.delta; return new TimeResolvedEvent(event, { ticks }); }); } private getEventsBetween(start: number, end: number, key: keyof AbsoluteTime): TimeResolvedEvent[] { if(this.events.length === 0) return []; const results: TimeResolvedEvent[] = []; start = Math.floor(start); end = Math.floor(end); let tree: any = null; switch(key) { case "milliseconds": if(!this.millisecondsBinaryTree) this.millisecondsBinaryTree = binarytree(this.events, (node: TimeResolvedEvent) => node.absolute.milliseconds); tree = this.millisecondsBinaryTree; break; case "ticks": if(!this.ticksBinaryTree) this.ticksBinaryTree = binarytree(this.events, (node: TimeResolvedEvent) => node.absolute.ticks); tree = this.ticksBinaryTree; break; default: throw new Error("Invalid key"); } tree.visit((node: any, x1: number, x2: number) => { if(!node.length) { do{ const d = node.data; const x = tree.x()(d); if(x >= start && x < end) results.push(d); }while(node = node.next); } return x1 >= end || x2 < start; }); const result = this.stripInjectedSetTempoEvents(results); if(this.options?.stable) result.sort((a, b) => a.index! - b.index!); return result; } private stripInjectedSetTempoEvents(events: TimeResolvedEvent[]): TimeResolvedEvent[] { return events.filter(event => !(event.original instanceof InjectedSetTempoEvent)); } getEventsBetweenTicks(start: number, end: number): TimeResolvedEvent[] { return this.getEventsBetween(start, end, "ticks"); } getEventsBetweenMilliseconds(start: number, end: number): TimeResolvedEvent[] { return this.getEventsBetween(start, end, "milliseconds"); } }