Oopsy Daisy — An Introduction, Part 2
In Part one of this tutorial, I spent some time walking through the process of setting up your system to make use of the Oopsy software for compiling and downloading gen~ patches to a Daisy-enabled device. I'm working with a Daisypatch in this tutorial, but those of you with a Daisypetal (the stompbox version of the Electrosmith offerings) will find very little in the way of difference - the few differences that exist have mostly to do with the knobs and encoder and what they address - things you can find described in the oopsy_petal.maxpat and oopsy_field.maxpat patches in the oopsy distribution.
In the time since you read Part one, I hope you've had a chance to try your hand at compiling and downloading the example patches that come with the Oopsy software. I've personally really enjoyed having that lovely reverb sitting in my Eurorack.
For this second part of our tutorial, I thought I'd create a few gen~ patches and go over some tips and tricks that I hope can be the start of you being able to create your own Daisypatch modules. I'm also going to be starting with something very simple, and the looking at the process of building that out to create something interesting and unique (those of you who are familiar with my book Step by Step will be familiar with this approach). I also want to concentrate on something that I know a number of you are probably going to be doing — porting gen~ patchers [your own or someone else's] to use with a Daisypatch. When we're done, you should have a couple of interesting things to run on your Daisypatch that may not be rocket surgery, but are really compelling. Let's get started!
Starting Small
I'd say that the very best place for us to start is by spending a few minutes with the video on the Oopsy software that my friend and colleague Graham Wakefield posted a few weeks back. It's a great visual overview of what Oopsy is all about, and I'll be reinforcing some of these ideas as we go along. (It’s funny, but taking in that same information feels quite different to me while staring at a Daisypatch and a folder full of example patches.) I did this very thing myself, to acquaint/reacquaint myself with "the lay of the land."
After I compiled and downloaded the example files and played with them (that reverb and the FM are pretty wonderful, by the way), I thought I’d try some stuff of my own. I’ve written some gen~-based tutorials of my own along the way, so I had a nice collection of things I figured I could repurpose as Eurorack modules. In a way, this installment is about the practice of porting patches to run on the Daisypatch.
Tips for Beginners
Once again, I’d like to start by sharing some tips to smooth your way — tips that might suggest to you that I made some silly mistakes getting started, let’s just say. Let’s get the “gotchas” out of the way first:
If you’re porting a gen~ patch to your Daisypatch module, it might not occur to you right away to be mindful of parts of your original gen~ patch that are associated with the sample rate MSP is using. Remember that the Oopsy software lets you change the rate at which your patching runs to 32kHz, 48 kHz. (the default), or even 96 kHz. If you’re like me and have grown used to MSP running at 44.1 kHz., you might be surprised when you finally work out why your 16-second delay line is actually only giving you 14.7 seconds of delay. (PSST! You forgot to change the argument that sets the size of your data or buffer operator!) That's definitely an error I made. Happily the basic gen~ operators (such as the mstosamps operator and the samplerate constant) automatically adjust for you. Keep an eye on those buffers, though….
When you’re creating, modifying, or porting your gen~ patch, make sure that every inlet and outlet is identified by a number that you only use once.
This one won’t confuse everyone, but it was an assumption that caught me by surprise, anyway: The audio output jacks on the Daisypatch are not DC-coupled. If, like me, you were accustomed to using an audio interface such as a MOTU FastLane to send control voltages to your Eurorack, it might not occur to you that the Daisypatch audio outputs work differently. The place I noticed it involved trying to send a fixed control voltage out one of the audio outputs and discovering that things weren’t working out as I expected (it’s not a problem at all with the CV out jacks, of course).
The CV and knob inputs update at a default block-rate of 1ms for your Daisy. The Oopsy help files suggest that you might want to add some filtering or smoothing for control voltages (the oopsy_field patch comments says to do so "...depending on how they are used in the patch.") There are a pair of very nice smoothing abstractions available to handle control voltages and knob inputs — a 10 Hz. two-pole lowpass filter (oopsy.ctrl.smooth2) and a three pole 30 Hz. lowpass filter (oopsy.ctrl.smooth3) for when you want to do a little more aggressive filtering from hardware input analog-to-digital convertors. With one exception I'll mention later on, I found that just adding an oopsy.ctrl.smooth2 abstraction on any CV or knob input was a good practice, generally speaking. You'll find that to be a general approach in the patches I'm going to be porting.
Don't give your parameters names that begin with a number. Oopsy will think it's dealing with a constant.
In my opinion, it's a really good idea to work with a consistent way of naming parameters. Although it's true that param ctrl1, param cv1, param knob1, kn1_<parameter name> or param cv1_<parameter name> will all associate a parameter with Knob 1, your life will be a good deal easier if you always use one way of specifying things (I'm a cv1_<parameter> person, myself).
Here are some patching tips for setting up your interface quickly and efficiently. Once coming up with a consistent naming convention becomes a matter or habit, you’ll be amazed at how rapidly you can work!
The first four in and out operators are associated with the audio in/audio out jacks on the Daisypatch front panel. But in addition, it’s good to get into the habit of remembering that you can use arguments to the param operator and to in and out operators to associate them with other input/output knobs and jacks on the front panel of your Eurorack module. Here are some examples:
param ctrl1, param cv1, param knob1, kn1_<parameter name> or param cv1_<parameter name> will all associate a parameter with Knob 1 on the Daisypatch module. The same works for all four knobs. If you don't use one of these naming conventions, Oopsy will grab the first four parameter names in alphabetical order and assign them to the four knobs.
You can identify the Daisypatch's GATE 1 and GATE 2 jacks as param gate1 and param gate2.
You can associate an out operator with another of the Daisypatch’s outputs using an argument to the out operator — out 5 cv1 associates the fifth out operator’s output with the CV 1 jack, out 7 gate1 associates the seventh out operator’s output with the GATE 1 jack, and so on.
MIDI input/output on the Daisypatch is converted to/from normalized byte streams that are identified using arguments such as in 5 midi or out 6 midi.
What happens if you have more parameters you want to control in your patch than what's afforded you by the Daisypatch hardware? Easy - you just add a param operator for them, too. There's one thing to keep in mind, though: those parameters will be listed in alphabetical order (upper case/lower case). While all the aliasing for the four knobs winds up appearing on the OLED display of your Daisypatch as kn1-kn4, using capital letters to specify the name of a parameter will list those parameters before the knobs, which may not be what you want. I tend to set up my parameter naming so that the knobs are all listed first and in order, followed by my other parameters. Your mileage may vary, of course.
My final bit of “advice to the beginner” isn’t about gotchas or naming conventions at all — I found that a little pre-planning goes a long way toward guaranteeing a patch creates a module that’s a joy to use. Taking a few minutes to sit down and plan out what parameters I wanted to assign to knobs and roughing out my interface meant that actually doing the patching/porting was not only quicker, but produced results that were compelling with my first download to the Daisypatch. I don’t know about you, but I always find it really satisfying when my initial UI design is such that I “live with” my patch/device for a while rather than redoing it right away - UIs that grow from the experience of using something and “getting it under your fingers” is always my goal.
Now - let’s port some gen~ patches!
My First LFO
For my first attempt at converting a patch to run on the Daisypatch, I thought I’d start simple: a single LFO based on what I created in this tutorial.
In that tutorial, I took my standard go-to MSP LFO patch and ported it to gen~. Here's what the original LFO ported to gen~ looked like:
For my first attempt, I decided to slim things down a bit to keep it simple — I thought I’d try to set my Daisypatch device up so that I only really had to have four parameters on knobs to start with. That meant that that I needed to trim things down a little bit.
Looking at my gen~ version of the LFO patch, I noticed that the patcher relied on an external phasor~. I'll need to replace that with an internal phasor operator controlled by a frequency parameter set using one of the knobs.
Next, I decided what other parameters I’d actually want to access from the front panel, which meant that I figured I might trim off some functionality from the original patch. Here's what I did:
I got rid of the phase offset
The low/high output scaling ranges were jettisoned, too
Since I was creating something that produced a single LFO waveform, I figured that I could get rid of the inverted ramp as a waveform choice (since inverting the output of the whole LFO would do the same thing).
I removed some interesting features, but I figured I could always go back and add them in later.
Then, I turned my attention to my param operators. Since I wanted the controls to make conceptual sense on my Daisypatch module (and since I knew about the naming and alphabetical parameter-to-knob assignments), I figured I ought to do some judicious param operator renaming. I decided to go with the cvN_<parameter_name> way of doing things, and to make sure that I didn't have any parameter names that came before lower-case "c" in the alphabet.
My parameters also needed minimum, maximum, and default values for each parameter using attributes. I decided to take Graham’s advice about smoothing input voltages, and so I added oopsy.ctrl.smooth2 abstractions to all of the param operators’ outlets.
I wanted an output in the 0. - 5.0 volt CV range, so some output scaling was in order. Since the out 1 cv1 operator will always map 0. - 1.0 gen~ signals to 0.v - 5.0v in Eurorack for me, the scale operator to add scales from 0- 1 (i.e. it's a scale -1 1 0 1 1 operator).
My original gen~ LFO patch included a nice trigger sent at the start of the waveform, which seemed like a useful feature to keep - I'd send that bit of the original patching out the GATE1 output on the Daisypatch. Since I’d spent some time with Graham’s intro video, I knew that it’d be good hygiene to use an oopsy.gate.min abstraction to make sure that all of my gates were output properly.
Here’s the result of that activity. Although I've tried to describe things in some detail, the work itself was almost entirely a matter of param object renaming and attribute addition:
I saved my patch, put my Daisy Seed into “receive mode” with a little button pushing on the Daisy Seed, and punched the big round button on my Oopsy bpatcher.
When the download was complete, I pushed in the rotary encoder on the Daisy front panel and turned it until I was my parameters displayed:
Looked good. Of course, the proof is in the hearing I patched the CV OUT1 jack to the cutoff of my trusty Filtare SEII, fed some yummy noise into it, and spent some happy moments enjoying some celebratory knobtwisty filtering.
This got me thinking a little about what kinds of LFO I didn’t currently have in my Eurorack. I realized that I’d been spending time recently using sync’d LFOs whose summed outputs were used for various control tasks. (I was doing this using MSP and my Expert Sleepers modules for this.) Staring at those displays, I realized that I could just grab that original MSP patch I was using, port it to gen~ (which is easy, as I described in the gen~ LFO tutorial). That would make a great addition to my module set, and doing it with Oopsy was really just a minor variation on what I’d just done.
One cup of coffee, a little gen~ patch copying (I took the basic gen~ patcher above and dropped it into a gen operator inside the gen~ patch I was working on), some decisions on the parameters I wanted, and I was on my way. First, I sat down and planned out what parameters I wanted as knobs, and what features my triple-summed LFO module would have.
The plan would be to have single main frequency that drove all three of my LFOs, with each individual LFO having
a multiplier-based variation on the main LFO frequency
a phase offset
a selectable waveform
an adjustable duty cycle for the square wave choice
the ability to invert the waveform output prior to summing
And — as long as I was at it, I thought it'd be cool to collect each retrigger gate for the three oscillators and output them as a single string of gates, too.
Apart from adding the phase offset back into my basic LFO patch, there was hardly any work to do at all.
The tripLFO.maxpat gen~ patch itself takes three copies of this patch, copies and modifies the param operators and reorders them to set which controls I want to be under the knobs. (I experimented with this a little bit. Being able to patch, save, and load again and again is really a joy.) Here’s what I wound up with:
The result of this patching gave me the following tweakable parameters for the patch:
kn1_mainfreq (knob 1)
kn2_mult1 (main frequency multiplier for LFO 1 - knob 2)
kn3_mult2 (main frequency multiplier for LFO 2 - knob 3)
kn2_mult3 (main frequency multiplier for LFO 3 - knob 4)
In addition, I had the following parameters available using the Daisypatch’s rotary encoder:
nvert (invert resulting waveform)
phase_1 (phase for LFO 1)
phase_2 (phase for LFO 2)
phase_3 (phase for LFO 3)
sq_duty1 (duty cycle for for LFO 1 when using square wave output)
sq_duty2 (duty cycle for for LFO 2 when using square wave output)
wq_duty3 (duty cycle for for LFO 3 when using square wave output)
wave1 (waveform select for LFO 1 - sine, ramp, triangle, square, random)
wave2 (waveform select for LFO 2 - sine, ramp, triangle, square, random)
wave3 (waveform select for LFO 3 - sine, ramp, triangle, square, random)
That’s a pretty full-featured set. It looked shipshape, so I saved, enabled the Daisy Seed, punched the button, and you guessed it. Wow!
Delaying Tactics
I decided to try leveraging a few more old gen~ patches I had laying around that I’d love to see as Eurorack tools. One of the features of gen~ I particularly love is - of course - its ability to use single-sample processing to create ringing delay lines. Do I have a patch for that? I do - created as part of this gen~ tutorial on delay lines, in fact.
Note: Synchronicity is wonderful. While I was writing this, I got a note from a friend out on the left coast of this country mentioning that he was interested in implementing this as a guitar pedal on the Daisypetal. (You know who you are, man. Shouting out to you!) That email just made me sure that I was on the right track, although what I was making might be a little different than a pedal incarnation of the gen~ patch. So I opened up my tutorial, and grabbed this patch:
This time, I'd be working with audio inputs, although some of the patch would have to be relocated for use on the Daisy.
There were a few small modifications and tweaks to make this one sing.
The major revision involved handling the keyboard input on the original patch, which was handled externally to the gen~ patch itself. I realized that I could replace the keyboard with a knob that selected a MIDI note number and used that to calculate the amount of delay from there. It was not only not very difficult, but the result looked quite a lot like my external patch minus some message boxing and dspstate~ stuff I could do with the samplerate constant, with a little smoothing courtesy of oopsy.ctrl.smooth3 thrown in:
My original patch named the history operators. That's fine in an ordinary gen~ patch, but naming the history operators here would mean that they'd show up as parameters, which I don't want. Time to un-name those history operators....
Oh — remember my comment about reminding yourself that the Daisypatch doesn't run at the usual 441 kHz. I usually use in Max? I can use the samplerate constant as an argument to the delay operator, and I'll never have to worry about setting the size of my delay operator ever again!
Finally, I thought I'd like to add a little clipping to the patch output. I usually use the ordinary clip -1 1 operator, but our mutual friend Graham Wakefield (to whom we really owe a pint) suggested something new: output saturation rather than clipping by using a * 0.5 > tanh > * 2 operator sequence. It's pretty nice and this tutorial's official "Graham pro-tip."
The rest of the patch was very similar to my tutorial patch.
Once again — patch saved, Daisy Seed enabled, button pressed, and I have a positively awesome ringing stereo delay that sounds amazing and is nothing like anything else in my rack.
Man, I am loving this workflow.
Repetition Is The Mirror Of Infinity
I was actually starting to feel a little guilty about pawing through my old gen~ patch pile and spending just a couple of minutes creating things that ran on my Daisypatch, but hey - I had some pretty nice stuff that I didn’t have on my Eurorack. The guilt passed and I turned my attention to the ever-popular N-second recirculating delay line (the “Fripp in the Box," as it is sometimes fondly called). Boy, that sure would be great to have in my rack, wouldn’t it? So I hunted around and found an example I’d created as a part of the gen~ for Beginners tutorial series - it was right there in Tutorial 4.
Well nothing too fancy here. In fact, I’ll hardly need to do anything at all. But wait. Given how much memory the Daisypatch has, how about a 32-second delay line? Sure - let’s see if it works....
Remember how I mentioned at the start of this tutorial that it was important to keep in mind that my Daisypatch modules would be running at 48 kHz. in stead of 44.1 kHz.? Yep, that's right - here's where I made my first big mistake of my porting frenzy. 32 seconds of delay at 44.100 kHz.
I took at run at the thing and thought, "That's weird. I've only got about 29 seconds of delay. What's the deal?"
It turns out that 32 seconds at 48000 samples per second will require a stereo buffer with 1,536,000 samples rather than 1,411,200 samples! D'oh!
How might I rescue myself from a lifetime of stupidity of this nature going forward? Our mutual friend Graham chimed in again with a brilliant solution that I will use hereafter: make the calculation for the length of the buffer a part of the data operator itself — Instead of trying to figure the number of samples forever, I could go with a data samplerate*32 2 operator. Cool, huh?
There was one other issue that I suppose I never had to think much about in MSP. I was getting some real glitching that it took a while (and the kind advice of my friend Graham Wakefield) to solve. Simply put, I needed to do some serious smoothing of the delay time parameter. Remember how those nice oopsy.ctrl.smooth operators are intended to deal with noisy hardware input? It turns out that even a small amount of noice amplified over the entire 32 seconds of the delay buffer builds up and causes a little trouble (even 1% noise will be a 1/3 second glitch). Certainly not something I ever run into working on a laptop.
Since I had a pretty long delay time, I added a pair of the more aggressive oopsy.ctrl.smooth3 operators in series.
Armed with the properly sized data operator, it worked like a charm once I’d set up the param operators for my user interface knobs (see fripp_in_the_box.maxpat in the patches used in this tutorial).
This patch is kind of interesting, if for no other reason that I now have lots of memory space for my delay. I think that I’m going to return to this patch at some point. My pal Darwin Grosse has this amazing recirculating delay matrix as part of the MODE plug-in collection that I have dearly missed. And there’s also that recent Forum exchange about changing the delay lengths on the fly without pitch change. Oooooh….
A Philtre (of Love)
I’m going to close this walkthrough and tutorial with one of the things that I Daisy-fied that I really love. Back in the day when gen~ was young and Great Sages scoured the interwebs in search of filter code that could be codeboxed, My Forum spelunking turned up a posting whose title plumbs the deep well of understatement: Found a very nice SVF this morning.
The State Variable Filter shared therein has been a favorite of mine ever since (with a minor addition I’ll show you in a minute), and has resided happily in my live performance laptop rig nearly ever since. Flushed with the success of my recent endeavors, I thought I might create a Daisypatch version of it (stkr_SVF.maxpat). Once again, it was really a case of grabbing the codebox version and duplicating it (since I wanted a stereo filter), working out my param operator labels, and… well, as long as I was at it I thought I’d add a lagniappe (“a little somethin’ extra,” as they say in New Orleans): the ability to crossfade between filter types. Here’s the basic patch I roughed out, with my addition there at the center right:
Yes, I know — the SVF has four output modes. At first, I thought that all I needed was a crossfade between lowpass, bandpass, and highpass outputs. But I suddenly realized that my Daisypatch had four outputs, so I could have a second stereo output that crossfaded lowpass, notch, and highpass output by just doing a little operator duplication.
And, as long as I was at it, I figured I'd replace my expr operators with sequences of math operators (It's a personal thing I'm doing at the moment). The other bit is my non-use of the pi constant that dates from my childhood days of learning that π = 3.14159 and the delight I always felt when typing those 5 decimal places as a way of feeling as though I understood the world, somehow.... You might have simply transformed the expr sin(in1*3.14159) operator in the version above to use the pi constant in gen~ and go with expr sin(in1*pi) as you ported without giving it a thought.
You'll notice that I also replaced my clip operators Graham's little saturation hack. Here's the result:
One Last Trick Before I Go
So I've installed my toolchain, got it working, and ported/modified a couple of patches to do some cool stuff in my rack. There's just one more little trick to perform: the Metapatch. This one's as wonderful as it is stupid simple: All I do is to take the four gen~ objects from the patches I've created, add a name to each of them by typing an argument into the gen~ object's message box, and then drop all four of those now-named gen~ objects into a new patch with an Oopsy bpatcher in it, and renamed it as My_New_Toolbox.maxpat:
And — that's right, you guessed it — I set my Daisy Seed to receive, punch the button, and all four of my programs are loaded to the Daisypatch, where I can use the rotary encoder to choose between them. Pretty sweet!
Well, I think it's time to pour a celebratory libation, take a rest, and enjoy my new Eurorack stuff! I hope that this manages to communicate a little of my enthusiasm for the Daisypatch, that it gives you a little help getting set up, and that these humble patches whet your enthusiasm.
Learn More: See all the articles in this series
by Gregory Taylor on 2021年3月2日