diff --git a/src/app/models/PlaylistItem.ts b/src/app/models/PlaylistItem.ts index b614d24..0186c9f 100644 --- a/src/app/models/PlaylistItem.ts +++ b/src/app/models/PlaylistItem.ts @@ -1,4 +1,5 @@ import {Track} from './Track'; +import {Signal, signal, WritableSignal} from '@angular/core'; enum STATE { Playing, @@ -7,46 +8,71 @@ enum STATE { } export class PlaylistItem { - protected element: HTMLAudioElement; + protected element: HTMLAudioElement | null = null; protected state: STATE = STATE.Stopped; + protected initialised: boolean = false; + public currentTime: WritableSignal = signal(0); + public duration: WritableSignal = signal(0); constructor( public track: Track, protected context: AudioContext ) { - this.element = this.createElement(this.track.file.filepath, this.track.getMimeType()) - context.createMediaElementSource(this.element).connect(context.destination); } - private createElement(filename: string, type: string) { - const element = document.createElement('audio'); + public initialise(): void { + if (this.initialised) return; + + this.initialised = true; + this.element = document.createElement('audio'); const source = document.createElement('source'); - source.src = filename; - source.type = type; - element.appendChild(source); - return element; + source.src = this.track.file.filepath; + source.type = this.track.getMimeType(); + this.element.appendChild(source); + for (const eventType of ['progress', 'durationchange', 'ended', 'loadeddata', 'pause', 'play', 'seeking', 'seeked', 'stalled', 'volumechange']) { + this.element.addEventListener(eventType, (event) => { + console.log(eventType, event); + }); + } + + this.element.addEventListener('timeupdate', () => { + this.currentTime.set(this.element?.currentTime || 0); + }) + this.element.addEventListener('durationchange', () => { + this.duration.set(this.element?.duration || 0); + }) + this.context.createMediaElementSource(this.element).connect(this.context.destination); } public async play() { + this.initialise(); if (this.state === STATE.Stopped || this.state === STATE.Paused) { - await this.element.play(); + await this.element?.play(); this.state = STATE.Playing; return; } } public async stop() { - this.element.currentTime = 0; - this.element.pause(); + this.initialise(); + this.element?.pause(); + this.setTime(); this.state = STATE.Stopped; } + protected setTime() { + if (this.element) { + this.element.currentTime = 0; + } + } + public async pause() { + this.initialise(); if (this.state === STATE.Paused) { return this.play(); } if (this.state === STATE.Playing) { - this.element.pause(); + this.element?.pause(); this.state = STATE.Paused; return; } diff --git a/src/app/pages/homepage/homepage.html b/src/app/pages/homepage/homepage.html index 4711d68..b78e787 100644 --- a/src/app/pages/homepage/homepage.html +++ b/src/app/pages/homepage/homepage.html @@ -16,3 +16,5 @@ Home {{ item.track.file.filename }} } + +
{{ audio.current()?.currentTime()}} / {{audio.current()?.duration()}} -> {{ audio.progress()}} %
diff --git a/src/app/services/audio.service.ts b/src/app/services/audio.service.ts index 90f7937..b82ef11 100644 --- a/src/app/services/audio.service.ts +++ b/src/app/services/audio.service.ts @@ -1,5 +1,5 @@ import {FileSystemFile, Track, TrackMeta, PlaylistItem} from '../models'; -import {Signal} from '@angular/core'; +import {computed, Signal} from '@angular/core'; import {IndexedArray} from '../lib'; export class AudioService { @@ -8,6 +8,12 @@ export class AudioService { public list: Signal = this.playlist.playlist; public current: Signal = this.playlist.current; public index: Signal = this.playlist.index; + public progress: Signal = computed(() => { + const current = this.current(); + if (!current) return 0; + if (current.duration() === 0) return 0; + return current.currentTime() / current.duration(); + }) constructor() { this.context = new AudioContext();