sounds, better files

This commit is contained in:
2024-08-18 19:11:41 +00:00
parent 9ed2f70701
commit 08489ed67b
86 changed files with 4226 additions and 86 deletions

View File

@@ -0,0 +1,177 @@
import { MountedFs } from "../FileSystem/MountedFs";
import { FileSystemSource, VirtualFile } from "../FileSystem/types";
import { SoundboardContext } from "./types";
export interface PartiaSoundNodeOptions {
vFile: VirtualFile;
volume?: number;
type: 'buffered' | 'mediaElement';
}
export interface SoundNodeOptions extends PartiaSoundNodeOptions {
volume: number;
}
export abstract class SoundNode {
protected gainNode: GainNode;
protected file?: File;
public options: SoundNodeOptions;
public readonly numberOfInputs = 0;
public readonly numberOfOutputs = 1;
protected activeSources: AudioBufferSourceNode[] = [];
protected audioContext: AudioContext;
protected readonly fs: MountedFs;
constructor(
options: PartiaSoundNodeOptions,
ctx: SoundboardContext,
) {
this.options = {
...options,
volume: options.volume ?? 100
}
this.audioContext = ctx.audioContext;
this.fs = ctx.fs;
this.gainNode = this.audioContext.createGain();
this.gainNode.gain.value = (this.options.volume / 100);
}
get isPlaying() {
return this.activeSources.length > 0;
}
get name() {
return this.options.vFile.name.replace(/\.[^/.]+$/, "");
}
get key() {
return this.options.vFile.path.join('/');
}
protected async preload() {
if (!this.file) {
this.file = await this.fs.download(this.options.vFile);
}
}
connect(destination: AudioNode) {
console.log('connecting', this, destination);
this.gainNode.connect(destination);
}
disconnect(destinationNode?: AudioNode) {
if (destinationNode) {
this.gainNode.disconnect(destinationNode);
} else {
this.gainNode.disconnect();
}
}
setVolume(volume: number) {
this.gainNode.gain.value = volume / 100;
}
getVolume() {
return this.gainNode.gain.value * 100;
}
abstract play(): Promise<void>;
abstract stop(): Promise<void>;
}
export class BufferedSoundNode extends SoundNode {
private buffer?: AudioBuffer;
async preload() {
await super.preload();
if (!this.buffer) {
const arrayBuffer = await this.file!.arrayBuffer();
this.buffer = await this.audioContext.decodeAudioData(arrayBuffer);
}
}
async play() {
await this.preload();
const source = this.audioContext.createBufferSource();
source.buffer = this.buffer!;
source.connect(this.gainNode);
source.start();
this.audioContext.resume();
this.activeSources.push(source);
source.addEventListener('ended', () => {
const index = this.activeSources.indexOf(source);
if (index !== -1) {
this.activeSources.splice(index, 1);
}
});
}
async stop() {
this.activeSources.forEach((source) => source.stop());
this.activeSources = [];
}
}
export class MediaElementSoundNode extends SoundNode {
private mediaElement?: HTMLMediaElement;
private source?: MediaElementAudioSourceNode;
private _isPlaying = false;
async preload() {
await super.preload();
if (!this.mediaElement) {
this.mediaElement = document.createElement('audio');
this.mediaElement.src = URL.createObjectURL(this.file!);
this.source = this.audioContext.createMediaElementSource(this.mediaElement);
this.source.connect(this.gainNode);
this.audioContext.resume();
this.mediaElement.addEventListener('ended', ()=> {
this._isPlaying = false;
});
this.mediaElement.addEventListener('playing', (event) => {
console.log(event);
this._isPlaying = true;
});
this.mediaElement.addEventListener('pause', () => {
this._isPlaying = false;
});
}
}
get isPlaying() {
return this._isPlaying;
}
get currentTime() {
return this.mediaElement!.currentTime;
}
get duration() {
return this.mediaElement!.duration;
}
async play() {
await this.preload();
await this.mediaElement?.play();
}
async stop() {
await this.mediaElement?.pause();
}
pause() {
this.mediaElement?.pause();
}
resume() {
this.mediaElement?.play();
}
seek(time: number) {
if (this.mediaElement) {
this.mediaElement.currentTime = time;
}
}
}