// ============================================================================
// UPLIFTING TRANCE CLASSIC (138 BPM) - Dittytoy Code
// ============================================================================
ditty.bpm = 138;
// ----------------------------------------------------------------------------
// 1. SYNTH DEFINITIONS
// ----------------------------------------------------------------------------
// High-Energy Supersaw Lead
const supersaw = synth.def(class {
constructor(options) {
this.detune = [0.985, 0.993, 1.000, 1.007, 1.015];
this.phases = this.detune.map(() => Math.random());
}
process(note, env, tick, options) {
let freq = midi_to_hz(note);
let sig = 0;
// Accumulate 5 detuned saw waves
for (let i = 0; i < this.detune.length; i++) {
this.phases[i] += freq * this.detune[i] * ditty.dt;
this.phases[i] %= 1.0;
sig += (this.phases[i] * 2 - 1);
}
// Dynamic Filter Cutoff Sweep via option
let cutoff = options.cutoff || 0.5;
sig = Math.sin(sig * Math.PI * cutoff);
return sig * 0.2 * env.value;
}
}, { attack: 0.01, release: 0.3 });
// Resonant 303-style Acid Saw Bass
const acidBass = synth.def(class {
constructor(options) {
this.phase = Math.random();
}
process(note, env, tick, options) {
let freq = midi_to_hz(note);
this.phase += freq * ditty.dt;
this.phase %= 1.0;
let saw = this.phase * 2 - 1;
// Simple resonant waveshaping filter simulation
let envCut = env.value ** 1.5;
let out = Math.tanh(saw * (1 + envCut * 4));
return out * 0.4 * env.value;
}
}, { attack: 0.005, release: 0.15 });
// Pluck / Arpeggiator Synth
const pluck = synth.def((phase, env, tick, options) => {
let sig = (phase % 1) * 2 - 1; // Saw wave
return Math.sin(sig * Math.PI * 0.8) * env.value;
}, { attack: 0.002, release: 0.18 });
// Drums: Kick, Clap, Hi-Hat
const kick = synth.def(class {
constructor(options) {
this.phase = 0;
}
process(note, env, tick, options) {
// Fast pitch drop from ~180 Hz down to ~50 Hz for the initial 'thump'
let pitchEnv = env.value ** 4;
let freq = 50 + pitchEnv * 130;
this.phase += freq * ditty.dt;
let sineWave = Math.sin(this.phase * Math.PI * 2);
// Add transient click at the start
let click = (env.value > 0.95) ? (Math.random() * 2 - 1) * 0.15 : 0;
// Apply drive/saturation using Math.tanh for body and punch
let drive = 1.8;
let saturated = Math.tanh((sineWave + click) * drive);
return saturated * 0.9 * (env.value ** 0.8);
}
}, { attack: 0.0005, release: 0.38 });
const hat = synth.def((phase, env, tick, options) => {
return (Math.random() * 2 - 1) * env.value;
}, { attack: 0.001, release: 0.05 });
// ----------------------------------------------------------------------------
// 2. MUSICAL DATA (Scale: C Minor / Eb Major Trance Progression)
// ----------------------------------------------------------------------------
const bassLine = [
c2, c2, c2, c2, c2, c2, c2, c2,
ab1, ab1, ab1, ab1, eb2, eb2, bb1, bb1
];
const arpNotes = [
c4, g4, c5, eb5, g5, eb5, c5, g4,
ab3, eb4, ab4, c5, eb5, c5, ab4, eb4,
eb4, bb4, eb5, g5, bb5, g5, eb5, bb4,
bb3, f4, bb4, d5, f5, d5, bb4, f4
];
const leadMelody = [
c5, g5, eb5, d5, c5, g5, eb5, f5,
ab5, g5, eb5, d5, c5, d5, eb5, g5
];
// ----------------------------------------------------------------------------
// 3. LOOPS (SEPARATE THREADS)
// ----------------------------------------------------------------------------
// Kick Loop (Classic 4-on-the-floor)
loop((loopCount) => {
kick.play(c2, { amp: 1.2 });
sleep(1.0);
}, { name: 'Kick' });
// Offbeat Open Hi-Hat
loop((loopCount) => {
sleep(0.5);
hat.play(c6, { amp: 0.6, release: 0.08 });
sleep(0.5);
}, { name: 'Offbeat Hat' });
// 16th Note Driving Bassline
loop((loopCount) => {
let idx = loopCount % bassLine.length;
acidBass.play(bassLine[idx], {
amp: idx % 2 === 0 ? 0.8 : 0.6,
pan: (loopCount % 4 === 0) ? -0.1 : 0.1
});
sleep(0.25);
}, { name: 'Driving Bass' });
// Fast 16th-note Arpeggio Layer
loop((loopCount) => {
let note = arpNotes[loopCount % arpNotes.length];
pluck.play(note, {
amp: 0.45,
pan: Math.sin(loopCount * 0.25) * 0.6 // Panning sweep
});
sleep(0.25);
}, { name: 'Trance Arp' });
// Main Euphoric Lead (Starts filtering up dynamically)
loop((loopCount) => {
let step = loopCount % leadMelody.length;
let cutoffValue = 0.3 + 0.6 * Math.abs(Math.sin(loopCount * 0.05)); // Filter modulation
supersaw.play(leadMelody[step], {
duration: 0.5,
attack: 0.01,
release: 0.4,
amp: 0.7,
cutoff: cutoffValue,
pan: (step % 2 === 0) ? 0.3 : -0.3
});
sleep(0.5);
}, { name: 'Main Anthem Lead' });