Files
@ e7e03c203c99
Branch filter:
Location: light9/web/light9-timeline-audio.ts
e7e03c203c99
3.1 KiB
video/MP2T
resize cursor canvas for 400px tall spectros. fix canvas resolution code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | 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 + "%";
}
}
|