tuneframes 0.1.0 → 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 +167 -199
- package/README.md +150 -97
- 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 +48 -0
- package/examples/example-lofi.html +45 -45
- package/examples/example-lofi.mp3 +0 -0
- package/examples/example-minimal.html +21 -19
- package/examples/example-minimal.mp3 +0 -0
- package/examples/example-orchestral.html +67 -67
- package/examples/example-orchestral.mp3 +0 -0
- package/examples/example-piano.html +53 -0
- 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 -24
- 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 +261 -89
- package/src/render.js +134 -7
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
<div id="tuneframes" style="display:none">{"bpm":98,"duration":"12s"}</div>
|
|
2
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
3
|
+
<script>
|
|
4
|
+
// TuneFrames — Chillwave / Retrowave
|
|
5
|
+
// A minor arpeggio over 80s drum machine, warm chorus + delay
|
|
6
|
+
// Nostalgic, hazy, neon-at-midnight
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
await Tone.start();
|
|
10
|
+
|
|
11
|
+
Tone.Transport.bpm.value = 98;
|
|
12
|
+
|
|
13
|
+
// ── Effects chain ──────────────────────────────────────────────────────────
|
|
14
|
+
const reverb = new Tone.Reverb({ decay: 3.5, preDelay: 0.02, wet: 0.5 });
|
|
15
|
+
await reverb.generate();
|
|
16
|
+
reverb.toDestination();
|
|
17
|
+
|
|
18
|
+
// Dotted-8th delay = the defining retrowave slap
|
|
19
|
+
const delay = new Tone.FeedbackDelay({ delayTime: '8n.', feedback: 0.32, wet: 0.38 });
|
|
20
|
+
delay.connect(reverb);
|
|
21
|
+
|
|
22
|
+
// Chorus — smears everything into warm tape haze
|
|
23
|
+
const chorus = new Tone.Chorus({ frequency: 1.4, delayTime: 3.5, depth: 0.72, wet: 0.62 }).start();
|
|
24
|
+
chorus.connect(delay);
|
|
25
|
+
|
|
26
|
+
// ── Arpeggio synth — sawtooth for shimmer ─────────────────────────────────
|
|
27
|
+
const arp = new Tone.Synth({
|
|
28
|
+
oscillator: { type: 'sawtooth' },
|
|
29
|
+
envelope: { attack: 0.01, decay: 0.12, sustain: 0.28, release: 0.45 },
|
|
30
|
+
volume: -6,
|
|
31
|
+
});
|
|
32
|
+
arp.connect(chorus);
|
|
33
|
+
|
|
34
|
+
// ── Background pad — PolySynth triangle, long attack ─────────────────────
|
|
35
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
36
|
+
oscillator: { type: 'triangle' },
|
|
37
|
+
envelope: { attack: 0.8, decay: 0.5, sustain: 0.75, release: 2.5 },
|
|
38
|
+
volume: -18,
|
|
39
|
+
});
|
|
40
|
+
pad.connect(reverb);
|
|
41
|
+
|
|
42
|
+
// ── Kick — four-on-the-floor, enters at 2s ────────────────────────────────
|
|
43
|
+
const kick = new Tone.MembraneSynth({
|
|
44
|
+
pitchDecay: 0.06, octaves: 8,
|
|
45
|
+
envelope: { attack: 0.001, decay: 0.38, sustain: 0, release: 0.1 },
|
|
46
|
+
volume: -4,
|
|
47
|
+
}).toDestination();
|
|
48
|
+
|
|
49
|
+
// ── Hi-hat — 16th notes, slightly gated ───────────────────────────────────
|
|
50
|
+
const hihat = new Tone.NoiseSynth({
|
|
51
|
+
noise: { type: 'white' },
|
|
52
|
+
envelope: { attack: 0.001, decay: 0.04, sustain: 0, release: 0.01 },
|
|
53
|
+
volume: -17,
|
|
54
|
+
}).toDestination();
|
|
55
|
+
|
|
56
|
+
// ── Clap — sits on 2 and 4 ───────────────────────────────────────────────
|
|
57
|
+
const clap = new Tone.NoiseSynth({
|
|
58
|
+
noise: { type: 'pink' },
|
|
59
|
+
envelope: { attack: 0.001, decay: 0.13, sustain: 0, release: 0.08 },
|
|
60
|
+
volume: -10,
|
|
61
|
+
}).toDestination();
|
|
62
|
+
|
|
63
|
+
// ── Schedule arpeggio ─────────────────────────────────────────────────────
|
|
64
|
+
// A minor → F → C → G, 16th-note arpeggio pattern
|
|
65
|
+
// At 98 BPM, one 16th note = 60/98/4 ≈ 0.153s
|
|
66
|
+
const sixteenth = 60 / 98 / 4;
|
|
67
|
+
const arpNotes = [
|
|
68
|
+
// Am: A4 C5 E5 A5
|
|
69
|
+
'A4','C5','E5','A5',
|
|
70
|
+
// F: F4 A4 C5 F5
|
|
71
|
+
'F4','A4','C5','F5',
|
|
72
|
+
// C: C4 E4 G4 C5
|
|
73
|
+
'C4','E4','G4','C5',
|
|
74
|
+
// G: G4 B4 D5 G5
|
|
75
|
+
'G4','B4','D5','G5',
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
// Four passes of the 16-note arpeggio
|
|
79
|
+
for (let pass = 0; pass < 4; pass++) {
|
|
80
|
+
const passStart = pass * arpNotes.length * sixteenth;
|
|
81
|
+
arpNotes.forEach((note, i) => {
|
|
82
|
+
arp.triggerAttackRelease(note, '16n', passStart + i * sixteenth);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ── Pad chords — one per bar (Am, F, C, G, repeating) ────────────────────
|
|
87
|
+
const beat = 60 / 98;
|
|
88
|
+
const padChords = [
|
|
89
|
+
{ notes: ['A3','C4','E4'], t: 0.8 },
|
|
90
|
+
{ notes: ['F3','A3','C4'], t: 0.8 + beat * 4 },
|
|
91
|
+
{ notes: ['C3','E3','G3'], t: 0.8 + beat * 8 },
|
|
92
|
+
{ notes: ['G2','B2','D3'], t: 0.8 + beat * 12 },
|
|
93
|
+
];
|
|
94
|
+
padChords.forEach(({ notes, t }) => {
|
|
95
|
+
pad.triggerAttackRelease(notes, '2n', t);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// ── Drums — enter at 2s ───────────────────────────────────────────────────
|
|
99
|
+
const drumStart = 2.0;
|
|
100
|
+
|
|
101
|
+
// Four-on-the-floor kick
|
|
102
|
+
for (let i = 0; i < 10; i++) {
|
|
103
|
+
const t = drumStart + i * beat;
|
|
104
|
+
if (t < 12) {
|
|
105
|
+
kick.triggerAttackRelease('C1', '8n', t);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 16th hi-hats
|
|
110
|
+
for (let i = 0; i < 40; i++) {
|
|
111
|
+
const t = drumStart + i * sixteenth;
|
|
112
|
+
if (t < 12) {
|
|
113
|
+
hihat.triggerAttackRelease('16n', t);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Clap on 2 and 4 (every other beat, starting on beat 2)
|
|
118
|
+
for (let bar = 0; bar < 3; bar++) {
|
|
119
|
+
[beat, beat * 3].forEach((offset) => {
|
|
120
|
+
const t = drumStart + bar * beat * 4 + offset;
|
|
121
|
+
if (t < 12) {
|
|
122
|
+
clap.triggerAttackRelease('8n', t);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
window.renderComposition = async function(wavPath) {
|
|
129
|
+
let attempts = 0;
|
|
130
|
+
while (typeof Tone === 'undefined' && attempts < 50) {
|
|
131
|
+
await new Promise(r => setTimeout(r, 100));
|
|
132
|
+
attempts++;
|
|
133
|
+
}
|
|
134
|
+
if (typeof Tone === 'undefined') throw new Error('Tone not loaded');
|
|
135
|
+
|
|
136
|
+
const meta = JSON.parse(document.getElementById('tuneframes').textContent);
|
|
137
|
+
const durationSec = parseFloat(meta.duration) + 1.0;
|
|
138
|
+
|
|
139
|
+
const buffer = await Tone.Offline(async () => { await main(); }, durationSec, 1, 44100);
|
|
140
|
+
const wav = audioBufferToWav(buffer);
|
|
141
|
+
window.writeFile(wavPath, Array.from(new Uint8Array(wav)));
|
|
142
|
+
return wav.byteLength;
|
|
143
|
+
};
|
|
144
|
+
</script>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-cinematic
|
|
3
|
+
description: Cinematic Score — epic orchestral builds, pad swells, ostinato, tension and release
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Cinematic Score
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 60–90 (slow and grand; let notes breathe)
|
|
10
|
+
- Key characteristics: Slow harmonic rhythm, long-attack pads, ostinato string figures, brass swells on climax, no drums until climax (if at all), layered dynamics from pp to ff
|
|
11
|
+
- Typical instruments: String pads (PolySynth sawtooth, long attack), brass (PolySynth sawtooth, fast attack), choir pad (PolySynth sine), low piano (Synth), timpani (MembraneSynth)
|
|
12
|
+
- Mood: Epic, emotional, suspenseful, grand, sweeping — Zimmer / Morricone / Williams
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Cinematic core: 72 BPM, Dm–Bb–F–C (minor epic progression)
|
|
18
|
+
async function main() {
|
|
19
|
+
await Tone.start();
|
|
20
|
+
Tone.Transport.bpm.value = 72;
|
|
21
|
+
|
|
22
|
+
const bar = Tone.Time('1n').toSeconds(); // ~3.33s at 72 BPM
|
|
23
|
+
|
|
24
|
+
// ── Grand reverb hall ────────────────────────────────────────────────
|
|
25
|
+
const hall = new Tone.Reverb({ decay: 6, preDelay: 0.05, wet: 0.6 }).toDestination();
|
|
26
|
+
const hall2 = new Tone.Reverb({ decay: 4, wet: 0.4 }).toDestination();
|
|
27
|
+
|
|
28
|
+
// ── Strings pad — slow attack, swell ─────────────────────────────────
|
|
29
|
+
const strings = new Tone.PolySynth(Tone.Synth, {
|
|
30
|
+
oscillator: { type: 'sawtooth' },
|
|
31
|
+
envelope: { attack: 1.8, decay: 0.5, sustain: 0.85, release: 4.0 }
|
|
32
|
+
}).connect(hall);
|
|
33
|
+
strings.volume.value = -14;
|
|
34
|
+
|
|
35
|
+
// Dm–Bb–F–C progression (1 bar each)
|
|
36
|
+
const prog = [
|
|
37
|
+
['D3','F3','A3'], // Dm
|
|
38
|
+
['Bb2','D3','F3'], // Bb
|
|
39
|
+
['F2','A2','C3'], // F
|
|
40
|
+
['C3','E3','G3'], // C
|
|
41
|
+
];
|
|
42
|
+
prog.forEach((ch, i) => strings.triggerAttackRelease(ch, '1n', i * bar));
|
|
43
|
+
|
|
44
|
+
// ── Piano ostinato (enters bar 2, driving 8ths) ───────────────────────
|
|
45
|
+
const piano = new Tone.Synth({
|
|
46
|
+
oscillator: { type: 'sine' },
|
|
47
|
+
envelope: { attack: 0.005, decay: 0.3, sustain: 0.1, release: 0.8 }
|
|
48
|
+
}).connect(hall2);
|
|
49
|
+
piano.volume.value = -16;
|
|
50
|
+
|
|
51
|
+
const q = Tone.Time('4n').toSeconds();
|
|
52
|
+
const ostinato = ['D3','A3','D4','A3','F3','A3','D4','A3']; // D-A-D-A pattern
|
|
53
|
+
ostinato.forEach((n, i) => piano.triggerAttackRelease(n, '8n', bar + i * q * 0.5));
|
|
54
|
+
|
|
55
|
+
// ── Brass swell (enters bar 3) ────────────────────────────────────────
|
|
56
|
+
const brass = new Tone.PolySynth(Tone.Synth, {
|
|
57
|
+
oscillator: { type: 'sawtooth' },
|
|
58
|
+
envelope: { attack: 0.4, decay: 0.2, sustain: 0.8, release: 2.0 }
|
|
59
|
+
}).connect(hall);
|
|
60
|
+
brass.volume.value = -18;
|
|
61
|
+
|
|
62
|
+
brass.triggerAttackRelease(['F3','A3','C4'], '2n', bar * 2);
|
|
63
|
+
brass.triggerAttackRelease(['C3','E3','G3'], '2n', bar * 3);
|
|
64
|
+
|
|
65
|
+
// ── Timpani on climax ─────────────────────────────────────────────────
|
|
66
|
+
const timp = new Tone.MembraneSynth({
|
|
67
|
+
pitchDecay: 0.08, octaves: 5,
|
|
68
|
+
envelope: { attack: 0.001, decay: 1.2, sustain: 0, release: 2.0 }
|
|
69
|
+
}).connect(hall);
|
|
70
|
+
timp.volume.value = -10;
|
|
71
|
+
|
|
72
|
+
timp.triggerAttackRelease('D2', '4n', bar * 2);
|
|
73
|
+
timp.triggerAttackRelease('A1', '4n', bar * 3);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Instrument Configuration
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
// Strings — sawtooth for harmonic richness, very slow attack for swell
|
|
81
|
+
const strings = new Tone.PolySynth(Tone.Synth, {
|
|
82
|
+
oscillator: { type: 'sawtooth' },
|
|
83
|
+
envelope: { attack: 1.8, decay: 0.5, sustain: 0.85, release: 4.0 }
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Choir/pad layer — pure sine, even slower attack for the "ahh" effect
|
|
87
|
+
const choir = new Tone.PolySynth(Tone.Synth, {
|
|
88
|
+
oscillator: { type: 'sine' },
|
|
89
|
+
envelope: { attack: 2.5, decay: 0.3, sustain: 0.9, release: 5.0 }
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Brass — faster attack, shorter release (punchy swell)
|
|
93
|
+
const brass = new Tone.PolySynth(Tone.Synth, {
|
|
94
|
+
oscillator: { type: 'sawtooth' },
|
|
95
|
+
envelope: { attack: 0.3, decay: 0.2, sustain: 0.8, release: 2.0 }
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Piano ostinato — percussive, short sustain, clean
|
|
99
|
+
const piano = new Tone.Synth({
|
|
100
|
+
oscillator: { type: 'sine' },
|
|
101
|
+
envelope: { attack: 0.005, decay: 0.3, sustain: 0.1, release: 0.8 }
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Hall reverb — long decay for cinematic space
|
|
105
|
+
const hall = new Tone.Reverb({ decay: 6, preDelay: 0.05, wet: 0.6 }).toDestination();
|
|
106
|
+
|
|
107
|
+
// Volume automation for the build:
|
|
108
|
+
strings.volume.rampTo(-8, 4); // swell from -14 to -8 over 4s
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Composition Structure
|
|
112
|
+
|
|
113
|
+
1. **Silence / soft pad intro (0–3s):** Choir pad alone on tonic, barely audible — establishes space
|
|
114
|
+
2. **Strings enter (3–6s):** Slow-attack string swell on Dm, then Bb — harmonic foundation
|
|
115
|
+
3. **Ostinato begins (6–9s):** Piano or cello ostinato (repeating 8th-note figure) adds momentum
|
|
116
|
+
4. **Tension build (9–12s):** Strings move to dissonant chord (dim or sus), dynamics rise
|
|
117
|
+
5. **Brass climax (12–15s):** Full brass swell on the tonic, timpani hit, everything fortissimo
|
|
118
|
+
6. **Release / decay (final 2s):** Let hall reverb tail ring out — cut instruments, tail continues
|
|
119
|
+
|
|
120
|
+
## Example Variations
|
|
121
|
+
|
|
122
|
+
### 1 — Tension chord (diminished, no resolution)
|
|
123
|
+
```js
|
|
124
|
+
// Hold on a dim7 for maximum suspense before resolving
|
|
125
|
+
const dim = ['F#3','A3','C4','Eb4']; // F#dim7
|
|
126
|
+
strings.triggerAttackRelease(dim, '2n', tensionTime);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 2 — Morricone-style solo melody (over strings)
|
|
130
|
+
```js
|
|
131
|
+
// High, yearning single note melody in the upper register
|
|
132
|
+
const solo = new Tone.Synth({
|
|
133
|
+
oscillator: { type: 'sine' },
|
|
134
|
+
envelope: { attack: 0.3, decay: 0.2, sustain: 0.9, release: 2.0 }
|
|
135
|
+
});
|
|
136
|
+
// Phrase: D5–C5–Bb4–A4 (descending minor scale, emotional)
|
|
137
|
+
const morriconeLine = ['D5','C5','Bb4','A4','F4','A4','D5'];
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 3 — Ostinato acceleration (builds tension)
|
|
141
|
+
```js
|
|
142
|
+
// Start with quarter-note ostinato, switch to 8ths on bar 3, 16ths on bar 4
|
|
143
|
+
const oSlow = Tone.Time('4n').toSeconds();
|
|
144
|
+
const oFast = Tone.Time('8n').toSeconds();
|
|
145
|
+
const oFast2 = Tone.Time('16n').toSeconds();
|
|
146
|
+
// Schedule the same note at progressively tighter intervals
|
|
147
|
+
```
|
|
@@ -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
|
+
```
|