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 {