Files @ af83aeef8b0a
Branch filter:

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

drewp@bigasterisk.com
fancier spectrograms
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
06da5db2fafe
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
06da5db2fafe
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
06da5db2fafe
06da5db2fafe
4556eebe5d73
06da5db2fafe
4556eebe5d73
06da5db2fafe
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
4556eebe5d73
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;
        }
        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 + "%";
  }
}