Changeset - 1dc96b97a544
[Not reviewed]
default
0 5 0
drewp@bigasterisk.com - 3 years ago 2022-06-03 07:41:13
drewp@bigasterisk.com
two zoomed spectrogram views in asco
5 files changed with 95 insertions and 49 deletions:
0 comments (0 inline, 0 general)
light9/ascoltami/Light9AscoltamiUi.ts
Show inline comments
 
import debug from "debug";
 
import { html, LitElement } from "lit";
 
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";
 
export { RdfdbSyncedGraph } from "../web/RdfdbSyncedGraph";
 
export { Light9TimelineAudio } from "../web/light9-timeline-audio";
 
import { classMap } from "lit/directives/class-map.js";
 
import { TimingUpdate } from "./main";
 
import { Zoom } from "../web/light9-timeline-audio";
 

	
 
debug.enable("*");
 
const log = debug("asco");
 

	
 
function byId(id: string): HTMLElement {
 
  return document.getElementById(id)!;
 
@@ -25,22 +26,41 @@ async function postJson(url: string, jsB
 
@customElement("light9-ascoltami-ui")
 
export class Light9AscoltamiUi extends LitElement {
 
  graph!: SyncedGraph;
 
  times!: { intro: number; post: number };
 
  @property() nextText: string = "";
 
  @property() isPlaying: boolean = false;
 
  @property() show: NamedNode | null = null;
 
  @property() song: NamedNode | null = null;
 
  @property() t: number = 0;
 
  @property() currentDuration: number = 0;
 
  @property() zoom: Zoom;
 
  @property() overviewZoom: Zoom;
 
  static styles = [
 
    css`
 
      .timeRow {
 
        margin: 14px;
 
      }
 
      light9-timeline-audio {
 
        height: 80px;
 
      }
 
    `,
 
  ];
 
  render() {
 
    return html`<rdfdb-synced-graph></rdfdb-synced-graph>
 

	
 
      <link rel="stylesheet" href="./style.css" />
 

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

	
 
      <div class="timeRow">
 
        <div id="timeSlider"></div>
 
        <light9-timeline-audio .show=${this.show} .song=${this.song} .zoom=${this.overviewZoom}></light9-timeline-audio>
 
        <light9-timeline-audio .show=${this.show} .song=${this.song} .zoom=${this.zoom}></light9-timeline-audio>
 
      </div>
 

	
 
      <div class="commands">
 
        <button id="cmd-stop" @click=${this.onCmdStop} class="playMode ${classMap({ active: !this.isPlaying })}">
 
          <strong>Stop</strong>
 
          <div class="key">s</div>
 
        </button>
 
        <button id="cmd-play" @click=${this.onCmdPlay} class="playMode ${classMap({ active: this.isPlaying })}">
 
@@ -103,19 +123,16 @@ export class Light9AscoltamiUi extends L
 
        return false;
 
      }
 
      return true;
 
    });
 
  }
 

	
 
  currentDurationChanged(newDuration: number): void {
 
    this.currentDuration = newDuration;
 
  }
 

	
 
  async musicSetup() {
 
    // shoveled over from the vanillajs version
 
    const config = await (await fetch("api/config")).json();
 
    this.show = new NamedNode(config.show);
 
    this.times = config.times;
 
    document.title = document.title.replace("{{host}}", config.host);
 
    const h1 = document.querySelector("h1")!;
 
    h1.innerText = h1.innerText.replace("{{host}}", config.host);
 

	
 
    byId("nav").innerText = navigator.userAgent;
 
@@ -127,20 +144,22 @@ export class Light9AscoltamiUi extends L
 
    byId("updateReq").innerText = "" + updateFreq;
 

	
 
    (window as any).finishOldStyleSetup(this.times, updateFreq, (data: TimingUpdate) => {
 
      this.nextText = data.next;
 
      this.isPlaying = data.playing;
 
      this.currentDuration = data.duration;
 
      this.song = new NamedNode(data.song);
 
      this.overviewZoom = { duration: data.duration, t1: 0, t2: data.duration };
 
      this.zoom = { duration: data.duration, t1: data.t - 2, t2: data.t + 20 };
 
    });
 
  }
 

	
 
  constructor() {
 
    super();
 
    this.bindKeys();
 
    //   byId("cmd-stop").addEventListener("click", (ev: Event) =>
 
    // );
 
    this.zoom = this.overviewZoom = { duration: null, t1: 0, t2: 1 };
 

	
 
    getTopGraph().then((g) => {
 
      this.graph = g;
 
      this.musicSetup(); // async
 
    });
 
  }
light9/ascoltami/index.html
Show inline comments
 
@@ -33,15 +33,12 @@
 
            <strong>Update freq:</strong> requested <span id="updateReq"></span>, actual <span id="updateActual"></span> <strong>States:</strong>
 
            <span id="states"></span>
 
          </td>
 
        </tr>
 
      </table>
 

	
 
      <div class="timeRow">
 
        <div id="timeSlider"></div>
 
      </div>
 
    </div>
 

	
 
    <hr />
 
    new ui is here
 
    <light9-ascoltami-ui></light9-ascoltami-ui>
 
    <hr />
light9/ascoltami/webapp.py
Show inline comments
 
@@ -7,13 +7,13 @@ from typing import cast
 

	
 
import cyclone.web
 
import cyclone.websocket
 
from cycloneerr import PrettyErrorHandler
 
from light9.metrics import metricsRoute
 
from light9.namespaces import L9
 
from light9.showconfig import getSongsFromShow, songOnDisk
 
from light9.showconfig import getSongsFromShow, showUri, songOnDisk
 
from rdflib import URIRef
 
from twisted.internet import reactor
 
from twisted.internet.interfaces import IReactorTime
 

	
 
log = logging.getLogger()
 
_songUris = {}  # locationUri : song
 
@@ -33,12 +33,13 @@ class config(cyclone.web.RequestHandler)
 

	
 
    def get(self):
 
        self.set_header("Content-Type", "application/json")
 
        self.write(
 
            json.dumps(dict(
 
                host=socket.gethostname(),
 
                show=str(showUri()),
 
                times={
 
                    # these are just for the web display. True values are on Player.__init__
 
                    'intro': 4,
 
                    'post': 0
 
                })))
 

	
light9/web/light9-timeline-audio.ts
Show inline comments
 
import { debug } from "debug";
 

	
 
import { css, html, LitElement, TemplateResult } from "lit";
 
import { customElement, property } from "lit/decorators.js";
 
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
 
import { customElement, property, state } from "lit/decorators.js";
 
import { NamedNode } from "n3";
 
import { loadConfigFromFile } from "vite";
 
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;
 
  render() {
 
    return html`
 
      <style>
 
        :host {
 
          display: block;
 
          /* shouldn't be seen, but black is correct for 'no
 
@@ -22,62 +43,73 @@ export class Light9TimelineAudio extends
 
          height: 100%;
 
          overflow: hidden;
 
        }
 
        img {
 
          height: 100%;
 
          position: relative;
 
          transition: left .1s linear;
 

	
 
        }
 
      </style>
 
      <div>
 
        <img src="{{imgSrc}}" style="width: {{imgWidth}} ; left: {{imgLeft}}" />
 
        <img src=${this.imgSrc} style="width: ${this.imgWidth}; left: ${this.imgLeft}" />
 
      </div>
 
    `;
 
  }
 
  //    properties= {
 
  //        graph: {type: Object, notify: true},
 
  //        show: {type: String, notify: true},
 
  //        song: {type: String, notify: true},
 
  //        zoom: {type: Object, notify: true},
 
  //        imgSrc: { type: String, notify: true},
 
  //        imgWidth: { computed: '_imgWidth(zoom)' },
 
  //        imgLeft: { computed: '_imgLeft(zoom)' },
 
  //    }
 
  //    observers= [
 
  //        'setImgSrc(graph, show, song)'
 
  //    ]
 
  ready() {
 
    this.zoom = { duration: 0 };
 
  @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();
 

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

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

	
 
  setImgSrc() {
 
    graph.runHandler(
 
      function () {
 
        try {
 
          var root = this.graph.stringValue(this.graph.Uri(this.show), this.graph.Uri(":spectrogramUrlRoot"));
 
        } catch (e) {
 
          return;
 
        }
 
    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;
 
        }
 
    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");
 
      }.bind(this),
 
      "timeline-audio " + this.song
 
    );
 
    this.imgSrc = root + "/" + filename.replace(".wav", ".png").replace(".ogg", ".png");
 
    log(`imgSrc ${this.imgSrc}`);
 
  }
 
  _imgWidth(zoom) {
 

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

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

	
 
    var percentPerSec = 100 / (zoom.t2 - zoom.t1);
 
    return -percentPerSec * zoom.t1 + "%";
light9/web/style.css
Show inline comments
 
@@ -95,15 +95,12 @@ div.keys {
 
}
 

	
 
.currentSong button {
 
  background: #a90707;
 
}
 

	
 
.timeRow {
 
  margin: 14px;
 
}
 

	
 
.stalled {
 
  opacity: 0.5;
 
}
 

	
 
.num {
0 comments (0 inline, 0 general)