109 lines
3.0 KiB
TypeScript
109 lines
3.0 KiB
TypeScript
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<BytebeatOptions>): 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()
|
|
}
|
|
} |