Files @ e7e03c203c99
Branch filter:

Location: light9/web/light9-timeline-audio.ts

drewp@bigasterisk.com
resize cursor canvas for 400px tall spectros. fix canvas resolution code
import { debug } from "debug";
import { html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { NamedNode } from "n3";
import { getTopGraph } from "./RdfdbSyncedGraph";
import { SyncedGraph } from "./SyncedGraph";

const log = debug("audio");

export interface Zoom {
  duration: number | null;
  t1: number;
  t2: number;
}

function nodeHasChanged(newVal?: NamedNode, oldVal?: NamedNode): boolean {
  if (newVal === undefined && oldVal === undefined) {
    return false;
  }
  if (newVal === undefined || oldVal === undefined) {
    return true;
  }
  return !newVal.equals(oldVal);
}

// (potentially-zoomed) spectrogram view
@customElement("light9-timeline-audio")
export class Light9TimelineAudio extends LitElement {
  graph!: SyncedGraph;
  graphReady: Promise<void>;
  render() {
    return html`
      <style>
        :host {
          display: block;
          /* shouldn't be seen, but black is correct for 'no
         audio'. Maybe loading stripes would be better */
          background: #202322;
          outline: 1px solid #333;
        }
        div {
          width: 100%;
          height: 100%;
          overflow: hidden;
        }
        img {
          height: 100%;
          position: relative;
          transition: left 0.1s linear;
        }
      </style>
      <div>
        <img src=${this.imgSrc} style="width: ${this.imgWidth}; left: ${this.imgLeft}" />
      </div>
    `;
  }
  @property({ hasChanged: nodeHasChanged }) show!: NamedNode;
  @property({ hasChanged: nodeHasChanged }) song!: NamedNode;
  @property() zoom: Zoom = { duration: null, t1: 0, t2: 1 };
  @state() imgSrc: string = "#";
  @state() imgWidth: string = "0"; // css
  @state() imgLeft: string = "0"; // css

  constructor() {
    super();

    this.graphReady = getTopGraph().then((g) => {
      this.graph = g;
    });
  }

  async updated(changedProperties: PropertyValues) {
    super.updated(changedProperties);
    if (changedProperties.has("song") || changedProperties.has("show")) {
      await this.graphReady;
      if (this.song && this.show) {
        this.graph.runHandler(this.setImgSrc.bind(this), "timeline-audio " + this.song.value);
      }
    }
    if (changedProperties.has("zoom")) {
      this.imgWidth = this._imgWidth(this.zoom);
      this.imgLeft = this._imgLeft(this.zoom);
    }
  }

  setImgSrc() {
    try {
      var root = this.graph.stringValue(this.show, this.graph.Uri(":spectrogramUrlRoot"));
    } catch (e) {
      return;
    }

    try {
      var filename = this.graph.stringValue(this.song, this.graph.Uri(":songFilename"));
    } catch (e) {
      return;
    }

    this.imgSrc = root + "/" + filename.replace(".wav", ".png").replace(".ogg", ".png");
    log(`imgSrc ${this.imgSrc}`);
  }

  _imgWidth(zoom: Zoom): string {
    if (!zoom.duration) {
      return "100%";
    }

    return 100 / ((zoom.t2 - zoom.t1) / zoom.duration) + "%";
  }
  _imgLeft(zoom: Zoom): string {
    if (!zoom.duration) {
      return "0";
    }

    var percentPerSec = 100 / (zoom.t2 - zoom.t1);
    return -percentPerSec * zoom.t1 + "%";
  }
}