From 8a25563b3d6f52e93d75467d449b62ac37bdc185 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 13:37:32 +0000 Subject: [PATCH] feat: enhance synth with glide, sub-osc, fine-tune and randomize - Added Glide (Portamento) control to Oscillator Node - Added Sub-Oscillator toggle (mixes in an octave down) to Oscillator Node - Added Fine Tuning (Cents) control to Oscillator Node - Added 'Randomize Parameters' button to Bitcrusher Node - Updated JOURNAL.md with completion status and new expansion ideas Co-authored-by: kkd16 <112675076+kkd16@users.noreply.github.com> --- projects/web-synth-9x1a/JOURNAL.md | 13 +++++ .../web-synth-9x1a/src/audio/nodes/sources.ts | 51 ++++++++++++++++--- .../src/components/nodes/BitcrusherNode.tsx | 5 +- .../src/components/nodes/OscillatorNode.tsx | 40 ++++++++++++++- projects/web-synth-9x1a/src/store.ts | 3 ++ 5 files changed, 102 insertions(+), 10 deletions(-) diff --git a/projects/web-synth-9x1a/JOURNAL.md b/projects/web-synth-9x1a/JOURNAL.md index f0ec8a08..507e09dd 100644 --- a/projects/web-synth-9x1a/JOURNAL.md +++ b/projects/web-synth-9x1a/JOURNAL.md @@ -170,3 +170,16 @@ - [x] Add "Randomize Parameters" button to Chorus Node - [x] Add "Randomize Parameters" button to Tremolo Node - [x] Add Bypass switch to Tremolo Node + + +## Expansion Phase 13 (New Features) +- [x] Add Glide (Portamento) control to Oscillator Node +- [ ] Add Symmetry/Pulse Width control to Oscillator Node (for Square/Triangle waves) +- [x] Add Sub-Oscillator toggle (mix in an octave down) to Oscillator Node +- [x] Add Fine Tuning (Cents) control to Oscillator Node +- [ ] Implement Bypass switch for Pitch Shift Node (if it existed, or add Pitch Shift node) +- [ ] Add Dry/Wet control to Bitcrusher Node +- [ ] Add Drive control to Gain Node to act as a preamp +- [ ] Add LFO Destination selection in LFO Node (pitch, filter, amp) +- [x] Add 'Randomize Parameters' button to Bitcrusher Node +- [ ] Add a visual meter (VU meter style) to Gain Node diff --git a/projects/web-synth-9x1a/src/audio/nodes/sources.ts b/projects/web-synth-9x1a/src/audio/nodes/sources.ts index 33c7b58c..59d3512c 100644 --- a/projects/web-synth-9x1a/src/audio/nodes/sources.ts +++ b/projects/web-synth-9x1a/src/audio/nodes/sources.ts @@ -5,6 +5,11 @@ export class OscillatorWrapper { public output: GainNode; private baseFreq: number = 440; private octave: number = 0; + private glideTime: number = 0; + private fineTune: number = 0; + private detuneAmt: number = 0; + private subOsc: OscillatorNode; + private subGain: GainNode; constructor(id: string) { const ctx = audioCore.getContext(); @@ -15,6 +20,15 @@ export class OscillatorWrapper { this.output = ctx.createGain(); this.output.gain.value = 1; + this.subOsc = ctx.createOscillator(); + this.subOsc.type = 'square'; + this.subOsc.frequency.value = 220; + this.subOsc.start(); + this.subGain = ctx.createGain(); + this.subGain.gain.value = 0; + this.subOsc.connect(this.subGain); + this.subGain.connect(this.output); + this.node.connect(this.output); audioCore.registerNode(id, this.output); @@ -28,12 +42,23 @@ export class OscillatorWrapper { public setFrequency(freq: number) { this.baseFreq = freq; - this.node.frequency.setValueAtTime(this.baseFreq * Math.pow(2, this.octave), audioCore.getContext().currentTime); + this.node.frequency.linearRampToValueAtTime(this.baseFreq * Math.pow(2, this.octave), audioCore.getContext().currentTime + this.glideTime); + this.subOsc.frequency.linearRampToValueAtTime(this.baseFreq * Math.pow(2, this.octave - 1), audioCore.getContext().currentTime + this.glideTime); + this.subOsc.frequency.linearRampToValueAtTime(this.baseFreq * Math.pow(2, this.octave - 1), audioCore.getContext().currentTime + this.glideTime); } public setOctave(oct: number) { this.octave = oct; - this.node.frequency.setValueAtTime(this.baseFreq * Math.pow(2, this.octave), audioCore.getContext().currentTime); + this.node.frequency.linearRampToValueAtTime(this.baseFreq * Math.pow(2, this.octave), audioCore.getContext().currentTime + this.glideTime); + this.subOsc.frequency.linearRampToValueAtTime(this.baseFreq * Math.pow(2, this.octave - 1), audioCore.getContext().currentTime + this.glideTime); + } + + public setGlideTime(time: number) { + this.glideTime = time; + } + + public setSubOscEnabled(enabled: boolean) { + this.subGain.gain.setValueAtTime(enabled ? 0.5 : 0, audioCore.getContext().currentTime); } public setInvertPhase(invert: boolean) { @@ -41,11 +66,23 @@ export class OscillatorWrapper { } public setDetune(cents: number) { - this.node.detune.setValueAtTime(cents, audioCore.getContext().currentTime); + this.detuneAmt = cents; + this.updateDetune(); + } + + public setFineTune(cents: number) { + this.fineTune = cents; + this.updateDetune(); + } + + private updateDetune() { + this.node.detune.setValueAtTime(this.detuneAmt + this.fineTune, audioCore.getContext().currentTime); } public destroy(id: string) { - this.node.stop(); +this.node.stop(); + if (this.subOsc) { this.subOsc.stop(); this.subOsc.disconnect(); } + if (this.subGain) { this.subGain.disconnect(); } this.node.disconnect(); this.output.disconnect(); audioCore.unregisterNode(id); @@ -75,7 +112,7 @@ export class NoiseWrapper { private generateAndPlayNoise() { if (this.node) { - this.node.stop(); + this.node.stop(); this.node.disconnect(); } @@ -126,7 +163,7 @@ export class NoiseWrapper { public destroy(id: string) { if (this.node) { - this.node.stop(); + this.node.stop(); this.node.disconnect(); } this.output.disconnect(); @@ -172,7 +209,7 @@ export class LfoWrapper { } public destroy(id: string) { - this.node.stop(); +this.node.stop(); this.node.disconnect(); this.depthNode.disconnect(); audioCore.unregisterNode(id); diff --git a/projects/web-synth-9x1a/src/components/nodes/BitcrusherNode.tsx b/projects/web-synth-9x1a/src/components/nodes/BitcrusherNode.tsx index bcc26963..cea630c0 100644 --- a/projects/web-synth-9x1a/src/components/nodes/BitcrusherNode.tsx +++ b/projects/web-synth-9x1a/src/components/nodes/BitcrusherNode.tsx @@ -40,7 +40,10 @@ export function BitcrusherNode({ id, data }: { id: string, data: Record - +
+ + +
diff --git a/projects/web-synth-9x1a/src/components/nodes/OscillatorNode.tsx b/projects/web-synth-9x1a/src/components/nodes/OscillatorNode.tsx index 43642528..4ce18378 100644 --- a/projects/web-synth-9x1a/src/components/nodes/OscillatorNode.tsx +++ b/projects/web-synth-9x1a/src/components/nodes/OscillatorNode.tsx @@ -37,6 +37,16 @@ export function OscillatorNode({ id, data }: { id: string, data: Record + + + + + +