pulusound

<<< back to home page

Algorave Helsinki meetup, 2025-05-06

posted 2025-05-09

livecoding on Macintosh IIci

following my work on building a performance system on SuperCollider 2 and my Mac OS 9 EP, i presented my latest project in retrocomputer music making: creating a music livecoding system for Macintosh IIci.

i wrote about this in a separate post.

Robot Uprising and ideas for sound robotics

Karim (Make Gyver) briefly presented Robot Uprising, a series of deep, story driven hackathons, and we discussed some preliminary ideas on how to incorporate sound and music related challenges.

Karim's notes:

Algorave meetup

Robot Uprising inspo

Tech for robots
  • normal microfone
  • mic array for directional sound stuff
  • contact mics (on an arm so you can point them somewhere)
  • echolocation -> sonar
  • light sensors for light2sound stuff
  • Use OSC (builds on UDP) --> provide code to go from OSC to our UDP packages → try writing s/t to steer the Robots with OSC
Ideas
  • having speaker all over the space with different frequencies, play around with it
    • maybe use it for navigation → fixed robot, you have to make them move by playing certain frequencies from certain boxes
      • find spots that have cool interference from the different frequency speakers
      • speakers make "noise" find spots that even out to clear signal like sine (-microfone sends sound to computer, move till its mostly clear)
  • detect certain colours or shapes that are mapped to sound, navigate the robot to make music or recreate a given melody -> reinforcement learning
  • or shoot light to sensor that triggers sound
  • entry level puzzle Sample triggering to make music -> Webcam if you go somewhere it plays a sample. Try to make music by moving to the right spot
  • Stuff in a dark room with only sound. Robots are only allowed to blink every so often
  • sound scaping
Learning
  • Interesting stuff to learnil from puzzles:
    • fourier transform
      • onset/transient detection

exponential rhythms

the topic of programming exponential rhythms came up.

in TidalCycles

i showed my old implementation of exponential rhythms in TidalCycles, but it was a bit broken. here is an improved version:

-- a version of `plyWith` taking an additional function `s` as argument. this
-- function maps the index of each repetition sub-event to the relative duration
-- of that event, allowing non-uniform rhythms to be created.
_plyShapeWith :: (Floating a, RealFrac a)
              => Int -> (a -> a) -> (Pattern b -> Pattern b) -> Pattern b
              -> Pattern b
_plyShapeWith n s f p = repeated
  where
    -- make an `n`-length list of relative event durations by applying the
    -- function `s` to 0, 1, ..., n-1.
    -- time is Rational, so lets spare the user the hassle of converting.
    times = map ((`approxRational` 1e-6) . s . fromIntegral) [0..n-1]
    -- given a single event `v`, build a list of sub-events by applying the
    -- function `f` repeatedly. this list is infinite, but it will be truncated
    -- to the length of `times` once we zip them together.
    reps v = iterate f $ pure v
    -- turn a single event `v` into a list of (duration, sub-event) pairs.
    timesAndReps v = zip times $ reps v
    -- for every event in the pattern `p`, generate the list of durations and
    -- sub-events, and pass it to `timeCat` to concatenate the sub-events using
    -- their corresponding durations.
    -- finally, `squeezeJoin` the resulting pattern, so that each result of
    -- `timeCat` gets fit into the duration of the original event from `p`.
    repeated = squeezeJoin $ (timeCat . timesAndReps) <$> p

-- same as the above, but accepts a pattern for the first argument (number of
-- repetitions).
plyShapeWith :: (Floating a, RealFrac a
             => Pattern Int -> (a -> a) -> (Pattern b -> Pattern b) -> Pattern b
             -> Pattern b
plyShapeWith np s f p = (\n -> _plyShapeWith n s f p) =<< np

-- special case for easy exponential rhythms. the second argument `bp` (base
-- pattern) now determines the base of the exponential function to generate the
-- duration for each repetition sub-event. pass >1 for rhythms that slow down,
-- <1 for rhythms that speed up.
plyExpWith :: (RealFrac a, Floating a)
           => Pattern Int -> Pattern a -> (Pattern b -> Pattern b) -> Pattern b
           -> Pattern b
plyExpWith np bp f p = innerJoin $ (\b -> plyShapeWith np (b**) f p) <$> bp

-- example:

-- steady kick as backdrop
d1 $ s "techno:1*4"

-- hihats alternately slowing down and speeding up
d2 $ plyExpWith 16 "<1.1 0.9>" id
   $ s "808:1"
   # legato 1

-- claps with occasional bounce
d3 $ sometimesBy 0.33 (plyExpWith 8 0.8 (|* lpf 0.75))
   $ s ((1/2) ~> "[cp]*2")
   # lpf 12000
   # legato 1

in SuperCollider

Joonas (Forces) showed how to use Pseg for exponential rhythm, together with VSTPlugin (1 bar loop of exponentially growing drum triggering):

VSTPlugin.search;

(
SynthDef(\vsti, { arg out = 0;
// VST instruments usually don't have inputs
Out.ar(out, VSTPlugin.ar(nil, 2));
}).add;
)

//vst here
~vsti = VSTPluginController(Synth(\vsti)).open("FM8", editor:true);
~vsti.editor;

(
~p = Pbindef(\pro,
\type, \vst_midi,
\vst, ~vsti, // the VSTPluginController instance
\degree, 50,
\dur, Pseg(Pseq([1/4,1/32],inf),Pseq([1,0],inf),\exp,inf),
\legato, 1.0,
\amp, 1
);
~pPlay=~p.play;
)


~pPlay.stop;

i came up with a more "manual" approach, using plain math to calculate an array of durations for Pseq:

(
// define some example synths
SynthDef(\kick, { |out=0, amp=0.2|
    var sig;
    sig = SinOsc.ar(EnvGen.ar(Env([800,40], [0.01], [\exp])));
    sig = sig * EnvGen.ar(Env.perc(0.001, 0.3)) * amp;
    Out.ar(out, sig ! 2);
}).add;

SynthDef(\hat, { |out=0, amp=0.2|
    var sig;
    sig = WhiteNoise.ar;
    sig = RHPF.ar(sig, 8000);
    sig = sig * EnvGen.ar(Env.perc(0.001, 0.1)) * amp;
    Out.ar(out, sig ! 2);
}).add;

// set a nice tempo
TempoClock.default.tempo = 130 / (4*60);
)

(
// steady kick
Pdef(\beat, Pbind(
    \instrument, \kick,
    \dur, 1/4
)).play(quant: 1);
)

(
// exponential hats
var num, base, durs;
num = 16; // number of steps
base = 1.1; // base of exponential
durs = num.collect { |i| base.pow(i) }; // relative durations
durs = durs / durs.sum; // divide by total duration to fit in 1 bar
// durs = durs / 2; // fit in half bar

Pdef(\hats, Pbind(
    \instrument, \hat,
    \dur, Pseq(durs, inf)
)).play(quant: 1);
)

taui's Max 4 Live patches

Aleksi (taui) showed some of his Max 4 Live patches with unconventional and creative interfaces: