Norns: R engine

There were previously some “hacks” or “tutorials” for R - are those still available somewhere?

1 Like

yep, thank goodness! r is pretty wonderful stuff

8 Likes

so i’ll be on vacation the next few days and plan to compare docs for R and crow to see how they could be used together

these tools are capable of so much!

6 Likes

@jah would it be possible to add phase as an input to sineosc ? this seems like a very flexible way of doing FM synthesis inside r. if so maybe I’ll try to implement it ?

thoughts ?

(after initial research looks like it’s pretty doable, I’ll probably go for it)

Hi Andrew,

Feel free to add inputs or features to modules in the R engine.

The MultiOsc module (SineOsc is just an optimized version of MultiOsc) is feature wise inspired by this Doepfer module: http://www.doepfer.de/a100_man/a110_man.pdf.

One difference is that no sync input is available on the MultiOsc module.

For FM synthesis, though, couldn’t you just use the frequency modulation (FM) input on MultiOsc for this? I’m not sure what you mean by phase.

Addition: Also, there’s a FMVoice module based on the sound generation part of my old (obsolete) Gong engine. This module is not tested much yet. And I should get around to add more documentation to all R modules.

2 Likes

would an osc going into the FM input produce linear FM or exponential FM ? (/ are frequencies specified in hz or v/oct? can’t yet tell from reading source so far)

if it’s linear then yea that would work for FM synthesis, though having a PM input would be a nice since you wouldn’t need to scale index * modulator frequency (or however it goes)

It’s exponential in MultiOsc, linear in the filter and freq shifter. But that is not the only difference between PM and FM. (I’d say not the most important difference, in this context.) if you attach an envelope to PM input you get pretty much nothing.

yea, I’m reading that frequency is specified exponentially as 0.1/oct

I’d propose an additional liner FM input for all oscillators and also a PM input to sineosc to cover all bases

I can add that all, seems pretty doable

Thanks, I’ll look into it. (Or, if you’re inclined, feel free to make a PR) FM/PM is not my area of expertise.

Just as an aside, also undocumented, after a refactor I made a while back it’s a bit simpler to run R in SuperCollider, for instance on your regular computer. These are the commands involved to set up and control modules similar to the 1_square tutorial script:

(
s.waitForBoot {r=Rrrr.new};
)

r.newCommand("Osc", "PulseOsc");
r.newCommand("SoundOut", "SoundOut");

r.connectCommand("Osc/Out", "SoundOut/Left");
r.connectCommand("Osc/Out", "SoundOut/Right");

r.setCommand("Osc.Range", -1);
r.setCommand("Osc.Range", 0);
r.setCommand("Osc.Range", -2);

r.setCommand("Osc.Tune", -600)
r.setCommand("Osc.Tune", 423)
r.setCommand("Osc.Tune", 0)

r.setCommand("Osc.PulseWidth", 0.25);
r.setCommand("Osc.PulseWidth", 0.5);
r.setCommand("Osc.PulseWidth", 0.75);

Some other features are hidden in the latest version too (visuals = a kind of poll for specific module values). I’ll try to add some docs this weekend.

cool, maybe I’ll try out a PR soon and see how that goes. I have a couple more ideas for modules too

  • slew rate limiter (using VarLag, probably?)
  • decimator
  • panner
  • wide bandpass filter

(progress)

2 Likes

This thread prompted me to have poke around Moln and see what sort of simple FM was currently possible with R.

As a quick experiment, I added a few lines of code to see what would happen. It didn’t take long for things to get wild with cross-modulation :slight_smile:

Moln_FM

Basic controls for FM are in the parameters menu. If I have time, I’ll add a better way to interact with these.
@jah - R is amazing and deep. Thank you!
@andrew - I’m looking forward to your updates!

3 Likes

@liquid_citizen i think u would enjoy my fork rn

image

(srry for the head tilt)

7 Likes

Awesome! FWIW: regarding UI. there’s a moln update coming along that exposes all parameters in script mode (K2 / K3 switches between pages). It’s in this script bundle: https://github.com/antonhornquist/roar (this is alpha stage and requires the latest norns update).

2 Likes

Looking for a little assistance… I’d like to have a separate ADSR for both Amp and Filter Frequency (using MMFilter)

so I have the following but I don’t get any change in filter frequency when I gate the envelope.

