diff --git a/apps/test/allugens.srp b/apps/test/allugens.srp index b6d80b5..4ce79d6 100644 --- a/apps/test/allugens.srp +++ b/apps/test/allugens.srp @@ -1,6 +1,5 @@ - -# ---- included from /Users/rbd/arco/serpent/srp/vu.srp ---- - + + # vu.srp -- record and play audio in memory # # Roger B. Dannenberg @@ -25,10 +24,9 @@ class Vu (Ugen): display "Vu set", id, value.id o2_send_cmd("/arco/vu/repl_input", 0, "UU", id, value.id) this - - -# ---- included from /Users/rbd/arco/serpent/srp/probe.srp ---- - + + + # probe.srp -- stream audio from file # # Roger B. Dannenberg @@ -67,10 +65,9 @@ class Probe (Ugen): running = false this - - -# ---- included from /Users/rbd/arco/serpent/srp/pwl.srp ---- - + + + # pwl.srp -- piece-wise linear envelope # # Roger B. Dannenberg @@ -92,10 +89,9 @@ class Pwlb (Envelope): def pwlb(keyword init, start = true, rest points): envelope(Pwlb(points), init, start) - - -# ---- included from /Users/rbd/arco/serpent/srp/pwe.srp ---- - + + + # pwe.srp -- piece-wise exponential envelope # # Roger B. Dannenberg @@ -117,113 +113,9 @@ class Pweb (Envelope): def pweb(keyword init, start = true, lin = false, rest points): envelope(Pweb(points), init, start, lin) - - -# ---- included from /Users/rbd/arco/serpent/srp/unaryugen.srp ---- - -# unaryugen.srp - constructor implementation for abs, neg, exp, log, -# log10, log2, sqrt, step_to_hz, hz_to_step, vel_to_lin, lin_to_vel. -# For completeness, you can create a Unary as well with ugen_unary. - -UNARY_OP_ABS = 0 -UNARY_OP_NEG = 1 -UNARY_OP_EXP = 2 -UNARY_OP_LOG = 3 -UNARY_OP_LOG10 = 4 -UNARY_OP_LOG2 = 5 -UNARY_OP_SQRT = 6 -UNARY_OP_STEP_TO_HZ = 7 -UNARY_OP_HZ_TO_STEP = 8 -UNARY_OP_VEL_TO_LINEAR = 9 -UNARY_OP_LINEAR_TO_VEL = 10 -UNARY_OP_DB_TO_LINEAR = 11 -UNARY_OP_LINEAR_TO_DB = 12 - -def unary(op, x1, optional chans): - if not chans: - chans = max_chans(1, x1) - Ugen(create_ugen_id(), "unary", chans, 'a', "iU", - 'op', op, 'x1', x1) - - -def unaryb(op, x1, optional chans): - if not isnumber(x1) and x1.rate != 'b': - display "ERROR: 'x1' input to Ugen 'mathb' must be block rate", op - return nil - if not chans: - chans = max_chans(1, x1) - Ugen(create_ugen_id(), "unaryb", chans, 'b', "iU", - 'op', op, 'x1', x1) - - -def ugen_abs(x1, optional chans): - unary(UNARY_OP_ABS, x1, chans) -def ugen_absb(x1, optional chans): - unaryb(UNARY_OP_ABS, x1, chans) - -def ugen_neg(x1, optional chans): - unary(UNARY_OP_NEG, x1, chans) -def ugen_negb(x1, optional chans): - unaryb(UNARY_OP_NEG, x1, chans) - -def ugen_exp(x1, optional chans): - unary(UNARY_OP_EXP, x1, chans) -def ugen_expb(x1, optional chans): - unaryb(UNARY_OP_EXP, x1, chans) - -def ugen_log(x1, optional chans): - unary(UNARY_OP_LOG, x1, chans) -def ugen_logb(x1, optional chans): - unaryb(UNARY_OP_LOG, x1, chans) - -def ugen_log10(x1, optional chans): - unary(UNARY_OP_LOG10, x1, chans) -def ugen_log10b(x1, optional chans): - unaryb(UNARY_OP_LOG10, x1, chans) - -def ugen_log2(x1, optional chans): - unary(UNARY_OP_LOG2, x1, chans) -def ugen_log2b(x1, optional chans): - unaryb(UNARY_OP_LOG2, x1, chans) - -def ugen_sqrt(x1, optional chans): - unary(UNARY_OP_SQRT, x1, chans) -def ugen_sqrtb(x1, optional chans): - unaryb(UNARY_OP_SQRT, x1, chans) - -def ugen_step_to_hz(x1, optional chans): - unary(UNARY_OP_STEP_TO_HZ, x1, chans) -def ugen_step_to_hzb(x1, optional chans): - unaryb(UNARY_OP_STEP_TO_HZ, x1, chans) - -def ugen_hz_to_step(x1, optional chans): - unary(UNARY_OP_HZ_TO_STEP, x1, chans) -def ugen_hz_to_stepb(x1, optional chans): - unaryb(UNARY_OP_HZ_TO_STEP, x1, chans) - -def ugen_vel_to_linear(x1, optional chans): - unary(UNARY_OP_VEL_TO_LINEAR, x1, chans) -def ugen_vel_to_linearb(x1, optional chans): - unaryb(UNARY_OP_VEL_TO_LINEAR, x1, chans) - -def ugen_linear_to_vel(x1, optional chans): - unary(UNARY_OP_LINEAR_TO_VEL, x1, chans) -def ugen_linear_to_velb(x1, optional chans): - unaryb(UNARY_OP_LINEAR_TO_VEL, x1, chans) - -def ugen_db_to_linear(x1, optional chans): - unary(UNARY_OP_DB_TO_LINEAR, x1, chans) -def ugen_db_to_linearb(x1, optional chans): - unaryb(UNARY_OP_DB_TO_LINEAR, x1, chans) - -def ugen_linear_to_db(x1, optional chans): - unary(UNARY_OP_LINEAR_TO_DB, x1, chans) -def ugen_linear_to_dbb(x1, optional chans): - unaryb(UNARY_OP_LINEAR_TO_DB, x1, chans) - - -# ---- included from /Users/rbd/arco/serpent/srp/mix.srp ---- - + + + # mix.srp -- audio mixer # # Roger B. Dannenberg @@ -334,10 +226,9 @@ def mix_name(i): # handy function to convert index into a symbol to name an input when # inputs are originally created in a loop or come from an array intern("in" + str(i)) - - -# ---- included from /Users/rbd/arco/ugens/sine/sine.srp ---- - + + + # sine.srp - constructor implementation # # (machine generated by u2f.py) @@ -358,10 +249,9 @@ def sineb(freq, amp, optional chans): chans = max_chans(max_chans(1, freq), amp) Ugen(create_ugen_id(), "sineb", chans, 'b', "UU", 'freq', freq, 'amp', amp) - - -# ---- included from /Users/rbd/arco/serpent/srp/mathugen.srp ---- - + + + # mathugen.srp - constructor implementation for mult, add, ugen_div, ugen_max, # ugen_min, ugen_clip, ugen_less, ugen_greater, ugen_soft_clip. # For completeness, you can create a Math as well with ugen_math. @@ -527,10 +417,9 @@ def ugen_cos(x1, x2, optional chans): Math(MATH_OP_COS, x1, x2, chans) def ugen_cosb(x1, x2, optional chans): Mathb(MATH_OP_COS, x1, x2, chans) - - -# ---- included from /Users/rbd/arco/ugens/reson/reson.srp ---- - + + + # reson.srp - constructor implementation # # (machine generated by u2f.py) @@ -559,10 +448,9 @@ def resonb(input, center, q, optional chans): Ugen(create_ugen_id(), "resonb", chans, 'b', "UUU", 'input', input, 'center', center, 'q', q) - - -# ---- included from /Users/rbd/arco/ugens/lowpass/lowpass.srp ---- - + + + # lowpass.srp - constructor implementation # # (machine generated by u2f.py) @@ -588,10 +476,9 @@ def lowpassb(input, cutoff, optional chans): Ugen(create_ugen_id(), "lowpassb", chans, 'b', "UU", 'input', input, 'cutoff', cutoff) - - -# ---- included from /Users/rbd/arco/serpent/srp/fileplay.srp ---- - + + + # fileplay.srp -- stream audio from file # # Roger B. Dannenberg @@ -618,10 +505,9 @@ class Fileplay (Ugen): def stop(): go(false) this - - -# ---- included from /Users/rbd/arco/serpent/srp/filerec.srp ---- - + + + # filerec.srp -- stream audio from file # # Roger B. Dannenberg @@ -645,10 +531,9 @@ class Filerec (Ugen): def stop(): go(false) this - - -# ---- included from /Users/rbd/arco/serpent/srp/recplay.srp ---- - + + + # recplay.srp -- record and play audio in memory # # Roger B. Dannenberg @@ -686,10 +571,9 @@ class Recplay (Ugen): def borrow(u): o2_send_cmd("/arco/recplay/borrow", 0, "UU", id, u.id) this - - -# ---- included from /Users/rbd/arco/serpent/srp/delay.srp ---- - + + + # delay.srp -- feedback delay unit generator # # Roger B. Dannenberg @@ -705,10 +589,9 @@ def delay(input, dur, fb, maxdur, optional chans = 1): Delay(chans, input, dur, fb, maxdur) # delay as a function - - -# ---- included from /Users/rbd/arco/serpent/srp/allpass.srp ---- - + + + # allpass.srp -- feedback all-pass filter unit generator # # Roger B. Dannenberg @@ -724,10 +607,9 @@ def allpass(input, dur, fb, maxdur, optional chans = 1): Allpass(chans, input, dur, fb, maxdur) # allpass as a function - - -# ---- included from /Users/rbd/arco/serpent/srp/olapitchshift.srp ---- - + + + # olapitchshift.srp -- overlap add pitch shift unit generator # # Roger B. Dannenberg @@ -746,10 +628,9 @@ class Ola_pitch_shift (Ugen): def ola_pitch_shift(input, ratio, xfade, windur, optional chans = 1): Ola_pitch_shift(chans, input, ratio, xfade, windur) - - -# ---- included from /Users/rbd/arco/serpent/srp/feedback.srp ---- - + + + # feedback.srp -- mix input with most recent output of some Ugen # # Roger B. Dannenberg @@ -769,10 +650,9 @@ class Feedback (Ugen): def feedback(input, from, gain, optional chans = 1): Feedback(chans, input, from, gain) - - -# ---- included from /Users/rbd/arco/serpent/srp/granstream.srp ---- - + + + # granstream.srp -- granular synthesis from input stream unit generator # # Roger B. Dannenberg @@ -829,10 +709,9 @@ def granstream(input, polyphony, dur, enable, optional chans = 1): Granstream(chans, input, polyphony, dur, enable) # granstream as a function - - -# ---- included from /Users/rbd/arco/serpent/srp/flsyn.srp ---- - + + + # flsyn.srp - fluid synth interface # # Roger B. Dannenberg @@ -882,10 +761,9 @@ class Flsyn (Ugen): def flsyn(path): Flsyn(path) - - -# ---- included from /Users/rbd/arco/serpent/srp/pv.srp ---- - + + + # pv.srp -- overlap add pitch shift unit generator # # Roger B. Dannenberg @@ -908,10 +786,9 @@ class Pv(Ugen): def pv(input, ratio, fftsize, hopsize, points, mode, optional chans = 1): Pv(chans, input, ratio, fftsize, hopsize, points, mode) - - -# ---- included from /Users/rbd/arco/serpent/srp/yin.srp ---- - + + + # yin.srp -- yin pitch estimation unit generator # # Roger B. Dannenberg @@ -926,10 +803,9 @@ class Yin(Ugen): def yin(input, minstep, maxstep, hopsize, address, optional chans = 1): Yin(chans, input, minstep, maxstep, hopsize, address) # yin as a function - - -# ---- included from /Users/rbd/arco/serpent/srp/trig.srp ---- - + + + # trig.srp -- sound event detection # # Roger B. Dannenberg @@ -961,10 +837,9 @@ class Trig(Ugen): def trig(input, reply_addr, window, threshold, pause): Trig(input, reply_addr, window, threshold, pause) # trig as a function - - -# ---- included from /Users/rbd/arco/serpent/srp/route.srp ---- - + + + # route.srp -- route input channels to output channels # # Roger B. Dannenberg @@ -999,10 +874,9 @@ class Route (Ugen): def route(chans): return Route(chans) - - -# ---- included from /Users/rbd/arco/serpent/srp/chorddetect.srp ---- - + + + def chorddetect(reply_addr): return Chorddetect(reply_addr) @@ -1023,10 +897,9 @@ class Chorddetect (Ugen): o2_send_cmd("/arco/chorddetect/repl_input", 0, "UU", id, value.id) this - - -# ---- included from /Users/rbd/arco/serpent/srp/onset.srp ---- - + + + # trig.srp -- sound event detection # # Roger B. Dannenberg @@ -1040,10 +913,9 @@ class Onset(Ugen): def onset(input, reply_addr): Onset(input, reply_addr) # onset as a function - - -# ---- included from /Users/rbd/arco/serpent/srp/spectralcentroid.srp ---- - + + + def spectralcentroid(reply_addr): return SpectralCentroid(reply_addr) @@ -1065,10 +937,9 @@ class SpectralCentroid (Ugen): this - - -# ---- included from /Users/rbd/arco/serpent/srp/spectralrolloff.srp ---- - + + + def spectralrolloff(reply_addr, threshold): return SpectralRolloff(reply_addr, threshold) @@ -1091,10 +962,9 @@ class SpectralRolloff (Ugen): - - -# ---- included from /Users/rbd/arco/serpent/srp/o2audioio.srp ---- - + + + # o2audioio.srp -- overlap add pitch shift unit generator # # Roger B. Dannenberg @@ -1121,205 +991,81 @@ def o2audioio(input, destaddr, destchans, recvchans, buffsize, sampletype, msgsize) O2audioio(input, destaddr, destchans, recvchans, buffsize, sampletype, msgsize) - - -# ---- included from /Users/rbd/arco/ugens/sttest/sttest.srp ---- - -# sttest.srp - constructor implementation + + + +# zitarev.srp - constructor implementation # # (machine generated by u2f.py) -def sttest(input, hz1, hz2): - if input.rate != 'a': - print "ERROR: 'input' input to Ugen 'sttest' must be audio rate" +def zitarev(snd, wetdry, gain, t60m): + if snd.rate != 'a': + print "ERROR: 'snd' input to Ugen 'zitarev' must be audio rate" return nil - if input.chans != 1 and input.chans != 2: - print "ERROR: 'input' input to Ugen 'sttest' must have 1 or 2 channels" + if not isnumber(wetdry) and wetdry.rate == 'a': + print "ERROR: 'wetdry' input to Ugen 'zitarev' must be block rate" return nil - if not isnumber(hz1) and hz1.chans != 1: - print "ERROR: 'hz1' input to Ugen '{c}' must be single channel" + if not isnumber(gain) and gain.rate == 'a': + print "ERROR: 'gain' input to Ugen 'zitarev' must be block rate" return nil - if not isnumber(hz2) and hz2.chans != 1: - print "ERROR: 'hz2' input to Ugen '{c}' must be single channel" + if not isnumber(t60m) and t60m.rate == 'a': + print "ERROR: 't60m' input to Ugen 'zitarev' must be block rate" return nil - - Ugen(create_ugen_id(), "sttest", 2, 'a', "Uff", omit_chans = true, 'input', - input, 'hz1', hz1, 'hz2', hz2) - - - -# ---- included from /Users/rbd/arco/serpent/srp/tableosc.srp ---- - -# tableosc.srp -- table-lookup oscillator with frequency control -# -# Roger B. Dannenberg -# Oct 2024 - -class Wavetables (Ugen): - var address_prefix // e.g. "/arco/tableosc/" - - def init(freq, amp, phase, chans, classname, rate): - if not chans: - chans = max_chans(max_chans(1, freq), amp) - super.init(create_ugen_id(), classname, chans, rate, - "UUf", 'freq', freq, 'amp', amp, 'phase', phase) - address_prefix = "/arco/" + tolower(classname) + "/" - - - def create_table(index, tlen, data, method_name): - o2_send_start() - o2_add_int32(arco_ugen_id(id)) - o2_add_int32(index) - if tlen: // omit length in case of time-domain data - o2_add_int32(tlen) - o2_add_vector(data, "f") - o2_send_finish(0, address_prefix + method_name, true) - - def create_tas(index, tlen, ampspec): - # table from amplitude spectrum - create_table(index, tlen, ampspec, "createtas") - - def create_tcs(index, tlen, spec): - # table from complex spectrum (amplitude and phase pairs) - create_table(index, tlen, spec, "createtcs") - - def create_ttd(index, samps): - # table from time-domain data (table length is len(samps)) - create_table(index, nil, samps, "createttd") - - def borrow(lender): - o2_send_start() - o2_add_int32(arco_ugen_id(id)) - o2_add_int32(arco_ugen_id(lender.id)) - o2_send_finish(0, address_prefix + "borrow", true) - - def select(index): - o2_send_start() - o2_add_int32(arco_ugen_id(id)) - o2_add_int32(index) - o2_send_finish(0, address_prefix + "sel", true) - - -class Tableosc (Wavetables): - def init(freq, amp, phase, chans): - super.init(freq, amp, phase, chans, "Tableosc", A_RATE) - - -class Tableoscb (Wavetables): - def init(freq, amp, phase, chans): - super.init(freq, amp, phase, chans, "Tableoscb", B_RATE) - - -def tableosc(freq, amp, optional chans, keyword phase = 0): - Tableosc(freq, amp, phase, chans) - - -def tableoscb(freq, amp, optional chans, keyword phase = 0): - if not isnumber(freq) and freq.rate != 'b': - print "ERROR: 'freq' input to Ugen 'tableoscb' must be block rate" + if snd.chans != 1 and snd.chans != 2: + print "ERROR: 'snd' input to Ugen 'zitarev' must have 1 or 2 channels" return nil - if not isnumber(amp) and amp.rate != 'b': - print "ERROR: 'amp' input to Ugen 'tableoscb' must be block rate" + if not isnumber(wetdry) and wetdry.chans != 1: + print "ERROR: 'wetdry' input to Ugen '{c}' must be single channel" + return nil + if not isnumber(gain) and gain.chans != 1: + print "ERROR: 'gain' input to Ugen '{c}' must be single channel" + return nil + if not isnumber(t60m) and t60m.chans != 1: + print "ERROR: 't60m' input to Ugen '{c}' must be single channel" return nil - if not chans: - chans = max_chans(max_chans(1, freq), amp) - Tableoscb(freq, amp, phase, chans) - - -# ---- included from /Users/rbd/arco/serpent/srp/blend.srp ---- - -# blend.srp -- granular synthesis from input stream unit generator -# -# Roger B. Dannenberg -# June 2023 - -BLEND_LINEAR = 0 -BLEND_POWER = 1 -BLEND_45 = 2 - -class Blend (Ugen): - def init(chans, x1, x2, b, mode, init_b): - super.init(create_ugen_id(), "Blend", chans, 'a', "UUUfif", - 'x1', x1, 'x2', x2, 'b', b, - 'mode', mode, 'init_b', init_b) - - def set_gain(gain) - o2_send_cmd("/arco/blend/gain", 0, "Uf", id, gain) - this - - def set_mode(mode) - o2_send_cmd("/arco/blend/mode", 0, "Ui", id, mode) - this - - -def blend(x1, x2, b, optional chans, mode, keyword gain = 1, init_b = 0.5): - chans = chans or max(x1.chans, max(x2.chans, b.chans)) - mode = mode or BLEND_POWER - var ugen = Blend(chans, x1, x2, b, mode, init_b) # blend as a function - if gain != 1: // default for Arco - ugen.set_gain(gain) - return ugen - - -class Blendb (Ugen): - def init(chans, x1, x2, b, mode): - super.init(create_ugen_id(), "Blendb", chans, 'b', "UUUfif", - 'x1', x1, 'x2', x2, 'b', b, 'mode', mode) - - def set_gain(gain) - o2_send_cmd("/arco/blendb/gain", 0, "Uf", id, gain) - this - - def set_mode(mode) - o2_send_cmd("/arco/blendb/mode", 0, "Ui", id, mode) - this - - -def blendb(x1, x2, b, optional chans, mode, keyword gain = 1): - chans = chans or max(x1.chans, max(x2.chans, b.chans)) - mode = mode or BLEND_POWER - var ugen = Blendb(chans, x1, x2, b, mode) # blend as a function - if gain != 1: // default for Arco - ugen.set_gain(gain) - return ugen - - -# ---- included from /Users/rbd/arco/serpent/srp/stdistr.srp ---- + Ugen(create_ugen_id(), "zitarev", 2, 'a', "UUUU", omit_chans = true, 'snd', + snd, 'wetdry', wetdry, 'gain', gain, 't60m', t60m) -# stdistr.srp -- stereo distribution + + + +# overdrive.srp - constructor implementation # -# Roger B. Dannenberg -# June 2023 - -class Stdistr (Ugen): - def init(n, width): - super.init(create_ugen_id(), "Stdistr", 2, 'a', "if", - 'n', n, 'width', width, omit_chans = true) - - def set_gain(gain) - o2_send_cmd("/arco/stdistr/gain", 0, "Uf", id, gain) - this - - def set_width(width) - o2_send_cmd("/arco/stdistr/width", 0, "Uf", id, width) - this - - def ins(index, ugen): - o2_send_cmd("/arco/stdistr/ins", 0, "UiU", id, index, ugen) - this - - def rem(index): - o2_send_cmd("/arco/stdistr/rem", 0, "Ui", id, index) - this - - -def stdistr(n, width): - Stdistr(n, width) # stdistr as a function +# (machine generated by u2f.py) +def overdrive(snd, gain, tone, volume): + if snd.rate != 'a': + print "ERROR: 'snd' input to Ugen 'overdrive' must be audio rate" + return nil + if not isnumber(gain) and gain.rate == 'a': + print "ERROR: 'gain' input to Ugen 'overdrive' must be block rate" + return nil + if not isnumber(tone) and tone.rate == 'a': + print "ERROR: 'tone' input to Ugen 'overdrive' must be block rate" + return nil + if not isnumber(volume) and volume.rate == 'a': + print "ERROR: 'volume' input to Ugen 'overdrive' must be block rate" + return nil + if snd.chans != 1 and snd.chans != 2: + print "ERROR: 'snd' input to Ugen 'overdrive' must have 1 or 2 channels" + return nil + if not isnumber(gain) and gain.chans != 1: + print "ERROR: 'gain' input to Ugen '{c}' must be single channel" + return nil + if not isnumber(tone) and tone.chans != 1: + print "ERROR: 'tone' input to Ugen '{c}' must be single channel" + return nil + if not isnumber(volume) and volume.chans != 1: + print "ERROR: 'volume' input to Ugen '{c}' must be single channel" + return nil -# ---- included from /Users/rbd/arco/serpent/srp/thru.srp ---- + Ugen(create_ugen_id(), "overdrive", 2, 'a', "UUUU", omit_chans = true, + 'snd', snd, 'gain', gain, 'tone', tone, 'volume', volume) + + + # thru.srp -- audio pass-through # # Roger B. Dannenberg @@ -1344,10 +1090,9 @@ def fanout(input, chans) # which requires the number of channels you are expanding to. You should # only use this if input is mono. Thru(input, chans) - - -# ---- included from /Users/rbd/arco/serpent/srp/zero.srp ---- - + + + # zero.srp -- audio zero # # Roger B. Dannenberg @@ -1372,10 +1117,9 @@ class Zerob (Ugen) # DO NOT DEFINE zerob() -- SEE arco.srp, WHICH DEFINES zerob() TO RETURN zerob_ugen # def zerob(): purposefully not defined here! - - -# ---- included from /Users/rbd/arco/serpent/srp/fader.srp ---- - + + + # fader.srp - constructor implementation # # Roger B. Dannenberg @@ -1450,10 +1194,9 @@ class Fader (Ugen): output_ugen.swap(this, ugen) // remove fader from ugen fade_in_lookup.remove(ugen) // otherwise, fade_in was cancelled by a fade() - - -# ---- included from /Users/rbd/arco/serpent/srp/sum.srp ---- - + + + # sum.srp -- sum and sumb unit generators # # Roger B. Dannenberg @@ -1541,4 +1284,4 @@ class Sumb(Ugen): def sumb(optional chans = 1, keyword wrap = true): Sumb(chans, wrap) - + diff --git a/apps/test/dspmanifest.txt b/apps/test/dspmanifest.txt index 87c64aa..14fe9a0 100644 --- a/apps/test/dspmanifest.txt +++ b/apps/test/dspmanifest.txt @@ -28,7 +28,8 @@ spectralcentroid spectralrolloff o2audioio sttest -# zitarev +zitarev tableosc* blend* stdistr +monodistortion \ No newline at end of file diff --git a/apps/test/init.srp b/apps/test/init.srp index 88a7142..a762038 100644 --- a/apps/test/init.srp +++ b/apps/test/init.srp @@ -294,6 +294,9 @@ def arco_ready(): Checkbox(win, "supersaw", 'S', 'D', WIDTH, 'S', 'supersawcheckbox') supersawcheckbox.add_target_method(nil, 'supersawtest') + Checkbox(win, "Overdrive Test", 'S', 'D', WIDTH, 'S', 'overdrivecheckbox') + overdrivecheckbox.add_target_method(nil, 'overdrivetest') + ui_initialized = true default_window.fit_to_children() @@ -1374,6 +1377,33 @@ def supersawtest(obj, event, x, y): return sss.mute() +########## Overdrive Test ############## + +ovd = nil + +def overdrivetest(obj, event, x, y): + display "overdrivetest", x + if x: + + var left_tone = sine(220, 0.3) // 220 Hz (A3) + var right_tone = sine(275, 0.3) // 275 Hz (~C#4) + + var env = pweb(0.2, 0.5, 1) + var left_env = mult(left_tone, env) + var right_env = mult(right_tone, env) + + // Mix left and right signals into a single mono signal before distortion + var mono_input = add(left_env, right_env) + + // Apply distortion + ovd = monodistortion(mono_input, 0.85, 0.85, 0.8) // snd, gain, tone, volume + ovd.play() + else: + // Fade out and stop processing + ovd.set('snd', zero_ugen) + sched_select(rtsched) + sched_cause(6, ovd, 'mute') + ovd = nil ########## Main Initialization ################ diff --git a/doc/ugens.md b/doc/ugens.md index e12e065..d0ae32a 100644 --- a/doc/ugens.md +++ b/doc/ugens.md @@ -1942,3 +1942,49 @@ zero() `/arco/zero/new id` - Create a signal consisting of all-zeros. +### monodistortion +''' +monodistortion(input, gain, tone, volume) +''' +The input signal can have multiple channels, which are first summed into +a mono signal and scaled by `gain`. After processing in mono, the signal +is fanned out and blended with the original input signal using `wetdry` +to control the balance from pure input (`wetdry` = 0) to pure effect +(`wetdry` = 1). + +The processing chain contains a first-order high-pass filter set at 720 Hz, +which removes low-frequency components. The filtered signal is then passed +through a cubic nonlinear distortion function, which shapes the +signal by introducing harmonic content and saturation. After +distortion, the signal is passed through a low-pass filter with a cutoff +frequency determined by the `tone` parameter (in Hz), allowing for control +over the brightness or warmth of the effect. Finally, the signal is +multiplied by the `volume` parameter, controlling the output gain. + +After the signal is processed, the `wetdry` parameter is used to blend the +distorted signal with the original input. When `wetdry` is set to 0, the +output consists entirely of the original input signal. When set to 1, the +output consists only of the distorted signal. Values in between allow for a +balance between the dry and processed signals + +`/arco/monodistortion/new id chans gain tone volume` - Create a new +monodistortion unit generator with `gain`, `tone`, and `volume` control +inputs, along with an audio input and output. + +`/arco/monodistortion/repl_gain id gain_id` - Set gain to object with id +`gain_id`. + +`/arco/monodistortion/set_gain id chan gain` - Set gain of channel `chan` to +float value `gain`. + +`/arco/monodistortion/repl_tone id tone_id` - Set tone to object with id +`tone_id`. + +`/arco/monodistortion/set_tone id chan tone` - Set tone of channel `chan` to +float value `tone`. + +`/arco/monodistortion/repl_volume id volume_id` - Set volume to object with +id `volume_id`. + +`/arco/monodistortion/set_volume id chan volume` - Set volume of channel +`chan` to float value `volume`. diff --git a/serpent/srp/overdrive.srp b/serpent/srp/overdrive.srp new file mode 100644 index 0000000..72cc3af --- /dev/null +++ b/serpent/srp/overdrive.srp @@ -0,0 +1,19 @@ +# overdrive.srp -- overdrive unit generator +# +# Vivek Mohan +# February 2025 + +class Overdrive(Instrument): + def init(input, gain, tone, volume, wetdry): + + mono_input = route(1) + for i = 0 to input.chans: + mono_input.ins(input, i, 0) + + scale_factor = 1.0 / sqrt(input.chans) + + processed = monodistortion(mono_input, gain, tone, volume * scale_factor) + + final_output = add(mult(input, mult(scale_factor,subb(1, wetdry))), mult(processed, mult(scale_factor,wetdry))) + + super.init("Overdrive", final_output) \ No newline at end of file diff --git a/ugens/monodistortion/monodistortion.cpp b/ugens/monodistortion/monodistortion.cpp new file mode 100644 index 0000000..a24737b --- /dev/null +++ b/ugens/monodistortion/monodistortion.cpp @@ -0,0 +1,179 @@ +/* monodistortion -- unit generator for arco + * + * generated by f2a.py + */ + +#include "arcougen.h" +#include "monodistortion.h" + +const char *Monodistortion_name = "Monodistortion"; + +/* O2SM INTERFACE: /arco/monodistortion/new int32 id, int32 chans, int32 snd, int32 gain, int32 tone, int32 volume; + */ +void arco_monodistortion_new(O2SM_HANDLER_ARGS) +{ + // begin unpack message (machine-generated): + int32_t id = argv[0]->i; + int32_t chans = argv[1]->i; + int32_t snd = argv[2]->i; + int32_t gain = argv[3]->i; + int32_t tone = argv[4]->i; + int32_t volume = argv[5]->i; + // end unpack message + + ANY_UGEN_FROM_ID(snd_ugen, snd, "arco_monodistortion_new"); + ANY_UGEN_FROM_ID(gain_ugen, gain, "arco_monodistortion_new"); + ANY_UGEN_FROM_ID(tone_ugen, tone, "arco_monodistortion_new"); + ANY_UGEN_FROM_ID(volume_ugen, volume, "arco_monodistortion_new"); + + new Monodistortion(id, chans, snd_ugen, gain_ugen, tone_ugen, volume_ugen); +} + + +/* O2SM INTERFACE: /arco/monodistortion/repl_snd int32 id, int32 snd_id; + */ +static void arco_monodistortion_repl_snd(O2SM_HANDLER_ARGS) +{ + // begin unpack message (machine-generated): + int32_t id = argv[0]->i; + int32_t snd_id = argv[1]->i; + // end unpack message + + UGEN_FROM_ID(Monodistortion, monodistortion, id, "arco_monodistortion_repl_snd"); + ANY_UGEN_FROM_ID(snd, snd_id, "arco_monodistortion_repl_snd"); + monodistortion->repl_snd(snd); +} + + +/* O2SM INTERFACE: /arco/monodistortion/set_snd int32 id, int32 chan, float val; + */ +static void arco_monodistortion_set_snd (O2SM_HANDLER_ARGS) +{ + // begin unpack message (machine-generated): + int32_t id = argv[0]->i; + int32_t chan = argv[1]->i; + float val = argv[2]->f; + // end unpack message + + UGEN_FROM_ID(Monodistortion, monodistortion, id, "arco_monodistortion_set_snd"); + monodistortion->set_snd(chan, val); +} + + +/* O2SM INTERFACE: /arco/monodistortion/repl_gain int32 id, int32 gain_id; + */ +static void arco_monodistortion_repl_gain(O2SM_HANDLER_ARGS) +{ + // begin unpack message (machine-generated): + int32_t id = argv[0]->i; + int32_t gain_id = argv[1]->i; + // end unpack message + + UGEN_FROM_ID(Monodistortion, monodistortion, id, "arco_monodistortion_repl_gain"); + ANY_UGEN_FROM_ID(gain, gain_id, "arco_monodistortion_repl_gain"); + monodistortion->repl_gain(gain); +} + + +/* O2SM INTERFACE: /arco/monodistortion/set_gain int32 id, int32 chan, float val; + */ +static void arco_monodistortion_set_gain (O2SM_HANDLER_ARGS) +{ + // begin unpack message (machine-generated): + int32_t id = argv[0]->i; + int32_t chan = argv[1]->i; + float val = argv[2]->f; + // end unpack message + + UGEN_FROM_ID(Monodistortion, monodistortion, id, "arco_monodistortion_set_gain"); + monodistortion->set_gain(chan, val); +} + + +/* O2SM INTERFACE: /arco/monodistortion/repl_tone int32 id, int32 tone_id; + */ +static void arco_monodistortion_repl_tone(O2SM_HANDLER_ARGS) +{ + // begin unpack message (machine-generated): + int32_t id = argv[0]->i; + int32_t tone_id = argv[1]->i; + // end unpack message + + UGEN_FROM_ID(Monodistortion, monodistortion, id, "arco_monodistortion_repl_tone"); + ANY_UGEN_FROM_ID(tone, tone_id, "arco_monodistortion_repl_tone"); + monodistortion->repl_tone(tone); +} + + +/* O2SM INTERFACE: /arco/monodistortion/set_tone int32 id, int32 chan, float val; + */ +static void arco_monodistortion_set_tone (O2SM_HANDLER_ARGS) +{ + // begin unpack message (machine-generated): + int32_t id = argv[0]->i; + int32_t chan = argv[1]->i; + float val = argv[2]->f; + // end unpack message + + UGEN_FROM_ID(Monodistortion, monodistortion, id, "arco_monodistortion_set_tone"); + monodistortion->set_tone(chan, val); +} + + +/* O2SM INTERFACE: /arco/monodistortion/repl_volume int32 id, int32 volume_id; + */ +static void arco_monodistortion_repl_volume(O2SM_HANDLER_ARGS) +{ + // begin unpack message (machine-generated): + int32_t id = argv[0]->i; + int32_t volume_id = argv[1]->i; + // end unpack message + + UGEN_FROM_ID(Monodistortion, monodistortion, id, "arco_monodistortion_repl_volume"); + ANY_UGEN_FROM_ID(volume, volume_id, "arco_monodistortion_repl_volume"); + monodistortion->repl_volume(volume); +} + + +/* O2SM INTERFACE: /arco/monodistortion/set_volume int32 id, int32 chan, float val; + */ +static void arco_monodistortion_set_volume (O2SM_HANDLER_ARGS) +{ + // begin unpack message (machine-generated): + int32_t id = argv[0]->i; + int32_t chan = argv[1]->i; + float val = argv[2]->f; + // end unpack message + + UGEN_FROM_ID(Monodistortion, monodistortion, id, "arco_monodistortion_set_volume"); + monodistortion->set_volume(chan, val); +} + + +static void monodistortion_init() +{ + // O2SM INTERFACE INITIALIZATION: (machine generated) + o2sm_method_new("/arco/monodistortion/new", "iiiiii", + arco_monodistortion_new, NULL, true, true); + o2sm_method_new("/arco/monodistortion/repl_snd", "ii", + arco_monodistortion_repl_snd, NULL, true, true); + o2sm_method_new("/arco/monodistortion/set_snd", "iif", + arco_monodistortion_set_snd, NULL, true, true); + o2sm_method_new("/arco/monodistortion/repl_gain", "ii", + arco_monodistortion_repl_gain, NULL, true, true); + o2sm_method_new("/arco/monodistortion/set_gain", "iif", + arco_monodistortion_set_gain, NULL, true, true); + o2sm_method_new("/arco/monodistortion/repl_tone", "ii", + arco_monodistortion_repl_tone, NULL, true, true); + o2sm_method_new("/arco/monodistortion/set_tone", "iif", + arco_monodistortion_set_tone, NULL, true, true); + o2sm_method_new("/arco/monodistortion/repl_volume", "ii", + arco_monodistortion_repl_volume, NULL, true, true); + o2sm_method_new("/arco/monodistortion/set_volume", "iif", + arco_monodistortion_set_volume, NULL, true, true); + // END INTERFACE INITIALIZATION + + // class initialization code from faust: +} + +Initializer monodistortion_init_obj(monodistortion_init); diff --git a/ugens/monodistortion/monodistortion.h b/ugens/monodistortion/monodistortion.h new file mode 100644 index 0000000..052864e --- /dev/null +++ b/ugens/monodistortion/monodistortion.h @@ -0,0 +1,246 @@ +/* monodistortion -- unit generator for arco + * + * generated by f2a.py + */ + +/*------------- BEGIN FAUST PREAMBLE -------------*/ + +/* ------------------------------------------------------------ +name: "monodistortion" +Code generated with Faust 2.74.6 (https://faust.grame.fr) +Compilation options: -lang cpp -light -ct 1 -cn Monodistortion -es 1 -mcd 16 -mdd 1024 -mdy 33 -single -ftz 0 +------------------------------------------------------------ */ + +#ifndef __Monodistortion_H__ +#define __Monodistortion_H__ + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +#include +#include +#include +#include + +#ifndef FAUSTCLASS +#define FAUSTCLASS Monodistortion +#endif + +#ifdef __APPLE__ +#define exp10f __exp10f +#define exp10 __exp10 +#endif + +#if defined(_WIN32) +#define RESTRICT __restrict +#else +#define RESTRICT __restrict__ +#endif + +static float Monodistortion_faustpower2_f(float value) { + return value * value; +} +/*-------------- END FAUST PREAMBLE --------------*/ + +extern const char *Monodistortion_name; + +class Monodistortion : public Ugen { +public: + struct Monodistortion_state { + float fVec0[2]; + float fRec1[2]; + FAUSTFLOAT fEntry0; + float fVec1[2]; + FAUSTFLOAT fEntry1; + float fRec0[2]; + FAUSTFLOAT fEntry2; + }; + Vec states; + void (Monodistortion::*run_channel)(Monodistortion_state *state); + + Ugen_ptr snd; + int snd_stride; + Sample_ptr snd_samps; + + Ugen_ptr gain; + int gain_stride; + Sample_ptr gain_samps; + + Ugen_ptr tone; + int tone_stride; + Sample_ptr tone_samps; + + Ugen_ptr volume; + int volume_stride; + Sample_ptr volume_samps; + + float fConst0; + float fConst1; + float fConst2; + float fConst3; + float fConst4; + + Monodistortion(int id, int nchans, Ugen_ptr snd_, Ugen_ptr gain_, Ugen_ptr tone_, Ugen_ptr volume_) : + Ugen(id, 'a', nchans) { + snd = snd_; + gain = gain_; + tone = tone_; + volume = volume_; + flags = CAN_TERMINATE; + states.set_size(chans); + fConst0 = std::min(1.92e+05f, std::max(1.0f, float(AR))); + fConst1 = 1.0f / std::tan(2261.9468f / fConst0); + fConst2 = 1.0f - fConst1; + fConst3 = 1.0f / (fConst1 + 1.0f); + fConst4 = 3.1415927f / fConst0; + init_snd(snd); + init_gain(gain); + init_tone(tone); + init_volume(volume); + run_channel = (void (Monodistortion::*)(Monodistortion_state *)) 0; + update_run_channel(); + } + + ~Monodistortion() { + snd->unref(); + gain->unref(); + tone->unref(); + volume->unref(); + } + + const char *classname() { return Monodistortion_name; } + + void initialize_channel_states() { + for (int i = 0; i < chans; i++) { + for (int l0 = 0; l0 < 2; l0 = l0 + 1) { + states[i].fVec0[l0] = 0.0f; + } + for (int l1 = 0; l1 < 2; l1 = l1 + 1) { + states[i].fRec1[l1] = 0.0f; + } + for (int l2 = 0; l2 < 2; l2 = l2 + 1) { + states[i].fVec1[l2] = 0.0f; + } + for (int l3 = 0; l3 < 2; l3 = l3 + 1) { + states[i].fRec0[l3] = 0.0f; + } + } + } + + void update_run_channel() { + // initialize run_channel based on input types + void (Monodistortion::*new_run_channel)(Monodistortion_state *state); + if (snd->rate == 'b') { + snd = new Upsample(-1, snd->chans, snd); + } + if (gain->rate == 'a') { + gain = new Dnsampleb(-1, gain->chans, gain, LOWPASS500); + } + if (tone->rate == 'a') { + tone = new Dnsampleb(-1, tone->chans, tone, LOWPASS500); + } + if (volume->rate == 'a') { + volume = new Dnsampleb(-1, volume->chans, volume, LOWPASS500); + } + new_run_channel = &Monodistortion::chan_abbb_a; + run_channel = new_run_channel; + } + + void print_sources(int indent, bool print_flag) { + snd->print_tree(indent, print_flag, "snd"); + gain->print_tree(indent, print_flag, "gain"); + tone->print_tree(indent, print_flag, "tone"); + volume->print_tree(indent, print_flag, "volume"); + } + + void repl_snd(Ugen_ptr ugen) { + snd->unref(); + init_snd(ugen); + update_run_channel(); + } + + void repl_gain(Ugen_ptr ugen) { + gain->unref(); + init_gain(ugen); + update_run_channel(); + } + + void repl_tone(Ugen_ptr ugen) { + tone->unref(); + init_tone(ugen); + update_run_channel(); + } + + void repl_volume(Ugen_ptr ugen) { + volume->unref(); + init_volume(ugen); + update_run_channel(); + } + + void set_snd(int chan, float f) { + snd->const_set(chan, f, "Monodistortion::set_snd"); + } + + void set_gain(int chan, float f) { + gain->const_set(chan, f, "Monodistortion::set_gain"); + } + + void set_tone(int chan, float f) { + tone->const_set(chan, f, "Monodistortion::set_tone"); + } + + void set_volume(int chan, float f) { + volume->const_set(chan, f, "Monodistortion::set_volume"); + } + + void init_snd(Ugen_ptr ugen) { init_param(ugen, snd, &snd_stride); } + + void init_gain(Ugen_ptr ugen) { init_param(ugen, gain, &gain_stride); } + + void init_tone(Ugen_ptr ugen) { init_param(ugen, tone, &tone_stride); } + + void init_volume(Ugen_ptr ugen) { init_param(ugen, volume, &volume_stride); } + + void chan_abbb_a(Monodistortion_state *state) { + FAUSTFLOAT* input0 = snd_samps; + FAUSTFLOAT* output0 = out_samps; + float fSlow0 = std::pow(1e+01f, 2.0f * float(gain_samps[0])); + float fSlow1 = 1.0f / std::tan(fConst4 * float(tone_samps[0])); + float fSlow2 = 1.0f - fSlow1; + float fSlow3 = 1.0f / (fSlow1 + 1.0f); + float fSlow4 = float(volume_samps[0]); + for (int i0 = 0; i0 < BL; i0 = i0 + 1) { + float fTemp0 = float(input0[i0]); + state->fVec0[0] = fTemp0; + state->fRec1[0] = -(fConst3 * (fConst2 * state->fRec1[1] + fConst1 * (state->fVec0[1] - fTemp0))); + float fTemp1 = std::max(-1.0f, std::min(1.0f, fSlow0 * state->fRec1[0])); + float fTemp2 = fTemp1 * (1.0f - 0.33333334f * Monodistortion_faustpower2_f(fTemp1)); + state->fVec1[0] = fTemp2; + state->fRec0[0] = -(fSlow3 * (fSlow2 * state->fRec0[1] - (fTemp2 + state->fVec1[1]))); + output0[i0] = FAUSTFLOAT(fSlow4 * state->fRec0[0]); + state->fVec0[1] = state->fVec0[0]; + state->fRec1[1] = state->fRec1[0]; + state->fVec1[1] = state->fVec1[0]; + state->fRec0[1] = state->fRec0[0]; + } + } + + void real_run() { + snd_samps = snd->run(current_block); // update input + gain_samps = gain->run(current_block); // update input + tone_samps = tone->run(current_block); // update input + volume_samps = volume->run(current_block); // update input + Monodistortion_state *state = &states[0]; + for (int i = 0; i < chans; i++) { + (this->*run_channel)(state); + state++; + out_samps += BL; + audio_samps += audio_stride; + gain_samps += gain_stride; + tone_samps += tone_stride; + volume_samps += volume_stride; + } + } +}; +#endif diff --git a/ugens/monodistortion/monodistortion.srp b/ugens/monodistortion/monodistortion.srp new file mode 100644 index 0000000..fa2c345 --- /dev/null +++ b/ugens/monodistortion/monodistortion.srp @@ -0,0 +1,22 @@ +# monodistortion.srp - constructor implementation +# +# (machine generated by u2f.py) + +def monodistortion(snd, gain, tone, volume, optional chans): + if snd.rate != 'a': + print "ERROR: 'snd' input to Ugen 'monodistortion' must be audio rate" + return nil + if not isnumber(gain) and gain.rate == 'a': + print "ERROR: 'gain' input to Ugen 'monodistortion' must be block rate" + return nil + if not isnumber(tone) and tone.rate == 'a': + print "ERROR: 'tone' input to Ugen 'monodistortion' must be block rate" + return nil + if not isnumber(volume) and volume.rate == 'a': + print "ERROR: 'volume' input to Ugen 'monodistortion' must be block rate" + return nil + if not chans: + chans = max_chans(max_chans(max_chans(max_chans(1, snd), gain), tone), volume) + Ugen(create_ugen_id(), "monodistortion", chans, 'a', "UUUU", 'snd', snd, + 'gain', gain, 'tone', tone, 'volume', volume) + diff --git a/ugens/monodistortion/monodistortion.ugen b/ugens/monodistortion/monodistortion.ugen new file mode 100644 index 0000000..55df6c6 --- /dev/null +++ b/ugens/monodistortion/monodistortion.ugen @@ -0,0 +1,33 @@ +# monodistortion.ugen -- Overdrive Unit Generator +# +# Vivek Mohan +# November 2024 + +#Parameters + +#snd: N-channel input signal. +#gain: Controls the intensity of distortion applied to the signal (Range: 0 to 1). +#tone: Adjusts the post-filter low-pass cutoff frequency. +#volume: Controls the output gain. + +monodistortion(snd: a, gain: b, tone: b, volume: b): a + +FAUST + +declare name "monodistortion"; +declare description "Overdrive Unit Generator for Arco"; + +import("stdfaust.lib"); + +monodistortion = _: input_filter : clipping : post_filter * volume + +with { + + input_filter = fi.highpass(1, 720); + + clipping = ef.cubicnl(gain, 0); + + post_filter = fi.lowpass(1, tone); +}; + +process(audio, gain, tone, volume) = audio:monodistortion; \ No newline at end of file