Mercurial > hg > ugly-duckling
diff src/app/services/audio-player/audio-player.service.ts @ 193:ac57ddba8ba9
Provide an observable in the audio service for when new audio has been loaded. The handling of errors is currently undesirable, using optional fields on the returned object. I couldn't figure out the proper Observable error flow without closing the stream.
author | Lucas Thompson <dev@lucas.im> |
---|---|
date | Thu, 23 Mar 2017 15:44:32 +0000 |
parents | e4f38975c2bc |
children | 3ef1aaa2ebed |
line wrap: on
line diff
--- a/src/app/services/audio-player/audio-player.service.ts Thu Mar 23 15:42:34 2017 +0000 +++ b/src/app/services/audio-player/audio-player.service.ts Thu Mar 23 15:44:32 2017 +0000 @@ -8,6 +8,19 @@ } export type ResourceReader = (resource: File | Blob) => Promise<ArrayBuffer>; + +export interface AudioResource { + samples: AudioBuffer; + url: string; + mimeType: string; +} + +export interface AudioResourceError { + message: string; +} + +export type AudioLoadResponse = AudioResource | AudioResourceError; + @Injectable() export class AudioPlayerService { @@ -16,6 +29,8 @@ playingStateChange$: Observable<boolean>; private seeked: Subject<number>; seeked$: Observable<number>; + private audioLoaded: Subject<AudioLoadResponse>; + audioLoaded$: Observable<AudioLoadResponse>; constructor(@Inject(HTMLAudioElement) private audioElement: HTMLAudioElement /* TODO probably shouldn't play audio this way */, @Inject('AudioContext') private audioContext: AudioContext, @@ -34,6 +49,8 @@ this.audioElement.addEventListener('seeked', () => { this.seeked.next(this.audioElement.currentTime); }); + this.audioLoaded = new Subject<AudioLoadResponse>(); + this.audioLoaded$ = this.audioLoaded.asObservable(); } getCurrentTime(): number { @@ -44,17 +61,36 @@ return !this.audioElement.paused; } - decodeAudioData(buffer: ArrayBuffer): Promise<AudioBuffer> { - return new Promise((res, rej) => this.audioContext.decodeAudioData(buffer, res, rej)); - } - loadAudioFromUrl(url: string): void { + loadAudio(resource: File | Blob): void { if (this.currentObjectUrl) this.resourceManager.revokeUrlToResource(this.currentObjectUrl); + const url: string = this.resourceManager.createUrlToResource(resource); this.currentObjectUrl = url; this.audioElement.pause(); this.audioElement.src = url; this.audioElement.load(); + + const decode: (buffer: ArrayBuffer) => Promise<AudioBuffer> = buffer => { + return new Promise( + (res, rej) => this.audioContext.decodeAudioData(buffer, res, rej) + ); + }; + + this.readResource(resource) + .then(decode) + .then(val => { + this.audioLoaded.next({ + samples: val, + url: url, + mimeType: resource.type + }); + }) + .catch(err => { + this.audioLoaded.next({ + message: err.message + }); + }); } togglePlaying(): void {