tuneframes 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +166 -166
- package/README.md +158 -55
- package/examples/example-ai-dj-chill.html +167 -0
- package/examples/example-ai-dj-dark.html +167 -0
- package/examples/example-ai-dj-energetic.html +167 -0
- package/examples/example-ai-dj-happy.html +167 -0
- package/examples/example-ai-dj.html +167 -0
- package/examples/example-ambient.html +63 -63
- package/examples/example-ambient.mp3 +0 -0
- package/examples/example-bass.html +47 -47
- package/examples/example-lofi.html +45 -45
- package/examples/example-lofi.mp3 +0 -0
- package/examples/example-minimal.html +20 -20
- package/examples/example-orchestral.html +67 -67
- package/examples/example-orchestral.mp3 +0 -0
- package/examples/example-piano.html +52 -52
- package/examples/example-piano.mp3 +0 -0
- package/examples/example-techno.html +69 -69
- package/examples/example-techno.mp3 +0 -0
- package/package.json +42 -37
- package/registry/presets/bass-electric.html +67 -0
- package/registry/presets/bass-saw.html +22 -0
- package/registry/presets/chord-progression.html +22 -0
- package/registry/presets/drums-808.html +85 -0
- package/registry/presets/drums-lofi.html +35 -0
- package/registry/presets/lead-piano.html +26 -0
- package/registry/presets/piano-salamander.html +69 -0
- package/registry/presets/reverb-warm.html +11 -0
- package/registry/samples.json +226 -0
- package/skills/audio-ambient/SKILL.md +141 -0
- package/skills/audio-ambient/example.html +113 -0
- package/skills/audio-boss-battle/SKILL.md +157 -0
- package/skills/audio-boss-battle/example.html +185 -0
- package/skills/audio-boss-battle/example.mp3 +0 -0
- package/skills/audio-chillwave/SKILL.md +142 -0
- package/skills/audio-chillwave/example.html +144 -0
- package/skills/audio-cinematic/SKILL.md +147 -0
- package/skills/audio-cinematic/example.html +123 -0
- package/skills/audio-classical/SKILL.md +138 -0
- package/skills/audio-classical/example.html +145 -0
- package/skills/audio-dnb/SKILL.md +142 -0
- package/skills/audio-dnb/example.html +124 -0
- package/skills/audio-downtempo/SKILL.md +152 -0
- package/skills/audio-downtempo/example.html +164 -0
- package/skills/audio-folk/SKILL.md +139 -0
- package/skills/audio-folk/example.html +132 -0
- package/skills/audio-funk/SKILL.md +149 -0
- package/skills/audio-funk/example.html +144 -0
- package/skills/audio-future-bass/SKILL.md +163 -0
- package/skills/audio-future-bass/example.html +164 -0
- package/skills/audio-hip-hop/SKILL.md +133 -0
- package/skills/audio-hip-hop/example.html +129 -0
- package/skills/audio-house/SKILL.md +147 -0
- package/skills/audio-house/example.html +128 -0
- package/skills/audio-indie-pop/SKILL.md +150 -0
- package/skills/audio-indie-pop/example.html +121 -0
- package/skills/audio-jazz/SKILL.md +141 -0
- package/skills/audio-jazz/example.html +146 -0
- package/skills/audio-lofi/SKILL.md +140 -0
- package/skills/audio-lofi/example.html +135 -0
- package/skills/audio-minimal/SKILL.md +155 -0
- package/skills/audio-minimal/example.html +118 -0
- package/skills/audio-orchestral/SKILL.md +156 -0
- package/skills/audio-orchestral/example.html +140 -0
- package/skills/audio-r-and-b/SKILL.md +134 -0
- package/skills/audio-r-and-b/example.html +154 -0
- package/skills/audio-r-and-b/example.mp3 +0 -0
- package/skills/audio-techno/SKILL.md +140 -0
- package/skills/audio-techno/example.html +125 -0
- package/skills/audio-trap/SKILL.md +123 -0
- package/skills/audio-trap/example.html +119 -0
- package/skills/render-retest/example.html +115 -0
- package/skills/tuneframes/SKILL.md +221 -0
- package/skills/tuneframes-cli/SKILL.md +46 -0
- package/skills/verify/example.html +104 -0
- package/src/cli.js +260 -151
- package/src/render.js +134 -7
- package/examples/example-bass.mp3 +0 -0
- package/examples/example-demo-beat.wav +0 -0
- package/examples/example-orchestral.wav +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>TuneFrames — Cinematic Score</title>
|
|
5
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<div id="tuneframes" style="display:none">{"bpm":72,"duration":"15s"}</div>
|
|
9
|
+
<script>
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
Tone.Transport.bpm.value = 72;
|
|
13
|
+
|
|
14
|
+
const bar = Tone.Time('1n').toSeconds(); // ~3.33s per bar at 72 BPM
|
|
15
|
+
const q = Tone.Time('4n').toSeconds(); // ~0.83s per beat
|
|
16
|
+
|
|
17
|
+
// ── Reverb halls ─────────────────────────────────────────────────────
|
|
18
|
+
const hall = new Tone.Reverb({ decay: 7, preDelay: 0.06, wet: 0.65 });
|
|
19
|
+
const room = new Tone.Reverb({ decay: 3, preDelay: 0.01, wet: 0.35 });
|
|
20
|
+
// Await IR generation before offline rendering begins — required in Tone.Offline
|
|
21
|
+
await Promise.all([hall.ready, room.ready]);
|
|
22
|
+
hall.toDestination();
|
|
23
|
+
room.toDestination();
|
|
24
|
+
|
|
25
|
+
// ── Choir pad (enters immediately, very soft) ─────────────────────────
|
|
26
|
+
const choir = new Tone.PolySynth(Tone.Synth, {
|
|
27
|
+
oscillator: { type: 'sine' },
|
|
28
|
+
envelope: { attack: 2.8, decay: 0.4, sustain: 0.85, release: 6.0 }
|
|
29
|
+
}).connect(hall);
|
|
30
|
+
choir.volume.value = -22;
|
|
31
|
+
|
|
32
|
+
// Dm open fifth — ethereal, thin, creates space
|
|
33
|
+
choir.triggerAttackRelease(['D3','A3'], '2n', 0);
|
|
34
|
+
|
|
35
|
+
// ── Strings — slow attack swell ──────────────────────────────────────
|
|
36
|
+
const strings = new Tone.PolySynth(Tone.Synth, {
|
|
37
|
+
oscillator: { type: 'sawtooth' },
|
|
38
|
+
envelope: { attack: 2.0, decay: 0.5, sustain: 0.85, release: 5.0 }
|
|
39
|
+
}).connect(hall);
|
|
40
|
+
strings.volume.value = -16;
|
|
41
|
+
|
|
42
|
+
// Dm – Bb – F – C (the cinematic minor-to-relative-major arc)
|
|
43
|
+
const prog = [
|
|
44
|
+
['D3','F3','A3','D4'], // Dm (bar 1)
|
|
45
|
+
['Bb2','D3','F3','Bb3'], // Bb (bar 2)
|
|
46
|
+
['F2','C3','F3','A3'], // F (bar 3)
|
|
47
|
+
['C3','G3','C4','E4'], // C (bar 4)
|
|
48
|
+
];
|
|
49
|
+
prog.forEach((ch, i) => strings.triggerAttackRelease(ch, '1n', i * bar));
|
|
50
|
+
|
|
51
|
+
// ── Cello ostinato (enters bar 2 — adds forward motion) ──────────────
|
|
52
|
+
const cello = new Tone.Synth({
|
|
53
|
+
oscillator: { type: 'sawtooth' },
|
|
54
|
+
envelope: { attack: 0.12, decay: 0.2, sustain: 0.7, release: 0.8 }
|
|
55
|
+
}).connect(hall);
|
|
56
|
+
cello.volume.value = -18;
|
|
57
|
+
|
|
58
|
+
// D–A–D pattern driving through bars 2–4
|
|
59
|
+
const ostinato = ['D3','A3','D3','A3','D3','A3','D3','F3'];
|
|
60
|
+
ostinato.forEach((n, i) => cello.triggerAttackRelease(n, '8n', bar + i * (q * 0.5)));
|
|
61
|
+
|
|
62
|
+
const ostinato2 = ['Bb2','F3','Bb2','F3','Bb2','F3','C3','G3'];
|
|
63
|
+
ostinato2.forEach((n, i) => cello.triggerAttackRelease(n, '8n', bar * 2 + i * (q * 0.5)));
|
|
64
|
+
|
|
65
|
+
const ostinato3 = ['F2','C3','F3','C3','F2','C3','G2','C3'];
|
|
66
|
+
ostinato3.forEach((n, i) => cello.triggerAttackRelease(n, '8n', bar * 3 + i * (q * 0.5)));
|
|
67
|
+
|
|
68
|
+
// ── Low piano (enters bar 1, punctuates downbeats) ───────────────────
|
|
69
|
+
const piano = new Tone.Synth({
|
|
70
|
+
oscillator: { type: 'sine' },
|
|
71
|
+
envelope: { attack: 0.01, decay: 1.2, sustain: 0.1, release: 2.0 }
|
|
72
|
+
}).connect(room);
|
|
73
|
+
piano.volume.value = -14;
|
|
74
|
+
|
|
75
|
+
piano.triggerAttackRelease('D2', '2n', 0);
|
|
76
|
+
piano.triggerAttackRelease('Bb1', '2n', bar);
|
|
77
|
+
piano.triggerAttackRelease('F1', '2n', bar * 2);
|
|
78
|
+
piano.triggerAttackRelease('C2', '2n', bar * 3);
|
|
79
|
+
|
|
80
|
+
// ── Yearning solo melody (enters bar 2, high register) ───────────────
|
|
81
|
+
const solo = new Tone.Synth({
|
|
82
|
+
oscillator: { type: 'sine' },
|
|
83
|
+
envelope: { attack: 0.35, decay: 0.3, sustain: 0.85, release: 2.5 }
|
|
84
|
+
}).connect(hall);
|
|
85
|
+
solo.volume.value = -14;
|
|
86
|
+
|
|
87
|
+
// Morricone-style descending phrase: D5–C5–Bb4–A4–F4–A4
|
|
88
|
+
const soloPhrase = [
|
|
89
|
+
{ note: 'D5', time: bar * 1.5, dur: '4n' },
|
|
90
|
+
{ note: 'C5', time: bar * 1.5 + q, dur: '4n' },
|
|
91
|
+
{ note: 'Bb4', time: bar * 1.5 + q*2, dur: '2n' },
|
|
92
|
+
{ note: 'A4', time: bar * 2.5, dur: '4n' },
|
|
93
|
+
{ note: 'F4', time: bar * 2.5 + q, dur: '4n' },
|
|
94
|
+
{ note: 'A4', time: bar * 2.5 + q*2, dur: '4n' },
|
|
95
|
+
{ note: 'D5', time: bar * 3, dur: '2n.' },
|
|
96
|
+
{ note: 'C5', time: bar * 3.75, dur: '4n' },
|
|
97
|
+
];
|
|
98
|
+
soloPhrase.forEach(({ note, time, dur }) => solo.triggerAttackRelease(note, dur, time));
|
|
99
|
+
|
|
100
|
+
// ── Brass swell (enters bar 3 — the climax) ──────────────────────────
|
|
101
|
+
const brass = new Tone.PolySynth(Tone.Synth, {
|
|
102
|
+
oscillator: { type: 'sawtooth' },
|
|
103
|
+
envelope: { attack: 0.5, decay: 0.3, sustain: 0.85, release: 3.0 }
|
|
104
|
+
}).connect(hall);
|
|
105
|
+
brass.volume.value = -20;
|
|
106
|
+
|
|
107
|
+
brass.triggerAttackRelease(['F3','A3','C4','F4'], '2n', bar * 2);
|
|
108
|
+
brass.triggerAttackRelease(['C3','G3','C4','E4'], '1n', bar * 3);
|
|
109
|
+
|
|
110
|
+
// ── Timpani — dramatic hits on bars 3 and 4 ──────────────────────────
|
|
111
|
+
const timp = new Tone.MembraneSynth({
|
|
112
|
+
pitchDecay: 0.1, octaves: 6,
|
|
113
|
+
envelope: { attack: 0.001, decay: 1.5, sustain: 0, release: 2.5 }
|
|
114
|
+
}).connect(hall);
|
|
115
|
+
timp.volume.value = -8;
|
|
116
|
+
|
|
117
|
+
timp.triggerAttackRelease('F1', '4n', bar * 2);
|
|
118
|
+
timp.triggerAttackRelease('D1', '4n', bar * 3);
|
|
119
|
+
timp.triggerAttackRelease('C1', '4n', bar * 3 + q * 2);
|
|
120
|
+
}
|
|
121
|
+
</script>
|
|
122
|
+
</body>
|
|
123
|
+
</html>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-classical
|
|
3
|
+
description: Classical solo piano — left hand Alberti bass or broken chords, right hand melody with ornaments, no effects beyond room reverb
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Classical Piano
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 80-140 (Allegro for Mozart, Andante for slow movements)
|
|
10
|
+
- Key characteristics: Alberti bass in left hand (bass-chord-chord pattern), melodic right hand in single notes or octaves, voice leading between the two hands, harmonic rhythm changes every half or full bar
|
|
11
|
+
- Typical instruments: Tone.Synth with triangle oscillator (cleanest piano approximation), slight room reverb only
|
|
12
|
+
- Mood: elegant, structured, emotionally direct, intellectually clear
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Classical piano: right hand melody over left hand Alberti bass
|
|
18
|
+
// Key = C major, BPM = 104 (Allegro moderato)
|
|
19
|
+
// Alberti bass = bass note → inner chord → upper chord, repeating 8th notes
|
|
20
|
+
Tone.Transport.bpm.value = 104;
|
|
21
|
+
|
|
22
|
+
// Piano synth — triangle wave with tight envelope
|
|
23
|
+
const piano = new Tone.Synth({
|
|
24
|
+
oscillator: { type: 'triangle' },
|
|
25
|
+
envelope: { attack: 0.008, decay: 0.3, sustain: 0.2, release: 1.2 },
|
|
26
|
+
volume: -6,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Gentle room reverb only
|
|
30
|
+
const reverb = new Tone.Reverb({ decay: 1.2, preDelay: 0.01, wet: 0.25 });
|
|
31
|
+
await reverb.generate();
|
|
32
|
+
reverb.toDestination();
|
|
33
|
+
piano.connect(reverb);
|
|
34
|
+
|
|
35
|
+
// A second instance for left hand (allows simultaneous notes)
|
|
36
|
+
const pianoL = new Tone.Synth({
|
|
37
|
+
oscillator: { type: 'triangle' },
|
|
38
|
+
envelope: { attack: 0.008, decay: 0.4, sustain: 0.15, release: 1.0 },
|
|
39
|
+
volume: -10, // left hand slightly quieter
|
|
40
|
+
});
|
|
41
|
+
pianoL.connect(reverb);
|
|
42
|
+
|
|
43
|
+
// RIGHT HAND: C major scale as 8th-note melody (C D E F G F E D)
|
|
44
|
+
const rhNotes = ['C5','D5','E5','F5','G5','F5','E5','D5'];
|
|
45
|
+
const rhTimes = [0, 0.29, 0.57, 0.86, 1.15, 1.44, 1.73, 2.02]; // 8th notes at 104 BPM
|
|
46
|
+
rhNotes.forEach((note, i) => {
|
|
47
|
+
Tone.Transport.scheduleOnce(time => {
|
|
48
|
+
piano.triggerAttackRelease(note, '8n', time);
|
|
49
|
+
}, rhTimes[i]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// LEFT HAND: Alberti bass on C major chord (C3 → E3 → G3 repeating)
|
|
53
|
+
// Pattern: C3, G3, E3, G3 (bass → upper → inner → upper)
|
|
54
|
+
const lhPattern = ['C3','G3','E3','G3', 'F2','A2','F2','A2'];
|
|
55
|
+
lhPattern.forEach((note, i) => {
|
|
56
|
+
Tone.Transport.scheduleOnce(time => {
|
|
57
|
+
pianoL.triggerAttackRelease(note, '8n', time);
|
|
58
|
+
}, i * 0.29); // 8th note spacing at 104 BPM
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Instrument Configuration
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
// Piano approximation — triangle + tight decay, slight room
|
|
66
|
+
// Two instances: right hand (melody) and left hand (accompaniment)
|
|
67
|
+
|
|
68
|
+
// Room reverb — small, not a concert hall
|
|
69
|
+
const reverb = new Tone.Reverb({ decay: 1.1, preDelay: 0.008, wet: 0.22 });
|
|
70
|
+
await reverb.generate();
|
|
71
|
+
reverb.toDestination();
|
|
72
|
+
|
|
73
|
+
// Right hand — melody voice, slightly brighter
|
|
74
|
+
const rh = new Tone.Synth({
|
|
75
|
+
oscillator: { type: 'triangle' },
|
|
76
|
+
envelope: { attack: 0.006, decay: 0.25, sustain: 0.15, release: 1.4 },
|
|
77
|
+
volume: -4,
|
|
78
|
+
}).connect(reverb);
|
|
79
|
+
|
|
80
|
+
// Left hand — accompaniment, slightly softer and darker
|
|
81
|
+
const lh = new Tone.Synth({
|
|
82
|
+
oscillator: { type: 'triangle' },
|
|
83
|
+
envelope: { attack: 0.01, decay: 0.4, sustain: 0.1, release: 1.0 },
|
|
84
|
+
volume: -9,
|
|
85
|
+
}).connect(reverb);
|
|
86
|
+
|
|
87
|
+
// For chords in the left hand, use PolySynth
|
|
88
|
+
const lhChord = new Tone.PolySynth(Tone.Synth, {
|
|
89
|
+
oscillator: { type: 'triangle' },
|
|
90
|
+
envelope: { attack: 0.01, decay: 0.35, sustain: 0.15, release: 1.2 },
|
|
91
|
+
volume: -11,
|
|
92
|
+
}).connect(reverb);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Composition Structure
|
|
96
|
+
|
|
97
|
+
**Classical follows phrase structure — typically 4-bar or 8-bar phrases:**
|
|
98
|
+
|
|
99
|
+
1. **Antecedent phrase (0-4s):** Opening melody in right hand. Left hand Alberti bass on I chord (tonic). Ends on V (half cadence — tension).
|
|
100
|
+
2. **Consequent phrase (4-8s):** Melody continues or develops. Left hand harmonic progression I→IV→V→I. Ends on I (full cadence — resolution).
|
|
101
|
+
3. **Middle section / contrast (8-12s):** Move to relative minor or vi. Melody gets more chromatic or ornamental. Left hand may switch from Alberti to block chords.
|
|
102
|
+
4. **Return (12s+):** Return to opening material. Final cadence: IV→V→I.
|
|
103
|
+
|
|
104
|
+
**Alberti bass pattern at any BPM:**
|
|
105
|
+
- Bass note (lowest note of chord) → Upper note → Middle note → Upper note
|
|
106
|
+
- Repeat for each 8th note subdivision
|
|
107
|
+
- C major: C3 → G3 → E3 → G3
|
|
108
|
+
- F major: F2 → C3 → A2 → C3
|
|
109
|
+
- G major: G2 → D3 → B2 → D3
|
|
110
|
+
|
|
111
|
+
## Example Variations
|
|
112
|
+
|
|
113
|
+
### Variation 1: Slow movement (Andante cantabile)
|
|
114
|
+
```js
|
|
115
|
+
Tone.Transport.bpm.value = 72;
|
|
116
|
+
// Right hand: long notes (quarter and half notes) — singable melody
|
|
117
|
+
// Left hand: broken octaves instead of Alberti — just bass + high octave alternating
|
|
118
|
+
// Reverb slightly wetter: 0.35 — feels more intimate
|
|
119
|
+
// Envelope release: 2.0 — notes ring longer
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Variation 2: Fast passage work (Allegro vivace)
|
|
123
|
+
```js
|
|
124
|
+
Tone.Transport.bpm.value = 132;
|
|
125
|
+
// Right hand: 16th-note scalar run (C major scale, 2 octaves up and down)
|
|
126
|
+
// Left hand: block chords on beats 1 and 3 only (not Alberti — too busy)
|
|
127
|
+
// Use PolySynth for left hand chords: ['C3','E3','G3'], ['G2','B2','D3'], etc.
|
|
128
|
+
// Shorter release: 0.6 — crisp, not muddy at fast tempo
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Variation 3: Ornamental turn
|
|
132
|
+
```js
|
|
133
|
+
// Right hand melody includes a trill approximation
|
|
134
|
+
// Instead of one quarter note on E5, play: E5, F5, E5, D5, E5 as 32nd notes
|
|
135
|
+
// Schedule these rapid notes as very short triggerAttackRelease calls
|
|
136
|
+
// const trillNotes = ['E5','F5','E5','D5','E5'];
|
|
137
|
+
// trillNotes.forEach((n, i) => rh.triggerAttackRelease(n, '32n', time + i * 0.045));
|
|
138
|
+
```
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<div id="tuneframes" style="display:none">{"bpm":104,"duration":"12s"}</div>
|
|
2
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
3
|
+
<script>
|
|
4
|
+
// TuneFrames — Classical Piano
|
|
5
|
+
// C major, Allegro moderato (104 BPM)
|
|
6
|
+
// Right hand: melodic line with ornamental turn
|
|
7
|
+
// Left hand: Alberti bass (bass-upper-inner-upper pattern)
|
|
8
|
+
// Harmony: I → V → vi → IV → V → I (complete progression)
|
|
9
|
+
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
|
|
13
|
+
Tone.Transport.bpm.value = 104;
|
|
14
|
+
|
|
15
|
+
const beat = 60 / 104; // 0.577s per quarter note
|
|
16
|
+
const eighth = beat / 2; // 0.288s per eighth note
|
|
17
|
+
const sixteenth = beat / 4; // 0.144s per sixteenth note
|
|
18
|
+
|
|
19
|
+
// ── Small room reverb — a piano in a parlour, not a concert hall ──────────
|
|
20
|
+
const reverb = new Tone.Reverb({ decay: 1.1, preDelay: 0.008, wet: 0.22 });
|
|
21
|
+
await reverb.generate();
|
|
22
|
+
reverb.toDestination();
|
|
23
|
+
|
|
24
|
+
// ── Right hand — melody voice, triangle wave ──────────────────────────────
|
|
25
|
+
const rh = new Tone.Synth({
|
|
26
|
+
oscillator: { type: 'triangle' },
|
|
27
|
+
envelope: { attack: 0.006, decay: 0.22, sustain: 0.18, release: 1.4 },
|
|
28
|
+
volume: -4,
|
|
29
|
+
});
|
|
30
|
+
rh.connect(reverb);
|
|
31
|
+
|
|
32
|
+
// ── Left hand single notes (bass) — slightly softer, same timbre ─────────
|
|
33
|
+
const lhBass = new Tone.Synth({
|
|
34
|
+
oscillator: { type: 'triangle' },
|
|
35
|
+
envelope: { attack: 0.008, decay: 0.35, sustain: 0.12, release: 1.0 },
|
|
36
|
+
volume: -10,
|
|
37
|
+
});
|
|
38
|
+
lhBass.connect(reverb);
|
|
39
|
+
|
|
40
|
+
// ── Left hand chord notes (inner voices of Alberti) ───────────────────────
|
|
41
|
+
const lhChord = new Tone.PolySynth(Tone.Synth, {
|
|
42
|
+
oscillator: { type: 'triangle' },
|
|
43
|
+
envelope: { attack: 0.008, decay: 0.3, sustain: 0.1, release: 0.9 },
|
|
44
|
+
volume: -13,
|
|
45
|
+
});
|
|
46
|
+
lhChord.connect(reverb);
|
|
47
|
+
|
|
48
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
49
|
+
// Helper: schedule an Alberti bass pattern for one bar using direct time
|
|
50
|
+
// root = bass note (e.g. 'C3'), inner = e.g. 'E3', upper = e.g. 'G3'
|
|
51
|
+
// Pattern of 8 eighth notes: root upper inner upper root upper inner upper
|
|
52
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
53
|
+
function alberti(root, inner, upper, startTime) {
|
|
54
|
+
const pattern = [root, upper, inner, upper, root, upper, inner, upper];
|
|
55
|
+
pattern.forEach((note, i) => {
|
|
56
|
+
const t = startTime + i * eighth;
|
|
57
|
+
if (i % 2 === 0) {
|
|
58
|
+
lhBass.triggerAttackRelease(note, '8n', t);
|
|
59
|
+
} else {
|
|
60
|
+
lhChord.triggerAttackRelease(note, '8n', t);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
66
|
+
// Harmony plan (one bar each, 4 beats = beat*4 per bar):
|
|
67
|
+
// Bar 1: I — C major (C3 E3 G3)
|
|
68
|
+
// Bar 2: V — G major (G2 B2 D3)
|
|
69
|
+
// Bar 3: vi — A minor (A2 C3 E3)
|
|
70
|
+
// Bar 4: IV — F major (F2 A2 C3)
|
|
71
|
+
// Bar 5: V — G major (G2 B2 D3)
|
|
72
|
+
// Bar 6: I — C major (C3 E3 G3) — final resolution
|
|
73
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
74
|
+
const barLen = beat * 4;
|
|
75
|
+
|
|
76
|
+
alberti('C3', 'E3', 'G3', 0); // I
|
|
77
|
+
alberti('G2', 'B2', 'D3', barLen); // V
|
|
78
|
+
alberti('A2', 'C3', 'E3', barLen * 2); // vi
|
|
79
|
+
alberti('F2', 'A2', 'C3', barLen * 3); // IV
|
|
80
|
+
alberti('G2', 'B2', 'D3', barLen * 4); // V
|
|
81
|
+
alberti('C3', 'E3', 'G3', barLen * 5); // I (final)
|
|
82
|
+
|
|
83
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
84
|
+
// Right hand melody — stepwise, fits the chord above
|
|
85
|
+
// Phrase 1 (bars 1-2): C major scale ascending, quarter + eighth mix
|
|
86
|
+
// Phrase 2 (bars 3-4): descent with ornamental turn on E5
|
|
87
|
+
// Phrase 3 (bars 5-6): cadential figure, landing on C5
|
|
88
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
// Phrase 1: ascending C major scale with rhythmic variety
|
|
91
|
+
const phrase1 = [
|
|
92
|
+
{ note: 'C5', dur: '4n', t: 0 },
|
|
93
|
+
{ note: 'D5', dur: '8n', t: beat },
|
|
94
|
+
{ note: 'E5', dur: '8n', t: beat + eighth },
|
|
95
|
+
{ note: 'F5', dur: '4n', t: beat * 2 },
|
|
96
|
+
{ note: 'G5', dur: '4n', t: beat * 3 },
|
|
97
|
+
// Bar 2: hold G5, then step up to A5
|
|
98
|
+
{ note: 'G5', dur: '8n', t: barLen },
|
|
99
|
+
{ note: 'A5', dur: '8n', t: barLen + eighth },
|
|
100
|
+
{ note: 'G5', dur: '4n', t: barLen + beat },
|
|
101
|
+
{ note: 'F5', dur: '4n', t: barLen + beat * 2 },
|
|
102
|
+
{ note: 'E5', dur: '4n', t: barLen + beat * 3 },
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
// Phrase 2: ornamental turn on E5 (F E D E), then descent
|
|
106
|
+
const turnStart = barLen * 2;
|
|
107
|
+
const turn = [
|
|
108
|
+
{ note: 'F5', dur: '16n', t: turnStart },
|
|
109
|
+
{ note: 'E5', dur: '16n', t: turnStart + sixteenth },
|
|
110
|
+
{ note: 'D5', dur: '16n', t: turnStart + sixteenth * 2 },
|
|
111
|
+
{ note: 'E5', dur: '8n', t: turnStart + sixteenth * 3 },
|
|
112
|
+
];
|
|
113
|
+
const phrase2 = [
|
|
114
|
+
...turn,
|
|
115
|
+
{ note: 'D5', dur: '4n', t: turnStart + beat },
|
|
116
|
+
{ note: 'C5', dur: '4n', t: turnStart + beat * 2 },
|
|
117
|
+
{ note: 'B4', dur: '4n', t: turnStart + beat * 3 },
|
|
118
|
+
// Bar 4:
|
|
119
|
+
{ note: 'A4', dur: '4n', t: barLen * 3 },
|
|
120
|
+
{ note: 'G4', dur: '8n', t: barLen * 3 + beat },
|
|
121
|
+
{ note: 'A4', dur: '8n', t: barLen * 3 + beat + eighth },
|
|
122
|
+
{ note: 'C5', dur: '2n', t: barLen * 3 + beat * 2 },
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
// Phrase 3: cadential figure V → I landing
|
|
126
|
+
const p3start = barLen * 4;
|
|
127
|
+
const phrase3 = [
|
|
128
|
+
{ note: 'B4', dur: '4n', t: p3start },
|
|
129
|
+
{ note: 'D5', dur: '4n', t: p3start + beat },
|
|
130
|
+
{ note: 'G5', dur: '4n', t: p3start + beat * 2 },
|
|
131
|
+
{ note: 'F5', dur: '4n', t: p3start + beat * 3 },
|
|
132
|
+
// Bar 6: resolution to I — long final note
|
|
133
|
+
{ note: 'E5', dur: '8n', t: barLen * 5 },
|
|
134
|
+
{ note: 'D5', dur: '8n', t: barLen * 5 + eighth },
|
|
135
|
+
{ note: 'C5', dur: '2n', t: barLen * 5 + beat },
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
// Schedule all right hand notes using direct time offsets (seconds)
|
|
139
|
+
[...phrase1, ...phrase2, ...phrase3].forEach(({ note, dur, t }) => {
|
|
140
|
+
if (t < 12) {
|
|
141
|
+
rh.triggerAttackRelease(note, dur, t);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
</script>
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-dnb
|
|
3
|
+
description: Drum and Bass — syncopated Amen-style breakbeat, reese bass with chorus and filter, atmospheric pad, heavy sub frequencies. Hard hitting and kinetic.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Drum and Bass
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 165–180
|
|
10
|
+
- Key characteristics: Complex syncopated breakbeat (kick not on the beat — that's the point), heavy Reese bass (sawtooth + chorus + filter), atmospheric pad underneath, powerful sub frequencies, kinetic forward momentum
|
|
11
|
+
- Typical instruments: MembraneSynth (kick), NoiseSynth (snare), MetalSynth (hi-hat), FMSynth or MonoSynth (Reese bass), PolySynth (pad)
|
|
12
|
+
- Mood: Intense, kinetic, dark, cerebral, euphoric at the peak
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Drum and Bass — 174 BPM
|
|
18
|
+
// Breakbeat: kick/snare NOT on beats 1,2,3,4 — syncopated Amen-style
|
|
19
|
+
// Reese bass: sawtooth + heavy chorus = that "wobble" character
|
|
20
|
+
// Pad: slow attack, atmospheric underneath
|
|
21
|
+
|
|
22
|
+
// Classic DnB breakbeat (Amen-style, 16-step)
|
|
23
|
+
// K=kick, S=snare, .=rest
|
|
24
|
+
// K . . S . K K S . . K . S . K S (feel, not strict)
|
|
25
|
+
const breakPattern = [
|
|
26
|
+
"kick", null, null, "snare", null, "kick", "kick", "snare",
|
|
27
|
+
null, null, "kick", null, "snare", null, "kick", "snare"
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
new Tone.Sequence((time, hit) => {
|
|
31
|
+
if (hit === "kick") kick.triggerAttackRelease("C1", "8n", time);
|
|
32
|
+
if (hit === "snare") snare.triggerAttackRelease("16n", time);
|
|
33
|
+
}, breakPattern, "16n").start(0);
|
|
34
|
+
|
|
35
|
+
// Reese bass — sawtooth through chorus
|
|
36
|
+
const chorus = new Tone.Chorus({ frequency: 1.8, delayTime: 3.5, depth: 0.7, wet: 0.7 });
|
|
37
|
+
const filter = new Tone.Filter({ frequency: 800, type: "lowpass", Q: 2 });
|
|
38
|
+
const reese = new Tone.MonoSynth({
|
|
39
|
+
oscillator: { type: "sawtooth" },
|
|
40
|
+
envelope: { attack: 0.01, decay: 0.5, sustain: 0.8, release: 0.3 },
|
|
41
|
+
volume: -4
|
|
42
|
+
}).chain(chorus, filter, Tone.Destination);
|
|
43
|
+
chorus.start();
|
|
44
|
+
|
|
45
|
+
Tone.Transport.bpm.value = 174;
|
|
46
|
+
Tone.Transport.start();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Instrument Configuration
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
// Kick — short, punchy, low
|
|
53
|
+
const kick = new Tone.MembraneSynth({
|
|
54
|
+
pitchDecay: 0.04,
|
|
55
|
+
octaves: 5,
|
|
56
|
+
envelope: { attack: 0.001, decay: 0.2, sustain: 0, release: 0.08 },
|
|
57
|
+
volume: 2
|
|
58
|
+
}).toDestination();
|
|
59
|
+
|
|
60
|
+
// Snare — crisp, with slight room
|
|
61
|
+
const snareVerb = new Tone.Reverb({ decay: 0.5, wet: 0.15 }).toDestination();
|
|
62
|
+
const snare = new Tone.NoiseSynth({
|
|
63
|
+
noise: { type: "white" },
|
|
64
|
+
envelope: { attack: 0.001, decay: 0.12, sustain: 0, release: 0.04 },
|
|
65
|
+
volume: -2
|
|
66
|
+
}).connect(snareVerb);
|
|
67
|
+
|
|
68
|
+
// Hi-hat — very fast, adds texture between beats
|
|
69
|
+
const hat = new Tone.MetalSynth({
|
|
70
|
+
frequency: 500, harmonicity: 5.1, modulationIndex: 32,
|
|
71
|
+
resonance: 4500, octaves: 1.5,
|
|
72
|
+
envelope: { attack: 0.001, decay: 0.035, release: 0.01 },
|
|
73
|
+
volume: -12
|
|
74
|
+
}).toDestination();
|
|
75
|
+
|
|
76
|
+
// Reese bass — the DnB defining sound
|
|
77
|
+
// Two detuned sawtooths + heavy chorus + filter
|
|
78
|
+
const reeseLimiter = new Tone.Limiter(-4).toDestination();
|
|
79
|
+
const reeseFilter = new Tone.Filter({ frequency: 700, type: "lowpass", Q: 3 }).connect(reeseLimiter);
|
|
80
|
+
const reeseChorus = new Tone.Chorus({
|
|
81
|
+
frequency: 1.8, // slow chorus = the "reese" movement
|
|
82
|
+
delayTime: 3.5,
|
|
83
|
+
depth: 0.75,
|
|
84
|
+
wet: 0.75
|
|
85
|
+
}).connect(reeseFilter);
|
|
86
|
+
reeseChorus.start();
|
|
87
|
+
const reese = new Tone.MonoSynth({
|
|
88
|
+
oscillator: { type: "sawtooth" },
|
|
89
|
+
filter: { Q: 1, type: "lowpass", frequency: 2000 },
|
|
90
|
+
filterEnvelope: { attack: 0.02, decay: 0.5, sustain: 0.5, release: 0.5,
|
|
91
|
+
baseFrequency: 200, octaves: 2 },
|
|
92
|
+
envelope: { attack: 0.01, decay: 0.3, sustain: 0.8, release: 0.3 },
|
|
93
|
+
volume: -2
|
|
94
|
+
}).connect(reeseChorus);
|
|
95
|
+
|
|
96
|
+
// Atmospheric pad — slow attack, long release
|
|
97
|
+
const padVerb = new Tone.Reverb({ decay: 5.0, wet: 0.7 }).toDestination();
|
|
98
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
99
|
+
oscillator: { type: "sawtooth4" },
|
|
100
|
+
envelope: { attack: 0.6, decay: 1.0, sustain: 0.6, release: 2.5 },
|
|
101
|
+
volume: -14
|
|
102
|
+
}).connect(padVerb);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Composition Structure
|
|
106
|
+
|
|
107
|
+
- **Bar 1-2:** Breakbeat only — establish the syncopated groove immediately
|
|
108
|
+
- **Bar 3-4:** Add hi-hat fills between kick/snare — density builds
|
|
109
|
+
- **Bar 5-8:** Add Reese bass — low sustained notes, filter opens slowly
|
|
110
|
+
- **Bar 9-16:** Full arrangement — breakbeat + Reese + atmospheric pad
|
|
111
|
+
- **Variation bar:** Every 4 bars, drop a kick or add a snare roll for surprise
|
|
112
|
+
- **Reese movement:** Filter automation — close at bar 5, sweep open over bar 6-8, close and re-open
|
|
113
|
+
- **Outro:** Break strip — just pad remains, then breakbeat returns for exit
|
|
114
|
+
|
|
115
|
+
The breakbeat is the identity. The kick landing OFF the downbeat is what separates DnB from techno — resist putting the kick on beat 1. The snare DOES land around beats 2 and 4 but offset slightly.
|
|
116
|
+
|
|
117
|
+
## Example Variations
|
|
118
|
+
|
|
119
|
+
### 1 — Half-time feel bridge
|
|
120
|
+
```js
|
|
121
|
+
// Every 8 bars, drop to a 2-step pattern (kick-snare only) for 1 bar
|
|
122
|
+
// Builds tension before the full break returns
|
|
123
|
+
const halfTimeBar = ["kick", null, null, null, null, null, null, null,
|
|
124
|
+
null, null, null, null, "snare", null, null, null];
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 2 — Reese filter wobble (1/4 note LFO)
|
|
128
|
+
```js
|
|
129
|
+
// LFO modulating the Reese filter — creates rhythmic wobble
|
|
130
|
+
const lfo = new Tone.LFO({ frequency: "4n", min: 200, max: 2000, type: "sine" });
|
|
131
|
+
lfo.connect(reeseFilter.frequency);
|
|
132
|
+
lfo.start();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 3 — Snare roll into drop
|
|
136
|
+
```js
|
|
137
|
+
// 16th-note snare roll over the last bar before a new section
|
|
138
|
+
// Schedule 8 snare hits in last bar
|
|
139
|
+
for (let i = 0; i < 8; i++) {
|
|
140
|
+
snare.triggerAttackRelease("32n", `${barStart} + ${i * 0.107}`, 0.3 + i * 0.087);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>TuneFrames — Drum and Bass</title>
|
|
6
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<!-- TuneFrames Metadata -->
|
|
10
|
+
<div id="tuneframes" style="display:none">{"bpm":174,"duration":"12s"}</div>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
async function main() {
|
|
14
|
+
await Tone.start();
|
|
15
|
+
Tone.Transport.bpm.value = 174;
|
|
16
|
+
|
|
17
|
+
// --- MASTER BUS ---
|
|
18
|
+
const masterLimiter = new Tone.Limiter(-1).toDestination();
|
|
19
|
+
const masterGain = new Tone.Gain(0.87).connect(masterLimiter);
|
|
20
|
+
|
|
21
|
+
// --- KICK — short, punchy, sub-heavy ---
|
|
22
|
+
const kickGain = new Tone.Gain(0.9).connect(masterGain);
|
|
23
|
+
const kick = new Tone.MembraneSynth({
|
|
24
|
+
pitchDecay: 0.038,
|
|
25
|
+
octaves: 5,
|
|
26
|
+
envelope: { attack: 0.001, decay: 0.18, sustain: 0, release: 0.07 }
|
|
27
|
+
}).connect(kickGain);
|
|
28
|
+
|
|
29
|
+
// --- SNARE — crisp white noise ---
|
|
30
|
+
const snareVerb = new Tone.Reverb({ decay: 0.45, wet: 0.14 }).connect(masterGain);
|
|
31
|
+
const snareGain = new Tone.Gain(0.65).connect(snareVerb);
|
|
32
|
+
const snare = new Tone.NoiseSynth({
|
|
33
|
+
noise: { type: "white" },
|
|
34
|
+
envelope: { attack: 0.001, decay: 0.11, sustain: 0, release: 0.035 }
|
|
35
|
+
}).connect(snareGain);
|
|
36
|
+
|
|
37
|
+
// --- HI-HAT — fast texture between beats ---
|
|
38
|
+
const hatGain = new Tone.Gain(0.28).connect(masterGain);
|
|
39
|
+
const hat = new Tone.MetalSynth({
|
|
40
|
+
frequency: 520,
|
|
41
|
+
envelope: { attack: 0.001, decay: 0.032, release: 0.01 },
|
|
42
|
+
harmonicity: 5.1,
|
|
43
|
+
modulationIndex: 32,
|
|
44
|
+
resonance: 4600,
|
|
45
|
+
octaves: 1.5
|
|
46
|
+
}).connect(hatGain);
|
|
47
|
+
|
|
48
|
+
// --- REESE BASS — MonoSynth sawtooth + Chorus + Filter ---
|
|
49
|
+
// The Reese is the defining DnB sound: two slightly detuned saws = chorus
|
|
50
|
+
const reeseLimiter = new Tone.Limiter(-4).connect(masterGain);
|
|
51
|
+
const reeseFilter = new Tone.Filter({ frequency: 500, type: "lowpass", Q: 2.5 }).connect(reeseLimiter);
|
|
52
|
+
const reeseChorus = new Tone.Chorus({
|
|
53
|
+
frequency: 1.7,
|
|
54
|
+
delayTime: 3.5,
|
|
55
|
+
depth: 0.78,
|
|
56
|
+
wet: 0.72
|
|
57
|
+
}).connect(reeseFilter);
|
|
58
|
+
reeseChorus.start();
|
|
59
|
+
const reese = new Tone.MonoSynth({
|
|
60
|
+
oscillator: { type: "sawtooth" },
|
|
61
|
+
filter: { Q: 1.5, type: "lowpass", frequency: 3000 },
|
|
62
|
+
filterEnvelope: {
|
|
63
|
+
attack: 0.02, decay: 0.6, sustain: 0.5, release: 0.4,
|
|
64
|
+
baseFrequency: 180, octaves: 2.5
|
|
65
|
+
},
|
|
66
|
+
envelope: { attack: 0.008, decay: 0.4, sustain: 0.8, release: 0.35 },
|
|
67
|
+
volume: 0
|
|
68
|
+
}).connect(reeseChorus);
|
|
69
|
+
|
|
70
|
+
// --- ATMOSPHERIC PAD — slow attack, very wet reverb ---
|
|
71
|
+
const padVerb = new Tone.Reverb({ decay: 5.5, wet: 0.72 }).connect(masterGain);
|
|
72
|
+
const padGain = new Tone.Gain(0.45).connect(padVerb);
|
|
73
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
74
|
+
oscillator: { type: "sawtooth4" },
|
|
75
|
+
envelope: { attack: 0.7, decay: 1.2, sustain: 0.6, release: 3.0 },
|
|
76
|
+
volume: -2
|
|
77
|
+
}).connect(padGain);
|
|
78
|
+
|
|
79
|
+
// --- BREAKBEAT PATTERN — syncopated Amen-style ---
|
|
80
|
+
// K=kick, S=snare, h=hat, .=rest
|
|
81
|
+
// Pattern: . . S . K K S . . K . S K . S h
|
|
82
|
+
const breakSeq = new Tone.Sequence((time, hit) => {
|
|
83
|
+
if (hit === "K") kick.triggerAttackRelease("C1", "8n", time);
|
|
84
|
+
if (hit === "S") snare.triggerAttackRelease("16n", time, 0.85);
|
|
85
|
+
if (hit === "s") snare.triggerAttackRelease("16n", time, 0.45); // ghost snare
|
|
86
|
+
if (hit === "h") hat.triggerAttackRelease("32n", time, 0.5);
|
|
87
|
+
}, [
|
|
88
|
+
null, "h", "S", null, "K", "K", "S", null,
|
|
89
|
+
"h", "K", null, "S", "K", "h", "S", "h"
|
|
90
|
+
], "16n");
|
|
91
|
+
breakSeq.start(0);
|
|
92
|
+
|
|
93
|
+
// --- REESE BASS LINE — D minor, sustained long notes ---
|
|
94
|
+
// Reese bass plays whole-bar notes — the filter movement IS the melody
|
|
95
|
+
const reeseLine = [
|
|
96
|
+
{ note: "D1", dur: "1m", time: "2m" },
|
|
97
|
+
{ note: "C1", dur: "1m", time: "3m" },
|
|
98
|
+
{ note: "Bb0", dur: "1m", time: "4m" },
|
|
99
|
+
{ note: "A0", dur: "1m", time: "5m" },
|
|
100
|
+
{ note: "D1", dur: "1m", time: "6m" },
|
|
101
|
+
{ note: "F1", dur: "1m", time: "7m" },
|
|
102
|
+
];
|
|
103
|
+
reeseLine.forEach(({ note, dur, time }) => {
|
|
104
|
+
reese.triggerAttackRelease(note, dur, time);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Filter sweep on Reese — opens over bars 3-5, closes back bar 6-7
|
|
108
|
+
const sweepStart = Tone.Time("2m").toSeconds();
|
|
109
|
+
reeseFilter.frequency.setValueAtTime(300, sweepStart);
|
|
110
|
+
reeseFilter.frequency.exponentialRampToValueAtTime(2200, sweepStart + 4);
|
|
111
|
+
reeseFilter.frequency.exponentialRampToValueAtTime(250, sweepStart + 5.5);
|
|
112
|
+
reeseFilter.frequency.exponentialRampToValueAtTime(1800, sweepStart + 9);
|
|
113
|
+
|
|
114
|
+
// --- PAD — atmospheric Dm chord, enters at bar 4 ---
|
|
115
|
+
const padNotes = ["D3", "F3", "A3", "C4"]; // Dm7
|
|
116
|
+
pad.triggerAttackRelease(padNotes, "4m", "3m");
|
|
117
|
+
pad.triggerAttackRelease(["C3", "Eb3", "G3", "Bb3"], "4m", "7m"); // Cm7
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
main();
|
|
122
|
+
</script>
|
|
123
|
+
</body>
|
|
124
|
+
</html>
|