forked from averyfelts/Lofi_Generator
- Set up Next.js project with shadcn/ui and Tailwind CSS - Created audio engine with MembraneSynth drums, FMSynth chords, and ambient noise layers - Implemented 16-step drum sequencer with boom bap patterns - Added jazz chord progressions (ii-V-I, minor key, neo soul) - Built React hook for audio state management - Created UI components: transport controls, volume sliders, layer mixer, beat visualizer - Applied lofi-themed dark color scheme with oklch colors Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
98 lines
2.4 KiB
TypeScript
98 lines
2.4 KiB
TypeScript
import * as Tone from 'tone';
|
|
|
|
export class AmbientLayer {
|
|
private rainNoise: Tone.Noise;
|
|
private vinylNoise: Tone.Noise;
|
|
private rainFilter: Tone.Filter;
|
|
private vinylFilter: Tone.Filter;
|
|
private rainGain: Tone.Gain;
|
|
private vinylGain: Tone.Gain;
|
|
private output: Tone.Gain;
|
|
private lfo: Tone.LFO;
|
|
|
|
constructor(destination: Tone.InputNode) {
|
|
this.output = new Tone.Gain(0.4);
|
|
|
|
// Rain sound - filtered pink noise
|
|
this.rainNoise = new Tone.Noise('pink');
|
|
this.rainFilter = new Tone.Filter({
|
|
frequency: 3000,
|
|
type: 'lowpass',
|
|
rolloff: -24,
|
|
});
|
|
this.rainGain = new Tone.Gain(0.15);
|
|
|
|
// Vinyl crackle - filtered brown noise with modulation
|
|
this.vinylNoise = new Tone.Noise('brown');
|
|
this.vinylFilter = new Tone.Filter({
|
|
frequency: 1500,
|
|
type: 'bandpass',
|
|
Q: 2,
|
|
});
|
|
this.vinylGain = new Tone.Gain(0.1);
|
|
|
|
// LFO for subtle rain intensity variation
|
|
this.lfo = new Tone.LFO({
|
|
frequency: 0.1,
|
|
min: 0.1,
|
|
max: 0.2,
|
|
});
|
|
this.lfo.connect(this.rainGain.gain);
|
|
|
|
// Chain rain: noise -> filter -> gain -> output
|
|
this.rainNoise.connect(this.rainFilter);
|
|
this.rainFilter.connect(this.rainGain);
|
|
this.rainGain.connect(this.output);
|
|
|
|
// Chain vinyl: noise -> filter -> gain -> output
|
|
this.vinylNoise.connect(this.vinylFilter);
|
|
this.vinylFilter.connect(this.vinylGain);
|
|
this.vinylGain.connect(this.output);
|
|
|
|
// Output to destination
|
|
this.output.connect(destination);
|
|
}
|
|
|
|
start(): void {
|
|
this.rainNoise.start();
|
|
this.vinylNoise.start();
|
|
this.lfo.start();
|
|
}
|
|
|
|
stop(): void {
|
|
this.rainNoise.stop();
|
|
this.vinylNoise.stop();
|
|
this.lfo.stop();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
setRainIntensity(intensity: number): void {
|
|
this.rainGain.gain.rampTo(intensity * 0.2, 0.5);
|
|
}
|
|
|
|
setVinylIntensity(intensity: number): void {
|
|
this.vinylGain.gain.rampTo(intensity * 0.15, 0.5);
|
|
}
|
|
|
|
dispose(): void {
|
|
this.rainNoise.stop();
|
|
this.vinylNoise.stop();
|
|
this.lfo.stop();
|
|
this.rainNoise.dispose();
|
|
this.vinylNoise.dispose();
|
|
this.rainFilter.dispose();
|
|
this.vinylFilter.dispose();
|
|
this.rainGain.dispose();
|
|
this.vinylGain.dispose();
|
|
this.lfo.dispose();
|
|
this.output.dispose();
|
|
}
|
|
}
|