Files @ f1df317f7c4c
Branch filter:

Location: light9/light9/effect/sequencer/web/Light9SequencerUi.ts

drewp@bigasterisk.com
effectSequencer mostly ported to asyncio
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` <rdfdb-synced-graph></rdfdb-synced-graph>

        <h1>Sequencer <a href="metrics">[metrics]</a></h1>

        <h2>Song</h2>`,
      this.report
        ? html`

      <resource-display .uri=${this.graph.Uri(this.report.song)}"></resource-display>
      t=${this.report.roundT}

      <h3>Notes</h3>

      <table>
        <tr>
          <th>Note</th>
          <th>Effect class</th>
          <th>Effect settings</th>
          <th>Devices affected</th>
        </tr>
        ${this.report.songNotes.map(
          (item: Note) => html`
            <tr class="${item.rowClass}">
              <td><resource-display .uri="${this.graph.Uri(item.note)}"></resource-display></td>
              <td><resource-display .uri="${this.graph.Uri(item.effectClass)}"></resource-display></td>
              <td>
                ${item.effectSettingsPairs.map(
                  (item) => html`
                    <div>
                      <span class="effectSetting">
                        <resource-display .uri="${this.graph.Uri(item.effectAttr)}"></resource-display>:
                        <span class="number">${item.value}</span>
                      </span>
                    </div>
                  `
                )}
              </td>
              <td>${item.devicesAffected}</td>
            </tr>
          `
        )}
      </table>
    `
        : 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;
  }
}