UNPKG

apphouse

Version:

Component library for React that uses observable state management and theme-able components.

168 lines (147 loc) 3.98 kB
import { action, computed, makeObservable, observable } from "mobx"; import AudioSource from "./AudioSource"; // import ImpulseResponseAudio from "../samples/impulse-responses/echohall.wav"; export default class Player extends AudioSource { audioBuffer: AudioBuffer | null; playing: boolean; progress: number; startTime: number; blob?: Blob; constructor() { super(); this.playing = false; this.audioBuffer = null; this.progress = 0; this.startTime = 0; this.blob = undefined; makeObservable(this, { audioBuffer: observable, currentProgress: computed, currentTime: computed, duration: computed, hasAudioBuffer: computed, isPlaying: computed, loadMedia: action, play: action, playing: observable, progress: observable, startTime: observable, blob: observable, startTimer: action, stop: action, togglePlayPause: action, setBlob: action, }); } get isPlaying() { return this.playing === true; } get hasAudioBuffer() { return this.audioBuffer !== null; } get duration() { return this.audioBuffer?.duration || 0; } get currentTime() { return this.progress; } get currentProgress() { return (100 * this.progress) / (this.duration || 1); } setBlob = (blob: Blob) => { this.blob = blob; }; loadMedia = async (filepath: string) => { this.initContext(); this.audioBuffer = await this.initWithMediaFilepath(filepath); return true; }; togglePlayPause = (reverb?: boolean) => { if (this.audioBuffer !== null) { if (this.isSuspended && this.audioContext) { this.audioContext.resume(); } // play or pause track depending on state if (this.isPlaying === false) { if (reverb) { this.playWithReverb(); } else { this.play(); } } else { this.stop(); } } else { console.warn("Must have an audio buffer to play"); } }; stop = () => { if (this.source) { this.progress = 0; if (this.audioBuffer && this.audioContext) { if (this.playing === true) { this.source.stop(); this.playing = false; } } } }; play = () => { if (this.audioBuffer && this.audioContext) { this.source = this.audioContext.createBufferSource(); this.source.buffer = this.audioBuffer; this.source .connect(this.baseEq) .connect(this.midEq) .connect(this.trebleEq) .connect(this.gainNode) .connect(this.analyzerNode) .connect(this.audioContext.destination); this.source.start(0); this.playing = true; this.startTimer(); this.source.onended = () => { this.stop(); }; return this.source; } return null; }; playWithReverb = async () => { if (this.audioBuffer && this.audioContext) { this.source = this.audioContext.createBufferSource(); this.source.buffer = this.audioBuffer; let convolver = this.audioContext.createConvolver(); // convolver.buffer = await this.audioContext.decodeAudioData( // await (await fetch(ImpulseResponseAudio)).arrayBuffer(), // ); this.source .connect(convolver) .connect(this.gainNode) .connect(this.audioContext.destination); this.source.start(0); this.playing = true; this.startTimer(); this.source.onended = () => { this.stop(); }; } }; startTimer = () => { if (this.startTime === 0) { if (this.audioContext) { this.startTime = this.audioContext.currentTime; } } if (this.isPlaying) { if (this.audioContext) { this.progress = this.audioContext.currentTime - this.startTime; } requestAnimationFrame(this.startTimer); } else { this.startTime = 0; this.progress = 0; } }; }