Pretty sure i’m missing something obvious but I’m stuck for the moment.

  engine.new("Env", "ADSREnv")
  engine.new("FEnv", "ADSREnv")
  engine.new("EnvFilter", "MMFilter")
  engine.new("Amp", "Amp")
  engine.new("SoundIn", "SoundIn")
  engine.new("SoundOut", "SoundOut")

  engine.connect("SoundIn/Left", "EnvFilter/In")
  engine.connect("SoundIn/Right", "EnvFilter/In")
  engine.connect("EnvFilter/Bandpass", "Amp/In")
  engine.connect("Env/Out", "Amp/Lin")
  engine.connect("FEnv/Out", "EnvFilter/Frequency")
  engine.connect("Amp/Out", "SoundOut/Left")
  engine.connect("Amp/Out", "SoundOut/Right")

-- then later a gate is sent
  engine.set("FEnv.Gate", 1)

Then of course I discover the SC tab in maiden is throwing some errors… Investigating further.

Ok - maybe I need

engine.connect("FEnv/Out", "EnvFilter/FM")

instead?.. but not clear on what the MMFilter FM input is looking for

‘EnvFilter/FM’ ? and set FM = 1 ?

haven’t checked to source or docs but essentially parameters are not interchangeable with inputs, might be getting them mixed up

dorp u just wrote that

yeah - I was mixed up for sure.

but… i think I got it sorted now. EnvFilter/FM is 0 to 1 (formatted as percentage for params).

now to figure out how that interacts with MMFilter.Frequency :thinking:

var frequency = frequencySpec.map(frequencySpec.unmap(param_Frequency) + (sig_FM * param_FM));

I’ve just been skimming the source code and it’s answered most of my q’s - not to bad to search around it even if ya don’t know much SC (= me) : ) the modules themselves are pretty darn simple

but to answer yr q I’m pretty sure across the board Frequency in specified in hertz and added to /FM * .FM (the input is in 0.1/oct and the parameter is range 0-1).

you might’ve solved this already but to control oscillator frequency route a FreqGate to /FM (and set .FM = 1 for tracking)

Hi,

The confusion here is probably due to the fact there’s both an input and parameter named FM for the MMFilter module.

To modulate filter frequency you both need to connect one (or more) signals to the FM input and set how much the incoming signal(s) should modulate the frequency by setting the FM parameter to a non-zero value.

Ie:

engine.new("LFO", "MultiLFO")
engine.new("Osc", "PulseOsc")
engine.new("Filter", "MMFilter")
engine.new("SoundOut", "SoundOut")

engine.connect("LFO/Sine", "Filter/FM")

engine.connect("Osc/Out", "Filter/In")

engine.connect("Filter/Lowpass", "SoundOut/Left")
engine.connect("Filter/Lowpass", "SoundOut/Right")

engine.set("Filter.FM", 0.5)

(remember that inputs are referred to with [ModuleName]/[InputName] notation (ie. Filter/FM) whereas parameters are referred to with [ModuleName].[ParameterName] notation (ie. Filter.FM).

2 Likes

Thanks for chiming in @andrew ! I’ve prioritized adding features over docs for now, but I’m going to shift focus for a bit and will be adding more documentation to existing features.

In the meantime:

What the code block you posted above means is the frequency set via the main frequency parameter (ie engine.set("Filter.Frequency")) is converted from its parameter range (0.1 ... 20000 Hz) to 0 ... 1.0, the external modulating control signal sig_FM typically ranging from -1.0 ... 1.0 (ie. “Filter/FM” in engine.connect("LFO/Sine", "Filter/FM")) is then added to this normalized cutoff frequency control signal (after being attenuated by the FM parameter - the one set in engine.set("Filter.FM")). The resulting control signal is then converted back from the normalized 0 ... 1.0 range to 0.1 ... 20000 Hz and used to control the filter UGen.

The modulation is thus determined both by the original control signal (which varies depending on module), the control signal attenuator and the parameter range mapping. Exactly how it translates is a bit involved.

Users should not really need to worry about details, it should be in the docs.

Anyhow - if you @okyeron want to know what the filter frequency is at any time after modulation (for debugging or visualization purposes) you can use a new undocumented feature called, uh, visuals. Each module can expose one or more visuals which are just data polled back to the lua script layer. The actual frequency of MMFilter has a visual called… yeah… Frequency. If your filter is called Filter use engine.pollvisual(0, "Filter.Frequency") and the poll called poll1 will be used to feedback cutoff frequency to lua layer.

For an example of visuals see bob : https://github.com/antonhornquist/roar/blob/master/bob.lua, r details in https://github.com/antonhornquist/roar/blob/master/lib/r_bob.lua (beware, alpha stage code)

FYI the MMFilter module is feature-wise inspired by this module: http://www.doepfer.de/a100_man/A121_man.pdf

2 Likes

Fantastic. I’ll play with this soon. Thx!!

Docs note - readme still mentions a bandpass filter module (BPFilter) , but that’s not in the lib (anymore?).