apphouse
Version:
Component library for React that uses observable state management and theme-able components.
168 lines (147 loc) • 3.98 kB
text/typescript
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;
}
};
}