diff --git a/BuboQuark.quark b/BuboQuark.quark index bde7e18..ceafb83 100644 --- a/BuboQuark.quark +++ b/BuboQuark.quark @@ -9,12 +9,14 @@ since: "2023", schelp: "BuboQuark", dependencies: [ + "https://github.com/cappelnord/BenoitLib", "https://github.com/scztt/Singleton.quark", "https://github.com/scztt/Require.quark", "https://github.com/dmorgan-github/Pdv", "https://github.com/supercollider-quarks/Bjorklund", "https://github.com/adcxyz/SafetyNet", "https://github.com/madskjeldgaard/nodeproxygui2", + "https://github.com/capital-G/sc-grids", ], url: "https://raphaelforment.fr", isCompatible: {Main.versionAtLeast(3, 1)}, diff --git a/BuboShortcuts.sc b/BuboShortcuts.sc deleted file mode 100644 index 569519e..0000000 --- a/BuboShortcuts.sc +++ /dev/null @@ -1 +0,0 @@ -Eu : Pbjorklund2 {} diff --git a/Classes/BuboArray.sc b/Classes/BuboArray.sc index 3663b28..fd7770b 100644 --- a/Classes/BuboArray.sc +++ b/Classes/BuboArray.sc @@ -10,98 +10,70 @@ ^Pindex(pat, this[1], repeats) } - findShortcuts { - arg pattern; - var short, correctedPattern; - correctedPattern = List.new(); - short = Dictionary.newFrom([ - // Instrument - \i, \instrument, - // Notes - \n, \note, - \mn, \midinote, - \vel, \velocity, - \deg, \degree, - \off, \timingOffset, - \o, \octave, - \f, \freq, - \det, \detune, - // Durations - \d, \dur, - \l, \legato, - // Amplitude - \p, \pan, - // Envelope - \a, \attack, - \d, \decay, - \s, \sustain, - \r, \release, - // Filter control - \r, \resonance, - \ff, \ffreq, - // Modulation - \m, \mod, - \mo, \midout, - \c, \midichan, - \st, \stretch, - \rt, \root, - \scl, \scale, - ]); - - // shortcuts are turned into regular keys; - pattern.do({| element | - if (short.includesKey(element), - {correctedPattern.add(short[element])}, - {correctedPattern.add(element)} - ); - }); - ^correctedPattern; - } - pat { arg quant=4, fade=0.05; var proxyName = this[0]; var newArray = this[1..] ++ [\type, \buboEvent]; currentEnvironment.at(proxyName.asSymbol).quant_(quant); currentEnvironment.at(proxyName.asSymbol).fadeTime = fade; - currentEnvironment.at(proxyName.asSymbol)[0] = Pbind(*(this.findShortcuts(newArray))); + currentEnvironment.at(proxyName.asSymbol)[0] = Pbind(*(EventShortener.findShortcuts(newArray))); ^currentEnvironment.at(proxyName.asSymbol); } + pwrap { + arg maxIndex, startIndex=0, repeats=inf; + ^PwrapSeq(this, maxIndex, startIndex, repeats) + } + + p { - ^Pbind(*(this.findShortcuts(this))) + ^Pbind(*(Eventshortener.findShortcuts(this))) } - euclid { + eu { arg repeats=inf; - ^Pbjorklund2(this[0], this[1], repeats); + var divisor = 1; + if (this[3] != nil, { divisor = this[3] }); + ^Pbjorklund2(this[0], this[1], repeats, this[2]) / divisor; } - pseq { arg repeats=inf, offset=0; - ^Pseq(this, repeats, offset); + pseq { + arg repeats=inf; + ^Pseq(this, repeats); } - pshuf { arg repeats=1; + pshuf { + arg repeats=1; ^Pshuf(this, repeats); } - prand { arg repeats=inf; + prand { + arg repeats=inf; ^Prand(this, repeats); } - pxrand { arg repeats=inf; + pxrand { + arg repeats=inf; ^Pxrand(this, repeats); } - pwrand { arg weights, repeats=1; + pwrand { + arg weights, repeats=1; ^Pwrand(this, weights.normalizeSum, repeats); } - pwhite { arg repeats=inf; + pwhite { + arg repeats=inf; ^Pwhite(this[0], this[1], repeats); } - pseries { arg repeats=inf; + pseries { + arg repeats=inf; ^Pseries(this[0], this[1], repeats); } + + pbrown { + arg repeats=inf; + ^Pbrown(this[0], this[1], this[2], repeats); + } } diff --git a/Classes/BuboBoot.sc b/Classes/BuboBoot.sc index 4aedb94..9e05458 100644 --- a/Classes/BuboBoot.sc +++ b/Classes/BuboBoot.sc @@ -8,13 +8,7 @@ Boot { *new { arg configPath, samplePath, serverOptions; var p; var c; var t; var s; - var banner = "┳┓ ┓ ┳┓\n" - "┣┫┓┏┣┓┏┓ ┣┫┏┓┏┓╋\n" - "┻┛┗┻┗┛┗┛ ┻┛┗┛┗┛┗"; - var ready = "┓ ┳┓┏┏┓ ┏┓┏┓┳┓┏┓ ┳┓┏┓┏┓┳┓┓┏\n" - "┃ ┃┃┃┣ ┃ ┃┃┃┃┣ ┣┫┣ ┣┫┃┃┗┫\n" - "┗┛┻┗┛┗┛ ┗┛┗┛┻┛┗┛ ┛┗┗┛┛┗┻┛┗┛"; - this.fancyPrint(banner, 40); + BuboUtils.fancyPrint(BuboUtils.banner, 40); if (serverOptions == nil, { @@ -55,29 +49,18 @@ Boot { Server.default.waitForBoot({ "-> Loading config from: %".format(configPath ? (this.localPath +/+ "Startup.scd")).postln; (configPath ? (this.localPath +/+ "Startup.scd")).load; - Safety.setLimit(0.8); - this.fancyPrint(ready, 40); + Safety.all; + Safety(s).defName = \safeLimit; + Safety.setLimit(1); + BuboUtils.fancyPrint(BuboUtils.ready, 40); this.installServerTreeBehavior(); this.clock.enableMeterSync(); }); } - *fancyPrint { - arg message, length; - var separator= length.collect({ - arg index; - if (index % 2 == 0, "=", "-") - }); - separator = separator.join(""); - [separator, message, separator].do({ - arg each; - each.postln; - }); - } - *installServerTreeBehavior { CmdPeriod.add({ - this.fancyPrint("\nBubo SuperCollider Session\nTempo: % | Peers: %\nCPU: % | Peak: %\n".format( + BuboUtils.fancyPrint("\nBubo SuperCollider Session\nTempo: % | Peers: %\nCPU: % | Peak: %\n".format( this.clock.tempo * 60, this.clock.numPeers, Server.default.avgCPU.round(2), Server.default.peakCPU.round(2)), 40) @@ -88,6 +71,7 @@ Boot { { ~buf = Bank(~sp)[~nb % Bank(~sp).buffers.size]; } ); if (~nb == nil) {~nb = 0}; + if (~sp == nil) {~sp = 'default'}; ~type = \note; currentEnvironment.play; }); diff --git a/Classes/BuboEnv.sc b/Classes/BuboEnv.sc new file mode 100644 index 0000000..e891aa8 --- /dev/null +++ b/Classes/BuboEnv.sc @@ -0,0 +1,16 @@ ++ Env { + + *rand { + arg numSegs=8, dur=1, bipolar=true; + var env, levels, times, curves, minLevel; + levels = {rrand(-1.0, 1.0)}!numSegs+1; + minLevel = bipolar.asInteger.neg; + levels = levels.normalize(minLevel, 1); + times = {exprand(1,10)}!numSegs; + times = times.normalizeSum * dur; + curves = {rrand(-4.0,4.0)}!numSegs; + env = this.new(levels, times, curves); + ^env; + } + +} diff --git a/Classes/BuboNodeProxy.sc b/Classes/BuboNodeProxy.sc index a9c5f40..880bdfc 100644 --- a/Classes/BuboNodeProxy.sc +++ b/Classes/BuboNodeProxy.sc @@ -1,5 +1,7 @@ + NodeProxy { + /* Simple FX chain management */ + fx { arg number=1, wet=1, function = {|in| in}; this[number] = \filter -> function; @@ -34,4 +36,54 @@ this.set(("wet" ++ number).asSymbol, wet); ^this; } + + /* Player-like syntax sugar */ + => { + arg pattern; + pattern = EventShortener.findShortcuts(pattern); + pattern = pattern ++ [\type, \buboEvent]; + this[0] = Pbind(*pattern); + this.quant = 4; this.fadeTime = 0.01; + ^this + } + + /* FIX: Completely broken. What is the event type + * BuboEvent should fall back to after tweaking + * the pattern to my liking? + */ + -> { + arg pattern; + pattern = EventShortener.findShortcuts(pattern); + // pattern = pattern ++ [\type, \buboMonoEvent]; + this[0] = Pmono(*pattern); + this.quant = 4; + this.fadeTime = 0.01; + } + + f { + arg value; + this.fadeTime = value; + ^this + } + + p { + arg quant, fade; + this.quant = quant; + this.fadeTime = fade; + this.play(fadeTime: fade); + ^this + } + + s { + arg duration; + this.stop(fadeTime: duration) + ^this + } + + / { + arg pattern; + this.stop(1); + ^this + } + } diff --git a/Classes/BuboUtils.sc b/Classes/BuboUtils.sc new file mode 100644 index 0000000..b36be13 --- /dev/null +++ b/Classes/BuboUtils.sc @@ -0,0 +1,30 @@ +BuboUtils { + + *banner { + var banner = "┳┓ ┓ ┳┓\n" + "┣┫┓┏┣┓┏┓ ┣┫┏┓┏┓╋\n" + "┻┛┗┻┗┛┗┛ ┻┛┗┛┗┛┗"; + ^banner + } + + *ready { + var ready = "┓ ┳┓┏┏┓ ┏┓┏┓┳┓┏┓ ┳┓┏┓┏┓┳┓┓┏\n" + "┃ ┃┃┃┣ ┃ ┃┃┃┃┣ ┣┫┣ ┣┫┃┃┗┫\n" + "┗┛┻┗┛┗┛ ┗┛┗┛┻┛┗┛ ┛┗┗┛┛┗┻┛┗┛"; + ^ready + } + + *fancyPrint { + arg message, length; + var separator= length.collect({ + arg index; + if (index % 2 == 0, "=", "-") + }); + separator = separator.join(""); + [separator, message, separator].do({ + arg each; + each.postln; + }); + } +} + diff --git a/Classes/Configuration/Startup.scd b/Classes/Configuration/Startup.scd index 3f26d79..3e4fafb 100644 --- a/Classes/Configuration/Startup.scd +++ b/Classes/Configuration/Startup.scd @@ -3,4 +3,4 @@ p = currentEnvironment; c = currentEnvironment.clock; "Loading SynthDefs".postln; "Synthdefs.scd".loadRelative; -// m = MIDIOut.newByName("MIDI", "Bus 1"); +m = MIDIControl(); diff --git a/Classes/Configuration/Synthdefs.scd b/Classes/Configuration/Synthdefs.scd index 0165990..a9ea798 100644 --- a/Classes/Configuration/Synthdefs.scd +++ b/Classes/Configuration/Synthdefs.scd @@ -16,124 +16,86 @@ f.vardel = { * This is the sampler used for 90% of sampling duties. */ ( - z = SynthDef.new(\s, { - arg buf, out=0, freq=440, rate=1, amp=1, begin=0, end=1, pan=0, attack=0.01, release=1, loop=0; - var sig; - var env = EnvGen.ar(Env.perc(attack, release, doneAction: 2)); - var startPos = begin * BufFrames.kr(buf); - var endPos = end * BufFrames.kr(buf); // TODO: unused + z = SynthDef.new(\player, { + arg buf; + var sig, env = EnvGen.ar( + Env.perc( + \attack.kr(0.01), + \release.kr(1), + doneAction: 2) + ); + var startPos = \begin.kr(0) * BufFrames.kr(buf); + var endPos = \end.kr(1) * BufFrames.kr(buf); // TODO: unused sig = PlayBuf.ar( numChannels: 1, bufnum: buf, - rate:BufRateScale.kr(buf) * (rate * freq / 60.midicps), + rate:BufRateScale.kr(buf) * (\rate.kr(1) * \freq.kr(400) / 60.midicps), trigger: 1, startPos:startPos, - loop:loop, doneAction: 2); - sig = sig * amp; - sig = Pan2.ar(sig * env, pan); - OffsetOut.ar(out, sig) + loop:\loop.kr(0), doneAction: 2); + sig = sig * \amp.kr(-6.dbamp); + sig = Pan2.ar(sig * env, \pan.kr(0)); + OffsetOut.ar(\out.kr(0), sig) }).add; - d.s = z; + d.player = z; +); + + +/* +* Stereo variant +*/ +( + z = SynthDef.new(\splayer, { + arg buf; + var sig, env = EnvGen.ar( + Env.perc( + \attack.kr(0.01), + \release.kr(1), + doneAction: 2) + ); + var startPos = \begin.kr(0) * BufFrames.kr(buf); + var endPos = \end.kr(1) * BufFrames.kr(buf); // TODO: unused + sig = PlayBuf.ar( + numChannels: 2, + bufnum: buf, + rate:BufRateScale.kr(buf) * (\rate.kr(1) * \freq.kr(400) / 60.midicps), + trigger: 1, startPos:startPos, + loop:\loop.kr(0), doneAction: 2); + sig = sig * \amp.kr(-6.dbamp); + sig = Pan2.ar(sig * env, \pan.kr(0)); + OffsetOut.ar(\out.kr(0), sig) + }).add; + d.splayer = 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)); + env = EnvGen.ar( + Env.linen( + \attack.kr(0.01), + \sustain.kr(0), + \release.kr(1) + ),1,1,0,1,2); + sig = SinOscFB.ar( + \freq.kr(400), + \fb.kr(0), + 1 + ); + sig = sig * env; + OffsetOut.ar(\out.kr(0), Pan2.ar(sig, \pan.kr(0), \amp.kr(-6.dbamp))); }).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(\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); + z = SynthDef(\omi, { + var envLength = \sustain.kr(1) * (\end.kr(1) - \begin.kr(0)) / \speed.kr(1); + var line = Line.ar(\begin.kr, \end.kr, envLength, doneAction: Done.freeSelf); var env = Env.asr; - var volume = IEnvGen.ar(env, line) * amp; + var volume = IEnvGen.ar(env, line) * \amp.kr(-6.dbamp); var sig; - - freq = max(0, freq * speed * (1 + (accelerate * line))); - sig = MiOmi.ar(pit: freq.cpsmidi); - Out.ar(out, Pan2.ar(sig * volume, pan)); + sig = MiOmi.ar(pit: \freq.kr(400).cpsmidi); + OffsetOut.ar(\out.kr(0), Pan2.ar(sig * volume, \pan.kr(0))); }).add; d.omi = z; ); @@ -141,52 +103,33 @@ f.vardel = { ( [ - 'analog', 'waveshape', - 'fm', 'grain', - 'additive', 'wavetable', - 'chord', 'speech', - 'swarm', 'noise', - 'particle', 'string', - 'modal', 'bass', - 'snare', 'hat' + 'Panalog', 'Pwaveshape', 'Pfm', 'Pgrain', + 'Padditive', 'Pwavetable', 'Pchord', 'Pspeech', + 'Pswarm', 'Pnoise', 'Pparticle', 'Pstring', + 'Pmodal', 'Pbass', 'Psnare', 'Phat' ].do({arg name, index; var synth; synth = SynthDef(name, { - arg out, - freq=440, - attack=0.01, - decay=0.3, - sustain=0.5, - release=1.0, - pan=0, - speed=1, - accelerate=0, - amp=1, - alt=1, - timbre=0.5, - harm=0.5, - morph=0.5, - level=1, - gate=1, - lpgdecay=0, - lpgcolour=0, - mode=0; - var env = EnvGen.ar(Env.adsr(attack, decay, sustain, release, curve: [2, -4, -4]), doneAction: Done.freeSelf, gate: gate); - var sig; - freq = max(0, freq * speed * (1 + (accelerate * env))); - sig = MiPlaits.ar( - pitch: freq.cpsmidi, - timbre: timbre, - harm: harm, - engine: index, - morph: morph, - level: level, - decay: lpgdecay, - lpg_colour: lpgcolour, + var env = EnvGen.ar( + Env.perc( + \attack.kr(0.01), + \release.kr(1) + ), doneAction: Done.freeSelf ); - sig = Pan2.ar(sig[0], pan); - sig = sig * env * amp; - OffsetOut.ar(out, sig); + var sig; + sig = MiPlaits.ar( + pitch: \freq.kr(400).cpsmidi, + timbre: \timbre.kr(0), + harm: \harm.kr(0), + engine: index, + morph: \morph.kr(0), + level: \level.kr(1), + decay: \lpgdecay.kr(0), + lpg_colour: \lpgcolour.kr(0), + ); + sig = Pan2.ar(sig[0], \pan.kr(0)); + sig = sig * env * \amp.kr(-6.dbamp); + OffsetOut.ar(\out.kr(0), sig); }).add; d.put(name, synth); }); @@ -194,68 +137,91 @@ f.vardel = { ( - ['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({ + [ + 'Bcsaw', + 'Bmorph', + 'Bsaw_square', + 'Bsine_triangle', + 'Bbuzz', + 'Bsquare_sub', + 'Bsaw_sub', + 'Bsquare_sync', + 'Bsaw_sync', + 'Bt_saw', + 'Bt_square', + 'Bt_triangle', + 'Bt_sine', + 'Bt_ring_mod', + 'Bsaw_swarm', + 'Bsaw_comb', + 'Btoy', + 'Bfilter_lp', + 'Bfilter_pk', + 'Bfilter_bp', + 'Bfilter_hp', + 'Bvosim', + 'Bvowel', + 'Bvowel_fof', + 'Bharmonics', + 'Bbfm', + 'Bfeedback_fm', + 'Bchaotic_feedback_fm', + 'Bplucked', + 'Bbowed', + 'Bblown', + 'Bfluted', + 'Bstruck_bell', + 'Bstruck_drum', + 'Bbkick', + 'Bcymbal', + 'Bbsnare', + 'Bbwavetable', + 'Bwave_map', + 'Bwave_line', + 'Bwave_paraphonic', + 'Bfiltered_noise', + 'Btwin_peaks_noise', + 'Bclocked_noise', + 'Bgranular_cloud', + 'Bparticle_noise', + 'Bdigital_modulation', + 'Bquestion_mark'].do({ arg name, index; var synth; synth = SynthDef(name, { - arg out, - freq=440, - amp=1, - attack=0.01, - decay=0.3, - sustain=0.5, - release=1.0, - pan=0,begin=0, - end=1,speed=1, - accelerate=0, - timbre=0.5, - color=0.5, - ws=0,bits=0, - resamp=0, - decim=32, - gate=1; var sig; - var env = EnvGen.ar(Env.adsr(attack, decay, sustain, release, curve: [2, -4, -4]), doneAction: Done.freeSelf, gate: gate); - freq = max(0, freq * speed * (1 + (accelerate * env))); - 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 * env * amp, pan)); + var env = EnvGen.ar(Env.perc(\attack.kr(0.01), \release.kr(1)), doneAction: Done.freeSelf); + var freq = \freq.kr; + sig = MiBraids.ar( + pitch: freq.cpsmidi, + timbre: \timbre.kr(0), + color: \color.kr(0), + resamp: 0, + decim: \decim.kr(32), + model: index, + ws: \ws.kr(0), + bits: \bits.kr(0) + ); + OffsetOut.ar(\out.kr(0), Pan2.ar(sig * env * \amp.kr(-6.dbamp), \pan.kr(0))); }).add; d.put(name, synth); }); ); ( - 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, + z = SynthDef(\tides, { + var env = Env.perc(\attack.kr(0.01), \release.kr(1)).ar(2); + var sig = MiTides.ar( + freq: \freq.kr(400), + shape: \shape.kr(0), + slope: \slope.kr(0), + smooth: \smooth.kr(0), + shift: \shift.kr(0), + output_mode: \mode.kr(2), ramp_mode: 1, rate: 1 ); - Out.ar(out, Pan2.ar(sig * volume, pan)); + OffsetOut.ar(\out.kr(0), Pan2.ar(sig * env * \amp.kr(-6.dbamp), \pan.kr(0))); }).add; d.tides = z; ); diff --git a/Classes/Controllers/MIDIMix.sc b/Classes/Controllers/MIDIMix.sc new file mode 100644 index 0000000..bbc5719 --- /dev/null +++ b/Classes/Controllers/MIDIMix.sc @@ -0,0 +1,135 @@ +ControllerValue { + + /* + * A ControllerValue represents a MIDI Controller value. + * It has a minimum and maximum value, and a curve. This + * is used to convert from the MIDI value to a value that + * is considered usable by the user. + * + * The curve is similar to the one used by the Env object. + */ + + var <>min = 0; + var <>max = 1; + var <>curve = 0; + var <>currentValue; + var <>bipolar = false; + + *new { + arg min, max, curve; + ^super.new.init() + } + + init { + this.min = min; + this.max = max; + this.curve = curve; + this.currentValue = Bus.control; + this.bipolar = false; + } + + set { + arg value; + // If bipolar is true, then the value must go from -1 to 1 + var conversion = value.lincurve( + inMin: 0, + inMax: 127, + outMin: this.min.neg, + outMax: this.max, + curve: this.curve + ); + this.currentValue.set(conversion); + ^this.currentValue; + } + +} + + +MIDIControl { + + /* + * This is my personal MIDI controller interface. I am using a + * MIDIMix. It has 8 faders, 24 knobs, and 16 buttons. I am only + * using the knobs and faders. Two buttons are used to change "bank" + * (increments the CC number value). + */ + + var <>currentBank = 0; + var <>values; + + *new { + ^super.new.init() + } + + init { + this.values = IdentityDictionary.new(); + this.connect(); this.installCallbacks(); + } + + getInit { + arg number; + if (this.values[number] == nil) { + this.values[number] = ControllerValue.new( + min: 0, max: 127, curve: 0 + ); + ^this.values[number] + } { + ^this.values[number] + } + } + + setCurve { + arg number, curve; + this.getInit(number).curve = curve; + } + + setBounds { + arg number, min, max; + var controller = this.getInit(number); + controller.min = min; + controller.max = max; + } + + at { + arg number; + var control = this.getInit(number); + var choices = ( + value: this.getInit(number).currentValue.getSynchronous, + bus: this.getInit(number).currentValue, + map: this.getInit(number).currentValue.asMap, + kr: In.kr(this.getInit(number).currentValue), + ); + ^choices + } + + connect { + MIDIClient.init; + MIDIIn.connectAll(verbose: true); + } + + installCallbacks { + MIDIIn.addFuncTo(\control, { + arg src, chan, num, val; + ("CONTROL:" + (num + (this.currentBank * 24)) + "=>" + val).postln; + this.getInit(num + (this.currentBank * 24)).set(val); + }); + MIDIIn.addFuncTo(\noteOn, { + arg src, chan, num, val; + "Changing bank".postln; + if (chan == 8 && num == 22) { + if (this.currentBank > 0) { + this.currentBank = this.currentBank - 1; + }; + this.currentBank.postln; + }; + if (chan == 8 && num == 24) { + if (this.currentBank < 3) { + this.currentBank = this.currentBank + 1; + }; + this.currentBank.postln; + }; + }); + } +} + + diff --git a/Classes/EventShortener.sc b/Classes/EventShortener.sc new file mode 100644 index 0000000..ef564c7 --- /dev/null +++ b/Classes/EventShortener.sc @@ -0,0 +1,50 @@ +EventShortener { + + *findShortcuts { + arg pattern; + var short, correctedPattern; + correctedPattern = List.new(); + short = Dictionary.newFrom([ + // Instrument + \i, \instrument, + // Notes + \n, \note, + \mn, \midinote, + \vel, \velocity, + \deg, \degree, + \off, \timingOffset, + \o, \octave, + \f, \freq, + \det, \detune, + // Durations + \d, \dur, + \l, \legato, + // Amplitude + \p, \pan, + // Envelope + \a, \attack, + \d, \decay, + \s, \sustain, + \r, \release, + // Filter control + \r, \resonance, + \ff, \ffreq, + // Modulation + \m, \mod, + \mo, \midout, + \c, \midichan, + \st, \stretch, + \rt, \root, + \scl, \scale, + ]); + + // shortcuts are turned into regular keys; + pattern.do({| element | + if (short.includesKey(element), + {correctedPattern.add(short[element])}, + {correctedPattern.add(element)} + ); + }); + ^correctedPattern; + } +} diff --git a/Classes/GUI/BuboFreqScope.sc b/Classes/GUI/BuboFreqScope.sc deleted file mode 100644 index fbbdb35..0000000 --- a/Classes/GUI/BuboFreqScope.sc +++ /dev/null @@ -1,7 +0,0 @@ -FScope { - - *new { - ^Server.default.freqscope.window.alwaysOnTop_(true); - } - -} diff --git a/Classes/GUI/BuboGui.sc b/Classes/GUI/BuboGui.sc index a1e3fe7..1c5af5e 100644 --- a/Classes/GUI/BuboGui.sc +++ b/Classes/GUI/BuboGui.sc @@ -1,3 +1,43 @@ +Scope { + + *new { + ^Server.default.scope.window.alwaysOnTop_(true); + } + +} + + +Meter { + + *new { + var window = Window.new( + "Meter", + Rect.new(left: 0, top: 0, width: 680, height: 250), + resizable: false, + border: true, + scroll: false + ); + var meters = ServerMeterView.new( + Server.default, window, + 0@0, 16, 16 + ); + window.front; + window.alwaysOnTop = true; + ^window + } + +} + + +FScope { + + *new { + ^Server.default.freqscope.window.alwaysOnTop_(true); + } + +} + + Gui { *new { diff --git a/Classes/GUI/BuboScope.sc b/Classes/GUI/BuboScope.sc deleted file mode 100644 index 3078b6e..0000000 --- a/Classes/GUI/BuboScope.sc +++ /dev/null @@ -1,28 +0,0 @@ -Scope { - - *new { - ^Server.default.scope.window.alwaysOnTop_(true); - } - -} - -Meter { - - *new { - var window = Window.new( - "Meter", - Rect.new(left: 0, top: 0, width: 680, height: 250), - resizable: false, - border: true, - scroll: false - ); - var meters = ServerMeterView.new( - Server.default, window, - 0@0, 16, 16 - ); - window.front; - window.alwaysOnTop = true; - ^window - } - -} diff --git a/Classes/Patterns/PXo.sc b/Classes/Patterns/PXo.sc new file mode 100644 index 0000000..dfd5d69 --- /dev/null +++ b/Classes/Patterns/PXo.sc @@ -0,0 +1,30 @@ +Pxo { + *new { + arg expression, divisor=1, repeats=inf; + var xoExp = Array.new(maxSize: expression.size); + expression.do({ + arg char, i; + if (char == $x, {xoExp.add(1, i)}, {xoExp.add(Rest(), i)}) + }); + xoExp = xoExp.collect({arg item; item / divisor}); + ^Pseq.new(xoExp, repeats) + } +} + + +Ptx { + *new { + arg expression, repeats=inf; + var xoExp = Array.new(maxSize: expression.size); + expression.do({ + arg char, i; + switch(char) + {$@} {xoExp.add(2, i)} + {$O} {xoExp.add(1, i)} + {$o} {xoExp.add(0.5, i)} + {$°} {xoExp.add(0.25, i)} + {} {xoExp.add(Rest(), i)}; + }); + ^Pseq.new(xoExp, repeats) + } +} diff --git a/Classes/Patterns/PwrapSeq.sc b/Classes/Patterns/PwrapSeq.sc new file mode 100644 index 0000000..6fe81c4 --- /dev/null +++ b/Classes/Patterns/PwrapSeq.sc @@ -0,0 +1,12 @@ +/* This one is taken from Mads Kjeldgaard's */ +/* https://github.com/madskjeldgaard/Monolithic/blob/main/Classes/Pattern/PwrapSeq.sc */ +PwrapSeq{ + *new{ + arg array, maxIndex, startIndex=0, repeats=inf; + + var indexPat = Pseries.new(startIndex, 1, length: inf) % (maxIndex+1); + + ^Pindex.new(listPat: array, indexPat: indexPat, repeats: repeats) + } + +}