summaryrefslogtreecommitdiff
path: root/internal/generator/templates/shared/shared.js
diff options
context:
space:
mode:
Diffstat (limited to 'internal/generator/templates/shared/shared.js')
-rw-r--r--internal/generator/templates/shared/shared.js65
1 files changed, 65 insertions, 0 deletions
diff --git a/internal/generator/templates/shared/shared.js b/internal/generator/templates/shared/shared.js
index 6391d6c..2f4c260 100644
--- a/internal/generator/templates/shared/shared.js
+++ b/internal/generator/templates/shared/shared.js
@@ -366,6 +366,7 @@
function snonuxWaveType(w) {
if (w === 'square') return 'square';
if (w === 'triangle') return 'triangle';
+ if (w === 'sawtooth') return 'sawtooth';
return 'sine';
}
@@ -384,6 +385,8 @@
var isPlaying = false;
var isWild = false;
var currentPreset = null;
+ var melodyTimer = null;
+ var melodyIndex = 0;
function wildifyPreset(base) {
if (!base) return base;
@@ -475,9 +478,69 @@
noiseSrc = src;
}
+ function stopMelody() {
+ if (melodyTimer) { clearTimeout(melodyTimer); melodyTimer = null; }
+ melodyIndex = 0;
+ }
+
+ function playMelodyNote(note, preset) {
+ var c = ensureCtx();
+ var freq = note.freq || 440;
+ var dur = note.dur || 0.3;
+ if (freq <= 0 || dur <= 0) return;
+ var wt = snonuxWaveType(preset.wave);
+ var g = note.gain != null ? note.gain : (preset.gain != null ? preset.gain : 0.08);
+ var pulseGain = Math.min(g * 0.6, 0.12);
+
+ var osc = c.createOscillator();
+ var gain = c.createGain();
+ osc.type = wt;
+ osc.frequency.value = freq;
+
+ var lastNode = osc;
+ if (preset.filterFreq) {
+ var filter = c.createBiquadFilter();
+ filter.type = 'lowpass';
+ filter.frequency.value = preset.filterFreq;
+ filter.Q.value = preset.filterQ || 1;
+ var now = c.currentTime;
+ filter.frequency.setValueAtTime(preset.filterFreq, now);
+ filter.frequency.exponentialRampToValueAtTime(Math.max(preset.filterFreq * 0.2, 100), now + dur);
+ lastNode.connect(filter);
+ lastNode = filter;
+ }
+
+ lastNode.connect(gain);
+ gain.connect(masterGain);
+ var now = c.currentTime;
+ gain.gain.setValueAtTime(0, now);
+ gain.gain.linearRampToValueAtTime(pulseGain, now + Math.min(0.05, dur * 0.2));
+ gain.gain.exponentialRampToValueAtTime(0.001, now + dur);
+ osc.start(now);
+ osc.stop(now + dur + 0.02);
+ }
+
+ function scheduleMelodyNote(preset) {
+ if (!isPlaying || !preset || !preset.melody || preset.melody.length === 0) return;
+ var note = preset.melody[melodyIndex];
+ playMelodyNote(note, preset);
+ melodyIndex = (melodyIndex + 1) % preset.melody.length;
+ var gap = preset.pulseInterval || (preset.bpm ? 60.0 / preset.bpm : 0.5);
+ var isEndOfPhrase = melodyIndex === 0;
+ var delay = note.dur * 1000 * 0.7 + (isEndOfPhrase ? gap * 1000 * 1.5 : gap * 1000 * 0.2);
+ melodyTimer = setTimeout(function() {
+ if (!isPlaying) return;
+ scheduleMelodyNote(currentPreset);
+ }, delay);
+ }
+
function schedulePulse() {
if (!isPlaying || !currentPreset) return;
var preset = currentPreset;
+ if (preset.melody && preset.melody.length > 0) {
+ scheduleMelodyNote(preset);
+ return;
+ }
var interval = preset.pulseInterval;
if (!interval && preset.bpm) {
interval = 60.0 / preset.bpm;
@@ -542,6 +605,7 @@
function stopAll() {
clearTimeout(pulseTimer);
pulseTimer = null;
+ stopMelody();
stopDrones();
stopNoise();
}
@@ -552,6 +616,7 @@
currentPreset = preset;
ensureCtx();
isPlaying = true;
+ melodyIndex = 0;
startDrones(preset);
startNoise(preset);