import * as Tone from 'tone'; import { BrassType, Genre } from '@/types/audio'; import { getRandomBrassPattern } from './patterns'; export class BrassEngine { private synth: Tone.Synth | null = null; private sequence: Tone.Sequence | null = null; private pattern: (string | null)[]; private output: Tone.Gain; private filter: Tone.Filter; private reverb: Tone.Reverb; private currentType: BrassType = 'trumpet'; private genre: Genre = 'hiphop'; constructor(destination: Tone.InputNode) { this.output = new Tone.Gain(0.4); this.filter = new Tone.Filter({ frequency: 3000, type: 'lowpass', rolloff: -12, }); this.reverb = new Tone.Reverb({ decay: 2, wet: 0.3, }); this.filter.connect(this.reverb); this.reverb.connect(this.output); this.output.connect(destination); this.pattern = getRandomBrassPattern(this.genre); this.createSynth('trumpet'); } private createSynth(type: BrassType): void { if (this.synth) { this.synth.dispose(); } const configs: Record> = { trumpet: { oscillator: { type: 'sawtooth' }, envelope: { attack: 0.05, decay: 0.2, sustain: 0.6, release: 0.3 }, }, horn: { oscillator: { type: 'sine' }, envelope: { attack: 0.1, decay: 0.3, sustain: 0.7, release: 0.5 }, }, 'synth-brass': { oscillator: { type: 'fatsawtooth', spread: 20, count: 3 } as Tone.OmniOscillatorOptions, envelope: { attack: 0.02, decay: 0.3, sustain: 0.5, release: 0.2 }, }, orchestra: { oscillator: { type: 'sawtooth' }, envelope: { attack: 0.15, decay: 0.4, sustain: 0.8, release: 0.6 }, }, }; this.synth = new Tone.Synth(configs[type]); this.synth.volume.value = -8; this.synth.connect(this.filter); this.currentType = type; } setInstrument(type: BrassType): void { this.createSynth(type); } setGenre(genre: Genre): void { this.genre = genre; this.pattern = getRandomBrassPattern(genre); if (this.sequence) { this.createSequence(); } } createSequence(): void { if (this.sequence) { this.sequence.dispose(); } const steps = Array.from({ length: 16 }, (_, i) => i); this.sequence = new Tone.Sequence( (time, step) => { const note = this.pattern[step]; if (note && this.synth) { this.synth.triggerAttackRelease(note, '8n', time, 0.6); } }, steps, '16n' ); this.sequence.start(0); } randomize(): void { this.pattern = getRandomBrassPattern(this.genre); if (this.sequence) { this.createSequence(); } } setVolume(volume: number): void { this.output.gain.rampTo(volume, 0.1); } mute(muted: boolean): void { this.output.gain.rampTo(muted ? 0 : 0.4, 0.1); } dispose(): void { this.sequence?.dispose(); this.synth?.dispose(); this.filter.dispose(); this.reverb.dispose(); this.output.dispose(); } }