import debug from "debug"; import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators.js"; import { NamedNode } from "n3"; import { getTopGraph } from "../../../web/RdfdbSyncedGraph"; import { SyncedGraph } from "../../../web/SyncedGraph"; debug.enable("*"); const log = debug("sequencer"); interface Note { note: string; nonZero: boolean; rowClass?: string; // added in message handler below effectClass: string; effectSettings: { [attr: string]: string }; effectSettingsPairs: EffectSettingsPair[]; // added in message handler below devicesAffected: number; } interface Report { song: string; songUri: NamedNode; // added in message handler below t: number; roundT?: number; // added in message handler below recentFps: number; recentDeltas: number[]; recentDeltasStyle: string[]; // added in message handler below songNotes: Note[]; } interface EffectSettingsPair { effectAttr: string; value: any; } @customElement("light9-sequencer-ui") export class Light9SequencerUi extends LitElement { static styles = [ css` :host { display: block; } td { white-space: nowrap; padding: 0 10px; vertical-align: top; vertical-align: top; text-align: start; } tr.active { background: #151515; } .inactive > * { opacity: 0.5; } .effectSetting { display: inline-block; background: #1b1e21; margin: 1px 3px; } .chart { height: 40px; background: #222; display: inline-flex; align-items: flex-end; } .chart > div { background: #a4a54f; width: 8px; margin: 0 1px; } .number { display: inline-block; min-width: 4em; } `, ]; render() { return [ html`

Sequencer [metrics]

Song

`, this.report ? html` t=${this.report.roundT}

Notes

${this.report.songNotes.map( (item: Note) => html` ` )}
Note Effect class Effect settings Devices affected
${item.effectSettingsPairs.map( (item) => html`
: ${item.value}
` )}
${item.devicesAffected}
` : html`waiting for first report...`, ]; } graph!: SyncedGraph; @property() report!: Report; constructor() { super(); getTopGraph().then((g) => { this.graph = g; const source = new EventSource("./api/updates"); source.addEventListener("message", this.onMessage.bind(this)); }); } onMessage(ev: MessageEvent) { const report = JSON.parse(ev.data) as Report; report.roundT = Math.floor((report.t || 0) * 1000) / 1000; report.recentFps = Math.floor((report.recentFps || 0) * 10) / 10; report.recentDeltasStyle = (report.recentDeltas || []).map((dt) => { const height = Math.min(40, (dt / 0.085) * 20); return `height: ${height}px;`; }); report.songUri = this.graph.Uri(report.song); const fakeUris = (report.songNotes || []).map((obj) => { return { value: obj.note, orig: obj }; }); const s = this.graph.sortedUris(fakeUris); report.songNotes = s.map((u) => { return u.orig; }); (report.songNotes || []).forEach((note) => { note.rowClass = note.nonZero ? "active" : "inactive"; note.effectSettingsPairs = []; const attrs = Object.keys(note.effectSettings); attrs.sort(); attrs.forEach((attr) => { note.effectSettingsPairs.push({ effectAttr: attr, value: note.effectSettings[attr] } as EffectSettingsPair); }); }); this.report = report; } }