diff --git a/Classes/Bank.sc b/Classes/Bank.sc index c909e6f..406b1e0 100644 --- a/Classes/Bank.sc +++ b/Classes/Bank.sc @@ -1,5 +1,6 @@ /* * This file is taken from: https://gist.github.com/scztt/73a2ae402d9765294ae8f72979d1720e +* I have added a method to list the samples in the bank. */ Bank : Singleton { diff --git a/Classes/BuboArray.sc b/Classes/BuboArray.sc index 5cf7ecd..3fe0823 100644 --- a/Classes/BuboArray.sc +++ b/Classes/BuboArray.sc @@ -55,16 +55,6 @@ ^correctedPattern; } - // pat { - // arg node_proxy, quant=4, fade=0.05; - // var newArray = this ++ [\type, \buboEvent]; - // node_proxy.quant_(quant); - // node_proxy.fadeTime = fade; - // node_proxy[0] = Pbind(*(this.findShortcuts(newArray))); - // ^node_proxy; - // } - - pat { arg quant=4, fade=0.05; var proxyName = this[0]; diff --git a/Classes/BuboNdef.sc b/Classes/BuboNdef.sc deleted file mode 100644 index c54907d..0000000 --- a/Classes/BuboNdef.sc +++ /dev/null @@ -1,3 +0,0 @@ -+ Ndef { -} - diff --git a/Classes/BuboObject.sc b/Classes/BuboPanic.sc similarity index 100% rename from Classes/BuboObject.sc rename to Classes/BuboPanic.sc diff --git a/Classes/BuboPbind.sc b/Classes/BuboPbind.sc deleted file mode 100644 index d666a1b..0000000 --- a/Classes/BuboPbind.sc +++ /dev/null @@ -1,2 +0,0 @@ -+ Pbind { -} diff --git a/Classes/BuboPlotTree.sc b/Classes/BuboPlotTree.sc deleted file mode 100644 index 6ee8174..0000000 --- a/Classes/BuboPlotTree.sc +++ /dev/null @@ -1,5 +0,0 @@ -PlotTree { - *new { - ^Server.default.plotTree.window.alwaysOnTop_(true); - } -} diff --git a/Classes/BuboString.sc b/Classes/BuboString.sc index ccbae1b..68f71aa 100644 --- a/Classes/BuboString.sc +++ b/Classes/BuboString.sc @@ -1,4 +1,5 @@ + String { + sp { arg sampleNumber = 0, repeats=inf; ^Pindex(Bank(this), sampleNumber, repeats); @@ -7,4 +8,5 @@ p { ^Pdv.parse(this) } + } diff --git a/Classes/BuboFreqScope.sc b/Classes/GUI/BuboFreqScope.sc similarity index 98% rename from Classes/BuboFreqScope.sc rename to Classes/GUI/BuboFreqScope.sc index 7687813..fbbdb35 100644 --- a/Classes/BuboFreqScope.sc +++ b/Classes/GUI/BuboFreqScope.sc @@ -3,4 +3,5 @@ FScope { *new { ^Server.default.freqscope.window.alwaysOnTop_(true); } + } diff --git a/Classes/BuboGui.sc b/Classes/GUI/BuboGui.sc similarity index 97% rename from Classes/BuboGui.sc rename to Classes/GUI/BuboGui.sc index d16d22e..a1e3fe7 100644 --- a/Classes/BuboGui.sc +++ b/Classes/GUI/BuboGui.sc @@ -1,5 +1,7 @@ Gui { + *new { ^Server.default.makeGui.window.alwaysOnTop_(true); } + } diff --git a/Classes/BuboScope.sc b/Classes/GUI/BuboScope.sc similarity index 98% rename from Classes/BuboScope.sc rename to Classes/GUI/BuboScope.sc index 5b4fa65..0da2299 100644 --- a/Classes/BuboScope.sc +++ b/Classes/GUI/BuboScope.sc @@ -3,4 +3,5 @@ Scope { *new { ^Server.default.scope.window.alwaysOnTop_(true); } + } diff --git a/Classes/PDyn.sc b/Classes/Patterns/PDyn.sc similarity index 100% rename from Classes/PDyn.sc rename to Classes/Patterns/PDyn.sc diff --git a/Classes/Startup.scd b/Classes/Startup.scd deleted file mode 100644 index 3eb940e..0000000 --- a/Classes/Startup.scd +++ /dev/null @@ -1,3 +0,0 @@ -c = currentEnvironment.clock; -MIDIClient.init; -m = MIDIOut.newByName("MIDI", "Bus 1"); diff --git a/Classes/Synthdefs.scd b/Classes/Synthdefs.scd deleted file mode 100644 index 73e2792..0000000 --- a/Classes/Synthdefs.scd +++ /dev/null @@ -1,291 +0,0 @@ -d = (); // This is a storage area for synthesizers -f = (); // This is a storage for various FX functions - -( -d.info = { arg obj, name; obj[name].allControlNames.do({arg i; i.postln;}); }; -d.show = { arg obj; obj.keys.do({arg i; i.postln}); }; -); - -f.vardel = { - arg in; - HPF.ar(CombC.ar(in, 2, c.beatDur / [1, 2], 2), - SinOsc.ar(c.beatDur * 4).range(500, 2000)); -}; - -( - z = SynthDef.new(\sampler, { - arg buf, rate=1, amp=1, pan=0, attack=0.01, release=1, loop=0; - var sig; - var env = EnvGen.ar(Env.perc(attack, release, doneAction: 2)); - var startPos = 0; - sig = PlayBuf.ar( - numChannels: 1, - bufnum: buf, - rate:BufRateScale.kr(buf) * rate, - trigger: 1, startPos:startPos, - loop:loop, doneAction: 2); - sig = sig * amp; - sig = Pan2.ar(sig * env, pan); - Out.ar(0, sig); - }).add; - d.sampler = z; -); - - -( - z = SynthDef.new(\revsampler, { - arg buf, rate=1, amp=1, pan=0, attack=0.01, release=1, loop=1; - var sig; - var env = EnvGen.ar(Env.perc(attack, release, doneAction: 2)); - var startPos = 0; - sig = PlayBuf.ar( - numChannels: 1, - bufnum: buf, - rate:BufRateScale.kr(buf) * rate, - trigger: 1, startPos:startPos, - loop:loop, doneAction: 2); - sig = sig * amp; - sig = Pan2.ar(sig * env, pan); - Out.ar(0, sig); - }).add; - d.revsampler = z; -); - -( - z = SynthDef(\sinfb, { - arg freq = 440, atk = 0.01, sus = 0, rel = 1, fb = 0, amp = 0.3, out = 0, pan=0; - var sig, env; - env = EnvGen.ar(Env.linen(atk,sus,rel),1,1,0,1,2); - sig = SinOscFB.ar(freq,fb,1); - sig = sig*env; - Out.ar(out,Pan2.ar(sig,pan,amp)); - }).add; - d.sinfb = z; -); - -( - z = SynthDef(\kick2, { - var snd; - snd = DC.ar(0); - snd = snd + (HPF.ar(Hasher.ar(Sweep.ar), 1320) * Env.perc(0.003, 0.03).ar * 0.5); - snd = snd + (SinOsc.ar(XLine.ar(750, 161, 0.02)) * Env.perc(0.0005, 0.02).ar); - snd = snd + (SinOsc.ar(XLine.ar(167, 52, 0.04)) * Env.perc(0.0005, 0.3).ar(2)); - snd = snd.tanh; - Out.ar(\out.kr(0), Pan2.ar(snd, \pan.kr(0), \amp.kr(0.9))); - }).add; - d.kick2 = z; -); - -( - z = SynthDef(\kick3, { - var snd; - snd = DC.ar(0); - snd = snd + (SinOsc.ar(XLine.ar(1500, 800, 0.01)) * Env.perc(0.0005, 0.01, curve: \lin).ar); - snd = snd + (BPF.ar(Impulse.ar(0) * SampleRate.ir / 48000, 6100, 1.0) * 3.dbamp); - snd = snd + (BPF.ar(Hasher.ar(Sweep.ar), 300, 0.9) * Env.perc(0.001, 0.02).ar); - snd = snd + (SinOsc.ar(XLine.ar(472, 60, 0.045)) * Env.perc(0.0001, 0.3, curve: \lin).delay(0.005).ar(2)); - snd = snd.tanh; - Out.ar(\out.kr(0), Pan2.ar(snd, \pan.kr(0), \amp.kr(0.1))); - }).add; - d.kick3 = z; -); - - -( - z = SynthDef("snare", {arg amp = 0.1, freq = 100, att = 0.01, rel = 0.2, ffreq = 2000, pan = 0; - var env, snd1, snd2, sum; - env = Env.perc(att, rel, amp).kr; - snd1 = HPF.ar( - in: WhiteNoise.ar, - freq: ffreq - ); - snd2 = SinOsc.ar(freq: freq); - sum = snd1 + snd2; - sum = sum * env; - Out.ar(0, Pan2.ar(sum, pan)); - DetectSilence.ar(sum, doneAction: 2); - }).add; - d.snare = z; -); - - -( - z = SynthDef("hihat", {arg amp = 0.5, att = 0.01, rel = 0.2, ffreq = 6000, pan = 0; - var env, snd; - env = Env.perc( - attackTime: att, - releaseTime: rel, - level: amp*1.2 - ).kr; - snd = WhiteNoise.ar; - snd = HPF.ar(in: snd, freq: ffreq); - snd = snd * env; - Out.ar(0, Pan2.ar(snd, pan)); - DetectSilence.ar(snd, doneAction: 2); - }).add; - d.hihat = z; -); - -( - z = SynthDef(\kick1, { - var snd; - snd = DC.ar(0); - snd = snd + (SinOsc.ar(XLine.ar(800, 400, 0.01)) * Env.perc(0.0005, 0.01).ar); - snd = snd + (BPF.ar(Hasher.ar(Sweep.ar), XLine.ar(800, 100, 0.01), 0.6) * Env.perc(0.001, 0.02).delay(0.001).ar); - snd = snd + (SinOsc.ar(XLine.ar(172, 50, 0.01)) * Env.perc(0.0001, 0.3, 1, \lin).delay(0.005).ar(2)); - snd = snd.tanh; - Out.ar(\out.kr(0), Pan2.ar(snd, \pan.kr(0), \amp.kr(0.1))); - }).add; - d.kick1 = z; -); - -( - z = SynthDef(\braids, {|out=0,freq=440,amp=0.5,sustain=1,pan=0,begin=0,end=1,speed=1,accelerate=0,timbre=0.5,color=0.5,model=0| - var envLength = sustain*(end-begin)/speed; - var line = Line.ar(begin, end, envLength, doneAction: Done.freeSelf); - var env = Env.asr; - var volume = IEnvGen.ar(env, line) * amp; - var sig; - - freq = max(0, freq * speed * (1 + (accelerate * line))); - sig = MiBraids.ar(pitch: freq.cpsmidi, timbre: timbre, color: color, model: model); - Out.ar(out, Pan2.ar(sig * volume, pan)); - }).add; - d.braids = z; -); - -( - z = SynthDef(\omi, {|out=0,freq=440,amp=0.5,sustain=1,pan=0,begin=0,end=1,speed=1,accelerate=0| - var envLength = sustain*(end-begin)/speed; - var line = Line.ar(begin, end, envLength, doneAction: Done.freeSelf); - var env = Env.asr; - var volume = IEnvGen.ar(env, line) * amp; - var sig; - - freq = max(0, freq * speed * (1 + (accelerate * line))); - sig = MiOmi.ar(pit: freq.cpsmidi); - Out.ar(out, Pan2.ar(sig * volume, pan)); - }).add; - d.omi = z; -); - - -( - [ - 'analog', 'waveshape', - 'fm', 'grain', - 'additive', 'wavetable', - 'chord', 'speech', - 'swarm', 'noise', - 'particle', 'string', - 'modal', 'bass', - 'snare', 'hat' - ].do({arg name, index; - var synth; - [name, index].postln; - synth = SynthDef(name, {|out=0,freq=440,sustain=1,pan=0,begin=0,end=1,speed=1,accelerate=0, - amp=0.5,timbre=0.5,harm=0.5,morph=0.5,level=1,lpgdecay=0,lpgcolour=0,mode=0| - var envLength = sustain*(end-begin)/speed; - var line = Line.ar(begin, end, envLength, doneAction: Done.freeSelf); - var env = Env.asr; - var volume = IEnvGen.ar(env, line) * amp; - var sig; - freq = max(0, freq * speed * (1 + (accelerate * line))); - sig = MiPlaits.ar( - pitch: freq.cpsmidi, - timbre: timbre, - harm: harm, - engine: index, - morph: morph, - level: level, - decay: lpgdecay, - lpg_colour: lpgcolour, - ); - sig = Select.ar(mode, sig); - Out.ar(out, Pan2.ar(sig * volume, pan)); - }).add; - d.put(name, synth); - }); -); - -( - ['csaw', 'morph', 'saw_square', 'sine_triangle', - 'buzz', 'square_sub', 'saw_sub', 'square_sync', - 'saw_sync', 'triple_saw', 'triple_square', - 'triple_triangle', 'triple_sine', 'triple_ring_mod', - 'saw_swarm', 'saw_comb', 'toy', 'filter_lp', - 'filter_pk', 'filter_bp', 'filter_hp', 'vosim', - 'vowel', 'vowel_fof', 'harmonics', 'bfm', 'feedback_fm', - 'chaotic_feedback_fm', 'plucked', 'bowed', 'blown', 'fluted', - 'struck_bell', 'struck_drum', 'bkick', 'cymbal', 'bsnare', - 'bwavetable', 'wave_map', 'wave_line', 'wave_paraphonic', - 'filtered_noise', 'twin_peaks_noise', 'clocked_noise', - 'granular_cloud', 'particle_noise', 'digital_modulation', - 'question_mark'].do({ - arg name, index; - var synth; - synth = SynthDef(name, {|out=0,freq=440,amp=0.5,sustain=1,pan=0,begin=0,end=1,speed=1,accelerate=0,timbre=0.5,color=0.5,ws=0,bits=0, resamp=0, decim=32| - var envLength = sustain*(end-begin)/speed; - var line = Line.ar(begin, end, envLength, doneAction: Done.freeSelf); - var env = Env.asr; - var volume = IEnvGen.ar(env, line) * amp; - var sig; - - freq = max(0, freq * speed * (1 + (accelerate * line))); - sig = MiBraids.ar(pitch: freq.cpsmidi, timbre: timbre, resamp: 0, decim: decim, color: color, model: index, ws: ws, bits:bits); - Out.ar(out, Pan2.ar(sig * volume, pan)); - }).add; - d.put(name, synth); - }); -); - -( - z = SynthDef(\plaits, {|out=0,freq=440,sustain=1,pan=0,begin=0,end=1,speed=1,accelerate=0, - amp=0.5,timbre=0.5,engine=0,harm=0.5,morph=0.5,level=1,lpgdecay=0,lpgcolour=0,mode=0| - var envLength = sustain*(end-begin)/speed; - var line = Line.ar(begin, end, envLength, doneAction: Done.freeSelf); - var env = Env.asr; - var volume = IEnvGen.ar(env, line) * amp; - var sig; - freq = max(0, freq * speed * (1 + (accelerate * line))); - sig = MiPlaits.ar( - pitch: freq.cpsmidi, - timbre: timbre, - harm: harm, - engine: engine, - morph: morph, - level: level, - decay: lpgdecay, - lpg_colour: lpgcolour, - ); - sig = Select.ar(mode, sig); - Out.ar(out, Pan2.ar(sig * volume, pan)); - }).add; - d.plaits = z; -); - -( - z = SynthDef(\tides, {|out=0,freq=440,amp=0.5,sustain=1,pan=0,begin=0,end=1,speed=1,accelerate=0,tidesshape=0.5,slope=0.5,tidessmooth=0.5,shift=0.5,mode=2| - var envLength = sustain*(end-begin)/speed; - var line = Line.ar(begin, end, envLength, doneAction: Done.freeSelf); - var env = Env.asr; - var volume = IEnvGen.ar(env, line) * amp; - var sig; - - freq = max(0, freq * speed * (1 + (accelerate * line))); - sig = MiTides.ar( - freq: freq, - shape: tidesshape, - slope: slope, - smooth: tidessmooth, - shift: shift, - output_mode: mode, - ramp_mode: 1, - rate: 1 - ); - Out.ar(out, Pan2.ar(sig * volume, pan)); - }).add; - d.tides = z; -); - -z = nil; // We don't need that variable anymore diff --git a/README.md b/README.md index 6cac3fd..4f4eeae 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ # BuboQuark: Simple, hassle-free live coding - -This repository is a collection of tools and methods that I use to make live coding easier on **SuperCollider**. My aim is to make that setup easy to install on my other computers. This repository is not bringing anything new or interesting. This is merely a default configuration for my **SuperCollider** install. It's just a bunch of scripts that I use to make my life easier and I am no expert. I rely heavily on **JITLib** and other Quarks that I find useful. +This repository is a collection of methods and hacks that I found to make live +coding easier on **SuperCollider**. A secondary goal is to make that setup easy +to install on other computers. This Quark is not bringing a lot of new features +and it twists the langauge in a way that is highly personal. Internally, it +relies a lot on **JITLib**, **Patterns** and **NodeProxies**. ## Installation @@ -9,32 +12,71 @@ To install the **BuboQuark**, simply run the following command in your favorite ```bash Quarks.install("https://github.com/Bubobubobubobubo/BuboQuark") ``` +To make use of the existing synth definitions, you will have to install the +[mi-Ugens](https://github.com/v7b1/mi-UGens), a collection of **SuperCollider** +UGens taken from Mutable Instruments module designs. Note that it is also +preferable to install [sc3-plugins](https://github.com/supercollider/sc3-plugins), the official **UGen** extension suite for **SuperCollider**. -## Usage +All the other dependencies will be installed automatically when installing the +**Quark**. -This quark brings syntax shortcuts and minor improvements (_highly personal matter_) to make **SuperCollider** easier to handle on stage. +## How to use BuboQuark? -### Boot method +The main goal of **BuboQuark** is to provide everything you need to live code right out of the box. It is a balance between staying close to the initial language features while offering convenient shortcuts when possible: -There is a `Boot()` class that acts as a configuration file for my setup. This configuration file is rather classic: +- **boot** the server with a suitable configuration for live coding +- **facililate** the use of patterns and blending with audio functions +- **simplify** the use of `NodeProxy` roles for FX and mixing +- **synchonize** the clock with other applications +- **share** the audio signals easily with other applications -- raises the conservative options of `Server.default` +**SuperCollider** already possesses everything you need to do so. It is just not pre-arranged by default. +It is a programming language after all, you have to do some work to get it right. + +### Booting the server + +There is a `Boot()` pseudo-class that acts as a configuration file. This configuration file is rather classic: + +- raises the conservative options of `Server.default` to allow more connexions, + more buffers, etc. - set the ProxySpace clock to use `LinkClock` for syncing with other applications -- pushes everything into a `ProxySpace` -- set paths for samples and synthdefs -- install a `StageLimiter` not to blow up my speakers +- make the default environment a **JITLib** `ProxySpace` +- Set custom default paths for sample and SynthDefs loading +- Install a `StageLimiter` not to blow up the speakers -The `LinkClock` is accessible through the `c` global variable. +The `Boot()` constructor takes three arguments: -**Note:** I put my configuration into `./config/livecoding/` and there should be a folder named `samples/` and a file called `Synthdefs.scd`. +- `configPath`: path to a `.scd` configuration file that will be automatically +loaded +- `samplePath`: path to a folder containing your audio samples (in sub-folders) +- `soundDevice`: name of the sound device to use + +All of these arguments are optional. However, they will default to my +configuration if not set. If you want to set one option but not the others, use +keywords arguments or `nil` values: `Boot(soundDevice: "BlackHole 16ch")`. + +### Controlling the clock + +The `LinkClock` is accessible through the `c` global variable. Be careful not to +override it. It behaves like a regular `TempoClock` with the usual methods. +There are a few useful methods to control it and to use it efficiently: + +- `c.tempo` : set or get the current tempo (will change other peers tempo) +- `c.beatDur` : duration of a beat + +I use these methods very frequently when writing delay lines and time-based +effects. ### Events -I am using some Events as classes to store some of the things I want to load with each session (FX templates, SynthDef reference, etc). I am using : +I am using some Events as pseudo-classes to store some things I want to keep track on during the session (FX templates, SynthDef reference, etc). I am using : - `d`: **D**efinitions (`SynthDefs`) -- `f`: **F**X templates (DSP functions with a simple name) + - `d.list` : list all the available SynthDefs + - `d.params('synth_name')` : list the parameters of a SynthDef + +- `f`: **F**X templates (DSP functions accessible through a simple name) To use one of the effects, you can use the following syntax: @@ -42,43 +84,57 @@ To use one of the effects, you can use the following syntax: ~my_ndef.fx(100, 0.5, f[\vardel]); ``` -### Simplified useful commands +These are not really hard-coded. They are in my `.scd` configuration files. You +can ignore this section entirely if you do things differently! + +### Simplified Server/Gui Control + +I like when SC panels stay on top of other applications by default: - `Panic()`: shortcut for `CmdPeriod.run`. -- `Boot(path)`: boot my config (**hardcoded** path or user specified path) - `Scope()`: a scope that always stays on top! - `FScope()`: a frequency scope that always stay on top! - `Gui()`: a server GUI window that always stay on top! -### Patterns +### Pattern tweaks Patterns are powerful but writing them is long and can lead to a lot of typing errors. Moreover, they are often centered around list manipulation. **BuboQuark** defines a few helpers to transform a regular `Array` into various patterns: ```supercollider [1, 2, 3, 4].pseq ``` -Consider the source as a documentation. -### Patterns +Consider the source as a documentation. You will find all the additional methods +in `BuboString` or `BuboArray`. I am not entirely convinced by shortening the +most complex Pattern types because they are complex after all. Consider blending +the regular syntax with shortcuts when necessary. -I don't like using keys because of the backslash (`\`), a symbol that is really hard to type on AZERTY keyboards. For that reason, I much prefer the `[instrument: 'plaits', dur: 2]` syntax. I added a `.pat` method to convert an array into a `Pbind`. There are optional arguments to specify the `fadeTime` and `quant` for that pattern. Demo: +### Pbind + +I don't like using keys because of the backslash (`\`), a symbol that is really hard to type on **AZERTY** keyboards. For that reason, I much prefer the `["my_pattern", instrument: 'plaits', dur: 2]` syntax. I added a `.pat` method to convert an array into a `Pbind`. There are optional arguments to specify the `fadeTime` and `quant` for that pattern. Demo: ```supercollider ( [ + "name_of_pattern", instrument: 'sinfb', rel: Pbrown(0.1, 0.5, 0.125, inf), note: Place([Pxrand([0, 3, 7, 10], 12), 0, 3, 5, 0, 12, 0, 7, 5, [5, 10, 7].pwhite(1)], inf), octave: [Pxrand([5, 6, 4], 4)].pxrand(inf), dur: Pbjorklund2(6, 8, inf) / 2, legato: 0.1 - ].pat(~test).play; + ].pat.play; ) ``` +`.pat` take a few optional arguments: + +- `quant` (defaults to `4`): pattern quantization (**LinkClock**) +- `fade` (defaults to `0.05`): fading time (**NodeProxy**) + +There is also the `.p` function that will just turn the array into a `Pbind` without any additional behavior. This is useful when dealing with classic `NodeProxy Roles` like `\set` and `\xset`. ### NodeProxy -The `NodeProxy` roles are somewhat verbose. I have tried to make the syntax easier on the eye by creating the `fx`, `wet` and `infx` methods. Here is a demo of how I use it: - +The `NodeProxy` roles are sometimes a bit verbose to my taste. I have tried to make the syntax easier on the eye by creating the `fx`, `wet` and `infx` methods. Here is a demo of how I use it: ```supercollider ( @@ -90,3 +146,8 @@ The `NodeProxy` roles are somewhat verbose. I have tried to make the syntax easi ~test.wet(10, 0.5) // bring the reverb up with the wet method ``` + +There is also `.fxin` and `.wet` functions, shortcuts for the `\filter` and +`\filterIn` NodeProxy mechanisms. I have also added some rather shady functions +that automatically pick up a slot for a specific fx: `fx1`, `fx2`, up to `fx9`. +