import type { BytebeatOptions, BitDepth } from './types' import type { EffectValues } from '../../types/effects' import { compileFormula } from '../../domain/audio/BytebeatCompiler' import { generateSamples } from '../../domain/audio/SampleGenerator' import { exportToWav } from '../../domain/audio/WavExporter' import { AudioPlayer } from '../../domain/audio/AudioPlayer' export class BytebeatGenerator { private sampleRate: number private duration: number private audioBuffer: Float32Array | null = null private audioPlayer: AudioPlayer constructor(options: BytebeatOptions = {}) { this.sampleRate = options.sampleRate ?? 8000 this.duration = options.duration ?? 10 this.audioPlayer = new AudioPlayer({ sampleRate: this.sampleRate, duration: this.duration }) } updateOptions(options: Partial): void { if (options.sampleRate !== undefined) { this.sampleRate = options.sampleRate this.audioBuffer = null } if (options.duration !== undefined) { this.duration = options.duration this.audioBuffer = null } this.audioPlayer.updateOptions({ sampleRate: this.sampleRate, duration: this.duration }) } setFormula(formula: string): void { const result = compileFormula(formula) if (!result.success || !result.compiledFormula) { throw new Error(`Invalid formula: ${result.error}`) } this.audioBuffer = generateSamples(result.compiledFormula, { sampleRate: this.sampleRate, duration: this.duration }) } generate(): Float32Array { if (!this.audioBuffer) { throw new Error('No formula set. Call setFormula() first.') } return this.audioBuffer } setEffects(values: EffectValues): void { this.audioPlayer.setEffects(values) } getPlaybackPosition(): number { return this.audioPlayer.getPlaybackPosition() } play(): void { if (!this.audioBuffer) { throw new Error('No audio buffer. Call setFormula() first.') } this.audioPlayer.play(this.audioBuffer) } onLoopEnd(callback: () => void): void { if (!this.audioBuffer) return this.audioPlayer.setLooping(false) this.audioPlayer.play(this.audioBuffer, callback) } setLooping(loop: boolean): void { this.audioPlayer.setLooping(loop) } scheduleNextTrack(callback: () => void): void { this.audioPlayer.scheduleNextTrack(callback) } pause(): void { this.audioPlayer.pause() } stop(): void { this.audioPlayer.stop() } exportWAV(bitDepth: BitDepth = 8): Blob { if (!this.audioBuffer) { throw new Error('No audio buffer. Call setFormula() first.') } return exportToWav(this.audioBuffer, { sampleRate: this.sampleRate, bitDepth }) } downloadWAV(filename: string = 'bytebeat.wav', bitDepth: BitDepth = 8): void { const blob = this.exportWAV(bitDepth) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = filename a.click() URL.revokeObjectURL(url) } dispose(): void { this.audioPlayer.dispose() } }