import {FileSystemFile, Track, TrackMeta, PlaylistItem} from '../models'; import {computed, Injectable, Signal} from '@angular/core'; import {IndexedArray} from '../lib'; import {AnalyserService} from './analyser.service'; @Injectable({providedIn: 'root'}) export class AudioService { protected context: AudioContext = new AudioContext({ sampleRate: 44100 }); public analyser: AnalyserService = new AnalyserService(this.context); protected playlist: IndexedArray = new IndexedArray(); 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.addItem(new Track( new FileSystemFile('1.mp3', 'audio/1.mp3', 0), new TrackMeta(), 0, 'mp3', 128 )); this.addItem(new Track( new FileSystemFile('2.mp3', 'audio/2.mp3', 0), new TrackMeta(), 0, 'mp3', 128 )); this.addItem(new Track( new FileSystemFile('3.mp3', 'audio/3.mp3', 0), new TrackMeta(), 0, 'mp3', 128 )); } addItem(track: Track) { const item = new PlaylistItem(track, this); this.analyser.connectAudio(item); this.playlist.add(item); } /** * Remove a track from the current playlist. * If we remove the current track, then, if it was playing, start playing the newly "current" item * @param item */ async removeItem(item: PlaylistItem) { this.playlist.remove(item); // If the item was not the currently selected track, then we're done. if (item !== this.current()) return; const wasPlaying = item.isPlaying(); await item.stop(); item.removeMedia(); // Remove the media so that if it is still being downloaded, we stop that request and unblock loading any other tracks // If we were playing the removed item, start playing the next item in the playlist (if there is one) if (wasPlaying) await this.current()?.play(); } async play(): Promise { // Make sure the audio context isn't blocked by the browser (disabled autoplay etc) if (this.context.state === 'suspended') await this.context.resume(); await this.current()?.play(); this.analyser.start(); } async pause(): Promise { await this.current()?.pause(); } async stop(): Promise { this.analyser.stop(); await this.current()?.stop(); } async next(): Promise { const current = this.current(); if (!current) return; const wasPlaying = current.isPlaying(); await current.stop(); const next = this.playlist.next(true); // If the track we have just moved on from was still doing the initial download, then reset the