New mono pattern implementation

Here’s a useful class I thought I’d share. It’s pretty beta, but I find that it works fine for the cases I’ve tested. Happy to take feedback!

It’s a replacement for Pmono that provides several additional things:

  • It has the same syntax as Pbind - you no longer need the SynthDef name as the first argument, so you can easily switch in your code between mono and poly Pbinds.
  • It provides a more convenient constructor, Pbind.mono.
  • It has several ways to restart your mono voice…
    • If it detects that the target SynthDef has changed since it started, it restarts the voice.
    • If you have a \monoId key, the voice is restarted every time this changes
    • If the \restart key contains or returns true.
  • It is SOMEWHAT resilient to node order changes. So, for example, if you specify a target \group or \addAction and this CHANGES while you’re playing, the node is re-ordered on the server.
  • Normally Pmono uses the \off event type to end a voice - so do I. But, you can override this using the \offEventType key. So, if you desire some special behavior when your voice is ending, you can make a custom event type and point to it here.
  • It’s composable: you can make any pattern monophonic by doing: Pmonophonic() <> Pdef(\someOtherPattern)

Example:

(
SynthDef(\saw, {
    Out.ar(
        \out.ir(0),
        RLPF.ar(
            Saw.ar(
                (
                    \freq.ar 
                        * [0, \foffset.kr(0.01)].poll
                        .resamp1(4)
                        .midiratio
                ).lag(\freqLag.kr(0.05))
            ).sum,
            \lpf.ar(300),
            0.3
        )
        * Env.asr(
            attackTime: \attack.kr(0.1),
            releaseTime: \release.kr(1)
        ).ar(gate:\gate.kr(1))
        * \amp.ar(1)
        ! 2
    )
}).add;
)

(
Pdef(\monoVoice, Pbind.mono(
    \instrument, \saw,
    \dur, 1/8,
    \amp, -24.dbamp,
    \lpf, Pseg([
        Pwhite(50, 120, 1),
        Pwhite(550, 6320, 1)
    ], [Pwhite(1, 8, 1)]).repeat,
    \foffset, Pseg(
        [
            0.01,
            Pwhite(0.01, 1, 1),
            0.01,
        ], 
        [Pwhite(1, 8, 1), Pwhite(1, 8, 1)],
        6 * [1, -1]
    ).repeat,
    \scale, Scale.augmented,
    \release, 6,
    \restart, Prand([false, false, true], inf),
    \degree, Pxrand([-4, 2, 1, 5, 3], inf) 
        + Pseq([0, -2, -3, 2, 1], inf).durStep(
            Pseq([2, 2, 1.5, 3.5], inf)
        )
)).play
)

You can see that if you change the SynthDef, the mono voice is restarted. Another interesting example - point the \monoId key to \octave, which starts a new voice each time the octave changes. This is useful for e.g. sample based synths, where you don’t want an abrupt click or transition when you switch samples.

(
Pdef(\monoVoice, Pbind.mono(
    \instrument, \saw,
    \dur, 1/8,
    \amp, -24.dbamp,
    \lpf, Pseg([
        Pwhite(50, 120, 1),
        Pwhite(550, 6320, 1)
    ], [Pwhite(1, 8, 1)]).repeat,
    \scale, Scale.augmented,
    \release, 6,
    \octave, Pxrand([2, 3, 4], inf).durStep(4),
    \monoId, { ~octave },
    \degree, Pxrand([-4, 2, 1, 5, 3], inf) 
        + Pseq([0, -2, -3, 2, 1], inf).durStep(
            Pseq([2, 2, 1.5, 3.5], inf)
        )
)).play
)
3 Likes

Also worth noting that it supports the kwargs syntax:

(
Pdef(\monoVoice, Pbind.mono(
    instrument: \saw,
    dur: 1/8,
    amp: -24.dbamp,
    lpf: Pseg([
        Pwhite(50, 120, 1),
        Pwhite(550, 6320, 1)
    ], [Pwhite(1, 8, 1)]).repeat,
    scale: Scale.augmented,
    release: 6,
    octave: Pxrand([2, 3, 4], inf).durStep(4),
    monoId: { ~octave },
    degree: Pxrand([-4, 2, 1, 5, 3], inf) 
        + Pseq([0, -2, -3, 2, 1], inf).durStep(
            Pseq([2, 2, 1.5, 3.5], inf)
        )
)).play
)

But this may cause trouble if you’re not on the latest SuperCollider builds.

Thank you very much for sharing!
Does the implementation rely on any other extensions? I wanted to give it a try, but after copying composeEvent.sc and Pmonophonic.sc into the extensions folder and recompiling the class library, I encountered the following error message:

ERROR: syntax error, unexpected ‘,’, expecting ‘|’
in file ‘/Users/sinansamanli/Library/Application Support/SuperCollider/Extensions/Pmonophonic.sc’
line 5 char 18:

      |...pairs, kwpairs|
               ^
      ^Pmonophonic() <> Pbind.performArgs(\new, pairs, kwpairs)

Yes, this requires a newer build of supercollider for the kwargs functionality. You can remove the kwargs in this case to fix the build error.

      |...pairs|
      ^Pmonophonic() <> Pbind.performArgs(\new, pairs)

You’re right, I have version 3.11.1 and removing kwargs solved the problem.

performArgscame with the kwargs change - it is specifically to let you pass kwargs along as there isn’t any other way of doing this. I think you’d want to change this to perform to get it to work on older versions.

Thanks for using it by the way, nice to see it out in the wild :stuck_out_tongue_winking_eye:

2 Likes

Current PmonoArtic can restart the voice after a note’s sustain time is shorter than its \dur – which is useful when the last note in a phrase knows that it’s going to be shorter. But this doesn’t help with a note that knows that it needs an accent. With PmonoArtic, the only way is for the preceding note to look ahead for an accent; this is difficult.

So \restart is very useful for that case.

Would a future version of this add the PmonoArtic behavior as well? Both are useful, in different cases. (Or, choose PmonoArtic vs Pbind.mono depending on the scenario?)

hjh

for PmonoArtic behavior can one not just set restart: { ~sustain < ~dur } ?

It would be nice if it was so simple, but actually I think whats required is - checking whether the LAST note was ~sustain < ~dur. I haven’t figured out an elegant way to doing this, but its bubbling in the back of my brain…

Yes, my preference would be that “artic” style behavior is just a simple + logical tweak to a parameter and not, e.g., and entire separate class. I

1 Like