| |
| |
| |
| |
| |
|
|
| import {LitElement, css, html} from 'lit'; |
| import {customElement, property} from 'lit/decorators.js'; |
| import {Analyser} from './analyser'; |
|
|
| @customElement('gdm-live-audio-visuals') |
| export class GdmLiveAudioVisuals extends LitElement { |
| private inputAnalyser: Analyser; |
| private outputAnalyser: Analyser; |
|
|
| private _outputNode: AudioNode; |
|
|
| @property() |
| set outputNode(node: AudioNode) { |
| this._outputNode = node; |
| this.outputAnalyser = new Analyser(this._outputNode); |
| } |
|
|
| get outputNode() { |
| return this._outputNode; |
| } |
|
|
| private _inputNode: AudioNode; |
|
|
| @property() |
| set inputNode(node: AudioNode) { |
| this._inputNode = node; |
| this.inputAnalyser = new Analyser(this._inputNode); |
| } |
|
|
| get inputNode() { |
| return this._inputNode; |
| } |
|
|
| private canvas: HTMLCanvasElement; |
| private canvasCtx: CanvasRenderingContext2D; |
|
|
| static styles = css` |
| canvas { |
| width: 400px; |
| aspect-ratio: 1 / 1; |
| } |
| `; |
|
|
| connectedCallback() { |
| super.connectedCallback(); |
| this.visualize(); |
| } |
|
|
| private visualize() { |
| if (this.canvas && this.outputAnalyser) { |
| const canvas = this.canvas; |
| const canvasCtx = this.canvasCtx; |
|
|
| const WIDTH = canvas.width; |
| const HEIGHT = canvas.height; |
|
|
| canvasCtx.clearRect(0, 0, WIDTH, HEIGHT); |
| canvasCtx.fillStyle = '#1f2937'; |
| canvasCtx.fillRect(0, 0, WIDTH, HEIGHT); |
|
|
| const barWidth = WIDTH / this.outputAnalyser.data.length; |
| let x = 0; |
|
|
| const inputGradient = canvasCtx.createLinearGradient(0, 0, 0, HEIGHT); |
| inputGradient.addColorStop(1, '#D16BA5'); |
| inputGradient.addColorStop(0.5, '#E78686'); |
| inputGradient.addColorStop(0, '#FB5F5F'); |
| canvasCtx.fillStyle = inputGradient; |
|
|
| this.inputAnalyser.update(); |
|
|
| for (let i = 0; i < this.inputAnalyser.data.length; i++) { |
| const barHeight = this.inputAnalyser.data[i] * (HEIGHT / 255); |
| canvasCtx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight); |
| x += barWidth; |
| } |
|
|
| canvasCtx.globalCompositeOperation = 'lighter'; |
|
|
| const outputGradient = canvasCtx.createLinearGradient(0, 0, 0, HEIGHT); |
| outputGradient.addColorStop(1, '#3b82f6'); |
| outputGradient.addColorStop(0.5, '#10b981'); |
| outputGradient.addColorStop(0, '#ef4444'); |
| canvasCtx.fillStyle = outputGradient; |
|
|
| x = 0; |
| this.outputAnalyser.update(); |
|
|
| for (let i = 0; i < this.outputAnalyser.data.length; i++) { |
| const barHeight = this.outputAnalyser.data[i] * (HEIGHT / 255); |
| canvasCtx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight); |
| x += barWidth; |
| } |
| } |
| requestAnimationFrame(() => this.visualize()); |
| } |
|
|
| private firstUpdated() { |
| this.canvas = this.shadowRoot!.querySelector('canvas'); |
| this.canvas.width = 400; |
| this.canvas.height = 400; |
| this.canvasCtx = this.canvas.getContext('2d'); |
| } |
|
|
| private render() { |
| return html`<canvas></canvas>`; |
| } |
| } |
|
|
| declare global { |
| interface HTMLElementTagNameMap { |
| 'gdm-live-audio-visuals': GdmLiveAudioVisuals; |
| } |
| } |
|
|