Mercurial > hg > ugly-duckling
view src/app/services/audio-player/audio-player.service.ts @ 192:e4f38975c2bc
Introduce interface for reading a file, once again avoiding specific browser implementation.
author | Lucas Thompson <dev@lucas.im> |
---|---|
date | Thu, 23 Mar 2017 15:42:34 +0000 |
parents | ea735ebeed0e |
children | ac57ddba8ba9 |
line wrap: on
line source
import {Injectable, Inject} from '@angular/core'; import {Subject} from "rxjs/Subject"; import {Observable} from "rxjs"; export interface UrlResourceLifetimeManager { createUrlToResource(resource: File | Blob): string; revokeUrlToResource(url: string): void; } export type ResourceReader = (resource: File | Blob) => Promise<ArrayBuffer>; @Injectable() export class AudioPlayerService { private currentObjectUrl: string; private playingStateChange: Subject<boolean>; playingStateChange$: Observable<boolean>; private seeked: Subject<number>; seeked$: Observable<number>; constructor(@Inject(HTMLAudioElement) private audioElement: HTMLAudioElement /* TODO probably shouldn't play audio this way */, @Inject('AudioContext') private audioContext: AudioContext, @Inject('ResourceReader') private readResource: ResourceReader, @Inject( 'UrlResourceLifetimeManager' ) private resourceManager: UrlResourceLifetimeManager) { this.currentObjectUrl = ''; this.playingStateChange = new Subject<boolean>(); this.playingStateChange$ = this.playingStateChange.asObservable(); this.seeked = new Subject<number>(); this.seeked$ = this.seeked.asObservable(); this.audioElement.addEventListener('ended', () => { this.playingStateChange.next(this.isPlaying()); }); this.audioElement.addEventListener('seeked', () => { this.seeked.next(this.audioElement.currentTime); }); } getCurrentTime(): number { return this.audioElement.currentTime; } isPlaying(): boolean { return !this.audioElement.paused; } decodeAudioData(buffer: ArrayBuffer): Promise<AudioBuffer> { return new Promise((res, rej) => this.audioContext.decodeAudioData(buffer, res, rej)); } loadAudioFromUrl(url: string): void { if (this.currentObjectUrl) this.resourceManager.revokeUrlToResource(this.currentObjectUrl); this.currentObjectUrl = url; this.audioElement.pause(); this.audioElement.src = url; this.audioElement.load(); } togglePlaying(): void { if (this.audioElement.readyState >= 2) { this.isPlaying() ? this.audioElement.pause() : this.audioElement.play(); this.playingStateChange.next(this.isPlaying()); } } setVolume(value: number): void { this.audioElement.volume = value; // TODO check bounds? } seekTo(seconds: number): void { if (seconds < 0) { this.audioElement.currentTime = 0; } else if (seconds < this.getDuration()) { this.audioElement.currentTime = seconds; } else { this.audioElement.currentTime = this.getDuration(); } } seekBy(seconds: number): void { // TODO some kind of error handling? this.audioElement.currentTime += seconds; } seekToStart(): void { this.audioElement.currentTime = 0; } seekToEnd(): void { this.audioElement.currentTime = this.getDuration(); } getDuration(): number { return this.audioElement.duration || 0; } }