Permalink
Cannot retrieve contributors at this time
| -- ??? (nc02-rs) | |
| -- @synthetivv | |
| sc = softcut | |
| dirty = false | |
| next_head = 1 | |
| onsets = { -- generated by `aubioonset -t 0.66 -O energy nc02-perc.wav` | |
| 0.000000, 0.427479, 0.495688, 0.999646, 1.496604, | |
| 1.993625, 2.499792, 2.996063, 3.089292, 3.191354, | |
| 3.465250, 4.338583, 4.519729, 4.594812, 4.680313, | |
| 4.745062, 4.915563, 4.995354, 5.134729, 5.343125, | |
| 5.981979, 6.270688, 6.463500, 6.521500, 6.780729, | |
| 7.289479, 7.353229, 7.497542, 7.615104, 7.698021, | |
| 7.857584, 7.950875, 8.077105, 8.157979, 8.261771, | |
| 8.419791, 8.547334, 8.718875, | |
| 10, 11, 12, 13 | |
| } | |
| instruments = { | |
| { o = 11, v = 1, l = 0.45, d = 0.98, e = 0.1 }, -- bd | |
| { o = 5, v = 0.7, l = 0.5, d = 0.7, e = 0.2 }, -- ch | |
| { o = 5, v = 0.7, l = 0.5, d = 1, e = 0.5 }, -- oh | |
| { o = 1, v = 1, l = 0.5, d = 1, e = 0.6 }, -- hc | |
| { o = 4, v = 1, l = 0.5, d = 0.98, e = 0.7 }, -- sd | |
| { o = 39, v = 0.3, l = 16, d = 1, e = 0.1 }, -- tonal | |
| { o = 40, v = 0.3, l = 16, d = 1, e = 0.1 } -- tonal | |
| } | |
| Voice = {} | |
| Voice.__index = Voice | |
| function Voice.new(buffer) | |
| local v = setmetatable({}, Voice) | |
| local head = next_head | |
| next_head = head + 1 | |
| sc.enable(head, 1) | |
| sc.buffer(head, buffer) | |
| sc.level(head, 1.0) | |
| sc.loop(head, 0) | |
| sc.loop_start(head, 1) | |
| sc.loop_end(head, 2) | |
| sc.position(head, 0) | |
| sc.fade_time(head, 0.0025) | |
| sc.level_slew_time(head, 0.01) | |
| sc.rate(head, 1.0) | |
| sc.play(head, 1) | |
| sc.level_slew_time(head, 0.01) | |
| v.head = head | |
| v.sidechain_level = 0 | |
| v.sidechain_factor = 0.9 | |
| return v | |
| end | |
| EchoVoice = {} | |
| EchoVoice.__index = EchoVoice | |
| function EchoVoice.new() | |
| local v = setmetatable(Voice.new(2), EchoVoice) | |
| local head = v.head | |
| sc.loop_start(head, 1) | |
| sc.loop_end(head, 1.125) | |
| sc.loop(head, 1) | |
| sc.position(head, 1) | |
| sc.rate(head, 1/3) | |
| sc.level(head, 1) | |
| sc.rec_level(head, 1) | |
| sc.pre_level(head, 0.5) | |
| sc.rec(head, 1) | |
| sc.play(head, 1) | |
| sc.pre_filter_fc(head, 9000) | |
| sc.pre_filter_dry(head, 0) | |
| sc.pre_filter_lp(head, 1) | |
| sc.pre_filter_rq(head, 0.8) | |
| sc.rate_slew_time(head, 0.3) | |
| return v | |
| end | |
| function EchoVoice:sidechain_decay() | |
| self.sidechain_level = self.sidechain_level * self.sidechain_factor | |
| sc.level(self.head, 1 - self.sidechain_level) | |
| end | |
| PercVoice = {} | |
| PercVoice.__index = PercVoice | |
| function PercVoice.new() | |
| local v = setmetatable(Voice.new(1), PercVoice) | |
| v.tick = 0 | |
| v.step_index = 1 | |
| v.shift = 0 | |
| v.shift_carry = 0 | |
| v.send = 0 | |
| v.flash_level = 0 | |
| v.decay_level = 0 | |
| v.instrument = instruments[1] | |
| return v | |
| end | |
| function PercVoice:envelope_decay() | |
| self.decay_level = self.decay_level * self.instrument.d | |
| self.sidechain_level = self.sidechain_level * self.sidechain_factor | |
| local level = self.instrument.v * self.decay_level * (1 - self.sidechain_level) | |
| sc.level(self.head, level) | |
| sc.level_cut_cut(self.head, echo.head, self.send * level) | |
| end | |
| function PercVoice:flash_decay() | |
| if self.flash_level > 0 then | |
| self.flash_level = math.floor(self.flash_level * 0.6) | |
| dirty = true | |
| end | |
| end | |
| function PercVoice:play() | |
| local head = self.head | |
| local start = onsets[self.instrument.o] | |
| local length = self.instrument.l | |
| local length_sec = clock.get_beat_sec(length) | |
| local instrument = self.instrument | |
| local send = 0 | |
| if math.random() <= instrument.e then | |
| send = 1 | |
| end | |
| sc.loop_start(head, onsets[instrument.o]) | |
| sc.loop_end(head, start + length) | |
| sc.position(head, start) | |
| sc.level(head, instrument.v) | |
| sc.level_cut_cut(head, echo.head, send * instrument.v) | |
| sc.play(head, 1) | |
| drums[3].sidechain_level = drums[3].sidechain_level + instrument.v | |
| echo.sidechain_level = echo.sidechain_level + instrument.v | |
| self.send = send | |
| self.decay_level = 1 | |
| self.start = start | |
| self.length = length | |
| self.flash_level = 16 | |
| end | |
| function PercVoice:run() | |
| self.clock = clock.run(function() | |
| while true do | |
| clock.sync(0.25) -- 16th notes | |
| local tick = self.tick | |
| tick = tick + 1 | |
| local step_index = self.step_index | |
| local step = self.pattern[step_index] | |
| if tick > (step._l or step.l) then | |
| tick = 1 | |
| step_index = step_index % #self.pattern + 1 | |
| step = self.pattern[step_index] | |
| local shift_carry = self.shift_carry | |
| local _l = step.l | |
| if math.random() <= step.r then | |
| _l = _l - shift_carry | |
| shift_carry = 0 | |
| end | |
| if math.random() <= step.s then | |
| _l = _l + 1 | |
| shift_carry = shift_carry + 1 | |
| end | |
| if math.random() <= step.s then | |
| _l = _l - 1 | |
| shift_carry = shift_carry - 1 | |
| end | |
| self.shift_carry = shift_carry | |
| step._l = _l | |
| self.instrument = instruments[step.i] | |
| if math.random() <= step.p then | |
| self:play() | |
| end | |
| self.step_index = step_index | |
| end | |
| self.tick = tick | |
| end | |
| end) | |
| end | |
| function PercVoice:stop() | |
| if self.clock ~= nil then | |
| clock.cancel(self.clock) | |
| end | |
| end | |
| echo = EchoVoice.new() | |
| drums = {} | |
| for d = 1, 3 do | |
| local drum = PercVoice.new() | |
| drum.start = d | |
| drum.length = 0.25 | |
| drum.rate = 0.25 | |
| drums[d] = drum | |
| end | |
| drums[1].start = onsets[11] -- kick | |
| drums[1].length = 0.45 | |
| drums[2].decay_factor = 0.98 | |
| drums[1].pattern = { | |
| { p = 1, i = 1, l = 4, s = 0.25, r = 0.7 }, | |
| { p = 0.33, i = 1, l = 2, s = 0.25, r = 0.5 }, | |
| { p = 0.05, i = 4, l = 2, s = 0.25, r = 0.5 }, | |
| { p = 1, i = 1, l = 4, s = 0.25, r = 0.7 }, | |
| { p = 0.1, i = 4, l = 4, s = 0.25, r = 0.5 } | |
| } | |
| drums[2].pattern = { | |
| { p = 0.2, i = 2, l = 2, s = 0, r = 1 }, | |
| { p = 0.8, i = 2, l = 2, s = 0, r = 1 }, | |
| { p = 0.2, i = 3, l = 2, s = 0, r = 1 }, | |
| { p = 0.8, i = 2, l = 2, s = 0, r = 1 } | |
| } | |
| drums[3].start = 11 | |
| drums[3].length = 16 | |
| drums[3].sidechain_factor = 0.9 | |
| drums[3].decay_factor = 1 | |
| drums[3].pattern = { | |
| { p = 1, i = 7, l = 16, s = 0, r = 0.25 }, | |
| { p = 0.2, i = 7, l = 8, s = 0, r = 0.25 }, | |
| { p = 0.2, i = 7, l = 8, s = 0, r = 0.25 }, | |
| { p = 0.7, i = 7, l = 16, s = 0, r = 0.25 }, | |
| { p = 1, i = 7, l = 8, s = 0, r = 0.25 }, | |
| { p = 0.2, i = 7, l = 8, s = 0, r = 0.25 }, | |
| { p = 0.2, i = 7, l = 8, s = 0, r = 0.25 } | |
| } | |
| sc.rate(drums[3].head, 2) | |
| function load_file(filename, buffer, buffer_start) | |
| local path = _path.code .. 'nc02-rs/lib/' .. filename | |
| sc.buffer_read_mono(path, 0, buffer_start, -1, 1, buffer) -- -1 = read whole file, I guess? | |
| end | |
| envelope_metro = metro.init() | |
| envelope_metro.time = 0.01 | |
| envelope_metro.event = function() | |
| for d = 1, 3 do | |
| drums[d]:envelope_decay() | |
| end | |
| end | |
| redraw_metro = metro.init() | |
| redraw_metro.time = 1/15 | |
| redraw_metro.event = function() | |
| for d = 1, 3 do | |
| drums[d]:flash_decay() | |
| end | |
| if dirty then | |
| redraw() | |
| end | |
| end | |
| flutter_metro = metro.init() | |
| flutter_metro.time = 0.3 | |
| flutter_metro.event = function() | |
| local deviation = (math.random() - 0.5) * 0.05 | |
| sc.rate(echo.head, 1/3 * math.pow(2, deviation)) | |
| end | |
| function init() | |
| load_file('nc02-perc.wav', 1, 0) | |
| load_file('nc02-tonal.wav', 1, 10) | |
| params:set('clock_tempo', 120) | |
| drums[1]:run() | |
| drums[2]:run() | |
| drums[3]:run() | |
| envelope_metro:start() | |
| redraw_metro:start() | |
| flutter_metro:start() | |
| end | |
| function enc(n, d) | |
| if n == 3 then | |
| params:delta('drum_onset', d) | |
| end | |
| end | |
| function redraw() | |
| screen.clear() | |
| for d = 1, 3 do | |
| local drum = drums[d] | |
| local x = 26 + (d - 1) * 32 | |
| screen.level(drum.flash_level) | |
| screen.rect(x, 27, 10, 10) | |
| screen.fill() | |
| for e = 1, 4 do | |
| screen.level(math.floor(drum.send * drum.flash_level / (e + 1))) | |
| screen.move(x, 37 + e * 3) | |
| screen.line_rel(10, 0) | |
| screen.stroke() | |
| screen.move(x, 28 - e * 3) | |
| screen.line_rel(10, 0) | |
| screen.stroke() | |
| end | |
| end | |
| screen.update() | |
| end | |
| function cleanup() | |
| redraw_metro:stop() | |
| envelope_metro:stop() | |
| flutter_metro:stop() | |
| end |