Add 'sound_algo/' from commit '38b17c42a0b5d2c7f52a2ad600316282cc52a02d'

git-subtree-dir: sound_algo
git-subtree-mainline: b7940d650f
git-subtree-split: 38b17c42a0
This commit is contained in:
L'électron rare
2026-05-07 11:51:51 +02:00
4627 changed files with 125334 additions and 0 deletions
@@ -0,0 +1,118 @@
---
name: adding-synthdef
description: Use when the user asks to "add a SynthDef", "create a new instrument", "ajouter un instrument", or names a synthesis technique to package (FM, granular, Karplus-Strong, formant, etc.). Generates a SynthDef under `synth/<category>/` (drums, bass, lead, pad, world, master) for instruments, or `fx/<type>/` (bus, insert, trick) for effects. The auto-loaders `synth/_index.scd` and `fx/_index.scd` discover new files recursively. Follows the rigid sound_algo convention: standard arg signature (freq/gate/ADSR/amp/pan/scBus/scAmt), `EnvGen.kr(..., doneAction: 2)`, `Pan2.ar` final, `LeakDC.ar` after feedback, `clip` on freq, `tanh` saturation pattern. Forgetting `doneAction: 2` creates Synth zombies that accumulate in RAM until the server crashes.
---
# Adding a SynthDef
## Where to put it
Pick a directory based on the SynthDef's role. The auto-loader (`_index.scd`)
recursively loads every `.scd` in the tree, so a new file is picked up
automatically without editing any index.
| Directory | Theme |
| --- | --- |
| `synth/drums/` | Kicks, snares, claps, hats, percs, taiko, tabla |
| `synth/bass/` | Sub bass, FM bass, reese, hooverbass |
| `synth/lead/` | Lead synths, plucks, scratches |
| `synth/pad/` | Warm pads, choirs, strings, drones |
| `synth/world/` | Exotic/world instruments (koto, erhu, didgeridoo, ...) |
| `synth/master/` | Master-bus instruments / utility synths |
| `fx/bus/` | Send-bus FX (reverb, delay, chorus, ...) |
| `fx/insert/` | Insert FX on a single channel |
| `fx/trick/` | Live tricks (freeze, glitch, stutter, ...) |
| `engine.scd` | DO NOT add here -- it's the core, kept stable |
## Standard arg signature
Every SynthDef MUST accept these args (with defaults) :
```supercollider
arg freq = 220, gate = 1, cutoff = 6000, rq = 0.5,
attack = 0.001, decay = 0.04, sustain = 0.0, release = 0.6,
drive = 1.2, amp = 0.22, pan = 0,
scBus = 0, scAmt = 0.4;
```
This way the SynthDef is a drop-in replacement in `~melodyInst`, `~kickInst`, etc.
## Template
```supercollider
SynthDef(\<instName>, {
arg freq = 220, gate = 1, cutoff = 6000, rq = 0.5,
attack = 0.001, decay = 0.04, sustain = 0.0, release = 0.6,
drive = 1.2, amp = 0.22, pan = 0,
scBus = 0, scAmt = 0.4;
var sig, env, duck;
// --- 1. Synthesis core (oscillator / noise / sample / ...) -------
sig = ...;
// --- 2. Filter (LPF/RLPF/HPF/...) --------------------------------
sig = LPF.ar(sig, freq.clip(40, 18000));
// --- 3. Drive / saturation (preserve gain) -----------------------
sig = (sig * drive).tanh / drive.sqrt;
// --- 4. Leak DC if any feedback / waveshaping --------------------
sig = LeakDC.ar(sig);
// --- 5. Envelope with doneAction:2 (free node when env ends) ----
env = EnvGen.kr(Env.adsr(attack, decay, sustain, release),
gate, doneAction: 2);
// --- 6. Sidechain duck (kick -> instrument) ---------------------
duck = 1 - (In.kr(scBus, 1) * scAmt);
// --- 7. Output : Pan2 stereo ------------------------------------
Out.ar(0, Pan2.ar(sig * env * amp * duck, pan));
}).add;
```
## Mandatory invariants
| Rule | Reason |
| --- | --- |
| `doneAction: 2` on EnvGen.kr | Without it, Synth zombies pile up forever |
| `Pan2.ar(sig, pan)` | Mono `[sig, sig]` breaks the mixer's stereo bus |
| `freq.clip(min, max)` | Unclamped freq -> NaN/Inf at filter |
| `(sig * drive).tanh / drive.sqrt` | Preserves perceived loudness across drive |
| `LeakDC.ar` after feedback | Comb/Karplus need it or DC offset accumulates |
| `In.kr(scBus, 1) * scAmt` | Standard sidechain bus pattern |
| Defaults on EVERY arg | Pdef breaks if any arg is missing |
## Synthesis recipes
```supercollider
// Karplus-Strong (plucked string)
exciter = WhiteNoise.ar(0.6) * EnvGen.kr(Env.perc(0.0005, 0.003));
sig = LeakDC.ar(CombC.ar(exciter, 0.05, freq.reciprocal, 3.0));
// FM bell
mod = SinOsc.ar(freq * 1.4) * 200 * EnvGen.kr(Env.perc(0.001, 0.5));
sig = SinOsc.ar(freq + mod);
// Granular pad
sig = GrainSin.ar(2, Impulse.kr(20), 0.1, freq, pan);
// Formant vowel
sig = Formant.ar(freq, formantFreq, bw);
```
## After creation
1. Run `validating-scd-files` on the synthdefs file (P:0 B:0, TLB:1).
2. Test SynthDef compilation : `tests/e2e_04_synthdef_compile.scd` should
still pass.
3. Add the new symbol to the README's instrument list if user-facing.
## Anti-patterns
- **No `doneAction: 2`** -> Synth zombies, RAM leak, eventual crash.
- **`Out.ar(0, sig)`** -> mono, breaks panning + stereo mixer.
- **Hardcoded `0.5` for amp** -> not user-controllable.
- **Missing default value for an arg** -> Pdef crashes when scheduling.
- **Duplicating an existing SynthDef name** -> silently overwrites.
- **`SinOsc.ar(freq)` without clip** -> NaN if freq <= 0 from a modulator.
@@ -0,0 +1,119 @@
---
name: generating-live-wrappers
description: Use when the user asks to "add a live wrapper", "expose dict X to live API", "ajouter une API live", or creates a new dict catalogue (`~newDict = ()`) that needs a live entry. Generates the dual API pattern used across the 5 sound_algo namespaces (`~kk`, `~mm`, `~ff`, `~cc`, `~p`): a dict for catalogue access AND env-direct wrappers for actual calls. Pseudo-method `~dict.key.(args)` is broken in SC (Event passes ~dict as 1st arg, real args lost — empirical bug on SC 3.14.1, see `live/melodies.scd`). Without env-direct wrappers, the live API is unusable.
---
# Generating live wrappers
## The problem this solves
```supercollider
// FAUX -- args lost, ~m passed as 1st arg
~m.short.(50)
// OK
~m[\short].(50) // bracket notation
~mShort.(50) // env-direct wrapper (PREFERRED)
~mm.(50) // short alias
```
Toute API live exposée via dict DOIT avoir des wrappers env directs, sinon
les utilisateurs se retrouvent à taper de la bracket-notation lourdingue.
## Reserved-name collisions
Ces clefs explosent silencieusement à cause de méthodes Object/Server :
| Réservé | Renommer en |
| --- | --- |
| `.s` | `.short` |
| `.l` | `.long` |
| `.x` | `.xlong` |
| `.e` | `.epic` |
| `.r` | `.rnd` |
| `.p` | `.pat` |
| `.t` | `.tim` |
| `.next` | `.nx` |
| `.prev` | `.pv` |
| `.send` | `.snd` |
| `.set` | `.st` |
| `.tap` | `.tp` |
| `.value` | (interdit -- méthode Function) |
## Template (1 dict + N wrappers)
```supercollider
(
// --- Dict catalogue ----------------------------------------------
~<dict> = (
\<key1>: { |arg1, arg2| /* impl */ },
\<key2>: { |arg1| /* impl */ },
\list: { ~<dict>.keys.asArray.sort.do(_.postln) }
);
// --- Env-direct wrappers (API utilisateur primaire) -------------
~<dictPrefix><Key1> = { |arg1, arg2| ~<dict>[\<key1>].(arg1, arg2) };
~<dictPrefix><Key2> = { |arg1| ~<dict>[\<key2>].(arg1) };
~<dictPrefix>List = { ~<dict>[\list].() };
// --- Alias court (optionnel) -------------------------------------
~<short> = ~<dictPrefix><Key1>; // ~mm = ~mShort, ~kk = ~kkTechno
"[OK] <dictPrefix> API ready".postln;
)
```
## Naming conventions
- **Dict** : `~<2-letter-name>` ou `~<short-word>` (ex: `~m`, `~kk`, `~ff`, `~ch`, `~p`)
- **Wrapper prefix** : same letters + Capitalized key (ex: `~mShort`, `~kkTechno`, `~fxDrop`, `~chIntro`, `~pHat`)
- **Short alias** : single 2-letter env var (ex: `~mm`, `~kk`, `~ff`, `~cc`)
## Mandatory invariants
| Rule | Why |
| --- | --- |
| Dict + env-direct wrappers (both) | Pseudo-method broken |
| Dict keys avoid reserved names | Crashes silently otherwise |
| Wrappers call `~dict[\key].()` not `~dict.key.()` | Same trap |
| Print `[OK] <name> API ready` at end | User sees confirmation |
| Wrapped in `(...)` single block | `.load` compatibility |
| Comment in French at top | Project convention |
## Concrete example (extracted from `live/melodies.scd`)
```supercollider
(
~m = (
\short: { |n| (~useMelody !? { ~useMelody.(("m"++n).asSymbol) }) ? nil },
\long: { |n| (~useMelody !? { ~useMelody.(("ml"++n).asSymbol) }) ? nil },
\rnd: { ~useMelodyRandom.value },
\nx: { ~melodyNext.value },
\pv: { ~melodyPrev.value }
);
~mShort = { |n| ~m[\short].(n) };
~mLong = { |n| ~m[\long].(n) };
~mRnd = { ~m[\rnd].() };
~mNx = { ~m[\nx].() };
~mPv = { ~m[\pv].() };
~mm = ~mShort; // alias court
"[OK] melody API ready".postln;
)
```
## After generation
1. Run `validating-scd-files` on the host file (P:0 B:0, TLB depends on file).
2. Add an example block in `01_live.scd` so users can discover the API.
3. Document the 5-namespace mapping at top of `live/CLAUDE.md` if a new
namespace is introduced.
## Anti-patterns
- **Dict only, no env-direct wrappers** : forces users into bracket notation,
breaks the `01_live.scd` cheat-sheet pattern.
- **Wrapper using `~dict.key.()`** : same bug it was meant to fix.
- **Reserved-name keys** : `.set`, `.send`, `.tap`, `.value`, `.s`, `.l`, ...
- **No `.list` key** : users can't discover available keys.
- **No `[OK] ... ready` log** : user can't tell if SETUP succeeded.
- **Wrapper that mutates global state without `~reload.value`** : Pdef stale.
@@ -0,0 +1,87 @@
---
name: registering-jump-section
description: Use when the user asks to "register a section", "add a jump target", "make section X jumpable", or after `scaffolding-track` finishes. Adds `~sections[\<LETTER>][\<slug>] = { ... }` to `control/jump.scd` so `~jumpTo.(\<LETTER>, \<slug>)` works during live play. The slug must match the timestamp log line in the track Routine. Without this skill, sections written in tracks are NOT jumpable from `01_live.scd` block [8].
---
# Registering a jump section
## Why this skill exists
`control/jump.scd` is a 2225-line append-only file that mirrors every section
of every track A-W. Each section is registered as a closure :
```supercollider
~sections[\A] = (
\minimal_kick_t5_sub_melody_pad: {
~bpm = 124; ~kickFreq = 42; ~melodyInst = \pad;
~melodyNotes = [60,0,0,0, ...];
},
\acid_rave_kick_t1_melody_lead_variations: {
~bpm = 132; ~melodyInst = \lead; ~acidCutoff = 800;
// ...
}
);
```
`~jumpTo.(\A, \acid_rave_kick_t1_melody_lead_variations)` then :
1. Stops `~track` and any pending Routines (`~filterSweep`, `~melodyEvolve`, ...)
2. Calls the closure (mutates `~xxx` vars)
3. Calls `~reload.value`
4. Replays the 8 standard Pdef
## Inputs to gather
1. **Track letter** : `\A` to `\W`.
2. **Section slug** : derived from the track's `.postln` log. Convert `"[A 2:00] acid rave kick t1 melody lead variations..."` to `\acid_rave_kick_t1_melody_lead_variations` (lowercase, underscores, no leading article).
3. **Mutation block** : the body of the section in the track Routine, **without** `TempoClock.default.tempo`, `~reload.value`, `Pdef.play` (the `~jumpTo` helper does those).
## Slug derivation rules
- Take the description after `[<L> M:SS] ` and before `...`.
- Lowercase everything.
- Replace spaces and punctuation with `_`.
- Strip leading articles ("the ", "le ", "la ").
- Collapse multiple underscores.
Examples :
| Log line | Slug |
| --- | --- |
| `[A 0:00] minimal -- kick T5 sub + melody PAD...` | `\minimal_kick_t5_sub_melody_pad` |
| `[E 3:30] DROP 1 -- growl dubstep + sub kick...` | `\drop_1_growl_dubstep_sub_kick` |
| `[F 2:00] uplifter 138 BPM...` | `\uplifter_138_bpm` |
## Insertion
Append to the existing `~sections[\<L>]` entry in `control/jump.scd`. If
`~sections[\<L>]` doesn't exist yet, create it (alphabetical order with
existing tracks).
```supercollider
~sections[\<L>][\<slug>] = {
~bpm = <N>;
// ... mutations from the track section, sans TempoClock/Pdef/reload
};
```
## Validation
After insertion :
1. Run `validating-scd-files` skill on `control/jump.scd` (P:0 B:0, TLB:1).
2. Test in SC : `~jumpTo.(\<L>, \<slug>)` should print `[JUMP] [<L> <slug>]`
and audibly switch the section.
3. Add a check in `tests/e2e_07_jump.scd` if the section is critical.
## Anti-patterns
- **Slug mismatch with track log** : silent jump lookup failure (just prints
`!! [<L> <slug>] inexistante`).
- **Including `~reload.value` in the closure** : called twice = potential
Pdef glitch.
- **Including `Pdef(...).play` in the closure** : `~jumpTo` already does it.
- **Calling `TempoClock.default.tempo` in the closure** : `~jumpTo` derives
it from `~bpm` if you set `~bpm`.
- **Forgetting to stop a custom Routine** : add it to `~jumpTo`'s stop list
if your section spawns its own (`~filterSweep`, `~melodyEvolve`, etc.).
@@ -0,0 +1,98 @@
---
name: scaffolding-track
description: Use when the user asks to "create a new track", "ajouter une track", "scaffolder une track X", or names a new track letter (A-W are taken, so X/Y/Z or beyond, OR overwriting an existing letter). Generates `tracks/<LETTER>_<slug>.scd` following the strict 23-track convention used in sound_algo: header banner, `~track !? { ~track.stop }` guard, `Routine` with timed sections, `~bpm`/`~kickFreq`/`~melodyInst`/`~melodyNotes` mutations, `TempoClock.default.tempo = ~bpm/60`, `~reload.value`, Pdef list `[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq, \acidSeq, \melodySeq, \harmonySeq]`, section logs `[L M:SS] description`, and `.wait` between sections. Forgetting any of these breaks live performance — this skill enforces them.
---
# Scaffolding a new track
## Inputs to gather
Ask the user (or infer from context):
1. **Letter** : single uppercase letter. A-W are taken; default to next free letter (X, Y, ...).
2. **Slug** : short snake_case name (`acid_journey`, `dub_techno`, `phonk`).
3. **Sections** : list of `(timestamp, name, BPM, vibe)` tuples. Minimum 3 sections (intro, drop, outro).
4. **Total duration** : sum of section waits. Typical track = 5-10 minutes.
5. **Style** : melody instrument (\pad/\lead/\saw3/\rhodes/\koto/...), kick timbre, scale.
## Template
```supercollider
// =====================================================================
// <LETTER>_<slug>.scd
// Track autonome -- Cmd+Entree sur le bloc (...) pour la lancer.
// PREREQUIS : engine.scd evalue (les SynthDef + helpers doivent exister).
// =====================================================================
// --- TRACK <LETTER> : "<TITLE>" <DURATION> minutes -------------------
// 0:00 <s1> -> M:SS <s2> -> M:SS <s3> -> ... -> M:SS outro
(
~track !? { ~track.stop };
~track = Routine({
"[<LETTER> 0:00] <section 1 description>...".postln;
~bpm = <BPM1>;
~kickFreq = 42; ~kickPitchAmt = 2.0; ~kickDecay = 0.5;
~kickAmp = 0.5; ~hatAmp = 0.08; ~melodyAmp = 0.14;
~melodyInst = \<inst1>;
~melodyAttack = 0.4; ~melodyRelease = 1.5; ~melodyCutoff = 1500;
~melodyNotes = [60,0,0,0, 0,0,0,0, 67,0,0,0, 0,0,0,0,
60,0,0,0, 0,0,0,0, 65,0,0,0, 0,0,0,0];
TempoClock.default.tempo = ~bpm/60;
~reload.value;
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).play };
<WAIT1>.wait;
"[<LETTER> M:SS] <section 2 description>...".postln;
~bpm = <BPM2>;
// ...mutations...
TempoClock.default.tempo = ~bpm/60;
~reload.value;
<WAIT2>.wait;
// ... more sections ...
"[<LETTER> END] outro".postln;
~masterFadeOut.(8);
}).play;
)
```
## Mandatory invariants
- `~track !? { ~track.stop }` BEFORE creating the new Routine. Without this, multiple Routines stack up.
- `~reload.value` after **every** mutation of `~xxx` parameters that feed Pdef.
- `TempoClock.default.tempo = ~bpm/60` whenever `~bpm` changes.
- One `.postln` log per section, format `"[<L> M:SS] <description>..."` -- this is parsed by tooling.
- `.wait` after each section (in seconds) -- sum should match total duration.
- Final `~masterFadeOut.(N)` (or stop Pdef explicitly) to avoid hanging Pdef.
## Variations within a section
Standard pattern for live mutations every 2-4 seconds inside a section :
```supercollider
30.do { |k|
if (0.25.coin) { ~acidCutoff = rrand(600, 1400); ~reload.value };
if (0.20.coin) {
~melodyNotes = ~genMelody.(0.55,
[~scaleMinorPent, ~scaleDorian].choose);
~reload.value;
};
4.wait; // 30 * 4 = 120s = 2 minutes
};
```
## After scaffolding
1. Run `validating-scd-files` skill on the new file (P:0 B:0, TLB:N OK for tracks).
2. Optionally use `registering-jump-section` skill to expose sections via `~jumpTo.(\<L>, \<slug>)`.
3. Add the track to `tests/e2e_09_tracks.scd` parse list.
## Anti-patterns to avoid
- **Hardcoded path** : never write absolute paths, use `~base`.
- **Section without log** : silent debug nightmare during live.
- **Total duration > 10 min without transition** : auditory fatigue.
- **Mutations without `~reload.value`** : Pdef stays stale, audible disconnect.
- **Reusing an A-W letter** : will overwrite an existing track.
@@ -0,0 +1,76 @@
---
name: validating-scd-files
description: Use when editing or creating any `.scd` file in the sound_algo project. Verifies that parentheses and square brackets are balanced (the project hard-requires P:0 B:0) and, for files loaded via `.load` (00_load.scd, live/_load.scd, palette/*/index.scd, engine.scd, palette/scales.scd), that there is exactly one top-level `(...)` block (TLB:1). The CLAUDE.md mandates this check after every modification — sclang gives parse errors at runtime that are far harder to diagnose than a quick awk balance.
---
# Validating `.scd` files
## When to run
Run after every edit to a `.scd` file. Mandatory before claiming a task is done.
## Quick check (one file)
```bash
awk '
{
for (i = 1; i <= length($0); i++) {
c = substr($0, i, 1)
if (c == "(") p++; else if (c == ")") p--
if (c == "[") b++; else if (c == "]") b--
# detect top-level "(" at column 1
if (i == 1 && c == "(") tlb++
}
}
END { printf "P:%d B:%d TLB:%d\n", p, b, tlb }
' path/to/file.scd
```
Acceptance :
- `P:0 B:0` for **all** `.scd` files
- `TLB:1` **only for files loaded via `.load`** (entry-point files). Multi-block files (tracks/*.scd, examples/*.scd, 01_live.scd) are IDE-only and `TLB:N` is fine there.
## Files that MUST have TLB:1
| File | Reason |
| --- | --- |
| `engine.scd` | Loaded via `00_load.scd` |
| `palette/scales.scd` | Same |
| `palette/melodies/index.scd` | Same |
| `palette/harmonics/index.scd` | Same |
| `palette/rhythm/fills.scd` | Same |
| `live/_load.scd` | Loaded by `01_live.scd` block [0] |
| Any other file referenced by `.load(...)` |
## Files where TLB:N is OK
- `tracks/*.scd` (Cmd+Entree per block during live play)
- `examples/*.scd` (tutorials)
- `01_live.scd` (live dashboard, blocks evaluated independently)
- `live/*.scd` except `_load.scd` (SETUP + examples in same file)
## Whole-project sweep
```bash
cd /Users/electron/Documents/Projets_Creatifs/sound_algo
for f in $(find . -name '*.scd' -not -path './_archive/*'); do
awk -v f="$f" '
{ for (i=1;i<=length($0);i++) { c=substr($0,i,1)
if (c=="(") p++; else if (c==")") p--
if (c=="[") b++; else if (c=="]") b-- }}
END { if (p!=0 || b!=0) printf "%s P:%d B:%d\n", f, p, b }
' "$f"
done
```
Output should be empty. Any line printed = file with unbalanced parens/brackets.
## What this skill does NOT check
- Semantic correctness (does the code work?) → run `tests/run_all.sh`
- SynthDef compilation → `e2e_04_synthdef_compile.scd`
- Pdef behavior → `e2e_03_reload_guards.scd`
- Notation pseudo-method `~dict.key.()` traps → see `live/CLAUDE.md`
This skill is **purely lexical**. Pass = parser will at least try to compile.
+40
View File
@@ -0,0 +1,40 @@
# Archives historiques (exclues du repo)
_archive/
# Caches
.ruff_cache/
.claude/cache/
# Node.js
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.env
.env.local
# Logs de tests
tests/.logs/
# macOS
.DS_Store
.AppleDouble
.LSOverride
._*
# Editeurs
*.swp
*.swo
*~
.vscode/
.idea/
# SuperCollider artefacts
*.scsyndef
*.scd~
# Debug probes (temporaires, snapshots OSC)
_debug/
# Settings Claude Code locaux
.claude/settings.json
+320
View File
@@ -0,0 +1,320 @@
// =====================================================================
// 00_load.scd -- Loader / index du projet
//
// STRUCTURE
//
// setup.scd Verification + init complete
// (~check / ~setupAll / ~setupLive /
// ~ensureLive / ~teardown)
// engine.scd Moteur (SynthDef base + Pdef + sidechain bus)
// synth/ SynthDef instruments (auto-loader _index.scd)
// drums/, bass/, lead/, pad/, world/, master/
// fx/ SynthDef effets (auto-loader _index.scd)
// bus/, insert/, trick/
// palette/
// scales.scd Scales (mineur/dorian/.../hijaz/vietnam) +
// helpers genMelody/mutate/octavate/transpose/
// retrograde/densify/sparsify/jitter/
// genAcidNotes/Accents/Slides/genKickPattern/
// randomTweak (SOURCE UNIQUE)
// melodies/ MODULE melodies (loader = melodies/index.scd)
// helpers.scd stretch/chain/mirror/sequenceUp/callResponse/...
// short.scd ~m1..~m78 (32 pas)
// long.scd ~ml1..~ml30 (64 pas)
// xlong.scd ~mxl1..~mxl15(96 pas)
// epic.scd ~mepic1..~mepic10 (128 pas)
// bank.scd ~melodyBank par genre + selecteurs
// tracks.scd multi-pistes melodiques (~melTrackAdd/On/...)
// harmonics/ MODULE harmonie (loader = harmonics/index.scd)
// helpers.scd genThirdHarmony / genFifthHarmony / genCounter / ...
// presets.scd ~enableHarmony.(\preset) -- 10 presets (SOURCE UNIQUE)
// rhythm/ MODULE rythmes / kicks / percussions
// _index.scd Auto-loader (charge fills/ + sequences/)
// kicks/ 25 patterns + 12 timbres kick (1 fichier/preset)
// percussions/ ~25 patterns clap/perc (1 fichier/preset)
// drum_kits/ 21 kits complets (1 fichier/kit)
// patterns_genre/ 40 patterns signature par genre (1 fichier/preset)
// sequences/ Macros de sequences (~seqIntro/Buildup/Drop...)
// fills/ Snare fill / tom fill
// timbres.scd 26 timbres melodie (L1-L26)
// styles.scd 5 styles complets (Acid Rave, Dub, Hard...)
// chords_progressions.scd ~35 progressions d'accords (auto + ~progXxx)
// fx.scd Riser/sweep/impact/crash/vinyl
// fx_genre.scd Combos FX prets a l'emploi par genre
// tweaks_template.scd Templates de tweaks par genre
// live/
// _load.scd Mini-loader API live (charge les 5 wrappers)
// kicks.scd WRAPPER ~k / ~kk -- combos kick (pattern+timbre)
// melodies.scd WRAPPER ~m / ~mm -- short/long/xlong/epic + harmonie
// fx.scd WRAPPER ~fx / ~ff -- presets / drop / VCF / LFO
// chains.scd WRAPPER ~ch / ~cc -- intro/drop/breakdown/loops
// patterns.scd WRAPPER ~p -- kits + presets de genre rythmiques
// live.scd Boite a outils live (LFO, VCF, drops, tricks)
// mixer.scd Faders / mute / solo par voie + master
// loops.scd Gestionnaire de boucles / pistes (live)
// fx_bus.scd 9 bus FX reverb/delay/chorus/... + presets
// live_fx_panel.scd PANNEAU FX LIVE tout-en-un (Cmd+Entree par bloc)
// scenes.scd Save / recall etat global (snapshot)
// tweaks.scd Tweaks live (cutoff, drive, mute, sidechain...)
// control/
// midi.scd Mapping controller MIDI (CC + notes)
// playlist.scd 11 playlists prefefinies + auto-fade
// jump.scd Lance n'importe quelle section de track directement
// tracks/
// A_acid_journey.scd ... W_dub_techno.scd (23 tracks A->W)
// examples/ Mini-tutoriels Cmd+Entree pas-a-pas (16 fichiers)
// 00_index.scd Catalogue + parcours recommande
// 01_quickstart.scd Premier "Hello, sound_algo"
// 02_first_track.scd Lancer une track autonome
// ... voir examples/00_index.scd
//
// WORKFLOW
// 1. Cmd+Entree sur "CHARGER TOUT" -> tout est charge automatiquement
// (engine + synthdefs + scales + melodies + harmonics + setup
// helpers + boot serveur + live/* + rhythm/fills + sequences +
// jump). Le bloc affiche un ~check.() final pour voir l'etat.
// 2. Lance une track (tracks/*.scd) ou les Pdef (PLAY VIDE / PLAY KICK
// / PLAY DEFAULT plus bas dans ce fichier).
// 3. Cmd+Entree sur ( ~check.() ) a tout moment pour voir l'etat.
// 4. ( ~teardown.() ) pour stopper et liberer proprement.
//
// Pour decouvrir le projet : ouvre examples/00_index.scd et suis le
// parcours pas-a-pas.
// =====================================================================
// --- BASE PATH (set ~base sans rien charger -- optionnel) ------------
// Utile si tu veux juste set le chemin avant de Cmd+Entree sur un
// autre bloc. CHARGER TOUT et CHARGER MINIMAL le font deja.
(
~base = "/Users/electron/Documents/Projets_Creatifs/sound_algo-refonte-v2/";
"~base = ".post; ~base.postln;
)
// --- CHARGER TOUT (charge TOUT en un seul Cmd+Entree) ----------------
// engine + synthdefs + scales + melodies + harmonics + setup helpers,
// puis ~setupAll.() qui charge fx_bus + live + mixer + scenes + loops
// + fills + sequences + jump apres boot serveur, puis ~check.() final.
//
// Ce bloc est AUTONOME : pas besoin d'evaluer BASE PATH avant.
(
~base = "/Users/electron/Documents/Projets_Creatifs/sound_algo-refonte-v2/";
(~base ++ "engine.scd").load;
(~base ++ "synth/_index.scd").load;
(~base ++ "fx/_index.scd").load;
(~base ++ "palette/scales.scd").load;
(~base ++ "palette/melodies/index.scd").load;
(~base ++ "palette/harmonics/index.scd").load;
(~base ++ "setup.scd").load; // ~check / ~setupAll / ~setupLive / ~teardown
// NOTE : ~setupAll.() est appele par live/_load.scd ci-dessous (qui gere
// l'idempotence via ~setupAll_done). L'appeler ici en plus declenchait
// un DOUBLE BOOT : 1er ~setupAll lance s.waitForBoot async, puis le
// .load de live/_load.scd voit ~setupAll_done = nil et relance ~setupAll
// -> "address in use" + zombie scsynth. Fix : un seul appel via _load.
(~base ++ "live/_load.scd").load; // boot + tous modules + wrappers API live
)
// --- CHARGER MINIMAL (engine + synthdefs + scales seulement) ---------
// Pour debugger ou demarrer sans live/mixer/fx. Pas de boot serveur
// automatique, pas de helpers ~check / ~setupAll.
(
~base = "/Users/electron/Documents/Projets_Creatifs/sound_algo-refonte-v2/";
(~base ++ "engine.scd").load;
(~base ++ "synth/_index.scd").load;
(~base ++ "fx/_index.scd").load;
(~base ++ "palette/scales.scd").load;
)
// --- CHARGER ENGINE SEUL (minimum vital) -----------------------------
(
(~base ++ "engine.scd").load;
)
// --- CHARGER SYNTH (tous les SynthDef instruments) -------------------
(
(~base ++ "synth/_index.scd").load;
)
// --- CHARGER FX (tous les SynthDef effets) ---------------------------
(
(~base ++ "fx/_index.scd").load;
)
// --- OUVRIR FICHIERS dans l'IDE -------------------------------------
// Plusieurs blocs cibles : choisis celui dont tu as besoin (Cmd+Entree).
// NB : les fichiers charges via CHARGER TOUT (engine, synthdefs, scales,
// melodies/, harmonics/, rhythm/fills) ne sont PAS proposes ici --
// leur contenu est interne et tu n'as pas a les editer en live.
// --------------------------------------------------------------------
// --- OUVRIR ESSENTIEL LIVE (outils performance + entree examples) ----
(
var root = if (thisProcess.nowExecutingPath.notNil) { thisProcess.nowExecutingPath.dirname } { "/Users/electron/Documents/Projets_Creatifs/sound_algo" };
[
"setup.scd", // ~check / ~setupAll / ~teardown
"live/tweaks.scd", // tweaks live (cutoff/drive/mute/SC)
"live/live_fx_panel.scd", // PANNEAU FX tout-en-un
"live/mixer.scd", // faders / mute / solo / master
"live/scenes.scd", // save / recall snapshots
"palette/rhythm/_index.scd", // ~snareFill / ~tomFill / ~seqPlay (auto)
"control/jump.scd", // sauter dans une section
"examples/00_index.scd", // catalogue tutoriels
].do { |p| Document.open(root +/+ p) };
)
// --- OUVRIR OUTILS LIVE AVANCES (LFO/VCF/loops/FX bus/MIDI) ----------
(
var root = if (thisProcess.nowExecutingPath.notNil) { thisProcess.nowExecutingPath.dirname } { "/Users/electron/Documents/Projets_Creatifs/sound_algo" };
[
"live/live.scd", // LFO / VCF / drops / tricks
"live/loops.scd", // gestionnaire de boucles
"live/fx_bus.scd", // 9 bus FX + presets + tap tempo
"control/midi.scd", // mapping MIDI controller
"control/playlist.scd", // playlists auto-fade
].do { |p| Document.open(root +/+ p) };
)
// --- OUVRIR PALETTES PRESETS (Cmd+Entree sur un preset) --------------
(
var root = if (thisProcess.nowExecutingPath.notNil) { thisProcess.nowExecutingPath.dirname } { "/Users/electron/Documents/Projets_Creatifs/sound_algo" };
[
"palette/styles.scd",
"palette/timbres.scd",
"palette/chords_progressions.scd",
"palette/fx_genre.scd",
"palette/tweaks_template.scd",
"palette/rhythm/_index.scd", // loader recursif fills+sequences
// NB : les presets rythmiques sont eclates en sous-dossiers
// palette/rhythm/{drum_kits,patterns_genre,kicks,percussions,sequences,fills}/
// 1 fichier = 1 preset Cmd+Entree. Ouvre-les a la demande dans l'IDE.
].do { |p| Document.open(root +/+ p) };
)
// --- OUVRIR TRACKS (23 tracks A->W) ----------------------------------
(
var root = if (thisProcess.nowExecutingPath.notNil) { thisProcess.nowExecutingPath.dirname } { "/Users/electron/Documents/Projets_Creatifs/sound_algo" };
PathName(root +/+ "tracks").files
.select { |f| f.extension == "scd" }
.sort { |a, b| a.fileName < b.fileName }
.do { |f| Document.open(f.fullPath) };
)
// --- OUVRIR EXAMPLES (16 tutoriels Cmd+Entree pas-a-pas) -------------
(
var root = if (thisProcess.nowExecutingPath.notNil) { thisProcess.nowExecutingPath.dirname } { "/Users/electron/Documents/Projets_Creatifs/sound_algo" };
PathName(root +/+ "examples").files
.select { |f| f.extension == "scd" }
.sort { |a, b| a.fileName < b.fileName }
.do { |f| Document.open(f.fullPath) };
)
// --- OUVRIR TOUT (ancien comportement -- ~60 onglets, lourd) ---------
// A eviter au quotidien -- prefere les blocs cibles ci-dessus.
(
var root = if (thisProcess.nowExecutingPath.notNil) { thisProcess.nowExecutingPath.dirname } { "/Users/electron/Documents/Projets_Creatifs/sound_algo" };
[
"engine.scd",
"synth/_index.scd", "fx/_index.scd",
"palette/scales.scd",
"palette/melodies/helpers.scd", "palette/melodies/short.scd",
"palette/melodies/long.scd", "palette/melodies/xlong.scd",
"palette/melodies/epic.scd", "palette/melodies/bank.scd",
"palette/melodies/tracks.scd",
"palette/harmonics/helpers.scd","palette/harmonics/presets.scd",
"palette/rhythm/_index.scd", "palette/fx.scd",
].do { |p| Document.open(root +/+ p) };
)
// --- PLAY VIDE -- demarre les sequencers en silence ------------------
// Tous les patterns sont vides : tu construis au fur et a mesure.
// Ex : ( ~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~reload.value; )
(
TempoClock.default.tempo = ~bpm / 60;
~kickSteps = (0 ! 16);
~hatSteps = (0 ! 16);
~snareSteps = (0 ! 16);
~clapSteps = (0 ! 16);
~percSteps = (0 ! 16);
~acidNotes = (0 ! 16);
~acidAccents = (0 ! 16);
~acidSlides = (0 ! 16);
~melodyNotes = (0 ! 32);
~harmonyNotes = (0 ! 32);
~reload.value;
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq, \acidSeq, \melodySeq, \harmonySeq]
.do { |k| Pdef(k).play };
"=== PLAY VIDE -- ajoute des steps puis ~reload.value ===".postln;
)
// --- PLAY KICK -- demarre avec juste un kick 4/4 ---------------------
// Ajoute hat/snare/melody en tweakant les ~xxxSteps puis ~reload.value
(
TempoClock.default.tempo = ~bpm / 60;
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0];
~hatSteps = (0 ! 16);
~snareSteps = (0 ! 16);
~clapSteps = (0 ! 16);
~percSteps = (0 ! 16);
~acidNotes = (0 ! 16);
~acidAccents = (0 ! 16);
~acidSlides = (0 ! 16);
~melodyNotes = (0 ! 32);
~harmonyNotes = (0 ! 32);
~reload.value;
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq, \acidSeq, \melodySeq, \harmonySeq]
.do { |k| Pdef(k).play };
"=== PLAY KICK 4/4 ===".postln;
)
// --- PLAY DEFAULT -- ancien comportement (kick + hat + snare + acid) -
// Lance avec les patterns par defaut definis dans engine.scd
(
TempoClock.default.tempo = ~bpm / 60;
~reload.value;
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq, \acidSeq, \melodySeq, \harmonySeq]
.do { |k| Pdef(k).play };
"=== PLAY DEFAULT ===".postln;
)
// --- STOP -- coupe les sequencers (garde le master limiter) ----------
(
~track !? { ~track.stop };
~filterSweep !? { ~filterSweep.stop };
~randomLoop !? { ~randomLoop.stop };
~melodyEvolve !? { ~melodyEvolve.stop };
~lRotate !? { ~lRotate.stop };
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq, \acidSeq, \melodySeq, \harmonySeq]
.do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
// --- ARRET TOTAL (libere bus + master) -------------------------------
(
~track !? { ~track.stop };
~filterSweep !? { ~filterSweep.stop };
~randomLoop !? { ~randomLoop.stop };
~melodyEvolve !? { ~melodyEvolve.stop };
~lRotate !? { ~lRotate.stop };
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq, \acidSeq, \melodySeq, \harmonySeq]
.do { |k| Pdef(k).stop };
~masterLim !? { ~masterLim.free; ~masterLim = nil };
~scBus !? { ~scBus.free; ~scBus = nil };
"=== ARRET TOTAL ===".postln;
)
+397
View File
@@ -0,0 +1,397 @@
// =====================================================================
// 01_live.scd -- TABLEAU DE BORD LIVE (Cmd+Entree par bloc)
//
// PREREQUIS :
// 1. 00_load.scd > Cmd+Entree sur "CHARGER TOUT"
// 2. Attends le boot serveur + ~check OK
// 3. Reviens ici, Cmd+Entree sur n'importe quel bloc
//
// API LIVE (wrappers env directs ~xxx -- jamais ~dict.key.()) :
// ~kk kicks ~kk.(\techno) ~kPat / ~kTim / ~kList
// ~mm melodies ~mm.(50) ~mLong / ~mXlong / ~mEpic / ~mRnd / ~mGen
// ~ff fx presets ~ff.(\dub) ~fxDrop / ~fxVcfOn / ~fxLfo / ~fxCut
// ~cc chains ~chDrop.() ~chIntro / ~chBuildup / ~chIntro2drop
// ~p patterns ~pKit.(\dub) ~pGenre.(\trance138) / ~pHat / ~pClap
//
// IMPORTANT : la notation `~dict.key.(args)` NE MARCHE PAS en SC --
// SC passe l'event ~dict comme 1er arg et perd les vrais args (test
// empirique sur SC 3.14.1, cf Dictionary.sc:549). De plus certaines
// cles courtes ou collisionnant avec methodes Object/Server (s/t/p/e/l,
// set/send/next/prev/tap...) plantent carrement.
// Solutions : wrappers env directs (~kPat, ~mLong...) ou notation
// indexee (~k[\pat].(\amen), ~m[\long].(15)).
// Renommages internes : .p -> .pat, .t -> .tim, .s -> .short,
// .l -> .long, .x -> .xlong, .e -> .epic, .r -> .rnd, .next -> .nx,
// .prev -> .pv, .send -> .snd, .set -> .st, .tap -> .tp.
// =====================================================================
// =====================================================================
// [0] CHARGEMENT COMPLET (1 seul Cmd+Entree -- engine + boot + live)
// Auto-detecte ~base depuis le chemin de ce fichier.
// =====================================================================
(
~base = if (thisProcess.nowExecutingPath.notNil) {
thisProcess.nowExecutingPath.dirname ++ "/"
} {
"/Users/electron/Documents/Projets_Creatifs/sound_algo-refonte-v2/"
};
(~base ++ "live/_load.scd").load;
)
// =====================================================================
// [1] KICKS -- ~kk
// =====================================================================
( ~kk.(\techno); ) // 4/4 + rond
( ~kk.(\gabber); ) // dense + distordu
( ~kk.(\dub); ) // one-drop + sub
( ~kk.(\dubstep); ) // half-time + 808
( ~kk.(\trance); ) // 4/4 punchy
( ~kk.(\hardstyle); ) // hardstyle distordu
( ~kk.(\amen); ) // amen break
( ~kk.(\dnb); ) // drum'n'bass
( ~kk.(\jungle); ) // jungle rapid
( ~kk.(\house); ) // house 4/4 + 909
( ~kk.(\detroit); ) // detroit + 909
( ~kk.(\phonk); ) // half-time + 808
( ~kk.(\afro); ) // afrobeat
( ~kk.(\garage); ) // 2-step
( ~kk.(\triphop); ) // lazy boomy
( ~kk.(\footwork); ) // 160 BPM triple
( ~kk.(\minimal); ) // 1 kick / mesure
( ~kk.(\electro); ) // electro syncope
// patterns / timbres seuls (variables env directes, pas ~k.pat !)
( ~kPat.(\amen); )
( ~kPat.(\dense); )
( ~kPat.(\off); ) // silence kick
( ~kTim.(\808); )
( ~kTim.(\909); )
( ~kTim.(\distorded); )
( ~kList.(); )
// =====================================================================
// [2] MELODIES -- ~mm + wrappers env directs ~mXxx
// =====================================================================
// short (32 steps, 78 dispos)
( ~mm.(1); ) // applique m1
( ~mm.(50); ) // applique m50
( ~mRnd.(); ) // random short
// long (64 steps, 30 dispos)
( ~mLong.(1); )
( ~mLong.(15); )
( ~mRndLong.(); )
// xlong (96 steps, 15 dispos)
( ~mXlong.(1); )
( ~mRndXlong.(); )
// epic (128 steps, 10 dispos)
( ~mEpic.(2); )
( ~mRndEpic.(); )
// par genre
( ~mGen.(\acid); )
( ~mGen.(\trance); )
( ~mGen.(\detroit); )
( ~mGen.(\dnb); )
( ~mGen.(\dub); )
( ~mGen.(\oriental); )
( ~mGen.(\synthwave); )
( ~mGen.(\phonk); )
// navigation
( ~mNx.(); ) // melodie suivante
( ~mPv.(); ) // precedente
// instrument melody
( ~mInst.(\warmPad); )
( ~mInst.(\pluck); )
( ~mInst.(\stab); )
( ~mInst.(\saw3); )
( ~mInst.(\fmBell); )
( ~mInst.(\supersaw); )
( ~mInst.(\rhodes); )
( ~mInst.(\guzheng); )
( ~mInst.(\koto); )
( ~mInst.(\handpan); )
// harmonie
( ~mHarm.(\third); ) // tierce automatique
( ~mHarm.(\fifth); )
( ~mHarm.(\counter); )
( ~mHarm.(\bassline); )
( ~mHarm.(\dubBass); )
( ~mHarm.(\arp); )
( ~mHarm.(\chord); )
( ~mHarm.(\tranceLayer); )
( ~mHarm.(\cinematic); )
( ~mHarm.(\off); )
( ~mHarmInst.(\subBoom); )
( ~mHarmInst.(\warmPad); )
// tweaks rapides
( ~mAmp.(0.18); )
( ~mCut.(2500); )
( ~mCut.(8000); )
( ~mDrive.(2.5); )
( ~mOff.(); ) // melody muette
( ~mList.(); )
// =====================================================================
// [3] FX -- ~ff + wrappers env directs ~fxXxx
// =====================================================================
// presets (10)
( ~ff.(\dub); )
( ~ff.(\space); )
( ~ff.(\trance); )
( ~ff.(\dubstep); )
( ~ff.(\retro); )
( ~ff.(\industrial); )
( ~ff.(\dream); )
( ~ff.(\cosmic); )
( ~ff.(\drone); )
( ~ff.(\dry); )
// sends individuels
( ~fxSnd.(\rev, 0.4); )
( ~fxSnd.(\dly, 0.3); )
( ~fxSnd.(\shim, 0.5); )
( ~fxSt.(\rev, \mix, 0.7); )
( ~fxSt.(\dly, \delayTime, 0.5); )
// freeze reverb
( ~fxFreeze.(1); )
( ~fxFreeze.(0); )
// tricks scéniques
( ~fxDrop.(); ) // build silence -> snareFill -> blast
( ~fxBreakdown.(8); ) // kill rythmique progressif sur dur s
( ~fxBuildup.(8); ) // crescendo (riser + fade in + sweep up)
( ~fxGlitch.(2); ) // bitcrush + stutter + delay courte
( ~fxStutter.(8, 1); ) // gate amp à rateHz pendant dur s
( ~fxFreezeR.(4); ) // reverb freeze + cut sec, retour apres dur
( ~fxTapeStop.(2); ) // ralenti tempo expo -> stop Pdef (dur s)
( ~fxTapeStart.(2); ) // RELANCE apres tapeStop : tempo 0.01 -> courant
( ~fxTapeStart.(2, 2.8); ) // idem mais cible 150 BPM (TempoClock = BPM/60)
( ~fxPlayAll.(); ) // relance instantanee de tous les Pdef sequenceurs
( ~fxTapeRev.(4); ) // riser inverse + filter sweep + freeze rev
( ~fxHandsUp.(); ) // mute kick + boost lead + delay courte + clap
( ~fxJumpCut.(0.5); ) // silence sec dur s puis reprend tout
( ~fxBitCrush.(6, 6000, 1); ) // bitcrush master (bits, sr, dur)
( ~fxRing.(200, 1); ) // ring modulator master (freq, dur)
( ~fxShift.(100, 1); ) // freq shifter master (hz, dur)
( ~fxGran.(2); ) // freeze granulaire master pendant dur s
// VCF master
( ~fxVcfOn.(\moog, 800, 0.5); )
( ~fxVcfOn.(\ladder, 1500, 0.7); )
( ~fxVcfOn.(\formant, 1, 0.3); ) // voyelle 'a'
( ~fxVcfFreq.(2000); )
( ~fxVcfQ.(0.6); )
( ~fxVcfSweep.(8000, 200, 8); )
( ~fxVcfPreset.(\acid); )
( ~fxVcfOff.(); )
// LFO
( ~fxLfo.(\melodyCutoff, 0.5, 1500); )
( ~fxLfo.(\acidAmp, 4, 0.4); )
( ~fxLfo.(\kickAmp, 0.3, 0.3); )
( ~fxLfoStop.(\melodyCutoff); )
( ~fxLfoStopAll.(); )
// shortcuts cutoff/RQ/drive par voie
( ~fxCut.(\mel, 5000); )
( ~fxCut.(\acid, 1500); )
( ~fxRq.(\mel, 0.4); )
( ~fxDrive.(\mel, 2.0); )
// master comp / saturation
( ~fxComp.(\glue); )
( ~fxComp.(\punch); )
( ~fxComp.(\pump); )
( ~fxCompOff.(); )
( ~fxSat.(\tanh, 1.5); )
( ~fxSat.(\softclip, 1.2); )
( ~fxSatOff.(); )
( ~fxList.(); )
// =====================================================================
// [4] CHAINS -- wrappers env directs ~chXxx (jamais ~cc.key.()!)
// =====================================================================
( ~chIntro.(); ) // sequence intro montante
( ~chDrop.(); ) // drop sec
( ~chBreakdown.(); )
( ~chBuildup.(8); ) // crescendo trance 8 mesures
( ~chOutro.(); )
// loops par genre
( ~chTechno.(4); )
( ~chAcid.(); )
( ~chDubstep.(); )
( ~chTrance.(); )
( ~chDnb.(4); )
( ~chHiphop.(8); )
( ~chWorld.(); )
// combos prêts
( ~chIntro2drop.(4, 4); ) // intro -> buildup -> drop
( ~chDropToBreak.(8); ) // drop -> breakdown -> drop
( ~chOutroFade.(12); ) // outro + masterFadeOut
// stop chain en cours
( ~chStop.(); )
( ~chList.(); )
// =====================================================================
// [5] PATTERNS rythmiques -- wrappers env directs ~pXxx
// =====================================================================
// kits complets (16 dispos)
( ~pKit.(\techno); )
( ~pKit.(\dubstep); )
( ~pKit.(\dnb); )
( ~pKit.(\trap); )
( ~pKit.(\house); )
( ~pKit.(\hardstyle); )
( ~pKit.(\jungle); )
( ~pKit.(\phonk); )
( ~pKit.(\industrial); )
( ~pKit.(\hardcore); )
( ~pKit.(\detroitDeep); )
( ~pKit.(\lofiHipHop); )
( ~pKit.(\tribalWorld); )
// genres rythmiques (20 dispos)
( ~pGenre.(\hardTechno); )
( ~pGenre.(\acidHouse); )
( ~pGenre.(\trance138); )
( ~pGenre.(\psyTrance); )
( ~pGenre.(\liquidDnB); )
( ~pGenre.(\neurofunk); )
( ~pGenre.(\jungle165); )
( ~pGenre.(\futureBass); )
( ~pGenre.(\deepHouse); )
( ~pGenre.(\techHouse); )
( ~pGenre.(\industrialV2); )
( ~pGenre.(\breakcore); )
( ~pGenre.(\footwork); )
( ~pGenre.(\idm); )
( ~pGenre.(\glitchHop); )
// elements isoles (noms reels : voir ~pList.())
( ~pHat.(\off2); ) // offbeat 8th
( ~pHat.(\eighth); )
( ~pHat.(\sixteen); )
( ~pHat.(\shuffle); )
( ~pHat.(\off); )
( ~pSnare.(\two4); )
( ~pSnare.(\ghost); )
( ~pSnare.(\roll); )
( ~pSnare.(\off); )
( ~pClap.(\two4); )
( ~pClap.(\offbeat); )
( ~pClap.(\trance); )
( ~pClap.(\dub); )
( ~pClap.(\off); )
( ~pPerc.(\rim); )
( ~pPerc.(\tom); )
( ~pPerc.(\cowbell); )
( ~pPerc.(\openHat); )
( ~pPerc.(\off); )
( ~pList.(); )
// =====================================================================
// [6] MIXER -- ~setVol / ~mute / ~mixPreset
// =====================================================================
( ~setVol.(\kick, 1.0); )
( ~setVol.(\acid, 0.7); )
( ~setVol.(\melody, 0.5); )
( ~masterVol.(0.5); ) // multiplicateur global
( ~masterVol.(1.0); ) // retour normal
( ~masterFade.(0.3, 4); ) // fade master vers 0.3 en 4s
( ~masterFadeOut.(8); ) // fade out + stop Pdef
( ~mute.(\acid); )
( ~unmute.(\acid); )
( ~solo.(\melody); )
( ~unsolo.(); )
( ~mixReset.(); )
( ~mixPreset.(\drumsHeavy); )
( ~mixPreset.(\melodicLead); )
( ~mixPreset.(\acidFocus); )
( ~mixPreset.(\breakdown); )
( ~mixPreset.(\drop); )
( ~mixPreset.(\dub); )
( ~mixPreset.(\trance); )
( ~mixPreset.(\hardcore); )
( ~saveMix.(\verseA); )
( ~loadMix.(\verseA); )
( ~mixShow.(); )
// =====================================================================
// [7] SCENES -- snapshot / recall
// =====================================================================
( ~saveScene.(\drop1); )
( ~saveScene.(\verseA); )
( ~saveScene.(\breakdown); )
( ~loadScene.(\drop1); )
( ~listScenes.(); )
// =====================================================================
// [8] JUMP -- ~jumpTo dans n'importe quelle section
// =====================================================================
( ~jumpTo.('A', 'minimal_kick_t5_sub_melody_pad'); )
( ~jumpTo.('A', 'acid_rave_kick_t1_melody_lead_variations'); )
( ~jumpTo.('B', 'kick_4_4_acid_ouvre_kick_t1_melody_pluck'); )
( ~jumpTo.('E', 'drop_1_growl_dubstep'); )
( ~jumpTo.('F', 'trance_uplifter_138_bpm'); )
( ~jumpTo.('W', 'dub_techno_120_bpm'); )
// =====================================================================
// [9] STATUS / TEARDOWN
// =====================================================================
( ~check.(); ) // rapport detaille
( ~teardown.(); ) // arret propre + free
// =====================================================================
// [10] PLAY -- demarrer les sequenceurs
// =====================================================================
(
TempoClock.default.tempo = (~bpm ? 128) / 60;
~kickSteps = (~kickSteps ? (1!16));
~hatSteps = (~hatSteps ? (0!16));
~snareSteps = (~snareSteps ? (0!16));
~clapSteps = (~clapSteps ? (0!16));
~percSteps = (~percSteps ? (0!16));
~acidNotes = (~acidNotes ? (0!16));
~melodyNotes = (~melodyNotes ? (0!32));
~harmonyNotes = (~harmonyNotes ? (0!32));
~reload.value;
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).play };
"=== PLAY ===".postln;
)
// --- STOP -- coupe les Pdef (garde l'engine) -------------------------
(
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
+41
View File
@@ -0,0 +1,41 @@
# sound_algo — Project Instructions
Live-coding SuperCollider system. Always communicate in French with the user.
## Conventions
- Variables d'environnement `~xxx` minuscules (sinon SC parse comme CLASSNAME)
- Fichiers `.load` doivent avoir UN SEUL bloc top-level `(...)`. Multi-blocs OK pour les fichiers ouverts manuellement dans l'IDE.
- Code commenté en français
- Pas d'emojis sauf demande explicite
## Architecture
Voir `README.md` pour l'arborescence et les helpers principaux.
## Validation
Après toute modification :
- `awk` balance des parens/brackets : doit être P:0 B:0
- Pour les fichiers `.load`-compatibles : un seul bloc top-level (TLB:1)
## Live Performance Workflow
1. CHARGER TOUT (00_load.scd) ou bloc [0] de `01_live.scd`
2. **Tableau de bord = `01_live.scd`** (Cmd+Entree par bloc, API ~kk/~mm/~ff/~cc/~p)
3. Lancer une track (tracks/*.scd) ou les Pdef (bloc [10] PLAY)
4. Tweak via `01_live.scd`, `live/tweaks.scd` ou `live/live_fx_panel.scd`
5. Jump entre sections via `control/jump.scd`
6. Sauvegarder l'état avec `~saveScene` (live/scenes.scd)
## Note sur `-> nil`
Chaque bloc `( ~xxx.(...); )` retourne nil (le `;` final supprime la valeur).
Une cascade de `-> nil` = evaluation reussie de plusieurs blocs, pas un bug.
## Tests E2E
```bash
cd tests && bash run_all.sh
```
+80
View File
@@ -0,0 +1,80 @@
# sound_algo — SuperCollider Live Coding System
Generative techno/electronic live performance engine.
90 SynthDefs, 23 playable tracks (A-W), 130+ melodies, 8-voice sequencer with sidechain, FX bus, LFO automation, mixer, loops manager.
## Quick Start
1. Open `00_load.scd` in SuperCollider IDE
2. Cmd+Enter on "CHARGER TOUT" block (boots server + loads engine)
3. Open any `tracks/*.scd`, Cmd+Enter to launch
4. Open `live/tweaks.scd` or `live/live_fx_panel.scd` for live tweaks
## Architecture
| Path | Contenu |
|---|---|
| `00_load.scd` | Loader + index (entry point) |
| `engine.scd` | Core SynthDefs + Pdef sequencer + ~reload |
| `synth/` | SynthDef instruments (drums/bass/lead/pad/world/master), auto-loaded via `_index.scd` |
| `fx/` | SynthDef effets (bus/insert/trick), auto-loaded via `_index.scd` |
| `palette/scales.scd` | Scales + melody generation helpers |
| `palette/melodies/` | 130+ melody presets + multi-track |
| `palette/harmonics/` | Harmony generators + ~enableHarmony |
| `palette/rhythm/` | Kicks, percussions, drum kits, patterns, sequences, fills |
| `palette/` | timbres, styles, fx_genre, chords_progressions, tweaks_template |
| `live/` | live.scd, mixer.scd, loops.scd, fx_bus.scd, live_fx_panel.scd, scenes.scd, tweaks.scd |
| `control/` | midi.scd, playlist.scd, jump.scd |
| `tracks/` | 23 autonomous tracks (A-W) |
| `examples/` | 16 step-by-step tutorials |
| `tests/` | E2E test suite |
## Load Order
CHARGER TOUT charge automatiquement dans l'ordre :
1. engine.scd
2. synth/_index.scd + fx/_index.scd (auto-loaders recursifs)
3. palette/scales.scd (scales + helpers melodiques)
4. palette/melodies/index.scd (130+ melodies + multi-track)
5. palette/harmonics/index.scd (~enableHarmony + helpers)
6. palette/rhythm/fills.scd (snareFill, tomFill)
À évaluer manuellement (Cmd+Entrée bloc par bloc) :
- live/live.scd SETUP (LFO, VCF, tricks)
- live/mixer.scd SETUP (faders, mute, solo, master)
- live/fx_bus.scd SETUP (FX bus, presets)
- live/loops.scd SETUP (loops manager)
- live/scenes.scd SETUP (snapshot/recall)
- control/midi.scd INIT + MAPPING DEFAULT
- control/jump.scd SETUP JUMP
## Key Helpers
| Helper | Fichier | Usage |
|---|---|---|
| `~reload.value` | engine.scd | Rebuild Pdef depuis ~xxx vars |
| `~enableHarmony.(\preset)` | palette/harmonics/presets.scd | 10 presets harmonie |
| `~genMelody.(density, scale, len)` | palette/scales.scd | Generateur melodie aleatoire |
| `~lfoTo.(\param, rate, depth)` | live/live.scd | LFO assignable a toute var |
| `~vcfOn.(\type, freq, q)` | live/live.scd | VCF master (8 types) |
| `~drop`, `~breakdown`, `~buildup` | live/live.scd | Tricks sceniques |
| `~send.(\voie, \fx, amount)` | live/fx_bus.scd | Routing audio vers FX |
| `~fxPreset.(\preset)` | live/fx_bus.scd | 10 combos FX |
| `~loopOn.(\name, source)` | live/loops.scd | Loop manager |
| `~setVol/mute/solo` | live/mixer.scd | Mixer 8 voies |
| `~useMelody.(\m50)` | palette/melodies/bank.scd | Charger melodie nommee |
| `~melodyByGenre.(\acid)` | palette/melodies/bank.scd | Random melodie par genre |
| `~jumpTo.(\letter, \slug)` | control/jump.scd | Saut direct dans une track |
| `~playPlaylist.([...])` | control/playlist.scd | Playlist auto-fade |
## Running Tests
```bash
cd tests && bash run_all.sh
```
## Requirements
- SuperCollider 3.13+
- macOS / Linux (testé sur Darwin)
- Pas de Quarks externes requis (toute la synthèse est en UGens natifs)
+140
View File
@@ -0,0 +1,140 @@
"""
Extract SynthDef(\name, { ... }).add; blocks from .scd source files
and route them into synth/ or fx/ subdirectories per classify().
"""
import re, sys
from pathlib import Path
CAMEL_TO_SNAKE = re.compile(r'(?<!^)(?=[A-Z])')
def to_snake(name: str) -> str:
return CAMEL_TO_SNAKE.sub('_', name).lower()
def classify(name):
# Master / utility
if name in {'masterLim','masterComp','masterSat','vinyl'}: return 'synth/master'
# Trick (FX oneshot) — includes master-bus trick FX
if name in {'riser','sweep','impact','crash','scratch',
'fxMasterPitch','fxMasterRing','fxMasterFreqShift',
'fxMasterBitcrush','fxMasterGran'}: return 'fx/trick'
# Drums
if name in {'kick','kickGabber','hat','openHat','snare','snareGated',
'clap','perc','rim','cowbell','tom','kick808'}: return 'synth/drums'
# Bass
if name in {'acid','reese','reeseDnB','reeseHard','subBoom','fmBass',
'hooverbass','didgeridoo','bass808'}: return 'synth/bass'
# Pad
if name in {'pad','warmPad','vocalPad','drone','choir','strings'}: return 'synth/pad'
# World
if name in {'koto','erhu','pipa','gong','handpan','guzheng','growl',
'taiko','tabla','shakuhachi'}: return 'synth/world'
# Lead
if name in {'saw3','lead','squareLead','fmLead','organLead','hardLead',
'supersaw','stab','pluck','fmBell','rhodes','flute'}: return 'synth/lead'
return None
# Source-driven fallback
SOURCE_FALLBACK = {
'live/fx_bus.scd': 'fx/bus',
'live/live.scd': 'fx/insert',
'synthdefs/asia.scd': 'synth/world',
}
def find_synthdefs(src: str):
results = []
i = 0
n = len(src)
while i < n:
m = re.search(r'SynthDef\(\\(\w+),', src[i:])
if not m:
break
name = m.group(1)
start = i + m.start()
depth = 0
j = start
in_string = False
in_block_comment = False
in_line_comment = False
found = False
while j < n:
ch = src[j]
ch2 = src[j:j+2]
if in_line_comment:
if ch == '\n': in_line_comment = False
elif in_block_comment:
if ch2 == '*/': in_block_comment = False; j += 1
elif in_string:
if ch == '"' and src[j-1] != '\\': in_string = False
elif ch2 == '//': in_line_comment = True; j += 1
elif ch2 == '/*': in_block_comment = True; j += 1
elif ch == '"': in_string = True
elif ch == '{': depth += 1
elif ch == '}':
depth -= 1
if depth == 0:
rest = src[j+1:j+30]
add_m = re.match(r'\s*\)\.add\s*;', rest)
if add_m:
end = j + 1 + add_m.end()
block = src[start:end]
results.append((name, block))
i = end
found = True
break
j += 1
if not found:
break
return results
def route(source_files):
base = Path(".")
for src_path in source_files:
src = Path(src_path).read_text()
blocks = find_synthdefs(src)
print(f"-- {src_path}: {len(blocks)} SynthDef")
for name, block in blocks:
target = classify(name)
if target is None:
# Source-driven fallback
target = SOURCE_FALLBACK.get(src_path)
if target is None:
print(f" [FAIL] no route for \\{name} from {src_path}")
sys.exit(1)
target_dir = base / target
target_dir.mkdir(parents=True, exist_ok=True)
out = target_dir / f"{to_snake(name)}.scd"
wrapped = (
f"// =====================================================================\n"
f"// {target}/{out.name} -- SynthDef \\{name}\n"
f"// Extracted from {Path(src_path).name} during v2 reorganization.\n"
f"// =====================================================================\n"
f"(\n{block}\n)\n"
)
out.write_text(wrapped)
print(f" \\{name:25s} -> {out}")
def strip(path):
src = Path(path).read_text()
blocks = find_synthdefs(src)
if not blocks:
return
for name, block in reversed(blocks):
idx = src.find(block)
if idx >= 0:
src = src[:idx] + src[idx + len(block):]
Path(path).write_text(src)
print(f" stripped {len(blocks)} SynthDef from {path}")
if __name__ == "__main__":
cmd = sys.argv[1]
if cmd == "route":
route(sys.argv[2:])
elif cmd == "strip":
for p in sys.argv[2:]:
strip(p)
else:
print("Usage: extract_synthdef.py {route|strip} <files...>")
sys.exit(1)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,119 @@
// =====================================================================
// palette/melodies/epic.scd
// ~mepic1..~mepic10 -- 10 patterns melodiques de 128 pas (8 mesures)
//
// Charge par palette/melodies/index.scd. Un seul bloc top-level.
// =====================================================================
(
~mepic1 = [
60, 0, 0, 0, 67, 0, 0, 0, 72, 0, 0, 0, 70, 0, 0, 0,
60, 0, 0, 0, 67, 0, 70, 0, 72, 0, 75, 0, 72, 0, 70, 67,
60, 67, 72, 67, 65, 67, 70, 72, 75, 72, 70, 67, 70, 72, 75, 79,
60, 67, 72, 75, 72, 67, 60, 67, 65, 72, 77, 79, 77, 72, 65, 72,
79, 77, 75, 72, 79, 77, 75, 72, 82, 79, 77, 75, 84, 82, 79, 77,
84, 82, 79, 77, 84, 82, 79, 84, 87, 84, 82, 79, 89, 87, 84, 82,
79, 0, 77, 0, 75, 0, 72, 0, 70, 0, 67, 0, 65, 0, 63, 0,
60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
~mepic2 = [
60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60, 0, 63, 0, 67, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60, 63, 67, 70, 72, 70, 67, 63, 65, 67, 70, 72, 75, 72, 70, 67,
65, 70, 72, 75, 77, 75, 72, 70, 72, 75, 77, 79, 82, 79, 77, 75,
72, 75, 77, 79, 82, 79, 77, 79, 84, 82, 79, 77, 79, 82, 84, 87,
79, 82, 84, 87, 84, 82, 79, 77, 75, 77, 79, 82, 79, 77, 75, 72,
70, 0, 67, 0, 65, 0, 63, 0, 60, 0, 0, 0, 0, 0, 0, 0
];
~mepic3 = [
63, 0, 70, 0, 65, 0, 72, 0, 63, 0, 70, 0, 65, 0, 72, 75,
65, 0, 72, 0, 67, 0, 75, 0, 65, 0, 72, 0, 67, 0, 75, 79,
63, 70, 65, 72, 67, 75, 70, 77, 72, 79, 75, 82, 77, 84, 79, 86,
65, 72, 67, 75, 70, 77, 72, 79, 75, 82, 77, 84, 79, 86, 82, 89,
79, 86, 82, 89, 84, 91, 86, 93, 82, 89, 84, 91, 86, 93, 88, 95,
84, 0, 82, 0, 79, 0, 77, 0, 75, 0, 72, 0, 70, 0, 67, 0,
65, 0, 63, 0, 60, 0, 65, 0, 67, 0, 63, 0, 65, 0, 60, 0,
63, 0, 65, 67, 65, 63, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
~mepic4 = [
60, 61, 0, 64, 65, 0, 67, 68, 71, 0, 68, 67, 65, 64, 0, 61,
60, 0, 64, 65, 67, 68, 71, 72, 71, 68, 67, 65, 64, 61, 60, 0,
63, 64, 67, 68, 70, 71, 0, 73, 75, 73, 71, 70, 68, 67, 64, 63,
60, 61, 64, 65, 67, 68, 71, 72, 75, 72, 71, 68, 67, 64, 61, 60,
65, 67, 68, 71, 72, 73, 76, 75, 76, 75, 73, 72, 71, 68, 67, 65,
72, 73, 76, 75, 73, 72, 71, 68, 76, 79, 80, 83, 84, 83, 80, 79,
79, 80, 83, 84, 87, 84, 83, 80, 76, 75, 73, 72, 71, 68, 67, 65,
72, 71, 68, 67, 65, 64, 61, 60, 0, 0, 0, 0, 0, 0, 0, 0
];
~mepic5 = [
36, 0, 48, 0, 36, 39, 0, 48, 36, 0, 51, 39, 0, 36, 48, 0,
36, 0, 48, 51, 36, 39, 48, 51, 39, 0, 36, 48, 0, 39, 0, 36,
36, 51, 48, 36, 39, 0, 51, 48, 36, 0, 51, 53, 39, 36, 48, 0,
36, 53, 51, 48, 39, 0, 36, 48, 36, 0, 51, 0, 48, 39, 36, 0,
39, 0, 51, 0, 39, 41, 0, 51, 39, 0, 53, 41, 0, 39, 51, 0,
36, 39, 41, 43, 41, 39, 36, 0, 41, 39, 36, 0, 39, 36, 0, 0,
48, 0, 60, 0, 48, 51, 0, 60, 48, 0, 63, 51, 0, 48, 60, 0,
36, 39, 41, 43, 46, 43, 41, 39, 36, 0, 48, 0, 36, 0, 0, 0
];
~mepic6 = [
72, 0, 70, 0, 67, 70, 0, 65, 63, 0, 65, 67, 70, 0, 72, 75,
77, 0, 75, 0, 72, 75, 0, 70, 67, 0, 70, 72, 75, 0, 77, 79,
75, 0, 77, 0, 75, 72, 0, 70, 67, 0, 65, 67, 70, 0, 75, 72,
79, 0, 82, 0, 79, 77, 0, 75, 72, 0, 70, 75, 77, 0, 82, 79,
84, 0, 87, 0, 84, 82, 0, 79, 77, 0, 79, 82, 84, 0, 87, 89,
87, 0, 84, 0, 82, 0, 79, 0, 77, 0, 75, 0, 72, 0, 70, 0,
75, 0, 72, 0, 70, 0, 67, 0, 65, 0, 63, 0, 60, 0, 65, 67,
65, 0, 63, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
~mepic7 = [
60, 0, 0, 0, 60, 0, 60, 0, 73, 0, 0, 0, 73, 0, 73, 0,
60, 60, 0, 73, 73, 0, 60, 73, 79, 0, 73, 0, 60, 0, 0, 0,
65, 0, 0, 0, 65, 0, 65, 0, 78, 0, 0, 0, 78, 0, 78, 0,
65, 65, 0, 78, 78, 0, 65, 78, 84, 0, 78, 0, 65, 0, 0, 0,
67, 0, 0, 0, 67, 0, 80, 0, 80, 67, 80, 67, 80, 84, 80, 67,
72, 84, 72, 84, 72, 84, 72, 84, 85, 0, 0, 0, 85, 0, 97, 0,
85, 97, 85, 97, 85, 97, 85, 97, 84, 96, 84, 96, 84, 96, 84, 96,
72, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
~mepic8 = [
60, 61, 63, 0, 65, 63, 61, 60, 0, 67, 65, 0, 68, 67, 65, 63,
60, 61, 63, 0, 65, 63, 61, 60, 67, 65, 63, 61, 60, 0, 0, 0,
63, 65, 67, 0, 68, 67, 65, 63, 0, 70, 68, 0, 72, 70, 68, 67,
65, 67, 68, 0, 70, 68, 67, 65, 0, 72, 70, 0, 73, 72, 70, 68,
67, 68, 70, 0, 72, 70, 68, 67, 72, 73, 75, 0, 77, 75, 73, 72,
73, 75, 77, 0, 79, 77, 75, 73, 75, 77, 79, 0, 80, 79, 77, 75,
73, 72, 70, 68, 67, 65, 63, 61, 60, 0, 67, 0, 65, 0, 63, 0,
61, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
~mepic9 = [
72, 0, 0, 67, 0, 65, 0, 62, 60, 0, 62, 0, 65, 0, 67, 72,
74, 0, 0, 69, 0, 67, 0, 65, 62, 0, 65, 0, 67, 0, 72, 0,
74, 0, 77, 0, 79, 0, 77, 74, 72, 0, 69, 0, 67, 0, 65, 62,
77, 0, 79, 0, 81, 0, 79, 77, 74, 0, 72, 0, 69, 0, 67, 65,
79, 81, 84, 86, 84, 81, 79, 77, 74, 72, 69, 67, 65, 62, 60, 0,
81, 84, 86, 89, 91, 89, 86, 84, 81, 79, 77, 74, 72, 69, 67, 65,
72, 0, 74, 0, 77, 0, 79, 0, 77, 0, 74, 0, 72, 0, 69, 0,
67, 0, 65, 0, 62, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
~mepic10 = [
48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
48, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0,
48, 0, 51, 0, 55, 0, 58, 0, 60, 0, 0, 0, 0, 0, 0, 0,
60, 63, 67, 70, 72, 70, 67, 63, 65, 67, 70, 72, 75, 72, 70, 67,
65, 70, 72, 75, 77, 75, 72, 70, 72, 75, 77, 79, 82, 79, 77, 75,
79, 82, 84, 87, 89, 87, 84, 82, 84, 87, 89, 91, 94, 91, 89, 87,
91, 89, 87, 84, 82, 79, 77, 75, 72, 70, 67, 65, 63, 60, 58, 55,
51, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
"[melodies/epic] mepic1..mepic10 (128 steps) prets".postln;
)
@@ -0,0 +1,219 @@
// =====================================================================
// palette/melodies/long.scd
// ~ml1..~ml30 -- 30 patterns melodiques de 64 pas (4 mesures)
//
// Charge par palette/melodies/index.scd. Un seul bloc top-level.
// =====================================================================
(
~ml1 = [
36, 0, 48, 0, 36, 39, 0, 48, 36, 0, 51, 39, 0, 36, 48, 0,
36, 0, 48, 51, 36, 39, 48, 51, 39, 0, 36, 48, 0, 39, 0, 36,
36, 51, 48, 36, 39, 0, 51, 48, 36, 0, 51, 53, 39, 36, 48, 0,
36, 53, 51, 48, 39, 0, 36, 48, 36, 0, 51, 0, 48, 39, 36, 0
];
~ml2 = [
60, 0, 0, 0, 67, 0, 0, 0, 72, 0, 0, 0, 70, 0, 0, 0,
60, 67, 72, 67, 65, 67, 70, 72, 75, 72, 70, 67, 70, 72, 75, 79,
60, 67, 72, 75, 72, 67, 60, 67, 65, 72, 77, 79, 77, 72, 65, 72,
79, 77, 75, 72, 70, 67, 65, 63, 60, 0, 0, 0, 0, 0, 0, 0
];
~ml3 = [
63, 0, 70, 0, 65, 0, 72, 0, 63, 0, 70, 0, 65, 0, 72, 75,
65, 0, 72, 0, 67, 0, 75, 0, 65, 0, 72, 0, 67, 0, 75, 79,
63, 70, 65, 72, 67, 75, 70, 77, 72, 79, 75, 82, 77, 84, 79, 86,
72, 0, 70, 0, 67, 0, 65, 0, 63, 0, 60, 0, 65, 0, 67, 0
];
~ml4 = [
36, 0, 0, 36, 0, 39, 0, 36, 41, 0, 0, 36, 0, 41, 0, 39,
36, 0, 0, 41, 0, 39, 0, 36, 43, 0, 0, 41, 0, 36, 0, 39,
34, 0, 0, 36, 0, 39, 0, 41, 36, 0, 0, 39, 0, 41, 0, 43,
36, 0, 39, 0, 41, 0, 39, 0, 36, 0, 0, 0, 0, 0, 0, 0
];
~ml5 = [
60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0,
65, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67, 0, 0, 0, 70, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0
];
~ml6 = [
60, 61, 0, 64, 65, 0, 67, 68, 71, 0, 68, 67, 65, 64, 0, 61,
60, 0, 64, 65, 67, 68, 71, 72, 71, 68, 67, 65, 64, 61, 60, 0,
63, 64, 67, 68, 70, 71, 0, 73, 75, 73, 71, 70, 68, 67, 64, 63,
60, 61, 64, 65, 67, 68, 71, 72, 75, 72, 71, 68, 67, 64, 61, 60
];
~ml7 = [
67, 0, 72, 0, 70, 0, 67, 65, 63, 0, 67, 0, 70, 0, 72, 75,
75, 0, 72, 0, 70, 0, 67, 65, 63, 0, 60, 0, 65, 0, 67, 70,
72, 0, 75, 0, 79, 0, 75, 72, 70, 0, 75, 0, 72, 0, 70, 67,
72, 0, 70, 0, 67, 0, 65, 0, 63, 0, 60, 0, 0, 0, 0, 0
];
~ml8 = [
60, 61, 63, 0, 65, 63, 61, 60, 0, 67, 65, 0, 68, 67, 65, 63,
60, 61, 63, 0, 65, 63, 61, 60, 67, 65, 63, 61, 60, 0, 0, 0,
63, 65, 67, 0, 68, 67, 65, 63, 0, 70, 68, 0, 72, 70, 68, 67,
65, 0, 63, 0, 61, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
~ml9 = [
72, 0, 0, 67, 0, 65, 0, 62, 60, 0, 62, 0, 65, 0, 67, 72,
74, 0, 0, 69, 0, 67, 0, 65, 62, 0, 65, 0, 67, 0, 72, 0,
74, 0, 77, 0, 79, 0, 77, 74, 72, 0, 69, 0, 67, 0, 65, 62,
60, 62, 65, 67, 69, 72, 74, 77, 74, 72, 69, 67, 65, 62, 60, 0
];
~ml10 = [
48, 0, 54, 0, 48, 0, 54, 0, 60, 0, 54, 0, 48, 0, 54, 0,
48, 54, 60, 54, 48, 0, 54, 0, 60, 54, 48, 54, 60, 54, 48, 0,
36, 0, 42, 0, 36, 0, 42, 0, 48, 0, 42, 0, 36, 0, 42, 0,
48, 54, 60, 66, 60, 54, 48, 42, 36, 42, 48, 54, 60, 54, 48, 36
];
~ml11 = [
36, 0, 36, 0, 36, 0, 36, 0, 39, 0, 39, 0, 39, 0, 39, 0,
36, 0, 36, 39, 36, 0, 39, 0, 41, 0, 39, 0, 36, 0, 41, 0,
34, 0, 34, 0, 36, 0, 39, 0, 41, 0, 39, 36, 41, 39, 36, 0,
36, 39, 41, 43, 41, 39, 36, 34, 36, 0, 39, 0, 41, 0, 0, 0
];
~ml12 = [
60, 67, 65, 0, 60, 67, 65, 0, 60, 67, 65, 0, 60, 67, 65, 0,
60, 67, 65, 70, 60, 67, 65, 0, 60, 67, 65, 70, 60, 67, 65, 72,
63, 70, 67, 0, 63, 70, 67, 0, 63, 70, 67, 72, 63, 70, 67, 75,
65, 72, 70, 0, 65, 72, 70, 75, 65, 72, 70, 77, 60, 67, 65, 0
];
~ml13 = [
60, 60, 67, 60, 65, 60, 67, 60, 62, 62, 69, 62, 67, 62, 69, 62,
63, 63, 70, 63, 68, 63, 70, 63, 65, 65, 72, 65, 70, 65, 72, 65,
67, 67, 74, 67, 72, 67, 74, 67, 65, 65, 72, 65, 70, 65, 72, 65,
63, 63, 70, 63, 68, 63, 70, 63, 60, 60, 67, 60, 65, 60, 67, 60
];
~ml14 = [
67, 67, 0, 70, 72, 0, 70, 67, 65, 0, 67, 0, 0, 0, 0, 0,
67, 67, 0, 70, 72, 0, 75, 72, 70, 0, 67, 0, 0, 0, 0, 0,
72, 75, 0, 77, 79, 0, 77, 75, 72, 0, 70, 0, 67, 0, 65, 0,
67, 0, 70, 0, 72, 0, 75, 0, 72, 70, 67, 65, 67, 0, 0, 0
];
~ml15 = [
72, 0, 70, 0, 67, 70, 0, 65, 63, 0, 65, 67, 70, 0, 72, 75,
77, 0, 75, 0, 72, 75, 0, 70, 67, 0, 70, 72, 75, 0, 77, 79,
75, 0, 77, 0, 75, 72, 0, 70, 67, 0, 65, 67, 70, 0, 75, 72,
70, 0, 67, 0, 65, 67, 0, 63, 60, 0, 0, 0, 0, 0, 0, 0
];
~ml16 = [
60, 62, 0, 65, 67, 0, 69, 70, 67, 65, 62, 60, 0, 65, 67, 70,
62, 0, 65, 67, 69, 0, 70, 72, 69, 67, 65, 62, 0, 60, 65, 67,
65, 67, 69, 70, 72, 70, 69, 67, 65, 62, 60, 0, 62, 65, 67, 69,
72, 70, 69, 67, 65, 62, 60, 0, 65, 67, 69, 70, 72, 0, 70, 0
];
~ml17 = [
60, 61, 0, 64, 65, 0, 67, 68, 71, 0, 68, 67, 65, 64, 0, 61,
60, 0, 64, 65, 67, 0, 68, 71, 72, 71, 68, 67, 65, 64, 61, 60,
65, 67, 0, 68, 71, 0, 72, 73, 76, 0, 73, 72, 71, 68, 0, 67,
72, 73, 76, 73, 72, 71, 68, 67, 65, 64, 61, 60, 64, 65, 67, 68
];
~ml18 = [
60, 0, 0, 0, 63, 0, 0, 0, 65, 0, 0, 0, 67, 0, 0, 0,
60, 0, 0, 0, 67, 0, 0, 0, 60, 0, 0, 0, 65, 0, 0, 0,
63, 0, 0, 0, 67, 0, 0, 0, 65, 0, 0, 0, 70, 0, 0, 0,
60, 0, 67, 0, 63, 0, 70, 0, 65, 0, 67, 0, 60, 0, 0, 0
];
~ml19 = [
48, 0, 51, 0, 53, 0, 48, 0, 46, 0, 51, 0, 53, 0, 55, 0,
48, 0, 51, 53, 51, 0, 48, 0, 46, 0, 53, 0, 55, 0, 51, 48,
44, 0, 48, 0, 51, 0, 48, 0, 53, 0, 55, 0, 51, 0, 48, 0,
48, 51, 53, 51, 48, 0, 46, 48, 55, 53, 51, 48, 46, 44, 0, 0
];
~ml20 = [
60, 64, 67, 72, 60, 64, 67, 72, 62, 65, 69, 74, 62, 65, 69, 74,
63, 67, 70, 75, 63, 67, 70, 75, 60, 64, 67, 72, 65, 67, 70, 72,
65, 69, 72, 77, 65, 69, 72, 77, 67, 71, 74, 79, 67, 71, 74, 79,
72, 76, 79, 84, 72, 76, 79, 84, 60, 64, 67, 72, 60, 64, 67, 72
];
~ml21 = [
60, 0, 0, 63, 0, 65, 0, 67, 0, 70, 0, 67, 65, 63, 0, 60,
0, 65, 0, 67, 0, 70, 0, 72, 0, 70, 0, 67, 0, 65, 0, 63,
65, 0, 0, 67, 0, 70, 0, 72, 0, 75, 0, 72, 70, 67, 0, 65,
0, 67, 0, 70, 0, 67, 0, 65, 0, 63, 0, 60, 0, 0, 0, 0
];
~ml22 = [
36, 0, 0, 0, 39, 0, 36, 0, 41, 0, 0, 0, 39, 0, 41, 36,
34, 0, 0, 0, 39, 0, 41, 0, 43, 0, 41, 0, 39, 0, 36, 0,
36, 0, 41, 0, 43, 0, 41, 0, 46, 0, 43, 0, 41, 0, 39, 0,
36, 39, 41, 43, 46, 43, 41, 39, 36, 0, 0, 0, 0, 0, 0, 0
];
~ml23 = [
67, 0, 70, 0, 0, 67, 0, 65, 67, 0, 70, 72, 0, 70, 0, 67,
65, 0, 67, 0, 0, 65, 0, 63, 65, 0, 67, 70, 0, 72, 0, 70,
72, 0, 75, 0, 0, 72, 0, 70, 72, 0, 75, 77, 0, 75, 0, 72,
70, 0, 72, 0, 0, 70, 0, 67, 65, 0, 67, 0, 70, 0, 72, 0
];
~ml24 = [
60, 0, 0, 0, 60, 0, 60, 0, 73, 0, 0, 0, 73, 0, 73, 0,
60, 60, 0, 73, 73, 0, 60, 73, 79, 0, 73, 0, 60, 0, 0, 0,
65, 0, 0, 0, 65, 0, 65, 0, 78, 0, 0, 0, 78, 0, 78, 0,
65, 65, 0, 78, 78, 0, 65, 78, 84, 0, 78, 0, 65, 0, 0, 0
];
~ml25 = [
60, 67, 72, 64, 67, 71, 60, 67, 62, 65, 69, 62, 65, 69, 60, 67,
64, 67, 71, 64, 67, 71, 60, 67, 65, 69, 72, 65, 69, 72, 60, 67,
67, 72, 76, 67, 71, 74, 60, 67, 65, 69, 72, 65, 69, 76, 65, 67,
64, 67, 71, 64, 67, 71, 62, 65, 60, 67, 72, 64, 60, 64, 67, 60
];
~ml26 = [
0, 67, 0, 70, 0, 67, 0, 65, 0, 67, 0, 72, 0, 70, 0, 67,
0, 70, 0, 72, 0, 70, 0, 67, 0, 65, 0, 67, 0, 70, 0, 72,
0, 72, 0, 75, 0, 72, 0, 70, 0, 72, 0, 77, 0, 75, 0, 72,
0, 75, 0, 77, 0, 75, 0, 72, 0, 70, 0, 67, 0, 70, 0, 72
];
~ml27 = [
36, 0, 38, 0, 39, 0, 41, 0, 43, 0, 44, 0, 43, 0, 41, 0,
39, 0, 41, 0, 43, 0, 44, 0, 46, 0, 48, 0, 46, 0, 43, 0,
44, 0, 46, 0, 48, 0, 50, 0, 51, 0, 50, 0, 48, 0, 46, 0,
43, 0, 46, 0, 48, 0, 51, 0, 46, 0, 43, 0, 41, 0, 39, 36
];
~ml28 = [
60, 61, 0, 70, 0, 73, 0, 67, 61, 0, 73, 0, 67, 70, 0, 76,
60, 61, 0, 70, 0, 73, 0, 67, 73, 76, 0, 70, 67, 61, 60, 0,
63, 64, 0, 73, 0, 76, 0, 70, 64, 0, 76, 0, 70, 73, 0, 79,
63, 64, 73, 76, 79, 76, 73, 70, 67, 64, 63, 61, 60, 0, 0, 0
];
~ml29 = [
72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 0, 0, 0,
72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57,
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
75, 73, 71, 69, 67, 65, 63, 61, 60, 0, 0, 0, 0, 0, 0, 0
];
~ml30 = [
60, 0, 62, 0, 64, 0, 66, 0, 68, 0, 70, 0, 72, 0, 0, 0,
72, 0, 70, 0, 68, 0, 66, 0, 64, 0, 62, 0, 60, 0, 0, 0,
66, 0, 68, 0, 70, 0, 72, 0, 74, 0, 76, 0, 78, 0, 0, 0,
78, 0, 76, 0, 74, 0, 72, 0, 70, 0, 68, 0, 66, 0, 0, 0
];
"[melodies/long] ml1..ml30 (64 steps) prets".postln;
)
@@ -0,0 +1,91 @@
// =====================================================================
// palette/melodies/short.scd
// ~m1..~m78 -- 78 patterns melodiques de 32 pas
//
// Charge par palette/melodies/index.scd. Un seul bloc top-level.
// =====================================================================
(
// ---------- M1-M28 (premiere serie historique) ----------------------
~m1 = [60,0,67,0,70,0,67,65, 63,0,60,0,0,67,70,72, 75,0,72,70,67,0,0,65, 63,60,0,67,70,0,72,0];
~m2 = [60,0,0,67,0,70,0,67, 60,0,0,67,0,70,0,72, 60,0,0,67,0,70,0,67, 63,0,0,70,0,72,0,75];
~m3 = [63,0,0,0,70,0,67,0, 65,0,0,0,70,0,72,0, 63,0,0,0,67,0,70,0, 65,0,60,0,67,0,0,0];
~m4 = [60,63,67,70,72,70,67,63, 60,63,67,70,75,72,70,67, 65,67,70,72,75,72,70,65, 63,67,70,75,79,75,70,63];
~m5 = [60,0,0,0,0,0,0,0, 67,0,0,0,0,0,0,0, 60,0,0,0,0,0,0,0, 65,0,0,0,0,0,0,0];
~m6 = [60,0,63,0,65,67,0,70, 72,0,75,79,0,75,72,70, 67,0,65,63,0,60,0,63, 65,0,67,0,70,0,72,0];
~m7 = [36,0,36,39,0,41,36,0, 38,0,36,39,0,43,41,0, 36,0,36,39,0,41,36,0, 38,0,41,39,36,0,41,0];
~m8 = [60,67,72,67,60,67,72,75, 60,67,72,67,65,72,77,72, 60,67,72,67,60,67,72,79, 65,72,77,72,60,67,72,67];
~m9 = [60,0,63,0,67,0,0,0, 0,0,0,0,0,0,0,0, 65,0,67,0,70,0,72,0, 0,0,0,0,0,0,0,0];
~m10 = [60,0,63,0,67,0,70,0, 66,0,69,0,73,0,76,0, 60,0,63,0,67,0,70,0, 66,0,69,0,76,0,73,0];
~m11 = [60,0,0,0,0,0,0,0, 67,0,0,0,0,0,0,0, 60,0,0,0,0,0,0,0, 67,0,0,0,0,0,0,0];
~m12 = [60,63,67,70,60,63,67,70, 60,63,67,72,60,63,67,75, 60,63,67,72,65,67,70,75, 65,70,72,79,67,72,75,79];
~m13 = [60,0,63,65,0,67,0,69, 0,65,0,63,65,0,60,0, 67,0,65,63,0,67,69,0, 65,63,0,60,0,63,65,67];
~m14 = [60,61,63,0,65,63,61,60, 0,67,65,0,68,67,65,63, 60,61,63,0,65,63,61,60, 67,65,63,61,60,0,0,0];
~m15 = [60,0,63,0,65,0,68,71, 72,0,71,0,68,65,63,0, 60,0,63,65,0,68,71,0, 72,0,68,65,63,60,0,0];
~m16 = [48,0,51,53,54,0,55,0, 58,0,55,0,53,51,48,0, 48,0,51,53,54,0,55,58, 60,0,58,55,53,51,48,0];
~m17 = [72,0,0,67,0,65,0,62, 60,0,62,0,65,0,67,72, 74,0,0,69,0,67,0,65, 62,0,65,0,67,0,72,0];
~m18 = [60,0,67,0,70,67,60,0, 67,0,70,60,0,67,70,75, 60,0,67,0,70,67,60,65, 67,0,70,0,75,70,67,60];
~m19 = [60,63,65,67,70,67,65,63, 60,63,67,70,72,70,67,63, 65,67,70,72,75,72,70,67, 65,70,72,75,77,75,72,70, 63,65,67,70,72,70,67,65, 63,67,70,72,75,72,70,67, 60,63,67,70,72,75,72,70, 67,65,63,67,65,63,60,0];
~m20 = [60,0,61,0,63,65,0,67, 68,0,67,65,63,61,0,60, 63,0,65,0,67,68,0,70, 72,0,70,68,67,65,0,63, 65,0,67,0,68,70,0,72, 74,0,72,70,68,67,0,65, 72,0,70,68,67,65,63,61, 60,65,63,65,61,60,0,0];
~m21 = [60,62,0,65,67,0,69,70, 67,65,62,60,0,65,67,70, 62,0,65,67,69,0,70,72, 69,67,65,62,0,60,65,67, 65,67,69,70,72,70,69,67, 65,62,60,0,62,65,67,69, 72,70,69,67,65,62,60,0, 65,67,69,70,72,0,70,0];
~m22 = [60,63,65,67,70,67,65,63, 60,67,65,63,65,70,65,67, 63,65,67,70,72,70,67,65, 63,70,67,65,67,72,67,70, 65,67,68,70,72,70,68,67, 65,72,70,67,70,72,70,72, 67,68,70,72,74,72,70,68, 67,74,72,70,72,75,72,75];
~m23 = [63,70,67,70,65,72,70,72, 63,70,67,70,65,72,75,72, 65,72,70,72,67,75,72,75, 65,72,70,72,60,67,65,67, 63,70,67,70,65,72,70,72, 63,67,65,67,65,72,75,79, 65,72,70,72,67,75,72,75, 60,67,65,67,63,70,67,70];
~m24 = [60,67,72,75,72,67,60,67, 63,70,75,77,75,70,63,70, 65,72,77,79,77,72,65,72, 67,74,79,82,79,74,67,74, 79,77,75,72,70,67,65,63, 72,75,77,79,82,79,77,75, 65,72,77,79,84,82,79,77, 72,70,67,65,63,60,0,0];
~m25 = [72,0,70,0,67,0,65,0, 63,0,65,0,67,0,70,0, 72,0,75,0,77,0,75,0, 72,0,70,0,67,0,65,0, 77,0,75,0,72,0,70,0, 67,0,65,0,63,0,65,0, 79,0,77,0,75,0,72,0, 77,0,75,0,70,0,67,0];
~m26 = [60,63,67,70,72,75,79,82, 79,75,72,70,67,63,60,63, 67,70,72,75,79,82,84,87, 84,82,79,75,72,70,67,63, 65,67,70,72,75,79,82,84, 82,79,75,72,70,67,65,67, 63,65,67,70,72,75,77,79, 77,75,72,70,67,65,63,60];
~m27 = [60,0,67,0,70,0,67,60, 60,0,67,0,70,0,72,75, 63,0,70,0,72,0,70,63, 63,0,70,0,72,0,75,77, 65,0,72,0,75,0,72,65, 65,0,72,0,75,0,77,79, 63,0,70,0,72,0,70,63, 60,0,67,0,65,0,63,60];
~m28 = [72,0,70,0,67,70,0,65, 63,0,65,67,70,0,72,75, 77,0,75,0,72,75,0,70, 67,0,70,72,75,0,77,79, 75,0,77,0,75,72,0,70, 67,0,65,67,70,0,75,72, 70,0,67,0,65,67,0,63, 60,0,0,0,0,0,0,0];
// ---------- M29-M78 (genres et timbres varies) ----------------------
~m29 = [36,0,48,0,36,39,0,48, 36,0,51,39,0,36,48,0, 36,0,48,0,36,39,0,51, 39,0,36,48,0,39,0,36];
~m30 = [36,36,48,0,36,0,39,48, 36,51,0,36,39,0,48,0, 36,0,48,51,36,0,39,48, 36,36,51,0,36,48,39,0];
~m31 = [36,0,0,0,39,0,0,0, 41,0,0,0,36,0,0,0, 34,0,0,0,39,0,0,0, 41,0,0,0,43,0,0,0];
~m32 = [72,0,75,0,72,0,70,0, 67,0,70,0,72,0,75,0, 77,0,75,0,72,0,70,0, 75,0,72,0,70,0,67,0];
~m33 = [60,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 63,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0];
~m34 = [60,0,0,0,67,0,0,0, 70,0,0,0,0,0,0,0, 63,0,0,0,70,0,0,0, 72,0,0,0,0,0,0,0];
~m35 = [67,67,0,70,72,0,70,67, 65,0,67,0,0,0,0,0, 67,67,0,70,72,0,75,72, 70,0,67,0,0,0,0,0];
~m36 = [60,0,67,0,70,0,72,0, 72,70,67,70,72,0,0,0, 60,0,67,0,70,0,75,0, 75,72,70,72,75,0,0,0];
~m37 = [48,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 48,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0];
~m38 = [48,0,0,0,0,0,0,0, 54,0,0,0,0,0,0,0, 48,0,0,0,0,0,0,0, 54,0,0,0,0,0,0,0];
~m39 = [60,61,0,64,65,0,67,68, 71,0,68,67,65,64,0,61, 60,0,64,65,67,0,68,71, 72,71,68,67,65,64,61,60];
~m40 = [60,0,62,0,63,0,67,0, 68,0,67,63,0,62,60,0, 60,0,63,0,67,0,68,0, 72,0,68,67,63,62,60,0];
~m41 = [60,0,65,0,67,0,70,0, 72,0,70,67,65,0,60,0, 65,0,67,0,70,0,72,0, 74,0,72,70,67,65,60,0];
~m42 = [64,65,67,65,64,0,60,0, 64,65,67,68,67,65,64,0, 67,68,70,68,67,0,64,0, 67,68,70,72,70,68,67,65];
~m43 = [60,61,64,65,67,65,64,61, 60,0,64,65,67,68,67,65, 64,61,60,61,64,65,67,68, 70,68,67,65,64,61,60,0];
~m44 = [60,0,63,65,67,0,68,71, 72,71,68,67,65,63,0,60, 63,65,67,68,71,72,71,68, 67,65,63,60,63,65,67,68];
~m45 = [48,0,54,0,48,0,54,0, 60,0,54,0,48,0,54,0, 48,54,60,54,48,0,54,0, 60,54,48,54,60,54,48,0];
~m46 = [36,0,41,0,43,0,41,0, 36,0,43,41,36,0,41,0, 36,41,43,41,48,0,43,41, 36,0,43,41,36,41,36,0];
~m47 = [60,0,63,64,65,67,0,70, 67,65,64,63,60,0,67,70, 63,0,65,67,70,0,67,65, 63,60,63,65,67,65,60,0];
~m48 = [60,0,62,0,64,0,67,0, 69,0,67,64,62,60,0,0, 62,0,64,0,67,0,69,0, 72,0,69,67,64,62,60,0];
~m49 = [60,0,64,0,67,0,0,0, 67,0,71,0,74,0,0,0, 69,0,72,0,76,0,0,0, 65,0,69,0,72,0,0,0];
~m50 = [69,0,72,76,72,0,69,0, 65,0,69,72,69,0,65,0, 60,0,64,67,64,0,60,0, 67,0,71,74,71,0,67,0];
~m51 = [36,0,38,0,39,0,41,0, 43,0,44,0,43,0,41,0, 39,0,41,0,43,0,44,0, 46,0,48,0,46,0,43,0];
~m52 = [38,41,45,41,38,0,45,41, 43,47,50,47,43,0,50,47, 48,52,55,52,48,0,55,52, 50,47,43,41,38,0,0,0];
~m53 = [60,61,0,70,0,73,0,67, 61,0,73,0,67,70,0,76, 60,61,0,70,0,73,0,67, 73,76,0,70,67,61,60,0];
~m54 = [60,67,65,0,60,67,65,0, 60,67,65,0,60,67,65,0, 63,70,67,0,63,70,67,0, 60,67,65,0,60,67,65,0];
~m55 = [60,63,67,70,60,63,67,70, 60,63,67,70,60,63,67,70, 60,63,67,72,60,63,67,72, 60,63,67,75,60,63,67,70];
~m56 = [60,67,60,67,60,67,60,67, 60,67,60,67,60,67,60,67, 60,65,60,65,60,65,60,65, 60,67,60,67,60,70,60,70];
~m57 = [72,71,70,69,68,67,66,65, 64,63,62,61,60,0,0,0, 72,71,70,69,68,67,66,65, 64,63,62,61,60,59,58,57];
~m58 = [60,0,62,0,64,0,66,0, 68,0,70,0,72,0,0,0, 72,0,70,0,68,0,66,0, 64,0,62,0,60,0,0,0];
~m59 = [60,0,66,0,60,0,66,0, 72,0,66,0,60,0,66,0, 60,66,60,66,72,66,72,66, 60,0,66,0,72,0,66,60];
~m60 = [60,61,60,64,65,64,67,68, 67,65,64,61,60,64,65,0, 67,68,67,71,72,71,68,67, 65,64,61,60,61,64,65,67];
~m61 = [67,0,72,0,70,0,67,65, 63,0,67,0,70,0,72,75, 75,0,72,0,70,0,67,65, 63,0,60,0,65,0,67,70];
~m62 = [60,64,67,72,60,64,67,72, 62,65,69,74,62,65,69,74, 63,67,70,75,63,67,70,75, 60,64,67,72,65,67,70,72];
~m63 = [36,0,36,0,39,0,36,0, 41,0,36,0,39,0,41,0, 34,0,34,0,39,0,41,0, 43,0,41,0,39,0,36,0];
~m64 = [67,0,70,0,0,67,0,65, 67,0,70,72,0,70,0,67, 65,0,67,0,0,65,0,63, 65,0,67,70,0,72,0,70];
~m65 = [0,67,0,70,0,67,0,65, 0,67,0,72,0,70,0,67, 0,70,0,72,0,70,0,67, 0,65,0,67,0,70,0,72];
~m66 = [48,0,51,0,53,0,48,0, 46,0,51,0,53,0,55,0, 48,0,51,53,51,0,48,0, 46,0,53,0,55,0,51,48];
~m67 = [60,0,0,0,60,0,60,0, 73,0,0,0,73,0,73,0, 60,60,0,73,73,0,60,73, 79,0,73,0,60,0,0,0];
~m68 = [36,0,36,0,36,0,36,0, 39,0,39,0,39,0,39,0, 36,0,36,39,36,0,39,0, 41,0,39,0,36,0,41,0];
~m69 = [60,0,0,0,0,0,0,0, 63,0,0,0,0,0,0,0, 65,0,0,0,0,0,0,0, 67,0,0,0,0,0,0,0];
~m70 = [36,0,39,36,48,0,36,39, 41,0,39,36,48,0,41,39, 36,39,41,39,36,0,48,36, 43,41,39,36,39,0,41,39];
~m71 = [60,63,67,70,0,0,0,0, 60,63,67,70,0,0,0,0, 58,62,65,68,0,0,0,0, 60,63,67,70,0,0,0,0];
~m72 = [60,0,0,63,0,0,67,0, 0,70,0,0,0,67,0,0, 65,0,0,67,0,0,70,0, 0,72,0,0,0,70,0,0];
~m73 = [72,0,0,0,0,0,70,0, 0,0,67,0,0,0,65,0, 0,0,67,0,0,0,70,0, 0,0,72,0,0,0,75,0];
~m74 = [60,67,72,64,67,71,60,67, 62,65,69,62,65,69,60,67, 64,67,71,64,67,71,60,67, 65,69,72,65,69,72,60,67];
~m75 = [72,75,79,75,72,75,79,84, 79,75,72,75,72,70,67,65, 67,70,72,75,79,75,72,70, 67,65,67,70,72,70,67,60];
~m76 = [67,0,70,67,0,65,67,0, 63,0,67,0,70,65,67,0, 75,0,72,70,0,67,65,0, 67,0,70,0,72,75,70,67];
~m77 = [60,60,67,60,65,60,67,60, 62,62,69,62,67,62,69,62, 63,63,70,63,68,63,70,63, 65,65,72,65,70,65,72,65];
~m78 = [0,67,0,70,67,0,70,0, 72,0,70,0,67,70,0,65, 0,67,0,70,67,0,75,0, 72,70,0,67,65,0,70,0];
"[melodies/short] m1..m78 (32 steps) prets".postln;
)
@@ -0,0 +1,144 @@
// =====================================================================
// palette/melodies/xlong.scd
// ~mxl1..~mxl15 -- 15 patterns melodiques de 96 pas (6 mesures, AABA)
//
// Charge par palette/melodies/index.scd. Un seul bloc top-level.
// =====================================================================
(
~mxl1 = [
60, 0, 0, 0, 67, 0, 0, 0, 72, 0, 0, 0, 70, 0, 0, 0,
60, 0, 0, 0, 67, 0, 70, 0, 72, 0, 0, 0, 75, 0, 72, 70,
60, 67, 72, 67, 65, 67, 70, 72, 75, 72, 70, 67, 70, 72, 75, 79,
60, 67, 72, 75, 72, 67, 60, 67, 65, 72, 77, 79, 77, 72, 65, 72,
79, 77, 75, 72, 79, 77, 75, 72, 82, 79, 77, 75, 84, 82, 79, 77,
72, 0, 70, 0, 67, 0, 65, 0, 63, 0, 60, 0, 0, 0, 0, 0
];
~mxl2 = [
63, 0, 70, 0, 65, 0, 72, 0, 63, 0, 70, 0, 65, 0, 72, 75,
65, 0, 72, 0, 67, 0, 75, 0, 65, 0, 72, 0, 67, 0, 75, 79,
63, 70, 65, 72, 67, 75, 70, 77, 72, 79, 75, 82, 77, 84, 79, 86,
65, 72, 67, 75, 70, 77, 72, 79, 75, 82, 77, 84, 79, 86, 82, 89,
72, 0, 70, 0, 67, 0, 65, 0, 63, 0, 60, 0, 65, 0, 67, 0,
65, 67, 63, 65, 60, 63, 65, 67, 63, 65, 60, 63, 0, 0, 0, 0
];
~mxl3 = [
60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0,
65, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67, 0, 0, 0, 70, 0, 0, 0, 72, 0, 0, 0, 75, 0, 0, 0,
77, 0, 0, 0, 75, 0, 0, 0, 72, 0, 0, 0, 70, 0, 0, 0,
67, 0, 0, 0, 65, 0, 0, 0, 63, 0, 0, 0, 60, 0, 0, 0
];
~mxl4 = [
60, 61, 0, 64, 65, 0, 67, 68, 71, 0, 68, 67, 65, 64, 0, 61,
60, 0, 64, 65, 67, 68, 71, 72, 71, 68, 67, 65, 64, 61, 60, 0,
63, 64, 67, 68, 70, 71, 0, 73, 75, 73, 71, 70, 68, 67, 64, 63,
60, 61, 64, 65, 67, 68, 71, 72, 75, 72, 71, 68, 67, 64, 61, 60,
65, 67, 68, 71, 72, 73, 76, 75, 76, 75, 73, 72, 71, 68, 67, 65,
72, 73, 76, 75, 73, 72, 71, 68, 67, 65, 64, 61, 60, 0, 0, 0
];
~mxl5 = [
36, 0, 48, 0, 36, 39, 0, 48, 36, 0, 51, 39, 0, 36, 48, 0,
36, 0, 48, 51, 36, 39, 48, 51, 39, 0, 36, 48, 0, 39, 0, 36,
36, 51, 48, 36, 39, 0, 51, 48, 36, 0, 51, 53, 39, 36, 48, 0,
36, 53, 51, 48, 39, 0, 36, 48, 36, 0, 51, 0, 48, 39, 36, 0,
39, 0, 51, 0, 39, 41, 0, 51, 39, 0, 53, 41, 0, 39, 51, 0,
36, 39, 41, 43, 41, 39, 36, 0, 36, 0, 48, 0, 36, 0, 0, 0
];
~mxl6 = [
67, 0, 72, 0, 70, 0, 67, 65, 63, 0, 67, 0, 70, 0, 72, 75,
75, 0, 72, 0, 70, 0, 67, 65, 63, 0, 60, 0, 65, 0, 67, 70,
72, 0, 75, 0, 79, 0, 75, 72, 70, 0, 75, 0, 72, 0, 70, 67,
72, 0, 75, 0, 79, 0, 82, 79, 75, 0, 79, 0, 75, 0, 72, 70,
79, 0, 75, 0, 72, 0, 70, 67, 65, 0, 67, 0, 70, 0, 72, 75,
72, 0, 70, 0, 67, 0, 65, 0, 63, 0, 60, 0, 0, 0, 0, 0
];
~mxl7 = [
60, 67, 65, 0, 60, 67, 65, 0, 60, 67, 65, 0, 60, 67, 65, 0,
60, 67, 65, 0, 60, 67, 65, 0, 60, 67, 65, 0, 60, 67, 65, 70,
63, 70, 67, 0, 63, 70, 67, 0, 65, 72, 70, 0, 65, 72, 70, 75,
67, 74, 72, 0, 67, 74, 72, 77, 70, 77, 75, 0, 72, 79, 77, 82,
63, 70, 67, 0, 63, 70, 67, 0, 60, 67, 65, 70, 60, 67, 65, 70,
60, 67, 65, 0, 60, 67, 65, 0, 60, 0, 0, 0, 0, 0, 0, 0
];
~mxl8 = [
60, 61, 63, 0, 65, 63, 61, 60, 0, 67, 65, 0, 68, 67, 65, 63,
60, 61, 63, 0, 65, 63, 61, 60, 67, 65, 63, 61, 60, 0, 0, 0,
63, 65, 67, 0, 68, 67, 65, 63, 0, 70, 68, 0, 72, 70, 68, 67,
65, 67, 68, 0, 70, 68, 67, 65, 0, 72, 70, 0, 73, 72, 70, 68,
67, 68, 70, 0, 72, 70, 68, 67, 65, 63, 61, 60, 61, 63, 65, 67,
65, 0, 63, 0, 61, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
~mxl9 = [
60, 0, 0, 0, 60, 0, 60, 0, 73, 0, 0, 0, 73, 0, 73, 0,
60, 60, 0, 73, 73, 0, 60, 73, 79, 0, 73, 0, 60, 0, 0, 0,
65, 0, 0, 0, 65, 0, 65, 0, 78, 0, 0, 0, 78, 0, 78, 0,
65, 65, 0, 78, 78, 0, 65, 78, 84, 0, 78, 0, 65, 0, 0, 0,
67, 0, 0, 0, 67, 0, 80, 0, 80, 67, 80, 67, 80, 84, 80, 67,
72, 84, 72, 84, 72, 84, 72, 84, 60, 0, 0, 0, 0, 0, 0, 0
];
~mxl10 = [
67, 67, 0, 70, 72, 0, 70, 67, 65, 0, 67, 0, 0, 0, 0, 0,
67, 67, 0, 70, 72, 0, 75, 72, 70, 0, 67, 0, 0, 0, 0, 0,
72, 75, 0, 77, 79, 0, 77, 75, 72, 0, 70, 0, 67, 0, 65, 0,
77, 79, 0, 80, 79, 0, 77, 75, 72, 0, 75, 0, 77, 0, 79, 0,
72, 75, 0, 77, 79, 0, 84, 79, 77, 0, 75, 0, 72, 0, 70, 0,
67, 0, 70, 0, 72, 0, 75, 0, 72, 70, 67, 65, 67, 0, 0, 0
];
~mxl11 = [
72, 0, 0, 67, 0, 65, 0, 62, 60, 0, 62, 0, 65, 0, 67, 72,
74, 0, 0, 69, 0, 67, 0, 65, 62, 0, 65, 0, 67, 0, 72, 0,
74, 0, 77, 0, 79, 0, 77, 74, 72, 0, 69, 0, 67, 0, 65, 62,
77, 0, 79, 0, 81, 0, 79, 77, 74, 0, 72, 0, 69, 0, 67, 65,
79, 81, 84, 86, 84, 81, 79, 77, 74, 72, 69, 67, 65, 62, 60, 0,
72, 0, 67, 0, 65, 0, 62, 0, 60, 0, 0, 0, 0, 0, 0, 0
];
~mxl12 = [
48, 0, 54, 0, 48, 0, 54, 0, 60, 0, 54, 0, 48, 0, 54, 0,
48, 54, 60, 54, 48, 0, 54, 0, 60, 54, 48, 54, 60, 54, 48, 0,
36, 0, 42, 0, 36, 0, 42, 0, 48, 0, 42, 0, 36, 0, 42, 0,
48, 54, 60, 66, 60, 54, 48, 42, 36, 42, 48, 54, 60, 54, 48, 36,
48, 54, 60, 66, 72, 66, 60, 54, 48, 42, 36, 42, 48, 54, 60, 66,
72, 0, 66, 0, 60, 0, 54, 0, 48, 0, 36, 0, 0, 0, 0, 0
];
~mxl13 = [
60, 64, 67, 72, 60, 64, 67, 72, 62, 65, 69, 74, 62, 65, 69, 74,
63, 67, 70, 75, 63, 67, 70, 75, 60, 64, 67, 72, 65, 67, 70, 72,
65, 69, 72, 77, 65, 69, 72, 77, 67, 71, 74, 79, 67, 71, 74, 79,
72, 76, 79, 84, 72, 76, 79, 84, 75, 79, 82, 87, 75, 79, 82, 87,
79, 83, 86, 91, 79, 83, 86, 91, 77, 81, 84, 89, 77, 81, 84, 89,
72, 76, 79, 84, 65, 69, 72, 77, 60, 64, 67, 72, 60, 64, 67, 72
];
~mxl14 = [
60, 61, 0, 64, 65, 0, 67, 68, 71, 0, 68, 67, 65, 64, 0, 61,
60, 0, 64, 65, 67, 0, 68, 71, 72, 71, 68, 67, 65, 64, 61, 60,
65, 67, 0, 68, 71, 0, 72, 73, 76, 0, 73, 72, 71, 68, 0, 67,
72, 73, 76, 73, 72, 71, 68, 67, 65, 64, 61, 60, 64, 65, 67, 68,
71, 72, 73, 76, 79, 76, 73, 72, 71, 68, 67, 65, 64, 61, 60, 61,
65, 67, 71, 72, 73, 76, 73, 72, 71, 68, 67, 65, 61, 60, 0, 0
];
~mxl15 = [
72, 0, 70, 0, 67, 70, 0, 65, 63, 0, 65, 67, 70, 0, 72, 75,
77, 0, 75, 0, 72, 75, 0, 70, 67, 0, 70, 72, 75, 0, 77, 79,
75, 0, 77, 0, 75, 72, 0, 70, 67, 0, 65, 67, 70, 0, 75, 72,
79, 0, 82, 0, 79, 77, 0, 75, 72, 0, 70, 75, 77, 0, 82, 79,
75, 0, 72, 0, 70, 67, 0, 65, 63, 0, 60, 0, 65, 0, 67, 0,
70, 0, 67, 0, 65, 0, 63, 0, 60, 0, 0, 0, 0, 0, 0, 0
];
"[melodies/xlong] mxl1..mxl15 (96 steps) prets".postln;
)
@@ -0,0 +1,268 @@
// =====================================================================
// drum_kits.scd
// 21 drum kits complets :
// Originaux : TECHNO / DUBSTEP / DnB / TRAP / HOUSE / BREAKBEAT
// Nouveaux : HARDSTYLE / DRUM'N'BASS / JUNGLE / GARAGE 2-STEP /
// TRAP808 / PHONK / FOOTWORK / LIQUID DnB / SYNTHWAVE /
// INDUSTRIAL / TRIBAL WORLD / HARDCORE / LO-FI HIP-HOP /
// FUTURE GARAGE / DETROIT DEEP
//
// PREREQUIS : engine.scd evalue. Cmd+Entree sur un bloc pour l'appliquer.
// =====================================================================
// =====================================================================
// KITS ORIGINAUX (conserves)
// =====================================================================
// --- KIT TECHNO -----------------------------------------------------
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.55;
~kickFreq = 55; ~kickPitchAmt = 3.0; ~kickDecay = 0.32; ~kickDrive = 1.3; ~kickClick = 0.2;
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.18;
~snareSteps = (0 ! 16); ~clapSteps = (0 ! 16); ~percSteps = (0 ! 16);
~reload.value; "=== KIT TECHNO ===".postln;
)
// --- KIT DUBSTEP ----------------------------------------------------
(
~kickSteps = [1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0]; ~kickAmp = 0.6;
~kickFreq = 48; ~kickPitchAmt = 3.5; ~kickDecay = 0.5; ~kickDrive = 1.5; ~kickClick = 0.15;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.5;
~snareFreq = 220; ~snareNoiseAmt = 0.65; ~snareDecay = 0.16;
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; ~hatAmp = 0.10;
~clapSteps = (0 ! 16); ~percSteps = (0 ! 16);
~reload.value; "=== KIT DUBSTEP ===".postln;
)
// --- KIT DRUM'N'BASS -----------------------------------------------
(
~kickSteps = [1,0,0,0, 0,0,1,0, 0,0,1,0, 1,0,0,0]; ~kickAmp = 0.55;
~kickFreq = 55; ~kickPitchAmt = 4.0; ~kickDecay = 0.18; ~kickDrive = 1.5; ~kickClick = 0.3;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,1]; ~snareAmp = 0.5;
~snareFreq = 240; ~snareNoiseAmt = 0.7; ~snareDecay = 0.10;
~hatSteps = [1,1,1,1, 0,1,1,1, 1,1,1,1, 0,1,1,1]; ~hatAmp = 0.12;
~clapSteps = (0 ! 16); ~percSteps = (0 ! 16);
~reload.value; "=== KIT DnB ===".postln;
)
// --- KIT TRAP ------------------------------------------------------
(
~kickSteps = [1,0,0,0, 0,0,0,1, 1,0,0,0, 0,1,0,0]; ~kickAmp = 0.55;
~kickFreq = 42; ~kickPitchAmt = 2.5; ~kickDecay = 0.6; ~kickDrive = 1.2; ~kickClick = 0.08;
~hatSteps = [1,0,1,1, 0,1,1,0, 1,0,1,1, 0,1,1,0]; ~hatAmp = 0.10;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.3;
~percInst = \rim; ~percSteps = [0,0,1,0, 0,1,0,0, 1,0,1,0, 0,1,0,1]; ~percAmp = 0.2;
~snareSteps = (0 ! 16);
~reload.value; "=== KIT TRAP ===".postln;
)
// --- KIT HOUSE ------------------------------------------------------
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.55;
~kickFreq = 50; ~kickPitchAmt = 2.8; ~kickDecay = 0.35; ~kickDrive = 1.2; ~kickClick = 0.12;
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.15;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.35;
~snareSteps = (0 ! 16); ~percSteps = (0 ! 16);
~reload.value; "=== KIT HOUSE ===".postln;
)
// --- KIT BREAKBEAT --------------------------------------------------
(
~kickSteps = [1,0,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0]; ~kickAmp = 0.55;
~kickFreq = 55; ~kickPitchAmt = 3.0; ~kickDecay = 0.3; ~kickDrive = 1.3; ~kickClick = 0.25;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,1]; ~snareAmp = 0.45;
~snareFreq = 200; ~snareNoiseAmt = 0.6;
~hatSteps = [0,1,0,1, 1,1,0,1, 0,1,0,1, 1,1,0,1]; ~hatAmp = 0.10;
~percInst = \openHat; ~percSteps = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,1,0]; ~percAmp = 0.22;
~clapSteps = (0 ! 16);
~reload.value; "=== KIT BREAKBEAT ===".postln;
)
// =====================================================================
// NOUVEAUX KITS
// =====================================================================
// --- KIT HARDSTYLE -- kick gabber + clap large + tail "reverse" -----
(
~kickSteps = [1,0,0,0, 1,0,0,1, 1,0,0,0, 1,1,0,0]; ~kickAmp = 0.65;
~kickFreq = 62; ~kickPitchAmt = 5.5; ~kickDecay = 0.30; ~kickDrive = 4.0; ~kickClick = 0.45;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.55;
~snareFreq = 230; ~snareNoiseAmt = 0.7; ~snareDecay = 0.18;
~hatSteps = [0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1]; ~hatAmp = 0.12;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.45;
~percInst = \rim; ~percSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,1]; ~percAmp = 0.22;
~reload.value; "=== KIT HARDSTYLE ===".postln;
)
// --- KIT DRUM'N'BASS PUNCHY (alt) -- kick punchy + amen + hat fast --
(
~kickSteps = [1,0,0,1, 0,0,1,0, 0,0,1,0, 1,0,0,0]; ~kickAmp = 0.6;
~kickFreq = 58; ~kickPitchAmt = 4.5; ~kickDecay = 0.16; ~kickDrive = 1.8; ~kickClick = 0.4;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,1, 1,0,1,1]; ~snareAmp = 0.55;
~snareFreq = 250; ~snareNoiseAmt = 0.75; ~snareDecay = 0.09;
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; ~hatAmp = 0.13;
~clapSteps = (0 ! 16);
~percInst = \openHat; ~percSteps = [0,0,1,0, 0,0,0,0, 0,0,1,0, 0,0,0,0]; ~percAmp = 0.18;
~reload.value; "=== KIT DRUM'N'BASS PUNCHY ===".postln;
)
// --- KIT JUNGLE -- chopped breakbeat + ragga rim --------------------
(
~kickSteps = [1,0,0,1, 0,1,0,0, 1,0,0,1, 0,1,0,0]; ~kickAmp = 0.55;
~kickFreq = 55; ~kickPitchAmt = 3.5; ~kickDecay = 0.20; ~kickDrive = 1.6; ~kickClick = 0.32;
~snareSteps = [0,0,0,0, 1,0,1,0, 0,0,0,1, 1,0,0,1]; ~snareAmp = 0.55;
~snareFreq = 220; ~snareNoiseAmt = 0.7; ~snareDecay = 0.10;
~hatSteps = [1,1,0,1, 1,0,1,1, 1,1,0,1, 1,0,1,1]; ~hatAmp = 0.12;
~clapSteps = (0 ! 16);
~percInst = \rim; ~percSteps = [0,0,1,0, 0,1,0,0, 0,0,1,0, 0,1,0,0]; ~percAmp = 0.30;
~reload.value; "=== KIT JUNGLE ===".postln;
)
// --- KIT GARAGE 2-STEP -- clap shuffled + snare pad -----------------
(
~kickSteps = [1,0,0,0, 0,0,1,0, 0,0,0,0, 1,0,0,0]; ~kickAmp = 0.5;
~kickFreq = 50; ~kickPitchAmt = 2.8; ~kickDecay = 0.35; ~kickDrive = 1.2; ~kickClick = 0.15;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 0,1,0,0]; ~snareAmp = 0.4;
~snareFreq = 200; ~snareNoiseAmt = 0.55; ~snareDecay = 0.20;
~hatSteps = [0,1,1,0, 1,1,0,1, 0,1,1,0, 1,1,0,1]; ~hatAmp = 0.11;
~clapSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.32;
~percInst = \rim; ~percSteps = (0 ! 16);
~reload.value; "=== KIT GARAGE 2-STEP ===".postln;
)
// --- KIT TRAP808 -- 808 boom + clap + hi-hat trap rolls -------------
(
~kickSteps = [1,0,0,0, 0,0,0,1, 1,0,0,0, 0,1,0,0]; ~kickAmp = 0.6;
~kickFreq = 38; ~kickPitchAmt = 1.8; ~kickDecay = 0.85; ~kickDrive = 1.0; ~kickClick = 0.05;
~snareSteps = (0 ! 16);
~hatSteps = [1,1,1,0, 1,1,1,1, 1,1,1,0, 1,1,1,1]; ~hatAmp = 0.09;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.35;
~percInst = \rim; ~percSteps = [0,0,1,1, 0,1,0,0, 1,0,1,0, 0,1,1,0]; ~percAmp = 0.18;
~reload.value; "=== KIT TRAP808 ===".postln;
)
// --- KIT PHONK -- cowbell + 808 + pitched snare --------------------
(
~kickSteps = [1,0,0,0, 0,0,0,1, 1,0,0,0, 0,1,0,0]; ~kickAmp = 0.6;
~kickFreq = 40; ~kickPitchAmt = 2.0; ~kickDecay = 0.7; ~kickDrive = 1.2; ~kickClick = 0.08;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.45;
~snareFreq = 280; ~snareNoiseAmt = 0.65; ~snareDecay = 0.14;
~hatSteps = [1,0,1,1, 0,1,1,0, 1,0,1,1, 0,1,1,0]; ~hatAmp = 0.11;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.30;
~percInst = \cowbell; ~cowbellFreq = 800;
~percSteps = [0,0,1,0, 0,1,0,0, 1,0,0,1, 0,0,1,0]; ~percAmp = 0.32;
~reload.value; "=== KIT PHONK ===".postln;
)
// --- KIT FOOTWORK -- rapid hat + 808 + clap stutter -----------------
(
~kickSteps = [1,0,0,1, 1,0,0,0, 1,0,1,1, 0,0,1,0]; ~kickAmp = 0.55;
~kickFreq = 42; ~kickPitchAmt = 2.4; ~kickDecay = 0.5; ~kickDrive = 1.3; ~kickClick = 0.10;
~snareSteps = (0 ! 16);
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; ~hatAmp = 0.10;
~clapSteps = [0,0,0,1, 1,0,0,0, 0,0,0,1, 1,0,0,0]; ~clapAmp = 0.32;
~percInst = \rim; ~percSteps = [0,0,1,1, 0,1,1,0, 1,0,1,1, 0,1,0,1]; ~percAmp = 0.22;
~reload.value; "=== KIT FOOTWORK ===".postln;
)
// --- KIT LIQUID DnB -- subtle kick + breakbeat + smooth hat ---------
(
~kickSteps = [1,0,0,0, 0,0,1,0, 0,0,1,0, 1,0,0,0]; ~kickAmp = 0.45;
~kickFreq = 50; ~kickPitchAmt = 3.0; ~kickDecay = 0.28; ~kickDrive = 1.2; ~kickClick = 0.18;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.42;
~snareFreq = 220; ~snareNoiseAmt = 0.55; ~snareDecay = 0.14;
~hatSteps = [1,0,1,1, 0,1,0,1, 1,0,1,1, 0,1,0,1]; ~hatAmp = 0.09;
~clapSteps = (0 ! 16);
~percInst = \openHat; ~percSteps = [0,0,1,0, 0,0,0,0, 0,0,1,0, 0,0,0,0]; ~percAmp = 0.18;
~reload.value; "=== KIT LIQUID DnB ===".postln;
)
// --- KIT SYNTHWAVE -- kick 80s + reverb snare + hat soft ------------
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.5;
~kickFreq = 55; ~kickPitchAmt = 3.0; ~kickDecay = 0.30; ~kickDrive = 1.2; ~kickClick = 0.18;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.5;
~snareFreq = 180; ~snareNoiseAmt = 0.55; ~snareDecay = 0.22;
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.13;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.30;
~percSteps = (0 ! 16);
~reload.value; "=== KIT SYNTHWAVE ===".postln;
)
// --- KIT INDUSTRIAL -- kick metal + snare reverse + perc clang ------
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.6;
~kickFreq = 60; ~kickPitchAmt = 5.0; ~kickDecay = 0.28; ~kickDrive = 3.5; ~kickClick = 0.4;
~snareSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,1,0]; ~snareAmp = 0.55;
~snareFreq = 260; ~snareNoiseAmt = 0.8; ~snareDecay = 0.10;
~hatSteps = [0,1,0,1, 1,1,0,1, 0,1,0,1, 1,1,0,1]; ~hatAmp = 0.13;
~clapSteps = (0 ! 16);
~percInst = \rim; ~percSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,1]; ~percAmp = 0.4;
~reload.value; "=== KIT INDUSTRIAL ===".postln;
)
// --- KIT TRIBAL WORLD -- perc layered + kick small + shaker ---------
(
~kickSteps = [1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0]; ~kickAmp = 0.4;
~kickFreq = 48; ~kickPitchAmt = 2.2; ~kickDecay = 0.4; ~kickDrive = 1.0; ~kickClick = 0.06;
~snareSteps = (0 ! 16);
~hatSteps = [1,1,0,1, 1,0,1,1, 0,1,1,1, 1,0,1,1]; ~hatAmp = 0.08;
~clapSteps = (0 ! 16);
~percInst = \tom; ~tomFreq = 95;
~percSteps = [1,0,0,1, 0,0,1,0, 1,0,0,0, 0,1,0,0]; ~percAmp = 0.4;
~reload.value; "=== KIT TRIBAL WORLD ===".postln;
)
// --- KIT HARDCORE -- kick sature + snare 909 distordu + claps -------
(
~kickSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0]; ~kickAmp = 0.65;
~kickFreq = 60; ~kickPitchAmt = 5.0; ~kickDecay = 0.20; ~kickDrive = 4.0; ~kickClick = 0.35;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,1]; ~snareAmp = 0.55;
~snareFreq = 240; ~snareNoiseAmt = 0.85; ~snareDecay = 0.10;
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; ~hatAmp = 0.12;
~clapSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,1,0]; ~clapAmp = 0.42;
~percSteps = (0 ! 16);
~reload.value; "=== KIT HARDCORE ===".postln;
)
// --- KIT LO-FI HIP-HOP -- kick muted + snare vinyl + perc dust ------
(
~kickSteps = [1,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,0]; ~kickAmp = 0.45;
~kickFreq = 45; ~kickPitchAmt = 2.0; ~kickDecay = 0.55; ~kickDrive = 1.0; ~kickClick = 0.04;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.40;
~snareFreq = 190; ~snareNoiseAmt = 0.45; ~snareDecay = 0.15;
~hatSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0]; ~hatAmp = 0.08;
~clapSteps = (0 ! 16);
~percInst = \rim; ~percSteps = [0,0,1,0, 0,0,0,1, 0,0,1,0, 0,1,0,0]; ~percAmp = 0.14;
~reload.value; "=== KIT LO-FI HIP-HOP ===".postln;
)
// --- KIT FUTURE GARAGE -- kick rolling + snare ghost + 16th hat -----
(
~kickSteps = [1,0,0,0, 0,0,1,0, 0,0,0,0, 1,0,0,0]; ~kickAmp = 0.5;
~kickFreq = 48; ~kickPitchAmt = 2.5; ~kickDecay = 0.5; ~kickDrive = 1.2; ~kickClick = 0.10;
~snareSteps = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,1,0,0]; ~snareAmp = 0.30;
~snareFreq = 210; ~snareNoiseAmt = 0.5; ~snareDecay = 0.15;
~hatSteps = [0,1,1,0, 1,1,0,1, 0,1,1,0, 1,1,0,1]; ~hatAmp = 0.10;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,1]; ~clapAmp = 0.32;
~percSteps = (0 ! 16);
~reload.value; "=== KIT FUTURE GARAGE ===".postln;
)
// --- KIT DETROIT DEEP -- kick 909 + clap warm + perc minimal --------
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,1, 1,0,0,0]; ~kickAmp = 0.5;
~kickFreq = 52; ~kickPitchAmt = 2.8; ~kickDecay = 0.32; ~kickDrive = 1.3; ~kickClick = 0.16;
~snareSteps = (0 ! 16);
~hatSteps = [0,1,1,0, 1,1,0,1, 0,1,1,0, 1,1,0,1]; ~hatAmp = 0.13;
~clapSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,1,0]; ~clapAmp = 0.32;
~percInst = \rim; ~percSteps = [0,0,1,0, 0,0,0,0, 0,0,1,0, 0,0,0,0]; ~percAmp = 0.18;
~reload.value; "=== KIT DETROIT DEEP ===".postln;
)
@@ -0,0 +1,37 @@
// =====================================================================
// fills.scd
// Helpers rythmiques : ~snareFill / ~tomFill
//
// PREREQUIS : engine.scd evalue. Cmd+Entree sur le bloc pour l'appliquer.
// Compatible avec .load (un seul bloc top-level).
// =====================================================================
(
// --- Snare fill (8 hits sur 1s, accelerando d'amp) -----------------
~snareFill = { |hits = 8, dur = 1, ampStart = 0.3, ampEnd = 0.5|
Routine({
hits.do { |k|
var a = ampStart + ((ampEnd - ampStart) * (k / hits.max(1)));
Synth(\snare, [\amp, a, \decay, 0.06, \freq, 200 + (k * 10)]);
(dur / hits).wait;
};
}).play;
};
// --- Tom fill (descente sur 4 toms) -------------------------------
~tomFill = {
Routine({
[180, 140, 110, 85].do { |f|
Synth(\tom, [\freq, f, \decay, 0.25, \amp, 0.5]);
0.15.wait;
};
}).play;
};
"helpers ~snareFill / ~tomFill prets".postln;
)
// Exemples manuels (Cmd+Entree, ne PAS evaluer au .load) :
( ~snareFill.value; ) // fill standard 8 hits / 1s
( ~snareFill.(16, 2, 0.2, 0.55); ) // fill long accelerando
( ~tomFill.value; ) // descente 4 toms
@@ -0,0 +1,223 @@
// =====================================================================
// kicks.scd
// ~25 patterns kick (K1-K25) + ~12 timbres kick (T1-T12)
//
// PREREQUIS : engine.scd evalue. Cmd+Entree sur un bloc pour l'appliquer.
//
// En plus des blocs auto-applicants, chaque pattern est stocke dans
// ~kK1..~kK25 (tableau 16 steps brut, sans appliquer). Util pour
// ~seqPlay.([[16, { ~kickSteps = ~kK3; ~reload.value }], ...])
// cf. palette/sequences.scd.
// =====================================================================
// =====================================================================
// STOCKAGE (variables ~kK1..~kK25) -- evaluer ce bloc une fois pour
// enregistrer tous les patterns sous forme de variables reutilisables
// par sequences.scd.
// =====================================================================
(
// --- 4-on-floor classique -------------------------------------------
~kK1 = [ 1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0 ];
// --- techno syncope (8th deplaces) ----------------------------------
~kK2 = [ 1,0,0,0, 1,0,0,1, 1,0,0,0, 1,0,1,0 ];
// --- gabber double-temps (toutes 8th notes) -------------------------
~kK3 = [ 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0 ];
// --- dembow / latin (1 + 1+e + 3 + 3+e) -----------------------------
~kK4 = [ 1,0,0,1, 0,0,1,0, 1,0,0,1, 0,0,1,0 ];
// --- half-time (1 et 9 -- dubstep / phonk / hip-hop) ----------------
~kK5 = [ 1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0 ];
// --- breakbeat amen-like --------------------------------------------
~kK6 = [ 1,0,0,0, 0,0,1,0, 0,0,1,0, 1,0,0,0 ];
// --- techno triplet feel (3 over 4) ---------------------------------
~kK7 = [ 1,0,0,1, 0,0,1,0, 0,1,0,0, 1,0,0,1 ];
// --- minimal -- 1 kick par mesure (downtempo) -----------------------
~kK8 = [ 1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 ];
// --- 4-on-floor + ghost offbeat (techno minimal) --------------------
~kK9 = [ 1,0,0,0, 1,0,0,1, 1,0,0,0, 1,0,1,0 ];
// --- 4-on-floor syncope avancee -------------------------------------
~kK10 = [ 1,0,1,0, 1,0,0,1, 1,0,1,0, 1,0,1,1 ];
// --- double-time (1, 5, 9, 13 + 3, 7, 11, 15) -----------------------
~kK11 = [ 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0 ];
// --- breakbeat (drum'n'bass / jungle base) --------------------------
~kK12 = [ 1,0,0,1, 0,0,0,0, 0,0,1,0, 0,0,1,0 ];
// --- amen break kick pattern (jungle origine) -----------------------
~kK13 = [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ];
// --- gabber dense (kick sur tous 16th) ------------------------------
~kK14 = [ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1 ];
// --- hardstyle (kick + reverse + tail simulee) ----------------------
~kK15 = [ 1,0,0,0, 1,0,0,1, 1,0,0,0, 1,1,0,0 ];
// --- techno minimal kick fantome ------------------------------------
~kK16 = [ 1,0,0,0, 1,0,0,0, 1,0,0,1, 1,0,0,0 ];
// --- trance gros kick (4/4 punchy + ghost 16) -----------------------
~kK17 = [ 1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,1 ];
// --- house classique (kick 4/4 + offbeat hat impl) ------------------
~kK18 = [ 1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0 ];
// --- electro syncope ------------------------------------------------
~kK19 = [ 1,0,0,1, 0,0,0,0, 1,0,0,0, 0,0,1,0 ];
// --- trip-hop lazy --------------------------------------------------
~kK20 = [ 1,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,0 ];
// --- jungle rapid breakbeat -----------------------------------------
~kK21 = [ 1,0,0,1, 0,1,0,0, 1,0,0,1, 0,1,0,0 ];
// --- reggae / dub one-drop (kick sur 3) -----------------------------
~kK22 = [ 0,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0 ];
// --- afrobeat (kick polyrhythmique) ---------------------------------
~kK23 = [ 1,0,0,1, 0,1,0,0, 1,0,1,0, 0,1,0,0 ];
// --- garage 2-step --------------------------------------------------
~kK24 = [ 1,0,0,0, 0,0,1,0, 0,0,0,0, 1,0,0,0 ];
// --- footwork / juke (160 BPM, kick triple) -------------------------
~kK25 = [ 1,0,0,1, 1,0,0,0, 1,0,1,1, 0,0,1,0 ];
"=== kicks : 25 patterns stockes dans ~kK1..~kK25 ===".postln;
)
// =====================================================================
// PATTERNS RYTHMIQUES (blocs auto-applicants)
// =====================================================================
// --- K1 : 4-on-the-floor (defaut) -----------------------------------
( ~kickSteps = [ 1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0 ]; ~reload.value; "K1 4/4".postln; )
// --- K2 : techno syncope (8th notes deplaces) -----------------------
( ~kickSteps = [ 1,0,0,0, 1,0,0,1, 1,0,0,0, 1,0,1,0 ]; ~reload.value; "K2 syncope".postln; )
// --- K3 : gabber double-temps (toutes les 2 doubles-croches) --------
( ~kickSteps = [ 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0 ]; ~reload.value; "K3 gabber".postln; )
// --- K4 : dembow / latin (1 + 1+e + 3 + 3+e) ------------------------
( ~kickSteps = [ 1,0,0,1, 0,0,1,0, 1,0,0,1, 0,0,1,0 ]; ~reload.value; "K4 dembow".postln; )
// --- K5 : half-time (kick 1 et 9 -- dubstep/phonk/hip-hop) ----------
( ~kickSteps = [ 1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0 ]; ~reload.value; "K5 half-time".postln; )
// --- K6 : breakbeat (Amen-like) -------------------------------------
( ~kickSteps = [ 1,0,0,0, 0,0,1,0, 0,0,1,0, 1,0,0,0 ]; ~reload.value; "K6 breakbeat".postln; )
// --- K7 : techno triplet feel (3 over 4) ----------------------------
( ~kickSteps = [ 1,0,0,1, 0,0,1,0, 0,1,0,0, 1,0,0,1 ]; ~reload.value; "K7 triplet feel".postln; )
// --- K8 : minimal -- 1 kick par mesure (downtempo) -----------------
( ~kickSteps = [ 1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 ]; ~reload.value; "K8 minimal".postln; )
// --- K9 : 4-on-floor + ghost offbeat (techno minimaliste) ----------
( ~kickSteps = [ 1,0,0,0, 1,0,0,1, 1,0,0,0, 1,0,1,0 ]; ~reload.value; "K9 minimal ghost".postln; )
// --- K10 : 4-on-floor syncope avance (drive +) ---------------------
( ~kickSteps = [ 1,0,1,0, 1,0,0,1, 1,0,1,0, 1,0,1,1 ]; ~reload.value; "K10 syncope avance".postln; )
// --- K11 : double-time (8th sur tous les temps) --------------------
( ~kickSteps = [ 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0 ]; ~reload.value; "K11 double-time".postln; )
// --- K12 : breakbeat irregulier (DnB / jungle) ---------------------
( ~kickSteps = [ 1,0,0,1, 0,0,0,0, 0,0,1,0, 0,0,1,0 ]; ~reload.value; "K12 breakbeat irreg".postln; )
// --- K13 : amen break kick pattern (jungle classique) --------------
( ~kickSteps = [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ]; ~reload.value; "K13 amen break".postln; )
// --- K14 : gabber tres dense (kick partout) ------------------------
( ~kickSteps = [ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1 ]; ~reload.value; "K14 gabber dense".postln; )
// --- K15 : hardstyle (kick + reverse + tail simulee) ---------------
( ~kickSteps = [ 1,0,0,0, 1,0,0,1, 1,0,0,0, 1,1,0,0 ]; ~reload.value; "K15 hardstyle".postln; )
// --- K16 : techno minimaliste avec kick fantome --------------------
( ~kickSteps = [ 1,0,0,0, 1,0,0,0, 1,0,0,1, 1,0,0,0 ]; ~reload.value; "K16 techno minimal".postln; )
// --- K17 : trance kick gros + ghost 16 ------------------------------
( ~kickSteps = [ 1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,1 ]; ~reload.value; "K17 trance".postln; )
// --- K18 : house 4/4 classique --------------------------------------
( ~kickSteps = [ 1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0 ]; ~reload.value; "K18 house".postln; )
// --- K19 : electro syncope ------------------------------------------
( ~kickSteps = [ 1,0,0,1, 0,0,0,0, 1,0,0,0, 0,0,1,0 ]; ~reload.value; "K19 electro".postln; )
// --- K20 : trip-hop lazy kick ---------------------------------------
( ~kickSteps = [ 1,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,0 ]; ~reload.value; "K20 trip-hop".postln; )
// --- K21 : jungle rapid breakbeat -----------------------------------
( ~kickSteps = [ 1,0,0,1, 0,1,0,0, 1,0,0,1, 0,1,0,0 ]; ~reload.value; "K21 jungle".postln; )
// --- K22 : reggae / dub one-drop (kick sur 3) -----------------------
( ~kickSteps = [ 0,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0 ]; ~reload.value; "K22 one-drop".postln; )
// --- K23 : afrobeat polyrythmique -----------------------------------
( ~kickSteps = [ 1,0,0,1, 0,1,0,0, 1,0,1,0, 0,1,0,0 ]; ~reload.value; "K23 afrobeat".postln; )
// --- K24 : garage 2-step --------------------------------------------
( ~kickSteps = [ 1,0,0,0, 0,0,1,0, 0,0,0,0, 1,0,0,0 ]; ~reload.value; "K24 2-step".postln; )
// --- K25 : footwork / juke (160 BPM, kick triple) -------------------
( ~kickSteps = [ 1,0,0,1, 1,0,0,0, 1,0,1,1, 0,0,1,0 ]; ~reload.value; "K25 footwork".postln; )
// =====================================================================
// VARIATIONS DE TIMBRE (T1..T12)
// =====================================================================
// --- T1 : kick rond (defaut) -- techno classique --------------------
( ~kickFreq = 55; ~kickPitchAmt = 3.0; ~kickDecay = 0.32;
~kickDrive = 1.3; ~kickClick = 0.2;
~reload.value; "T1 rond".postln; )
// --- T2 : kick claquant -- snare-like, drive plus haut --------------
( ~kickFreq = 65; ~kickPitchAmt = 4.0; ~kickDecay = 0.18;
~kickDrive = 1.8; ~kickClick = 0.45;
~reload.value; "T2 claquant".postln; )
// --- T3 : kick boomy -- queue longue, sub fort, peu de click --------
( ~kickFreq = 48; ~kickPitchAmt = 2.5; ~kickDecay = 0.6;
~kickDrive = 1.2; ~kickClick = 0.08;
~reload.value; "T3 boomy".postln; )
// --- T4 : kick distordu -- gabber/hardcore --------------------------
( ~kickFreq = 60; ~kickPitchAmt = 5.0; ~kickDecay = 0.28;
~kickDrive = 3.5; ~kickClick = 0.3;
~reload.value; "T4 distordu".postln; )
// --- T5 : kick sub deep -- pour dub / techno minimal ----------------
( ~kickFreq = 42; ~kickPitchAmt = 2.0; ~kickDecay = 0.5;
~kickDrive = 1.0; ~kickClick = 0.05;
~reload.value; "T5 sub deep".postln; )
// --- T6 : kick punchy -- tres tight, snappy attack ------------------
( ~kickFreq = 70; ~kickPitchAmt = 3.5; ~kickDecay = 0.22;
~kickDrive = 2.0; ~kickClick = 0.35;
~reload.value; "T6 punchy".postln; )
// --- T7 : clap-style (melange kick + snare body) -------------------
( ~kickFreq = 75; ~kickPitchAmt = 4.5; ~kickDecay = 0.16;
~kickDrive = 1.5; ~kickClick = 0.55;
~reload.value; "T7 clap-style".postln; )
// --- T8 : 909 vintage (long decay, soft click) ---------------------
( ~kickFreq = 52; ~kickPitchAmt = 2.8; ~kickDecay = 0.45;
~kickDrive = 1.1; ~kickClick = 0.12;
~reload.value; "T8 909 vintage".postln; )
// --- T9 : 808 boom (ultra sub, decay tres long) --------------------
( ~kickFreq = 38; ~kickPitchAmt = 1.8; ~kickDecay = 0.85;
~kickDrive = 1.0; ~kickClick = 0.04;
~reload.value; "T9 808 boom".postln; )
// --- T10 : gabber distorded (drive max + click) --------------------
( ~kickFreq = 62; ~kickPitchAmt = 5.5; ~kickDecay = 0.22;
~kickDrive = 4.5; ~kickClick = 0.5;
~reload.value; "T10 gabber distorded".postln; )
// --- T11 : breakcore (tres court, metallique) ---------------------
( ~kickFreq = 80; ~kickPitchAmt = 4.0; ~kickDecay = 0.10;
~kickDrive = 2.5; ~kickClick = 0.6;
~reload.value; "T11 breakcore".postln; )
// --- T12 : dub kick (long sub tail, peu de click) ------------------
( ~kickFreq = 40; ~kickPitchAmt = 1.5; ~kickDecay = 0.95;
~kickDrive = 1.0; ~kickClick = 0.03;
~reload.value; "T12 dub kick".postln; )
@@ -0,0 +1,579 @@
// =====================================================================
// patterns_genre.scd -- Patterns rythmiques signature par genre
// (issus de la deep recherche musicologique)
//
// Chaque preset charge kick/hat/snare/clap/perc en alignement
// authentique au genre. Cmd+Entree = applique le pattern complet.
// =====================================================================
// =====================================================================
// ACID TECHNO (135-150) -- 4-on-floor + hihat shuffle 16ths
// =====================================================================
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.55;
~kickFreq=55;~kickPitchAmt=3.0;~kickDecay=0.32;~kickDrive=1.4;~kickClick=0.22;
~hatSteps = [1,0,1,1, 0,1,1,0, 1,0,1,1, 0,1,1,0]; ~hatAmp = 0.13; // shuffle
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.35; // discret
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.30;
~percSteps = (0!16);
~reload.value; "=== ACID TECHNO 135-150 BPM ===".postln;
)
// =====================================================================
// HARD TECHNO (140-148) -- kick punchy distorted + hat 16ths rapide
// =====================================================================
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.6;
~kickFreq=60;~kickPitchAmt=4.5;~kickDecay=0.28;~kickDrive=3.0;~kickClick=0.32;
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; ~hatAmp = 0.14;
~snareSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,1,0]; ~snareAmp = 0.5;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.4;
~percSteps = (0!16);
~reload.value; "=== HARD TECHNO 140-148 BPM ===".postln;
)
// =====================================================================
// HYPNOTIC TECHNO (128-135) -- kick discret + ghost snare lointain
// =====================================================================
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.45;
~kickFreq=50;~kickPitchAmt=2.5;~kickDecay=0.35;~kickDrive=1.0;~kickClick=0.08;
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.06; // tres discret
~snareSteps = (0!16);
~clapSteps = (0!16);
~percSteps = [0,0,0,0, 0,0,0,1, 0,0,0,0, 0,0,1,0]; ~percAmp = 0.15; // ghost
~reload.value; "=== HYPNOTIC TECHNO 128-135 BPM ===".postln;
)
// =====================================================================
// DUBSTEP (140 half-time) -- kick on 1, snare on 3 (ressenti 70 BPM)
// =====================================================================
(
~kickSteps = [1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0]; ~kickAmp = 0.6;
~kickFreq=48;~kickPitchAmt=3.5;~kickDecay=0.5;~kickDrive=1.5;~kickClick=0.15;
~hatSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0]; ~hatAmp = 0.10;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.55;
~snareFreq=220;~snareNoiseAmt=0.7;~snareDecay=0.18;
~clapSteps = (0!16);
~percSteps = (0!16);
~reload.value; "=== DUBSTEP 140 BPM half-time ===".postln;
)
// =====================================================================
// UPLIFTING TRANCE (138) -- kick 4/4 + offbeat bass + clap 2&4
// =====================================================================
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.55;
~kickFreq=55;~kickPitchAmt=3.2;~kickDecay=0.28;~kickDrive=1.5;~kickClick=0.20;
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.16;
~snareSteps = (0!16);
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.35;
~percSteps = (0!16);
~reload.value; "=== UPLIFTING TRANCE 138 BPM ===".postln;
)
// =====================================================================
// DETROIT TECHNO (124) -- 909 swing 56% + ghost notes
// =====================================================================
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,1, 1,0,0,0]; ~kickAmp = 0.5; // ghost
~kickFreq=52;~kickPitchAmt=2.8;~kickDecay=0.32;~kickDrive=1.3;~kickClick=0.16;
~hatSteps = [0,1,1,0, 1,1,0,1, 0,1,1,0, 1,1,0,1]; ~hatAmp = 0.13; // shuffle
~snareSteps = (0!16);
~clapSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,1,0]; ~clapAmp = 0.32; // ghost claps
~percSteps = (0!16);
~reload.value; "=== DETROIT TECHNO 124 BPM swing ===".postln;
)
// =====================================================================
// LIQUID DNB (174) -- amen feel + ghost notes
// =====================================================================
(
~kickSteps = [1,0,0,0, 0,0,1,0, 0,0,1,0, 1,0,0,0]; ~kickAmp = 0.5;
~kickFreq=55;~kickPitchAmt=4.0;~kickDecay=0.18;~kickDrive=1.5;~kickClick=0.30;
~hatSteps = [1,1,1,1, 0,1,1,1, 1,1,1,1, 0,1,1,1]; ~hatAmp = 0.13;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,1, 1,0,0,1]; ~snareAmp = 0.55;
~snareFreq=240;~snareNoiseAmt=0.7;~snareDecay=0.10;
~clapSteps = (0!16);
~percSteps = (0!16);
~reload.value; "=== LIQUID DNB 174 BPM amen ===".postln;
)
// =====================================================================
// NEUROFUNK (174) -- breakbeat complex + 32th rolls
// =====================================================================
(
~kickSteps = [1,0,0,0, 0,0,1,0, 0,0,1,0, 1,0,0,1]; ~kickAmp = 0.55;
~kickFreq=58;~kickPitchAmt=4.5;~kickDecay=0.15;~kickDrive=2.0;~kickClick=0.4;
~hatSteps = [1,1,1,1, 0,1,1,1, 1,1,1,1, 0,1,1,1]; ~hatAmp = 0.14;
~snareSteps = [0,0,0,1, 1,0,0,0, 0,1,0,0, 1,0,1,1]; ~snareAmp = 0.55;
~snareFreq=260;~snareNoiseAmt=0.75;~snareDecay=0.08;
~clapSteps = (0!16);
~percSteps = (0!16);
~reload.value; "=== NEUROFUNK DNB 174 BPM ===".postln;
)
// =====================================================================
// FUTURE GARAGE (130-140) -- 2-step syncope, kick irregulier
// =====================================================================
(
~kickSteps = [1,0,0,0, 0,0,1,0, 0,0,0,0, 1,0,0,0]; ~kickAmp = 0.5;
~kickFreq=48;~kickPitchAmt=2.5;~kickDecay=0.5;~kickDrive=1.2;~kickClick=0.10;
~hatSteps = [0,1,1,0, 1,1,0,1, 0,1,1,0, 1,1,0,1]; ~hatAmp = 0.10; // skippy
~snareSteps = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,1,0,0]; ~snareAmp = 0.3; // sparse
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,1]; ~clapAmp = 0.32;
~percSteps = (0!16);
~reload.value; "=== FUTURE GARAGE 2-STEP ===".postln;
)
// =====================================================================
// HARDCORE GABBER (180) -- kick gabber distorted intense, hihat absent
// =====================================================================
(
~kickSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0]; ~kickAmp = 0.65;
~kickFreq=60;~kickPitchAmt=5.0;~kickDecay=0.18;~kickDrive=4.0;~kickClick=0.35;
~hatSteps = (0!16); ~hatAmp = 0;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.5;
~clapSteps = (0!16);
~percSteps = (0!16);
~reload.value; "=== HARDCORE GABBER 180 BPM ===".postln;
)
// =====================================================================
// PHONK (130 half-time) -- cowbell Memphis + 808 + trap hats
// =====================================================================
(
~kickSteps = [1,0,0,0, 0,0,0,1, 1,0,0,0, 0,1,0,0]; ~kickAmp = 0.55;
~kickFreq=42;~kickPitchAmt=2.5;~kickDecay=0.6;~kickDrive=1.3;~kickClick=0.10;
~hatSteps = [1,0,1,1, 0,1,1,0, 1,0,1,1, 0,1,1,0]; ~hatAmp = 0.10; // trap triplets
~snareSteps = (0!16);
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.3;
~percInst = \cowbell; ~cowbellFreq = 800;
~percSteps = [0,0,1,0, 0,1,0,0, 1,0,0,1, 0,0,1,0]; ~percAmp = 0.32;
~reload.value; "=== PHONK 130 BPM ===".postln;
)
// =====================================================================
// TRIBAL ETHNO (100-128) -- toms polyrhythm 3:4
// =====================================================================
(
~kickSteps = [1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0]; ~kickAmp = 0.45;
~kickFreq=48;~kickPitchAmt=2.2;~kickDecay=0.4;~kickDrive=1.1;~kickClick=0.08;
~hatSteps = [0,0,1,0, 0,0,0,1, 0,0,1,0, 0,1,0,0]; ~hatAmp = 0.08; // organique
~snareSteps = (0!16);
~clapSteps = (0!16);
~percInst = \tom; ~tomFreq = 90;
~percSteps = [1,0,0,1, 0,0,1,0, 1,0,0,0, 0,1,0,0]; ~percAmp = 0.4;
~reload.value; "=== TRIBAL ETHNO 100 BPM ===".postln;
)
// =====================================================================
// AMBIENT CINEMATIC (60-80) -- percussions ultra rares + tom solitaire
// =====================================================================
(
~kickSteps = (0!16); ~kickAmp = 0;
~hatSteps = (0!16);
~snareSteps = (0!16);
~clapSteps = (0!16);
~percInst = \tom; ~tomFreq = 60;
~percSteps = [1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; ~percAmp = 0.3;
~reload.value; "=== AMBIENT CINEMATIC -- pas de kick ===".postln;
)
// =====================================================================
// CHIPTUNE 8-BIT (150) -- kick triangle sharp, noise burst
// =====================================================================
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.45;
~kickFreq=80;~kickPitchAmt=4.5;~kickDecay=0.12;~kickDrive=1.4;~kickClick=0.5; // chip kick
~hatSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0]; ~hatAmp = 0.12;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.40;
~clapSteps = (0!16);
~percSteps = (0!16);
~reload.value; "=== CHIPTUNE 8-BIT 150 BPM ===".postln;
)
// =====================================================================
// SYNTHWAVE 80s (100) -- kick + LinnDrum gated snare + hat 8ths
// =====================================================================
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.5;
~kickFreq=55;~kickPitchAmt=3.0;~kickDecay=0.3;~kickDrive=1.2;~kickClick=0.18;
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.13;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.5; // gated
~snareFreq=180;~snareNoiseAmt=0.55;~snareDecay=0.20;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.30; // doubl
~percSteps = (0!16);
~reload.value; "=== SYNTHWAVE 100 BPM ===".postln;
)
// =====================================================================
// INDUSTRIAL TECHNO (140-160) -- kick hard distorded + metal hits
// =====================================================================
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.6;
~kickFreq=60;~kickPitchAmt=5.0;~kickDecay=0.28;~kickDrive=3.5;~kickClick=0.4;
~hatSteps = [0,1,0,1, 1,1,0,1, 0,1,0,1, 1,1,0,1]; ~hatAmp = 0.13; // breakbeat
~snareSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,1,0]; ~snareAmp = 0.55;
~clapSteps = (0!16);
~percInst = \rim; ~percSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~percAmp = 0.4;
~reload.value; "=== INDUSTRIAL TECHNO ===".postln;
)
// =====================================================================
// DUB TECHNO (126-130) -- kick muffle + clap echo dub
// =====================================================================
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.45;
~kickFreq=48;~kickPitchAmt=2.3;~kickDecay=0.45;~kickDrive=1.0;~kickClick=0.06; // muffled
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.06;
~snareSteps = (0!16);
~clapSteps = [0,0,0,0, 0,1,0,0, 0,0,0,0, 0,1,0,0]; ~clapAmp = 0.25; // delay
~percSteps = (0!16);
~reload.value; "=== DUB TECHNO 126 BPM ===".postln;
)
// =====================================================================
// GLITCH IDM (variable) -- patterns generes aleatoires
// =====================================================================
(
~kickSteps = ~genKickPattern.(0.4);
~snareSteps = ~genKickPattern.(0.3);
~hatSteps = ~genKickPattern.(0.7);
~clapSteps = ~genKickPattern.(0.2);
~percSteps = ~genKickPattern.(0.4);
~kickFreq=55;~kickPitchAmt=3.5;~kickDecay=0.18;~kickDrive=2.0;~kickClick=0.3;
~kickAmp = 0.5; ~hatAmp = 0.12; ~snareAmp = 0.45; ~clapAmp = 0.3; ~percAmp = 0.3;
~reload.value; "=== GLITCH IDM random ===".postln;
)
// =====================================================================
// DRONE DOOM (60) -- kick massif espace + crash long
// =====================================================================
(
~kickSteps = [1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; ~kickAmp = 0.6;
~kickFreq=40;~kickPitchAmt=2.0;~kickDecay=0.8;~kickDrive=2.0;~kickClick=0.04;
~hatSteps = (0!16);
~snareSteps = (0!16);
~clapSteps = (0!16);
~percInst = \tom; ~tomFreq = 50;
~percSteps = [1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; ~percAmp = 0.4;
~reload.value; "=== DRONE DOOM 60 BPM ===".postln;
)
// =====================================================================
// ===== 21 GENRES SUPPLEMENTAIRES (deuxieme batch) =================
// =====================================================================
// =====================================================================
// HARD TECHNO BERLIN (140-145) -- 4/4 hard kick + open hat sustain
// =====================================================================
(
~bpm = 142; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.62;
~kickFreq=62;~kickPitchAmt=4.8;~kickDecay=0.26;~kickDrive=3.2;~kickClick=0.36;
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; ~hatAmp = 0.13;
~snareSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,1,0]; ~snareAmp = 0.5;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.40;
~percInst = \openHat; ~percSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~percAmp = 0.20;
~reload.value; "=== HARD TECHNO BERLIN 142 BPM ===".postln;
)
// =====================================================================
// ACID HOUSE 88 BPM (Chicago classique)
// =====================================================================
(
~bpm = 88; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.50;
~kickFreq=50;~kickPitchAmt=2.8;~kickDecay=0.38;~kickDrive=1.2;~kickClick=0.10;
~hatSteps = [0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1]; ~hatAmp = 0.12;
~snareSteps = (0!16);
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.34;
~percSteps = (0!16);
~reload.value; "=== ACID HOUSE 88 BPM ===".postln;
)
// =====================================================================
// TRANCE 138 UPLIFTING (variation grand kick + clap reverb)
// =====================================================================
(
~bpm = 138; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.58;
~kickFreq=55;~kickPitchAmt=3.5;~kickDecay=0.30;~kickDrive=1.6;~kickClick=0.22;
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.18;
~snareSteps = (0!16);
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.42;
~percSteps = [0,0,0,0, 0,0,0,1, 0,0,0,0, 0,0,0,1]; ~percAmp = 0.18;
~percInst = \rim;
~reload.value; "=== TRANCE 138 UPLIFTING (V2) ===".postln;
)
// =====================================================================
// PSY TRANCE 145 (basse offbeat + kick punchy + hat dense)
// =====================================================================
(
~bpm = 145; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.6;
~kickFreq=58;~kickPitchAmt=4.0;~kickDecay=0.22;~kickDrive=1.8;~kickClick=0.28;
~hatSteps = [0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1]; ~hatAmp = 0.16;
~snareSteps = (0!16);
~clapSteps = (0!16);
~percInst = \rim; ~percSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~percAmp = 0.25;
~reload.value; "=== PSY TRANCE 145 BPM ===".postln;
)
// =====================================================================
// GOA TRANCE 145 (psy roots, 16th notes acid)
// =====================================================================
(
~bpm = 145; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.55;
~kickFreq=56;~kickPitchAmt=3.5;~kickDecay=0.25;~kickDrive=1.5;~kickClick=0.22;
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; ~hatAmp = 0.10;
~snareSteps = (0!16);
~clapSteps = (0!16);
~percInst = \tom; ~tomFreq = 100;
~percSteps = [0,0,1,0, 0,1,0,0, 1,0,0,1, 0,0,1,0]; ~percAmp = 0.30;
~reload.value; "=== GOA TRANCE 145 BPM ===".postln;
)
// =====================================================================
// HARDSTYLE 150 (kick gabber + reverse kick simulee + clap large)
// =====================================================================
(
~bpm = 150; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 1,0,0,1, 1,0,0,0, 1,1,0,0]; ~kickAmp = 0.65;
~kickFreq=62;~kickPitchAmt=5.5;~kickDecay=0.30;~kickDrive=4.0;~kickClick=0.45;
~hatSteps = [0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1]; ~hatAmp = 0.13;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.55;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.45;
~percSteps = (0!16);
~reload.value; "=== HARDSTYLE 150 BPM ===".postln;
)
// =====================================================================
// HANDS UP TRANCE 150 (eurodance moderne, clap large)
// =====================================================================
(
~bpm = 150; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.55;
~kickFreq=58;~kickPitchAmt=3.8;~kickDecay=0.26;~kickDrive=1.6;~kickClick=0.25;
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.20;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.5;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.45;
~percInst = \openHat; ~percSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~percAmp = 0.22;
~reload.value; "=== HANDS UP TRANCE 150 BPM ===".postln;
)
// =====================================================================
// LIQUID DNB 174 (alt) -- amen smooth + ghost note + open hat
// =====================================================================
(
~bpm = 174; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 0,0,1,0, 0,0,1,0, 1,0,0,0]; ~kickAmp = 0.5;
~kickFreq=52;~kickPitchAmt=3.5;~kickDecay=0.20;~kickDrive=1.3;~kickClick=0.25;
~hatSteps = [1,0,1,1, 0,1,0,1, 1,0,1,1, 0,1,0,1]; ~hatAmp = 0.11;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.45;
~snareFreq=220;~snareNoiseAmt=0.55;~snareDecay=0.13;
~clapSteps = (0!16);
~percInst = \openHat; ~percSteps = [0,0,1,0, 0,0,0,0, 0,0,1,0, 0,0,0,0]; ~percAmp = 0.18;
~reload.value; "=== LIQUID DNB 174 BPM (V2) ===".postln;
)
// =====================================================================
// NEUROFUNK DNB 174 (alt) -- snare reese + breakbeat hard
// =====================================================================
(
~bpm = 174; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,1, 0,0,1,0, 0,0,1,1, 1,0,0,0]; ~kickAmp = 0.6;
~kickFreq=60;~kickPitchAmt=4.8;~kickDecay=0.14;~kickDrive=2.2;~kickClick=0.42;
~hatSteps = [1,1,1,1, 0,1,1,1, 1,1,0,1, 1,1,1,0]; ~hatAmp = 0.13;
~snareSteps = [0,0,0,1, 1,0,0,0, 0,1,0,0, 1,0,1,1]; ~snareAmp = 0.6;
~snareFreq=270;~snareNoiseAmt=0.8;~snareDecay=0.07;
~clapSteps = (0!16);
~percSteps = (0!16);
~reload.value; "=== NEUROFUNK DNB 174 BPM (V2) ===".postln;
)
// =====================================================================
// JUNGLE 165 (chopped breakbeat + ragga rim)
// =====================================================================
(
~bpm = 165; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,1, 0,1,0,0, 1,0,0,1, 0,1,0,0]; ~kickAmp = 0.55;
~kickFreq=55;~kickPitchAmt=3.5;~kickDecay=0.20;~kickDrive=1.6;~kickClick=0.32;
~hatSteps = [1,1,0,1, 1,0,1,1, 1,1,0,1, 1,0,1,1]; ~hatAmp = 0.12;
~snareSteps = [0,0,0,0, 1,0,1,0, 0,0,0,1, 1,0,0,1]; ~snareAmp = 0.55;
~snareFreq=220;~snareNoiseAmt=0.7;~snareDecay=0.10;
~clapSteps = (0!16);
~percInst = \rim; ~percSteps = [0,0,1,0, 0,1,0,0, 0,0,1,0, 0,1,0,0]; ~percAmp = 0.30;
~reload.value; "=== JUNGLE 165 BPM ===".postln;
)
// =====================================================================
// DRUM & BASS JUMP-UP 174 (kick funky + snare bouncy)
// =====================================================================
(
~bpm = 174; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 0,1,1,0, 0,0,1,0, 0,1,0,0]; ~kickAmp = 0.55;
~kickFreq=58;~kickPitchAmt=4.2;~kickDecay=0.18;~kickDrive=1.8;~kickClick=0.35;
~hatSteps = [1,1,1,1, 0,1,1,1, 1,1,1,1, 0,1,1,1]; ~hatAmp = 0.13;
~snareSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,1,0]; ~snareAmp = 0.55;
~snareFreq=240;~snareNoiseAmt=0.7;~snareDecay=0.10;
~clapSteps = (0!16);
~percSteps = (0!16);
~reload.value; "=== DNB JUMP-UP 174 BPM ===".postln;
)
// =====================================================================
// FUTURE BASS 150 (half-time, snare large, hat trap)
// =====================================================================
(
~bpm = 150; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0]; ~kickAmp = 0.55;
~kickFreq=48;~kickPitchAmt=3.0;~kickDecay=0.45;~kickDrive=1.4;~kickClick=0.15;
~hatSteps = [1,0,1,1, 0,1,1,0, 1,0,1,1, 0,1,1,0]; ~hatAmp = 0.10;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.55;
~snareFreq=210;~snareNoiseAmt=0.6;~snareDecay=0.20;
~clapSteps = (0!16);
~percInst = \rim; ~percSteps = [0,0,1,0, 0,0,0,1, 0,0,1,0, 0,0,0,1]; ~percAmp = 0.18;
~reload.value; "=== FUTURE BASS 150 BPM ===".postln;
)
// =====================================================================
// DEEP HOUSE 122 (kick warm + clap soft + hat shuffled)
// =====================================================================
(
~bpm = 122; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.50;
~kickFreq=48;~kickPitchAmt=2.5;~kickDecay=0.40;~kickDrive=1.1;~kickClick=0.10;
~hatSteps = [0,1,1,0, 1,1,0,1, 0,1,1,0, 1,1,0,1]; ~hatAmp = 0.10;
~snareSteps = (0!16);
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.30;
~percInst = \rim; ~percSteps = [0,0,1,0, 0,0,0,0, 0,0,1,0, 0,0,0,0]; ~percAmp = 0.16;
~reload.value; "=== DEEP HOUSE 122 BPM ===".postln;
)
// =====================================================================
// TECH HOUSE 126 (groove tight + hat 16th + clap tight)
// =====================================================================
(
~bpm = 126; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.55;
~kickFreq=52;~kickPitchAmt=2.8;~kickDecay=0.32;~kickDrive=1.3;~kickClick=0.18;
~hatSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0]; ~hatAmp = 0.15;
~snareSteps = (0!16);
~clapSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.34;
~percInst = \rim; ~percSteps = [0,0,1,0, 0,0,0,1, 0,0,1,0, 0,0,0,1]; ~percAmp = 0.22;
~reload.value; "=== TECH HOUSE 126 BPM ===".postln;
)
// =====================================================================
// PROGRESSIVE HOUSE 124 (kick pad + clap echo + hat air)
// =====================================================================
(
~bpm = 124; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.52;
~kickFreq=50;~kickPitchAmt=2.6;~kickDecay=0.36;~kickDrive=1.2;~kickClick=0.12;
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.15;
~snareSteps = (0!16);
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,1, 1,0,0,0]; ~clapAmp = 0.32;
~percInst = \openHat; ~percSteps = [0,0,1,0, 0,0,0,0, 0,0,1,0, 0,0,0,0]; ~percAmp = 0.20;
~reload.value; "=== PROGRESSIVE HOUSE 124 BPM ===".postln;
)
// =====================================================================
// TRIBAL TECHNO 130 (toms layered + kick warm + percussions dense)
// =====================================================================
(
~bpm = 130; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.50;
~kickFreq=50;~kickPitchAmt=2.6;~kickDecay=0.34;~kickDrive=1.2;~kickClick=0.12;
~hatSteps = [0,0,1,0, 0,1,0,1, 0,0,1,0, 0,1,0,1]; ~hatAmp = 0.10;
~snareSteps = (0!16);
~clapSteps = (0!16);
~percInst = \tom; ~tomFreq = 95;
~percSteps = [1,0,0,1, 0,1,1,0, 1,0,0,1, 0,1,1,0]; ~percAmp = 0.40;
~reload.value; "=== TRIBAL TECHNO 130 BPM ===".postln;
)
// =====================================================================
// INDUSTRIAL 145 (alt sombre, perc clang)
// =====================================================================
(
~bpm = 145; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 1,0,1,0, 1,0,0,0, 1,0,1,0]; ~kickAmp = 0.62;
~kickFreq=62;~kickPitchAmt=5.2;~kickDecay=0.28;~kickDrive=3.8;~kickClick=0.42;
~hatSteps = [0,1,0,1, 1,1,0,1, 0,1,0,1, 1,1,0,1]; ~hatAmp = 0.13;
~snareSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,1,0]; ~snareAmp = 0.55;
~snareFreq=270;~snareNoiseAmt=0.85;~snareDecay=0.08;
~clapSteps = (0!16);
~percInst = \rim; ~percSteps = [1,0,0,1, 1,0,1,0, 1,0,0,1, 1,0,1,1]; ~percAmp = 0.45;
~reload.value; "=== INDUSTRIAL 145 BPM (V2) ===".postln;
)
// =====================================================================
// BREAKCORE 200+ (kick hyperdense + breakbeat + snare metallic)
// =====================================================================
(
~bpm = 200; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,1,0,1, 1,0,1,1, 0,1,1,1, 1,1,0,1]; ~kickAmp = 0.55;
~kickFreq=80;~kickPitchAmt=4.0;~kickDecay=0.10;~kickDrive=2.5;~kickClick=0.6;
~hatSteps = [1,1,1,1, 1,0,1,1, 1,1,0,1, 1,1,1,1]; ~hatAmp = 0.12;
~snareSteps = [0,1,0,1, 0,0,1,1, 1,0,0,1, 0,1,1,0]; ~snareAmp = 0.55;
~snareFreq=290;~snareNoiseAmt=0.85;~snareDecay=0.06;
~clapSteps = [0,0,1,0, 0,1,0,0, 0,0,1,0, 0,1,0,0]; ~clapAmp = 0.30;
~percSteps = (0!16);
~reload.value; "=== BREAKCORE 200 BPM ===".postln;
)
// =====================================================================
// FOOTWORK 160 (juke triplet kick + clap stutter)
// =====================================================================
(
~bpm = 160; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,1, 1,0,0,0, 1,0,1,1, 0,0,1,0]; ~kickAmp = 0.55;
~kickFreq=42;~kickPitchAmt=2.4;~kickDecay=0.50;~kickDrive=1.3;~kickClick=0.10;
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; ~hatAmp = 0.10;
~snareSteps = (0!16);
~clapSteps = [0,0,0,1, 1,0,0,0, 0,0,0,1, 1,0,0,0]; ~clapAmp = 0.32;
~percInst = \rim; ~percSteps = [0,0,1,1, 0,1,1,0, 1,0,1,1, 0,1,0,1]; ~percAmp = 0.22;
~reload.value; "=== FOOTWORK 160 BPM ===".postln;
)
// =====================================================================
// IDM (intelligent dance music, variable, glitchy random)
// =====================================================================
(
~bpm = 130; TempoClock.default.tempo = ~bpm/60;
~kickSteps = ~genKickPattern.(0.35);
~snareSteps = ~genKickPattern.(0.25);
~hatSteps = ~genKickPattern.(0.65);
~clapSteps = ~genKickPattern.(0.18);
~percSteps = ~genKickPattern.(0.40);
~kickFreq=55;~kickPitchAmt=3.2;~kickDecay=0.22;~kickDrive=1.8;~kickClick=0.28;
~kickAmp = 0.5; ~hatAmp = 0.12; ~snareAmp = 0.45; ~clapAmp = 0.28; ~percAmp = 0.30;
~reload.value; "=== IDM (random) ===".postln;
)
// =====================================================================
// GLITCH HOP 110 (half-time hip-hop + glitch perc)
// =====================================================================
(
~bpm = 110; TempoClock.default.tempo = ~bpm/60;
~kickSteps = [1,0,0,0, 0,0,0,1, 0,0,1,0, 0,1,0,0]; ~kickAmp = 0.55;
~kickFreq=45;~kickPitchAmt=2.5;~kickDecay=0.45;~kickDrive=1.3;~kickClick=0.15;
~hatSteps = [1,0,1,1, 0,1,1,0, 1,0,1,1, 0,1,1,0]; ~hatAmp = 0.11;
~snareSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,1,0]; ~snareAmp = 0.50;
~snareFreq=220;~snareNoiseAmt=0.6;~snareDecay=0.12;
~clapSteps = (0!16);
~percInst = \rim; ~percSteps = ~genKickPattern.(0.5); ~percAmp = 0.25;
~reload.value; "=== GLITCH HOP 110 BPM ===".postln;
)
@@ -0,0 +1,194 @@
// =====================================================================
// percussions.scd
// ~25 patterns clap + perc + ~12 timbres perc selectionnable
//
// PREREQUIS : engine.scd evalue. Cmd+Entree sur un bloc pour l'appliquer.
//
// SynthDef percussion disponibles dans engine.scd :
// \rim, \cowbell, \tom, \openHat
// (\crash existe aussi mais hors selecteur ~percInst standard)
// SynthDef percussion bonus dans synth/_index.scd :
// \taiko, \tabla (utilisable via ~percInst = \taiko;)
//
// Storage :
// ~clC1..~clC12 = patterns clap
// ~pcP1..~pcP15 = patterns perc generale
// Util par sequences.scd pour sequencer les changements automatiques.
// =====================================================================
// =====================================================================
// STOCKAGE -- evaluer ce bloc une fois pour enregistrer toutes les
// variables reutilisables.
// =====================================================================
(
// --- Patterns clap (~clC1..~clC12) ----------------------------------
~clC1 = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; // backbeat 2 et 4
~clC2 = [0,0,0,0, 0,0,0,0, 0,0,0,0, 1,0,0,0]; // techno minimal : 4 seul
~clC3 = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; // offbeat (entre beats)
~clC4 = [1,1,1,0, 0,0,0,0, 1,1,1,0, 0,0,0,0]; // roll/flam (3 claps)
~clC5 = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,0,1]; // house ghost
~clC6 = [0,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0]; // dubstep gros (sur 3)
~clC7 = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; // trance fort 2/4
~clC8 = [1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0]; // reggae 1 et 3
~clC9 = [0,0,0,0, 1,0,0,0, 0,0,0,1, 1,0,0,0]; // garage shuffled
~clC10 = [0,0,1,0, 1,0,0,1, 0,0,1,0, 1,0,0,1]; // dense funk
~clC11 = [0,0,0,1, 1,0,0,0, 0,0,0,1, 1,0,0,0]; // stutter trap
~clC12 = (0 ! 16); // mute
// --- Patterns perc generale (~pcP1..~pcP15) -------------------------
~pcP1 = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; // offbeat techno
~pcP2 = [1,0,1,1, 0,0,1,0, 1,1,0,1, 0,1,1,0]; // trap ghosts
~pcP3 = [1,0,0,1, 0,0,1,0, 1,0,0,0, 0,1,0,0]; // tribal congas
~pcP4 = [1,0,0,1, 0,0,1,0, 0,0,1,0, 0,0,0,0]; // clave 3-2
~pcP5 = [0,0,1,0, 0,0,1,0, 1,0,0,1, 0,0,1,0]; // clave 2-3
~pcP6 = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0]; // tambourine 8th
~pcP7 = [1,0,0,0, 0,1,0,0, 1,0,0,1, 0,1,0,0]; // cowbell tribal
~pcP8 = [0,0,1,0, 0,1,0,0, 0,0,1,0, 0,1,0,0]; // rim shot offbeat
~pcP9 = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; // open hat techno
~pcP10 = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; // closed hat 16th
~pcP11 = [1,1,0,1, 1,0,1,1, 0,1,1,1, 1,1,0,1]; // shaker rapide
~pcP12 = [1,0,1,0, 0,1,0,1, 1,0,0,1, 0,1,0,1]; // bongo polyrythme
~pcP13 = [0,0,0,0, 0,0,0,0, 0,0,0,0, 1,1,1,1]; // tom roll fill
~pcP14 = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; // perc sur le beat
~pcP15 = (0 ! 16); // mute
"=== percussions : 12 claps + 15 perc patterns stockes ===".postln;
)
// =====================================================================
// CLAP PATTERNS (blocs auto-applicants)
// =====================================================================
// --- C1 : clap backbeat (5+13) ---- classique ------------------------
( ~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.35;
~reload.value; "C1 clap backbeat 2/4".postln; )
// --- C2 : clap techno minimal (sur 4 seul) --------------------------
( ~clapSteps = [0,0,0,0, 0,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.4;
~reload.value; "C2 clap minimal sur 4".postln; )
// --- C3 : clap offbeat (entre les beats) ----------------------------
( ~clapSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~clapAmp = 0.3;
~reload.value; "C3 clap offbeat".postln; )
// --- C4 : clap roll/flam (3 claps rapides) --------------------------
( ~clapSteps = [1,1,1,0, 0,0,0,0, 1,1,1,0, 0,0,0,0]; ~clapAmp = 0.32;
~reload.value; "C4 clap roll/flam".postln; )
// --- C5 : clap house ghost (2/4 + ghost) ----------------------------
( ~clapSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,0,1]; ~clapAmp = 0.34;
~reload.value; "C5 clap house".postln; )
// --- C6 : clap dubstep (sur 3, gros) --------------------------------
( ~clapSteps = [0,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0]; ~clapAmp = 0.5;
~reload.value; "C6 clap dubstep".postln; )
// --- C7 : clap trance (fort 2 et 4) ---------------------------------
( ~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.42;
~reload.value; "C7 clap trance".postln; )
// --- C8 : clap reggae (sur 1 et 3) ----------------------------------
( ~clapSteps = [1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0]; ~clapAmp = 0.30;
~reload.value; "C8 clap reggae".postln; )
// --- Clap mute ------------------------------------------------------
( ~clapSteps = (0 ! 16); ~reload.value; "clap mute".postln; )
// =====================================================================
// SELECTEUR D'INSTRUMENT PERC (~percInst)
// SynthDef disponibles : \rim \cowbell \tom \openHat
// Bonus si authentic.scd charge : \taiko, \tabla
// =====================================================================
( ~percInst = \rim; ~reload.value; "perc = rim".postln; )
( ~percInst = \cowbell; ~reload.value; "perc = cowbell".postln; )
( ~percInst = \tom; ~reload.value; "perc = tom".postln; )
( ~percInst = \openHat; ~reload.value; "perc = openHat".postln; )
// --- Bonus (si synth/_index.scd evalue) ------------------------------
( ~percInst = \taiko; ~reload.value; "perc = taiko (authentic)".postln; )
( ~percInst = \tabla; ~reload.value; "perc = tabla (authentic)".postln; )
// =====================================================================
// PATTERNS PERCUSSION GENERALE (utilise ~percInst courant)
// =====================================================================
// --- P1 : offbeat techno (defaut) -----------------------------------
( ~percSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~percAmp = 0.25;
~reload.value; "P1 perc offbeat".postln; )
// --- P2 : trap ghosts ----------------------------------------------
( ~percSteps = [1,0,1,1, 0,0,1,0, 1,1,0,1, 0,1,1,0]; ~percAmp = 0.18;
~reload.value; "P2 perc trap ghosts".postln; )
// --- P3 : tribal congas (utiliser ~percInst = \tom) ----------------
( ~percInst = \tom; ~tomFreq = 90;
~percSteps = [1,0,0,1, 0,0,1,0, 1,0,0,0, 0,1,0,0]; ~percAmp = 0.4;
~reload.value; "P3 tribal congas (tom)".postln; )
// --- P4 : clave 3-2 (afro-cubain classique) ------------------------
( ~percInst = \rim;
~percSteps = [1,0,0,1, 0,0,1,0, 0,0,1,0, 0,0,0,0]; ~percAmp = 0.35;
~reload.value; "P4 clave 3-2 (rim)".postln; )
// --- P5 : clave 2-3 (variation) ------------------------------------
( ~percInst = \rim;
~percSteps = [0,0,1,0, 0,0,1,0, 1,0,0,1, 0,0,1,0]; ~percAmp = 0.35;
~reload.value; "P5 clave 2-3 (rim)".postln; )
// --- P6 : tambourine sur 8th notes ---------------------------------
( ~percInst = \rim;
~percSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0]; ~percAmp = 0.22;
~reload.value; "P6 tambourine 8th".postln; )
// --- P7 : cowbell tribal -------------------------------------------
( ~percInst = \cowbell; ~cowbellFreq = 800;
~percSteps = [1,0,0,0, 0,1,0,0, 1,0,0,1, 0,1,0,0]; ~percAmp = 0.32;
~reload.value; "P7 cowbell tribal".postln; )
// --- P8 : rim shot offbeat -----------------------------------------
( ~percInst = \rim;
~percSteps = [0,0,1,0, 0,1,0,0, 0,0,1,0, 0,1,0,0]; ~percAmp = 0.30;
~reload.value; "P8 rim offbeat".postln; )
// --- P9 : open hat sur offbeat (techno) ----------------------------
( ~percInst = \openHat;
~percSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~percAmp = 0.25;
~reload.value; "P9 openHat offbeat".postln; )
// --- P10 : closed hat 16th (perc layer) ----------------------------
( ~percInst = \rim;
~percSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; ~percAmp = 0.12;
~reload.value; "P10 hat 16th (rim layer)".postln; )
// --- P11 : shaker rapide -------------------------------------------
( ~percInst = \rim;
~percSteps = [1,1,0,1, 1,0,1,1, 0,1,1,1, 1,1,0,1]; ~percAmp = 0.15;
~reload.value; "P11 shaker rapide (rim)".postln; )
// --- P12 : bongo polyrythme (3:4 contre kick) ----------------------
( ~percInst = \tom; ~tomFreq = 110;
~percSteps = [1,0,1,0, 0,1,0,1, 1,0,0,1, 0,1,0,1]; ~percAmp = 0.32;
~reload.value; "P12 bongo polyrythme (tom)".postln; )
// --- P13 : tom roll fill (4 dernieres steps) -----------------------
( ~percInst = \tom; ~tomFreq = 100;
~percSteps = [0,0,0,0, 0,0,0,0, 0,0,0,0, 1,1,1,1]; ~percAmp = 0.45;
~reload.value; "P13 tom roll fill".postln; )
// --- P14 : perc sur le beat (renforce kick) ------------------------
( ~percInst = \cowbell; ~cowbellFreq = 600;
~percSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~percAmp = 0.20;
~reload.value; "P14 perc on beat".postln; )
// --- Perc mute -----------------------------------------------------
( ~percSteps = (0 ! 16); ~reload.value; "perc mute".postln; )
@@ -0,0 +1,578 @@
// =====================================================================
// sequences.scd -- Sequences de patterns automatiques (macros temps)
//
// CONCEPT
// Une sequence est une Routine qui change ~kickSteps / ~melodyNotes /
// etc. a intervalles reguliers. 1 step = ~stepDur (defaut 0.25 = 1/16).
// Donc 16 steps = 1 mesure 4/4 a 0.25 dur.
//
// PREREQUIS
// engine.scd evalue (definit ~kickSteps, ~reload, etc.).
// palette/kicks.scd evalue le bloc STOCKAGE pour avoir ~kK1..~kK25.
// palette/percussions.scd evalue le bloc STOCKAGE pour ~clC*, ~pcP*.
//
// HELPERS exposes
// ~seqPlay.(actions) joue [[steps, action], ...]
// ~seqStop.() stoppe la sequence courante
// ~seqLoop.(actions, repeats) enchaine actions en boucle N fois
// ~seqEvolve.(steps, mutator) mute progressivement toutes N steps
// ~beatChainPlay.(presets) enchaine plusieurs presets nommes
// ~stepDur duree d'un step (defaut 0.25)
//
// SEQUENCES PRE-CONSTRUITES (~seqXxx.())
// seqIntro seqBuildup seqBreakdown seqDrop
// seqOutro seqTechnoLoop seqAcidEvolve seqDubstepDrop
// seqTrance16 seqDnBSwitch seqHipHopLoop seqWorldEvolve
// =====================================================================
// =====================================================================
// HELPERS DE BASE -- evaluer ce bloc une fois pour exposer les helpers
// =====================================================================
(
~stepDur = 0.25; // duree d'un step en battement (1/16 a 4/4)
~currentSeq = nil; // Routine en cours
// --- ~seqPlay.([[steps, action], ...]) ------------------------------
// Joue une suite d'actions a intervalles donnes en steps.
// Stoppe toute sequence precedente avant de demarrer.
~seqPlay = { |actions|
~seqStop.value;
~currentSeq = Routine({
actions.do { |entry|
var steps = entry[0];
var action = entry[1];
action.value;
(steps * ~stepDur).wait;
};
~currentSeq = nil;
}).play(TempoClock.default);
"=== seqPlay : % steps en cours ===".format(actions.collect(_[0]).sum).postln;
};
// --- ~seqStop.() ----------------------------------------------------
~seqStop = {
~currentSeq !? { ~currentSeq.stop; ~currentSeq = nil; "=== seqStop ===".postln };
};
// --- ~seqLoop.(actions, repeats) ------------------------------------
// Enchaine la liste d'actions en boucle N fois (defaut 1).
~seqLoop = { |actions, repeats = 1|
var expanded = Array.new(actions.size * repeats);
repeats.do { actions.do { |a| expanded.add(a) } };
~seqPlay.value(expanded);
"=== seqLoop : % cycles ===".format(repeats).postln;
};
// --- ~seqEvolve.(steps, mutator) ------------------------------------
// Appelle 'mutator' en boucle toutes les 'steps' steps. La routine
// tourne jusqu'a seqStop.
~seqEvolve = { |stepsBetween = 16, mutator|
~seqStop.value;
~currentSeq = Routine({
inf.do { |i|
mutator.value(i);
(stepsBetween * ~stepDur).wait;
};
}).play(TempoClock.default);
"=== seqEvolve : mutator chaque % steps ===".format(stepsBetween).postln;
};
// --- ~beatChainPlay.(presets) ---------------------------------------
// Enchaine plusieurs presets sous forme de [stepsToHold, action].
// Equivalent a ~seqPlay mais sucre syntaxique pour chains de drumkits.
~beatChainPlay = { |presets|
~seqPlay.value(presets);
};
"=== sequences helpers : ~seqPlay ~seqStop ~seqLoop ~seqEvolve ~beatChainPlay ===".postln;
)
// =====================================================================
// SEQUENCES PRE-CONSTRUITES
// Evaluer chaque bloc pour DEFINIR la sequence (variable ~seqXxx).
// Ensuite l'invoquer : ~seqIntro.value
// =====================================================================
// =====================================================================
// ~seqIntro -- monte progressive sur 64 steps (4 mesures)
// mesure 1 : kick seul
// mesure 2 : kick + hat
// mesure 3 : kick + hat + clap
// mesure 4 : tout (kick + hat + clap + perc)
// =====================================================================
(
~seqIntro = {
~seqPlay.value([
[16, {
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0];
~hatSteps = (0!16);
~clapSteps = (0!16);
~percSteps = (0!16);
~reload.value;
"intro 1/4 : kick seul".postln;
}],
[16, {
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0];
~reload.value;
"intro 2/4 : + hat".postln;
}],
[16, {
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0];
~clapAmp = 0.35;
~reload.value;
"intro 3/4 : + clap".postln;
}],
[16, {
~percSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0];
~percAmp = 0.25;
~reload.value;
"intro 4/4 : tout".postln;
}],
]);
};
"=== ~seqIntro defini ===".postln;
)
// =====================================================================
// ~seqBuildup -- crescendo trance (riser + cutoff sweep + clap denser)
// Argument optionnel : duree totale en mesures (defaut 4)
// =====================================================================
(
~seqBuildup = { |bars = 4|
var stepsPerBar = 16;
~seqPlay.value([
[stepsPerBar, {
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0];
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0];
~clapAmp = 0.30;
~reload.value;
"buildup 1: hat + clap".postln;
}],
[stepsPerBar, {
~hatSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0];
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,1,0, 1,0,0,0];
~clapAmp = 0.36;
~reload.value;
"buildup 2: hat 8th + clap dense".postln;
}],
[stepsPerBar, {
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1];
~clapSteps = [0,0,1,0, 1,0,1,0, 0,1,0,1, 1,0,1,0];
~clapAmp = 0.40;
~reload.value;
"buildup 3: hat 16th + clap dense".postln;
}],
[stepsPerBar * (bars - 3).max(1), {
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1];
~snareSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1];
~snareAmp = 0.5;
~reload.value;
"buildup 4: snare roll TENSION".postln;
}],
]);
};
"=== ~seqBuildup defini ===".postln;
)
// =====================================================================
// ~seqBreakdown -- reduit a melody seule, puis revient
// =====================================================================
(
~seqBreakdown = {
var saveKick, saveHat, saveClap, savePerc, saveSnare;
saveKick = ~kickSteps.copy;
saveHat = ~hatSteps.copy;
saveClap = ~clapSteps.copy;
savePerc = ~percSteps.copy;
saveSnare = ~snareSteps.copy;
~seqPlay.value([
[32, {
~kickSteps = (0!16);
~hatSteps = (0!16);
~clapSteps = (0!16);
~percSteps = (0!16);
~snareSteps = (0!16);
~reload.value;
"breakdown : melody seule".postln;
}],
[16, {
~kickSteps = saveKick;
~hatSteps = saveHat;
~clapSteps = saveClap;
~percSteps = savePerc;
~snareSteps = saveSnare;
~reload.value;
"breakdown : retour".postln;
}],
]);
};
"=== ~seqBreakdown defini ===".postln;
)
// =====================================================================
// ~seqDrop -- 4 steps silence + 1 step impact + reprise
// =====================================================================
(
~seqDrop = {
var saveKick, saveHat, saveClap, savePerc, saveSnare;
saveKick = ~kickSteps.copy;
saveHat = ~hatSteps.copy;
saveClap = ~clapSteps.copy;
savePerc = ~percSteps.copy;
saveSnare = ~snareSteps.copy;
~seqPlay.value([
[4, {
~kickSteps = (0!16);
~hatSteps = (0!16);
~clapSteps = (0!16);
~percSteps = (0!16);
~snareSteps = (0!16);
~reload.value;
"drop : SILENCE".postln;
}],
[1, {
// impact one-shot via clap fort
~clapSteps = [1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
~clapAmp = 0.7;
~reload.value;
"drop : IMPACT".postln;
}],
[16, {
~kickSteps = saveKick;
~hatSteps = saveHat;
~clapSteps = saveClap; ~clapAmp = 0.35;
~percSteps = savePerc;
~snareSteps = saveSnare;
~reload.value;
"drop : DROP !!!".postln;
}],
]);
};
"=== ~seqDrop defini ===".postln;
)
// =====================================================================
// ~seqOutro -- fade-out progressif sur 32 steps (2 mesures)
// =====================================================================
(
~seqOutro = {
~seqPlay.value([
[8, {
~percSteps = (0!16);
~reload.value;
"outro 1 : perc out".postln;
}],
[8, {
~clapSteps = (0!16);
~reload.value;
"outro 2 : clap out".postln;
}],
[8, {
~hatSteps = (0!16);
~snareSteps = (0!16);
~reload.value;
"outro 3 : hat + snare out".postln;
}],
[8, {
~kickSteps = [1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
~reload.value;
"outro 4 : kick minimal".postln;
}],
]);
};
"=== ~seqOutro defini ===".postln;
)
// =====================================================================
// ~seqTechnoLoop.(repeats) -- enchaine 4 patterns techno mute par mesure
// Necessite kicks.scd STOCKAGE evalue (~kK1..~kK25 dispo)
// =====================================================================
(
~seqTechnoLoop = { |repeats = 4|
~seqLoop.value([
[16, {
~kickSteps = ~kK1 ? [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0];
~reload.value; "techno A : 4/4".postln;
}],
[16, {
~kickSteps = ~kK9 ? [1,0,0,0, 1,0,0,1, 1,0,0,0, 1,0,1,0];
~reload.value; "techno B : ghost".postln;
}],
[16, {
~kickSteps = ~kK16 ? [1,0,0,0, 1,0,0,0, 1,0,0,1, 1,0,0,0];
~reload.value; "techno C : minimal".postln;
}],
[16, {
~kickSteps = ~kK10 ? [1,0,1,0, 1,0,0,1, 1,0,1,0, 1,0,1,1];
~reload.value; "techno D : syncope".postln;
}],
], repeats);
};
"=== ~seqTechnoLoop defini ===".postln;
)
// =====================================================================
// ~seqAcidEvolve -- acid pattern qui mute progressivement
// Note: lit ~acidNotes / ~acidAccents / ~acidSlides du moteur
// =====================================================================
(
~seqAcidEvolve = {
~seqPlay.value([
[16, {
~acidNotes = [36,36,36,36, 36,36,36,36, 36,36,36,36, 36,36,36,36];
~acidAccents = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0];
~acidSlides = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
~reload.value; "acid 1: drone tonic".postln;
}],
[16, {
~acidNotes = [36,0,39,0, 36,0,43,0, 36,0,39,0, 36,0,43,0];
~acidAccents = [1,0,0,0, 1,0,1,0, 1,0,0,0, 1,0,1,0];
~reload.value; "acid 2: minor 3rd".postln;
}],
[16, {
~acidNotes = [36,39,43,36, 39,43,48,43, 36,39,43,48, 43,39,36,43];
~acidAccents = [1,0,0,1, 0,1,0,0, 1,0,0,1, 0,0,1,0];
~acidSlides = [0,1,0,0, 1,0,1,0, 0,0,1,0, 1,1,0,0];
~reload.value; "acid 3: arpege".postln;
}],
[16, {
~acidNotes = [36,43,39,48, 36,46,43,48, 39,43,48,46, 43,39,48,36];
~acidAccents = [1,0,1,1, 0,1,0,1, 1,0,1,0, 0,1,1,0];
~acidSlides = [1,1,0,1, 0,0,1,1, 0,1,0,1, 1,0,1,0];
~reload.value; "acid 4: full chaos".postln;
}],
]);
};
"=== ~seqAcidEvolve defini ===".postln;
)
// =====================================================================
// ~seqDubstepDrop -- pre-drop / drop / breakdown
// =====================================================================
(
~seqDubstepDrop = {
~seqPlay.value([
[16, {
~kickSteps = [1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0];
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0];
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1];
~hatAmp = 0.06;
~kickAmp = 0.4; ~snareAmp = 0.35;
~reload.value; "dubstep 1: pre-drop tension".postln;
}],
[4, {
~kickSteps = (0!16); ~snareSteps = (0!16); ~hatSteps = (0!16);
~reload.value; "dubstep 2: SILENCE".postln;
}],
[32, {
~kickSteps = [1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0];
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0];
~hatSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0];
~kickAmp = 0.65; ~snareAmp = 0.55; ~hatAmp = 0.10;
~reload.value; "dubstep 3: DROP !!!".postln;
}],
[16, {
~kickSteps = (0!16); ~hatSteps = (0!16);
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0];
~snareAmp = 0.4;
~reload.value; "dubstep 4: breakdown".postln;
}],
]);
};
"=== ~seqDubstepDrop defini ===".postln;
)
// =====================================================================
// ~seqTrance16 -- 16 mesures de trance avec build implicite
// =====================================================================
(
~seqTrance16 = {
~seqPlay.value([
[64, {
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0];
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0];
~clapSteps = (0!16);
~reload.value; "trance 1-4 : kick + offbeat hat".postln;
}],
[64, {
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0];
~clapAmp = 0.35;
~reload.value; "trance 5-8 : + clap 2/4".postln;
}],
[64, {
~hatSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0];
~percSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0];
~percAmp = 0.20;
~reload.value; "trance 9-12 : + hat 8th + perc".postln;
}],
[64, {
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1];
~snareSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1];
~snareAmp = 0.45;
~reload.value; "trance 13-16 : SNARE ROLL final".postln;
}],
]);
};
"=== ~seqTrance16 defini ===".postln;
)
// =====================================================================
// ~seqDnBSwitch -- alterne 4 patterns DnB toutes les 32 steps
// =====================================================================
(
~seqDnBSwitch = { |repeats = 2|
~seqLoop.value([
[32, {
~kickSteps = [1,0,0,0, 0,0,1,0, 0,0,1,0, 1,0,0,0];
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,1];
~hatSteps = [1,1,1,1, 0,1,1,1, 1,1,1,1, 0,1,1,1];
~kickAmp = 0.55; ~snareAmp = 0.5; ~hatAmp = 0.12;
~reload.value; "DnB A: amen".postln;
}],
[32, {
~kickSteps = [1,0,0,1, 0,0,1,0, 0,0,1,0, 1,0,0,0];
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,1, 1,0,1,1];
~hatSteps = [1,1,0,1, 1,0,1,1, 1,1,0,1, 1,0,1,1];
~reload.value; "DnB B: jungle".postln;
}],
[32, {
~kickSteps = [1,0,0,0, 0,1,1,0, 0,0,1,0, 0,1,0,0];
~snareSteps = [0,0,0,0, 1,0,0,1, 0,0,0,0, 1,0,1,0];
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1];
~reload.value; "DnB C: jump-up".postln;
}],
[32, {
~kickSteps = [1,0,0,0, 0,0,1,0, 0,0,1,0, 1,0,0,0];
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0];
~hatSteps = [1,0,1,1, 0,1,0,1, 1,0,1,1, 0,1,0,1];
~reload.value; "DnB D: liquid".postln;
}],
], repeats);
};
"=== ~seqDnBSwitch defini ===".postln;
)
// =====================================================================
// ~seqHipHopLoop -- 8 mesures de hip-hop avec petites variations
// =====================================================================
(
~seqHipHopLoop = { |repeats = 1|
~seqLoop.value([
[16, {
~kickSteps = [1,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,0];
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0];
~hatSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0];
~kickAmp = 0.5; ~snareAmp = 0.4; ~hatAmp = 0.10;
~reload.value; "hip-hop 1: classic".postln;
}],
[16, {
~kickSteps = [1,0,0,0, 0,0,1,0, 0,0,1,0, 0,0,0,0];
~hatSteps = [1,0,1,1, 1,0,1,0, 1,0,1,1, 1,0,1,0];
~reload.value; "hip-hop 2: ghost kick".postln;
}],
[16, {
~kickSteps = [1,0,0,0, 0,0,0,1, 1,0,0,0, 0,1,0,0];
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,1];
~reload.value; "hip-hop 3: trap shift".postln;
}],
[16, {
~kickSteps = [1,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,0];
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,1];
~hatSteps = [1,0,1,0, 1,0,1,1, 1,0,1,0, 1,0,1,1];
~reload.value; "hip-hop 4: ghost snare".postln;
}],
], repeats);
};
"=== ~seqHipHopLoop defini ===".postln;
)
// =====================================================================
// ~seqWorldEvolve -- ajout progressif de couches ethno world
// =====================================================================
(
~seqWorldEvolve = {
~seqPlay.value([
[16, {
~kickSteps = [1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0];
~hatSteps = (0!16);
~clapSteps = (0!16);
~percSteps = (0!16);
~snareSteps = (0!16);
~kickAmp = 0.4;
~reload.value; "world 1: kick base".postln;
}],
[16, {
~percInst = \tom; ~tomFreq = 95;
~percSteps = [1,0,0,1, 0,0,1,0, 1,0,0,0, 0,1,0,0];
~percAmp = 0.4;
~reload.value; "world 2: + congas".postln;
}],
[16, {
~hatSteps = [1,1,0,1, 1,0,1,1, 0,1,1,1, 1,0,1,1];
~hatAmp = 0.08;
~reload.value; "world 3: + shaker".postln;
}],
[16, {
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0];
~clapAmp = 0.30;
~reload.value; "world 4: + clap".postln;
}],
[16, {
~kickSteps = [1,0,0,1, 0,1,0,0, 1,0,1,0, 0,1,0,0];
~kickAmp = 0.5;
~reload.value; "world 5: kick afrobeat".postln;
}],
]);
};
"=== ~seqWorldEvolve defini ===".postln;
)
// =====================================================================
// EXEMPLES D'INVOCATION (a evaluer manuellement)
// =====================================================================
// Lance l'intro :
~seqIntro.value
// Buildup 4 mesures :
~seqBuildup.value(4)
// Drop dubstep complet :
~seqDubstepDrop.value
// Stop la sequence en cours :
// ~seqStop.value
// --- Chaine plusieurs sections via beatChainPlay --------------------
~beatChainPlay.value([
[64, { ~seqIntro.value }], // ne marche PAS dans une seq
[64, { ~kickSteps = ~kK1; ~reload.value }],
]);
// Note : ne pas imbriquer ~seqXxx.value dans une sequence. Pour chainer
// des sections complexes, utilise des Routine externes ou enchaine
// plusieurs ~seqLoop.value successifs apres un ~seqStop.value.
@@ -0,0 +1,537 @@
#!/usr/bin/env python3
"""Split palette/melodies/{short,long,xlong,epic}.scd in one file per melody,
then generate ~20x new idiomatic melodies per genre.
Convention finale (decision projet) : camelCase pour nouvelles vars
(~mAcid100, ~mlTrance100, ~mxlAmbient100, ~mepicCinematic100). Les vars
historiques ~m1..~m78 / ~ml1..~ml30 / ~mxl1..~mxl15 / ~mepic1..~mepic10
sont conservees telles quelles.
"""
from __future__ import annotations
import re
import shutil
import random
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
MEL = ROOT / "palette" / "melodies"
MIG = ROOT / "_migrate"
SOURCES = {
"short": (MEL / "short.scd", "m", 32),
"long": (MEL / "long.scd", "ml", 64),
"xlong": (MEL / "xlong.scd", "mxl", 96),
"epic": (MEL / "epic.scd", "mepic", 128),
}
# ---------------------------------------------------------------------------
# 1. PARSE EXISTING FILES
# ---------------------------------------------------------------------------
VAR_RE = re.compile(
r"~(m|ml|mxl|mepic)(\d+)\s*=\s*\[([^\]]+)\]\s*;",
re.DOTALL,
)
def parse_source(path: Path) -> dict[str, list[int]]:
text = path.read_text()
found = {}
for m in VAR_RE.finditer(text):
prefix, num, body = m.group(1), m.group(2), m.group(3)
ints = [int(x) for x in re.findall(r"-?\d+", body)]
found[f"{prefix}{num}"] = ints
return found
def write_one(path: Path, var: str, length_label: str, genre_hint: str, notes: list[int]) -> None:
# Pretty-format : 8 ints par ligne, 4 lignes pour 32, 8 lignes pour 64, etc.
chunks = [notes[i:i + 8] for i in range(0, len(notes), 8)]
body_lines = []
for ch in chunks:
body_lines.append(" " + ", ".join(f"{n:>2}" for n in ch) + ",")
if body_lines:
# supprime virgule finale du dernier element
body_lines[-1] = body_lines[-1].rstrip(",")
content = (
"// =====================================================================\n"
f"// ~{var} -- {length_label} melody ({len(notes)} steps), genre: {genre_hint}\n"
"// =====================================================================\n"
"(\n"
f"~{var} = [\n"
+ "\n".join(body_lines) + "\n"
"];\n"
")\n"
)
path.write_text(content)
# ---------------------------------------------------------------------------
# 2. GENRE SPECS
# ---------------------------------------------------------------------------
# scales (intervals from root, semitones)
SCALES = {
"minor": [0, 2, 3, 5, 7, 8, 10],
"natural_minor": [0, 2, 3, 5, 7, 8, 10],
"harmonic_minor": [0, 2, 3, 5, 7, 8, 11],
"phrygian": [0, 1, 3, 5, 7, 8, 10],
"dorian": [0, 2, 3, 5, 7, 9, 10],
"mixolydian": [0, 2, 4, 5, 7, 9, 10],
"ionian": [0, 2, 4, 5, 7, 9, 11],
"lydian": [0, 2, 4, 6, 7, 9, 11],
"locrian": [0, 1, 3, 5, 6, 8, 10],
"major": [0, 2, 4, 5, 7, 9, 11],
"minor_pent": [0, 3, 5, 7, 10],
"major_pent": [0, 2, 4, 7, 9],
"blues": [0, 3, 5, 6, 7, 10],
"hirajoshi": [0, 2, 3, 7, 8], # E,F#,G,B,C (transpose root)
"iwato": [0, 1, 5, 6, 10],
"hijaz": [0, 1, 4, 5, 7, 8, 10],
"altered": [0, 1, 3, 4, 6, 8, 10],
"diminished": [0, 2, 3, 5, 6, 8, 9, 11],
"chromatic": list(range(12)),
}
# genre -> dict(roots, scales, range, density, motif, struct_weights)
GENRES = {
"acid": dict(roots=[40, 42], scales=["phrygian", "minor"], lo=36, hi=60,
density=(0.6, 0.8), motif="acid"),
"dub": dict(roots=[40, 36], scales=["minor_pent"], lo=36, hi=60,
density=(0.3, 0.5), motif="dub"),
"trance": dict(roots=[42, 45], scales=["minor", "harmonic_minor"], lo=60, hi=84,
density=(0.7, 0.9), motif="trance"),
"detroit": dict(roots=[36, 41], scales=["dorian"], lo=48, hi=72,
density=(0.5, 0.7), motif="detroit"),
"dnb": dict(roots=[40, 43], scales=["natural_minor"], lo=36, hi=72,
density=(0.4, 0.6), motif="dnb"),
"ambient": dict(roots=[60, 65], scales=["ionian", "lydian"], lo=60, hi=84,
density=(0.2, 0.4), motif="ambient"),
"world": dict(roots=[50, 55], scales=["dorian", "mixolydian"], lo=60, hi=84,
density=(0.6, 0.8), motif="modal"),
"retro": dict(roots=[60, 67], scales=["major", "lydian"], lo=60, hi=84,
density=(0.6, 0.8), motif="diatonic"),
"dark": dict(roots=[40, 43], scales=["locrian", "harmonic_minor"], lo=36, hi=60,
density=(0.5, 0.7), motif="dark"),
"asian": dict(roots=[52, 57], scales=["hirajoshi", "iwato"], lo=52, hi=76,
density=(0.5, 0.7), motif="asian"),
"oriental": dict(roots=[50, 55], scales=["hijaz"], lo=50, hi=74,
density=(0.6, 0.8), motif="oriental"),
"tribal": dict(roots=[45, 50], scales=["minor_pent"], lo=36, hi=60,
density=(0.7, 0.9), motif="tribal"),
"industrial": dict(roots=[40, 43], scales=["chromatic", "minor"], lo=36, hi=60,
density=(0.7, 0.9), motif="industrial"),
"synthwave": dict(roots=[57, 62], scales=["minor"], lo=48, hi=84,
density=(0.6, 0.8), motif="synthwave"),
"chiptune": dict(roots=[60, 67], scales=["major", "mixolydian"], lo=60, hi=96,
density=(0.7, 0.9), motif="chiptune"),
"phonk": dict(roots=[40, 45], scales=["minor_pent"], lo=36, hi=60,
density=(0.3, 0.5), motif="phonk"),
"hardcore": dict(roots=[40, 41], scales=["locrian", "chromatic"], lo=36, hi=60,
density=(0.8, 1.0), motif="hardcore"),
"pop": dict(roots=[60, 67], scales=["major"], lo=60, hi=84,
density=(0.5, 0.7), motif="pop"),
"jazz": dict(roots=[60, 65], scales=["dorian", "mixolydian", "altered"], lo=48, hi=84,
density=(0.6, 0.8), motif="jazz"),
"blues": dict(roots=[45, 52], scales=["blues"], lo=45, hi=72,
density=(0.5, 0.7), motif="blues"),
"hypnotic": dict(roots=[60], scales=["minor_pent"], lo=60, hi=72,
density=(0.3, 0.5), motif="hypnotic"),
"pad": dict(roots=[60, 65], scales=["major", "dorian"], lo=60, hi=84,
density=(0.2, 0.4), motif="pad"),
"pluck": dict(roots=[60, 67], scales=["major_pent"], lo=60, hi=84,
density=(0.4, 0.6), motif="pluck"),
"sub": dict(roots=[24, 28], scales=["minor_pent"], lo=24, hi=48,
density=(0.3, 0.5), motif="sub"),
"drone": dict(roots=[24, 29], scales=["minor_pent"], lo=24, hi=48,
density=(0.1, 0.3), motif="drone"),
"tension": dict(roots=[40, 47], scales=["locrian", "diminished"], lo=36, hi=60,
density=(0.6, 0.8), motif="tension"),
"chromatic": dict(roots=[60], scales=["chromatic"], lo=48, hi=84,
density=(0.7, 0.9), motif="chromatic"),
"cinematic": dict(roots=[48, 53], scales=["minor", "harmonic_minor"], lo=36, hi=84,
density=(0.4, 0.7), motif="cinematic"),
}
def palette_for(root: int, scale: list[int], lo: int, hi: int) -> list[int]:
pal = []
for octv in range(-2, 4):
for d in scale:
n = root + d + 12 * octv
if lo <= n <= hi:
pal.append(n)
return sorted(set(pal))
def is_strong_beat(i: int, length: int) -> bool:
if length == 32:
return i % 4 == 0
if length == 64:
return i % 4 == 0
if length == 96:
return i % 4 == 0
return i % 4 == 0
def pick_note(rng: random.Random, pal: list[int], strong: bool, root: int) -> int:
if not pal:
return 0
if strong and rng.random() < 0.55:
# bias toward root + fifth
candidates = [n for n in pal if (n - root) % 12 in (0, 7, 3, 4)]
if candidates:
return rng.choice(candidates)
# bias to mid range : note proche de la mediane palette
return rng.choice(pal)
def apply_motif(rng: random.Random, motif: str, notes: list[int], pal: list[int], root: int) -> list[int]:
L = len(notes)
if motif == "acid":
# slides + octave jumps : add lowest+12 sometimes
for i in range(0, L, 4):
if rng.random() < 0.4 and notes[i] > 0:
if i + 1 < L and notes[i + 1] == 0:
notes[i + 1] = max(min(notes[i] + 12, max(pal)), min(pal))
if rng.random() < 0.25 and notes[i] > 0 and i + 2 < L:
# ghost slide
notes[i + 2] = notes[i] + rng.choice([-2, -1, 1, 2])
elif motif == "trance":
# arpege ascendant 4 notes consecutives
for i in range(0, L - 3, 8):
if rng.random() < 0.7:
base_idx = pal.index(rng.choice(pal[: max(1, len(pal) // 2)])) if pal else 0
for k in range(4):
if base_idx + k < len(pal):
notes[i + k] = pal[base_idx + k]
elif motif == "ambient" or motif == "pad" or motif == "drone":
# etire les notes : silence apres une note frappee
for i in range(L):
if notes[i] > 0 and rng.random() < 0.7:
for j in range(1, rng.randint(2, 5)):
if i + j < L:
notes[i + j] = 0
elif motif == "chiptune":
# arpeges rapides 1-3-5
for i in range(0, L - 2, 6):
if rng.random() < 0.5 and pal:
a = rng.choice(pal[: len(pal) // 2 or 1])
# 1-3-5 over scale (find in palette)
third = a + 4 if a + 4 in pal else (a + 3 if a + 3 in pal else a + 5)
fifth = a + 7 if a + 7 in pal else a + 5
notes[i] = a
if i + 1 < L:
notes[i + 1] = third
if i + 2 < L:
notes[i + 2] = fifth
elif motif == "dub":
# sub bass tenue + offbeat stab
sub = root
for i in range(L):
notes[i] = 0
for i in range(0, L, 8):
notes[i] = sub
if i + 4 < L and rng.random() < 0.6:
notes[i + 4] = rng.choice(pal) if pal else 0
elif motif == "tribal":
# repetitions hypnotiques : pattern court repete
if pal:
cell = [rng.choice(pal) if rng.random() < 0.7 else 0 for _ in range(8)]
for i in range(L):
notes[i] = cell[i % 8]
elif motif == "hypnotic":
# 2-3 notes repetees
if pal:
cell = [rng.choice(pal) if rng.random() < 0.5 else 0 for _ in range(4)]
for i in range(L):
if rng.random() < 0.7:
notes[i] = cell[i % 4]
elif motif == "sub":
# sub bass espace
for i in range(L):
notes[i] = 0
for i in range(0, L, 8):
if rng.random() < 0.6 and pal:
notes[i] = rng.choice([n for n in pal if n <= root + 12] or pal)
elif motif == "hardcore":
# tres dense : moins de 0
for i in range(L):
if notes[i] == 0 and rng.random() < 0.6 and pal:
notes[i] = rng.choice(pal)
elif motif == "industrial":
# patterns mecaniques (repetition stricte 4-step)
if pal:
cell = [rng.choice(pal) if rng.random() < 0.8 else 0 for _ in range(4)]
for i in range(L):
notes[i] = cell[i % 4]
elif motif == "asian" or motif == "oriental":
# ornements descendants apres note forte
for i in range(0, L - 2, 4):
if notes[i] > 0 and rng.random() < 0.4 and pal:
idx = pal.index(notes[i]) if notes[i] in pal else 0
if idx > 0 and i + 1 < L:
notes[i + 1] = pal[idx - 1]
elif motif == "phonk":
# half-time : double les silences sur off-beats
for i in range(L):
if i % 2 == 1 and rng.random() < 0.7:
notes[i] = 0
elif motif == "cinematic":
# crescendo : densifie progressivement
for i in range(L):
ratio = i / max(1, L - 1)
if notes[i] == 0 and rng.random() < ratio * 0.5 and pal:
notes[i] = rng.choice(pal)
elif motif == "jazz":
# chromatic approaches
for i in range(0, L - 1):
if notes[i] > 0 and notes[i + 1] > 0 and rng.random() < 0.2:
if abs(notes[i] - notes[i + 1]) > 2:
notes[i] = notes[i + 1] - rng.choice([-1, 1])
elif motif == "blues":
# bend implicite : note repetee
for i in range(0, L - 1):
if notes[i] > 0 and rng.random() < 0.3 and i + 1 < L:
notes[i + 1] = notes[i]
return notes
def generate(rng: random.Random, genre: str, length: int) -> list[int]:
spec = GENRES[genre]
root = rng.choice(spec["roots"])
scale_name = rng.choice(spec["scales"])
scale = SCALES[scale_name]
pal = palette_for(root, scale, spec["lo"], spec["hi"])
if not pal:
pal = [root]
dlow, dhigh = spec["density"]
density = rng.uniform(dlow, dhigh)
notes = []
for i in range(length):
if rng.random() < density:
notes.append(pick_note(rng, pal, is_strong_beat(i, length), root))
else:
notes.append(0)
notes = apply_motif(rng, spec["motif"], notes, pal, root)
# AABA / ABCA structuring pour longs formats
if length >= 64:
section_len = length // 4
if rng.random() < 0.5:
# AABA
a = notes[:section_len]
b = notes[section_len * 2:section_len * 3]
notes = a + a + b + a
# else garde la generation lineaire
if length >= 96 and rng.random() < 0.4:
# last section : variation
section_len = length // 6
last = notes[-section_len:]
last = [(n + rng.choice([-2, -1, 0, 1, 2])) if n > 0 else 0 for n in last]
notes = notes[:-section_len] + last
# clamp
out = []
for n in notes:
if n == 0:
out.append(0)
else:
out.append(max(24, min(96, n)))
# ensure exact length
if len(out) < length:
out += [0] * (length - len(out))
return out[:length]
# ---------------------------------------------------------------------------
# 3. GENRE -> SIZE FROM melodyBank
# ---------------------------------------------------------------------------
# transcrit depuis bank.scd (entries originales par genre)
ORIGINAL_BANK = {
"acid": ["m29", "m30", "m70", "ml1", "mxl5", "mepic5"],
"dub": ["m65", "ml26"],
"trance": ["m8", "m24", "m28", "ml2", "mxl1", "mepic1", "mxl15", "mepic6", "ml15"],
"detroit": ["m3", "m23", "m69", "m71", "ml3", "ml18", "mxl2", "mepic3"],
"dnb": ["m63", "ml4", "ml22"],
"ambient": ["m33", "m34", "m72", "m73", "ml5", "mxl3"],
"world": ["m17", "m48", "m74", "ml9", "ml25", "mxl11", "mepic9"],
"retro": ["m61", "m75", "m77", "ml7", "ml13", "mxl6"],
"dark": ["m14", "m38", "m53", "ml8", "ml28", "mxl8", "mepic8"],
"asian": ["m17", "m40", "m48", "m74", "ml9", "ml25", "mxl11", "mepic9"],
"oriental": ["m15", "m20", "m39", "m41", "m44", "m60", "ml6", "ml17", "mxl4", "mxl14", "mepic4"],
"tribal": ["m45", "m46", "ml10", "mxl12"],
"industrial": ["m67", "m68", "ml11", "ml24", "mxl9", "mepic7"],
"synthwave": ["m61", "ml7", "mxl6"],
"chiptune": ["m62", "ml20", "mxl13"],
"phonk": ["m66", "ml19"],
"hardcore": ["m67", "ml24", "mxl9", "mepic7"],
"pop": ["m35", "m36", "m49", "m50", "ml14", "mxl10"],
"jazz": ["m51", "m52", "ml16", "ml27"],
"blues": ["m16", "m47"],
"hypnotic": ["m2", "m11", "m27", "m54", "m55", "m56", "ml12", "mxl7"],
"pad": ["m33", "m34", "ml5", "mxl3"],
"pluck": ["m32"],
"sub": ["m5", "m31", "m37", "m63", "ml4"],
"drone": ["m37", "m38"],
"tension": ["m53", "m59", "ml28", "m58", "ml30"],
"chromatic": ["m57", "ml29"],
"cinematic": ["m73", "mxl3", "mepic2", "mepic10"],
}
def length_split(n: int) -> tuple[int, int, int, int]:
"""70% short, 20% long, 8% xlong, 2% epic; ensure at least 1 each."""
s = max(1, round(n * 0.70))
l = max(1, round(n * 0.20))
x = max(1, round(n * 0.08))
e = max(1, round(n * 0.02))
return s, l, x, e
# ---------------------------------------------------------------------------
# 4. MAIN
# ---------------------------------------------------------------------------
def main() -> None:
rng = random.Random(42)
# 4.1 Backups (skip if exists)
for name, (path, _, _) in SOURCES.items():
bak = MIG / f"palette_melodies_{name}.scd.bak"
if not bak.exists() and path.exists():
shutil.copy2(path, bak)
print(f"backup {bak.name}")
# 4.2 Parse + create dirs + write per-melody files
parsed: dict[str, dict[str, list[int]]] = {}
for name, (path, prefix, length) in SOURCES.items():
sub = MEL / name
sub.mkdir(exist_ok=True)
parsed[name] = parse_source(path)
# write per-var files
for var, notes in parsed[name].items():
# detect genre hint via reverse lookup in ORIGINAL_BANK
hint = "various"
for g, vlist in ORIGINAL_BANK.items():
if var in vlist:
hint = g
break
out = sub / f"{var}.scd"
write_one(out, var, name, hint, notes)
print(f"{name}: {len(parsed[name])} fichiers individuels ecrits dans {sub}")
# 4.3 Generate new melodies, count per genre per length-bucket
new_bank: dict[str, list[str]] = {g: [] for g in ORIGINAL_BANK}
stats = {}
for genre, originals in ORIGINAL_BANK.items():
target = max(1, len(originals)) * 20
s, l, x, e = length_split(target)
stats[genre] = dict(orig=len(originals), target=target, s=s, l=l, x=x, e=e)
# camelCase: m + Genre + N (capitalize first letter only)
g_cap = genre[0].upper() + genre[1:]
# numbering starts at 100 for safety
for i, length in [(s, 32), (l, 64), (x, 96), (e, 128)]:
pass # placeholder
configs = [
("short", "m", 32, s),
("long", "ml", 64, l),
("xlong", "mxl", 96, x),
("epic", "mepic", 128, e),
]
for sub_name, prefix, length, count in configs:
sub = MEL / sub_name
for n in range(count):
var = f"{prefix}{g_cap}{100 + n}"
notes = generate(rng, genre, length)
out = sub / f"{var}.scd"
write_one(out, var, sub_name, genre, notes)
new_bank[genre].append(var)
# 4.4 stats summary
total_orig = sum(len(v) for v in parsed.values())
total_new = sum(len(v) for v in new_bank.values())
print(f"\n=== STATS ===")
for g, st in stats.items():
print(f" {g:12s}: {st['orig']:3d} originales x 20 = {st['target']:3d} nouvelles "
f"(s={st['s']} l={st['l']} x={st['x']} e={st['e']})")
print(f"\nTotal original : {total_orig}")
print(f"Total nouvelles : {total_new}")
print(f"Total fichiers : {total_orig + total_new}")
# 4.5 update bank.scd : append new vars to each genre
bank_path = MEL / "bank.scd"
bank_text = bank_path.read_text()
# remplace la table ~melodyBank en ajoutant les nouvelles entries
new_block_lines = ["~melodyBank = ("]
items = list(ORIGINAL_BANK.items())
for i, (g, originals) in enumerate(items):
all_keys = originals + new_bank[g]
keys_fmt = ", ".join(f"\\{k}" for k in all_keys)
suffix = "," if i < len(items) - 1 else ""
new_block_lines.append(f" \\{g}: [{keys_fmt}]{suffix}")
new_block_lines.append(");")
new_block = "\n".join(new_block_lines)
# split par ligne et trouve le bloc ~melodyBank = ( ... );
# on cherche la ligne qui commence par ~melodyBank et on remplace
# jusqu'a la ligne qui contient "); " seul (fin du dict)
lines = bank_text.split("\n")
start_idx = None
end_idx = None
for i, ln in enumerate(lines):
if start_idx is None and ln.strip().startswith("~melodyBank"):
start_idx = i
elif start_idx is not None and ln.strip() == ");":
end_idx = i
break
if start_idx is None or end_idx is None:
raise RuntimeError("bloc ~melodyBank introuvable dans bank.scd")
bank_text_new = "\n".join(
lines[:start_idx] + [new_block] + lines[end_idx + 1:]
)
bank_path.write_text(bank_text_new)
print(f"\nbank.scd mis a jour ({len(new_block)} chars dans le bloc melodyBank)")
# 4.6 update index.scd : recursive load via PathName
index_path = MEL / "index.scd"
index_content = (
"// =====================================================================\n"
"// palette/melodies/index.scd -- loader recursif (post-split v2)\n"
"//\n"
"// Charge :\n"
"// helpers.scd (helpers melodiques)\n"
"// short/<*.scd> ~m1..~m78 + ~m{Genre}100..\n"
"// long/<*.scd> ~ml1..~ml30 + ~ml{Genre}100..\n"
"// xlong/<*.scd> ~mxl1..~mxl15 + ~mxl{Genre}100..\n"
"// epic/<*.scd> ~mepic1..~mepic10 + ~mepic{Genre}100..\n"
"// bank.scd ~melodyBank + selecteurs\n"
"// tracks.scd systeme multi-pistes\n"
"//\n"
"// Un seul bloc top-level (compatible .load).\n"
"// =====================================================================\n"
"(\n"
"var dir = thisProcess.nowExecutingPath.dirname;\n"
"(dir +/+ \"helpers.scd\").load;\n"
"[\"short\", \"long\", \"xlong\", \"epic\"].do { |sub|\n"
" PathName(dir +/+ sub).files.do { |f|\n"
" if (f.extension == \"scd\") { f.fullPath.load };\n"
" };\n"
"};\n"
"(dir +/+ \"bank.scd\").load;\n"
"(dir +/+ \"tracks.scd\").load;\n"
"\"=== melodies module loaded (recursive) ===\".postln;\n"
")\n"
)
index_path.write_text(index_content)
print("index.scd mis a jour (loader recursif)")
# 4.7 supprime les 4 sources monolithiques (backups deja faits)
for name, (path, _, _) in SOURCES.items():
if path.exists():
path.unlink()
print(f"supprime {path.name}")
if __name__ == "__main__":
main()
+461
View File
@@ -0,0 +1,461 @@
#!/usr/bin/env python3
"""Split monolithic palette/melodies/*.scd and palette/rhythm/*.scd files
into one-file-per-unit structure.
Strategy A (melodies short/long/xlong/epic, kicks STOCKAGE bloc):
Each top-level `~varname = ...;` (statement) becomes its own file.
Strategy B (rhythm drum_kits, patterns_genre, percussions, sequences, fills):
Each top-level `(...)` block becomes its own file. Filename derived from
the closest preceding `// === NAME ===` header comment.
"""
from __future__ import annotations
import os
import re
import shutil
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
PAL = ROOT / "palette"
# ---------------------------------------------------------------------------
# Tokenizer-aware bracket scanner. Tracks (), [], strings, line/block comments.
# Returns the list of top-level statements/blocks with character positions.
# ---------------------------------------------------------------------------
def scan_top_level(src: str):
"""Yield (start, end, kind) for each top-level item.
kind = 'block' if the item is a `(...)` paren block at depth 0
'stmt' if it's a top-level statement (ends at `;` at depth 0)
Whitespace and comments between items are skipped (start points at first
non-ws char of the item).
"""
i = 0
n = len(src)
items = []
def skip_ws_and_comments(j):
while j < n:
c = src[j]
if c in " \t\r\n":
j += 1
elif src.startswith("//", j):
# line comment
k = src.find("\n", j)
j = n if k == -1 else k + 1
elif src.startswith("/*", j):
k = src.find("*/", j + 2)
j = n if k == -1 else k + 2
else:
return j
return j
while i < n:
i = skip_ws_and_comments(i)
if i >= n:
break
start = i
c = src[i]
if c == "(":
# consume balanced paren block
depth = 0
in_str = False
in_sym = False
j = i
while j < n:
ch = src[j]
if in_str:
if ch == "\\":
j += 2
continue
if ch == '"':
in_str = False
j += 1
continue
if in_sym:
# symbol literal: 'foo bar'
if ch == "\\":
j += 2
continue
if ch == "'":
in_sym = False
j += 1
continue
if ch == '"':
in_str = True
j += 1
continue
if ch == "'":
in_sym = True
j += 1
continue
if src.startswith("//", j):
k = src.find("\n", j)
j = n if k == -1 else k + 1
continue
if src.startswith("/*", j):
k = src.find("*/", j + 2)
j = n if k == -1 else k + 2
continue
if ch == "(":
depth += 1
elif ch == ")":
depth -= 1
if depth == 0:
j += 1
# consume optional trailing `;`
k = j
while k < n and src[k] in " \t":
k += 1
if k < n and src[k] == ";":
j = k + 1
items.append((start, j, "block"))
i = j
break
j += 1
else:
raise ValueError(f"unbalanced ( at offset {start}")
else:
# top-level statement -> read until `;` at depth 0 of [] and ()
depth_p = 0
depth_b = 0
in_str = False
in_sym = False
j = i
while j < n:
ch = src[j]
if in_str:
if ch == "\\":
j += 2
continue
if ch == '"':
in_str = False
j += 1
continue
if in_sym:
if ch == "\\":
j += 2
continue
if ch == "'":
in_sym = False
j += 1
continue
if ch == '"':
in_str = True
j += 1
continue
if ch == "'":
in_sym = True
j += 1
continue
if src.startswith("//", j):
k = src.find("\n", j)
j = n if k == -1 else k + 1
continue
if src.startswith("/*", j):
k = src.find("*/", j + 2)
j = n if k == -1 else k + 2
continue
if ch == "(":
depth_p += 1
elif ch == ")":
depth_p -= 1
elif ch == "[":
depth_b += 1
elif ch == "]":
depth_b -= 1
elif ch == ";" and depth_p == 0 and depth_b == 0:
j += 1
items.append((start, j, "stmt"))
i = j
break
j += 1
else:
# EOF without `;` -- emit rest as stmt
items.append((start, n, "stmt"))
i = n
return items
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def slugify(s: str) -> str:
s = s.lower().strip()
s = re.sub(r"[\(\)\[\]\{\}]", " ", s)
s = re.sub(r"[^a-z0-9_\- ]+", "", s)
s = re.sub(r"\s+", "_", s)
s = re.sub(r"_+", "_", s).strip("_-")
return s or "block"
def find_block_header(src: str, block_start: int) -> str | None:
"""Look immediately before block_start for the closest header comment.
Walks backward over whitespace/blank lines, then tries to identify the
nearest comment header preceding the block. Recognized forms:
// === NAME ===
// ----- NAME -----
// =====================
// NAME
// =====================
// ---- NAME (extra) ----
"""
# Walk back, collecting at most ~12 preceding non-empty comment lines
# (skipping blank lines).
end = block_start
# find start of the line containing block_start
while end > 0 and src[end - 1] != "\n":
end -= 1
# collect lines going backward
lines = []
pos = end
while pos > 0:
# back up one line
line_end = pos - 1 # the \n
line_start = src.rfind("\n", 0, line_end)
line_start = 0 if line_start == -1 else line_start + 1
line = src[line_start:line_end]
stripped = line.strip()
if stripped == "":
# blank line: only allowed BEFORE we have any header line
if lines:
break
pos = line_start
continue
if not stripped.startswith("//"):
break
lines.append(stripped)
pos = line_start
if len(lines) >= 12:
break
if not lines:
return None
# lines is in reverse order: lines[0] is the line directly above the block
# Look at lines[0] first
def extract(comment_line: str) -> str | None:
s = comment_line.lstrip("/").strip()
# strip leading/trailing runs of = or -
s = re.sub(r"^[=\-\s]+", "", s)
s = re.sub(r"[=\-\s]+$", "", s)
# Remove any "(extra)" trailing parens content
s = re.sub(r"\([^)]*\)\s*$", "", s).strip()
if not s:
return None
# Reject if pure separators leftover
if re.fullmatch(r"[=\-\s]+", s):
return None
return s
# Form A: line 0 is `// === NAME ===` or `// ---- NAME ----`
# Some lines could be just `// =====` (separator). Detect those.
def is_separator(line: str) -> bool:
s = line.lstrip("/").strip()
return bool(re.fullmatch(r"[=\-\s]+", s))
# If lines[0] is a NAME-bearing line:
if not is_separator(lines[0]):
cand = extract(lines[0])
if cand:
return cand
# Otherwise lines[0] is a separator -> look for `// NAME` between two separators
if is_separator(lines[0]) and len(lines) >= 3 and is_separator(lines[2]):
cand = extract(lines[1])
if cand:
return cand
# Try lines[1] anyway
if len(lines) >= 2:
cand = extract(lines[1])
if cand:
return cand
return None
VAR_RE = re.compile(r"^\s*~([a-zA-Z][a-zA-Z0-9_]*)\s*=", re.MULTILINE)
def find_stmt_var(src: str, start: int, end: int) -> str | None:
m = VAR_RE.search(src, start, end)
return m.group(1) if m else None
def find_inner_var_in_block(src: str, start: int, end: int) -> str | None:
"""For kicks-style: a block contains a list of `~kK1 = ...; ~kK2 = ...;`.
But strategy B treats the whole block as one unit. Fallback for naming
when no header found.
"""
m = VAR_RE.search(src, start, end)
return m.group(1) if m else None
# ---------------------------------------------------------------------------
# Splitting drivers
# ---------------------------------------------------------------------------
def write_unit(out_path: Path, header_comment: str, body: str):
out_path.parent.mkdir(parents=True, exist_ok=True)
txt = f"// =========================================================\n// {header_comment}\n// =========================================================\n{body}\n"
out_path.write_text(txt, encoding="utf-8")
def split_strategy_A(src_path: Path, out_dir: Path, prefix_filter: str | None = None):
"""Each top-level statement `~var = ...;` -> file <var>.scd wrapped in
`(...)` block.
If the source file is a single big `(...)` block wrapping many
`~var = ...;` statements (cas: short.scd, long.scd, ...), unwrap the
outer block and split the inner statements.
"""
src = src_path.read_text(encoding="utf-8")
items = scan_top_level(src)
# Detect "single big block" pattern: only one top-level item which is a block.
# In that case, descend into the block content.
if len(items) == 1 and items[0][2] == "block":
s, e, _ = items[0]
# strip outer ( ... ) (and optional trailing ;)
text = src[s:e].rstrip()
if text.endswith(";"):
text = text[:-1].rstrip()
# text looks like "( ... )"
assert text[0] == "(" and text[-1] == ")"
inner = text[1:-1]
# parse inner as top-level statements
inner_items = scan_top_level(inner)
count = 0
for (s2, e2, kind) in inner_items:
if kind != "stmt":
continue
var = find_stmt_var(inner, s2, e2)
if var is None:
continue
if prefix_filter and not var.startswith(prefix_filter):
continue
body_raw = inner[s2:e2].strip()
wrapped = f"(\n{body_raw}\n)"
header = f"~{var} -- extrait de {src_path.name}"
write_unit(out_dir / f"{var}.scd", header, wrapped)
count += 1
return count
count = 0
for (s, e, kind) in items:
if kind != "stmt":
continue
var = find_stmt_var(src, s, e)
if var is None:
continue
if prefix_filter and not var.startswith(prefix_filter):
continue
body_raw = src[s:e].strip()
# wrap in single top-level (...) block
wrapped = f"(\n{body_raw}\n)"
header = f"~{var} -- extrait de {src_path.name}"
write_unit(out_dir / f"{var}.scd", header, wrapped)
count += 1
return count
def split_strategy_B(src_path: Path, out_dir: Path):
"""Each top-level `(...)` block -> file. Naming = slug of preceding header."""
src = src_path.read_text(encoding="utf-8")
items = scan_top_level(src)
count = 0
used_slugs: dict[str, int] = {}
# also pick up top-level statements (assignations directes ~xxx = ...;)
blocks_only = [it for it in items if it[2] == "block"]
stmts_only = [it for it in items if it[2] == "stmt"]
for (s, e, _kind) in blocks_only:
body_raw = src[s:e].rstrip()
# strip trailing `;` if present (we re-add as bare block)
if body_raw.endswith(";"):
body_raw = body_raw[:-1].rstrip()
header_text = find_block_header(src, s)
if not header_text:
inner = find_inner_var_in_block(src, s, e)
header_text = inner if inner else "block"
slug = slugify(header_text)
n = used_slugs.get(slug, 0) + 1
used_slugs[slug] = n
fname_slug = slug if n == 1 else f"{slug}_{n}"
write_unit(out_dir / f"{fname_slug}.scd",
f"{header_text} -- extrait de {src_path.name}",
body_raw)
count += 1
# Strategy A fallback for top-level `~xx = ...;` statements
for (s, e, _kind) in stmts_only:
var = find_stmt_var(src, s, e)
if var is None:
continue
body_raw = src[s:e].strip()
wrapped = f"(\n{body_raw}\n)"
header = f"~{var} -- extrait de {src_path.name}"
write_unit(out_dir / f"{var}.scd", header, wrapped)
count += 1
return count
def backup(src_path: Path):
rel = src_path.relative_to(ROOT)
dst = ROOT / "_migrate" / (str(rel).replace("/", "_") + ".bak")
dst.parent.mkdir(parents=True, exist_ok=True)
if not dst.exists():
shutil.copy2(src_path, dst)
return dst
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
summary = []
melodies_jobs = [
("short.scd", "short", "m"),
("long.scd", "long", "ml"),
("xlong.scd", "xlong", "mxl"),
("epic.scd", "epic", "mepic"),
]
for fname, subdir, prefix in melodies_jobs:
src = PAL / "melodies" / fname
if not src.exists():
print(f"skip (missing): {src}")
continue
backup(src)
out_dir = PAL / "melodies" / subdir
n = split_strategy_A(src, out_dir, prefix_filter=prefix)
summary.append((str(src.relative_to(ROOT)), n, "A"))
rhythm_files = ["drum_kits.scd", "patterns_genre.scd", "kicks.scd",
"percussions.scd", "sequences.scd", "fills.scd"]
for fname in rhythm_files:
src = PAL / "rhythm" / fname
if not src.exists():
print(f"skip (missing): {src}")
continue
backup(src)
out_dir = PAL / "rhythm" / fname.removesuffix(".scd")
n = split_strategy_B(src, out_dir)
summary.append((str(src.relative_to(ROOT)), n, "B"))
print("\n=== SPLIT SUMMARY ===")
total = 0
for path, n, strat in summary:
print(f" [{strat}] {path}: {n} unites")
total += n
print(f" TOTAL: {total} fichiers generes")
if __name__ == "__main__":
main()
+55
View File
@@ -0,0 +1,55 @@
# control/ — MIDI, jump, playlist
Outils de pilotage : controleur MIDI, saut direct dans une track,
playlist auto-fade. SETUP manuel apres `00_load.scd`.
## Fichiers
| Fichier | Role |
| --- | --- |
| `midi.scd` | INIT MIDIClient + MAPPING DEFAULT (CC + Notes) |
| `jump.scd` | `~jumpTo.(\X, \section_slug)` |
| `playlist.scd` | `~playPlaylist.([...])` avec fades |
## Pattern SETUP
Chaque fichier expose un bloc SETUP (a evaluer 1x) puis des
helpers `~xxx`. Bloc SETUP enrobe en `(...)` pour eval atomique.
```supercollider
( // SETUP JUMP
~sections = ();
~jumpTo = { |trackLetter, sectionSlug| ... };
"[OK] SETUP JUMP".postln;
)
```
## Jump : invariants
`~jumpTo` doit :
1. Stopper toutes les Routines actives (`~track`, `~filterSweep`, ...)
2. Appliquer la section (mute `~xxx` vars)
3. `~reload.value` pour rebuild Pdef
4. Relancer les Pdef principaux (`\kickSeq`, `\hatSeq`, ...)
Sections enregistrees par chaque track via :
```supercollider
~sections[\A] = (
\intro: { ~bpm = 124; ~melodyInst = \pad; ... },
\drop: { ~bpm = 132; ~melodyInst = \lead; ... }
);
```
## MIDI
- `MIDIClient.init` puis `MIDIIn.connectAll` une seule fois
- `MIDIdef.cc(\name, { |val, num, chan, src| ... }, ccNum, chan)`
- Toujours `MIDIdef.freeAll` avant un re-mapping pour eviter les doublons
## Anti-patterns
- Ne pas evaluer SETUP plusieurs fois sans `MIDIdef.freeAll` -> CC empiles
- Ne pas oublier de stopper les Routines dans `~jumpTo` -> superposition
- Ne pas hardcoder un device MIDI (utiliser device index ou nom)
- Ne pas mapper sur `~xxx` sans `~reload.value` derriere si Pdef-related
- Ne pas appeler `~jumpTo` sur une section non enregistree (silencieux mais inutile)
File diff suppressed because it is too large Load Diff
+119
View File
@@ -0,0 +1,119 @@
// =====================================================================
// midi.scd -- Mapping MIDI controller (CC + Notes)
//
// Branche un controller (Launch Control, BCR2000, Midi Fighter, etc.)
// et map les CC sur les parametres essentiels du moteur.
//
// USAGE :
// 1. Cmd+Entree sur INIT MIDI (initialise + liste devices)
// 2. Cmd+Entree sur MAPPING DEFAULT (ou copier-coller pour customiser)
// 3. Tweak en temps reel via le controller MIDI
// =====================================================================
// --- INIT MIDI -- liste les devices disponibles --------------------
(
MIDIClient.init;
"=== MIDI sources disponibles ===".postln;
MIDIClient.sources.do { |src, i|
(" [" ++ i ++ "] " ++ src.device ++ " : " ++ src.name).postln;
};
"=== MIDI destinations ===".postln;
MIDIClient.destinations.do { |dst, i|
(" [" ++ i ++ "] " ++ dst.device ++ " : " ++ dst.name).postln;
};
MIDIIn.connectAll;
"=== MIDIIn.connectAll OK ===".postln;
)
// --- MAPPING DEFAULT (16 CC standard) ------------------------------
// Adapte les numeros CC selon ton controller (ici : Launch Control XL like)
(
~midiFuncs !? { ~midiFuncs.do { |f| f.free } };
~midiFuncs = List.new;
// Debounce ~reload pour eviter le spam (~reload reconstruit 8 Pdef !)
~reloadPending = ~reloadPending ?? false;
~debouncedReload = {
if (~reloadPending.not) {
~reloadPending = true;
AppClock.sched(0.05, { ~reloadPending = false; ~reload.value; nil });
};
};
// CC 13-20 = potards rangée 1 (volumes par voix)
~midiFuncs.add(MIDIFunc.cc({ |val| ~kickAmp = val.linlin(0,127, 0,1); ~debouncedReload.() }, 13));
~midiFuncs.add(MIDIFunc.cc({ |val| ~hatAmp = val.linlin(0,127, 0,0.4); ~debouncedReload.() }, 14));
~midiFuncs.add(MIDIFunc.cc({ |val| ~snareAmp = val.linlin(0,127, 0,0.8); ~debouncedReload.() }, 15));
~midiFuncs.add(MIDIFunc.cc({ |val| ~clapAmp = val.linlin(0,127, 0,0.6); ~debouncedReload.() }, 16));
~midiFuncs.add(MIDIFunc.cc({ |val| ~percAmp = val.linlin(0,127, 0,0.5); ~debouncedReload.() }, 17));
~midiFuncs.add(MIDIFunc.cc({ |val| ~acidAmp = val.linlin(0,127, 0,0.5); ~debouncedReload.() }, 18));
~midiFuncs.add(MIDIFunc.cc({ |val| ~melodyAmp = val.linlin(0,127, 0,0.4); ~debouncedReload.() }, 19));
~midiFuncs.add(MIDIFunc.cc({ |val| ~harmonyAmp = val.linlin(0,127, 0,0.4); ~debouncedReload.() }, 20));
// CC 29-36 = potards rangée 2 (cutoffs / filtres)
~midiFuncs.add(MIDIFunc.cc({ |val| ~melodyCutoff = val.linexp(0,127, 100,12000); ~debouncedReload.() }, 29));
~midiFuncs.add(MIDIFunc.cc({ |val| ~melodyRq = val.linlin(0,127, 0.05,0.8); ~debouncedReload.() }, 30));
~midiFuncs.add(MIDIFunc.cc({ |val| ~acidCutoff = val.linexp(0,127, 100,8000); ~debouncedReload.() }, 31));
~midiFuncs.add(MIDIFunc.cc({ |val| ~acidRq = val.linlin(0,127, 0.05,0.5); ~debouncedReload.() }, 32));
~midiFuncs.add(MIDIFunc.cc({ |val| ~melodyDrive = val.linlin(0,127, 0.5,5.0); ~debouncedReload.() }, 33));
~midiFuncs.add(MIDIFunc.cc({ |val| ~acidDrive = val.linlin(0,127, 0.5,5.0); ~debouncedReload.() }, 34));
~midiFuncs.add(MIDIFunc.cc({ |val| ~melodyBoost = val.linlin(0,127, 0.5,3.0); ~debouncedReload.() }, 35));
~midiFuncs.add(MIDIFunc.cc({ |val| TempoClock.default.tempo = val.linlin(0,127, 60,200) / 60 }, 36));
// CC 49-56 = potards rangée 3 (wobble / sidechain)
~midiFuncs.add(MIDIFunc.cc({ |val| ~melodyWobbleRate = val.linexp(0,127, 0.5,32); ~debouncedReload.() }, 49));
~midiFuncs.add(MIDIFunc.cc({ |val| ~melodyWobbleDepth = val.linlin(0,127, 0,4500); ~debouncedReload.() }, 50));
~midiFuncs.add(MIDIFunc.cc({ |val| ~melodySubAmt = val.linlin(0,127, 0,1.5); ~debouncedReload.() }, 51));
~midiFuncs.add(MIDIFunc.cc({ |val| ~acidScAmt = val.linlin(0,127, 0,1); ~debouncedReload.() }, 52));
~midiFuncs.add(MIDIFunc.cc({ |val| ~leadScAmt = val.linlin(0,127, 0,1); ~debouncedReload.() }, 53));
~midiFuncs.add(MIDIFunc.cc({ |val| ~kickScShape = val.linlin(0,127, 0.05,0.5); ~debouncedReload.() }, 54));
~midiFuncs.add(MIDIFunc.cc({ |val|
~fxFx !? { ~fxFx[\rev] !? { |s| s.set(\mix, val.linlin(0,127, 0,1)) } };
}, 55));
~midiFuncs.add(MIDIFunc.cc({ |val|
~fxFx !? { ~fxFx[\dly] !? { |s| s.set(\feedback, val.linlin(0,127, 0,0.95)) } };
}, 56));
// NOTES MIDI : 36-51 = pads de declenchement FX / fills
~midiFuncs.add(MIDIFunc.noteOn({ Synth(\impact, [\freq, 40, \decay, 2, \amp, 0.6]) }, 36));
~midiFuncs.add(MIDIFunc.noteOn({ Synth(\riser, [\dur, 4, \startCutoff, 400, \endCutoff, 11000, \amp, 0.3]) }, 37));
~midiFuncs.add(MIDIFunc.noteOn({ Synth(\sweep, [\dur, 4, \startCutoff, 8000, \endCutoff, 200, \amp, 0.3]) }, 38));
~midiFuncs.add(MIDIFunc.noteOn({ Synth(\crash, [\decay, 3, \amp, 0.4]) }, 39));
~midiFuncs.add(MIDIFunc.noteOn({ Synth(\gong, [\freq, 50, \decay, 8, \amp, 0.5]) }, 40));
~midiFuncs.add(MIDIFunc.noteOn({ ~snareFill !? { ~snareFill.value } }, 41));
~midiFuncs.add(MIDIFunc.noteOn({ ~tomFill !? { ~tomFill.value } }, 42));
// NOTES 48-59 = recall scenes (si scenes definies)
12.do { |i|
var name = ("scene_" ++ (i + 1)).asSymbol;
~midiFuncs.add(MIDIFunc.noteOn({
~loadScene !? { ~loadScene.(name) };
}, 48 + i));
};
"=== MIDI MAPPING ACTIF ===".postln;
" CC 13-20 : volumes par voix".postln;
" CC 29-36 : cutoffs / drive / bpm".postln;
" CC 49-56 : wobble / sidechain / FX".postln;
" Notes 36-42 : FX oneshot (impact/riser/sweep/crash/gong/fills)".postln;
" Notes 48-59 : recall scene_1 a scene_12".postln;
)
// --- DEBUG -- print tous les MIDI in entrants ----------------------
(
~midiDebug = MIDIFunc.cc({ |val, num, chan|
("[CC] " ++ num ++ " = " ++ val ++ " ch" ++ chan).postln;
});
)
( ~midiDebug !? { ~midiDebug.free; ~midiDebug = nil }; "MIDI debug OFF".postln; )
// --- STOP MIDI (libere tous les mappings) --------------------------
(
~midiFuncs !? { ~midiFuncs.do { |f| f.free }; ~midiFuncs = nil };
~midiDebug !? { ~midiDebug.free; ~midiDebug = nil };
"=== MIDI STOPPED ===".postln;
)
+279
View File
@@ -0,0 +1,279 @@
// =====================================================================
// playlist.scd -- Lance les tracks les unes apres les autres
//
// PREREQUIS : engine + synthdefs (charger d'abord 00_load.scd)
// USAGE : Cmd+Entree sur une playlist (au choix), elle s'enchaine
// automatiquement.
// Pour stopper : evaluer le bloc STOP PLAYLIST
// =====================================================================
// --- HELPER ~playPlaylist -------------------------------------------
// trackList = [[ "filename.scd", duration_seconds ], ...]
(
~playPlaylist = { |trackList, fadeOut = 4|
~playlist !? { ~playlist.stop };
~playlist = Routine({
var total = trackList.collect { |item| item[1] }.sum;
("=== PLAYLIST -- " ++ trackList.size ++ " tracks, " ++
(total / 60).round(0.1) ++ " min total ===").postln;
trackList.do { |item, idx|
var path = item[0]; var dur = item[1];
("[" ++ (idx + 1) ++ "/" ++ trackList.size ++ "] " ++ path ++
" (" ++ (dur/60).round(0.1) ++ " min)").postln;
~fading = false;
(~base ++ "tracks/" ++ path).load;
(dur - fadeOut).wait;
// FIX RACE : stopper la Routine track AVANT le fade pour qu'elle
// n'ecrase pas les amps qu'on baisse
~track !? { ~track.stop };
~fading = true;
("[fade out " ++ fadeOut ++ "s]").postln;
(fadeOut * 4).do { |k|
~melodyAmp = ~melodyAmp * 0.92;
~harmonyAmp = ~harmonyAmp * 0.92;
~kickAmp = ~kickAmp * 0.92;
~hatAmp = ~hatAmp * 0.92;
~snareAmp = ~snareAmp * 0.92;
~clapAmp = ~clapAmp * 0.92;
~percAmp = ~percAmp * 0.92;
~acidAmp = ~acidAmp * 0.92; // FIX : acid line absente du fade
~reload.value;
0.25.wait;
};
~fading = false;
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
2.wait;
};
"=== PLAYLIST TERMINEE ===".postln;
}).play;
};
"=== ~playPlaylist pret ===".postln;
)
// --- STOP PLAYLIST ---------------------------------------------------
(
~playlist !? { ~playlist.stop; ~playlist = nil };
~track !? { ~track.stop; ~track = nil };
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== PLAYLIST ARRETEE ===".postln;
)
// =====================================================================
// PLAYLISTS PREDEFINIES (Cmd+Entree sur celle qu'on veut)
// =====================================================================
// --- PLAYLIST FULL : toutes les 23 tracks (~3h12) ---------------------
(
~playPlaylist.([
["A_acid_journey.scd", 420], // 7 min
["B_deep_to_hard.scd", 480], // 8 min
["C_hypnotic_loop.scd", 600], // 10 min
["D_wobble_drop.scd", 420], // 7 min
["E_vietnam_hard.scd", 480], // 8 min
["F_trance_uplifter.scd", 600], // 10 min
["G_detroit_soulful.scd", 480], // 8 min
["H_acid_trance.scd", 480], // 8 min
["I_liquid_dnb.scd", 420], // 7 min
["J_ambient_cinematic.scd",720], // 12 min
["K_tribal_ethno.scd", 480], // 8 min
["L_future_garage.scd", 420], // 7 min
["M_chiptune_madness.scd", 360], // 6 min
["N_phonk.scd", 300], // 5 min
["O_asian_cinematic.scd", 600], // 10 min
["P_industrial.scd", 480], // 8 min
["Q_neurofunk_dnb.scd", 420], // 7 min
["R_synthwave.scd", 480], // 8 min
["S_drone_doom.scd", 900], // 15 min
["T_hardcore_gabber.scd", 360], // 6 min
["U_vocal_trance.scd", 600], // 10 min
["V_glitch_idm.scd", 480], // 8 min
["W_dub_techno.scd", 600], // 10 min
]);
)
// --- PLAYLIST DANCE FLOOR : tracks dansantes (~1h30) ------------------
(
~playPlaylist.([
["A_acid_journey.scd", 420],
["F_trance_uplifter.scd", 600],
["G_detroit_soulful.scd", 480],
["H_acid_trance.scd", 480],
["I_liquid_dnb.scd", 420],
["L_future_garage.scd", 420],
["U_vocal_trance.scd", 600],
["W_dub_techno.scd", 600],
]);
)
// --- PLAYLIST CHILL / AMBIENT : tracks calmes (~1h) ------------------
(
~playPlaylist.([
["J_ambient_cinematic.scd",720],
["O_asian_cinematic.scd", 600],
["K_tribal_ethno.scd", 480],
["S_drone_doom.scd", 900],
["W_dub_techno.scd", 600],
]);
)
// --- PLAYLIST BASS MUSIC : dubstep + dnb + bass-heavy (~1h) ----------
(
~playPlaylist.([
["D_wobble_drop.scd", 420],
["E_vietnam_hard.scd", 480],
["L_future_garage.scd", 420],
["I_liquid_dnb.scd", 420],
["Q_neurofunk_dnb.scd", 420],
["N_phonk.scd", 300],
["P_industrial.scd", 480],
]);
)
// --- PLAYLIST HARD : techno hard / hardcore / industrial (~50 min) ---
(
~playPlaylist.([
["B_deep_to_hard.scd", 480],
["P_industrial.scd", 480],
["T_hardcore_gabber.scd", 360],
["E_vietnam_hard.scd", 480],
["Q_neurofunk_dnb.scd", 420],
]);
)
// --- PLAYLIST WORLD MUSIC : asia + tribal + cinematic (~50 min) ------
(
~playPlaylist.([
["O_asian_cinematic.scd", 600],
["K_tribal_ethno.scd", 480],
["E_vietnam_hard.scd", 480],
["J_ambient_cinematic.scd",720],
]);
)
// --- PLAYLIST RETRO : synthwave + chiptune + house (~30 min) ---------
(
~playPlaylist.([
["R_synthwave.scd", 480],
["M_chiptune_madness.scd", 360],
["G_detroit_soulful.scd", 480],
]);
)
// --- PLAYLIST EXPERIMENTAL : glitch + drone + ambient (~38 min) ------
(
~playPlaylist.([
["V_glitch_idm.scd", 480],
["S_drone_doom.scd", 900],
["C_hypnotic_loop.scd", 600],
]);
)
// --- PLAYLIST SHORT (5 tracks, ~35 min) -------------------------------
(
~playPlaylist.([
["A_acid_journey.scd", 420],
["F_trance_uplifter.scd", 600],
["I_liquid_dnb.scd", 420],
["E_vietnam_hard.scd", 480],
["W_dub_techno.scd", 600],
]);
)
// --- PLAYLIST DEMO (durees reduites a 90s par track, ~35 min) --------
// Pour tester rapidement la palette sans attendre 3h
(
~playPlaylist.([
["A_acid_journey.scd", 90], ["B_deep_to_hard.scd", 90],
["C_hypnotic_loop.scd",90], ["D_wobble_drop.scd", 90],
["E_vietnam_hard.scd", 90], ["F_trance_uplifter.scd",90],
["G_detroit_soulful.scd",90],["H_acid_trance.scd",90],
["I_liquid_dnb.scd", 90], ["J_ambient_cinematic.scd",90],
["K_tribal_ethno.scd",90], ["L_future_garage.scd",90],
["M_chiptune_madness.scd",90],["N_phonk.scd",90],
["O_asian_cinematic.scd",90],["P_industrial.scd",90],
["Q_neurofunk_dnb.scd",90], ["R_synthwave.scd",90],
["S_drone_doom.scd", 90], ["T_hardcore_gabber.scd",90],
["U_vocal_trance.scd",90], ["V_glitch_idm.scd",90],
["W_dub_techno.scd", 90],
]);
)
// --- PLAYLIST RANDOM (toutes les tracks dans un ordre aleatoire) -----
(
~playPlaylist.(
[
"A_acid_journey.scd", "B_deep_to_hard.scd", "C_hypnotic_loop.scd",
"D_wobble_drop.scd", "E_vietnam_hard.scd", "F_trance_uplifter.scd",
"G_detroit_soulful.scd", "H_acid_trance.scd", "I_liquid_dnb.scd",
"J_ambient_cinematic.scd", "K_tribal_ethno.scd", "L_future_garage.scd",
"M_chiptune_madness.scd", "N_phonk.scd", "O_asian_cinematic.scd",
"P_industrial.scd", "Q_neurofunk_dnb.scd", "R_synthwave.scd",
"S_drone_doom.scd", "T_hardcore_gabber.scd", "U_vocal_trance.scd",
"V_glitch_idm.scd", "W_dub_techno.scd"
].scramble.collect { |fn|
var dur = (
"A_acid_journey": 420, "B_deep_to_hard": 480, "C_hypnotic_loop": 600,
"D_wobble_drop": 420, "E_vietnam_hard": 480, "F_trance_uplifter": 600,
"G_detroit_soulful": 480, "H_acid_trance": 480, "I_liquid_dnb": 420,
"J_ambient_cinematic": 720, "K_tribal_ethno": 480, "L_future_garage": 420,
"M_chiptune_madness": 360, "N_phonk": 300, "O_asian_cinematic": 600,
"P_industrial": 480, "Q_neurofunk_dnb": 420, "R_synthwave": 480,
"S_drone_doom": 900, "T_hardcore_gabber": 360, "U_vocal_trance": 600,
"V_glitch_idm": 480, "W_dub_techno": 600
)[fn.replace(".scd","").asSymbol] ?? 420;
[fn, dur];
}
);
)
// =====================================================================
// PLAYLIST CUSTOM -- copier ce template et editer la liste
// =====================================================================
(
~playPlaylist.([
// [filename, duree_secondes],
["F_trance_uplifter.scd", 300], // 5 min seulement
["U_vocal_trance.scd", 300],
["H_acid_trance.scd", 300],
]);
)
// =====================================================================
// CONTROLES PENDANT LA PLAYLIST
// =====================================================================
// --- SKIP TRACK (passe a la suivante immediatement) ------------------
(
~track !? { ~track.stop };
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"[SKIP] passage a la track suivante".postln;
)
// --- PAUSE / RESUME (toggle mute global via master limiter) ---------
( ~masterLim !? { ~masterLim.run(false) }; "[PAUSE]".postln; )
( ~masterLim !? { ~masterLim.run(true) }; "[RESUME]".postln; )
// --- INFO -- track en cours ------------------------------------------
( ("synths actifs: " ++ s.numSynths ++ " | CPU: " ++ s.avgCPU.round(0.1) ++ "%").postln; )
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,504 @@
# Refonte complète de l'organisation du code et des fichiers
**Date** : 2026-05-04
**Statut** : Design approuvé, en attente de plan d'implémentation
**Cible** : passage à une organisation atomique granulaire (1 fichier = 1 unité)
---
## Contexte
Le projet `sound_algo` a actuellement une structure mixte :
- 6 fichiers contenant 38 SynthDef éparpillés (engine.scd + synthdefs/{asia,extra,authentic}.scd + live/{live,fx_bus}.scd)
- 4 fichiers groupés contenant 133 mélodies (`palette/melodies/{short,long,xlong,epic}.scd`)
- 6 fichiers contenant 120+ patterns rythmiques (`palette/rhythm/`)
- 23 fichiers track monolithiques où chaque section est imbriquée dans une `Routine`
- API live `~kk/~mm/~ff/~cc/~p` classifiée par technique (longueur de mélodie, type de FX) plutôt que par genre musical
**Limites observées** :
- Difficile d'ajouter une mélodie / pattern / SynthDef sans toucher un gros fichier multi-blocs
- Impossible d'exécuter une section de track isolément (tout est dans la Routine parente)
- Classification par technique sépare les mélodies d'un même genre dans 4 fichiers différents
- L'API live force à connaître la longueur (`~mShort/~mLong`) plutôt que l'intention musicale
## Objectif
Refondre l'organisation pour atteindre :
1. **Granularité atomique** : 1 fichier = 1 SynthDef, 1 FX, 1 mélodie, 1 pattern, 1 section de track
2. **Classification par genre musical** (pas par technique) pour mélodies et patterns
3. **Auto-discovery** : ajouter un nouveau fichier suffit (pas de mise à jour d'index manuel)
4. **Sections de track exécutables** Cmd+Enter individuellement, sans dépendre du Routine parent
5. **Nouvelle API live** par genre, l'ancienne disparaît complètement (pas de double maintenance)
## Contraintes
- Les 10 tests E2E doivent rester verts à chaque phase de migration
- Convention SC : variables `~xxx` minuscules ; un seul bloc top-level dans les fichiers `.load`-compatibles ; balance parens P:0 B:0
- `web_bridge.scd` doit continuer à fonctionner (le bridge OSC alimenté par la web surface)
- Les 23 tracks A-W doivent rester jouables (boutons jump, chains, web surface)
- Repo GitHub privé déjà push, branche `main` protégée par convention
---
## Architecture cible
### Structure top-level
```
sound_algo/
├── 00_load.scd # loader / index — entry point
├── 01_live.scd # tableau de bord live, Cmd+Enter par bloc
├── engine.scd # Pdef + ~reload + helpers (plus de SynthDef)
├── setup.scd # ~check / ~setupAll / ~teardown
├── web_bridge.scd # OSC bridge SC ↔ web
├── synth/ # 1 fichier = 1 SynthDef, sous-dossiers par rôle
├── fx/ # 1 fichier = 1 FX, sous-dossiers par routage
├── melodies/ # 1 fichier = 1 mélodie, sous-dossiers par genre
├── patterns/ # 1 fichier = 1 pattern, sous-dossiers par genre
├── tracks/ # 1 sous-dossier par track, 1 fichier par section
├── palette/ # banques compositionnelles (scales, chords, ...)
├── audio/ # ce qui était live/ sans les SynthDef
├── control/ # jump, midi, playlist
├── tests/ # E2E
├── examples/ # tutoriels
├── web/ # Node bridge + UI
├── _archive/ # legacy (ignoré)
└── docs/
└── superpowers/specs/
```
### `synth/` — 38 SynthDef en sous-dossiers par rôle
```
synth/
├── _index.scd
├── drums/ kick.scd, kick_gabber.scd, hat.scd, snare.scd, snare_gated.scd,
│ clap.scd, perc.scd, rim.scd, cowbell.scd, tom.scd
├── bass/ acid.scd, reese.scd, reese_dnb.scd, reese_hard.scd, sub_boom.scd,
│ fm_bass.scd, hooverbass.scd, didgeridoo.scd
├── lead/ saw3.scd, lead.scd, square_lead.scd, fm_lead.scd, organ_lead.scd,
│ hard_lead.scd, supersaw.scd, stab.scd, pluck.scd, fm_bell.scd, rhodes.scd
├── pad/ pad.scd, warm_pad.scd, vocal_pad.scd, drone.scd
├── world/ koto.scd, erhu.scd, pipa.scd, gong.scd, handpan.scd, guzheng.scd, growl.scd
└── master/ master_lim.scd, master_comp.scd, master_sat.scd, vinyl.scd
```
**Convention par fichier** :
```supercollider
// synth/drums/kick.scd
(
SynthDef(\kick, {
arg freq = 55, pitchAmt = 3.0, ...;
...
}).add;
)
```
Un seul bloc top-level, **uniquement** un `SynthDef.add`. Pas de variable `~xxx`, pas de helpers.
### `fx/` — ~30 FX en sous-dossiers par routage
```
fx/
├── _index.scd
├── bus/ # FX wet send, lus depuis ~fxBuses
│ reverb.scd, delay.scd, chorus.scd, phaser.scd, flanger.scd,
│ bitcrush.scd, tape.scd, shimmer.scd, dist.scd, send.scd
├── insert/ # FX en chaîne avant masterLim
│ vcf_moog.scd, vcf_ms20.scd, vcf_ladder.scd, vcf_formant.scd,
│ vcf_comb.scd, vcf_hpf.scd, vcf_bpf.scd, vcf_notch.scd,
│ ringmod.scd, freq_shift.scd, gran.scd, comb.scd, formant.scd,
│ auto_filter.scd, tremolo.scd, autopan.scd, stutter.scd,
│ resonator.scd, spring_rev.scd, plate.scd
└── trick/ # FX oneshot
riser.scd, sweep.scd, impact.scd, crash.scd
```
### Auto-glob index
Chaque dossier (sauf `tracks/`) a un `_index.scd` qui scanne récursivement et charge :
```supercollider
// synth/_index.scd
(
PathName(thisProcess.nowExecutingPath.dirname).deepFiles
.select { |f| f.extension == "scd"
and: { f.fileName != "_index.scd" } }
.sort { |a, b| a.fullPath < b.fullPath }
.do { |f| f.fullPath.load };
)
```
`tracks/_index.scd` ne charge PAS les tracks au boot — elles sont lazy, on charge à la demande via `~jumpTo` ou Cmd+Enter manuel sur `_track.scd`.
### `melodies/` — 133 fichiers en sous-dossiers par genre
Mapping inverse depuis `~melodyBank` (palette/melodies/bank.scd actuel) → genre dominant pour chaque mélodie. Une mélodie présente dans plusieurs genres reste dans son genre principal (pas de duplication).
```
melodies/
├── _index.scd
├── acid/ m1.scd ... m12.scd # ~mAcid1..~mAcid12
├── trance/ m1.scd ... m{N}.scd # ~mTrance1..
├── detroit/ ... # ~mDetroit*
├── dnb/ ... # ~mDnb*
├── dub/ ... # ~mDub*
├── dubstep/ ... # ~mDubstep*
├── house/ ... # ~mHouse*
├── hardstyle/ ... # ~mHardstyle*
├── phonk/ ... # ~mPhonk*
├── synthwave/ ... # ~mSynthwave*
├── oriental/ ... # ~mOriental*
├── world/ ... # ~mWorld*
├── ambient/ ... # ~mAmbient*
└── industrial/ ... # ~mIndustrial*
```
**Convention par fichier mélodie** :
```supercollider
// melodies/acid/m1.scd
(
~mAcid1 = [36, 0, 36, 0, 39, 36, 41, 36,
36, 0, 36, 39, 41, 0, 43, 39];
)
```
La longueur (32/64/96/128 pas) est détectée via `.size` à l'usage — pas de classification technique séparée.
### `patterns/` — 120+ fichiers en sous-dossiers
```
patterns/
├── _index.scd
├── kick/ techno.scd, gabber.scd, dub.scd, ... # ~pKickTechno, ...
├── hat/ offbeat.scd, eighth.scd, sixteen.scd, ...
├── snare/ two4.scd, ghost.scd, ...
├── clap/ ...
├── perc/ ...
└── genre/ # patterns multi-instrument complets
├── _index.scd
├── acid/ house.scd, hard_techno.scd, ...
├── trance/ trance138.scd, psy_trance.scd, ...
├── dnb/ liquid.scd, neurofunk.scd, jungle.scd, ...
├── dubstep/ ...
├── house/ deep.scd, tech.scd, ...
├── hardstyle/ ...
├── phonk/ ...
├── industrial/ ...
├── breakcore/ ...
├── footwork/ ...
├── idm/ ...
└── glitch_hop/ ...
```
**Pattern simple** (1 instrument) :
```supercollider
// patterns/kick/techno.scd
( ~pKickTechno = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; )
```
**Pattern multi-instrument** (`genre/`) :
```supercollider
// patterns/genre/acid/house.scd
(
~pAcidHouse = (
kick: [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0],
hat: [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0],
snare: [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0],
clap: [0!16],
perc: [0!16],
bpm: 125,
bassline: [36, 0, 36, 0, 39, 36, 41, 36, 36, 0, 36, 39, 41, 0, 43, 39],
style: \acid_house
);
)
```
### `tracks/` — sous-dossier par track + sections autonomes
```
tracks/
├── _index.scd # ne charge PAS les tracks au boot
├── A_acid_journey/
│ ├── _track.scd # orchestrateur Routine
│ ├── 01_minimal.scd # Cmd+Enter pour appliquer la section
│ ├── 02_acid_rave.scd
│ ├── 03_detroit.scd
│ ├── 04_hard_push.scd
│ ├── 05_supersaw_dub.scd
│ ├── 06_guzheng_acid.scd
│ ├── 07_outro.scd
│ └── README.md # timeline, BPM, style — optionnel
├── B_deep_to_hard/
│ ├── _track.scd
│ └── ...
└── ... (W tracks, soit 23 dossiers)
```
**Section autonome** :
```supercollider
// tracks/A_acid_journey/02_acid_rave.scd
// Section [A 2:00] : acid rave -- kick T1 + melody LEAD + variations
(
"[A 2:00] acid rave -- kick T1 + melody LEAD + variations".postln;
~bpm = 132; TempoClock.default.tempo = ~bpm/60;
~kickFreq = 55; ~kickPitchAmt = 3.0; ~kickDecay = 0.32;
~kickDrive = 1.3; ~kickClick = 0.2;
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.18;
~melodyInst = \lead;
~melodyAttack = 0.012; ~melodyRelease = 0.25; ~melodyCutoff = 2400;
~melodyAmp = 0.16;
~acidNotes = [36,36,48,36,39,36,43,39, 36,41,36,39,43,36,36,41];
~acidSlides = [0,0,1,0,0,0,0,1, 0,0,0,1,0,0,0,0];
~melodyNotes = ~mAcid1; // nouvelle API par genre
~reload.value;
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).play };
)
```
**Section idempotente** : un Cmd+Enter applique tous les paramètres + reload, ne lance PAS de Routine, ne fait PAS de `.wait`.
**Orchestrateur `_track.scd`** :
```supercollider
// tracks/A_acid_journey/_track.scd
(
~track !? { ~track.stop };
~track = Routine({
var dir = thisProcess.nowExecutingPath.dirname;
(dir +/+ "01_minimal.scd").load;
120.wait;
(dir +/+ "02_acid_rave.scd").load;
120.wait;
(dir +/+ "03_detroit.scd").load;
90.wait;
// ...
"[A] === fin ACID JOURNEY ===".postln;
~masterFadeOut.(8);
}).play;
)
```
### `audio/` — ce qui était `live/` sans les SynthDef
```
audio/
├── _index.scd
├── fx_bus.scd # ~fxBuses + ~send + ~setFx + ~fxPreset + ~addFx +
│ # ~lfo / ~lfoStop / ~tap (LFO sur params FX)
├── insert.scd # ~vcfOn / ~vcfOff / ~vcfSweep / ~vcfPreset
├── tricks.scd # ~drop, ~breakdown, ~buildup, ~stutter, ~glitch,
│ # ~freeze, ~tapeStop, ~tapeReverse, ~jumpCut,
│ # ~bitCrushNow, ~ringMod, ~freqShift, ~granFreeze,
│ # ~scratch, ~repeat, ~swap, ~reverbTail, ~handsUp,
│ # ~chokePerc, ~chokeLead
│ # + ~trickRoutines, ~trickStop, ~trickStopAll
├── lfo.scd # ~lfoTo, ~lfoSync, ~lfoStopVar, ~lfoStopAllVars,
│ # ~lfos dict, ~setVar, ~getVar, ~envSweep, ~envs
├── mixer.scd # ~setVol, ~mute, ~solo, ~mixPreset, ~saveMix,
│ # ~masterVol, ~masterFade, ~masterFadeOut, ~xfade
├── scenes.scd # ~saveScene, ~loadScene, ~listScenes, ~deleteScene
├── loops.scd # ~loopOn, ~loopOff, ~loopChain, ~loopFade
├── chains.scd # ~ch dict + ~chainRoutine + ~chStop + helpers
├── wrappers.scd # ~mAcid, ~mTrance, ~pAcid, ~fx, ~synth (généré
│ # dynamiquement depuis melodies/ patterns/ synth/ fx/)
└── panel.scd # ex live_fx_panel — IDE-only
```
**`engine.scd`** ne contient plus de SynthDef. Il garde uniquement `~reload`, les Pdef (avec Pfunc déjà appliqué post-fix RAM), `~scBus`, `~melodyBoost`.
### `palette/` — banques compositionnelles
```
palette/
├── _index.scd
├── scales.scd # 16 ~scaleXxx + ~genMelody + ~mutate
├── harmonics/
│ ├── _index.scd
│ ├── helpers.scd # ~genThirdHarmony, ~genCounterMelody, ...
│ └── presets.scd # ~enableHarmony.(\third) -- 10 presets
├── chords_progressions.scd # ~progDetroit, ~progTrance, ...
├── timbres.scd # 26 ~timbreXxx -- L1-L26
├── styles.scd # 5 ~styleXxx complets
├── fx_genre.scd # combos FX par genre
├── fills.scd # ex palette/rhythm/fills.scd
├── sequences.scd # ex palette/rhythm/sequences.scd
└── tweaks_template.scd
```
### `control/` (chemins jump.scd ajustés)
```
control/
├── _index.scd
├── midi.scd # MIDIClient.init + mapping
├── jump.scd # ~jumpTo charge tracks/<X>/NN_section.scd
└── playlist.scd
```
`~jumpTo.(\A, \acid_rave)` ne contient plus une closure inline. Il fait :
```supercollider
~jumpTo = { |trackLetter, slug|
var dir = ~base ++ "tracks/" ++ ~trackDir.(trackLetter);
var matches = PathName(dir).files
.select { |f| f.fileName.contains(slug.asString) };
if (matches.notEmpty) {
~track !? { ~track.stop; ~track = nil };
matches.first.fullPath.load; // applique la section
} { "[JUMP] " ++ slug ++ " inexistante".postln };
};
```
## Nouvelle API live
Les wrappers actuels `~kk/~mm/~ff/~cc/~p` et tous les sous-wrappers (`~mShort, ~mGen, ~kPat, ~fxDrop, ~chIntro, ~pKit`...) sont **supprimés**. Nouvelle API par genre, plus orthogonale.
### Mélodies — `~mGenre.(N | nil | rnd)`
```supercollider
~mAcid.(1) # applique ~mAcid1 -> ~melodyNotes + reload
~mAcid.() # random dans le dossier acid/
~mTrance.(3) # applique ~mTrance3
~mDnb.() # random dnb
~mList.(\acid) # liste les mélodies du genre acid
~mList.() # liste tous les genres et leurs counts
~mNext.() # mélodie suivante (dans le genre courant)
~mPrev.() # précédente
~mInst.(\warmPad) # change l'instrument melody
~mHarm.(\third) # 10 presets harmonie (\off \third \fifth ...)
```
### Patterns — `~pGenre.(\name)`
```supercollider
~pAcid.(\house) # applique ~pAcidHouse (kick + hat + snare + bpm)
~pAcid.() # random dans patterns/genre/acid/
~pTrance.(\trance138)
~pDubstep.(\classic)
~pKick.(\techno) # pattern kick seul
~pHat.(\offbeat)
~pSnare.(\two4)
~pList.(\acid)
```
### Synth — `~synth.(\group, \name)`
```supercollider
~synth.(\drums, \kick) # info SynthDef
~melodyInst = \warmPad # change l'instrument actif
~kickInst = \kickGabber # change le kick (re-route Pdef)
~synthList.(\drums) # liste tous les drums
```
### FX — `~fx.(\type, \name, ...args)`
```supercollider
~fx.(\bus, \rev, 0.4) # ~send vers reverb wet 0.4
~fx.(\insert, \vcfMoog, 1500, 0.6) # active VCF Moog en chain
~fx.(\trick, \riser, 4) # oneshot riser dur=4s
# Tricks scéniques (inchangés)
~drop.()
~breakdown.(8)
~buildup.(8)
~glitch.(2)
~stutter.(8, 1)
~freeze.()
~tapeStop.(2)
~tapeReverse.(4)
~jumpCut.(0.5)
```
### Chains — `~ch.(\name, ...args)`
```supercollider
~ch.(\intro)
~ch.(\drop)
~ch.(\breakdown)
~ch.(\buildup, 8)
~ch.(\intro2drop, 4, 4)
~ch.(\dropToBreak, 8)
~ch.(\outroFade, 12)
~chStop.()
```
### Mixer / Scenes / Jump (inchangés)
```supercollider
~setVol.(\kick, 0.8)
~mute.(\acid) ; ~solo.(\melody)
~mixPreset.(\drumsHeavy)
~masterVol.(0.7) ; ~masterFadeOut.(8)
~saveMix.(\verseA)
~saveScene.(\drop1) ; ~loadScene.(\drop1)
~jumpTo.(\A, \acid_rave)
```
### Génération dynamique des wrappers
`audio/wrappers.scd` introspect les dossiers `melodies/`, `patterns/`, `synth/`, `fx/` au boot pour générer les helpers `~mAcid`, `~pTrance`, etc. Ajouter un nouveau genre = créer un dossier + un fichier dedans, l'API est mise à jour automatiquement au prochain `~setupAll`.
## Plan de migration (7 phases)
Chaque phase = 1 commit propre, testé, pushé. Si une phase fail, rollback de ce commit sans casser les précédents.
| # | Phase | Risque | Tests E2E |
|---|---|---|---|
| 1 | **Extract SynthDef** : créer `synth/` et `fx/`, déplacer les 38+30 SynthDef. `engine.scd` perd ses SynthDef mais garde Pdef. Auto-glob `_index.scd`. | Bas | Tous doivent passer |
| 2 | **Renommer `live/` → `audio/`** + déplacer `palette/rhythm/{fills,sequences}.scd``palette/`. Ajuster `setup.scd`, `00_load.scd`, `_load.scd`, `web_bridge.scd`. | Moyen | Tous doivent passer |
| 3 | **Éclater mélodies** : `melodies/<genre>/m{N}.scd`. Mapping inverse depuis `~melodyBank`. Variables `~m{Genre}{N}`. Supprimer `palette/melodies/`. | Élevé | `e2e_05_melodies` réécrit |
| 4 | **Éclater patterns** : `patterns/<genre>/<name>.scd`. Variables `~p{Genre}{Name}`. Supprimer `palette/rhythm/{kicks,percussions,drum_kits,patterns_genre}.scd`. | Élevé | tests adaptés |
| 5 | **Éclater tracks** : 23 dossiers `tracks/<X>_<slug>/{_track.scd, NN_section.scd}`. Section files autonomes. `_track.scd` orchestrateur. | Élevé | `e2e_09_tracks` adapté + `e2e_10_timestamps` adapté |
| 6 | **Nouvelle API live** : `audio/wrappers.scd` génère `~mAcid/~pAcid/~fx/~synth` dynamiquement. Réécrit `01_live.scd` + `web_bridge.scd` + `web/public/control/app.js` (chips dynamiques via `/api/genres`). Supprime ancienne API. | Très élevé | `e2e_08_live` réécrit |
| 7 | **Nettoyage** : suppression `_archive/` legacy, mise à jour `CLAUDE.md`, `README.md`, skills, `tests/CLAUDE.md`. Commit final tag `v2.0`. | Bas | Tous doivent passer |
## Acceptance criteria
- `bash tests/run_all.sh` retourne 10/10 PASS
- `awk balance` P:0 B:0 sur tous les `.scd`
- Boot complet via `(~base ++ "00_load.scd").load``~check.value` reporte tout `[OK]`
- `01_live.scd` Cmd+Enter sur chaque bloc fonctionne (transport, mixer, mélodies, patterns, FX, tricks, chains, scenes, jump)
- Web surface : sliders/chips fonctionnent (genres dynamiques chargés depuis `/api/genres`)
- Track A `_track.scd` Cmd+Enter joue l'intégralité, ET chaque section individuelle Cmd+Enter applique ses params
- `~jumpTo.(\A, \acid_rave)` charge `tracks/A_acid_journey/02_acid_rave.scd` au runtime
- Aucune fuite RAM scsynth/sclang après 30 min de session
- Documentation : root CLAUDE.md + nested CLAUDE.md dans chaque nouveau dossier
## Risques identifiés et mitigation
| Risque | Mitigation |
|---|---|
| Mélodie présente dans plusieurs genres → mauvais classement | Mapping inverse depuis `~melodyBank` actuel + arbitre par genre dominant ; possibilité de la dupliquer manuellement après si dissonance |
| Web surface (`web/public/control/app.js`) hardcoded sur l'ancienne API | Phase 6 inclut endpoint `GET /api/genres` qui scanne `melodies/` et `patterns/` ; les chips JS sont rendues dynamiquement |
| Tests E2E qui hardcodent les anciens chemins (`palette/melodies/short.scd`) | Adapter chaque test E2E dans la phase qui change ses chemins. `e2e_05`, `e2e_09`, `e2e_10` impactés |
| `~jumpTo` actuel utilise des closures inline dans `control/jump.scd` | Phase 5 réécrit `~jumpTo` pour `.load` dynamique d'un fichier section |
| Boot lent à cause de 200+ `.load` séquentiels | Mesurer après phase 1. Si > 5s, batcher ou paralléliser via `Task` |
| Dépendances non triviales entre `synth/` et `palette/` (ex: scales lues par certains SynthDef) | Aucune actuellement (les SynthDef sont autonomes), mais à valider phase 1 |
## Notes complémentaires
- Le pattern `Pfunc { ~xxx }` introduit en commit `62e10b3` (fix RAM LFO+VCF) reste appliqué — la migration ne le touche pas
- Le pattern `~trickRoutines` introduit en commit `c4f17fd` reste appliqué dans `audio/tricks.scd`
- `~crossfadeFxRoutine` et `~chainRoutine` (commit `5f96d57`) restent appliqués dans `audio/fx_bus.scd` et `audio/chains.scd`
- L'auto-launch web (commit `afaa072`) reste dans `web_bridge.scd`
---
**Statut** : design approuvé section par section. Prochaine étape : génération du plan d'implémentation détaillé via la skill `superpowers:writing-plans`.
+280
View File
@@ -0,0 +1,280 @@
// =====================================================================
// engine.scd -- Moteur (SynthDef + helpers + Pdef + master limiter)
//
// USAGE
// 1. Cmd+Entree sur le bloc SETUP (une fois par session)
// 2. Cmd+Entree sur le bloc PLAY pour lancer le beat
// 3. Charger separement : presets.scd, tweaks.scd, tracks/*.scd
// 4. Cmd+Entree sur STOP en bas pour couper
// =====================================================================
// 1. SETUP -- evalue UNE FOIS au demarrage de la session
// =====================================================================
(
s.waitForBoot {
// ----- Acid 303 -----
// Env ADSR sur l'amp avec doneAction:2 -- garanti pas de fuite
// memoire meme si on l'utilise dans un Pbind classique. Avec
// PmonoArtic legato<1, le release (50ms) ne finit jamais avant
// le retrigger -> le synth reste vivant et le slide fonctionne.
// ----- Kick (rond, sub-charge, click discret + sidechain out) -----
// ----- Hi-hat -----
// ----- Lead melody (saw+pulse detunes + chorus stereo + LPF) -----
// ----- Pad (super-saw 5 voix detunes + chorus + LPF) -----
// ----- Pluck (Karplus-Strong, decay naturel) -----
// ----- FM Bell DUBSTEP -- wobble + sub + detune + saturation -----
// Inspire des growl/wobble dubstep : RLPF resonant module par LFTri
// (wobble), 2 voix detunees, sub-bass parallele, distortion lourde.
// ----- Stab (court, saw + saturation pour Detroit) -----
// ----- Saw3 (3 saws empilees, octaves -1/0/+1) -----
// ----- Snare (tone + bruit blanc filtre) -----
// ----- Clap (4 attaques rapides puis tail) -----
// ----- Riser (bruit qui monte sur N secondes) -----
// Courbe \sin (qui supporte zero) -- \exp + valeur 0 = NaN
// qui silenciait tout le bus.
// ----- Impact (boom + reverse hit, marque les drops) -----
// ----- Open hat (cymbale ouverte, decay long) -----
// ----- Crash cymbal -----
// ----- Tom (drum tone melodique) -----
// ----- Rim shot (clic metallique court) -----
// ----- Cowbell -----
// ----- Reese bass (2 saws detunes filtres -- DnB / dubstep) -----
// ----- 808 bass (trap-style sub avec pitch glide) -----
// ----- Sweep down (l'inverse du riser, descend) -----
// ----- Vinyl noise (texture continue, ambiance) -----
// ----- Master limiter -----
s.sync;
// ----- Bus de sidechain (control) ----------------------------
// Le kick ecrit son enveloppe sur ce bus, acid + lead lisent et
// duckent leur sortie -> mix techno qui pompe sur le kick.
~scBus !? { ~scBus.free };
~scBus = Bus.control(s, 1);
// ----- Master limiter en queue de groupe ---------------------
~masterLim !? { ~masterLim.free };
~masterLim = Synth.tail(s.defaultGroup, \masterLim);
// ----- Patterns par defaut -----------------------------------
~bpm = 132;
~acidNotes = [ 36, 36, 48, 36, 39, 36, 43, 39,
36, 41, 36, 39, 43, 36, 36, 41 ];
~acidAccents = [ 1, 0, 0, 0, 1, 0, 1, 0,
0, 1, 0, 0, 1, 0, 0, 1 ];
~acidSlides = [ 0, 0, 1, 0, 0, 0, 0, 1,
0, 0, 0, 1, 0, 0, 0, 0 ];
~kickSteps = [ 1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0 ];
~hatSteps = [ 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0 ];
~snareSteps = (0 ! 16);
~snareAmp = 0.45;
~snareFreq = 200; ~snareNoiseAmt = 0.6; ~snareDecay = 0.13;
// Percussions secondaires
~clapSteps = (0 ! 16); ~clapAmp = 0.35;
~percSteps = (0 ! 16); ~percAmp = 0.25; ~percInst = \rim; // \rim | \cowbell | \tom | \openHat
~tomFreq = 110; ~cowbellFreq = 800;
~melodyNotes = [
60, 0, 67, 0, 70, 0, 67, 65,
63, 0, 60, 0, 0, 67, 70, 72,
75, 0, 72, 70, 67, 0, 0, 65,
63, 60, 0, 67, 70, 0, 72, 0
];
// ----- Parametres de timbre ----------------------------------
~acidCutoff = 800; ~acidRq = 0.15; ~acidCutoffEnv = 3500;
~acidDrive = 2.5; ~acidAmp = 0.32;
~kickAmp = 0.5;
~kickFreq = 55; // frequence fondamentale (45=sub, 55=normal, 70=punchy)
~kickPitchAmt = 3.0; // ratio du drop initial (2=soft, 3=normal, 5=snappy)
~kickDecay = 0.32; // duree (0.18=tight, 0.32=normal, 0.6=boomy)
~kickDrive = 1.3; // saturation (1.0=clean, 1.5=normal, 3.0=distort)
~kickClick = 0.2; // amplitude du click HF (0=rond, 0.4=claquant)
~hatAmp = 0.18;
~melodyInst = \lead; // \lead | \pad | \pluck | \fmBell | \stab | \saw3
~melodyBoost = 1.6; // multiplicateur global du volume melodie
~melodyCutoff = 2400; ~melodyRq = 0.5; ~melodyAmp = 0.16;
~melodyDrive = 1.2; ~melodyAttack = 0.012; ~melodyRelease = 0.25;
~melodySawAmt = 0.6; ~melodyDetune = 0.006;
// Specifique fmBell dubstep
~melodyWobbleRate = 4; // Hz (4=1/8 a 120BPM, 8=1/16, 2=1/4)
~melodyWobbleDepth = 1800; // ouverture du wobble en Hz
~melodyWobbleShape = 0; // 0 = LFTri smooth, 1 = LFPulse chop
~melodySubAmt = 0.85; // sub fmBell (0.85 = MASSIF)
// ----- 2e voix melodique (harmony / counter-melody) ----------
~harmonyInst = \pad; // synth pour la 2e voix
~harmonyNotes = (0 ! 32); // muet par defaut
~harmonyCutoff = 1800; ~harmonyRq = 0.6;
~harmonyAttack = 0.05; ~harmonyDecay = 0.15;
~harmonySustain = 0.6; ~harmonyRelease = 0.5;
~harmonyDrive = 1.2; ~harmonyAmp = 0.14;
~harmonyDur = 0.25; // 0.25 = 1/16 ; 0.5 = 1/8 ; 0.125 = 1/32
~harmonyDetune = 0.006; ~harmonySawAmt = 0.6;
// Sidechain : amount par voix (0 = pas de duck, 1 = mute total sur kick)
~acidScAmt = 0.7; // l'acid pompe fort sur le kick
~leadScAmt = 0.5; // le lead pompe modere
~kickScShape = 0.18; // duree de l'env sidechain (s)
// ----- Reload : reconstruit les 4 Pdef depuis les variables --
~reload = {
// Guards : les Pseq plantent si on leur passe nil/array vide.
// On force une valeur par defaut pour chaque variable de pattern.
if (~kickSteps.isNil or: { ~kickSteps.isEmpty }) { ~kickSteps = (0 ! 16) };
if (~hatSteps.isNil or: { ~hatSteps.isEmpty }) { ~hatSteps = (0 ! 16) };
if (~snareSteps.isNil or: { ~snareSteps.isEmpty }) { ~snareSteps = (0 ! 16) };
if (~clapSteps.isNil or: { ~clapSteps.isEmpty }) { ~clapSteps = (0 ! 16) };
if (~percSteps.isNil or: { ~percSteps.isEmpty }) { ~percSteps = (0 ! 16) };
if (~acidNotes.isNil or: { ~acidNotes.isEmpty }) { ~acidNotes = [36] };
if (~acidAccents.isNil or: { ~acidAccents.isEmpty }) { ~acidAccents = [0] };
if (~acidSlides.isNil or: { ~acidSlides.isEmpty }) { ~acidSlides = [0] };
if (~melodyNotes.isNil or: { ~melodyNotes.isEmpty }) { ~melodyNotes = (0 ! 32) };
if (~harmonyNotes.isNil or: { ~harmonyNotes.isEmpty }) { ~harmonyNotes = (0 ! 32) };
// Amps / params scalaires : default 0 evite NaN
~kickAmp = ~kickAmp ? 0; ~hatAmp = ~hatAmp ? 0;
~snareAmp = ~snareAmp ? 0; ~clapAmp = ~clapAmp ? 0;
~percAmp = ~percAmp ? 0; ~acidAmp = ~acidAmp ? 0;
~melodyAmp = ~melodyAmp ? 0; ~harmonyAmp = ~harmonyAmp ? 0;
~melodyBoost = ~melodyBoost ? 1; ~harmonyDur = ~harmonyDur ? 0.25;
// Pan defaults (utilises par ~autoPan -- Pfunc dynamique ci-dessous)
~acidPan = ~acidPan ? 0;
~melodyPan = ~melodyPan ? 0;
~harmonyPan = ~harmonyPan ? 0;
Pdef(\kickSeq, Pbind(
\instrument, \kick, \dur, 0.25,
\amp, Pseq(~kickSteps.collect { |s| s * ~kickAmp }, inf),
\freq, Pfunc { ~kickFreq }, \pitchAmt, Pfunc { ~kickPitchAmt },
\decay, Pfunc { ~kickDecay }, \drive, Pfunc { ~kickDrive }, \click, Pfunc { ~kickClick },
\scBus, ~scBus.index, \scShape, Pfunc { ~kickScShape },
));
Pdef(\hatSeq, Pbind(
\instrument, \hat, \dur, 0.25,
\amp, Pseq(~hatSteps.collect { |s| s * ~hatAmp }, inf),
));
Pdef(\snareSeq, Pbind(
\instrument, \snare, \dur, 0.25,
\amp, Pseq(~snareSteps.collect { |s| s * ~snareAmp }, inf),
\freq, Pfunc { ~snareFreq }, \noiseAmt, Pfunc { ~snareNoiseAmt }, \decay, Pfunc { ~snareDecay },
));
Pdef(\clapSeq, Pbind(
\instrument, \clap, \dur, 0.25,
\amp, Pseq(~clapSteps.collect { |s| s * ~clapAmp }, inf),
));
Pdef(\percSeq, Pbind(
\instrument, ~percInst, \dur, 0.25,
\amp, Pseq(~percSteps.collect { |s| s * ~percAmp }, inf),
\freq, Pfunc { if (~percInst == \tom) { ~tomFreq } { ~cowbellFreq } },
));
Pdef(\acidSeq, PmonoArtic(\acid,
\dur, 0.25,
\legato, 0.85,
\midinote, Pseq(~acidNotes, inf),
\accent, Pseq(~acidAccents, inf),
\slideTime, Pseq(~acidSlides.collect { |s| if(s == 1) {0.12} {0.005} }, inf),
\cutoff, Pfunc { ~acidCutoff },
\cutoffEnv, Pfunc { ~acidCutoffEnv },
\rq, Pfunc { ~acidRq },
\drive, Pfunc { ~acidDrive },
\amp, Pfunc { ~acidAmp },
\pan, Pfunc { ~acidPan ? 0 },
\scBus, ~scBus.index, // lit le sidechain
\scAmt, Pfunc { ~acidScAmt },
));
Pdef(\melodySeq, Pbind(
\instrument, ~melodyInst, \dur, 0.25,
\legato, 0.9,
\type, Pseq(~melodyNotes.collect { |n| if(n > 0) { \note } { \rest } }, inf),
\midinote, Pseq(~melodyNotes.collect { |n| if(n > 0) { n } { 60 } }, inf),
\cutoff, Pfunc { ~melodyCutoff },
\rq, Pfunc { ~melodyRq },
\attack, Pfunc { ~melodyAttack },
\release, Pfunc { ~melodyRelease },
\drive, Pfunc { ~melodyDrive },
\sawAmt, Pfunc { ~melodySawAmt },
\detune, Pfunc { ~melodyDetune },
\wobbleRate, Pfunc { ~melodyWobbleRate }, // pour fmBell
\wobbleDepth, Pfunc { ~melodyWobbleDepth },
\wobbleShape, Pfunc { ~melodyWobbleShape },
\subAmt, Pfunc { ~melodySubAmt },
\amp, Pfunc { ~melodyAmp * ~melodyBoost },
\pan, Pfunc { ~melodyPan ? 0 },
\scBus, ~scBus.index,
\scAmt, Pfunc { ~leadScAmt },
));
// 2e voix : peut tourner en parallele avec une duree differente
// (32th, 16th, 8th) pour creer du contrepoint / phasing
Pdef(\harmonySeq, Pbind(
\instrument, ~harmonyInst, \dur, Pfunc { ~harmonyDur },
\legato, 0.9,
\type, Pseq(~harmonyNotes.collect { |n| if(n > 0) { \note } { \rest } }, inf),
\midinote, Pseq(~harmonyNotes.collect { |n| if(n > 0) { n } { 60 } }, inf),
\cutoff, Pfunc { ~harmonyCutoff },
\rq, Pfunc { ~harmonyRq },
\attack, Pfunc { ~harmonyAttack },
\decay, Pfunc { ~harmonyDecay },
\sustain, Pfunc { ~harmonySustain },
\release, Pfunc { ~harmonyRelease },
\drive, Pfunc { ~harmonyDrive },
\detune, Pfunc { ~harmonyDetune },
\sawAmt, Pfunc { ~harmonySawAmt },
\amp, Pfunc { ~harmonyAmp * ~melodyBoost },
\pan, Pfunc { ~harmonyPan ? 0 },
\scBus, ~scBus.index,
\scAmt, Pfunc { ~leadScAmt },
));
};
// ----- ~enableHarmony : voir palette/harmonics/presets.scd ---
// ----- Scales + helpers : voir palette/scales.scd ------------
// (charges automatiquement par 00_load.scd > CHARGER TOUT)
"=== setup OK -- lance le bloc PLAY ===".postln;
};
)
+60
View File
@@ -0,0 +1,60 @@
// =====================================================================
// examples/00_index.scd -- Index des exemples de workflow
//
// CONVENTION
// Chaque fichier de ce dossier est un mini-tutoriel a executer
// bloc par bloc dans l'IDE SuperCollider (Cmd+Entree sur chaque
// parenthese top-level). Il N'EST PAS charge via .load -- on
// avance manuellement, en lisant les commentaires.
//
// PARCOURS RECOMMANDE pour decouvrir le projet
// 01_quickstart.scd Premier "Hello world" sonore (debutant)
// 02_first_track.scd Lancer une track autonome (debutant)
// 04_filter_lfo_vcf.scd LFO + VCF -- la base du modular live
// 06_drum_kits_sequences.scd Macros rapides pour kits + sequences
// 07_loops_manager.scd Gestionnaire de boucles propre
// 09_scenes_recall.scd Save / recall snapshots
// 11_playlist_auto.scd Lancer une session sans rien faire
//
// CATALOGUE COMPLET
// 01_quickstart.scd (debutant) Premier demarrage
// 02_first_track.scd (debutant) Track autonome A
// 03_multi_melody_tracks.scd (intermediaire) Multi-pistes melodiques
// 04_filter_lfo_vcf.scd (intermediaire) LFO + VCF master
// 05_drops_breakdowns_buildups (intermediaire) Tricks scéniques
// 06_drum_kits_sequences.scd (debutant) Drum kits + sequences pretes
// 07_loops_manager.scd (intermediaire) loops.scd workflow
// 08_jump_sections.scd (intermediaire) Naviguer dans les tracks
// 09_scenes_recall.scd (intermediaire) Snapshot / recall
// 10_midi_controller.scd (avance) Setup controller MIDI
// 11_playlist_auto.scd (debutant) Playlist auto + controles
// 12_chord_progressions.scd (intermediaire) ~progXxx + chordsToMelody
// 13_world_ethno.scd (intermediaire) Workflow oriental/asia
// 14_fx_chains_dynamic.scd (avance) FX bus + LFO sur params FX
// 15_full_live_set.scd (avance) Set live 30 minutes complet
// =====================================================================
// --- Ouvrir tous les exemples dans l'IDE -----------------------------
(
var root = if (thisProcess.nowExecutingPath.notNil) { thisProcess.nowExecutingPath.dirname } { "/Users/electron/Documents/Projets_Creatifs/sound_algo-refonte-v2/examples" };
[
"01_quickstart.scd",
"02_first_track.scd",
"03_multi_melody_tracks.scd",
"04_filter_lfo_vcf.scd",
"05_drops_breakdowns_buildups.scd",
"06_drum_kits_sequences.scd",
"07_loops_manager.scd",
"08_jump_sections.scd",
"09_scenes_recall.scd",
"10_midi_controller.scd",
"11_playlist_auto.scd",
"12_chord_progressions.scd",
"13_world_ethno.scd",
"14_fx_chains_dynamic.scd",
"15_full_live_set.scd",
].do { |path|
Document.open(root +/+ path);
};
)
+92
View File
@@ -0,0 +1,92 @@
// =====================================================================
// examples/01_quickstart.scd -- Premier demarrage
//
// TITRE : Hello, sound_algo
// DUREE : ~2 minutes
// NIVEAU : debutant
// PREREQUIS : aucun (on charge tout depuis ce fichier)
//
// BUT
// - Lancer le moteur complet (engine + synthdefs + helpers + live/*)
// - Demarrer un beat techno minimal (kick + hat + acid)
// - Modifier 2-3 parametres en live
// - Stopper proprement
//
// Chaque parenthese (...) ci-dessous est un bloc independant. Pose
// ton curseur dedans, Cmd+Entree pour evaluer. Avance bloc par bloc.
// =====================================================================
// --- [etape 1] CHARGER TOUT (~5s, attend que le serveur boote) ------
// Reproduit exactement le bloc CHARGER TOUT de 00_load.scd : charge
// engine + synthdefs + scales + melodies + harmonics + setup, puis
// appelle ~setupAll.() qui boote le serveur et charge fx_bus + live
// + mixer + scenes + loops + fills + sequences + jump. Termine par
// un ~check.() qui rapporte l'etat de chaque module.
(
~base = "/Users/electron/Documents/Projets_Creatifs/sound_algo-refonte-v2/";
(~base ++ "engine.scd").load;
(~base ++ "synth/_index.scd").load;
(~base ++ "fx/_index.scd").load;
(~base ++ "palette/scales.scd").load;
(~base ++ "palette/melodies/index.scd").load;
(~base ++ "palette/harmonics/index.scd").load;
(~base ++ "setup.scd").load;
~setupAll.();
"=== CHARGEMENT EN COURS -- attendre la fin du boot et le ~check final ===".postln;
)
// --- [etape 2] PLAY -- lance les 8 sequenceurs Pdef -------------------
// Reproduit le bloc PLAY DEFAULT de 00_load.scd.
// Attendre que le bloc precedent ait fini son boot (3-5s) et que tu
// voies "=== setupAll DONE ===" dans le post window.
(
TempoClock.default.tempo = ~bpm / 60;
~reload.value;
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).play };
"=== PLAY ===".postln;
)
// >> Tu dois entendre kick + hat + acid 303. Laisse tourner 30s.
// --- [etape 3] TWEAK 1 : monter le volume du kick --------------------
// ~kickAmp est une variable globale. Apres modif, ~reload.value
// reconstruit les Pdef avec la nouvelle valeur.
(
~kickAmp = 0.7; // defaut 0.5
~reload.value;
("kickAmp = " ++ ~kickAmp).postln;
)
// --- [etape 4] TWEAK 2 : ouvrir le filtre de la melodie -------------
// ~melodyCutoff = frequence de coupure du LPF de la voix melody.
(
~melodyCutoff = 5000; // defaut 2400, plus haut = plus brillant
~reload.value;
("melodyCutoff = " ++ ~melodyCutoff).postln;
)
// --- [etape 5] TWEAK 3 : changer le BPM -------------------------------
(
~bpm = 140;
TempoClock.default.tempo = ~bpm / 60;
("bpm = " ++ ~bpm).postln;
)
// --- [etape 6] STOP -- coupe les sequenceurs --------------------------
(
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
// --- [etape 7] ARRET TOTAL (libere tout proprement) -------------------
// ~teardown.() vient de setup.scd : stop tous les Pdef/routines/LFO,
// libere ~masterLim + ~scBus + bus FX. A faire quand tu veux quitter.
( ~teardown.(); )
+76
View File
@@ -0,0 +1,76 @@
// =====================================================================
// examples/02_first_track.scd -- Lancer ta premiere track
//
// TITRE : Track A "Acid Journey" en autopilot
// DUREE : ~7 minutes (la duree complete de la track)
// NIVEAU : debutant
// PREREQUIS : evaluer CHARGER TOUT dans 00_load.scd
//
// BUT
// - Charger une track autonome (tracks/A_acid_journey.scd)
// - La lancer via le bloc principal de la track
// - Tweaker quelques params pendant qu'elle joue
// - Stop avec ~track.stop
// =====================================================================
// --- [etape 1] Verifier que le moteur est charge --------------------
// Si tu vois "engine pas charge", retourne dans 00_load.scd et
// evalue CHARGER TOUT.
(
if (~reload.isNil) {
"!!! engine pas charge -- evalue CHARGER TOUT dans 00_load.scd".postln;
} {
"engine OK -- pret a lancer la track".postln;
};
)
// --- [etape 2] Charger le fichier de la track -----------------------
// .load ne fait QUE definir ~track (Routine), il ne lance rien.
// Ouvre tracks/A_acid_journey.scd dans l'IDE pour voir le code.
(
(~base ++ "tracks/A_acid_journey.scd").load;
)
// -- attendre le post "[A 0:00]" : il indique que la track a demarre.
// ATTENTION : load() execute le contenu, donc la Routine .play est
// declenche. La track joue maintenant.
// --- [etape 3] (optionnel) Si tu prefere demarrer manuellement ------
// Ouvre tracks/A_acid_journey.scd, place ton curseur dans le bloc
// commencant par "( ~track !? { ~track.stop }; ~track = Routine({"
// et fais Cmd+Entree. C'est exactement ce que .load fait.
// --- [etape 4] TWEAK pendant que la track joue -----------------------
// La track a sa propre logique, mais tu peux toujours surcharger
// les variables globales. Ton override tient jusqu'au prochain
// "passage" interne de la track qui les reecrit.
// Boost le volume kick pendant la section acid rave
( ~kickAmp = 0.75; ~reload.value; "kickAmp boost".postln; )
// Casser la melodie : nouveau pattern aleatoire dans une scale
( ~melodyNotes = ~genMelody.(0.6, ~scaleMinorPent); ~reload.value; )
// Ajouter une harmonie a la tierce par-dessus
( ~enableHarmony.(\third); )
// Acidifier le filtre acid (resonance up)
( ~acidRq = 0.08; ~acidCutoffEnv = 5500; ~reload.value; )
// --- [etape 5] STOP de la track --------------------------------------
// ~track.stop arrete la Routine ; les Pdef continuent de jouer
// le dernier etat. Pour TOUT couper, voir [etape 6].
( ~track !? { ~track.stop; "track stoppee".postln }; )
// --- [etape 6] STOP complet (sequenceurs + track) -------------------
(
~track !? { ~track.stop };
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP COMPLET ===".postln;
)
@@ -0,0 +1,102 @@
// =====================================================================
// examples/03_multi_melody_tracks.scd -- Multi-pistes melodiques
//
// TITRE : Plusieurs voix melodiques en parallele
// DUREE : ~5 minutes
// NIVEAU : intermediaire
// PREREQUIS : CHARGER TOUT dans 00_load.scd (charge melodies/index.scd
// qui charge tracks.scd avec ses 3 pistes par defaut)
//
// BUT
// Le moteur a UNE voix melody (\melodySeq). Ce systeme s'AJOUTE
// en parallele : chaque piste \m1, \m2, \m3 a son propre Pdef
// independant + Event de parametres dans ~mel[\name].
//
// Helpers : ~melTrackOn / Off / Set / Solo / Harm / List
// =====================================================================
// --- [etape 1] Setup de base -- demarrer la rythmique --------------
// On garde le kick + hat du moteur, on coupe la melodie LEGACY (\melodySeq)
// pour que les nouvelles pistes soient seules a faire de la melodie.
(
~kickAmp = 0.55; ~hatAmp = 0.18;
~melodyAmp = 0; // mute la melodie legacy
~reload.value;
[\kickSeq, \hatSeq].do { |k| Pdef(k).play };
"rythmique en place -- prochaine etape : ~melTrackOn".postln;
)
// --- [etape 2] Lancer la piste m1 (lead pad par defaut) -------------
// m1 est preconfigure avec ~m1 (32 notes, scale minor pent).
( ~melTrackOn.(\m1); )
// --- [etape 3] Changer l'instrument de m1 -- warmPad ---------------
// ~melTrackSet rebuild le Pdef automatiquement.
( ~melTrackSet.(\m1, \inst, \warmPad); )
// --- [etape 4] Changer la melodie de m1 -- on prend ml5 (64 pas) ---
// ml5 est une melodie longue de 64 pas (palette/melodies/long.scd).
// .copy car on ne veut pas que les futures mutations modifient ~ml5.
( ~melTrackSet.(\m1, \notes, ~ml5.copy); )
// --- [etape 5] Lancer la piste m2 (counter pluck) -------------------
// m2 est preconfiguree pluck pan -0.3 (cote gauche).
( ~melTrackOn.(\m2); )
// --- [etape 6] Changer m2 en pluck rapide --------------------------
// On peut empiler les ~melTrackSet.
(
~melTrackSet.(\m2, \inst, \pluck);
~melTrackSet.(\m2, \dur, 0.125); // 1/32 (rapide)
~melTrackSet.(\m2, \pan, -0.5);
)
// --- [etape 7] Generer une harmonie automatique sur m1 -------------
// ~melTrackHarm.(source, dest, preset) cree une nouvelle piste
// "harm1" derivee de m1, harmonisee a la tierce.
// Presets : \third \fifth \counter \arp \chord \tranceLayer ...
( ~melTrackHarm.(\m1, \harm1, \third); )
// (variante) harmonie a la quinte au lieu de la tierce
// ( ~melTrackHarm.(\m1, \harm1, \fifth); )
// --- [etape 8] Solo / Unsolo (mute toutes sauf une) -----------------
( ~melTrackSolo.(\m1); ) // seule m1 est audible
// -- attendre 8 secondes
( ~melTrackUnsolo.(); )
// --- [etape 9] Lister toutes les pistes actives ---------------------
( ~melTrackList.(); )
// --- [etape 10] Tirer une melodie aleatoire par genre --------------
// Exemple : trance pour m1, oriental pour m2.
( ~melTrackByGenre.(\m1, \trance); )
( ~melTrackByGenre.(\m2, \oriental); )
// --- [etape 11] Stop des pistes melodiques --------------------------
(
~melTrackOff.(\m1);
~melTrackOff.(\m2);
~melTrackOff.(\harm1);
"melodic tracks OFF".postln;
)
// --- [etape 12] STOP complet (rythmique aussi) ---------------------
(
~melTrackChoke.(); // stop TOUTES les pistes melodiques
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP COMPLET ===".postln;
)
+117
View File
@@ -0,0 +1,117 @@
// =====================================================================
// examples/04_filter_lfo_vcf.scd -- Filtres et modulations
//
// TITRE : LFO sur variables + VCF master en chaine
// DUREE : ~5 minutes
// NIVEAU : intermediaire
// PREREQUIS : CHARGER TOUT (00_load.scd) PUIS evaluer le SETUP
// de live.scd (premier bloc top-level "(...)" du fichier)
//
// CONCEPTS
// - ~lfoTo modulation par LFO d'une variable d'environnement
// (rebuild les Pdef en boucle)
// - VCF master : un Synth qui filtre tout le bus 0 avant le
// master limiter. Reproduit un SH-101, MS-20, Moog, formant...
//
// Si tu vois "~lfoTo is not defined", c'est que live.scd n'a pas
// ete charge -- ouvre live.scd et evalue son SETUP.
// =====================================================================
// --- [etape 0] Verification ----------------------------------------
(
if (~lfoTo.isNil) {
"!!! live.scd pas charge -- ouvre live.scd et evalue son SETUP".postln;
} {
"live.scd OK".postln;
};
)
// --- [etape 1] Demarrer un beat techno minimal ---------------------
(
~kickAmp = 0.55; ~hatAmp = 0.18; ~acidAmp = 0.32; ~melodyAmp = 0.16;
~melodyInst = \saw3;
~reload.value;
[\kickSeq, \hatSeq, \acidSeq, \melodySeq].do { |k| Pdef(k).play };
"beat lance -- pret pour les LFO".postln;
)
// --- [etape 2] LFO sin sur le cutoff de la melodie ------------------
// ~lfoTo.(\param, rateHz, depth, shape, center)
// param = nom de variable d'environnement (sans tilde)
// rate = Hz (0.5 = lent, 4 = rapide)
// depth = amplitude de modulation (additionnee a center)
// shape = \sin \tri \saw \sqr \rand \smoothRand \drunk \noise
// center = valeur centrale (autour de laquelle le LFO oscille)
( ~lfoTo.(\melodyCutoff, 0.5, 1500, \sin, 2500); )
// -- la melodie respire (cutoff oscille entre 1000 et 4000 Hz).
// Laisser tourner 30s.
// --- [etape 3] LFO sqr sur l'ampli acid (gate rythmique) ------------
// shape \sqr cree un gate on/off rapide => effet trance gate.
( ~lfoTo.(\acidAmp, 4, 0.4, \sqr, 0.5); )
// --- [etape 4] LFO drunk sur le BPM (subtile derive) ----------------
// shape \drunk fait un random walk lent (jamais de saut brutal).
( ~lfoTo.(\bpm, 0.1, 8, \drunk, 132); )
// -- BPM oscille entre 124 et 140 lentement.
// --- [etape 5] STOP les LFO un par un ------------------------------
( ~lfoStopVar.(\melodyCutoff); )
( ~lfoStopVar.(\acidAmp); )
( ~lfoStopVar.(\bpm); )
// ou TOUT couper d'un coup :
// ( ~lfoStopAllVars.(); )
// --- [etape 6] VCF master Moog -- filtre tout le mix ----------------
// ~vcfOn.(\type, freq, q)
// types : \moog \ms20 \ladder \formant \comb \hpf \bpf \notch
( ~vcfOn.(\moog, 800, 0.4); )
// -- on entend une coupure douce ; tout le mix est plus sombre.
// --- [etape 7] Sweep automatique du cutoff (8s) --------------------
// ~vcfSweep.(fromHz, toHz, dur, curve)
( ~vcfSweep.(8000, 200, 8); )
// -- fait une descente filtre 8000 -> 200 Hz sur 8s. Effet "filter close".
// --- [etape 8] Couper le VCF (signal redevient brillant) ------------
( ~vcfOff.(); )
// --- [etape 9] VCF formant -- voyelle 'a' --------------------------
// Pour le formant, freq = INDICE de voyelle (0=a 1=e 2=i 3=o 4=u),
// q = resonance (0.05 a 1).
( ~vcfOn.(\formant, 0, 0.3); ) // 'a' large
// changer en voyelle 'i' (plus aigue)
( ~vcfSet.(\vowel, 2); )
// arreter
( ~vcfOff.(); )
// --- [etape 10] Preset VCF acid (Moog 600Hz, q tres haute) ---------
( ~vcfPreset.(\acid); )
// preset deep, dub, bright, dark, formantA-U
( ~vcfPreset.(\dub); )
( ~vcfPreset.(\bright); )
( ~vcfOff.(); )
// --- [etape 11] STOP global -----------------------------------------
(
~lfoStopAllVars.();
~vcfOff.();
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
@@ -0,0 +1,132 @@
// =====================================================================
// examples/05_drops_breakdowns_buildups.scd -- Tricks scéniques
//
// TITRE : Les "tricks" classiques d'une perf live
// DUREE : ~10 minutes (en testant tout)
// NIVEAU : intermediaire
// PREREQUIS : CHARGER TOUT (00_load.scd) + SETUP de live.scd
//
// BUT
// Helpers ergonomiques de live.scd pour les transitions :
// drop, breakdown, buildup, tape stop, freeze, glitch, stutter...
//
// Tous ces helpers sont des Routines : ils tournent jusqu'a la
// fin de la duree donnee, puis restaurent l'etat precedent
// automatiquement.
// =====================================================================
// --- [etape 0] Verification ----------------------------------------
(
if (~drop.isNil) {
"!!! live.scd pas charge -- ouvre live.scd et evalue son SETUP".postln;
} {
"live.scd OK".postln;
};
)
// --- [etape 1] Demarrer un beat de base ----------------------------
(
~kickAmp = 0.55; ~hatAmp = 0.18; ~melodyAmp = 0.18; ~acidAmp = 0.3;
~melodyInst = \lead;
~reload.value;
[\kickSeq, \hatSeq, \acidSeq, \melodySeq].do { |k| Pdef(k).play };
"beat lance -- pret pour les tricks".postln;
)
// -- laisse tourner 16-32 beats avant de tenter un drop.
// --- [etape 2] DROP -- silence sec puis blast (4s de buildup) -------
// ~drop.(buildDur)
// Coupe tout, monte un riser, puis re-explose la rythmique a fond.
( ~drop.(4); )
// -- attendre 8s, le drop fait son cycle complet.
// --- [etape 3] BREAKDOWN -- mute drums, garde melodie + reverb ------
// ~breakdown.(dur)
// Coupe kick + hat, ajoute reverb sur la melodie, restaure apres dur.
( ~breakdown.(8); )
// -- attendre 8s.
// --- [etape 4] BUILDUP -- crescendo qui monte sur dur secondes ------
// ~buildup.(dur)
// Snare roll + filtre qui s'ouvre + amp qui monte.
( ~buildup.(8); )
// --- [etape 5] HANDS UP -- cut sub + boost lead --------------------
// Effet typique festival : on enleve le bas + clap et on pousse le lead.
( ~handsUp.(); )
// -- attendre 4s.
// --- [etape 6] TAPE STOP -- ralentissement du tempo (2s) -----------
// ~tapeStop.(dur)
// Tempo descend a 0 sur dur secondes ; effet bande magnetique.
( ~tapeStop.(2); )
// --- [etape 7] FREEZE -- gel tout en reverb (4s) -------------------
// Reverb saturee + master mute = effet "vide aspirant" puis retour.
( ~freeze.(4); )
// --- [etape 8] GLITCH -- bitcrush + rate variations sur 2s ----------
( ~glitch.(2); )
// --- [etape 9] STUTTER -- repete tres vite a 8Hz pendant 1s --------
// ~stutter.(rateHz, dur)
( ~stutter.(8, 1); )
// -- on entend un effet "bbbbbbbb" tres serre.
// --- [etape 10] JUMP CUT -- silence sec de 0.5s --------------------
// Effet "pause" tres courte, comme une coupe au montage video.
( ~jumpCut.(0.5); )
// --- [etape 11] REVERB TAIL -- queue de reverb 4s ------------------
// Mute la rythmique, garde uniquement la queue de reverb.
( ~reverbTail.(4); )
// --- [etape 12] BIT CRUSH NOW -- bitcrush sur 1s ------------------
// ~bitCrushNow.(bits, sr, dur)
( ~bitCrushNow.(6, 6000, 1); )
// --- [etape 13] RING MOD -- mod en anneau a 200Hz pendant 1s ------
( ~ringMod.(200, 1); )
// --- [etape 14] TAPE REVERSE -- inverse la lecture 2s -------------
( ~tapeReverse.(2); )
// --- [etape 15] COMBO classique : breakdown -> buildup -> drop -----
// Chaine 3 helpers en sequence (separes par .wait).
// Ce bloc est un Routine asynchrone qui dure ~24 secondes au total.
(
Routine({
"[combo] BREAKDOWN".postln;
~breakdown.(8);
8.wait;
"[combo] BUILDUP".postln;
~buildup.(8);
8.wait;
"[combo] DROP".postln;
~drop.(4);
}).play;
)
// --- [etape 16] STOP global ----------------------------------------
(
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
@@ -0,0 +1,129 @@
// =====================================================================
// examples/06_drum_kits_sequences.scd -- Kits + sequences
//
// TITRE : Macros pour swap de kit + sequences scenaristiques
// DUREE : ~6 minutes
// NIVEAU : debutant
// PREREQUIS : CHARGER TOUT (00_load.scd)
//
// BUT
// palette/rhythm/drum_kits/ : 21 kits complets (Techno, DnB, Phonk, ...)
// -- chaque kit est un fichier qui set ~kickSteps + ~hatSteps + ...
// et appelle ~reload.value
// palette/rhythm/sequences/ : macros de sequences pretes a l'emploi
// -- chaque seq est une Routine qui orchestre transitions + tempo
//
// Le _index.scd charge fills + sequences. Les drum_kits restent
// evalues a la demande (Cmd+Entree sur le fichier d'un kit).
// =====================================================================
// --- [etape 1] Charger sequences + un kit au choix -----------------
(
(~base ++ "palette/rhythm/_index.scd").load; // fills + sequences
(~base ++ "palette/rhythm/drum_kits/kit_techno.scd").load; // exemple kit
"sequences + kit techno charges".postln;
)
// --- [etape 2] Lancer le moteur -----------------------------------
(
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).play };
"PLAY".postln;
)
// --- [etape 3] KIT TECHNO -- 4/4 classique -------------------------
// Le kit set kickSteps + hatSteps + amp, puis ~reload.value.
// On entend tout de suite la diff.
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.55;
~kickFreq = 55; ~kickPitchAmt = 3.0; ~kickDecay = 0.32;
~kickDrive = 1.3; ~kickClick = 0.2;
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.18;
~snareSteps = (0 ! 16); ~clapSteps = (0 ! 16); ~percSteps = (0 ! 16);
~reload.value; "=== KIT TECHNO ===".postln;
)
// -- ecouter 16 beats puis passer au kit suivant.
// --- [etape 4] KIT DUBSTEP -- kick lourd + snare backbeat ----------
(
~kickSteps = [1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0]; ~kickAmp = 0.6;
~kickFreq = 48; ~kickPitchAmt = 3.5; ~kickDecay = 0.5;
~kickDrive = 1.5; ~kickClick = 0.15;
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.5;
~snareFreq = 220; ~snareNoiseAmt = 0.65; ~snareDecay = 0.16;
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; ~hatAmp = 0.10;
~clapSteps = (0 ! 16); ~percSteps = (0 ! 16);
~reload.value; "=== KIT DUBSTEP ===".postln;
)
// --- [etape 5] KIT PHONK -- cowbell + 808 + pitched snare ----------
// Ce kit existe dans drum_kits.scd ; on peut aussi recharger
// directement le fichier et evaluer le bloc dedie.
(
~kickSteps = [1,0,0,0, 0,0,1,0, 1,0,0,0, 0,0,1,0]; ~kickAmp = 0.55;
~kickFreq = 42; ~kickPitchAmt = 2.5; ~kickDecay = 0.6;
~kickDrive = 1.2; ~kickClick = 0.05; // 808 boomy
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,1]; ~snareAmp = 0.5;
~snareFreq = 280; ~snareNoiseAmt = 0.4; ~snareDecay = 0.08;
~hatSteps = [1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0]; ~hatAmp = 0.12;
~percInst = \cowbell;
~percSteps = [0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,1,0]; ~percAmp = 0.2;
~clapSteps = (0 ! 16);
~reload.value; "=== KIT PHONK ===".postln;
)
// --- [etape 6] Couper le moteur avant de tester les sequences -----
// Les ~seqXxx prennent le controle ; on coupe les Pdef pour eviter
// les conflits avec leur orchestration interne.
(
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"sequenceurs stoppes -- pret pour ~seqXxx".postln;
)
// --- [etape 7] ~seqIntro -- montee progressive (~30s) --------------
( ~seqIntro.(); )
// -- la sequence dure ~30 secondes ; attendre la fin.
// --- [etape 8] ~seqBuildup.(bars) -- crescendo trance --------------
// .(8) = 8 mesures. Snare roll + filtre qui s'ouvre + amp up.
( ~seqBuildup.(8); )
// --- [etape 9] ~seqDrop -- drop techno (silence sec + blast) -------
( ~seqDrop.(); )
// --- [etape 10] ~seqTechnoLoop.(repeats) -- N pattern techno -------
( ~seqTechnoLoop.(4); )
// -- joue 4 iterations d'un pattern techno classique.
// --- [etape 11] ~seqDubstepDrop -- drop dubstep ---------------------
( ~seqDubstepDrop.(); )
// --- [etape 12] ~seqAcidEvolve -- acid qui mute progressivement ----
// Genere ~30s d'acid avec mutations toutes les 4 mesures.
( ~seqAcidEvolve.(); )
// --- [etape 13] STOP la sequence en cours --------------------------
( ~seqStop.(); )
// --- [etape 14] STOP global ----------------------------------------
(
~seqStop.();
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
+118
View File
@@ -0,0 +1,118 @@
// =====================================================================
// examples/07_loops_manager.scd -- Gestionnaire de boucles
//
// TITRE : Demarrer/stopper des Pdef en boucles propres
// DUREE : ~6 minutes
// NIVEAU : intermediaire
// PREREQUIS : CHARGER TOUT (00_load.scd)
// + ouvrir loops.scd et evaluer son SETUP (1er bloc top-level)
//
// CONCEPTS
// Le moteur expose 8 Pdef (~kickSeq ... ~harmonySeq) que l'on lance
// classiquement avec Pdef(\xxx).play. loops.scd est une sur-couche
// qui te permet de :
// - Nommer chaque boucle (\kick1, \hat1, \verse, \drop ...)
// - Limiter la duree (en beats / bars / repetitions)
// - Chainer / parall des boucles
// - Snapshot l'etat des params au moment du lancement
// - Fade in / out + countdown
// =====================================================================
// --- [etape 0] Verification ----------------------------------------
(
if (~loopOn.isNil) {
"!!! loops.scd pas charge -- ouvre loops.scd et evalue son SETUP".postln;
} {
"loops.scd OK".postln;
};
)
// --- [etape 1] Demarrer kick + hat en boucle ----------------------
// ~loopOn.(name, source) ; source = symbol Pdef ou Function
(
~kickAmp = 0.55; ~hatAmp = 0.18; ~reload.value;
~loopOn.(\kick1, \kickSeq);
~loopOn.(\hat1, \hatSeq);
)
// --- [etape 2] Lister les boucles actives --------------------------
( ~loopList.(); )
// --- [etape 3] Repeat snare 4 fois sur 4 secondes chacune ----------
// ~loopRepeat.(name, source, n, dur)
// Demarre snareSeq, joue 4 cycles de 4s, puis stop auto.
(
~snareSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~snareAmp = 0.5;
~reload.value;
~loopRepeat.(\snare1, \snareSeq, 4, 4);
)
// -- attendre 16s. Apres, le snare s'arrete tout seul.
// --- [etape 4] Acid sur 32 beats puis stop --------------------------
// ~loopBeats.(name, source, beats)
( ~loopBeats.(\acid1, \acidSeq, 32); )
// -- 32 beats = 8 mesures 4/4. Stop automatique.
// --- [etape 5] Lancer m1 avec un countdown 4 beats -----------------
// ~loopWithCountdown.(name, source, beats, max)
// Affiche "4...3...2...1..." dans la console puis lance le Pdef.
// max = inf -> boucle infinie apres le countdown.
( ~loopWithCountdown.(\m1main, \melodySeq, 4, inf); )
// --- [etape 6] Fade-out de la kick sur 4 secondes ------------------
( ~loopFade.(\kick1, 4); )
// -- la kick descend lineairement vers 0 et se libere.
// --- [etape 7] Chainer 3 boucles dans l'ordre ----------------------
// ~loopChain.([[name, source, beats], ...])
// -- chaque entry joue beats puis declenche la suivante.
// Avant : on coupe ce qui tourne pour avoir un demarrage propre.
( ~loopOffAll.(); )
(
~loopChain.([
[\a, \kickSeq, 8], // 8 beats kick seul
[\b, \melodySeq, 16], // 16 beats melodie
[\c, \hatSeq, 8], // 8 beats hat
]);
)
// -- duree totale = 32 beats. Suivre dans la console.
// --- [etape 8] Boucles paralleles (toutes en meme temps) ----------
( ~loopOffAll.(); )
(
~loopParallel.([
[\k, \kickSeq],
[\h, \hatSeq],
[\m, \melodySeq],
]);
)
// --- [etape 9] Voir la progression en temps reel -------------------
// Utile quand tu as des ~loopBeats / ~loopRepeat en cours.
( ~loopProgress.(); )
// --- [etape 10] ~allOn / ~allOff -- les 8 voies du moteur ----------
( ~loopOffAll.(); )
( ~allOn.(); ) // les 8 Pdef du moteur en boucle
// -- attendre 8s
( ~allOff.(); ) // les 8 stop
// --- [etape 11] STOP global ----------------------------------------
(
~loopOffAll.();
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
+96
View File
@@ -0,0 +1,96 @@
// =====================================================================
// examples/08_jump_sections.scd -- Naviguer dans les tracks
//
// TITRE : ~jumpTo -- aller direct a une section de track
// DUREE : ~5 minutes
// NIVEAU : intermediaire
// PREREQUIS : CHARGER TOUT (00_load.scd)
// + ouvrir jump.scd, evaluer SETUP JUMP (1er bloc) et
// evaluer le bloc ~sections['A'] = (...) pour A
//
// CONCEPTS
// jump.scd indexe ~sections[trackLetter][slug] -> { ...code... }
// pour chaque section de chaque track. Utile pour :
// - Tester rapidement le drop d'une track sans attendre 4 minutes
// - Faire un medley en alternant les sections de plusieurs tracks
// - Skip-mode pendant une perf live
// =====================================================================
// --- [etape 0] Verification ----------------------------------------
(
if (~jumpTo.isNil) {
"!!! jump.scd pas charge -- ouvre jump.scd et evalue SETUP JUMP".postln;
} {
if (~sections['A'].isNil) {
"!!! ~sections['A'] vide -- evalue le bloc 'TRACK A' de jump.scd".postln;
} {
"jump.scd + sections A OK".postln;
};
};
)
// --- [etape 1] Saut direct sur l'intro de A ------------------------
// ~jumpTo.(letter, slug) -- letter = 'A' (string char) ou \A
// Le slug doit correspondre a une cle de ~sections['A'].
( ~jumpTo.('A', 'minimal_kick_t5_sub_melody_pad'); )
// -- on entend immediatement le 1er moment de A (intro pad lent).
// Laisser tourner ~30s.
// --- [etape 2] Saut direct sur l'acid rave (drop de A) -------------
( ~jumpTo.('A', 'acid_rave_kick_t1_melody_lead_variations'); )
// -- le drop arrive instantanement, sans attendre les 2 minutes
// d'intro normale.
// --- [etape 3] Detroit stabs (3e section) --------------------------
( ~jumpTo.('A', 'detroit_stabs_kick_t2_melody_stab'); )
// --- [etape 4] Hard push (4e section) ------------------------------
( ~jumpTo.('A', 'hard_push_kick_t4_distordu'); )
// --- [etape 5] Outro -----------------------------------------------
( ~jumpTo.('A', 'outro_melody_pad_qui_s_efface'); )
// --- [etape 6] MEDLEY -- comparer des sections rapidement ----------
// On joue 4 sections, ~10s chacune, dans une Routine.
// Utile pour audition rapide.
(
Routine({
"[medley] intro".postln;
~jumpTo.('A', 'minimal_kick_t5_sub_melody_pad');
10.wait;
"[medley] acid rave".postln;
~jumpTo.('A', 'acid_rave_kick_t1_melody_lead_variations');
10.wait;
"[medley] detroit".postln;
~jumpTo.('A', 'detroit_stabs_kick_t2_melody_stab');
10.wait;
"[medley] hard push".postln;
~jumpTo.('A', 'hard_push_kick_t4_distordu');
10.wait;
"[medley] OUTRO".postln;
~jumpTo.('A', 'outro_melody_pad_qui_s_efface');
}).play;
)
// --- [etape 7] (bonus) Comparer la meme section de plusieurs tracks
// Ouvre jump.scd, evalue le bloc ~sections['B'] et ~sections['F'].
// Puis :
// ( ~jumpTo.('B', 'dub_start_kick_t3_boomy_melody_pad'); )
// ( ~jumpTo.('F', '...'); )
// (les slugs disponibles sont commentes en bas de chaque bloc de jump.scd)
// --- [etape 8] STOP ------------------------------------------------
(
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
+88
View File
@@ -0,0 +1,88 @@
// =====================================================================
// examples/09_scenes_recall.scd -- Save / recall scenes
//
// TITRE : Snapshot / restore de l'etat global
// DUREE : ~5 minutes
// NIVEAU : intermediaire
// PREREQUIS : CHARGER TOUT (00_load.scd)
// + ouvrir scenes.scd, evaluer son SETUP (1er bloc)
//
// CONCEPTS
// Une "scene" est un snapshot de TOUTES les variables d'environnement
// listees dans ~sceneKeys (kickAmp, melodyNotes, melodyCutoff...).
// Tu peux la sauvegarder avec ~saveScene.(\nom), la rappeler avec
// ~loadScene.(\nom), et persister le tout sur disque.
// =====================================================================
// --- [etape 0] Verification ----------------------------------------
(
if (~saveScene.isNil) {
"!!! scenes.scd pas charge -- ouvre scenes.scd et evalue son SETUP".postln;
} {
"scenes.scd OK".postln;
};
)
// --- [etape 1] Lancer une track A pour avoir un point de depart ----
// Charge tracks/A_acid_journey.scd qui set tous les params ET
// demarre les Pdef. La track passe en "minimal" pour les ~30 1eres s.
(
(~base ++ "tracks/A_acid_journey.scd").load;
)
// -- attendre 5s que les params soient stables.
// --- [etape 2] SAVE l'etat actuel comme \verseA --------------------
( ~saveScene.(\verseA); )
// --- [etape 3] Modifier les params (kick + cutoff + melodie) -------
(
~kickAmp = 0.8;
~melodyCutoff = 6500;
~melodyNotes = ~genMelody.(0.7, ~scaleMinor);
~reload.value;
"params modifies".postln;
)
// --- [etape 4] SAVE cet etat comme \verseAvariant ------------------
( ~saveScene.(\verseAvariant); )
// --- [etape 5] LOAD \verseA -- on revient a l'etat initial ---------
( ~loadScene.(\verseA); )
// -- on entend les params revenir : kick 0.5, cutoff 1500, etc.
// --- [etape 6] LOAD \verseAvariant -- retour a la variante --------
( ~loadScene.(\verseAvariant); )
// --- [etape 7] Lister les scenes sauvegardees ----------------------
( ~listScenes.value; )
// --- [etape 8] Supprimer une scene ---------------------------------
( ~deleteScene.(\verseAvariant); )
( ~listScenes.value; )
// --- [etape 9] Persistence sur disque ------------------------------
// ~saveScenesToDisk ecrit dans scenes_saved.scd a la racine du projet.
// ~loadScenesFromDisk recharge depuis ce fichier.
( ~saveScenesToDisk.value; )
// -- on peut quitter SuperCollider, relancer la session, recharger
// CHARGER TOUT + scenes.scd, puis :
( ~loadScenesFromDisk.value; )
// --- [etape 10] STOP -----------------------------------------------
(
~track !? { ~track.stop };
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
@@ -0,0 +1,95 @@
// =====================================================================
// examples/10_midi_controller.scd -- Setup MIDI
//
// TITRE : Mapper un controller MIDI sur les variables
// DUREE : ~3 minutes
// NIVEAU : avance
// PREREQUIS : CHARGER TOUT (00_load.scd)
// + un controller MIDI USB branche
//
// CONCEPTS
// midi.scd cree des MIDIFunc.cc(...) qui :
// 1. Recoivent un CC du controller
// 2. Mappent la valeur 0-127 sur une plage utile (linlin / linexp)
// 3. Set la variable d'environnement (~kickAmp, ~melodyCutoff, ...)
// 4. Appellent ~debouncedReload (50ms debounce -> evite le spam)
//
// Le mapping de defaut cible un Launch Control XL (8 fad + 8 pots),
// adapte les numeros CC selon ton controller.
// =====================================================================
// --- [etape 0] Verification ----------------------------------------
(
if (~reload.isNil) {
"!!! engine pas charge -- evalue CHARGER TOUT dans 00_load.scd".postln;
} {
"engine OK".postln;
};
)
// --- [etape 1] INIT MIDI -- lister les devices --------------------
// Reproduit le 1er bloc de control/midi.scd. La console affiche les sources
// et destinations. Si tu vois ton controller, c'est OK.
(
MIDIClient.init;
"=== MIDI sources disponibles ===".postln;
MIDIClient.sources.do { |src, i|
(" [" ++ i ++ "] " ++ src.device ++ " : " ++ src.name).postln;
};
"=== MIDI destinations ===".postln;
MIDIClient.destinations.do { |dst, i|
(" [" ++ i ++ "] " ++ dst.device ++ " : " ++ dst.name).postln;
};
MIDIIn.connectAll;
"=== MIDIIn.connectAll OK ===".postln;
)
// --- [etape 2] MAPPING DEFAULT -- charger control/midi.scd --------
// .load execute le 2e bloc de control/midi.scd qui cree ~midiFuncs
// avec les 16 mappings standards (CC 13-20 = volumes, CC 29-36 =
// filtres, CC 49-56 = wobble/sidechain, notes 36-42 = oneshot FX).
(
(~base ++ "control/midi.scd").load;
)
// -- la console doit afficher "=== MIDI MAPPING ACTIF ===".
// --- [etape 3] Demarrer un beat pour entendre les changements -----
(
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).play };
)
// -- bouge tes potards : CC 13 = kickAmp, CC 29 = melodyCutoff, etc.
// --- [etape 4] DEBUG -- afficher tous les CC recus ----------------
// Utile pour decouvrir les numeros CC envoyes par TON controller.
(
~midiDebug = MIDIFunc.cc({ |val, num, chan|
("[CC] " ++ num ++ " = " ++ val ++ " ch" ++ chan).postln;
});
"MIDI debug ON -- bouge un potard".postln;
)
// --- [etape 5] Couper le debug -------------------------------------
( ~midiDebug !? { ~midiDebug.free; ~midiDebug = nil }; "MIDI debug OFF".postln; )
// --- [etape 6] STOP MIDI -- libere tous les MIDIFunc --------------
(
~midiFuncs !? { ~midiFuncs.do { |f| f.free }; ~midiFuncs = nil };
~midiDebug !? { ~midiDebug.free; ~midiDebug = nil };
"=== MIDI STOPPED ===".postln;
)
// --- [etape 7] STOP global ----------------------------------------
(
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
+87
View File
@@ -0,0 +1,87 @@
// =====================================================================
// examples/11_playlist_auto.scd -- Session auto playlist
//
// TITRE : Lancer plusieurs tracks d'affilee sans rien faire
// DUREE : variable (selon la playlist choisie)
// NIVEAU : debutant
// PREREQUIS : CHARGER TOUT (00_load.scd)
// + ouvrir playlist.scd, evaluer son SETUP (1er bloc)
//
// CONCEPTS
// ~playPlaylist.([[file, dur], ...]) joue chaque track pendant
// "dur" secondes, puis crossfade vers la suivante.
// Pendant que ca tourne, tu peux SKIP, PAUSE, RESUME.
// =====================================================================
// --- [etape 0] Verification ----------------------------------------
(
if (~playPlaylist.isNil) {
"!!! playlist.scd pas charge -- ouvre playlist.scd et evalue son SETUP".postln;
} {
"playlist.scd OK".postln;
};
)
// --- [etape 1] PLAYLIST DEMO -- 4 tracks de 90s chacune (6 min) ----
// Custom playlist courte pour tester. Pour les playlists 1h+ voir
// playlist.scd (PLAYLIST INTEGRALE, DANCE FLOOR, CHILL, ...).
(
~playPlaylist.([
["A_acid_journey.scd", 90],
["F_trance_uplifter.scd", 90],
["G_detroit_soulful.scd", 90],
["W_dub_techno.scd", 90],
]);
)
// -- la 1ere track demarre. Toutes les 90s, on enchaine la suivante
// avec un crossfade.
// --- [etape 2] INFO -- combien de synths actifs / CPU --------------
// A relancer ponctuellement pendant la playlist.
( ("synths actifs: " ++ s.numSynths ++ " | CPU: " ++ s.avgCPU.round(0.1) ++ "%").postln; )
// --- [etape 3] PAUSE -- mute le master limiter ---------------------
// Pratique pour repondre a un appel sans tout couper.
( ~masterLim !? { ~masterLim.run(false) }; "[PAUSE]".postln; )
// --- [etape 4] RESUME ---------------------------------------------
( ~masterLim !? { ~masterLim.run(true) }; "[RESUME]".postln; )
// --- [etape 5] SKIP TRACK -- passe a la suivante immediatement -----
// Stop la track + tous les Pdef ; la playlist reprend le controle
// et lance la track suivante au prochain tick.
(
~track !? { ~track.stop };
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"[SKIP] passage a la track suivante".postln;
)
// --- [etape 6] STOP PLAYLIST ---------------------------------------
// Stoppe la playlist, la track en cours, et les Pdef.
(
~playlist !? { ~playlist.stop; ~playlist = nil };
~track !? { ~track.stop; ~track = nil };
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== PLAYLIST STOPPED ===".postln;
)
// --- [etape 7] (variantes) Lancer une playlist deja definie -------
// Ouvre playlist.scd et evalue le bloc qui te plait :
// - PLAYLIST INTEGRALE (~1h30 de tout)
// - PLAYLIST DANCE FLOOR (~1h30 dansant)
// - PLAYLIST CHILL (~1h ambient)
// - PLAYLIST BASS MUSIC (~1h dubstep + dnb)
// - PLAYLIST HARD (~50 min hardcore)
// - PLAYLIST WORLD (~50 min asia + tribal)
// - PLAYLIST RETRO (~30 min synthwave)
// - PLAYLIST EXPERIMENTAL (~38 min glitch + drone)
@@ -0,0 +1,126 @@
// =====================================================================
// examples/12_chord_progressions.scd -- Progressions d'accords
//
// TITRE : Generer melodie/harmonie depuis ~progXxx
// DUREE : ~5 minutes
// NIVEAU : intermediaire
// PREREQUIS : CHARGER TOUT (00_load.scd) -- charge automatiquement
// palette/chords_progressions.scd via melodies/index.scd ?
// NON -- il faut le charger separement :
(
(~base ++ "palette/chords_progressions.scd").load;
)
//
// CONCEPTS
// ~progXxx = [[c1n1, c1n2, c1n3], [c2n1, c2n2, c2n3], ...]
// -- tableau d'accords, chaque accord = liste de notes MIDI
// ~chordsToMelody.(prog, stepsPerChord) deroule la prog en melodie
// ~chordsToHarmony.(prog, stepsPerChord) deroule la prog en harmony
//
// PROGRESSIONS DISPO : ~progIIVI ~progPop ~progSadPop ~progEpicMin
// ~progAndalou ~progFlamenco ~progPhrygien ~progLydien ~progDorien
// ~progHungar ~progHijaz ~progVietnam ~progHirajoshi ~progDetroit
// ~progTrance ~progDnB ~progDubstep ~progSynthwave ~progAmbient
// (voir palette/chords_progressions.scd pour la liste complete)
// =====================================================================
// --- [etape 1] Demarrer un beat de base -------------------------
(
~kickAmp = 0.55; ~hatAmp = 0.18; ~melodyAmp = 0.18;
~melodyInst = \warmPad;
~melodyAttack = 0.4; ~melodyRelease = 1.5;
~reload.value;
[\kickSeq, \hatSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).play };
)
// --- [etape 2] Detroit prog -- soulful Cm-Eb-Ab-Bb -----------------
(
~melodyNotes = ~chordsToMelody.(~progDetroit);
~reload.value;
"=== prog DETROIT ===".postln;
)
// -- on entend la melodie deroulee depuis la prog. Laisser 16-32 beats.
// --- [etape 3] Trance prog (i-VII-VI-VII) --------------------------
(
~melodyNotes = ~chordsToMelody.(~progTrance);
~reload.value;
"=== prog TRANCE ===".postln;
)
// --- [etape 4] Pop prog (axis of awesome : I-V-vi-IV) -------------
(
~melodyNotes = ~chordsToMelody.(~progPop);
~reload.value;
"=== prog POP ===".postln;
)
// --- [etape 5] Andalouse prog (i-VI-III-VII en Am) -----------------
(
~melodyNotes = ~chordsToMelody.(~progAndalou);
~reload.value;
"=== prog ANDALOU ===".postln;
)
// --- [etape 6] Hijaz prog (gamme arabe) ----------------------------
(
~melodyNotes = ~chordsToMelody.(~progHijaz);
~reload.value;
"=== prog HIJAZ ===".postln;
)
// --- [etape 7] Synthwave prog (Am-F-G-Am 80s) ----------------------
(
~melodyNotes = ~chordsToMelody.(~progSynthwave);
~reload.value;
"=== prog SYNTHWAVE ===".postln;
)
// --- [etape 8] DnB prog (Em melodique) -----------------------------
(
~melodyNotes = ~chordsToMelody.(~progDnB);
~reload.value;
"=== prog DnB ===".postln;
)
// --- [etape 9] Empiler une harmonie en plus (depuis la meme prog) --
// ~chordsToHarmony empile toutes les notes de chaque accord =>
// genere des "stabs" d'accords joues a 1/4, par dessus la melodie.
(
~harmonyInst = \pad;
~harmonyAmp = 0.16;
~harmonyDur = 1.0; // 1 noir = 1 stab par beat
~harmonyNotes = ~chordsToHarmony.(~progSynthwave);
~reload.value;
"=== harmony SYNTHWAVE empilee ===".postln;
)
// --- [etape 10] Trance + Trance harmony (full layered) -------------
(
~melodyNotes = ~chordsToMelody.(~progTrance);
~harmonyNotes = ~chordsToHarmony.(~progTrance);
~reload.value;
"=== prog TRANCE full layered ===".postln;
)
// --- [etape 11] Couper l'harmonie ----------------------------------
( ~harmonyAmp = 0; ~reload.value; "harmony OFF".postln; )
// --- [etape 12] STOP global -----------------------------------------
(
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
+128
View File
@@ -0,0 +1,128 @@
// =====================================================================
// examples/13_world_ethno.scd -- Workflow oriental / world / asia
//
// TITRE : Construire un morceau ethnique
// DUREE : ~6 minutes
// NIVEAU : intermediaire
// PREREQUIS : CHARGER TOUT (00_load.scd) charge synth/_index.scd
// qui inclut taiko, didgeridoo, guzheng, shakuhachi, tabla
// ainsi que toute la palette asia (koto, erhu, pipa, ...)
// + palette/chords_progressions.scd pour ~progHijaz +
// ~progVietnam (a charger separement) :
(
(~base ++ "palette/chords_progressions.scd").load;
(~base ++ "palette/rhythm/_index.scd").load;
)
//
// PALETTE
// asia : \koto \erhu \pipa \gong \growl \reeseHard
// authentic : \didgeridoo \guzheng \shakuhachi \taiko \tabla
// progressions : ~progHijaz ~progVietnam ~progHirajoshi
// melodies : ~melodyByGenre.(\oriental | \asian | \tribal)
// =====================================================================
// --- [etape 1] Demarrer un beat tribal de base --------------------
// Kick lent + tabla sur les contre-temps.
(
~bpm = 92;
TempoClock.default.tempo = ~bpm / 60;
~kickAmp = 0.45; ~hatAmp = 0.0;
~kickFreq = 50; ~kickDecay = 0.5;
~percInst = \tabla; // tabla en lieu de rim/cowbell
~percSteps = [0,0,1,0, 1,0,1,0, 0,1,0,1, 1,0,0,1]; ~percAmp = 0.4;
~melodyAmp = 0.18;
~reload.value;
[\kickSeq, \percSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).play };
"beat tribal en place".postln;
)
// --- [etape 2] Melodie au koto + scale orientale ------------------
// ~m17 est une melodie asian dans palette/melodies/short.scd.
(
~melodyInst = \koto;
~melodyAttack = 0.005; ~melodyRelease = 0.4;
~melodyCutoff = 5500; ~melodyAmp = 0.2;
~melodyNotes = ~m17.copy;
~reload.value;
"=== melodie KOTO -- ~m17 ===".postln;
)
// --- [etape 3] Tirer une melodie aleatoire dans le bank \oriental --
// ~melodyByGenre.(\oriental) lit ~melodyBank[\oriental] (10 melodies)
// et applique une au hasard.
( ~melodyByGenre.(\oriental); )
// -- relancer plusieurs fois pour decouvrir d'autres melodies.
// --- [etape 4] Couche basse au didgeridoo (drone bourdonnant) -----
// On utilise la voix harmony : pose un drone profond sur la fond.
(
~harmonyInst = \didgeridoo;
~harmonyAttack = 0.05; ~harmonyRelease = 1.5;
~harmonyAmp = 0.22;
~harmonyDur = 1.0;
~enableHarmony.(\bassline); // genere une bassline depuis melody
~reload.value;
"=== didgeridoo bassline ===".postln;
)
// --- [etape 5] Reemplacer perc par taiko -------------------------
// Le taiko sonne plus martial, parfait pour buildup.
(
~percInst = \taiko;
~percSteps = [1,0,0,1, 0,0,1,0, 1,0,0,1, 0,1,0,0]; ~percAmp = 0.5;
~reload.value;
"=== taiko perc ===".postln;
)
// --- [etape 6] Melodie depuis prog Hijaz (gamme arabe) ------------
// chordsToMelody deroule les notes des accords sur 32 pas.
(
~melodyNotes = ~chordsToMelody.(~progHijaz);
~melodyInst = \shakuhachi; // flute japonaise
~reload.value;
"=== shakuhachi -- prog HIJAZ ===".postln;
)
// --- [etape 7] Melodie depuis prog Vietnam ------------------------
(
~melodyNotes = ~chordsToMelody.(~progVietnam);
~melodyInst = \erhu; // violon chinois
~melodyAttack = 0.2; ~melodyRelease = 0.6;
~reload.value;
"=== erhu -- prog VIETNAM ===".postln;
)
// --- [etape 8] ~seqWorldEvolve -- sequence world toute prete ------
// Avant : on coupe la rythmique, la sequence prend le controle.
(
[\kickSeq, \percSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
~seqWorldEvolve.();
"=== seqWorldEvolve ===".postln;
)
// -- Routine de ~30s qui fait evoluer le mix world.
// --- [etape 9] VCF formant -- voyelles pour le coté incantatoire --
// live.scd doit avoir ete setup avant cette etape (sinon ~vcfOn nil).
( ~vcfOn !? { ~vcfOn.(\formant, 0, 0.2) }; ) // 'a' large
// -- changer de voyelle avec ~vcfSet
( ~vcfSet !? { ~vcfSet.(\vowel, 2) }; ) // 'i' aigue
( ~vcfSet !? { ~vcfSet.(\vowel, 3) }; ) // 'o' rond
( ~vcfOff !? { ~vcfOff.() }; )
// --- [etape 10] STOP global ----------------------------------------
(
~seqStop !? { ~seqStop.() };
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
@@ -0,0 +1,114 @@
// =====================================================================
// examples/14_fx_chains_dynamic.scd -- FX bus dynamique
//
// TITRE : Bus FX en send + LFO sur params FX + presets
// DUREE : ~6 minutes
// NIVEAU : avance
// PREREQUIS : CHARGER TOUT (00_load.scd)
// + ouvrir fx_bus.scd, evaluer son SETUP (3 blocs en haut)
//
// ATTENTION
// fx_bus.scd a une signature different de live.scd :
// ~send.(fxName, amount) -- envoie LE BUS 0 ENTIER vers le FX
// (donc tout le mix passe dedans)
// ~setFx.(fxName, key, val) -- modifie un parametre du FX
// ~lfo.(fxName, key, rate, lo, hi, shape) -- LFO sur un param FX
//
// FX dispos : \rev \dly \chr \phs \flg \crsh \tape \shim \dist
// Presets : \dub \space \trance \dubstep \retro \industrial
// \dream \cosmic \drone \dry
// =====================================================================
// --- [etape 0] Verification ----------------------------------------
(
if (~send.isNil or: { ~fxFx.isNil }) {
"!!! fx_bus.scd pas charge -- ouvre fx_bus.scd et evalue ses 3 SETUP".postln;
} {
"fx_bus.scd OK".postln;
};
)
// --- [etape 1] Demarrer un beat de base ----------------------------
(
~kickAmp = 0.55; ~hatAmp = 0.18; ~melodyAmp = 0.18; ~acidAmp = 0.32;
~melodyInst = \lead;
~reload.value;
[\kickSeq, \hatSeq, \acidSeq, \melodySeq].do { |k| Pdef(k).play };
"beat lance".postln;
)
// --- [etape 2] Preset DUB -- reverb hall + delay long + tape ------
( ~fxPreset.(\dub); )
// -- on entend tout le mix prendre une couleur dub. Laisser 16 beats.
// --- [etape 3] Reverb seule a 40% ---------------------------------
// ~send.(fxName, amount) avec amount = 0.4 -> 40% du mix part dans
// le bus reverb (reste 100% en dry).
( ~send.(\rev, 0.4); )
// Couper le delay du preset DUB (pour ne garder que la reverb)
( ~send.(\dly, 0); )
( ~send.(\tape, 0); )
// --- [etape 4] Delay 1/8 (375ms) avec feedback 50% ---------------
(
~send.(\dly, 0.3);
~setFx.(\dly, \delayTime, 0.375); // 1/8 a 120 BPM
~setFx.(\dly, \feedback, 0.5);
"delay 1/8 fb 50%".postln;
)
// --- [etape 5] LFO sur le feedback du delay -----------------------
// ~lfo.(fxName, param, rateHz, lo, hi, shape)
// Le feedback oscille entre 30% et 80% a 0.2Hz (cycle 5s).
( ~lfo.(\dly, \feedback, 0.2, 0.3, 0.8, \sin); )
// -- on entend le delay grossir/decroitre. Laisser tourner 30s.
// --- [etape 6] Stop le LFO sur ce param ---------------------------
( ~lfoStop.(\dly, \feedback); )
// --- [etape 7] Reverb FREEZE -- nappe figee infinie ---------------
( ~setFx.(\rev, \freeze, 1); "REV FREEZE ON".postln; )
// ... ecoute la nappe, joue avec d'autres params
( ~setFx.(\rev, \freeze, 0); "REV FREEZE OFF".postln; )
// --- [etape 8] Tap tempo -- ajuste le delay au tempo qu'on tape ---
// Tape ~tap au moins 3 fois pour calibrer ; il calcule la moyenne
// des intervalles et ajuste ~setFx.(\dly, \delayTime, ...).
( ~tap.(); )
( ~tap.(); )
( ~tap.(); )
( ~tap.(); )
// --- [etape 9] Preset SPACE -- shimmer + reverb + chorus ----------
( ~fxPreset.(\space); )
// --- [etape 10] Preset INDUSTRIAL -- distortion + flanger -------
( ~fxPreset.(\industrial); )
// --- [etape 11] Preset DRY -- mute tous les sends -----------------
( ~fxPreset.(\dry); )
// --- [etape 12] STOP global ----------------------------------------
(
~lfoStopAll !? { ~lfoStopAll.() };
~fxPreset.(\dry);
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"=== STOP ===".postln;
)
+274
View File
@@ -0,0 +1,274 @@
// =====================================================================
// examples/15_full_live_set.scd -- Set live complet 30 minutes
//
// TITRE : Performance live structuree avec tous les outils
// DUREE : ~30 minutes
// NIVEAU : avance
// PREREQUIS : CHARGER TOUT (00_load.scd)
// + SETUP de live.scd (~drop ~breakdown ~vcfOn ...)
// + SETUP de fx_bus.scd (~send ~fxPreset)
// + SETUP de mixer.scd (~mixPreset ~setVol ~mute)
// + SETUP de scenes.scd (~saveScene ~loadScene)
//
// STRUCTURE DU SET (timeline en minutes)
// 00:00 INTRO drone + ambient + buildup (4 min)
// 04:00 DROP 1 techno minimal (6 min) avec mute/solo/tweaks
// 10:00 ACID multi-mel + LFO cutoff + drop (6 min)
// 16:00 BREAKDOWN chord prog + reverb tail (4 min)
// 20:00 DROP 2 hard kit + bitcrush + glitch (4 min)
// 24:00 TRANCE seqBuildup + seqDrop final (3 min)
// 27:00 OUTRO masterFadeOut 4 min
//
// CONVENTION
// Tu evalues UN BLOC a chaque transition (les tags [00:00] etc.
// indiquent le temps suggere). Entre les blocs, tu fais des tweaks
// libres (changer ~melodyCutoff, lancer un ~drop, etc.).
// =====================================================================
// --- [etape 0] VERIFICATIONS ---------------------------------------
(
var ok = true;
if (~reload.isNil) { "!!! engine pas charge".postln; ok = false };
if (~drop.isNil) { "!!! live.scd SETUP manquant".postln; ok = false };
if (~send.isNil) { "!!! fx_bus.scd SETUP manquant".postln; ok = false };
if (~mixPreset.isNil) { "!!! mixer.scd SETUP manquant".postln; ok = false };
if (~saveScene.isNil) { "!!! scenes.scd SETUP manquant".postln; ok = false };
if (ok) { "=== TOUS LES MODULES SONT PRETS ===".postln };
)
// --- [00:00] INTRO -- drone + ambient + buildup -------------------
// On part d'un mix vide, on monte progressivement.
// masterVol bas + reverb saturee = ambiance ouverture.
(
~bpm = 124; TempoClock.default.tempo = ~bpm / 60;
~kickAmp = 0; ~hatAmp = 0; ~snareAmp = 0; ~clapAmp = 0; ~percAmp = 0;
~acidAmp = 0; ~melodyAmp = 0; ~harmonyAmp = 0;
~melodyInst = \warmPad;
~melodyAttack = 0.8; ~melodyRelease = 2.0; ~melodyCutoff = 1500;
~harmonyInst = \drone;
~harmonyAttack = 1.0; ~harmonyRelease = 3.0; ~harmonyAmp = 0.0;
~harmonyNotes = [36,0,0,0,0,0,0,0, 36,0,0,0,0,0,0,0,
36,0,0,0,0,0,0,0, 36,0,0,0,0,0,0,0];
~melodyNotes = ~m33.copy; // melodie ambient
~reload.value;
[\melodySeq, \harmonySeq].do { |k| Pdef(k).play };
~fxPreset.(\space); // shimmer + reverb + chorus
"=== [00:00] INTRO -- drone + ambient ===".postln;
)
// -- monter doucement ~melodyAmp et ~harmonyAmp en quelques etapes :
// attendre 30s avant le bloc suivant.
( ~melodyAmp = 0.10; ~harmonyAmp = 0.18; ~reload.value; ) // [00:30]
( ~melodyAmp = 0.16; ~harmonyAmp = 0.22; ~reload.value; ) // [01:30]
// --- [03:30] BUILDUP vers le drop techno -- 30s ------------------
// Snare roll + filtre qui s'ouvre + masterFadeIn.
(
~buildup.(30); // helper de live.scd : snare roll + open filter
~saveScene.(\preDrop1); // au cas ou
"=== [03:30] BUILDUP -- 30s ===".postln;
)
// --- [04:00] DROP 1 -- TECHNO MINIMAL -----------------------------
// Kit techno pur 4/4, melody en lead, ~mixPreset \drop.
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.6;
~kickFreq = 55; ~kickDecay = 0.32; ~kickDrive = 1.3; ~kickClick = 0.2;
~hatSteps = [0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0]; ~hatAmp = 0.18;
~snareSteps = (0 ! 16); ~clapSteps = (0 ! 16); ~percSteps = (0 ! 16);
~melodyInst = \lead;
~melodyAttack = 0.012; ~melodyRelease = 0.25; ~melodyCutoff = 2400;
~melodyAmp = 0.18;
~melodyNotes = ~m1.copy;
~harmonyAmp = 0.10;
~reload.value;
[\kickSeq, \hatSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).play };
~fxPreset.(\trance); // rev + delay 1/8 + chorus
~mixPreset.(\drop);
~saveScene.(\drop1);
"=== [04:00] DROP 1 -- TECHNO MINIMAL ===".postln;
)
// -- laisser tourner 1 minute, puis tweaks libres :
// ( ~mute.(\hat); ) ;; couper le hat 16 beats
// ( ~unmute.(\hat); )
// ( ~setVol.(\acid, 1.4); ) ;; pousser l'acid
// ( ~drop.(4); ) ;; mini-drop interne
// --- [07:00] VARIATIONS DROP 1 -- mute / solo --------------------
( ~mute.(\hat); ) // hat coupee
// -- 8 beats
( ~unmute.(\hat); )
( ~focus.(\melody, 0.4); ) // melodie en avant, autres a 40%
// -- 16 beats
( ~unfocus.(); )
// --- [08:00] LFO sur le cutoff de la melodie ---------------------
( ~lfoTo.(\melodyCutoff, 0.25, 2000, \sin, 3500); )
// -- la melodie respire lentement (cycle 4s).
// --- [09:30] mini drop interne avant la transition ACID ----------
( ~drop.(4); )
// --- [10:00] TRANSITION VERS ACID --------------------------------
// On stoppe le LFO, on bascule sur \saw3 + acid, on enclenche
// le systeme multi-pistes melodiques.
(
~lfoStopVar.(\melodyCutoff);
~melodyInst = \saw3; ~melodyAmp = 0.18;
~acidAmp = 0.32;
~acidNotes = [36,36,48,36,39,36,43,39, 36,41,36,39,43,36,36,41];
~acidSlides = [0,0,1,0,0,0,0,1, 0,0,0,1,0,0,0,0];
~melodyNotes = ~m29.copy; // melodie acid
~reload.value;
Pdef(\acidSeq).play;
~mixPreset.(\acidFocus);
"=== [10:00] ACID JOURNEY ===".postln;
)
// --- [11:00] Multi-piste : ajouter m2 en pluck contrer la mel ----
// m2 est preconfiguree dans melodies/tracks.scd ; on la lance et
// on lui colle une counter-melody.
(
~melTrackSet.(\m2, \inst, \pluck);
~melTrackSet.(\m2, \notes, ~m25.copy);
~melTrackSet.(\m2, \pan, -0.4);
~melTrackOn.(\m2);
"=== m2 counter pluck ON ===".postln;
)
// --- [12:00] LFO drunk sur le BPM -- subtile derive --------------
( ~lfoTo.(\bpm, 0.05, 6, \drunk, 132); )
// --- [14:00] Drop interne (acid section) -------------------------
( ~drop.(4); )
// --- [15:30] BREAKDOWN -- chord prog + reverb tail ---------------
// On stoppe le LFO + acid + m2, on fait un breakdown.
(
~lfoStopVar.(\bpm);
TempoClock.default.tempo = 124 / 60; ~bpm = 124;
~melTrackOff.(\m2);
Pdef(\acidSeq).stop;
Pdef(\hatSeq).stop;
~breakdown.(8);
"=== [15:30] BREAKDOWN ===".postln;
)
// --- [16:00] CHORDS PROGRESSION -- Synthwave + harmony ----------
// chordsToMelody + chordsToHarmony pour empiler une nappe.
(
~melodyInst = \warmPad;
~melodyAttack = 0.4; ~melodyRelease = 1.5; ~melodyCutoff = 3500;
~melodyAmp = 0.16;
~melodyNotes = ~chordsToMelody.(~progSynthwave);
~harmonyInst = \strings;
~harmonyAttack = 0.5; ~harmonyRelease = 2.0; ~harmonyAmp = 0.16;
~harmonyDur = 1.0;
~harmonyNotes = ~chordsToHarmony.(~progSynthwave);
~kickAmp = 0; // pas de kick pendant le breakdown
~reload.value;
~fxPreset.(\dream);
~mixPreset.(\breakdown);
"=== [16:00] CHORDS SYNTHWAVE ===".postln;
)
// --- [18:00] REVERB TAIL puis BUILDUP final --------------------
( ~reverbTail.(8); ) // [18:00] tail 8s
( ~buildup.(30); ~saveScene.(\preDrop2); ) // [18:30] buildup 30s
// --- [20:00] DROP 2 -- HARD kit + bitcrush -----------------------
// Kit hardcore + bitcrush en send + masterSat.
(
~kickSteps = [1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0]; ~kickAmp = 0.7;
~kickFreq = 60; ~kickDecay = 0.25; ~kickDrive = 3.0; ~kickClick = 0.3;
~hatSteps = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1]; ~hatAmp = 0.10;
~clapSteps = [0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0]; ~clapAmp = 0.4;
~melodyInst = \hardLead; ~melodyAmp = 0.22; ~melodyDrive = 3.0;
~melodyNotes = ~m67.copy; // melodie hardcore
~harmonyAmp = 0.0;
~reload.value;
[\kickSeq, \hatSeq, \clapSeq, \melodySeq].do { |k| Pdef(k).play };
~fxPreset.(\industrial);
~satOn !? { ~satOn.(0.4) }; // master saturation legere
~mixPreset.(\hardcore);
~saveScene.(\drop2);
"=== [20:00] DROP 2 -- HARD ===".postln;
)
// --- [22:00] GLITCH + bitcrush moments ---------------------------
( ~glitch.(2); )
// -- 8 beats
( ~bitCrushNow.(6, 8000, 1); )
// -- 8 beats
( ~stutter.(8, 1); )
// -- 8 beats
( ~freeze.(4); )
// --- [24:00] TRANSITION TRANCE -- seqBuildup + seqDrop -----------
// On stoppe les Pdef pour laisser ~seqXxx prendre le controle.
(
~satOff !? { ~satOff.() };
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
~bpm = 138; TempoClock.default.tempo = ~bpm / 60;
~melodyInst = \supersaw;
~melodyNotes = ~chordsToMelody.(~progTrance);
~reload.value;
~fxPreset.(\trance);
"=== [24:00] TRANCE -- seqBuildup ===".postln;
)
( ~seqBuildup.(16); ) // 16 mesures crescendo
// --- [26:00] DROP TRANCE FINAL -----------------------------------
( ~seqDrop.(); )
// -- attendre la fin de la sequence puis relancer un beat.
(
[\kickSeq, \hatSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).play };
~mixPreset.(\trance);
~saveScene.(\dropFinal);
"=== [26:00] TRANCE FINAL ===".postln;
)
// --- [27:00] OUTRO -- masterFadeOut 4 minutes -------------------
// ~masterFadeOut diminue masterVol vers 0 sur dur secondes.
// On laisse les Pdef tourner ; le silence vient progressivement.
(
~masterFadeOut.(240);
"=== [27:00] OUTRO -- fade-out 4 min ===".postln;
)
// -- patience : 4 minutes pour atteindre 0. Tu peux profiter.
// --- [31:00] STOP TOTAL -----------------------------------------
// Apres le fade, libere tout proprement.
(
~lfoStopAllVars !? { ~lfoStopAllVars.() };
~lfoStopAll !? { ~lfoStopAll.() };
~vcfOff !? { ~vcfOff.() };
~melTrackChoke !? { ~melTrackChoke.() };
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
~track !? { ~track.stop };
~masterLim !? { ~masterLim.free; ~masterLim = nil };
~scBus !? { ~scBus.free; ~scBus = nil };
"=== SET TERMINE -- merci ===".postln;
)
+15
View File
@@ -0,0 +1,15 @@
// =====================================================================
// fx/_index.scd -- Auto-glob loader pour tous les FX SynthDef
//
// Charge recursivement chaque .scd des sous-dossiers (bus/, insert/,
// trick/). Ajouter un nouveau FX = juste creer le fichier, pas besoin
// de modifier ce loader.
// =====================================================================
(
PathName(thisProcess.nowExecutingPath.dirname).deepFiles
.select { |f| f.extension == "scd"
and: { f.fileName != "_index.scd" } }
.sort { |a, b| a.fullPath < b.fullPath }
.do { |f| f.fullPath.load };
"=== fx/ loaded ===".postln;
)
+16
View File
@@ -0,0 +1,16 @@
// =====================================================================
// fx/bus/fx_bitcrush.scd -- SynthDef \fxBitcrush
// Extracted from fx_bus.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxBitcrush, {
arg in, out = 0, bits = 8, sampleRate = 8000,
mix = 0.6, amp = 1.0;
var dry = In.ar(in, 2);
// Bit reduction native (sans Decimator de sc3-plugins)
var step = 2.pow(bits - 1);
var crushed = (dry * step).round / step;
var sig = Latch.ar(crushed, Impulse.ar(sampleRate));
Out.ar(out, ((dry * (1 - mix)) + (sig * mix)) * amp);
}).add;
)
+15
View File
@@ -0,0 +1,15 @@
// =====================================================================
// fx/bus/fx_chorus.scd -- SynthDef \fxChorus
// Extracted from fx_bus.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxChorus, {
arg in, out = 0, rate = 0.4, depth = 0.005,
mix = 0.5, amp = 1.0;
var sig = In.ar(in, 2);
var l = DelayC.ar(sig[0], 0.05, SinOsc.kr(rate, 0).range(0.005, 0.005 + depth));
var r = DelayC.ar(sig[1], 0.05, SinOsc.kr(rate, pi).range(0.005, 0.005 + depth));
var wet = [l, r];
Out.ar(out, ((sig * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+21
View File
@@ -0,0 +1,21 @@
// =====================================================================
// fx/bus/fx_delay.scd -- SynthDef \fxDelay
// Extracted from fx_bus.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxDelay, {
arg in, out = 0, delayTime = 0.375, feedback = 0.5,
mix = 0.5, color = 0.3, amp = 1.0;
var dry, wet, l, r, fb;
dry = In.ar(in, 2);
fb = LocalIn.ar(2); // vrai feedback stereo
l = DelayC.ar(fb[1] + dry[0], 2.0, delayTime) * feedback; // ping
r = DelayC.ar(fb[0] + dry[1], 2.0, delayTime) * feedback; // pong
// Color : LPF pour effet tape vintage
l = LPF.ar(l, color.linexp(0, 1, 800, 16000));
r = LPF.ar(r, color.linexp(0, 1, 800, 16000));
LocalOut.ar([l, r]);
wet = [l, r];
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+15
View File
@@ -0,0 +1,15 @@
// =====================================================================
// fx/bus/fx_dist.scd -- SynthDef \fxDist
// Extracted from fx_bus.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxDist, {
arg in, out = 0, drive = 3.0, tone = 0.5,
mix = 1.0, amp = 1.0;
var dry = In.ar(in, 2);
var sig = (dry * drive).tanh;
sig = (sig * 1.5).tanh;
sig = MoogFF.ar(sig, tone.linexp(0, 1, 500, 12000), 1.5);
Out.ar(out, ((dry * (1 - mix)) + (sig * mix)) * amp);
}).add;
)
+16
View File
@@ -0,0 +1,16 @@
// =====================================================================
// fx/bus/fx_flanger.scd -- SynthDef \fxFlanger
// Extracted from fx_bus.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxFlanger, {
arg in, out = 0, rate = 0.2, depth = 0.003, feedback = 0.6,
mix = 0.5, amp = 1.0;
var dry = In.ar(in, 2);
var lfo = SinOsc.kr(rate).range(0.0005, 0.0005 + depth);
var fb = LocalIn.ar(2) * feedback;
var sig = DelayC.ar(dry + fb, 0.02, lfo);
LocalOut.ar(sig);
Out.ar(out, ((dry * (1 - mix)) + (sig * mix)) * amp);
}).add;
)
+18
View File
@@ -0,0 +1,18 @@
// =====================================================================
// fx/bus/fx_phaser.scd -- SynthDef \fxPhaser
// Extracted from fx_bus.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxPhaser, {
arg in, out = 0, rate = 0.3, depth = 0.7, feedback = 0.4,
mix = 0.5, amp = 1.0;
var dry = In.ar(in, 2);
var lfo = SinOsc.kr(rate).range(200, 200 + (3000 * depth));
var sig = AllpassN.ar(dry, 0.01, lfo.reciprocal, 0.05);
sig = AllpassN.ar(sig, 0.01, (lfo * 1.3).reciprocal, 0.05);
sig = AllpassN.ar(sig, 0.01, (lfo * 1.7).reciprocal, 0.05);
sig = AllpassN.ar(sig + (LocalIn.ar(2) * feedback), 0.01, (lfo * 2.1).reciprocal, 0.05);
LocalOut.ar(sig);
Out.ar(out, ((dry * (1 - mix)) + (sig * mix)) * amp);
}).add;
)
+16
View File
@@ -0,0 +1,16 @@
// =====================================================================
// fx/bus/fx_reverb.scd -- SynthDef \fxReverb
// Extracted from fx_bus.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxReverb, {
arg in, out = 0, mix = 0.4, room = 0.85, damp = 0.4,
freeze = 0, amp = 1.0;
var sig = In.ar(in, 2);
sig = FreeVerb2.ar(sig[0], sig[1],
Select.kr(freeze, [mix, 1.0]),
Select.kr(freeze, [room, 0.999]),
Select.kr(freeze, [damp, 0.001]));
Out.ar(out, sig * amp);
}).add;
)
+11
View File
@@ -0,0 +1,11 @@
// =====================================================================
// fx/bus/fx_send.scd -- SynthDef \fxSend
// Extracted from fx_bus.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxSend, {
arg srcBus = 0, dstBus, amount = 0.3;
var sig = In.ar(srcBus, 2);
Out.ar(dstBus, sig * amount);
}).add;
)
+14
View File
@@ -0,0 +1,14 @@
// =====================================================================
// fx/bus/fx_shimmer.scd -- SynthDef \fxShimmer
// Extracted from fx_bus.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxShimmer, {
arg in, out = 0, mix = 0.5, room = 0.92, shimmer = 0.5, amp = 1.0;
var dry = In.ar(in, 2);
var pitched = PitchShift.ar(dry, 0.2, 2, 0.01, 0.005) * shimmer;
var sig = FreeVerb2.ar(dry[0] + pitched[0], dry[1] + pitched[1],
1.0, room, 0.3);
Out.ar(out, ((dry * (1 - mix)) + (sig * mix)) * amp);
}).add;
)
+18
View File
@@ -0,0 +1,18 @@
// =====================================================================
// fx/bus/fx_tape.scd -- SynthDef \fxTape
// Extracted from fx_bus.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxTape, {
arg in, out = 0, drive = 1.5, wow = 0.0015, flutter = 0.0008,
mix = 1.0, amp = 1.0;
var dry = In.ar(in, 2);
var modL = SinOsc.kr(0.3).range(0, wow) + LFNoise2.kr(8).range(0, flutter);
var modR = SinOsc.kr(0.31).range(0, wow) + LFNoise2.kr(8).range(0, flutter);
var sig = [DelayC.ar(dry[0], 0.02, 0.005 + modL),
DelayC.ar(dry[1], 0.02, 0.005 + modR)];
sig = (sig * drive).tanh / drive.sqrt;
sig = LPF.ar(sig, 12000); // EQ tape rolloff aigu
Out.ar(out, ((dry * (1 - mix)) + (sig * mix)) * amp);
}).add;
)
+14
View File
@@ -0,0 +1,14 @@
// =====================================================================
// fx/insert/fx_auto_filter.scd -- SynthDef \fxAutoFilter
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxAutoFilter, {
arg in, out = 0, rate = 0.5, lo = 200, hi = 4000, q = 0.4,
mix = 0.7, amp = 1.0;
var dry = In.ar(in, 2);
var lfo = SinOsc.kr(rate).range(lo, hi);
var wet = RLPF.ar(dry, lfo, q.clip(0.05, 1.5));
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+13
View File
@@ -0,0 +1,13 @@
// =====================================================================
// fx/insert/fx_autopan.scd -- SynthDef \fxAutopan
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxAutopan, {
arg in, out = 0, rate = 0.3, depth = 1.0, mix = 1.0, amp = 1.0;
var dry = In.ar(in, 2);
var p = SinOsc.kr(rate) * depth;
var wet = Balance2.ar(dry[0], dry[1], p);
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+12
View File
@@ -0,0 +1,12 @@
// =====================================================================
// fx/insert/fx_comb.scd -- SynthDef \fxComb
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxComb, {
arg in, out = 0, freq = 220, decay = 1.0, mix = 0.5, amp = 1.0;
var dry = In.ar(in, 2);
var wet = CombC.ar(dry, 0.05, freq.reciprocal.clip(0.0001, 0.05), decay);
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+18
View File
@@ -0,0 +1,18 @@
// =====================================================================
// fx/insert/fx_formant.scd -- SynthDef \fxFormant
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxFormant, {
arg in, out = 0, vowel = 0, q = 0.15, mix = 0.5, amp = 1.0;
var dry = In.ar(in, 2);
var f1 = Select.kr(vowel, [800, 400, 350, 450, 325]);
var f2 = Select.kr(vowel, [1150, 1700, 1900, 800, 700]);
var f3 = Select.kr(vowel, [2900, 2600, 2500, 2830, 2530]);
var wet = BPF.ar(dry, f1, q)
+ BPF.ar(dry, f2, q) * 0.7
+ BPF.ar(dry, f3, q) * 0.4;
wet = wet * 1.5;
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+12
View File
@@ -0,0 +1,12 @@
// =====================================================================
// fx/insert/fx_freq_shift.scd -- SynthDef \fxFreqShift
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxFreqShift, {
arg in, out = 0, shift = 100, mix = 0.5, amp = 1.0;
var dry = In.ar(in, 2);
var wet = FreqShift.ar(dry, shift);
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+13
View File
@@ -0,0 +1,13 @@
// =====================================================================
// fx/insert/fx_gran.scd -- SynthDef \fxGran
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxGran, {
arg in, out = 0, grainDur = 0.1, density = 30, pitch = 1.0,
mix = 0.5, amp = 1.0;
var dry = In.ar(in, 2);
var wet = PitchShift.ar(dry, grainDur, pitch, 0.05, 0.05);
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+14
View File
@@ -0,0 +1,14 @@
// =====================================================================
// fx/insert/fx_plate.scd -- SynthDef \fxPlate
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxPlate, {
arg in, out = 0, decay = 2.5, damp = 0.3, mix = 0.4, amp = 1.0;
var dry = In.ar(in, 2);
var wet = FreeVerb2.ar(dry[0], dry[1], 1.0,
decay.linlin(0.5, 5.0, 0.7, 0.95), damp);
wet = HPF.ar(wet, 80);
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+18
View File
@@ -0,0 +1,18 @@
// =====================================================================
// fx/insert/fx_resonator.scd -- SynthDef \fxResonator
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxResonator, {
arg in, out = 0, freq = 220, decay = 2.0, mix = 0.4, amp = 1.0;
var dry = In.ar(in, 2);
var ratios = [1, 1.5, 2.01, 2.5, 3.01];
var amps = [1, 0.6, 0.4, 0.3, 0.2];
var ringL = Klank.ar(`[ratios * freq, amps, ratios.collect { decay }],
dry[0] * 0.1);
var ringR = Klank.ar(`[ratios * freq, amps, ratios.collect { decay }],
dry[1] * 0.1);
var wet = [ringL, ringR];
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+12
View File
@@ -0,0 +1,12 @@
// =====================================================================
// fx/insert/fx_ring_mod.scd -- SynthDef \fxRingMod
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxRingMod, {
arg in, out = 0, freq = 200, modAmt = 1.0, mix = 0.5, amp = 1.0;
var dry = In.ar(in, 2);
var wet = dry * SinOsc.ar(freq) * modAmt;
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+18
View File
@@ -0,0 +1,18 @@
// =====================================================================
// fx/insert/fx_spring_rev.scd -- SynthDef \fxSpringRev
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxSpringRev, {
arg in, out = 0, decay = 1.5, mix = 0.4, amp = 1.0;
var dry = In.ar(in, 2);
var wet = dry;
// chaine d'allpass typique d'un spring tank
[0.013, 0.029, 0.041, 0.067, 0.089].do { |t|
wet = AllpassN.ar(wet, 0.1, t, decay);
};
wet = HPF.ar(wet, 200); // pas de basses dans un spring
wet = LPF.ar(wet, 4500);
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+13
View File
@@ -0,0 +1,13 @@
// =====================================================================
// fx/insert/fx_stutter.scd -- SynthDef \fxStutter
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxStutter, {
arg in, out = 0, rate = 8, dutyCycle = 0.5, mix = 1.0, amp = 1.0;
var dry = In.ar(in, 2);
var gate = LFPulse.kr(rate, 0, dutyCycle);
var wet = dry * gate;
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+13
View File
@@ -0,0 +1,13 @@
// =====================================================================
// fx/insert/fx_tremolo.scd -- SynthDef \fxTremolo
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxTremolo, {
arg in, out = 0, rate = 5, depth = 0.7, mix = 1.0, amp = 1.0;
var dry = In.ar(in, 2);
var lfo = SinOsc.kr(rate).range(1 - depth, 1);
var wet = dry * lfo;
Out.ar(out, ((dry * (1 - mix)) + (wet * mix)) * amp);
}).add;
)
+12
View File
@@ -0,0 +1,12 @@
// =====================================================================
// fx/insert/vcf_bpf.scd -- SynthDef \vcfBpf
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\vcfBpf, {
arg busIn = 0, freq = 1000, q = 0.3, mix = 1.0;
var sig = In.ar(busIn, 2);
var wet = BPF.ar(sig, freq.clip(40, 18000), q.clip(0.05, 2)) * 1.5;
ReplaceOut.ar(busIn, (sig * (1 - mix)) + (wet * mix));
}).add;
)
+12
View File
@@ -0,0 +1,12 @@
// =====================================================================
// fx/insert/vcf_comb.scd -- SynthDef \vcfComb
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\vcfComb, {
arg busIn = 0, freq = 220, decay = 1.0, mix = 0.6;
var sig = In.ar(busIn, 2);
var wet = CombC.ar(sig, 0.05, freq.reciprocal.clip(0.0001, 0.05), decay);
ReplaceOut.ar(busIn, (sig * (1 - mix)) + (wet * mix));
}).add;
)
+18
View File
@@ -0,0 +1,18 @@
// =====================================================================
// fx/insert/vcf_formant.scd -- SynthDef \vcfFormant
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\vcfFormant, {
arg busIn = 0, vowel = 0, q = 0.15, mix = 1.0;
var sig = In.ar(busIn, 2);
var f1 = Select.kr(vowel, [800, 400, 350, 450, 325]);
var f2 = Select.kr(vowel, [1150, 1700, 1900, 800, 700]);
var f3 = Select.kr(vowel, [2900, 2600, 2500, 2830, 2530]);
var wet = BPF.ar(sig, f1, q) * 1.0
+ BPF.ar(sig, f2, q) * 0.7
+ BPF.ar(sig, f3, q) * 0.4;
wet = wet * 1.5;
ReplaceOut.ar(busIn, (sig * (1 - mix)) + (wet * mix));
}).add;
)
+12
View File
@@ -0,0 +1,12 @@
// =====================================================================
// fx/insert/vcf_hpf.scd -- SynthDef \vcfHpf
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\vcfHpf, {
arg busIn = 0, freq = 200, q = 0.5, mix = 1.0;
var sig = In.ar(busIn, 2);
var wet = RHPF.ar(sig, freq.clip(20, 18000), q.clip(0.05, 2));
ReplaceOut.ar(busIn, (sig * (1 - mix)) + (wet * mix));
}).add;
)
+13
View File
@@ -0,0 +1,13 @@
// =====================================================================
// fx/insert/vcf_ladder.scd -- SynthDef \vcfLadder
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\vcfLadder, {
arg busIn = 0, freq = 1500, q = 1.0, mix = 1.0, drive = 1.0;
var sig = In.ar(busIn, 2);
var wet = LPF.ar(LPF.ar(sig * drive, freq), freq);
wet = wet + (RHPF.ar(wet, freq, q.reciprocal.clip(0.1, 2)) * (q * 0.3));
ReplaceOut.ar(busIn, (sig * (1 - mix)) + (wet * mix));
}).add;
)
+12
View File
@@ -0,0 +1,12 @@
// =====================================================================
// fx/insert/vcf_moog.scd -- SynthDef \vcfMoog
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\vcfMoog, {
arg busIn = 0, freq = 1500, gain = 2.0, mix = 1.0, drive = 1.0;
var sig = In.ar(busIn, 2);
var wet = MoogFF.ar(sig * drive, freq.clip(40, 18000), gain.clip(0, 4));
ReplaceOut.ar(busIn, (sig * (1 - mix)) + (wet * mix));
}).add;
)
+14
View File
@@ -0,0 +1,14 @@
// =====================================================================
// fx/insert/vcf_ms20.scd -- SynthDef \vcfMs20
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\vcfMs20, {
arg busIn = 0, freq = 1500, q = 1.5, mix = 1.0, drive = 1.2;
var sig = In.ar(busIn, 2);
var wet = (sig * drive).tanh;
wet = RLPF.ar(wet, freq.clip(40, 18000), q.reciprocal.clip(0.05, 1.5));
wet = (wet * 1.3).tanh;
ReplaceOut.ar(busIn, (sig * (1 - mix)) + (wet * mix));
}).add;
)
+12
View File
@@ -0,0 +1,12 @@
// =====================================================================
// fx/insert/vcf_notch.scd -- SynthDef \vcfNotch
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\vcfNotch, {
arg busIn = 0, freq = 1000, q = 0.3, mix = 1.0;
var sig = In.ar(busIn, 2);
var wet = BRF.ar(sig, freq.clip(40, 18000), q.clip(0.05, 2));
ReplaceOut.ar(busIn, (sig * (1 - mix)) + (wet * mix));
}).add;
)
+14
View File
@@ -0,0 +1,14 @@
// =====================================================================
// fx/trick/crash.scd -- SynthDef \crash
// Extracted from engine.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\crash, {
arg hpf = 4000, decay = 1.5, amp = 0.3, pan = 0;
var sig, env;
sig = HPF.ar(WhiteNoise.ar + (PinkNoise.ar * 0.5), hpf);
sig = BPF.ar(sig, 7000, 0.4) + (sig * 0.3);
env = EnvGen.kr(Env.perc(0.002, decay, curve: -3), doneAction: 2);
Out.ar(0, Pan2.ar(sig * env * amp, pan));
}).add;
)
@@ -0,0 +1,17 @@
// =====================================================================
// fx/trick/fx_master_bitcrush.scd -- SynthDef \fxMasterBitcrush
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxMasterBitcrush, {
arg dur = 1, bits = 6, sr = 6000, mix = 0.7;
var sig = In.ar(0, 2);
// Bit + SR reduction native (sans Decimator de sc3-plugins)
var step = 2.pow(bits - 1);
var crushed = (sig * step).round / step;
var wet = Latch.ar(crushed, Impulse.ar(sr));
var env = EnvGen.kr(Env.linen(0.01, dur, 0.05), doneAction: 2);
ReplaceOut.ar(0, ((sig * (1 - mix)) + (wet * mix)) * env
+ (sig * (1 - env)));
}).add;
)
@@ -0,0 +1,13 @@
// =====================================================================
// fx/trick/fx_master_freq_shift.scd -- SynthDef \fxMasterFreqShift
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxMasterFreqShift, {
arg dur = 1, shift = 100;
var sig = In.ar(0, 2);
var wet = FreqShift.ar(sig, shift);
var env = EnvGen.kr(Env.linen(0.01, dur, 0.05), doneAction: 2);
ReplaceOut.ar(0, XFade2.ar(sig, wet, env.linlin(0, 1, -1, 1)));
}).add;
)
+13
View File
@@ -0,0 +1,13 @@
// =====================================================================
// fx/trick/fx_master_gran.scd -- SynthDef \fxMasterGran
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxMasterGran, {
arg dur = 2, grainDur = 0.1, pitch = 1.0;
var sig = In.ar(0, 2);
var wet = PitchShift.ar(sig, grainDur, pitch, 0.05, 0.05);
var env = EnvGen.kr(Env.linen(0.01, dur, 0.05), doneAction: 2);
ReplaceOut.ar(0, XFade2.ar(sig, wet, env.linlin(0, 1, -1, 1)));
}).add;
)
+13
View File
@@ -0,0 +1,13 @@
// =====================================================================
// fx/trick/fx_master_pitch.scd -- SynthDef \fxMasterPitch
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxMasterPitch, {
arg dur = 2, pitch = 1.0, releaseTime = 0.05;
var sig = In.ar(0, 2);
var wet = PitchShift.ar(sig, 0.2, pitch, 0.01, 0.005);
var env = EnvGen.kr(Env.linen(0.01, dur, releaseTime), doneAction: 2);
ReplaceOut.ar(0, XFade2.ar(sig, wet, env.linlin(0, 1, -1, 1)));
}).add;
)
+14
View File
@@ -0,0 +1,14 @@
// =====================================================================
// fx/trick/fx_master_ring.scd -- SynthDef \fxMasterRing
// Extracted from live.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\fxMasterRing, {
arg dur = 1, freq = 200, mix = 0.7;
var sig = In.ar(0, 2);
var wet = sig * SinOsc.ar(freq);
var env = EnvGen.kr(Env.linen(0.01, dur, 0.05), doneAction: 2);
ReplaceOut.ar(0, ((sig * (1 - mix)) + (wet * mix)) * env
+ (sig * (1 - env)));
}).add;
)
+20
View File
@@ -0,0 +1,20 @@
// =====================================================================
// fx/trick/impact.scd -- SynthDef \impact
// Extracted from engine.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\impact, {
arg freq = 45, decay = 1.5, amp = 0.7, pan = 0;
var pitchEnv, body, noise, env, sig;
pitchEnv = EnvGen.kr(Env([freq * 5, freq * 0.5], [0.12], \exp));
body = SinOsc.ar(pitchEnv);
noise = LPF.ar(WhiteNoise.ar, 800) *
EnvGen.kr(Env.perc(0.001, 0.4));
sig = body + (noise * 0.5);
env = EnvGen.kr(Env.perc(0.002, decay, curve: -4), doneAction: 2);
sig = sig * env;
sig = (sig * 1.5).tanh;
sig = LeakDC.ar(sig) * amp;
Out.ar(0, Pan2.ar(sig, pan));
}).add;
)
+21
View File
@@ -0,0 +1,21 @@
// =====================================================================
// fx/trick/riser.scd -- SynthDef \riser
// Extracted from engine.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\riser, {
arg dur = 4, startCutoff = 300, endCutoff = 8000,
amp = 0.3, pan = 0;
var noise, cutoff, env, sig;
cutoff = Line.kr(startCutoff, endCutoff, dur).clip(20, 18000);
noise = (WhiteNoise.ar * 0.5) + (PinkNoise.ar * 0.3);
sig = HPF.ar(noise, cutoff);
sig = BPF.ar(sig, cutoff.min(15000), 0.4);
env = EnvGen.kr(
Env.new([0, 0.3, 1, 0], [dur * 0.5, dur * 0.45, dur * 0.05], \sin),
doneAction: 2
);
sig = LeakDC.ar(sig * env * amp);
Out.ar(0, Pan2.ar(sig, pan));
}).add;
)
+19
View File
@@ -0,0 +1,19 @@
// =====================================================================
// fx/trick/scratch.scd -- SynthDef \scratch
// Extracted from authentic.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\scratch, {
arg dur = 0.5, freq = 800, sweepDir = 1, amp = 0.3, pan = 0;
var sig, env, sweepFreq;
sweepFreq = Select.kr(sweepDir > 0, [
Line.kr(freq * 2, freq * 0.5, dur),
Line.kr(freq * 0.5, freq * 2, dur)
]);
sig = (SinOsc.ar(sweepFreq) * 0.5) +
(HPF.ar(WhiteNoise.ar, sweepFreq * 0.8) * 0.3);
sig = sig * LFPulse.kr(20).range(0.3, 1); // stutter
env = EnvGen.kr(Env.linen(0.005, dur, 0.02), doneAction: 2);
Out.ar(0, Pan2.ar(sig * env * amp, pan));
}).add;
)
+17
View File
@@ -0,0 +1,17 @@
// =====================================================================
// fx/trick/sweep.scd -- SynthDef \sweep
// Extracted from engine.scd during v2 reorganization.
// =====================================================================
(
SynthDef(\sweep, {
arg dur = 4, startCutoff = 8000, endCutoff = 200,
amp = 0.25, pan = 0;
var noise, cutoff, env, sig;
cutoff = Line.kr(startCutoff, endCutoff, dur);
noise = WhiteNoise.ar + (PinkNoise.ar * 0.5);
sig = LPF.ar(noise, cutoff);
sig = BPF.ar(sig, cutoff * 1.2, 0.5);
env = EnvGen.kr(Env.linen(0.05, dur - 0.1, 0.05), doneAction: 2);
Out.ar(0, Pan2.ar(sig * env * amp, pan));
}).add;
)
+63
View File
@@ -0,0 +1,63 @@
# live/ — API live performance
Helpers pour mixer, FX, scenes, loops, tweaks, melodies live.
Charges via `00_load.scd` puis SETUP bloc par bloc.
## Notation d'appel (CRITIQUE)
SuperCollider Event pseudo-method ne passe PAS les args :
```supercollider
~m.short.(50) // FAUX : ~m est passe comme 1er arg, 50 perdu
~m[\short].(50) // OK : notation indexee
~mShort.(50) // OK : variable env directe (alias prefere)
~mm.(50) // OK : alias court
```
Toute API exposee via `~xxx` doit avoir un wrapper env direct,
pas seulement une cle dans un dict.
## Patterns
- Chaque module expose un dict catalogue (`~m`, `~fx`, `~ch`, ...)
ET des wrappers env directs (`~mShort`, `~fxRev`, ...).
- SETUP en commentaire en haut du fichier, exemples en bas.
- VCF/FX inserent via `ReplaceOut.ar(busIn, ...)` (chain in-place).
- Sidechain : duck = `1 - (In.kr(scBus, 1) * scAmt)`.
## 5 namespaces (cf `01_live.scd`)
| Dict | Wrappers directs | Domaine |
| --- | --- | --- |
| `~kk.(\genre)` | `~kPat`, `~kTim`, `~kList` | Kicks (techno/gabber/dub/...) |
| `~mm.(N)` | `~mLong`, `~mXlong`, `~mEpic`, `~mRnd`, `~mGen`, `~mNx`, `~mPv`, `~mInst`, `~mHarm`, `~mAmp`, `~mCut`, `~mDrive`, `~mOff` | Melodies |
| `~ff.(\preset)` | `~fxSnd`, `~fxSt`, `~fxFreeze`, `~fxDrop`, `~fxBreakdown`, `~fxBuildup`, `~fxGlitch`, `~fxStutter`, `~fxVcfOn`, `~fxLfo`, `~fxCut`, `~fxRq`, `~fxDrive`, `~fxComp`, `~fxSat` | FX presets + tricks |
| `~cc` | `~chIntro`, `~chDrop`, `~chBreakdown`, `~chBuildup`, `~chTechno`, `~chDnb`, `~chOutroFade`, `~chStop` | Chains/sequences |
| `~p` | `~pKit`, `~pGenre`, `~pHat`, `~pSnare`, `~pClap`, `~pPerc`, `~pList` | Patterns rythmiques |
Mixer : `~setVol`, `~mute`, `~solo`, `~mixPreset`, `~saveMix`, `~masterVol`, `~masterFade`.
LFO/VCF : `~lfoTo`, `~vcfOn`. Loops : `~loopOn`. Scenes : `~saveScene`, `~loadScene`.
## Convention de renommage (clefs courtes = collisions Object/Server)
Noms reserves SC (`s`=Server, `next`/`prev`/`set`/`send`/`tap`/`p`/`t`/`e`/`l`)
plantent comme cles d'Event. Renommages internes :
| Ancien | Nouveau |
| --- | --- |
| `.p` | `.pat` |
| `.t` | `.tim` |
| `.s` | `.short` |
| `.l` | `.long` |
| `.x` | `.xlong` |
| `.e` | `.epic` |
| `.r` | `.rnd` |
| `.next` / `.prev` | `.nx` / `.pv` |
| `.send` / `.set` / `.tap` | `.snd` / `.st` / `.tp` |
## Anti-patterns
- Ne pas appeler `~dict.key.(args)` -- les args sont avales
- Ne pas oublier le wrapper env direct (`~mShort`) en plus de la cle
- Ne pas charger live/*.scd avant `engine.scd` + `fx_bus.scd`
- Ne pas mute un `~xxx` sans `~reload.value` si lie a un Pdef
+162
View File
@@ -0,0 +1,162 @@
// =====================================================================
// live/_load.scd -- CHARGEMENT COMPLET LIVE en un Cmd+Entree
//
// IDEMPOTENT : detecte si le bootstrap a deja tourne (via ~setupAll_done)
// et ne refait pas le travail inutilement.
//
// Fait :
// 1. Set ~base si nil
// 2. Si pas deja boote : charge engine + synthdefs + scales + melodies
// + harmonics + setup.scd, puis ~setupAll.()
// (~setupAll charge fx_bus / live / mixer / scenes / loops / fills /
// sequences / jump + les 5 wrappers ~kk/~mm/~ff/~cc/~p)
// 3. Polling sur ~kk (defini en DERNIER par liveSetup async) pour
// confirmer que tout est charge
// 4. Ouvre les fichiers essentiels dans l'editeur SC
// 5. Affiche "=== LIVE READY ==="
//
// Utilisation :
// (~base ++ "live/_load.scd").load;
// OU
// "/Users/electron/Documents/Projets_Creatifs/sound_algo-refonte-v2/live/_load.scd".load;
// =====================================================================
(
~base = ~base ?? { "/Users/electron/Documents/Projets_Creatifs/sound_algo-refonte-v2/" };
Routine({
var maxWait = 30, waited = 0;
if (~setupAll_done == true and: { ~kk.notNil }) {
"[live/_load] bootstrap deja fait, skip".postln;
} {
// --- 1. Modules .load-compatibles -------------------------------
"[live/_load] engine + synthdefs + palette ...".postln;
(~base ++ "engine.scd").load;
(~base ++ "synth/_index.scd").load;
(~base ++ "fx/_index.scd").load;
(~base ++ "palette/scales.scd").load;
(~base ++ "palette/melodies/index.scd").load;
(~base ++ "palette/harmonics/index.scd").load;
// --- 2. setup.scd : definit ~check / ~setupAll / ~teardown -----
"[live/_load] setup.scd ...".postln;
(~base ++ "setup.scd").load;
// --- 3. ~setupAll : boot serveur + charge tous modules live + --
// les 5 wrappers (~kk / ~mm / ~ff / ~cc / ~p)
// liveSetup tourne dans s.waitForBoot{}, donc async.
"[live/_load] ~setupAll (boot + modules live + wrappers) ...".postln;
~setupAll.();
// --- 4. Polling : ~kk est defini en DERNIER par liveSetup ------
// (apres tous les autres modules + apres ~loadFirstBlock kicks).
// Quand ~kk est non-nil, liveSetup a TOUT charge.
while { ~kk.isNil and: { waited < maxWait } } {
0.2.wait;
waited = waited + 0.2;
};
if (~kk.isNil) {
("[live/_load] !! timeout " ++ maxWait
++ "s sur ~kk. liveSetup a peut-etre echoue.").postln;
} {
~setupAll_done = true;
};
};
if (s.serverRunning) { s.sync };
// --- 4a. Albums (catalogue 23 albums × 15 tracks) -------------------
"[live/_load] tracks/_index.scd ...".postln;
{
var path = ~base ++ "tracks/_index.scd";
if (File.exists(path)) {
try { path.load } { |err|
("[live/_load] !! erreur load tracks/_index: " ++ err.errorString).postln;
};
} { ("[live/_load] !! manquant : " ++ path).postln };
}.value;
// --- 4b. Pont web (OSC bridge + serveur Node + UI control/hydra) ----
"[live/_load] web_bridge ...".postln;
{
var path = ~base ++ "web_bridge.scd";
if (File.exists(path)) {
try { path.load } { |err|
("[live/_load] !! erreur load web_bridge: " ++ err.errorString).postln;
};
// ~webStart est expose par web_bridge.scd : lance Node + ouvre les pages
currentEnvironment[\webStart] !? { ~webStart.value } ?? {
"[live/_load] !! ~webStart absent (web_bridge mal charge)".postln;
};
} { ("[live/_load] !! manquant : " ++ path).postln };
}.value;
// --- 4c. Auto-PLAY : demarre les sequenceurs immediatement ---------
"[live/_load] auto-play sequenceurs ...".postln;
{
// Defaults pour les patterns si non definis (kicks 4/4 minimum)
~kickSteps = ~kickSteps ? (1!16);
~hatSteps = ~hatSteps ? (0!16);
~snareSteps = ~snareSteps ? (0!16);
~clapSteps = ~clapSteps ? (0!16);
~percSteps = ~percSteps ? (0!16);
~acidNotes = ~acidNotes ? (0!16);
~melodyNotes = ~melodyNotes ? (0!32);
~harmonyNotes = ~harmonyNotes ? (0!32);
TempoClock.default.tempo = (~bpm ? 128) / 60;
currentEnvironment[\reload] !? { ~reload.value };
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).play(quant: 4) };
}.value;
// --- 5. Ouvrir les fichiers essentiels dans l'editeur SC ------------
// Document.open est IDE-only (ScIDE classique). En sclang CLI ou
// dans certaines extensions VSCode, la classe Document n'existe
// pas -> on skip silencieusement avec try{}.
// Ouvrir les fichiers essentiels dans l'editeur SC -- si Document
// est dispo. La classe Document n'existe pas en sclang CLI ni dans
// certaines extensions VSCode (pas de Qt charge) -> on skip.
{
var docClass = \Document.asClass;
if (docClass.notNil) {
"[live/_load] ouverture des fichiers essentiels dans l'IDE ...".postln;
[
"01_live.scd", // tableau de bord live
"live/kicks.scd", // ~kk
"live/melodies.scd", // ~mm
"live/fx.scd", // ~ff
"live/chains.scd", // ~cc
"live/patterns.scd", // ~p
"live/live_fx_panel.scd", // panneau FX exhaustif
"live/mixer.scd", // ~setVol / ~mixPreset
"live/scenes.scd", // ~saveScene / ~loadScene
"live/tweaks.scd", // tweaks Cmd+Entree par bloc
"control/jump.scd", // ~jumpTo
"palette/rhythm/_index.scd", // ~snareFill / ~tomFill / ~seqPlay / ...
].do { |path|
var full = ~base ++ path;
if (File.exists(full)) {
try { docClass.perform(\open, full) } { |err|
(" [skip Document.open] " ++ path).postln;
};
} { (" [skip] " ++ path ++ " inexistant").postln };
};
} {
"[live/_load] Document indisponible (sclang CLI ou VSCode sans IDE) -- skip ouverture".postln;
};
}.value;
"".postln;
"=== LIVE READY ===".postln;
" Wrappers : ~kk(kicks) ~mm(melodies) ~ff(fx) ~cc(chains) ~p(patterns)".postln;
" Mixer : ~setVol ~mute ~solo ~mixPreset ~masterVol".postln;
" Live : ~lfoTo ~vcfOn ~drop ~breakdown ~buildup".postln;
" Guard : ~killAllOrphans ~memReport ~watchdogOn ~watchdogOff".postln;
" Scenes : ~saveScene ~loadScene ~listScenes".postln;
" Jump : ~jumpTo.(\\X, \\section_slug)".postln;
" Status : ~check.()".postln;
" Stop : ~teardown.()".postln;
" Web : http://localhost:3000/control/ (UI live)".postln;
" -> ouvre 01_live.scd pour les blocs Cmd+Entree prets a l'emploi".postln;
}).play(AppClock);
)
+174
View File
@@ -0,0 +1,174 @@
// =====================================================================
// live/chains.scd -- CHAINS / SEQUENCES LIVE (API ultra-courte)
//
// ATTENTION SC -- noter la notation des appels :
//
// NE MARCHE PAS : ~ch.drop.() ou ~cc.drop.()
// -> SC passe l'event ~ch comme 1er arg, et les vrais args sont
// PERDUS (test empirique sur SC 3.14.1).
// -> Pour les fonctions sans args (~ch[\drop]), l'effet visible
// peut etre OK, MAIS pour celles avec args (~ch[\buildup].(8))
// l'arg est silencieusement perdu.
//
// MARCHE BIEN : ~ch[\drop].() (notation indexee)
// ~chDrop.() (variable env directe)
//
// ~ch // dict catalogue
// ~ch[\intro], [\drop], [\breakdown], [\buildup], [\outro]
// ~ch[\techno], [\acid], [\dubstep], [\trance], [\dnb], [\hiphop], [\world]
// ~ch[\stop], [\custom]
// ~ch[\full][\intro2drop], [\dropToBreak], [\outroFade]
// ~ch[\list].()
//
// RACCOURCIS env (API utilisateur primaire) :
// ~chIntro, ~chDrop, ~chBreakdown, ~chBuildup, ~chOutro
// ~chTechno, ~chAcid, ~chDubstep, ~chTrance, ~chDnb, ~chHiphop, ~chWorld
// ~chStop, ~chCustom
// ~chIntro2drop, ~chDropToBreak, ~chOutroFade
// ~chList
// ~cc -- alias env directe vers le dict (toujours valable pour
// inspecter, mais pour APPELS utiliser les ~chXxx)
//
// Tous les wrappers gardes par ~helper !? { ... }.
//
// PREREQUIS : engine + palette/rhythm/sequences.scd + live/mixer.scd
// charges (ou setupAll).
// =====================================================================
(
~ch = ();
// --- SECTIONS de track ---------------------------------------------
~ch[\intro] = { ~seqIntro !? { ~seqIntro.() } };
~ch[\drop] = { ~seqDrop !? { ~seqDrop.() } };
~ch[\breakdown] = { ~seqBreakdown !? { ~seqBreakdown.() } };
~ch[\buildup] = { |bars = 8| ~seqBuildup !? { ~seqBuildup.(bars) } };
~ch[\outro] = { ~seqOutro !? { ~seqOutro.() } };
// --- LOOPS / EVOLUTIONS par genre ----------------------------------
~ch[\techno] = { |repeats = 4| ~seqTechnoLoop !? { ~seqTechnoLoop.(repeats) } };
~ch[\acid] = { ~seqAcidEvolve !? { ~seqAcidEvolve.() } };
~ch[\dubstep] = { ~seqDubstepDrop !? { ~seqDubstepDrop.() } };
~ch[\trance] = { ~seqTrance16 !? { ~seqTrance16.() } };
~ch[\dnb] = { |repeats = 4| ~seqDnBSwitch !? { ~seqDnBSwitch.(repeats) } };
~ch[\hiphop] = { |repeats = 8| ~seqHipHopLoop !? { ~seqHipHopLoop.(repeats) } };
~ch[\world] = { ~seqWorldEvolve !? { ~seqWorldEvolve.() } };
// --- STOP / CUSTOM --------------------------------------------------
~chainRoutine = ~chainRoutine ?? { nil };
~ch[\stop] = {
~chainRoutine !? { ~chainRoutine.stop; ~chainRoutine = nil };
~seqStop !? { ~seqStop.() };
};
~ch[\custom] = { |actions| ~seqPlay !? { ~seqPlay.(actions) } };
// --- COMBOS pre-faits (enchaines) -----------------------------------
~ch[\full] = ();
~ch[\full][\intro2drop] = { |introDur = 4, buildDur = 4|
~chainRoutine !? { ~chainRoutine.stop };
~chainRoutine = Routine({
~seqIntro !? { ~seqIntro.() };
introDur.wait;
~seqBuildup !? { ~seqBuildup.(buildDur) };
(buildDur * ~stepDur ? 0.25).wait;
~seqDrop !? { ~seqDrop.() };
~chainRoutine = nil;
}).play(TempoClock.default);
"[ch.full.intro2drop] intro -> buildup -> drop".postln;
};
~ch[\full][\dropToBreak] = { |breakDur = 8|
~chainRoutine !? { ~chainRoutine.stop };
~chainRoutine = Routine({
~seqDrop !? { ~seqDrop.() };
4.wait;
~seqBreakdown !? { ~seqBreakdown.() };
breakDur.wait;
~seqDrop !? { ~seqDrop.() };
~chainRoutine = nil;
}).play(TempoClock.default);
"[ch.full.dropToBreak] drop -> break -> drop".postln;
};
~ch[\full][\outroFade] = { |dur = 12|
~seqOutro !? { ~seqOutro.() };
~masterFadeOut !? { ~masterFadeOut.(dur) };
("[ch.full.outroFade] outro + fade " ++ dur ++ "s").postln;
};
// --- LISTE ----------------------------------------------------------
~ch[\list] = {
"=== ~ch -- CHAINS / SEQUENCES ===".postln;
" sections : ~chIntro / ~chDrop / ~chBreakdown / ~chBuildup / ~chOutro".postln;
" loops : ~chTechno / ~chAcid / ~chDubstep / ~chTrance / ~chDnb / ~chHiphop / ~chWorld".postln;
" stop : ~chStop.()".postln;
" custom : ~chCustom.([[N, action], ...])".postln;
" combos : ~chIntro2drop.(4,4) | ~chDropToBreak.() | ~chOutroFade.(12)".postln;
};
// --- API utilisateur : variables d'env top-level --------------------
~chIntro = { ~ch[\intro].() };
~chDrop = { ~ch[\drop].() };
~chBreakdown = { ~ch[\breakdown].() };
~chBuildup = { |bars = 8| ~ch[\buildup].(bars) };
~chOutro = { ~ch[\outro].() };
~chTechno = { |repeats = 4| ~ch[\techno].(repeats) };
~chAcid = { ~ch[\acid].() };
~chDubstep = { ~ch[\dubstep].() };
~chTrance = { ~ch[\trance].() };
~chDnb = { |repeats = 4| ~ch[\dnb].(repeats) };
~chHiphop = { |repeats = 8| ~ch[\hiphop].(repeats) };
~chWorld = { ~ch[\world].() };
~chStop = { ~ch[\stop].() };
~chCustom = { |actions| ~ch[\custom].(actions) };
~chIntro2drop = { |introDur = 4, buildDur = 4| ~ch[\full][\intro2drop].(introDur, buildDur) };
~chDropToBreak = { |breakDur = 8| ~ch[\full][\dropToBreak].(breakDur) };
~chOutroFade = { |dur = 12| ~ch[\full][\outroFade].(dur) };
~chList = { ~ch[\list].() };
// --- ALIAS court (catalogue, pour inspection) -----------------------
~cc = ~ch; // ~cc[\drop].() encore valable, mais prefere ~chDrop.()
"=== ~ch pret -- ~chDrop.() | ~chTechno.(4) | ~chIntro2drop.() | ~chList.() ===".postln;
)
// =====================================================================
// EXEMPLES (Cmd+Entree)
// =====================================================================
// --- sections ---
// ( ~chIntro.(); )
// ( ~chDrop.(); )
// ( ~chBreakdown.(); )
// ( ~chBuildup.(8); )
// ( ~chOutro.(); )
// --- loops par genre ---
// ( ~chTechno.(4); )
// ( ~chAcid.(); )
// ( ~chDubstep.(); )
// ( ~chTrance.(); )
// ( ~chDnb.(4); )
// ( ~chHiphop.(8); )
// ( ~chWorld.(); )
// --- stop ---
// ( ~chStop.(); )
// --- combos pré-faits ---
// ( ~chIntro2drop.(4, 4); )
// ( ~chDropToBreak.(8); )
// ( ~chOutroFade.(12); )
// --- custom (chaine arbitraire) ---
// ( ~chCustom.([
// [ 16, { ~kickSteps = [1,0,0,0]!4.flat; ~reload.value } ],
// [ 16, { ~kickSteps = [1,0,1,0]!4.flat; ~reload.value } ],
// [ 16, { ~kickSteps = (0!16); ~reload.value } ]
// ]); )
// --- liste ---
// ( ~chList.(); )
+291
View File
@@ -0,0 +1,291 @@
// =====================================================================
// live/fx.scd -- FX LIVE (API ultra-courte, wrappers consolides)
//
// ATTENTION SC -- noter la notation des appels :
//
// NE MARCHE PAS : ~fx.preset.(\dub)
// -> SC passe l'event ~fx comme 1er arg, et les vrais args sont
// PERDUS (test empirique sur SC 3.14.1).
//
// MARCHE BIEN : ~fx[\preset].(\dub) (notation indexee)
// ~fxPreset.(\dub) (variable env directe)
// ~ff.(\dub) (alias env direct)
//
// ~fx // dict catalogue
// ~fx[\preset].(\dub|\trance|...)
// ~fx[\snd].(\rev, 0.4) // ~send (.send reservee)
// ~fx[\st].(\dly, \delayTime, 0.5) // ~setFx (.set reservee)
// ~fx[\tp].() // tap tempo (.tap reservee)
// ~fx[\freeze].(0|1)
// ~fx[\drop] / [\breakdown] / [\buildup]
// ~fx[\glitch] / [\stutter] / [\freezeR]
// ~fx[\tapeStop] / [\tapeRev] / [\handsUp] / [\jumpCut]
// ~fx[\bitCrush] / [\ring] / [\shift] / [\gran]
// ~fx[\vcf][\on], [\off], [\freq], [\q], [\sweep], [\preset]
// ~fx[\lfo][\go] / [\stop][\go] / [\stop][\all]
// ~fx[\cut] / [\rq] / [\drive]
// ~fx[\comp][\go] / [\off] ~fx[\sat][\go] / [\off]
// ~fx[\list].()
//
// RACCOURCIS env (API utilisateur primaire) :
// ~fxPreset, ~fxSnd, ~fxSt, ~fxTp, ~fxFreeze
// ~fxDrop, ~fxBreakdown, ~fxBuildup
// ~fxGlitch, ~fxStutter, ~fxFreezeR
// ~fxTapeStop, ~fxTapeRev, ~fxHandsUp, ~fxJumpCut
// ~fxBitCrush, ~fxRing, ~fxShift, ~fxGran
// ~fxVcfOn, ~fxVcfOff, ~fxVcfFreq, ~fxVcfQ, ~fxVcfSweep, ~fxVcfPreset
// ~fxLfo, ~fxLfoStop, ~fxLfoStopAll
// ~fxCut, ~fxRq, ~fxDrive
// ~fxComp, ~fxCompOff, ~fxSat, ~fxSatOff
// ~fxList
// ~ff (= ~fxPreset)
//
// TOUS les wrappers gardes par ~helper !? { ... } pour ne pas planter
// si le helper sous-jacent n'est pas charge.
//
// PREREQUIS : engine + live/fx_bus.scd + live/live.scd charges.
// =====================================================================
(
~fx = ();
// --- PRESETS FX (dub/space/trance/...) ------------------------------
~fx[\preset] = { |name = \dry|
~fxPreset !? { ~fxPreset.(name) };
};
// --- SEND / SET sur un bus FX --------------------------------------
~fx[\snd] = { |fxName, amount = 0.3|
~send !? { ~send.(fxName, amount) };
};
~fx[\st] = { |fxName, param, value|
~setFx !? { ~setFx.(fxName, param, value) };
};
// --- FREEZE reverb permanent / TAP tempo ---------------------------
~fx[\freeze] = { |on = 1|
~setFx !? { ~setFx.(\rev, \freeze, on) };
("[fx.freeze] " ++ on).postln;
};
~fx[\tp] = { ~tap !? { ~tap.() } };
// --- DROP / BREAKDOWN / BUILDUP ------------------------------------
~fx[\drop] = { |dur = 4| ~drop !? { ~drop.(dur) } };
~fx[\breakdown] = { |dur = 8| ~breakdown !? { ~breakdown.(dur) } };
~fx[\buildup] = { |dur = 8| ~buildup !? { ~buildup.(dur) } };
// --- TRICKS scéniques ----------------------------------------------
~fx[\glitch] = { |dur = 2| ~glitch !? { ~glitch.(dur) } };
~fx[\stutter] = { |rate = 8, dur = 1| ~stutter !? { ~stutter.(rate, dur) } };
~fx[\freezeR] = { |dur = 4| ~freeze !? { ~freeze.(dur) } };
~fx[\tapeStop] = { |dur = 2| ~tapeStop !? { ~tapeStop.(dur) } };
~fx[\tapeStart] = { |dur = 2, t| ~tapeStart !? { ~tapeStart.(dur, t) } };
~fx[\playAll] = { ~playAll !? { ~playAll.() } };
~fx[\tapeRev] = { |dur = 4| ~tapeReverse!? { ~tapeReverse.(dur) } };
~fx[\handsUp] = { ~handsUp !? { ~handsUp.() } };
~fx[\jumpCut] = { |dur = 0.5| ~jumpCut !? { ~jumpCut.(dur) } };
~fx[\bitCrush] = { |bits = 6, sr = 6000, dur = 1| ~bitCrushNow !? { ~bitCrushNow.(bits, sr, dur) } };
~fx[\ring] = { |freq = 200, dur = 1| ~ringMod !? { ~ringMod.(freq, dur) } };
~fx[\shift] = { |hz = 100, dur = 1| ~freqShift !? { ~freqShift.(hz, dur) } };
~fx[\gran] = { |dur = 2| ~granFreeze!? { ~granFreeze.(dur) } };
// --- VCF (filtre live tout-source) ---------------------------------
~fx[\vcf] = ();
~fx[\vcf][\on] = { |type = \moog, freq = 1500, q = 0.5|
~vcfOn !? { ~vcfOn.(type, freq, q) };
};
~fx[\vcf][\off] = { ~vcfOff !? { ~vcfOff.() } };
~fx[\vcf][\freq] = { |hz = 1500| ~vcfSet !? { ~vcfSet.(\freq, hz) } };
~fx[\vcf][\q] = { |val = 0.5| ~vcfSet !? { ~vcfSet.(\q, val) } };
~fx[\vcf][\sweep] = { |from = 200, to = 5000, dur = 4|
~vcfSweep !? { ~vcfSweep.(from, to, dur) };
};
~fx[\vcf][\preset] = { |preset = \acid|
~vcfPreset !? { ~vcfPreset.(preset) };
};
// --- LFO sur n'importe quelle variable ~xxx ------------------------
~fx[\lfo] = ();
~fx[\lfo][\go] = { |paramName, rate = 0.5, depth = 0.3, shape = \sin, center = nil|
~lfoTo !? { ~lfoTo.(paramName, rate, depth, shape, center) };
};
~fx[\lfo][\stop] = ();
~fx[\lfo][\stop][\go] = { |paramName| ~lfoStopVar !? { ~lfoStopVar.(paramName) } };
~fx[\lfo][\stop][\all] = { ~lfoStopAllVars !? { ~lfoStopAllVars.() } };
// --- SHORTCUTS cutoff / rq / drive par voie ------------------------
~fx[\cut] = { |voie = \mel, hz = 2000|
var v = voie.asSymbol;
if (v == \mel or: { v == \melody }) {
~melodyCutoff = hz; ~reload !? { ~reload.value };
("[fx.cut] melody " ++ hz ++ "Hz").postln;
} { if (v == \acid) {
~acidCutoff = hz; ~reload !? { ~reload.value };
("[fx.cut] acid " ++ hz ++ "Hz").postln;
} { ("[fx.cut] voie inconnue : " ++ v ++ " (utilise \\mel ou \\acid)").postln } };
};
~fx[\rq] = { |voie = \mel, val = 0.5|
var v = voie.asSymbol;
if (v == \mel or: { v == \melody }) {
~melodyRq = val; ~reload !? { ~reload.value };
("[fx.rq] melody " ++ val).postln;
} { if (v == \acid) {
~acidRq = val; ~reload !? { ~reload.value };
("[fx.rq] acid " ++ val).postln;
} { ("[fx.rq] voie inconnue : " ++ v).postln } };
};
~fx[\drive] = { |voie = \mel, val = 1.5|
var v = voie.asSymbol;
if (v == \mel or: { v == \melody }) {
~melodyDrive = val; ~reload !? { ~reload.value };
("[fx.drive] melody " ++ val).postln;
} { if (v == \acid) {
~acidDrive = val; ~reload !? { ~reload.value };
("[fx.drive] acid " ++ val).postln;
} { ("[fx.drive] voie inconnue : " ++ v).postln } };
};
// --- COMP / SAT master ---------------------------------------------
~fx[\comp] = ();
~fx[\comp][\go] = { |preset = \glue| ~compOn !? { ~compOn.(preset) } };
~fx[\comp][\off] = { ~compOff !? { ~compOff.() } };
~fx[\sat] = ();
~fx[\sat][\go] = { |mode = \tanh, amount = 1.5|
var modeNum = switch (mode.asSymbol, \tanh, 0, \softclip, 1, \fold, 2, 0);
~satOn !? { ~satOn.(amount, modeNum) };
};
~fx[\sat][\off] = { ~satOff !? { ~satOff.() } };
// --- LISTE -----------------------------------------------------------
~fx[\list] = {
"=== ~fx -- FX LIVE ===".postln;
" presets : dry / dub / space / trance / dubstep / retro /".postln;
" industrial / dream / cosmic / drone".postln;
" bus : rev / dly / chr / phs / flg / crsh / tape / shim / dist".postln;
" tricks : drop / breakdown / buildup / glitch / stutter / freezeR /".postln;
" tapeStop / tapeRev / handsUp / jumpCut / bitCrush / ring /".postln;
" shift / gran".postln;
" vcf : on / off / freq / q / sweep / preset".postln;
" lfo : ~fxLfo.(\\param,rate,depth) | ~fxLfoStop.(\\p) | ~fxLfoStopAll.()".postln;
" voie cut : ~fxCut.(\\mel|\\acid, hz)".postln;
" comp/sat : ~fxComp.(\\glue) | ~fxSat.(\\tanh, 1.5)".postln;
};
// --- API utilisateur : variables d'env top-level --------------------
// SUPPRIME : ~fxPreset etait redefinie ici en wrapper de ~fx[\preset],
// mais ~fx[\preset] appelle DEJA ~fxPreset -> RECURSION INFINIE.
// Cause de la fuite RAM 40GB+ historique. ~fxPreset reste celui de
// fx_bus.scd (vraie implementation). ~ff l'alias court reste plus bas.
// ~fxPreset = { |name = \dry| ~fx[\preset].(name) }; // SUPPRIME
~fxSnd = { |fxName, amount = 0.3| ~fx[\snd].(fxName, amount) };
~fxSt = { |fxName, param, value| ~fx[\st].(fxName, param, value) };
~fxTp = { ~fx[\tp].() };
~fxFreeze = { |on = 1| ~fx[\freeze].(on) };
~fxDrop = { |dur = 4| ~fx[\drop].(dur) };
~fxBreakdown = { |dur = 8| ~fx[\breakdown].(dur) };
~fxBuildup = { |dur = 8| ~fx[\buildup].(dur) };
~fxGlitch = { |dur = 2| ~fx[\glitch].(dur) };
~fxStutter = { |rate = 8, dur = 1| ~fx[\stutter].(rate, dur) };
~fxFreezeR = { |dur = 4| ~fx[\freezeR].(dur) };
~fxTapeStop = { |dur = 2| ~fx[\tapeStop].(dur) };
~fxTapeStart = { |dur = 2, t| ~fx[\tapeStart].(dur, t) };
~fxPlayAll = { ~fx[\playAll].() };
~fxTapeRev = { |dur = 4| ~fx[\tapeRev].(dur) };
~fxHandsUp = { ~fx[\handsUp].() };
~fxJumpCut = { |dur = 0.5| ~fx[\jumpCut].(dur) };
~fxBitCrush = { |bits = 6, sr = 6000, dur = 1| ~fx[\bitCrush].(bits, sr, dur) };
~fxRing = { |freq = 200, dur = 1| ~fx[\ring].(freq, dur) };
~fxShift = { |hz = 100, dur = 1| ~fx[\shift].(hz, dur) };
~fxGran = { |dur = 2| ~fx[\gran].(dur) };
~fxVcfOn = { |type = \moog, freq = 1500, q = 0.5| ~fx[\vcf][\on].(type, freq, q) };
~fxVcfOff = { ~fx[\vcf][\off].() };
~fxVcfFreq = { |hz = 1500| ~fx[\vcf][\freq].(hz) };
~fxVcfQ = { |val = 0.5| ~fx[\vcf][\q].(val) };
~fxVcfSweep = { |from = 200, to = 5000, dur = 4| ~fx[\vcf][\sweep].(from, to, dur) };
~fxVcfPreset = { |preset = \acid| ~fx[\vcf][\preset].(preset) };
~fxLfo = { |paramName, rate = 0.5, depth = 0.3, shape = \sin, center = nil|
~fx[\lfo][\go].(paramName, rate, depth, shape, center) };
~fxLfoStop = { |paramName| ~fx[\lfo][\stop][\go].(paramName) };
~fxLfoStopAll = { ~fx[\lfo][\stop][\all].() };
~fxCut = { |voie = \mel, hz = 2000| ~fx[\cut].(voie, hz) };
~fxRq = { |voie = \mel, val = 0.5| ~fx[\rq].(voie, val) };
~fxDrive = { |voie = \mel, val = 1.5| ~fx[\drive].(voie, val) };
~fxComp = { |preset = \glue| ~fx[\comp][\go].(preset) };
~fxCompOff = { ~fx[\comp][\off].() };
~fxSat = { |mode = \tanh, amount = 1.5| ~fx[\sat][\go].(mode, amount) };
~fxSatOff = { ~fx[\sat][\off].() };
~fxList = { ~fx[\list].() };
~ff = { |name = \dry| ~fxPreset.(name) }; // alias court
"=== ~fx pret -- ~ff.(\\dub) | ~fxDrop.() | ~fxVcfPreset.(\\acid) | ~fxList.() ===".postln;
)
// =====================================================================
// EXEMPLES (Cmd+Entree)
// =====================================================================
// --- presets prets ---
// ( ~ff.(\dub); )
// ( ~ff.(\trance); )
// ( ~ff.(\dubstep); )
// ( ~ff.(\space); )
// ( ~ff.(\dry); )
// --- send / set manuel ---
// ( ~fxSnd.(\rev, 0.4); )
// ( ~fxSnd.(\dly, 0.3); )
// ( ~fxSt.(\dly, \delayTime, 0.375); )
// ( ~fxSt.(\rev, \room, 0.95); )
// --- freeze reverb ---
// ( ~fxFreeze.(1); ~fxSnd.(\rev, 0.6); )
// ( ~fxFreeze.(0); )
// --- drop / breakdown / buildup ---
// ( ~fxDrop.(); )
// ( ~fxBreakdown.(8); )
// ( ~fxBuildup.(8); )
// --- tricks scéniques ---
// ( ~fxGlitch.(2); )
// ( ~fxStutter.(8, 1); )
// ( ~fxFreezeR.(4); )
// ( ~fxTapeStop.(2); )
// ( ~fxTapeRev.(4); )
// ( ~fxHandsUp.(); )
// ( ~fxJumpCut.(0.5); )
// ( ~fxBitCrush.(4, 4000, 1); )
// ( ~fxRing.(200, 1); )
// ( ~fxShift.(100, 1); )
// ( ~fxGran.(2); )
// --- VCF live ---
// ( ~fxVcfPreset.(\acid); )
// ( ~fxVcfPreset.(\dub); )
// ( ~fxVcfOn.(\moog, 800, 0.7); )
// ( ~fxVcfFreq.(1200); )
// ( ~fxVcfQ.(0.6); )
// ( ~fxVcfSweep.(200, 4000, 6); )
// ( ~fxVcfOff.(); )
// --- LFO sur var ---
// ( ~fxLfo.(\melodyCutoff, 0.2, 800, \sin, 1500); )
// ( ~fxLfoStop.(\melodyCutoff); )
// ( ~fxLfoStopAll.(); )
// --- shortcuts par voie ---
// ( ~fxCut.(\mel, 1200); )
// ( ~fxCut.(\acid, 600); )
// ( ~fxRq.(\mel, 0.7); )
// ( ~fxDrive.(\acid, 3.0); )
// --- comp / sat master ---
// ( ~fxComp.(\glue); )
// ( ~fxCompOff.(); )
// ( ~fxSat.(\tanh, 2.0); )
// ( ~fxSatOff.(); )
// --- liste ---
// ( ~fxList.(); )
+340
View File
@@ -0,0 +1,340 @@
// =====================================================================
// live/fx_bus.scd -- Systeme FX live complet
//
// 9 bus FX : reverb / delay / chorus / phaser / flanger / bitcrush /
// tape / shimmer / distortion
// + helpers send / setFx
// + automation LFO sur tout param
// + presets / snapshots
// + tap tempo pour delay
// + freeze reverb (capture)
// + crossfade entre FX
//
// USAGE
// 1. SETUP FX -- charge SynthDefs + alloue les bus + lance les FX
// en queue + cree les helpers (~send / ~lfo / ~fxPreset / ~tap /
// ~crossfadeFx). Compatible .load (un seul bloc top-level).
// 2. Helpers ~sendXxx pour envoyer dans un FX (live)
// 3. Helpers ~lfoXxx pour automatiser un parametre (live)
// 4. Presets ~fxPreset.(\name) pour rappeler un combo (live)
// =====================================================================
// =====================================================================
// 1. SETUP FX (single-block) -- SynthDefs + bus + FX en queue + helpers
// =====================================================================
(
// --- BUS allocation ---
~fxBuses !? { ~fxBuses.do(_.free) };
~fxBuses = (
rev: Bus.audio(s, 2),
dly: Bus.audio(s, 2),
chr: Bus.audio(s, 2),
phs: Bus.audio(s, 2),
flg: Bus.audio(s, 2),
crsh: Bus.audio(s, 2),
tape: Bus.audio(s, 2),
shim: Bus.audio(s, 2),
dist: Bus.audio(s, 2),
);
// --- REVERB (FreeVerb stereo + freeze) ---
// --- DELAY ping-pong avec saturation tape ---
// --- CHORUS (modulation stereo) ---
// --- PHASER (4-stage allpass + LFO) ---
// --- FLANGER (court delay + LFO + feedback) ---
// --- BITCRUSHER (lo-fi sample rate + bit depth) ---
// --- TAPE (saturation analog + wow/flutter) ---
// --- SHIMMER REVERB (octave up + reverb hall) ---
// --- DISTORTION (overdrive + tone) ---
// --- SEND (copie audio vers un FX bus, ratio reglable) ---
s.sync;
// --- INSTANCIER les 9 FX en queue + master limiter ----------------
// .do sur Event : 1 arg = value. Le 2-arg form est |value, key| (synth d'abord).
~fxFx !? { ~fxFx.do { |syn| syn !? { syn.free } } };
~fxFx = (
rev: Synth.tail(s.defaultGroup, \fxReverb, [\in, ~fxBuses[\rev].index]),
dly: Synth.tail(s.defaultGroup, \fxDelay, [\in, ~fxBuses[\dly].index]),
chr: Synth.tail(s.defaultGroup, \fxChorus, [\in, ~fxBuses[\chr].index]),
phs: Synth.tail(s.defaultGroup, \fxPhaser, [\in, ~fxBuses[\phs].index]),
flg: Synth.tail(s.defaultGroup, \fxFlanger, [\in, ~fxBuses[\flg].index]),
crsh: Synth.tail(s.defaultGroup, \fxBitcrush,[\in, ~fxBuses[\crsh].index]),
tape: Synth.tail(s.defaultGroup, \fxTape, [\in, ~fxBuses[\tape].index]),
shim: Synth.tail(s.defaultGroup, \fxShimmer, [\in, ~fxBuses[\shim].index]),
dist: Synth.tail(s.defaultGroup, \fxDist, [\in, ~fxBuses[\dist].index, \mix, 0.5]),
);
// Master limiter en queue absolue
~masterLim !? { ~masterLim.free };
~masterLim = Synth.tail(s.defaultGroup, \masterLim);
~fxSends = (); // dictionnaire des Synth de send actifs
~fxLfos = (); // dictionnaire des Routines LFO actives
// ---------- HELPERS SEND ---------------------------------------------
~send = { |fxName, amount = 0.3|
var key = fxName.asSymbol;
~fxSends[key] !? { ~fxSends[key].free };
if (amount > 0) {
~fxSends[key] = Synth.before(~masterLim, \fxSend,
[\srcBus, 0, \dstBus, ~fxBuses[key].index, \amount, amount]);
} { ~fxSends.removeAt(key) };
("send " ++ key ++ " = " ++ amount).postln;
};
// Raccourcis
~sendReverb = { |a = 0.3| ~send.(\rev, a) };
~sendDelay = { |a = 0.3| ~send.(\dly, a) };
~sendChorus = { |a = 0.3| ~send.(\chr, a) };
~sendPhaser = { |a = 0.3| ~send.(\phs, a) };
~sendFlanger = { |a = 0.3| ~send.(\flg, a) };
~sendBitcrush = { |a = 0.3| ~send.(\crsh, a) };
~sendTape = { |a = 1.0| ~send.(\tape, a) };
~sendShimmer = { |a = 0.3| ~send.(\shim, a) };
~sendDist = { |a = 0.3| ~send.(\dist, a) };
// Set un parametre de FX (~setFx.(\rev, \room, 0.95))
~setFx = { |fxName, param, value|
~fxFx[fxName.asSymbol] !? { ~fxFx[fxName.asSymbol].set(param, value) };
};
// ---------- AUTOMATION LFO -------------------------------------------
~lfo = { |fxName, param, rateHz = 0.5, lo = 0.0, hi = 1.0, shape = \sin|
var key = (fxName ++ "_" ++ param).asSymbol;
~fxLfos[key] !? { ~fxLfos[key].stop };
~fxLfos[key] = Routine({
var phase = 0;
inf.do { |i|
var x;
x = switch (shape,
\sin, { (phase * 2 * pi).sin * 0.5 + 0.5 },
\tri, { (phase * 4 - 1).abs.linlin(0, 1, 1, 0) },
\saw, { phase },
\sqr, { if (phase < 0.5) {0} {1} },
\rand, { 1.0.rand }
);
~setFx.(fxName, param, lo + (x * (hi - lo)));
phase = (phase + (rateHz * 0.05)).mod(1);
0.05.wait;
};
}).play;
("LFO " ++ fxName ++ "." ++ param ++ " " ++ rateHz ++ "Hz " ++ shape).postln;
};
~lfoStop = { |fxName, param|
var key = (fxName ++ "_" ++ param).asSymbol;
~fxLfos[key] !? { ~fxLfos[key].stop; ~fxLfos.removeAt(key) };
("LFO " ++ fxName ++ "." ++ param ++ " STOPPED").postln;
};
~lfoStopAll = {
~fxLfos.do { |r| r.stop }; ~fxLfos = ();
"all LFOs stopped".postln;
};
// ---------- TAP TEMPO pour le delay ----------------------------------
~tapTempo = ();
~tap = {
var now = SystemClock.seconds;
~tapTempo[\last] !? { |last|
var interval = now - last;
if (interval < 2.0 and: { interval > 0.1 }) {
~setFx.(\dly, \delayTime, interval);
("TAP : " ++ interval.round(0.001) ++ "s = " ++
(60 / interval).round(0.1) ++ " BPM").postln;
};
};
~tapTempo[\last] = now;
};
// ---------- CROSSFADE entre 2 FX -------------------------------------
~crossfadeFxRoutine = ~crossfadeFxRoutine ?? { nil };
~crossfadeFx = { |fromFx, toFx, dur = 4|
~crossfadeFxRoutine !? { ~crossfadeFxRoutine.stop };
~crossfadeFxRoutine = Routine({
var steps = (dur * 20).asInteger;
steps.do { |i|
var x = (i + 1) / steps;
~send.(fromFx, 0.3 * (1 - x));
~send.(toFx, 0.3 * x);
(dur / steps).wait;
};
~crossfadeFxRoutine = nil;
}).play;
("crossfade " ++ fromFx ++ " -> " ++ toFx ++ " sur " ++ dur ++ "s").postln;
};
~crossfadeFxStop = {
~crossfadeFxRoutine !? { ~crossfadeFxRoutine.stop; ~crossfadeFxRoutine = nil };
"crossfade stopped".postln;
};
// ---------- PRESETS FX (combos pret a l'emploi par genre) ------------
//
// REVERT : ~fxPresetReset retiree car cause une fuite/crash explosive
// sclang quand combinee avec switch+sub-calls. La cascade des sends
// reste un probleme connu mais geree par ~ff.(\dry) explicit reset.
~fxPresetReset = nil;
// Dispatch via dictionary pour eviter le bug de switch() qui crashe sclang
// avec 11 cases sur certaines versions SC.
~fxPresetTable = (
dry: {
[\rev,\dly,\chr,\phs,\flg,\crsh,\tape,\shim,\dist]
.do { |k| ~send.(k, 0) };
"preset DRY".postln;
},
dub: {
~sendReverb.(0.5); ~sendDelay.(0.6); ~sendTape.(0.7);
~setFx.(\dly, \delayTime, 0.5);
~setFx.(\dly, \feedback, 0.7);
~setFx.(\rev, \room, 0.92);
"preset DUB (rev hall + delay long + tape)".postln;
},
space: {
~sendReverb.(0.55); ~sendShimmer.(0.4); ~sendChorus.(0.3);
~setFx.(\rev, \room, 0.95);
"preset SPACE (shimmer + reverb + chorus)".postln;
},
trance: {
~sendReverb.(0.35); ~sendDelay.(0.4); ~sendChorus.(0.5);
~setFx.(\dly, \delayTime, 0.375);
~setFx.(\dly, \feedback, 0.5);
"preset TRANCE (rev + delay 1/8 + chorus)".postln;
},
dubstep: {
~sendReverb.(0.2); ~sendDist.(0.3); ~sendBitcrush.(0.2);
~setFx.(\dist, \drive, 4); ~setFx.(\dist, \tone, 0.4);
~setFx.(\crsh, \bits, 6);
"preset DUBSTEP (dist + bitcrush + reverb)".postln;
},
retro: {
~sendBitcrush.(0.4); ~sendTape.(1.0); ~sendChorus.(0.4);
~setFx.(\crsh, \bits, 8); ~setFx.(\crsh, \sampleRate, 12000);
~setFx.(\tape, \drive, 1.8);
"preset RETRO (bitcrush + tape + chorus)".postln;
},
industrial: {
~sendDist.(0.6); ~sendFlanger.(0.4); ~sendBitcrush.(0.3);
~setFx.(\dist, \drive, 5);
~setFx.(\flg, \rate, 0.15); ~setFx.(\flg, \feedback, 0.7);
"preset INDUSTRIAL (dist + flanger + bitcrush)".postln;
},
dream: {
~sendShimmer.(0.6); ~sendReverb.(0.5); ~sendChorus.(0.4);
~setFx.(\shim, \shimmer, 0.7);
"preset DREAM (shimmer + reverb + chorus)".postln;
},
cosmic: {
~sendPhaser.(0.5); ~sendShimmer.(0.4); ~sendReverb.(0.4);
~lfo.(\phs, \rate, 0.05, 0.1, 0.8);
"preset COSMIC (phaser LFO + shimmer + reverb)".postln;
},
drone: {
~sendReverb.(0.7); ~sendShimmer.(0.5);
~setFx.(\rev, \freeze, 1); ~setFx.(\rev, \room, 0.99);
"preset DRONE (reverb FROZEN + shimmer)".postln;
}
);
~fxPreset = { |name|
var fn = ~fxPresetTable[name.asSymbol];
if (fn.notNil) { fn.value } { ("!! preset " ++ name ++ " inconnu").postln };
};
"=== 9 FX BUSES + master limiter en queue ===".postln;
" bus dispos : rev / dly / chr / phs / flg / crsh / tape / shim / dist".postln;
"=== ~send / ~sendReverb / ~setFx / ~lfo / ~tap / ~crossfadeFx / ~fxPreset prets ===".postln;
"=== presets : dry / dub / space / trance / dubstep / retro / industrial / dream / cosmic / drone ===".postln;
)
// =====================================================================
// 2. EXEMPLES / TWEAKS LIVE -- Cmd+Entree sur ligne pendant track
// (tout ce qui est en bas est interactif et NON .load -- garde
// du multi-bloc pour evaluer un effet a la fois)
// =====================================================================
// --- Send levels ----------------------------------------------------
( ~sendReverb.(0.4); ~sendDelay.(0.3); )
( ~sendReverb.(0); ~sendDelay.(0); ~sendChorus.(0); )
( ~sendShimmer.(0.5); ~setFx.(\shim, \shimmer, 0.7); )
( ~sendBitcrush.(0.5); ~setFx.(\crsh, \bits, 4); )
( ~sendDist.(0.5); ~setFx.(\dist, \drive, 4); ~setFx.(\dist, \tone, 0.3); )
( ~sendFlanger.(0.5); ~setFx.(\flg, \rate, 0.15); )
( ~sendPhaser.(0.4); ~setFx.(\phs, \rate, 0.2); ~setFx.(\phs, \depth, 0.8); )
( ~sendTape.(1.0); ~setFx.(\tape, \drive, 2.5); )
// --- Tempo synced delay ---------------------------------------------
( ~setFx.(\dly, \delayTime, 60 / TempoClock.default.tempo / 4); ) // 1/16
( ~setFx.(\dly, \delayTime, 60 / TempoClock.default.tempo / 2); ) // 1/8
( ~setFx.(\dly, \delayTime, 60 / TempoClock.default.tempo); ) // 1/4
( ~setFx.(\dly, \delayTime, 60 / TempoClock.default.tempo * 0.75); ) // 1/4 dotted
// --- Reverb size ----------------------------------------------------
( ~setFx.(\rev, \room, 0.5); ~setFx.(\rev, \damp, 0.6); ) // room
( ~setFx.(\rev, \room, 0.85);~setFx.(\rev, \damp, 0.4); ) // hall
( ~setFx.(\rev, \room, 0.97);~setFx.(\rev, \damp, 0.1); ) // cathedral
// --- LFO automation -------------------------------------------------
( ~lfo.(\dly, \feedback, 0.1, 0.2, 0.85, \sin); ) // feedback qui pulse
( ~lfo.(\rev, \mix, 0.05, 0.1, 0.7, \tri); ) // wash de reverb
( ~lfo.(\crsh, \bits, 0.2, 4, 12, \sqr); ) // glitch lo-fi
( ~lfo.(\phs, \rate, 0.02, 0.1, 1.5, \sin); ) // phaser qui accelere
( ~lfo.(\shim, \shimmer, 0.1, 0.2, 0.8, \rand); ) // shimmer chaos
( ~lfoStopAll.value; )
// --- Tap tempo (au moins 2 fois pour calibrer) ---------------------
( ~tap.value; )
// --- Freeze reverb (drone infini) -----------------------------------
( ~setFx.(\rev, \freeze, 1); ~sendReverb.(0.6); )
( ~setFx.(\rev, \freeze, 0); )
// --- Crossfade entre FX --------------------------------------------
( ~crossfadeFx.(\dly, \rev, 8); ) // delay -> reverb sur 8s
( ~crossfadeFx.(\dist, \shim, 4); ) // dist -> shimmer sur 4s
// --- Presets --------------------------------------------------------
( ~fxPreset.(\dry); ) // tout off
( ~fxPreset.(\dub); ) // dub techno
( ~fxPreset.(\trance); ) // uplifting trance
( ~fxPreset.(\dubstep); ) // dubstep dirty
( ~fxPreset.(\space); ) // space ambient
( ~fxPreset.(\retro); ) // 80s lo-fi
( ~fxPreset.(\industrial); ) // hard distorted
( ~fxPreset.(\dream); ) // dream pop
( ~fxPreset.(\cosmic); ) // psy cosmic + LFO phaser
( ~fxPreset.(\drone); ) // drone freeze infini
// =====================================================================
// 3. STOP FX -- libere tout
// =====================================================================
(
~lfoStopAll.value;
~fxSends.do { |s| s.free }; ~fxSends = ();
~fxFx.do { |s| s.free }; ~fxFx = ();
~fxBuses.do { |b| b.free }; ~fxBuses = ();
"=== ALL FX STOPPED ===".postln;
)
+199
View File
@@ -0,0 +1,199 @@
// =====================================================================
// live/kicks.scd -- KICKS LIVE (API ultra-courte)
//
// ATTENTION SC -- noter la notation des appels :
//
// NE MARCHE PAS : ~k.pat.(\amen)
// -> SC passe l'event ~k comme 1er arg, et les vrais args sont
// PERDUS (test empirique sur SC 3.14.1, cf Dictionary.sc:549).
//
// MARCHE BIEN : ~k[\pat].(\amen) (notation indexee)
// ~kPat.(\amen) (variable env directe)
// ~kk.(\techno) (alias env direct)
//
// ~k // dict : catalogue de toutes les
// // donnees + helpers
// ~k[\pat].(\name|N) // applique SEULEMENT un pattern
// ~k[\tim].(\name|N) // applique SEULEMENT un timbre
// ~k[\go].(\name) // pattern + timbre + reload
// ~k[\list].() // liste tout
//
// RACCOURCIS env (API utilisateur primaire) :
// ~kPat.(\amen) | ~kTim.(\808) | ~kk.(\techno) (combo)
// ~kList.()
//
// EXEMPLES :
// ~kk.(\techno) // 4/4 + rond
// ~kk.(\gabber) // dense + distordu
// ~kPat.(\amen) // juste le pattern amen-break
// ~kTim.(\808) // juste le timbre 808 boom
// ~kPat.(7) // pattern n°7
//
// PREREQUIS : engine.scd evalue (defines ~kickSteps, ~kickFreq, etc.)
// =====================================================================
(
~k = ();
// --- 25 patterns kick (16 steps chacun) -----------------------------
~k[\patterns] = (
\four: [ 1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0 ],
\techno: [ 1,0,0,0, 1,0,0,1, 1,0,0,0, 1,0,1,0 ],
\gabber: [ 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0 ],
\dembow: [ 1,0,0,1, 0,0,1,0, 1,0,0,1, 0,0,1,0 ],
\half: [ 1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0 ],
\break: [ 1,0,0,0, 0,0,1,0, 0,0,1,0, 1,0,0,0 ],
\triplet: [ 1,0,0,1, 0,0,1,0, 0,1,0,0, 1,0,0,1 ],
\minimal: [ 1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 ],
\ghost: [ 1,0,0,0, 1,0,0,1, 1,0,0,0, 1,0,1,0 ],
\synco: [ 1,0,1,0, 1,0,0,1, 1,0,1,0, 1,0,1,1 ],
\dnb: [ 1,0,0,1, 0,0,0,0, 0,0,1,0, 0,0,1,0 ],
\amen: [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ],
\dense: [ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1 ],
\hardstyle:[1,0,0,0, 1,0,0,1, 1,0,0,0, 1,1,0,0 ],
\trance: [ 1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,1 ],
\house: [ 1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0 ],
\electro: [ 1,0,0,1, 0,0,0,0, 1,0,0,0, 0,0,1,0 ],
\triphop: [ 1,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,0 ],
\jungle: [ 1,0,0,1, 0,1,0,0, 1,0,0,1, 0,1,0,0 ],
\dub: [ 0,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0 ],
\afro: [ 1,0,0,1, 0,1,0,0, 1,0,1,0, 0,1,0,0 ],
\garage: [ 1,0,0,0, 0,0,1,0, 0,0,0,0, 1,0,0,0 ],
\footwork:[ 1,0,0,1, 1,0,0,0, 1,0,1,1, 0,0,1,0 ],
\off: (0 ! 16),
);
// --- 12 timbres kick (params SynthDef) ------------------------------
~k[\timbres] = (
\rond: (freq: 55, pitchAmt: 3.0, decay: 0.32, drive: 1.3, click: 0.2),
\sub: (freq: 42, pitchAmt: 2.0, decay: 0.50, drive: 1.0, click: 0.05),
\punchy: (freq: 65, pitchAmt: 4.0, decay: 0.18, drive: 1.8, click: 0.45),
\distorded:(freq:60, pitchAmt: 5.0, decay: 0.28, drive: 3.5, click: 0.30),
\click: (freq: 70, pitchAmt: 2.5, decay: 0.10, drive: 1.0, click: 0.60),
\boomy: (freq: 50, pitchAmt: 6.0, decay: 0.40, drive: 1.5, click: 0.10),
\clap: (freq: 90, pitchAmt: 1.5, decay: 0.08, drive: 1.2, click: 0.50),
\909: (freq: 60, pitchAmt: 3.5, decay: 0.45, drive: 1.4, click: 0.15),
\808: (freq: 38, pitchAmt: 1.8, decay: 0.65, drive: 1.0, click: 0.05),
\gabberT: (freq: 65, pitchAmt: 5.5, decay: 0.22, drive: 4.5, click: 0.50),
\breakcore:(freq:80, pitchAmt: 2.0, decay: 0.06, drive: 2.5, click: 0.70),
\dubT: (freq: 40, pitchAmt: 1.5, decay: 0.80, drive: 1.0, click: 0.02),
);
// --- Combos pre-mappes (1 nom = pattern + timbre + reload) ----------
~k[\combos] = (
\techno: [\techno, \rond],
\gabber: [\gabber, \gabberT],
\dub: [\dub, \dubT],
\dubstep: [\half, \808],
\trance: [\trance, \punchy],
\hardstyle: [\hardstyle, \distorded],
\dnb: [\dnb, \punchy],
\amen: [\amen, \punchy],
\jungle: [\jungle, \click],
\house: [\house, \909],
\electro: [\electro, \click],
\detroit: [\four, \909],
\minimal: [\minimal, \rond],
\phonk: [\half, \808],
\afro: [\afro, \rond],
\garage: [\garage, \rond],
\triphop: [\triphop, \boomy],
\footwork: [\footwork, \punchy],
);
// --- Fonctions stockees dans le dict (appelees via ~k[\key].(args)) -
// Signatures normales (PAS de `self`) -- jamais appelees en pseudo-
// method (`.key`) sur l'event `~k`.
~k[\pat] = { |name|
var p = if (name.isInteger) {
~k[\patterns].values[(name - 1).clip(0, ~k[\patterns].size - 1)];
} { ~k[\patterns][name] };
if (p.isNil) {
("[k] pattern '" ++ name ++ "' inconnu. Liste : " ++ ~k[\patterns].keys.asArray).postln;
} {
~kickSteps = p.copy;
~reload.value;
("[k.pat] " ++ name).postln;
};
};
~k[\tim] = { |name|
var t = if (name.isInteger) {
~k[\timbres].values[(name - 1).clip(0, ~k[\timbres].size - 1)];
} { ~k[\timbres][name] };
if (t.isNil) {
("[k] timbre '" ++ name ++ "' inconnu. Liste : " ++ ~k[\timbres].keys.asArray).postln;
} {
~kickFreq = t[\freq];
~kickPitchAmt = t[\pitchAmt];
~kickDecay = t[\decay];
~kickDrive = t[\drive];
~kickClick = t[\click];
~reload.value;
("[k.tim] " ++ name).postln;
};
};
~k[\go] = { |name|
var c = ~k[\combos][name];
if (c.isNil) {
~k[\pat].(name);
} {
~k[\pat].(c[0]);
~k[\tim].(c[1]);
("[k] combo " ++ name ++ " (" ++ c[0] ++ "+" ++ c[1] ++ ")").postln;
};
};
~k[\list] = {
"=== ~k -- KICKS ===".postln;
" combos : ".post; ~k[\combos].keys.asArray.sort.postln;
" patterns : ".post; ~k[\patterns].keys.asArray.sort.postln;
" timbres : ".post; ~k[\timbres].keys.asArray.sort.postln;
" USAGE : ~kk.(\\techno) | ~kPat.(\\amen) | ~kTim.(\\808)".postln;
};
// --- API utilisateur : variables d'env top-level --------------------
// Ces wrappers acceptent les vrais args correctement.
~kPat = { |name| ~k[\pat].(name) };
~kTim = { |name| ~k[\tim].(name) };
~kGo = { |name| ~k[\go].(name) };
~kList = { ~k[\list].() };
~kk = { |name| ~k[\go].(name) }; // alias court
"=== ~k pret -- ~kk.(\\techno) | ~kPat.(\\amen) | ~kTim.(\\808) | ~kList.() ===".postln;
)
// =====================================================================
// EXEMPLES (Cmd+Entree pour appliquer)
// =====================================================================
--- combos prets ---
( ~kk.(\techno); )
( ~kk.(\gabber); )
( ~kk.(\dub); )
( ~kk.(\trance); )
( ~kk.(\amen); )
( ~kk.(\hardstyle); )
( ~kk.(\dnb); )
( ~kk.(\phonk); )
( ~kk.(\detroit); )
--- pattern seul ---
( ~kPat.(\amen); )
( ~kPat.(\dense); )
( ~kPat.(\off); ) // silence
--- timbre seul ---
( ~kTim.(\808); )
( ~kTim.(\909); )
( ~kTim.(\distorded); )
--- accès direct par index ---
( ~kPat.(7); ) // pattern n°7
( ~kTim.(3); ) // timbre n°3
--- liste tout ---
( ~kList.(); )
File diff suppressed because it is too large Load Diff
+826
View File
@@ -0,0 +1,826 @@
// =====================================================================
// live_fx_panel.scd -- PANNEAU FX LIVE (tout-en-un)
//
// USAGE :
// 1. PREREQUIS -- evaluer les SETUP de :
// - engine.scd (charge avec CHARGER TOUT du 00_load.scd)
// - fx_bus.scd (Cmd+Entree sur le SETUP)
// - live.scd (Cmd+Entree sur le SETUP)
// 2. Lance une track ou les Pdef
// 3. Cmd+Entree sur n'importe quel bloc de ce panneau pour declencher l'effet
//
// Toutes les commandes sont protegees par garde `~helper !? { ... }`
// pour ne rien casser si un setup n'a pas ete evalue.
//
// CATEGORIES :
// [A] FX BUS DYNAMIQUE (send / setFx / preset / freeze)
// [B] LFO ASSIGNABLES (sur n'importe quelle variable)
// [C] VCF MASTER (8 types + presets)
// [D] TRICKS SCENIQUES (drop / breakdown / buildup / stutter ...)
// [E] FX MOMENTANES (bitcrush / ringmod / freqshift / granfreeze ...)
// [F] AUTO-PAN / TREMOLO
// [G] FX SYNTHS ONESHOT (riser / sweep / impact / crash / vinyl / gong ...)
// [H] FILLS RYTHMIQUES (snareFill / tomFill)
// [I] MASTER COMP / SAT
// [J] FX PAR GENRE (combos prets a l'emploi)
// [K] TAP TEMPO
// [L] FX BUS DETAIL (param par param par FX)
// [M] STOP ALL FX (reset global)
// =====================================================================
// =====================================================================
// [A] FX BUS DYNAMIQUE
// ~send.(\fx, amount) | ~setFx.(\fx, key, val) | ~fxPreset.(\preset)
// =====================================================================
// --- Sends raccourcis (par bus) -----
( ~sendReverb !? { ~sendReverb.(0.4) }; )
( ~sendDelay !? { ~sendDelay.(0.3) }; )
( ~sendChorus !? { ~sendChorus.(0.3) }; )
( ~sendPhaser !? { ~sendPhaser.(0.4) }; )
( ~sendFlanger !? { ~sendFlanger.(0.4) }; )
( ~sendBitcrush !? { ~sendBitcrush.(0.4) }; )
( ~sendTape !? { ~sendTape.(1.0) }; )
( ~sendShimmer !? { ~sendShimmer.(0.5) }; )
( ~sendDist !? { ~sendDist.(0.4) }; )
// --- Sends par FX/key (si tu veux varier) -----
( ~send !? { ~send.(\rev, 0.4) }; )
( ~send !? { ~send.(\dly, 0.3) }; )
( ~send !? { ~send.(\chr, 0.3) }; )
( ~send !? { ~send.(\phs, 0.4) }; )
( ~send !? { ~send.(\flg, 0.4) }; )
( ~send !? { ~send.(\crsh, 0.4) }; )
( ~send !? { ~send.(\tape, 1.0) }; )
( ~send !? { ~send.(\shim, 0.5) }; )
( ~send !? { ~send.(\dist, 0.4) }; )
// --- Tout couper (sec) -----
( ~send !? {
[\rev,\dly,\chr,\phs,\flg,\crsh,\tape,\shim,\dist].do { |k| ~send.(k, 0) };
}; )
// --- Reverb : mix max + room cathedrale -----
( ~setFx !? { ~setFx.(\rev, \mix, 0.7) }; )
( ~setFx !? { ~setFx.(\rev, \room, 0.97); ~setFx.(\rev, \damp, 0.1) }; )
// --- Reverb FREEZE on / off (drone infini) -----
( ~setFx !? { ~setFx.(\rev, \freeze, 1) }; )
( ~setFx !? { ~setFx.(\rev, \freeze, 0) }; )
// --- Crossfade entre 2 FX -----
( ~crossfadeFx !? { ~crossfadeFx.(\dly, \rev, 8) }; )
( ~crossfadeFx !? { ~crossfadeFx.(\dist, \shim, 4) }; )
// --- PRESETS FX (combo predefini sur le bus FX) -----
( ~fxPreset !? { ~fxPreset.(\dry) }; ) // tout off
( ~fxPreset !? { ~fxPreset.(\dub) }; ) // rev hall + delay long + tape
( ~fxPreset !? { ~fxPreset.(\space) }; ) // shimmer + reverb + chorus
( ~fxPreset !? { ~fxPreset.(\trance) }; ) // rev + delay 1/8 + chorus
( ~fxPreset !? { ~fxPreset.(\dubstep) }; ) // dist + bitcrush + reverb
( ~fxPreset !? { ~fxPreset.(\retro) }; ) // bitcrush + tape + chorus
( ~fxPreset !? { ~fxPreset.(\industrial) }; ) // dist + flanger + bitcrush
( ~fxPreset !? { ~fxPreset.(\dream) }; ) // shimmer + reverb + chorus
( ~fxPreset !? { ~fxPreset.(\cosmic) }; ) // phaser LFO + shimmer + reverb
( ~fxPreset !? { ~fxPreset.(\drone) }; ) // reverb FROZEN + shimmer
// =====================================================================
// [B] LFO ASSIGNABLES (~lfoTo / ~lfoSync)
// shape : \sin \tri \saw \sqr \rand \smoothRand \drunk \noise
// =====================================================================
// --- LFO sin sur cutoff melody (rate 0.5Hz, depth 1500, center 2500) -----
( ~lfoTo !? { ~lfoTo.(\melodyCutoff, 0.5, 1500, \sin, 2500) }; )
// --- LFO triangle slow sur cutoff acid -----
( ~lfoTo !? { ~lfoTo.(\acidCutoff, 0.25, 800, \tri, 1000) }; )
// --- LFO saw rising (montee progressive) sur cutoff melody -----
( ~lfoTo !? { ~lfoTo.(\melodyCutoff, 0.2, 2500, \saw, 3000) }; )
// --- LFO sqr asymetrique (pulseWidth=0.3) sur kickAmp -> rythmique -----
( ~lfoTo !? { ~lfoTo.(\kickAmp, 2, 0.2, \sqr, 0.5, true, 0.3) }; )
// --- LFO random (sample-and-hold) sur amp acid -----
( ~lfoTo !? { ~lfoTo.(\acidAmp, 4, 0.15, \rand, 0.32) }; )
// --- LFO smooth random sur amp harmony -----
( ~lfoTo !? { ~lfoTo.(\harmonyAmp, 0.3, 0.08, \smoothRand, 0.14) }; )
// --- LFO drunken walk sur drive melody -----
( ~lfoTo !? { ~lfoTo.(\melodyDrive, 0.1, 0.8, \drunk, 1.2) }; )
// --- LFO noise pure (chaos) sur Rq melody -----
( ~lfoTo !? { ~lfoTo.(\melodyRq, 5, 0.2, \noise, 0.3) }; )
// --- LFO sin sur wobbleRate (bass design en wobble) -----
( ~lfoTo !? { ~lfoTo.(\melodyWobbleRate, 0.1, 4, \sin, 6) }; )
// --- LFO SYNC TEMPO : 1 cycle complet sur N beats -----
( ~lfoSync !? { ~lfoSync.(\melodyCutoff, 4, 1500, \sin, 2500) }; ) // 4 beats
( ~lfoSync !? { ~lfoSync.(\acidCutoff, 8, 600, \sin, 800) }; ) // 8 beats
( ~lfoSync !? { ~lfoSync.(\melodyAmp, 0.25, 0.1, \tri, 0.16) }; ) // 1/4 beat = 16eme
// --- Stop UN LFO precis -----
( ~lfoStopVar !? { ~lfoStopVar.(\melodyCutoff) }; )
( ~lfoStopVar !? { ~lfoStopVar.(\acidCutoff) }; )
( ~lfoStopVar !? { ~lfoStopVar.(\kickAmp) }; )
// --- Stop tous les LFO de variables (~xxx) -----
( ~lfoStopAllVars !? { ~lfoStopAllVars.() }; )
// --- Stop TOUT (LFO vars + LFO FX) -----
( ~lfoStopEverything !? { ~lfoStopEverything.() }; )
// --- Liste les LFO actifs -----
( ~lfoList !? { ~lfoList.() }; )
// --- LFO sur PARAM FX (sur le bus FX, different de ~lfoTo) -----
( ~lfo !? { ~lfo.(\dly, \feedback, 0.1, 0.2, 0.85, \sin) }; )
( ~lfo !? { ~lfo.(\rev, \mix, 0.05, 0.1, 0.7, \tri) }; )
( ~lfo !? { ~lfo.(\crsh, \bits, 0.2, 4, 12, \sqr) }; )
( ~lfo !? { ~lfo.(\phs, \rate, 0.02, 0.1, 1.5, \sin) }; )
( ~lfo !? { ~lfo.(\shim, \shimmer, 0.1, 0.2, 0.8, \rand) }; )
// --- Stop un LFO FX precis -----
( ~lfoStop !? { ~lfoStop.(\dly, \feedback) }; )
// --- Stop tous les LFO FX (sans toucher aux LFO vars) -----
( ~lfoStopAll !? { ~lfoStopAll.() }; )
// =====================================================================
// [C] VCF MASTER (filtre insere sur tout le mix)
// types : \moog \ms20 \ladder \formant \comb \hpf \bpf \notch
// =====================================================================
// --- Activation par type -----
( ~vcfOn !? { ~vcfOn.(\moog, 800, 0.7) }; ) // 4-pole vintage
( ~vcfOn !? { ~vcfOn.(\ms20, 900, 0.6) }; ) // Sallen-Key agressif
( ~vcfOn !? { ~vcfOn.(\ladder, 900, 0.5) }; ) // 24 dB/oct
( ~vcfOn !? { ~vcfOn.(\formant, 0, 0.15) }; ) // freq=vowel index 0..4
( ~vcfOn !? { ~vcfOn.(\comb, 220, 1.5) }; ) // resonator
( ~vcfOn !? { ~vcfOn.(\hpf, 400, 0.5) }; ) // HPF resonant
( ~vcfOn !? { ~vcfOn.(\bpf, 1200, 0.4) }; ) // BPF resonant
( ~vcfOn !? { ~vcfOn.(\notch, 1000, 0.3) }; ) // BRF (anti-bande)
// --- Set un parametre du VCF actif -----
( ~vcfSet !? { ~vcfSet.(\freq, 1500) }; )
( ~vcfSet !? { ~vcfSet.(\freq, 350) }; )
( ~vcfSet !? { ~vcfSet.(\mix, 0.8) }; )
// --- Sweep automatique du cutoff -----
( ~vcfSweep !? { ~vcfSweep.(200, 5000, 4) }; ) // up 4s
( ~vcfSweep !? { ~vcfSweep.(8000, 200, 4) }; ) // down 4s
( ~vcfSweep !? { ~vcfSweep.(400, 6000, 8, \exp) }; )
// --- PRESETS VCF -----
( ~vcfPreset !? { ~vcfPreset.(\acid) }; ) // moog 600 q=0.85
( ~vcfPreset !? { ~vcfPreset.(\dub) }; ) // ladder 900 q=0.5
( ~vcfPreset !? { ~vcfPreset.(\deep) }; ) // moog 400 q=0.4
( ~vcfPreset !? { ~vcfPreset.(\bright) }; ) // bpf 3500 q=0.4
( ~vcfPreset !? { ~vcfPreset.(\dark) }; ) // moog 250 q=0.3
( ~vcfPreset !? { ~vcfPreset.(\formantA) }; )
( ~vcfPreset !? { ~vcfPreset.(\formantE) }; )
( ~vcfPreset !? { ~vcfPreset.(\formantI) }; )
( ~vcfPreset !? { ~vcfPreset.(\formantO) }; )
( ~vcfPreset !? { ~vcfPreset.(\formantU) }; )
// --- VCF off -----
( ~vcfOff !? { ~vcfOff.() }; )
// =====================================================================
// [D] TRICKS SCENIQUES (~drop / ~breakdown / ~buildup / ~stutter ...)
// =====================================================================
// --- DROP : silence -> fill -> freeze -> blast -----
( ~drop !? { ~drop.(4) }; ) // build 4s
( ~drop !? { ~drop.(8) }; ) // build 8s
// --- BREAKDOWN progressif -----
( ~breakdown !? { ~breakdown.(8) }; )
( ~breakdown !? { ~breakdown.(16) }; )
// --- BUILDUP crescendo (riser + filterSweepUp) -----
( ~buildup !? { ~buildup.(8) }; )
( ~buildup !? { ~buildup.(16) }; )
// --- HANDS UP : sub mute + lead boost + delay courte -----
( ~handsUp !? { ~handsUp.() }; )
// --- STUTTER master (gate rythmique sur amps) -----
( ~stutter !? { ~stutter.(8, 1) }; ) // 8Hz pendant 1s
( ~stutter !? { ~stutter.(16, 0.5) }; ) // 16Hz pendant 0.5s
( ~stutter !? { ~stutter.(4, 2) }; ) // 4Hz pendant 2s
// --- GLITCH : bitcrush + stutter + delay courte -----
( ~glitch !? { ~glitch.(2) }; )
( ~glitch !? { ~glitch.(4) }; )
// --- FREEZE : reverb freeze + cut sec -----
( ~freeze !? { ~freeze.(4) }; )
( ~freeze !? { ~freeze.(8) }; )
// --- REVERB TAIL : kill all + reverb send max + queue dur s -----
( ~reverbTail !? { ~reverbTail.(4) }; )
( ~reverbTail !? { ~reverbTail.(8) }; )
// --- TAPE STOP : ralenti tempo puis stop -----
( ~tapeStop !? { ~tapeStop.(2) }; )
( ~tapeStop !? { ~tapeStop.(4) }; )
// --- TAPE REVERSE : sweep + freeze -----
( ~tapeReverse !? { ~tapeReverse.(2) }; )
( ~tapeReverse !? { ~tapeReverse.(4) }; )
// --- JUMP CUT : silence sec dur s puis reprend -----
( ~jumpCut !? { ~jumpCut.(0.5) }; )
( ~jumpCut !? { ~jumpCut.(1) }; )
( ~jumpCut !? { ~jumpCut.(2) }; )
// --- SWAP scene A -> scene B (necessite ~scene) -----
( ~swap !? { ~swap.(\sceneA, \sceneB, 4) }; )
// --- CHOKE : coupe la rythmique / lead -----
( ~chokePerc !? { ~chokePerc.() }; )
( ~chokeLead !? { ~chokeLead.() }; )
// --- ENV one-shot sur une var (attack/sustain/release) -----
( ~envTo !? { ~envTo.(\melodyCutoff, 0.5, 1.0, 2.0, 6000, 1500) }; )
// --- ENV burst percussif sur une var -----
( ~envBurst !? { ~envBurst.(\acidCutoff, 4000, 0.3) }; )
// --- ENV sweep (monte/descend lineaire ou expo) -----
( ~envSweep !? { ~envSweep.(\melodyCutoff, 200, 6000, 4, \exp) }; )
( ~envSweep !? { ~envSweep.(\melodyCutoff, 6000, 200, 4, \exp) }; )
// --- Filter sweep raccourcis -----
( ~filterSweepUp !? { ~filterSweepUp.(4) }; )
( ~filterSweepDown !? { ~filterSweepDown.(4) }; )
// --- AMP DUCK momentane sur tous les amps -----
( ~ampDuck !? { ~ampDuck.(0.3, 0.7) }; )
( ~ampDuck !? { ~ampDuck.(0.6, 0.5) }; )
// --- Sidechain dynamique (kick -> cible) -----
( ~scTrigger !? { ~scTrigger.(\kick, \acid, 0.85) }; )
( ~scTrigger !? { ~scTrigger.(\kick, \melody, 0.6) }; )
( ~scTrigger !? { ~scTrigger.(\kick, \harmony, 0.6) }; )
( ~scTrigger !? { ~scTrigger.(\kick, \master, 0.7) }; )
// --- SC auto global (pompe sur tout le mix) -----
( ~scAuto !? { ~scAuto.(0.7) }; )
// --- SC off -----
( ~scOff !? { ~scOff.() }; )
// --- COMBOS sceniques -----
( ~breakdown !? { ~breakdown.(8); }; ~buildup !? { ~buildup.(8); }; ~drop !? { ~drop.(2); }; )
( ~vcfPreset !? { ~vcfPreset.(\acid) }; ~lfoSync !? { ~lfoSync.(\acidCutoff, 8, 600, \sin, 800) }; )
( ~scAuto !? { ~scAuto.(0.7) }; ~compOn !? { ~compOn.(\pump) }; ~satOn !? { ~satOn.(1.5) }; )
// =====================================================================
// [E] FX MOMENTANES (~bitCrushNow / ~ringMod / ~freqShift / ~granFreeze ...)
// =====================================================================
// --- SLIP TIME (pitch master sur dur s) -----
( ~slipTime !? { ~slipTime.(0.5, 1) }; ) // -1 octave 1s
( ~slipTime !? { ~slipTime.(2.0, 0.5) }; ) // +1 octave 0.5s
( ~slipTime !? { ~slipTime.(0.75, 2) }; ) // -3st 2s
// --- BITCRUSH momentane -----
( ~bitCrushNow !? { ~bitCrushNow.(4, 4000, 1) }; )
( ~bitCrushNow !? { ~bitCrushNow.(6, 8000, 2) }; )
( ~bitCrushNow !? { ~bitCrushNow.(3, 3000, 0.5) }; )
// --- RING MOD momentane -----
( ~ringMod !? { ~ringMod.(150, 1) }; )
( ~ringMod !? { ~ringMod.(300, 2) }; )
( ~ringMod !? { ~ringMod.(50, 0.5) }; )
// --- FREQ SHIFT momentane -----
( ~freqShift !? { ~freqShift.(50, 1) }; )
( ~freqShift !? { ~freqShift.(-100, 2) }; )
( ~freqShift !? { ~freqShift.(200, 0.5) }; )
// --- GRAN FREEZE momentane -----
( ~granFreeze !? { ~granFreeze.(2) }; )
( ~granFreeze !? { ~granFreeze.(4) }; )
// --- SCRATCH (vinyl-like) -----
( ~scratch !? { ~scratch.(1, 0.3) }; )
( ~scratch !? { ~scratch.(-1, 0.5) }; )
// --- BEAT REPEAT (gate stutter rythmique) -----
( ~repeat !? { ~repeat.(8, 1) }; )
( ~repeat !? { ~repeat.(16, 0.5) }; )
( ~repeat !? { ~repeat.(4, 2) }; )
// =====================================================================
// [F] AUTO-PAN / TREMOLO (par voix)
// =====================================================================
// --- AUTOPAN par voix (sur ~xxxPan) -----
( ~autoPan !? { ~autoPan.(\melody, 0.5, 0.8) }; )
( ~autoPan !? { ~autoPan.(\acid, 0.3, 0.6) }; )
( ~autoPan !? { ~autoPan.(\harmony, 0.2, 1.0) }; )
// --- TREMOLO par voix (LFO sur ~xxxAmp) -----
( ~tremolo !? { ~tremolo.(\melody, 5, 0.5) }; )
( ~tremolo !? { ~tremolo.(\acid, 8, 0.4) }; )
( ~tremolo !? { ~tremolo.(\harmony, 3, 0.6) }; )
// --- Stop l'auto-fx d'une voie -----
( ~stopAutoFx !? { ~stopAutoFx.(\melody) }; )
( ~stopAutoFx !? { ~stopAutoFx.(\acid) }; )
( ~stopAutoFx !? { ~stopAutoFx.(\harmony) }; )
// --- AUTOPAN / TREMOLO en SEND (sur le bus FX, voix indep) -----
( ~addFx !? { ~addFx.(\autopan, [\rate, 0.5, \depth, 1]) }; ~send !? { ~send.(\autopan, 1.0) }; )
( ~addFx !? { ~addFx.(\trem, [\rate, 6, \depth, 0.7]) }; ~send !? { ~send.(\trem, 1.0) }; )
// =====================================================================
// [G] FX SYNTHS ONESHOT (riser / sweep / impact / crash / vinyl / gong ...)
// =====================================================================
// --- RISER (montee filtree) -----
( Synth(\riser, [\dur, 4, \startCutoff, 200, \endCutoff, 8000, \amp, 0.25]); )
( Synth(\riser, [\dur, 8, \startCutoff, 300, \endCutoff, 10000, \amp, 0.30]); )
( Synth(\riser, [\dur, 16, \startCutoff, 400, \endCutoff, 12000, \amp, 0.35]); )
// --- SWEEP (descente filtree) -----
( Synth(\sweep, [\dur, 4, \startCutoff, 8000, \endCutoff, 200, \amp, 0.25]); )
( Synth(\sweep, [\dur, 8, \startCutoff, 12000, \endCutoff, 200, \amp, 0.35]); )
// --- IMPACT (sub-boom de drop) -----
( Synth(\impact, [\freq, 45, \decay, 1.5, \amp, 0.7]); )
( Synth(\impact, [\freq, 35, \decay, 2.5, \amp, 0.8]); )
( Synth(\impact, [\freq, 25, \decay, 4, \amp, 0.9]); ) // gabber
// --- CRASH (cymbale crash) -----
( Synth(\crash, [\decay, 2.0, \amp, 0.3]); )
( Synth(\crash, [\decay, 4.0, \amp, 0.4]); )
( Synth(\crash, [\decay, 6.0, \amp, 0.5]); )
// --- GONG (asia, decay long) -----
( Synth(\gong, [\freq, 50, \decay, 8, \amp, 0.5]); )
( Synth(\gong, [\freq, 40, \decay, 12, \amp, 0.6]); )
( Synth(\gong, [\freq, 25, \decay, 30, \amp, 0.95]); ) // drone doom
// --- VINYL (crackle continu) on / off -----
( ~vinyl !? { ~vinyl.free }; ~vinyl = Synth(\vinyl, [\amp, 0.05]); "vinyl ON".postln; )
( ~vinyl !? { ~vinyl.free }; ~vinyl = Synth(\vinyl, [\amp, 0.10]); "vinyl ON normal".postln; )
( ~vinyl !? { ~vinyl.free }; ~vinyl = Synth(\vinyl, [\amp, 0.15]); "vinyl ON fort".postln; )
( ~vinyl !? { ~vinyl.free; ~vinyl = nil }; "vinyl OFF".postln; )
// --- TAIKO / TOM / SNARE / CLAP / KICKS one-shots -----
( Synth(\taiko, [\freq, 80, \decay, 0.6, \amp, 0.5]); )
( Synth(\tom, [\freq, 120, \decay, 0.3, \amp, 0.5]); )
( Synth(\snare, [\amp, 0.5, \decay, 0.06, \freq, 220]); )
( Synth(\clap, [\amp, 0.4, \decay, 0.3]); )
( Synth(\kick808, [\freq, 35, \decay, 1.2, \amp, 0.7]); )
( Synth(\kickGabber, [\amp, 0.7]); )
// --- Cowbell, guzheng (asian) -----
( Synth(\cowbell, [\freq, 800, \decay, 0.2, \amp, 0.4]); )
( Synth(\guzheng, [\freq, 220, \amp, 0.18, \release, 1.0]); )
// --- SCRATCH SynthDef direct -----
( Synth(\scratch, [\dur, 0.2, \freq, 1000, \sweepDir, 1, \amp, 0.4]); )
( Synth(\scratch, [\dur, 0.3, \freq, 800, \sweepDir, -1, \amp, 0.4]); )
// =====================================================================
// [H] FILLS RYTHMIQUES
// =====================================================================
// --- Snare fill standard (8 hits / 1s) -----
( ~snareFill !? { ~snareFill.value }; )
// --- Snare fill long accelerando (16 hits / 2s) -----
( ~snareFill !? { ~snareFill.(16, 2, 0.2, 0.55) }; )
// --- Snare fill court rapide (32 hits / 1s -- roll) -----
( ~snareFill !? { ~snareFill.(32, 1, 0.15, 0.6) }; )
// --- Snare fill epique (32 hits / 16s, build) -----
( ~snareFill !? { ~snareFill.(32, 16, 0.2, 0.55) }; )
// --- Tom fill standard (descente 4 toms) -----
( ~tomFill !? { ~tomFill.value }; )
// =====================================================================
// [I] MASTER COMP / SAT
// =====================================================================
// --- Compresseur ON par preset -----
( ~compOn !? { ~compOn.(\soft) }; ) // glue tres leger
( ~compOn !? { ~compOn.(\glue) }; ) // glue (default)
( ~compOn !? { ~compOn.(\punch) }; ) // attack rapide
( ~compOn !? { ~compOn.(\pump) }; ) // sidechain-style
// --- Compresseur OFF -----
( ~compOff !? { ~compOff.() }; )
// --- Saturation ON par mode -----
( ~satOn !? { ~satOn.(1.5, 0) }; ) // tanh doux
( ~satOn !? { ~satOn.(2.0, 0) }; ) // tanh fort
( ~satOn !? { ~satOn.(2.0, 1) }; ) // softclip
( ~satOn !? { ~satOn.(1.8, 2) }; ) // fold (extreme)
// --- Saturation OFF -----
( ~satOff !? { ~satOff.() }; )
// =====================================================================
// [J] FX PAR GENRE (combos prets a l'emploi -- palette/fx_genre.scd)
// Chaque bloc = transition / signature scenique complete
// =====================================================================
// --- TRANCE BUILD-UP : snare roll + riser long + impact (16s) -----
(
~snareFill !? {
Synth(\riser, [\dur, 16, \startCutoff, 400, \endCutoff, 12000, \amp, 0.35]);
~snareFill.(32, 16, 0.2, 0.55);
fork {
16.wait;
Synth(\impact, [\freq, 35, \decay, 3, \amp, 0.7]);
Synth(\crash, [\decay, 4, \amp, 0.4]);
};
"=== TRANCE BUILD 16s ===".postln;
};
)
// --- DUBSTEP DROP : impact + crash + riser court (4s) -----
(
Synth(\riser, [\dur, 4, \startCutoff, 500, \endCutoff, 11000, \amp, 0.4]);
fork {
4.wait;
Synth(\impact, [\freq, 30, \decay, 3.5, \amp, 0.85]);
Synth(\crash, [\decay, 3, \amp, 0.5]);
};
"=== DUBSTEP DROP 4s ===".postln;
)
// --- HARDCORE GABBER DROP : impact massif + double crash -----
(
Synth(\impact, [\freq, 25, \decay, 4, \amp, 0.9]);
Synth(\crash, [\decay, 5, \amp, 0.6]);
fork { 0.5.wait; Synth(\crash, [\decay, 3, \amp, 0.4]); };
"=== HARDCORE DROP ===".postln;
)
// --- DnB BUILD : snare roll accelerando + drop (4s) -----
(
~snareFill !? {
~snareFill.(16, 4, 0.3, 0.6);
fork {
4.wait;
Synth(\impact, [\freq, 40, \decay, 2.5, \amp, 0.65]);
};
"=== DnB BUILD 4s ===".postln;
};
)
// --- ASIAN CINEMATIC : gong cascade descendant -----
(
[40, 50, 60, 70].do { |f, i|
fork { (i * 0.8).wait; Synth(\gong, [\freq, f, \decay, 8 - (i * 0.5), \amp, 0.45]); };
};
"=== GONG CASCADE 3s ===".postln;
)
// --- DUB TECHNO ECHO : clap dub avec delay -----
(
fork {
5.do { |i|
Synth(\clap, [\amp, 0.4 - (i * 0.06), \decay, 0.3]);
0.4.wait;
};
};
"=== DUB ECHO CLAP ===".postln;
)
// --- DETROIT FILTER SWEEP DOWN -----
( Synth(\sweep, [\dur, 8, \startCutoff, 12000, \endCutoff, 200, \amp, 0.35]); )
// --- INDUSTRIAL TENSION : sweep slow + drone vinyl (16s) -----
(
Synth(\sweep, [\dur, 16, \startCutoff, 8000, \endCutoff, 100, \amp, 0.4]);
~vinyl !? { ~vinyl.free };
~vinyl = Synth(\vinyl, [\amp, 0.12]);
fork { 16.wait; ~vinyl !? { ~vinyl.free; ~vinyl = nil }; };
"=== INDUSTRIAL TENSION 16s ===".postln;
)
// --- GLITCH IDM : scratch + impacts random -----
(
fork {
8.do {
if (0.6.coin) { Synth(\scratch, [\dur, rrand(0.1, 0.4), \freq, rrand(400, 2000),
\sweepDir, [-1, 1].choose, \amp, 0.3]) };
if (0.3.coin) { Synth(\impact, [\freq, rrand(40, 80), \decay, rrand(0.5, 1.5),
\amp, 0.4]) };
rrand(0.15, 0.5).wait;
};
};
"=== GLITCH 8 hits 4s ===".postln;
)
// --- VOCAL TRANCE SWELL : riser + crash + impact (12s) -----
(
Synth(\riser, [\dur, 12, \startCutoff, 600, \endCutoff, 13000, \amp, 0.4]);
fork {
12.wait;
Synth(\impact, [\freq, 38, \decay, 3, \amp, 0.7]);
Synth(\crash, [\decay, 5, \amp, 0.45]);
};
"=== VOCAL TRANCE SWELL 12s ===".postln;
)
// --- TRIBAL TOM ROLL DESCENDANT (taiko) -----
(
[180, 140, 110, 85, 70, 55, 40].do { |f, i|
fork { (i * 0.12).wait; Synth(\taiko, [\freq, f, \decay, 0.6, \amp, 0.5]); };
};
"=== TRIBAL TOM ROLL ===".postln;
)
// --- DRONE DOOM : gong tres profond (30s) -----
(
Synth(\gong, [\freq, 25, \decay, 30, \amp, 0.95]);
fork { 5.wait; Synth(\gong, [\freq, 30, \decay, 25, \amp, 0.7]); };
"=== DRONE DOOM gong massif 30s ===".postln;
)
// --- HARDSTYLE BUILD : kick reverse + snare roll + impact (4s) -----
(
Synth(\sweep, [\dur, 4, \startCutoff, 200, \endCutoff, 8000, \amp, 0.45]);
fork {
4.wait;
Synth(\impact, [\freq, 30, \decay, 3.5, \amp, 0.85]);
Synth(\kickGabber, [\amp, 0.7]);
};
"=== HARDSTYLE BUILD 4s ===".postln;
)
// --- PHONK : vinyl + cowbell + 808 sub -----
(
~vinyl !? { ~vinyl.free };
~vinyl = Synth(\vinyl, [\amp, 0.15]);
fork {
4.do {
Synth(\cowbell, [\freq, 800, \decay, 0.2, \amp, 0.4]);
0.5.wait;
};
Synth(\kick808, [\freq, 35, \decay, 1.2, \amp, 0.7]);
};
"=== PHONK VINYL 2.5s ===".postln;
)
// --- FUTURE GARAGE : vocal-like swell + sub (6s) -----
(
Synth(\sweep, [\dur, 6, \startCutoff, 6000, \endCutoff, 800, \amp, 0.3]);
fork { 5.wait; Synth(\impact, [\freq, 40, \decay, 2.5, \amp, 0.55]); };
"=== FUTURE GARAGE SWELL 6s ===".postln;
)
// --- SCRATCH FX x4 -----
(
4.do { |i|
fork {
(i * 0.15).wait;
Synth(\scratch, [\dur, 0.2, \freq, 800 + (i * 200),
\sweepDir, [-1, 1].choose, \amp, 0.4]);
};
};
"=== SCRATCH x4 ===".postln;
)
// --- GLITCH STUTTER : impacts rapides random -----
(
fork {
16.do { |i|
Synth(\impact, [\freq, rrand(35, 60), \decay, 0.15, \amp, 0.4]);
rrand(0.06, 0.15).wait;
};
};
"=== GLITCH STUTTER 2s ===".postln;
)
// --- ASIAN MELODIC FILL (guzheng arpege descendant) -----
(
[79, 77, 75, 72, 70, 67, 65, 63, 60].do { |n, i|
fork {
(i * 0.15).wait;
Synth(\guzheng, [\freq, n.midicps, \amp, 0.18, \release, 1.0]);
};
};
"=== ASIAN GUZHENG FILL ===".postln;
)
// --- DROP CUT brutal : coupe tout + sweep down + impact (2s) -----
(
~reload !? {
Routine({
var oldKick = ~kickAmp; var oldHat = ~hatAmp; var oldClap = ~clapAmp;
~kickAmp = 0; ~hatAmp = 0; ~clapAmp = 0; ~reload.value;
Synth(\sweep, [\dur, 2, \startCutoff, 8000, \endCutoff, 200, \amp, 0.4]);
2.wait;
Synth(\impact, [\freq, 30, \decay, 3, \amp, 0.85]);
Synth(\crash, [\decay, 3, \amp, 0.4]);
~kickAmp = oldKick; ~hatAmp = oldHat; ~clapAmp = oldClap; ~reload.value;
}).play;
"=== DROP CUT 2s ===".postln;
};
)
// =====================================================================
// [K] TAP TEMPO (tap au moins 2 fois en rythme pour caler le delay)
// =====================================================================
// --- Tap (au moins 2 fois successifs) -----
( ~tap !? { ~tap.value }; )
( ~tap !? { ~tap.value }; )
( ~tap !? { ~tap.value }; )
( ~tap !? { ~tap.value }; )
// --- Reset du tap (efface l'historique) -----
( ~tapTempo !? { ~tapTempo[\last] = nil; "tap reset".postln }; )
// --- Set delayTime par division de tempo (sans tap) -----
( ~setFx !? { ~setFx.(\dly, \delayTime, 60 / TempoClock.default.tempo / 4) }; ) // 1/16
( ~setFx !? { ~setFx.(\dly, \delayTime, 60 / TempoClock.default.tempo / 2) }; ) // 1/8
( ~setFx !? { ~setFx.(\dly, \delayTime, 60 / TempoClock.default.tempo) }; ) // 1/4
( ~setFx !? { ~setFx.(\dly, \delayTime, 60 / TempoClock.default.tempo * 0.75) }; ) // 1/4 dotted
( ~setFx !? { ~setFx.(\dly, \delayTime, 60 / TempoClock.default.tempo * 2) }; ) // 1/2
// --- Tempo en live (BPM direct) -----
( TempoClock.default.tempo = 110/60; )
( TempoClock.default.tempo = 132/60; )
( TempoClock.default.tempo = 145/60; )
( TempoClock.default.tempo = 174/60; )
// =====================================================================
// [L] FX BUS DETAIL (param par param par FX)
// Pour chaque FX du bus, exposer les params principaux
// =====================================================================
// ---------- REVERB ----------
( ~setFx !? { ~setFx.(\rev, \mix, 0.4) }; ) // 0..1
( ~setFx !? { ~setFx.(\rev, \room, 0.5) }; ) // 0..1 (small room)
( ~setFx !? { ~setFx.(\rev, \room, 0.85) }; ) // hall
( ~setFx !? { ~setFx.(\rev, \room, 0.97) }; ) // cathedral
( ~setFx !? { ~setFx.(\rev, \damp, 0.6) }; ) // 0=brillant, 1=mat
( ~setFx !? { ~setFx.(\rev, \damp, 0.1) }; ) // tres brillant
( ~setFx !? { ~setFx.(\rev, \freeze, 1) }; )
( ~setFx !? { ~setFx.(\rev, \freeze, 0) }; )
// ---------- DELAY ----------
( ~setFx !? { ~setFx.(\dly, \delayTime, 0.375) }; ) // 1/8 a 120bpm
( ~setFx !? { ~setFx.(\dly, \delayTime, 0.5) }; ) // 1/4
( ~setFx !? { ~setFx.(\dly, \delayTime, 0.25) }; ) // 1/8 a 120
( ~setFx !? { ~setFx.(\dly, \feedback, 0.5) }; ) // normal
( ~setFx !? { ~setFx.(\dly, \feedback, 0.85) }; ) // self-oscillating
( ~setFx !? { ~setFx.(\dly, \feedback, 0.2) }; ) // 2 echos
( ~setFx !? { ~setFx.(\dly, \mix, 0.5) }; )
( ~setFx !? { ~setFx.(\dly, \color, 0.3) }; ) // 0=tape dark, 1=clean
// ---------- CHORUS ----------
( ~setFx !? { ~setFx.(\chr, \rate, 0.4) }; )
( ~setFx !? { ~setFx.(\chr, \rate, 1.5) }; ) // rapide
( ~setFx !? { ~setFx.(\chr, \depth, 0.005) }; ) // subtil
( ~setFx !? { ~setFx.(\chr, \depth, 0.015) }; ) // large
( ~setFx !? { ~setFx.(\chr, \mix, 0.5) }; )
// ---------- PHASER ----------
( ~setFx !? { ~setFx.(\phs, \rate, 0.3) }; )
( ~setFx !? { ~setFx.(\phs, \rate, 0.05) }; ) // tres slow
( ~setFx !? { ~setFx.(\phs, \depth, 0.7) }; )
( ~setFx !? { ~setFx.(\phs, \depth, 1.0) }; )
( ~setFx !? { ~setFx.(\phs, \feedback, 0.4) }; )
( ~setFx !? { ~setFx.(\phs, \feedback, 0.85) }; ) // self-osc
( ~setFx !? { ~setFx.(\phs, \mix, 0.5) }; )
// ---------- FLANGER ----------
( ~setFx !? { ~setFx.(\flg, \rate, 0.2) }; )
( ~setFx !? { ~setFx.(\flg, \rate, 0.05) }; ) // jet plane slow
( ~setFx !? { ~setFx.(\flg, \depth, 0.003) }; )
( ~setFx !? { ~setFx.(\flg, \depth, 0.008) }; ) // tres prononce
( ~setFx !? { ~setFx.(\flg, \feedback, 0.6) }; )
( ~setFx !? { ~setFx.(\flg, \feedback, 0.9) }; ) // metallic
( ~setFx !? { ~setFx.(\flg, \mix, 0.5) }; )
// ---------- BITCRUSH ----------
( ~setFx !? { ~setFx.(\crsh, \bits, 8) }; ) // 8bit
( ~setFx !? { ~setFx.(\crsh, \bits, 4) }; ) // 4bit
( ~setFx !? { ~setFx.(\crsh, \bits, 2) }; ) // 2bit destroy
( ~setFx !? { ~setFx.(\crsh, \sampleRate, 8000) }; ) // lo-fi
( ~setFx !? { ~setFx.(\crsh, \sampleRate, 4000) }; ) // C64
( ~setFx !? { ~setFx.(\crsh, \sampleRate, 2000) }; ) // gameboy
( ~setFx !? { ~setFx.(\crsh, \mix, 0.6) }; )
// ---------- TAPE ----------
( ~setFx !? { ~setFx.(\tape, \drive, 1.5) }; )
( ~setFx !? { ~setFx.(\tape, \drive, 3.0) }; ) // saturated
( ~setFx !? { ~setFx.(\tape, \wow, 0.0015) }; )
( ~setFx !? { ~setFx.(\tape, \wow, 0.005) }; ) // wobble fort
( ~setFx !? { ~setFx.(\tape, \flutter, 0.0008) }; )
( ~setFx !? { ~setFx.(\tape, \flutter, 0.003) }; )
( ~setFx !? { ~setFx.(\tape, \mix, 1.0) }; )
// ---------- SHIMMER ----------
( ~setFx !? { ~setFx.(\shim, \shimmer, 0.5) }; )
( ~setFx !? { ~setFx.(\shim, \shimmer, 0.9) }; ) // octave-up tres present
( ~setFx !? { ~setFx.(\shim, \room, 0.92) }; )
( ~setFx !? { ~setFx.(\shim, \mix, 0.5) }; )
// ---------- DIST ----------
( ~setFx !? { ~setFx.(\dist, \drive, 3.0) }; )
( ~setFx !? { ~setFx.(\dist, \drive, 5.0) }; ) // industriel
( ~setFx !? { ~setFx.(\dist, \drive, 8.0) }; ) // wall of sound
( ~setFx !? { ~setFx.(\dist, \tone, 0.5) }; ) // 0=dark, 1=bright
( ~setFx !? { ~setFx.(\dist, \tone, 0.2) }; )
( ~setFx !? { ~setFx.(\dist, \tone, 0.8) }; )
( ~setFx !? { ~setFx.(\dist, \mix, 0.5) }; )
// ---------- FX additionnels (ajoutes via ~addFx) ----------
( ~addFx !? { ~addFx.(\ringmod, [\freq, 200, \mix, 0.5]) }; ~send !? { ~send.(\ringmod, 0.4) }; )
( ~addFx !? { ~addFx.(\fshift, [\shift, 80]) }; ~send !? { ~send.(\fshift, 0.3) }; )
( ~addFx !? { ~addFx.(\gran, [\grainDur, 0.15, \pitch, 0.5]) }; ~send !? { ~send.(\gran, 0.4) }; )
( ~addFx !? { ~addFx.(\comb, [\freq, 220, \decay, 2]) }; ~send !? { ~send.(\comb, 0.4) }; )
( ~addFx !? { ~addFx.(\formant, [\vowel, 0]) }; ~send !? { ~send.(\formant, 0.5) }; )
( ~addFx !? { ~addFx.(\autoflt, [\rate, 0.3, \lo, 200, \hi, 4000]) }; ~send !? { ~send.(\autoflt, 0.5) }; )
( ~addFx !? { ~addFx.(\stut, [\rate, 8, \dutyCycle, 0.5]) }; ~send !? { ~send.(\stut, 1.0) }; )
( ~addFx !? { ~addFx.(\reson, [\freq, 220, \decay, 3]) }; ~send !? { ~send.(\reson, 0.5) }; )
( ~addFx !? { ~addFx.(\spring, [\decay, 1.5]) }; ~send !? { ~send.(\spring, 0.4) }; )
( ~addFx !? { ~addFx.(\plate, [\decay, 2.5, \damp, 0.3]) }; ~send !? { ~send.(\plate, 0.4) }; )
// =====================================================================
// [M] STOP ALL FX (reset global -- panique mode)
// =====================================================================
// --- Stop tous les LFO (vars + FX) -----
( ~lfoStopEverything !? { ~lfoStopEverything.() }; )
// --- VCF off -----
( ~vcfOff !? { ~vcfOff.() }; )
// --- Comp / Sat off -----
( ~compOff !? { ~compOff.() }; )
( ~satOff !? { ~satOff.() }; )
// --- Reset FX bus (tout coupe) -----
( ~fxPreset !? { ~fxPreset.(\dry) }; )
// --- Stop autoFx par voix -----
( ~stopAutoFx !? { ~stopAutoFx.(\melody); }; )
( ~stopAutoFx !? { ~stopAutoFx.(\acid); }; )
( ~stopAutoFx !? { ~stopAutoFx.(\harmony); }; )
// --- Sidechain off -----
( ~scOff !? { ~scOff.() }; )
// --- Vinyl off -----
( ~vinyl !? { ~vinyl.free; ~vinyl = nil }; "vinyl OFF".postln; )
// --- Unfreeze reverb (au cas ou) -----
( ~setFx !? { ~setFx.(\rev, \freeze, 0) }; )
// --- RESET TOTAL : combine tout ce qui precede -----
(
~lfoStopEverything !? { ~lfoStopEverything.() };
~vcfOff !? { ~vcfOff.() };
~compOff !? { ~compOff.() };
~satOff !? { ~satOff.() };
~scOff !? { ~scOff.() };
~stopAutoFx !? { [\melody, \acid, \harmony].do { |t| ~stopAutoFx.(t) } };
~vinyl !? { ~vinyl.free; ~vinyl = nil };
~setFx !? { ~setFx.(\rev, \freeze, 0) };
~fxPreset !? { ~fxPreset.(\dry) };
"=== FX RESET TOTAL ===".postln;
)
+693
View File
@@ -0,0 +1,693 @@
// =====================================================================
// loops.scd -- Gestionnaire de pistes / boucles pour live SuperCollider
//
// PREREQUIS : engine.scd charge (Pdef nommes + ~bpm + ~reload).
// Optionnel : palette/melodies/tracks.scd (~melTrackOn/Off),
// palette/rhythm/sequences.scd (~seqPlay/~seqStop),
// live/mixer.scd.
//
// CONCEPT
// Une "boucle" est une entite a etat (ON / OFF, count, max, source).
// Source possible :
// - un Symbol -> Pdef de ce nom (ex: \kickSeq, \m1Seq)
// - une Function -> jouee en Routine (yields = beats)
//
// HELPERS PRINCIPAUX
// ~loopOn / ~loopOff / ~loopOffAll / ~loopToggle / ~loopIsOn
// ~loopList / ~loopInfo / ~loopProgress / ~loopShow
// ~loopRepeat / ~loopBeats / ~loopBars / ~loopSync
// ~loopCount / ~loopReset / ~loopMax
// ~loopCountdown / ~loopWithCountdown
// ~loopSnapshot / ~loopRestore / ~loopSave / ~loopLoad
// ~loopChain / ~loopParallel
// ~loopFade / ~loopFadeIn / ~loopAutoStop / ~loopAutoFade
// ~kickLoop / ~hatLoop / ~snareLoop / ~clapLoop / ~percLoop
// ~acidLoop / ~melodyLoop / ~harmonyLoop / ~allOn / ~allOff
//
// Un seul bloc top-level pour compatibilite .load
// =====================================================================
(
// ---------- Etat global ---------------------------------------------
~loops = ~loops ?? { () }; // dict des boucles (Event)
~loopsSnap = ~loopsSnap ?? { () }; // snapshots (params + amps)
~loopAutos = ~loopAutos ?? { () }; // routines d'auto-stop / auto-fade
~loopChainR = ~loopChainR ?? { nil }; // routine chain en cours
~loopParR = ~loopParR ?? { () }; // routines parallel par groupe
// ---------- Pdef du moteur (8 voies nommees) ------------------------
~loopEngineSyms = [\kickSeq, \hatSeq, \snareSeq, \clapSeq,
\percSeq, \acidSeq, \melodySeq, \harmonySeq];
// ---------- Utilitaires internes ------------------------------------
// Renvoie true si la source est un Symbol qui correspond a un Pdef
~loopIsPdef = { |source| source.isKindOf(Symbol) };
// Demarre une source dans une Routine (Function) ou via Pdef (Symbol).
// Renvoie une "handle" : un EventStreamPlayer / Routine selon le cas.
~loopStartSource = { |source, name|
var handle;
if (~loopIsPdef.(source)) {
Pdef(source).play;
handle = (kind: \pdef, sym: source);
} {
// Function : on l'execute en Routine. La fonction recoit le
// nom de la boucle pour pouvoir incrementer le compteur si elle
// veut (sinon on incrementera depuis l'exterieur).
var r = Routine({ source.value(name) }).play;
handle = (kind: \routine, rout: r);
};
handle
};
~loopStopHandle = { |h|
if (h.notNil) {
switch (h[\kind],
\pdef, { Pdef(h[\sym]).stop },
\routine, { h[\rout] !? { h[\rout].stop } }
);
};
};
// =====================================================================
// 1. GESTION DE BASE (on/off/toggle/list/isOn)
// =====================================================================
// ~loopOn.(\name, source [, max])
// source : Symbol (Pdef) ou Function. max optionnel (defaut inf).
~loopOn = { |name, source, max = inf|
var l = ~loops[name];
if (l.notNil and: { l[\state] == \on }) {
("[loop] " ++ name ++ " deja ON").postln;
} {
// Source heritee si pas fournie
var src = source ?? { l !? { l[\source] } };
if (src.isNil) {
("[loop] " ++ name ++ " : pas de source connue").postln;
} {
var handle = ~loopStartSource.(src, name);
~loops[name] = (
state: \on,
source: src,
handle: handle,
count: (l !? { l[\count] }) ? 0,
max: max,
startedAt: Main.elapsedTime,
lastResetAt: Main.elapsedTime,
);
("[loop] ON " ++ name).postln;
};
};
name
};
~loopOff = { |name|
var l = ~loops[name];
if (l.isNil) {
("[loop] " ++ name ++ " inconnue").postln;
} {
~loopStopHandle.(l[\handle]);
// Annule auto-stop / auto-fade en cours
~loopAutos[name] !? { |r| r.stop; ~loopAutos[name] = nil };
l[\state] = \off;
l[\handle] = nil;
("[loop] OFF " ++ name).postln;
};
};
~loopOffAll = {
~loops.keysDo { |k|
var l = ~loops[k];
if (l.notNil and: { l[\state] == \on }) { ~loopOff.(k) };
};
"[loop] offAll".postln;
};
~loopToggle = { |name|
var l = ~loops[name];
if (l.notNil and: { l[\state] == \on }) {
~loopOff.(name);
} {
~loopOn.(name);
};
};
~loopIsOn = { |name|
var l = ~loops[name];
(l.notNil and: { l[\state] == \on })
};
// Affichage : etat / source / count / max
~loopList = {
"=== loops ===".postln;
if (~loops.isEmpty) {
" (aucune boucle)".postln;
} {
~loops.keysValuesDo { |k, v|
var st = if (v[\state] == \on) { "ON " } { "off" };
var src = v[\source];
var srcStr = if (src.isKindOf(Symbol)) { src.asString }
{ if (src.isKindOf(Function)) { "<fn>" } { "?" } };
var maxStr = if (v[\max] == inf) { "inf" } { v[\max].asString };
(" [" ++ st ++ "] " ++ k
++ " src=" ++ srcStr
++ " count=" ++ (v[\count] ? 0)
++ "/" ++ maxStr
).postln;
};
};
};
// =====================================================================
// 2. COMPTEURS
// =====================================================================
// ~loopRepeat.(\name, source, n, dur)
// Joue source N fois (chaque iter dure `dur` battements), puis stop.
~loopRepeat = { |name, source, n = 4, dur = 4|
var t = TempoClock.default;
var max = n;
~loopOn.(name, source, max);
// Routine qui incremente le compteur a chaque dur, et stoppe a max
~loopAutos[name] !? { |r| r.stop };
~loopAutos[name] = Routine({
n.do { |i|
dur.wait;
if (~loops[name].notNil) {
~loops[name][\count] = (~loops[name][\count] ? 0) + 1;
};
};
// Fin atteinte : stop
~loopOff.(name);
}).play(t);
("[loop] repeat " ++ name ++ " n=" ++ n ++ " dur=" ++ dur ++ "b").postln;
name
};
~loopCount = { |name|
var l = ~loops[name];
if (l.isNil) { 0 } { l[\count] ? 0 }
};
~loopReset = { |name|
var l = ~loops[name];
if (l.notNil) {
l[\count] = 0;
l[\lastResetAt] = Main.elapsedTime;
("[loop] reset " ++ name).postln;
};
};
~loopMax = { |name, n|
var l = ~loops[name];
if (l.notNil) {
l[\max] = n;
("[loop] max " ++ name ++ " = " ++ n).postln;
};
};
// =====================================================================
// 3. SYNCHRONISATION AU TEMPO
// =====================================================================
// Joue une source pendant N battements, puis stop.
~loopBeats = { |name, source, beats = 16|
var t = TempoClock.default;
~loopOn.(name, source, beats);
~loopAutos[name] !? { |r| r.stop };
~loopAutos[name] = Routine({
beats.wait;
~loopOff.(name);
}).play(t);
("[loop] " ++ name ++ " pendant " ++ beats ++ " beats").postln;
name
};
// N mesures (defaut 4 battements/mesure)
~loopBars = { |name, source, bars = 4, beatsPerBar = 4|
~loopBeats.(name, source, bars * beatsPerBar);
};
// Aligne le demarrage de toutes les boucles ON au prochain temps fort
// (utilise pour resync apres avoir lance plusieurs boucles).
~loopSync = { |beatsPerBar = 4|
var t = TempoClock.default;
var now = t.beats;
var nextBar = now.ceil + ((beatsPerBar - (now.ceil.asInteger % beatsPerBar)) % beatsPerBar);
var wait = nextBar - now;
var snapshot = (); // {name -> source}
~loops.keysValuesDo { |k, v|
if (v[\state] == \on) { snapshot[k] = v[\source] };
};
if (snapshot.isEmpty) {
"[loop] sync : aucune boucle ON".postln;
} {
("[loop] sync dans " ++ wait.round(0.01) ++ " beats ("
++ snapshot.size ++ " boucles)").postln;
// Stop immediat, restart sur la barre suivante
snapshot.keysDo { |k| ~loopOff.(k) };
Routine({
wait.wait;
snapshot.keysValuesDo { |k, src| ~loopOn.(k, src) };
"[loop] sync OK".postln;
}).play(t);
};
};
// =====================================================================
// 4. PRE-ROLL / COUNTDOWN
// =====================================================================
// Compte avant de lancer une action. action est une Function (sans arg).
~loopCountdown = { |beats = 4, action|
var t = TempoClock.default;
Routine({
beats.do { |i|
var n = beats - i;
(n.asString ++ "...").postln;
1.wait;
};
"GO".postln;
action !? { action.value };
}).play(t);
("[loop] countdown " ++ beats ++ "b").postln;
};
// Countdown puis lance la boucle (max optionnel)
~loopWithCountdown = { |name, source, beats = 4, max = inf|
~loopCountdown.(beats, { ~loopOn.(name, source, max) });
};
// =====================================================================
// 5. SNAPSHOT / RESTORE / SAVE / LOAD
// =====================================================================
// Cle des "params interessants" a sauvegarder (heuristique).
// On capture toutes les vars ~xxx qui sont des Number/Symbol/SimpleNumber
// liees au prefixe du nom (ex: \kick -> ~kickAmp, ~kickDrive...) +
// les amps connus du moteur.
~loopSnapKeys = (~loopSnapKeys ?? { [
\bpm,
\kickAmp, \kickDrive, \kickDecay,
\hatAmp, \hatDecay,
\snareAmp, \snareDrive,
\clapAmp,
\percAmp,
\acidAmp, \acidCutoff, \acidDrive, \acidRq,
\melodyAmp, \melodyBoost, \melodyCutoff,
\harmonyAmp,
\leadScAmt, \scShape,
] });
~loopSnapshot = { |name|
var snap = ();
~loopSnapKeys.do { |k|
var v = currentEnvironment[k];
if (v.notNil and: { v.isKindOf(Number) or: { v.isKindOf(Symbol) } }) {
snap[k] = v;
};
};
// Ajoute les coefs mixer si presents
if (~mix.notNil) { snap[\__mix] = ~mix.copy };
// Ajoute l'etat de la boucle si elle existe
if (~loops[name].notNil) {
snap[\__loopState] = (
count: ~loops[name][\count] ? 0,
max: ~loops[name][\max] ? inf,
source: ~loops[name][\source]
);
};
~loopsSnap[name] = snap;
("[loop] snapshot " ++ name ++ " (" ++ snap.size ++ " cles)").postln;
snap
};
~loopRestore = { |name|
var snap = ~loopsSnap[name];
if (snap.isNil) {
("[loop] restore : pas de snapshot pour " ++ name).postln;
} {
snap.keysValuesDo { |k, v|
if (k == \__mix) {
if (~mix.notNil) {
v.keysValuesDo { |k2, v2| ~mix[k2] = v2 };
};
} {
if (k != \__loopState) {
currentEnvironment[k] = v;
};
};
};
// Reload des Pdef du moteur si dispo
~reload !? { ~reload.value };
("[loop] restore " ++ name).postln;
};
};
// Save / Load disque (en .scd : on serialise via Object.asCompileString)
~loopSavePath = { |name|
(~base ? "") ++ ".loops_" ++ name.asString ++ ".scd"
};
~loopSave = { |name|
var snap = ~loopsSnap[name] ?? { ~loopSnapshot.(name) };
var path = ~loopSavePath.(name);
var f = File.open(path, "w");
if (f.isNil) {
("[loop] save : impossible d'ouvrir " ++ path).postln;
} {
f.write(snap.asCompileString);
f.close;
("[loop] save " ++ name ++ " -> " ++ path).postln;
};
};
~loopLoad = { |name|
var path = ~loopSavePath.(name);
if (File.exists(path).not) {
("[loop] load : fichier absent " ++ path).postln;
} {
var snap = path.load;
if (snap.notNil) {
~loopsSnap[name] = snap;
("[loop] load " ++ name ++ " <- " ++ path).postln;
};
};
};
// =====================================================================
// 6. COMPOSITIONS
// =====================================================================
// Chaine sequentielle : [[name, source, beats], ...]
// Chaque entree joue `beats` battements, puis on passe au suivant.
~loopChain = { |entries|
var t = TempoClock.default;
~loopChainR !? { |r| r.stop };
~loopChainR = Routine({
entries.do { |e|
var name = e[0];
var source = e[1];
var beats = e[2] ? 16;
~loopOn.(name, source);
beats.wait;
~loopOff.(name);
};
"[loop] chain done".postln;
}).play(t);
("[loop] chain " ++ entries.size ++ " entrees").postln;
};
// Lance plusieurs boucles simultanement : [[name, source], ...]
~loopParallel = { |entries|
entries.do { |e|
var name = e[0];
var source = e[1];
~loopOn.(name, source);
};
("[loop] parallel " ++ entries.size ++ " boucles").postln;
};
// Fade-out d'une boucle (sur l'amp de la voie correspondante au
// prefixe du nom : \kickXxx -> ~kickAmp). Stoppe a la fin.
~loopFadeAmpKey = { |name|
// \kickLoop -> \kickAmp ; \m1 -> nil (cas multi-pistes : on
// fait un fade local en surtout via un Pdef)
var s = name.asString;
var prefixes = ["kick","hat","snare","clap","perc","acid",
"melody","harmony"];
var found = nil;
prefixes.do { |p|
if (found.isNil and: { s.beginsWith(p) }) {
found = (p ++ "Amp").asSymbol;
};
};
found
};
~loopFade = { |name, fadeOutDur = 2|
var t = TempoClock.default;
var ampKey = ~loopFadeAmpKey.(name);
var l = ~loops[name];
if (l.isNil) {
("[loop] fade : " ++ name ++ " inconnue").postln;
} {
if (ampKey.notNil) {
var startAmp = currentEnvironment[ampKey] ? 0.3;
var steps = (fadeOutDur * 20).asInteger.max(1);
var step = 1.0 / steps;
~loopAutos[name] !? { |r| r.stop };
~loopAutos[name] = Routine({
steps.do { |i|
currentEnvironment[ampKey] = startAmp * (1 - ((i + 1) * step));
~reload !? { ~reload.value };
(fadeOutDur / steps).wait;
};
~loopOff.(name);
currentEnvironment[ampKey] = startAmp; // restore amp
~reload !? { ~reload.value };
}).play(t);
("[loop] fade " ++ name ++ " (" ++ fadeOutDur ++ "s)").postln;
} {
// Pas d'ampKey : on attend simplement, puis stop
~loopAutos[name] !? { |r| r.stop };
~loopAutos[name] = Routine({
fadeOutDur.wait;
~loopOff.(name);
}).play(t);
("[loop] fade " ++ name ++ " (timed, no amp key)").postln;
};
};
};
~loopFadeIn = { |name, source, fadeInDur = 2, max = inf|
var t = TempoClock.default;
var ampKey = ~loopFadeAmpKey.(name);
var targetAmp = if (ampKey.notNil) {
currentEnvironment[ampKey] ? 0.3
} { nil };
if (ampKey.notNil) {
currentEnvironment[ampKey] = 0.0001;
~reload !? { ~reload.value };
};
~loopOn.(name, source, max);
if (ampKey.notNil) {
var steps = (fadeInDur * 20).asInteger.max(1);
~loopAutos[name] !? { |r| r.stop };
~loopAutos[name] = Routine({
steps.do { |i|
currentEnvironment[ampKey] = targetAmp * ((i + 1) / steps);
~reload !? { ~reload.value };
(fadeInDur / steps).wait;
};
}).play(t);
("[loop] fadeIn " ++ name ++ " -> " ++ targetAmp).postln;
} {
("[loop] fadeIn " ++ name ++ " (no amp key)").postln;
};
};
// =====================================================================
// 7. AFFICHAGE / DEBUG
// =====================================================================
// Bargraph ASCII court pour count/max
~loopBar = { |count, max, width = 20|
var ratio, full, str;
if (max == inf or: { max == 0 }) {
// Pas de max : on affiche un placeholder
"[--------------------]"
} {
ratio = (count / max).clip(0, 1);
full = (ratio * width).asInteger;
str = "[" ++ ("#" ! full).join ++ ("." ! (width - full)).join ++ "]";
str
}
};
~loopProgress = {
"=== loop progress ===".postln;
if (~loops.isEmpty) {
" (aucune boucle)".postln;
} {
~loops.keysValuesDo { |k, v|
var st = if (v[\state] == \on) { "ON " } { "off" };
var c = v[\count] ? 0;
var m = v[\max] ? inf;
var bar = ~loopBar.(c, m, 20);
var maxStr = if (m == inf) { "inf" } { m.asString };
(" [" ++ st ++ "] " ++ k ++ " " ++ bar
++ " " ++ c ++ "/" ++ maxStr).postln;
};
};
};
// Alias visuel
~loopShow = { ~loopProgress.value };
// Info compacte d'une seule boucle
~loopInfo = { |name|
var v = ~loops[name];
if (v.isNil) {
("[loop] " ++ name ++ " inconnue").postln;
} {
var st = if (v[\state] == \on) { "ON" } { "off" };
var src = v[\source];
var srcStr = if (src.isKindOf(Symbol)) { src.asString }
{ if (src.isKindOf(Function)) { "<fn>" } { "?" } };
var c = v[\count] ? 0;
var m = v[\max] ? inf;
var bar = ~loopBar.(c, m, 20);
var maxStr = if (m == inf) { "inf" } { m.asString };
var elapsed = (Main.elapsedTime - (v[\startedAt] ? Main.elapsedTime)).round(0.1);
("[loop] " ++ name ++ " " ++ st
++ " src=" ++ srcStr
++ " " ++ bar ++ " " ++ c ++ "/" ++ maxStr
++ " age=" ++ elapsed ++ "s").postln;
};
};
// =====================================================================
// 8. AUTO-STOP / AUTO-FADE PROGRAMMES
// =====================================================================
~loopAutoStop = { |name, beats = 16|
var t = TempoClock.default;
if (~loops[name].isNil) {
("[loop] autoStop : " ++ name ++ " inconnue").postln;
} {
~loopAutos[name] !? { |r| r.stop };
~loopAutos[name] = Routine({
beats.wait;
~loopOff.(name);
}).play(t);
("[loop] autoStop " ++ name ++ " dans " ++ beats ++ "b").postln;
};
};
~loopAutoFade = { |name, beats = 16, fadeDur = 1|
var t = TempoClock.default;
if (~loops[name].isNil) {
("[loop] autoFade : " ++ name ++ " inconnue").postln;
} {
~loopAutos[name] !? { |r| r.stop };
~loopAutos[name] = Routine({
beats.wait;
~loopFade.(name, fadeDur);
}).play(t);
("[loop] autoFade " ++ name ++ " dans " ++ beats ++ "b ("
++ fadeDur ++ "s)").postln;
};
};
// =====================================================================
// 9. HELPERS POUR LES PDEF DU MOTEUR
// =====================================================================
// Chaque helper utilise un nom de boucle base sur la voie + source
// Pdef correspondant. Si beats = inf -> simple ~loopOn ; sinon ~loopBeats.
~loopEngineHelper = { |voiceSym, beats|
var seqSym = (voiceSym.asString ++ "Seq").asSymbol;
var loopName = (voiceSym.asString ++ "Loop").asSymbol;
if (beats == inf or: { beats.isNil }) {
~loopOn.(loopName, seqSym);
} {
~loopBeats.(loopName, seqSym, beats);
};
loopName
};
~kickLoop = { |beats = inf| ~loopEngineHelper.(\kick, beats) };
~hatLoop = { |beats = inf| ~loopEngineHelper.(\hat, beats) };
~snareLoop = { |beats = inf| ~loopEngineHelper.(\snare, beats) };
~clapLoop = { |beats = inf| ~loopEngineHelper.(\clap, beats) };
~percLoop = { |beats = inf| ~loopEngineHelper.(\perc, beats) };
~acidLoop = { |beats = inf| ~loopEngineHelper.(\acid, beats) };
~melodyLoop = { |beats = inf| ~loopEngineHelper.(\melody, beats) };
~harmonyLoop = { |beats = inf| ~loopEngineHelper.(\harmony, beats) };
// Lance / stoppe les 8 Pdef du moteur d'un coup
~allOn = {
~loopEngineSyms.do { |sym|
var voice = sym.asString.replace("Seq", "").asSymbol;
var loopName = (voice.asString ++ "Loop").asSymbol;
~loopOn.(loopName, sym);
};
"[loop] allOn (8 voies moteur)".postln;
};
~allOff = {
~loopEngineSyms.do { |sym|
var voice = sym.asString.replace("Seq", "").asSymbol;
var loopName = (voice.asString ++ "Loop").asSymbol;
if (~loops[loopName].notNil and: { ~loops[loopName][\state] == \on }) {
~loopOff.(loopName);
};
};
// Coupe aussi les Pdef directement, au cas ou on n'est pas passe
// par ~loopOn (compatibilite avec l'ancien workflow PLAY)
~loopEngineSyms.do { |sym| Pdef(sym).stop };
"[loop] allOff (8 voies moteur)".postln;
};
"[loops] gestionnaire pret -- ~loopList ~loopProgress ~allOn ~allOff".postln;
)
// =====================================================================
// EXEMPLES (Cmd+Entree sur une parenthese)
// =====================================================================
//
// // --- Boucles simples sur les Pdef du moteur -----------------------
// ( ~kickLoop.(); ) // kick infini
// ( ~hatLoop.(16); ) // hat sur 16 beats
// ( ~loopOff.(\kickLoop); )
// ( ~allOn.(); ) // les 8 voies du moteur
// ( ~allOff.(); )
//
// // --- Boucles sur fonctions custom ---------------------------------
// ( ~loopOn.(\blip, { 8.do { |i| (instrument: \pluck, midinote: 60+i).play; 0.25.wait } }); )
// ( ~loopOff.(\blip); )
//
// // --- Repeat / countdown -------------------------------------------
// ( ~loopRepeat.(\drop, \kickSeq, 4, 4); ) // 4 fois 4 beats
// ( ~loopWithCountdown.(\intro, \melodySeq, 4, inf); )
// ( ~loopCountdown.(4, { "drop !".postln }); )
//
// // --- Tempo-sync ---------------------------------------------------
// ( ~loopBeats.(\stab, \snareSeq, 8); )
// ( ~loopBars.(\verse, \melodySeq, 4); ) // 4 mesures
// ( ~loopSync.(); ) // resync au temps fort
//
// // --- Compositions -------------------------------------------------
// ( ~loopChain.([
// [\a, \kickSeq, 8],
// [\b, \snareSeq, 8],
// [\c, \melodySeq, 16]
// ]); )
// ( ~loopParallel.([[\k, \kickSeq], [\h, \hatSeq], [\m, \melodySeq]]); )
// ( ~loopFade.(\kickLoop, 2); )
// ( ~loopFadeIn.(\melodyLoop, \melodySeq, 4); )
//
// // --- Compteurs ----------------------------------------------------
// ( ~loopCount.(\drop).postln; )
// ( ~loopReset.(\drop); )
// ( ~loopMax.(\drop, 8); )
//
// // --- Auto-stop / auto-fade ----------------------------------------
// ( ~loopAutoStop.(\kickLoop, 32); )
// ( ~loopAutoFade.(\acidLoop, 16, 2); )
//
// // --- Snapshot / restore -------------------------------------------
// ( ~loopSnapshot.(\acidLoop); )
// ( ~loopRestore.(\acidLoop); )
// ( ~loopSave.(\acidLoop); )
// ( ~loopLoad.(\acidLoop); )
//
// // --- Affichage ----------------------------------------------------
// ( ~loopList.(); )
// ( ~loopProgress.(); )
// ( ~loopShow.(); )
// ( ~loopInfo.(\kickLoop); )
// ( ~loopIsOn.(\kickLoop).postln; )
//
// =====================================================================
+222
View File
@@ -0,0 +1,222 @@
// =====================================================================
// live/melodies.scd -- MELODIES LIVE (API ultra-courte)
//
// ATTENTION SC -- noter la notation des appels :
//
// NE MARCHE PAS : ~m.short.(50)
// -> SC passe l'event ~m comme 1er arg, et les vrais args sont
// PERDUS (test empirique sur SC 3.14.1).
//
// MARCHE BIEN : ~m[\short].(50) (notation indexee)
// ~mShort.(50) (variable env directe)
// ~mm.(50) (alias env direct)
//
// ~m // dict catalogue
// ~m[\short].(N) // applique m{N} (32 pas, 1..78)
// ~m[\long].(N) // applique ml{N} (64 pas, 1..30)
// ~m[\xlong].(N) // applique mxl{N} (96 pas, 1..15)
// ~m[\epic].(N) // applique mepic{N}(128 pas, 1..10)
// ~m[\rnd][\short].() etc.
// ~m[\gen].(\acid|\trance|...) // random par genre
// ~m[\nx].() / ~m[\pv].() // next/prev
// ~m[\inst].(\warmPad) // change ~melodyInst + reload
// ~m[\harm][\go].(\third) // ~enableHarmony
// ~m[\harm][\inst].(\warmPad) // change ~harmonyInst
// ~m[\amp] / [\cut] / [\drive] / [\off]
// ~m[\list].()
//
// RACCOURCIS env (API utilisateur primaire) :
// ~mShort, ~mLong, ~mXlong, ~mEpic
// ~mRnd, ~mRndLong, ~mRndXlong, ~mRndEpic
// ~mGen, ~mNx, ~mPv
// ~mInst, ~mHarm, ~mHarmInst
// ~mAmp, ~mCut, ~mDrive, ~mOff
// ~mList
// ~mm (= ~mShort, alias court)
//
// EXEMPLES :
// ~mm.(29) // melodie acid m29
// ~mLong.(15) // melodie longue ml15
// ~mGen.(\trance) // random dans le genre trance
// ~mHarm.(\third) // ajoute la 2e voix en tierce
// ~mNx.() // melodie suivante
//
// NOTE : noms reserves Object/Server (`s` = Server.default, `next`,
// `prev`...) ne marcheraient pas comme cles d'Event SC pseudo-method.
// Mais comme on N'UTILISE PAS la pseudo-method (~m.key.()), les noms
// des cles dans ~m peuvent rester courts (\short, \long, \nx, \pv).
// Les renommages c->cur etc. sont quand meme appliques pour clarte.
//
// PREREQUIS : engine + palette/melodies/* + palette/harmonics/* charges.
// =====================================================================
(
~m = ();
// --- etat courant : kind + numero (pour ~m[\nx] / ~m[\pv]) ----------
~m[\cur] = ( kind: \short, n: 1 );
// --- helper interne ------------------------------------------------
~m[\apply] = { |kind, n|
var prefix, max, key, notes;
prefix = switch (kind,
\short, "m",
\long, "ml",
\xlong, "mxl",
\epic, "mepic",
"m"
);
max = switch (kind, \short, 78, \long, 30, \xlong, 15, \epic, 10, 78);
n = ((n - 1).mod(max)) + 1;
key = (prefix ++ n).asSymbol;
notes = currentEnvironment[key];
if (notes.notNil and: { notes.isArray }) {
~melodyNotes = notes.copy;
~reload !? { ~reload.value };
~m[\cur] = ( kind: kind, n: n );
("[m." ++ kind ++ "] " ++ key).postln;
} {
("[m] melodie " ++ key ++ " inconnue (charge palette/melodies ?)").postln;
};
};
~m[\short] = { |n = 1| ~m[\apply].(\short, n) };
~m[\long] = { |n = 1| ~m[\apply].(\long, n) };
~m[\xlong] = { |n = 1| ~m[\apply].(\xlong, n) };
~m[\epic] = { |n = 1| ~m[\apply].(\epic, n) };
~m[\rnd] = ();
~m[\rnd][\short] = { ~m[\apply].(\short, 78.rand + 1) };
~m[\rnd][\long] = { ~randomLong !? { ~randomLong.() }; ~m[\cur] = ( kind: \long, n: 1 ) };
~m[\rnd][\xlong] = { ~randomXLong !? { ~randomXLong.() }; ~m[\cur] = ( kind: \xlong, n: 1 ) };
~m[\rnd][\epic] = { ~randomEpic !? { ~randomEpic.() }; ~m[\cur] = ( kind: \epic, n: 1 ) };
~m[\rnd][\value] = { ~m[\apply].(\short, 78.rand + 1) };
~m[\gen] = { |genre = \acid|
~melodyByGenre !? { ~melodyByGenre.(genre) };
};
~m[\nx] = { ~m[\apply].(~m[\cur][\kind], ~m[\cur][\n] + 1) };
~m[\pv] = { ~m[\apply].(~m[\cur][\kind], ~m[\cur][\n] - 1) };
~m[\inst] = { |inst = \lead|
~melodyInst = inst;
~reload !? { ~reload.value };
("[m.inst] ~melodyInst = " ++ inst).postln;
};
~m[\harm] = ();
~m[\harm][\go] = { |preset = \off|
~enableHarmony !? { ~enableHarmony.(preset) };
("[m.harm] " ++ preset).postln;
};
~m[\harm][\inst] = { |inst = \pad|
~harmonyInst = inst;
~reload !? { ~reload.value };
("[m.harm.inst] ~harmonyInst = " ++ inst).postln;
};
~m[\amp] = { |val = 0.16|
~melodyAmp = val.clip(0, 1);
~reload !? { ~reload.value };
("[m.amp] " ++ ~melodyAmp).postln;
};
~m[\cut] = { |hz = 2400|
~melodyCutoff = hz.clip(20, 20000);
~reload !? { ~reload.value };
("[m.cut] " ++ ~melodyCutoff ++ "Hz").postln;
};
~m[\drive] = { |val = 1.2|
~melodyDrive = val.max(0.1);
~reload !? { ~reload.value };
("[m.drive] " ++ ~melodyDrive).postln;
};
~m[\off] = {
~melodyAmp = 0;
~reload !? { ~reload.value };
"[m.off] melody silencieuse".postln;
};
~m[\list] = {
"=== ~m -- MELODIES ===".postln;
(" courant : " ++ ~m[\cur][\kind] ++ " #" ++ ~m[\cur][\n]).postln;
" ranges : short 1..78 / long 1..30 / xlong 1..15 / epic 1..10".postln;
if (~melodyBank.notNil) {
" genres : ".post; ~melodyBank.keys.asArray.sort.postln;
} { " genres : (~melodyBank non charge)".postln };
" USAGE : ~mm.(29) | ~mLong.(15) | ~mGen.(\\trance) | ~mNx.()".postln;
};
// --- API utilisateur : variables d'env top-level --------------------
~mShort = { |n = 1| ~m[\apply].(\short, n) };
~mLong = { |n = 1| ~m[\apply].(\long, n) };
~mXlong = { |n = 1| ~m[\apply].(\xlong, n) };
~mEpic = { |n = 1| ~m[\apply].(\epic, n) };
~mRnd = { ~m[\apply].(\short, 78.rand + 1) };
~mRndLong = { ~randomLong !? { ~randomLong.() }; ~m[\cur] = ( kind: \long, n: 1 ) };
~mRndXlong = { ~randomXLong !? { ~randomXLong.() }; ~m[\cur] = ( kind: \xlong, n: 1 ) };
~mRndEpic = { ~randomEpic !? { ~randomEpic.() }; ~m[\cur] = ( kind: \epic, n: 1 ) };
~mGen = { |genre = \acid| ~melodyByGenre !? { ~melodyByGenre.(genre) } };
~mNx = { ~m[\apply].(~m[\cur][\kind], ~m[\cur][\n] + 1) };
~mPv = { ~m[\apply].(~m[\cur][\kind], ~m[\cur][\n] - 1) };
~mInst = { |inst = \lead| ~m[\inst].(inst) };
~mHarm = { |preset = \off| ~m[\harm][\go].(preset) };
~mHarmInst = { |inst = \pad| ~m[\harm][\inst].(inst) };
~mAmp = { |val = 0.16| ~m[\amp].(val) };
~mCut = { |hz = 2400| ~m[\cut].(hz) };
~mDrive = { |val = 1.2| ~m[\drive].(val) };
~mOff = { ~m[\off].() };
~mList = { ~m[\list].() };
~mm = { |n = 1| ~m[\apply].(\short, n) }; // alias court
"=== ~m pret -- ~mm.(29) | ~mLong.(15) | ~mGen.(\\trance) | ~mList.() ===".postln;
)
// =====================================================================
// EXEMPLES (Cmd+Entree pour appliquer)
// =====================================================================
// --- short / long / xlong / epic par numero ---
// ( ~mm.(29); ) // m29 (acid classique)
// ( ~mm.(50); ) // m50
// ( ~mLong.(15); ) // ml15 (long 64 pas)
// ( ~mXlong.(5); ) // mxl5 (xlong 96 pas)
// ( ~mEpic.(1); ) // mepic1 (epic 128 pas)
// --- random ---
// ( ~mRnd.(); ) // short random
// ( ~mRndLong.(); ) // long random
// ( ~mRndXlong.(); ) // xlong random
// ( ~mRndEpic.(); ) // epic random
// --- random par genre ---
// ( ~mGen.(\acid); )
// ( ~mGen.(\trance); )
// ( ~mGen.(\dub); )
// ( ~mGen.(\detroit); )
// ( ~mGen.(\oriental); )
// --- navigation ---
// ( ~mNx.(); ) // suivante
// ( ~mPv.(); ) // precedente
// --- timbre / harmonie ---
( ~mInst.(\warmPad); )
( ~mInst.(\pluck); )
( ~mInst.(\stab); )
// ( ~mHarm.(\third); ) // tierce
( ~mHarm.(\fifth); ) // quinte
// ( ~mHarm.(\counter); )
// ( ~mHarm.(\off); )
// ( ~mHarmInst.(\warmPad); )
// --- reglages live ---
( ~mAmp.(0.2); )
( ~mCut.(1200); )
( ~mDrive.(2.0); )
( ~mOff.(); ) // mute melodie
// --- liste ---
// ( ~mList.(); )
+307
View File
@@ -0,0 +1,307 @@
// =====================================================================
// live/memory_guard.scd -- Garde-fous fuite memoire scsynth
//
// Expose les helpers live :
//
// ~killAllOrphans.() stop toutes les Routines + Pdefs +
// LFO connus + libere les bus FX
// dynamiques, SANS toucher au master
// bus ni aux FX bus statiques.
// -> a appeler avant chaque relance.
//
// ~freeDynFxBuses.() libere uniquement les Bus.audio
// alloues a la volee par ~addFx.
//
// ~nukeOrphanSynths.() PANIQUE : defaultGroup.freeAll.
// Coupe l'audio, relance ~setupAll.
//
// ~auditBuses.() compte bus statiques + dynamiques
// et liste les orphelins (dyn sans FX).
//
// ~memReport.() snapshot numSynths/Groups/UGens/CPU.
//
// ~watchdogOn.(seuil, interval) Routine fond, log si numSynths
// depasse le seuil. Defauts : 80, 10s.
//
// ~watchdogOff.() arrete le watchdog.
//
// ~guardCmdPeriodOn.() hook auto : Cmd-. declenche aussi
// ~killAllOrphans. Idempotent.
//
// ~guardCmdPeriodOff.() retire le hook.
//
// PREREQUIS : engine.scd + setup.scd evalues, serveur boote.
// Convention : un seul bloc top-level (compatible .load).
// =====================================================================
(
// =====================================================================
// Registry des Routines connues
// On ne touche jamais a master_comp / master_lim / vinyl /
// les FX bus (~fxFx) -- ils doivent rester pour ne pas tuer l'audio.
// =====================================================================
~guardKnownRoutines = [
\track, // tracks/*.scd
\albumRoutine, // tracks/<L>_<slug>/_album.scd
\filterSweep, // engine.scd
\randomLoop, // engine.scd
\melodyEvolve, // engine.scd
\lRotate, // engine.scd / live
\crossfadeFxRoutine, // live/fx_bus.scd
\chainRoutine, // live/chains.scd
\playlist, // live/scenes
\vcfSweepRoutine, // live/live.scd ~vcfSweep (sweep cutoff VCF)
\guardWatchdog, // notre propre watchdog (auto-stop)
];
~guardKnownPdefs = [
\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq,
];
// =====================================================================
// ~killAllOrphans : stop tout ce qui peut accumuler des Synths
// Ne free pas l'infra audio (master bus + FX bus restent vivants).
// =====================================================================
~killAllOrphans = {
var stopped = 0;
// 1. Routines simples enregistrees
~guardKnownRoutines.do { |key|
var r = currentEnvironment[key];
if (r.notNil) {
try { r.stop } { |err| };
currentEnvironment[key] = nil;
stopped = stopped + 1;
};
};
// 2. Pdefs sequenceurs connus
~guardKnownPdefs.do { |key| Pdef(key).stop };
// 3. Tricks routines (live.scd : ~trickRoutines[\stutter|\drop|...])
~trickRoutines !? {
~trickRoutines.keysValuesDo { |k, v|
try { v.stop } { |err| };
stopped = stopped + 1;
};
~trickRoutines.clear;
};
// 4. LFO assignables (live.scd : ~lfos[paramName])
~lfos !? {
~lfos.keysValuesDo { |k, v|
try { v.stop } { |err| };
stopped = stopped + 1;
};
~lfos.clear;
};
~lfoStopEverything !? { ~lfoStopEverything.value };
// 5. Envelopes one-shot (live.scd : ~envs[paramName])
~envs !? {
~envs.keysValuesDo { |k, v|
try { v.stop } { |err| };
stopped = stopped + 1;
};
~envs.clear;
};
// 6. LFO du bus FX (fx_bus.scd : ~fxLfos[fxKey])
~fxLfos !? {
~fxLfos.keysValuesDo { |k, v|
try { v.stop } { |err| };
stopped = stopped + 1;
};
~fxLfos.clear;
};
~lfoStopAll !? { ~lfoStopAll.value };
// 7. Loops live (live.scd : ~loops[name])
~loopOffAll !? { ~loopOffAll.value };
// 8. Auto-fx panning / tremolo
~stopAutoFx !? {
[\kick, \hat, \snare, \clap, \perc, \acid, \melody, \harmony]
.do { |v| try { ~stopAutoFx.(v) } { |err| } };
};
// 9. Bus FX alloues dynamiquement par ~addFx (live.scd) - non
// couverts par le guard de fx_bus.scd
~freeDynFxBuses !? { ~freeDynFxBuses.value };
("[guard] killAllOrphans : " ++ stopped ++ " routines stoppees, "
++ ~guardKnownPdefs.size ++ " Pdefs stoppes").postln;
stopped;
};
// =====================================================================
// ~freeDynFxBuses : libere les Bus.audio alloues a la volee par
// ~addFx (live.scd). Detruit aussi le Synth FX correspondant pour
// eviter qu'il pointe sur un bus libere.
// =====================================================================
~freeDynFxBuses = {
var n = 0;
~fxBusesDyn !? {
~fxBusesDyn.keysValuesDo { |key, bus|
~fxFx !? {
~fxFx[key] !? { try { ~fxFx[key].free }; ~fxFx[key] = nil };
};
~fxBuses !? { ~fxBuses[key] = nil };
try { bus.free };
n = n + 1;
};
~fxBusesDyn.clear;
};
if (n > 0) { ("[guard] freeDynFxBuses : " ++ n ++ " bus liberes").postln };
n;
};
// =====================================================================
// ~nukeOrphanSynths : option PANIQUE. Free tous les Synths du
// defaultGroup -- a n'utiliser que si ~killAllOrphans n'a pas suffi.
// Risque : coupe l'audio en cours. Conserve les Groups et l'infra.
// =====================================================================
~nukeOrphanSynths = {
var srv = Server.default;
if (srv.serverRunning) {
var before = srv.numSynths;
srv.defaultGroup.freeAll;
("[guard] NUKE : defaultGroup.freeAll (synths avant=" ++ before
++ ") -- relance ~setupAll pour reconstruire").postln;
} {
"[guard] !! serveur non boote".postln;
};
};
// =====================================================================
// ~auditBuses : compte les Bus alloues (statiques + dynamiques) et
// detecte les fuites probables (dyn buses orphelins sans Synth FX).
// =====================================================================
~auditBuses = {
var nStatic = 0, nDyn = 0, orphans = [];
~fxBuses !? { nStatic = ~fxBuses.size };
~fxBusesDyn !? {
nDyn = ~fxBusesDyn.size;
~fxBusesDyn.keysValuesDo { |k, b|
if (~fxFx.isNil or: { ~fxFx[k].isNil }) {
orphans = orphans.add(k);
};
};
};
("[audit] fxBuses static=" ++ nStatic ++ " dyn=" ++ nDyn
++ " orphans=" ++ orphans).postln;
(static: nStatic, dyn: nDyn, orphans: orphans);
};
// =====================================================================
// ~guardCmdPeriodOn / Off : hook CmdPeriod automatique.
// Quand l'utilisateur fait Cmd-. (panic stop), on lance
// ~killAllOrphans en plus du free standard de SC. Idempotent.
// =====================================================================
~guardCmdPeriodFn = ~guardCmdPeriodFn ?? { { ~killAllOrphans.value } };
~guardCmdPeriodOn = {
CmdPeriod.remove(~guardCmdPeriodFn); // idempotence
CmdPeriod.add(~guardCmdPeriodFn);
"[guard] CmdPeriod hook ON (~killAllOrphans sur Cmd-.)".postln;
};
~guardCmdPeriodOff = {
CmdPeriod.remove(~guardCmdPeriodFn);
"[guard] CmdPeriod hook OFF".postln;
};
// =====================================================================
// ~memReport : snapshot ponctuel de l'etat scsynth
// =====================================================================
~memReport = {
var srv = Server.default;
var nSynths, nGroups, nUGens, line;
if (srv.serverRunning.not) {
"[guard] !! serveur non boote".postln;
nil;
} {
nSynths = srv.numSynths;
nGroups = srv.numGroups;
nUGens = srv.numUGens;
line = "[mem] synths=" ++ nSynths
++ " groups=" ++ nGroups
++ " ugens=" ++ nUGens
++ " avgCPU=" ++ srv.avgCPU.round(0.1) ++ "%"
++ " peakCPU=" ++ srv.peakCPU.round(0.1) ++ "%";
line.postln;
nSynths;
};
};
// =====================================================================
// ~watchdogOn / ~watchdogOff : surveillance numSynths en tache de fond
// Ne tue rien automatiquement -- log seulement un warning si depasse.
// Tu decides si tu veux ~killAllOrphans.() en reponse.
// =====================================================================
~watchdogOn = { |seuil = 80, interval = 10|
~guardWatchdog !? { ~guardWatchdog.stop; ~guardWatchdog = nil };
~guardSeuil = seuil;
~guardInterval = interval;
~guardLastN = 0;
~guardWatchdog = Routine({
loop {
interval.wait;
if (Server.default.serverRunning) {
var n = Server.default.numSynths;
var dn = n - ~guardLastN;
~guardLastN = n;
if (n > seuil) {
("[GUARD] !! synths=" ++ n ++ " > seuil " ++ seuil
++ " (Δ=" ++ dn ++ ")"
++ " -- envisage ~killAllOrphans.()").postln;
};
};
};
}).play(AppClock);
("[guard] watchdog ON (seuil=" ++ seuil
++ " synths, interval=" ++ interval ++ "s)").postln;
};
~watchdogOff = {
~guardWatchdog !? { ~guardWatchdog.stop; ~guardWatchdog = nil };
"[guard] watchdog OFF".postln;
};
"[memory_guard] OK -- ~killAllOrphans / ~freeDynFxBuses / ~nukeOrphanSynths / ~auditBuses / ~memReport / ~watchdogOn(seuil,intv) / ~watchdogOff / ~guardCmdPeriodOn".postln;
)
// =====================================================================
// RACCOURCIS LIVE -- Cmd+Entree sur la ligne souhaitee
// =====================================================================
( ~memReport.(); ) // snapshot ponctuel
( ~auditBuses.(); ) // compte bus + orphelins
( ~killAllOrphans.(); ) // nettoyage avant relance
( ~freeDynFxBuses.(); ) // free uniquement bus FX dynamiques
( ~nukeOrphanSynths.(); ) // PANIQUE : defaultGroup.freeAll
( ~watchdogOn.(80, 10); ) // surveille (seuil 80, toutes les 10s)
( ~watchdogOn.(120, 5); ) // session lourde (seuil 120, toutes les 5s)
( ~watchdogOff.(); ) // arrete la surveillance
( ~guardCmdPeriodOn.(); ) // Cmd-. declenche aussi killAllOrphans
( ~guardCmdPeriodOff.(); ) // retire le hook
+693
View File
@@ -0,0 +1,693 @@
// =====================================================================
// mixer.scd -- Mixer / faders par voie pour systeme live
//
// PREREQUIS : engine.scd evalue (~kickAmp, ~hatAmp, ..., ~reload defini)
//
// PHILOSOPHIE
// Les amps "de reference" (~mixRef) sont les valeurs courantes au
// moment de l'init du mixer. Les coefficients ~mix sont des
// multiplicateurs (0..1+) appliques sur ~mixRef avant chaque reload.
// -> ~setVol.(\kick, 0.5) baisse le kick a 50% de sa valeur de base
// -> ~mixReset remet tous les coefs a 1 (= valeurs de reference)
// -> changer ~mixRef.kick rebase la reference (ex: changement de kit)
//
// USAGE RAPIDE
// ( ~setVol.(\kick, 0.7); )
// ( ~mute.(\hat); ) ( ~unmute.(\hat); )
// ( ~solo.(\acid); ) ( ~unsolo.(); )
// ( ~xfade.(\acid, \melody, 4); )
// ( ~mixPreset.(\drumsHeavy); )
// ( ~mixShow.(); )
// =====================================================================
(
// --- 1. Init des structures ----------------------------------------
~mixVoices = [\kick, \hat, \snare, \clap, \perc, \acid, \melody, \harmony];
// Coefficients courants (multiplicateurs 0..1+ par voie)
~mix = ~mix ?? {
(kick: 1, hat: 1, snare: 1, clap: 1, perc: 1, acid: 1, melody: 1, harmony: 1)
};
// Etats internes
~mixMuted = ~mixMuted ?? { Dictionary.new }; // voies mutees -> ancien coef
~mixSoloed = ~mixSoloed ?? { Dictionary.new }; // snapshot avant solo
~mixesSaved = ~mixesSaved ?? { Dictionary.new }; // mixes nommes
~mixFocusBak = ~mixFocusBak ?? { nil }; // snapshot avant focus
~mixDuckRout = ~mixDuckRout ?? { nil }; // routine duck en cours
~mixFadeRout = ~mixFadeRout ?? { Dictionary.new }; // routines de fade par voie
~mixMasterRt = ~mixMasterRt ?? { nil }; // routine master fade
// ~mixRef : valeurs "de reference" des amps moteur (capturees a l'init)
// Si une voie est consideree mutee (amp = 0), on garde une reference par
// defaut pour pouvoir restaurer.
~mixRefDefaults = (
kick: 0.5, hat: 0.18, snare: 0.45, clap: 0.35,
perc: 0.25, acid: 0.32, melody: 0.16, harmony: 0.14
);
// Capture les amps moteur courants comme reference (1ere fois seulement)
~mixCaptureRef = {
~mixRef = ();
~mixVoices.do { |v|
var key = (v.asString ++ "Amp").asSymbol;
var cur = currentEnvironment[key];
// si amp courant est nil ou 0 -> prend la valeur par defaut
if (cur.isNil or: { cur == 0 }) {
~mixRef[v] = ~mixRefDefaults[v];
} {
~mixRef[v] = cur;
};
};
};
// Capture seulement si pas deja fait (preserve la reference entre reloads)
~mixRef ?? { ~mixCaptureRef.value };
// Re-capture force (utile apres changement de kit / preset moteur)
~mixRebase = {
~mixCaptureRef.value;
"[mixer] reference rebase".postln;
};
// --- 2. Application des coefficients --------------------------------
// amp = ref * coef_voie * master (puis reload des Pdef)
~mixMaster = ~mixMaster ?? 1.0; // multiplicateur global non-destructif
~mixApply = {
~mixVoices.do { |v|
var key = (v.asString ++ "Amp").asSymbol;
var refV = ~mixRef[v] ? 0;
var coef = ~mix[v] ? 1;
currentEnvironment.put(key, refV * coef * ~mixMaster);
};
~reload.value;
};
// --- 3. Set / get / reset volume par voie ---------------------------
~setVol = { |voie, val|
if (~mixVoices.includes(voie).not) {
("!! voie inconnue : " ++ voie ++ " -- voies : " ++ ~mixVoices).postln;
} {
~mix[voie] = val.max(0);
~mixApply.value;
("[mix] " ++ voie ++ " = " ++ val.round(0.01)).postln;
};
};
~getVol = { |voie| ~mix[voie] ? 1 };
~mixReset = {
~mixVoices.do { |v| ~mix[v] = 1 };
~mixMuted.clear;
~mixSoloed.clear;
~mixApply.value;
"[mix] reset (tous coefs = 1)".postln;
};
// --- 4. Mute / Unmute / Solo ----------------------------------------
~mute = { |voie|
if (~mixVoices.includes(voie).not) {
("!! voie inconnue : " ++ voie).postln;
} {
if (~mixMuted.includesKey(voie).not) {
~mixMuted[voie] = ~mix[voie] ? 1;
~mix[voie] = 0;
~mixApply.value;
("[mute] " ++ voie).postln;
};
};
};
~unmute = { |voie|
if (~mixMuted.includesKey(voie)) {
~mix[voie] = ~mixMuted[voie];
~mixMuted.removeAt(voie);
~mixApply.value;
("[unmute] " ++ voie).postln;
} {
("[unmute] " ++ voie ++ " n'etait pas mute").postln;
};
};
~muteToggle = { |voie|
if (~mixMuted.includesKey(voie)) { ~unmute.(voie) } { ~mute.(voie) };
};
~muteAll = {
~mixVoices.do { |v|
if (~mixMuted.includesKey(v).not) {
~mixMuted[v] = ~mix[v] ? 1;
~mix[v] = 0;
};
};
~mixApply.value;
"[mute all]".postln;
};
~unmuteAll = {
~mixMuted.keysValuesDo { |v, prev| ~mix[v] = prev };
~mixMuted.clear;
~mixApply.value;
"[unmute all]".postln;
};
~mutedList = {
var l = ~mixMuted.keys.asArray;
("[muted] " ++ l).postln;
l;
};
// Solo : mute toutes les autres voies (snapshot dans ~mixSoloed)
~solo = { |voie|
if (~mixVoices.includes(voie).not) {
("!! voie inconnue : " ++ voie).postln;
} {
// Si pas deja en solo, snapshot l'etat courant
if (~mixSoloed.size == 0) {
~mixVoices.do { |v| ~mixSoloed[v] = ~mix[v] ? 1 };
};
~mixVoices.do { |v|
~mix[v] = if (v == voie) { ~mixSoloed[v] } { 0 };
};
~mixApply.value;
("[solo] " ++ voie).postln;
};
};
~unsolo = {
if (~mixSoloed.size == 0) {
"[unsolo] aucun solo actif".postln;
} {
~mixSoloed.keysValuesDo { |v, val| ~mix[v] = val };
~mixSoloed.clear;
~mixApply.value;
"[unsolo]".postln;
};
};
// --- 5. Master volume / fades ---------------------------------------
// Set le multiplicateur master ABSOLU (non destructif sur les coefs voies)
// ~masterVol.(0.5) -> moitie ; ~masterVol.(1) -> retour normal
~masterVol = { |val|
~mixMaster = val.max(0);
~mixApply.value;
("[master] = " ++ ~mixMaster.round(0.01)).postln;
};
// Fade le multiplicateur master vers une valeur cible (preserve les coefs voies)
~masterFade = { |target = 0, dur = 4|
~mixMasterRt !? { ~mixMasterRt.stop };
~mixMasterRt = Routine({
var steps = (dur * 30).max(2).asInteger;
var startMaster = ~mixMaster ? 1;
steps.do { |i|
var t = (i + 1) / steps;
~mixMaster = startMaster.blend(target, t);
~mixApply.value;
(dur / steps).wait;
};
("[master fade] -> " ++ target.round(0.01)).postln;
}).play;
};
// Fade-in depuis 0 vers le master courant (1.0 par defaut)
~masterFadeIn = { |dur = 4, target = 1|
~mixMaster = 0;
~mixApply.value;
~masterFade.(target, dur);
};
// Fade-out vers 0 puis stop des Pdef sequencer (preserve les coefs voies)
~masterFadeOut = { |dur = 4|
~mixMasterRt !? { ~mixMasterRt.stop };
~mixMasterRt = Routine({
var steps = (dur * 30).max(2).asInteger;
var startMaster = ~mixMaster ? 1;
steps.do { |i|
var t = (i + 1) / steps;
~mixMaster = startMaster.blend(0, t);
~mixApply.value;
(dur / steps).wait;
};
[\kickSeq, \hatSeq, \snareSeq, \clapSeq, \percSeq,
\acidSeq, \melodySeq, \harmonySeq].do { |k| Pdef(k).stop };
"[master fade-out] STOP".postln;
}).play;
};
// Duck momentane : baisse a depth*current pendant attack, restaure en release
~masterDuck = { |depth = 0.3, attack = 0.05, release = 0.4|
~mixDuckRout !? { ~mixDuckRout.stop };
~mixDuckRout = Routine({
var snapshot = ();
var stepsA = (attack * 30).max(2).asInteger;
var stepsR = (release * 30).max(2).asInteger;
~mixVoices.do { |v| snapshot[v] = ~mix[v] ? 1 };
// attack : descente vers depth*current
stepsA.do { |i|
var t = (i + 1) / stepsA;
~mixVoices.do { |v|
~mix[v] = snapshot[v].blend(snapshot[v] * depth, t);
};
~mixApply.value;
(attack / stepsA).wait;
};
// release : remontee
stepsR.do { |i|
var t = (i + 1) / stepsR;
~mixVoices.do { |v|
~mix[v] = (snapshot[v] * depth).blend(snapshot[v], t);
};
~mixApply.value;
(release / stepsR).wait;
};
"[duck] OK".postln;
}).play;
};
// --- 6. Presets de balance ------------------------------------------
~mixPresets = (
flat: (kick: 1.0, hat: 1.0, snare: 1.0, clap: 1.0,
perc: 1.0, acid: 1.0, melody: 1.0, harmony: 1.0),
drumsHeavy: (kick: 1.3, hat: 1.2, snare: 1.2, clap: 1.1,
perc: 1.1, acid: 0.7, melody: 0.5, harmony: 0.4),
melodicLead: (kick: 0.7, hat: 0.6, snare: 0.6, clap: 0.5,
perc: 0.5, acid: 0.6, melody: 1.5, harmony: 1.1),
acidFocus: (kick: 0.8, hat: 0.6, snare: 0.5, clap: 0.4,
perc: 0.4, acid: 1.5, melody: 0.5, harmony: 0.3),
bassFocus: (kick: 1.3, hat: 0.5, snare: 0.6, clap: 0.4,
perc: 0.4, acid: 1.3, melody: 1.1, harmony: 0.5),
breakdown: (kick: 0.0, hat: 0.0, snare: 0.0, clap: 0.0,
perc: 0.3, acid: 0.6, melody: 1.1, harmony: 1.0),
buildup: (kick: 0.4, hat: 0.7, snare: 0.5, clap: 0.5,
perc: 0.6, acid: 0.9, melody: 1.0, harmony: 0.8),
drop: (kick: 1.2, hat: 1.1, snare: 1.1, clap: 1.1,
perc: 1.0, acid: 1.0, melody: 1.0, harmony: 0.9),
dub: (kick: 1.3, hat: 1.0, snare: 0.5, clap: 0.6,
perc: 1.2, acid: 0.7, melody: 1.4, harmony: 0.6),
trance: (kick: 1.2, hat: 1.2, snare: 0.8, clap: 1.2,
perc: 0.7, acid: 0.6, melody: 1.4, harmony: 1.0),
detroit: (kick: 1.0, hat: 0.9, snare: 0.9, clap: 0.8,
perc: 0.8, acid: 1.0, melody: 1.0, harmony: 0.9),
hardcore: (kick: 1.5, hat: 1.0, snare: 1.4, clap: 1.0,
perc: 0.9, acid: 1.1, melody: 0.5, harmony: 0.3),
);
~mixPreset = { |name|
var p = ~mixPresets[name];
if (p.isNil) {
("!! preset inconnu : " ++ name ++
" -- dispos : " ++ ~mixPresets.keys.asArray).postln;
} {
~mixVoices.do { |v| ~mix[v] = p[v] ? 1 };
~mixMuted.clear;
~mixSoloed.clear;
~mixApply.value;
("[preset] " ++ name).postln;
};
};
// --- 7. Save / recall des balances ----------------------------------
~saveMix = { |name|
var snap = ();
~mixVoices.do { |v| snap[v] = ~mix[v] ? 1 };
~mixesSaved[name] = snap;
("[mix saved] " ++ name).postln;
};
~loadMix = { |name|
var snap = ~mixesSaved[name];
if (snap.isNil) {
("!! mix '" ++ name ++ "' inexistant. Dispos : " ++
~mixesSaved.keys.asArray).postln;
} {
~mixVoices.do { |v| ~mix[v] = snap[v] ? 1 };
~mixMuted.clear;
~mixSoloed.clear;
~mixApply.value;
("[mix loaded] " ++ name).postln;
};
};
~listMixes = {
"=== MIXES sauvegardes ===".postln;
if (~mixesSaved.size == 0) {
" (aucun)".postln;
} {
~mixesSaved.keysValuesDo { |k, v|
(" " ++ k).postln;
};
};
~mixesSaved.keys.asArray;
};
~deleteMix = { |name|
if (~mixesSaved.includesKey(name)) {
~mixesSaved.removeAt(name);
("[mix deleted] " ++ name).postln;
} { ("mix '" ++ name ++ "' inexistant").postln };
};
// --- 8. Helpers de balance live -------------------------------------
// Crossfade entre 2 voies sur dur secondes
~xfade = { |voieA, voieB, dur = 4|
var key = (voieA.asString ++ "_" ++ voieB.asString).asSymbol;
~mixFadeRout[key] !? { ~mixFadeRout[key].stop };
~mixFadeRout[key] = Routine({
var steps = (dur * 30).max(2).asInteger;
var startA = ~mix[voieA] ? 1;
var startB = ~mix[voieB] ? 1;
var targetA = 0;
var targetB = if (startB > 0) { startB } { 1 };
steps.do { |i|
var t = (i + 1) / steps;
~mix[voieA] = startA.blend(targetA, t);
~mix[voieB] = startB.blend(targetB, t);
~mixApply.value;
(dur / steps).wait;
};
("[xfade] " ++ voieA ++ " -> " ++ voieB).postln;
}).play;
};
// Fade smooth d'une voie vers une cible
~fadeVol = { |voie, target = 1, dur = 2|
if (~mixVoices.includes(voie).not) {
("!! voie inconnue : " ++ voie).postln;
} {
~mixFadeRout[voie] !? { ~mixFadeRout[voie].stop };
~mixFadeRout[voie] = Routine({
var steps = (dur * 30).max(2).asInteger;
var start = ~mix[voie] ? 1;
steps.do { |i|
var t = (i + 1) / steps;
~mix[voie] = start.blend(target, t);
~mixApply.value;
(dur / steps).wait;
};
("[fade] " ++ voie ++ " -> " ++ target.round(0.01)).postln;
}).play;
};
};
// Echange instantane de 2 voies
~swapVol = { |voieA, voieB|
var tmp = ~mix[voieA] ? 1;
~mix[voieA] = ~mix[voieB] ? 1;
~mix[voieB] = tmp;
~mixApply.value;
("[swap] " ++ voieA ++ " <-> " ++ voieB).postln;
};
// Focus : boost une voie, baisse les autres a "others"
~focus = { |voie, others = 0.3|
if (~mixVoices.includes(voie).not) {
("!! voie inconnue : " ++ voie).postln;
} {
// snapshot avant focus
~mixFocusBak = ();
~mixVoices.do { |v| ~mixFocusBak[v] = ~mix[v] ? 1 };
~mixVoices.do { |v|
~mix[v] = if (v == voie) { 1.0 } { others };
};
~mixApply.value;
("[focus] " ++ voie ++ " (others=" ++ others ++ ")").postln;
};
};
~unfocus = {
if (~mixFocusBak.isNil) {
"[unfocus] aucun focus actif".postln;
} {
~mixFocusBak.keysValuesDo { |v, val| ~mix[v] = val };
~mixFocusBak = nil;
~mixApply.value;
"[unfocus]".postln;
};
};
// --- 9. Affichage / debug -------------------------------------------
// Bargraph ASCII (12 cells, max coef = 1.5 pour la visu)
~mixShow = {
var max = 1.5;
var cells = 12;
"=== MIXER ===".postln;
~mixVoices.do { |v|
var coef = ~mix[v] ? 1;
var filled = ((coef / max) * cells).round.asInteger.clip(0, cells);
var empty = cells - filled;
var bar = String.fill(filled, $|) ++ String.fill(empty, $.);
var lbl = (v.asString ++ " ").copyRange(0, 7);
var muted = if (~mixMuted.includesKey(v)) { " [MUTED]" } { "" };
var soloed = if (~mixSoloed.size > 0 and: { coef > 0 }) {
" [SOLO]"
} { "" };
// remplace les pipes pour rendre visuel : utilise des chars block
bar = bar.replace("|", "#");
(" " ++ lbl ++ "|" ++ bar ++ " " ++
coef.round(0.01).asString ++ muted ++ soloed).postln;
};
"=============".postln;
};
// Info compacte une ligne
~mixInfo = {
var s = "[mix] ";
~mixVoices.do { |v|
s = s ++ v.asString ++ ":" ++ (~mix[v] ? 1).round(0.01).asString ++ " ";
};
s.postln;
};
// --- 10. Limiter / clip warning -------------------------------------
// Calcul approximatif : somme des coef * amp_ref par voie
// Si > seuil -> warn (le master limiter sauvera, mais le mix est sale)
~mixClipWarn = { |seuil = 1.2|
var sum = 0;
~mixVoices.do { |v|
var refV = ~mixRef[v] ? 0;
var coef = ~mix[v] ? 1;
sum = sum + (refV * coef);
};
if (sum > seuil) {
("!! [clip warn] somme amps = " ++ sum.round(0.01) ++
" > seuil " ++ seuil ++ " -- risque de pompage limiter").postln;
} {
("[ok] somme amps = " ++ sum.round(0.01) ++
" (seuil " ++ seuil ++ ")").postln;
};
sum;
};
"=== mixer pret -- ~setVol ~mute ~solo ~mixPreset ~mixShow ===".postln;
)
// =====================================================================
// EXEMPLES D'USAGE -- Cmd+Entree sur un bloc pour l'executer
// PREREQUIS : avoir evalue le bloc SETUP en tete de fichier d'abord
// =====================================================================
// --- Faders simples --------------------------------------------------
// Baisse kick a 70%
( ~setVol.(\kick, 0.7); )
// Boost acid a 130%
( ~setVol.(\acid, 1.3); )
// Baisse melody a 40%
( ~setVol.(\melody, 0.4); )
// Lit le coef courant d'une voie
( ~getVol.(\kick); )
// Reset tous les coefs a 1
( ~mixReset.(); )
// --- Mute / unmute ---------------------------------------------------
// Mute hat
( ~mute.(\hat); )
// Unmute hat
( ~unmute.(\hat); )
// Toggle mute acid
( ~muteToggle.(\acid); )
// Mute toutes les voies
( ~muteAll.(); )
// Unmute toutes les voies
( ~unmuteAll.(); )
// Liste les voies mutees
( ~mutedList.(); )
// --- Solo ------------------------------------------------------------
// Solo acid (tout sauf acid -> mute)
( ~solo.(\acid); )
// Restore l'etat avant solo
( ~unsolo.(); )
// --- Master volume / fades -------------------------------------------
// Baisse global x0.5
( ~masterVol.(0.5); )
// Fade tous les coefs vers 0 en 8s
( ~masterFade.(0, 8); )
// Fade-in 4s depuis silence
( ~masterFadeIn.(4); )
// Fade-out 6s puis stop des Pdef
( ~masterFadeOut.(6); )
// Duck momentane (drop) : depth=0.3, attack=0.05s, release=0.5s
( ~masterDuck.(0.3, 0.05, 0.5); )
// --- Presets de balance ----------------------------------------------
// Tous coefs = 1
( ~mixPreset.(\flat); )
// Drums devant
( ~mixPreset.(\drumsHeavy); )
// Melody devant
( ~mixPreset.(\melodicLead); )
// Acid devant
( ~mixPreset.(\acidFocus); )
// Kick + acid devant
( ~mixPreset.(\bassFocus); )
// Drums coupes (breakdown)
( ~mixPreset.(\breakdown); )
// Buildup
( ~mixPreset.(\buildup); )
// Drop
( ~mixPreset.(\drop); )
// Dub
( ~mixPreset.(\dub); )
// Trance
( ~mixPreset.(\trance); )
// Detroit
( ~mixPreset.(\detroit); )
// Hardcore
( ~mixPreset.(\hardcore); )
// --- Save / recall des balances --------------------------------------
// Sauvegarde la balance courante sous \verseA
( ~saveMix.(\verseA); )
// Sauvegarde sous \drop1
( ~saveMix.(\drop1); )
// Recharge \verseA
( ~loadMix.(\verseA); )
// Liste les mixes sauvegardes
( ~listMixes.(); )
// Supprime un mix sauvegarde
( ~deleteMix.(\verseA); )
// --- Helpers live ----------------------------------------------------
// Crossfade acid -> melody en 4s
( ~xfade.(\acid, \melody, 4); )
// Fade kick vers 0.3 en 2s
( ~fadeVol.(\kick, 0.3, 2); )
// Echange instantane des coefs acid <-> melody
( ~swapVol.(\acid, \melody); )
// Focus acid (devant), autres voies a 0.2
( ~focus.(\acid, 0.2); )
// Restore l'etat avant focus
( ~unfocus.(); )
// --- Affichage / debug -----------------------------------------------
// Bargraph ASCII de tous les coefs
( ~mixShow.(); )
// Info compacte une ligne
( ~mixInfo.(); )
// Alerte si la somme des amps depasse le seuil (1.2 par defaut)
( ~mixClipWarn.(); )
// --- Rebase reference (apres changement de kit moteur) ---------------
// Re-capture les ~xxxAmp courants comme nouvelle reference
( ~mixRebase.(); )
// =====================================================================
// WORKFLOW LIVE PERFORMANCE COMPLET (sequence d'exemples chainables)
// =====================================================================
// 1. Demarrage en silence puis montee
( ~mixPreset.(\breakdown); ~masterFadeIn.(8); )
// 2. Verse calme (melody devant) + sauvegarde
( ~mixPreset.(\melodicLead); ~saveMix.(\verseA); )
// 3. Buildup progressif
( ~mixPreset.(\buildup); ~fadeVol.(\hat, 1.2, 4); )
// 4a. DROP : duck preparatoire
( ~masterDuck.(0.3, 0.1, 0.5); )
// 4b. DROP : preset drop + sauvegarde
( ~mixPreset.(\drop); ~saveMix.(\drop1); )
// 5a. Section acid solo : focus
( ~focus.(\acid, 0.25); )
// 5b. ... 16 mesures plus tard : unfocus
( ~unfocus.(); )
// 6. Breakdown ambient
( ~mixPreset.(\breakdown); )
// 7. Retour drop final
( ~loadMix.(\drop1); )
// 8. Fade-out final
( ~masterFadeOut.(12); )

Some files were not shown because too many files have changed in this diff Show More