Newbie: .range question

``````(
SynthDef("wow",
{arg freq = 60, amp = 0.1, gate = 1, wowrelease = 3;
var chorus, source, filtermod, env, snd;
chorus = freq.lag(2) * LFNoise2.kr([0.4, 0.5, 0.7, 1, 2, 5, 10]).range(1, 1.02);
source = LFSaw.ar(chorus) * 0.5;
filtermod = SinOsc.kr(1/16).range(1, 10);
env = Env.asr(1, amp, wowrelease).kr(2, gate);
snd = LPF.ar(in: source, freq: freq * filtermod, mul: env);
Out.ar(0, Splay.ar(snd))
)
``````

"The source sound LFSaw.ar takes the variable chorus as its frequency. In a concrete
example: for a freq value of 60 Hz, the variable chorus would result in an expression like:

60 â [1.01, 1.009, 1.0, 1.02, 1.015, 1.004, 1.019]

Question: How do you get those values in the string? [1.01âŚ ]?

I think I donât see how that range command is working in relation to the string:

LFNoise2.kr([0.4, 0.5, 0.7, 1, 2, 5, 10]

60 * 0.4 = 24?! How can the answer be 1.01?

1 Like

60 * a list of random values generated by LFNoise2 between 1 and 1.02. Makes the list of frequencies. The list given as example, could have been different values from 1 to 1.02.

The list [0.4, 0.5, 0.7, 1, 2, 5, 10], takes care of the rate of random values generated by LFNoise2. That list is given and fixed.

Is that correct?

Hello and welcome,

you are refering to the example on page 95 / 96 in Bruno Ruviaroâs tutorial:

It"s meant that the frequency values at any time could look like this, they must be in the range 1 - 1.02.

Yes, itâs the (LFO) frequencies of (audio) frequency changes.

Greetings

Daniel

2 Likes

Thanks.

A similar question

``````
(
{
SinOsc.ar(freq:
EnvGen.kr(Env.new(
[ 0, 0.5, 0, 0.3, 0.3, 0, 0.1, 0, 0.2, 0.3, 0.15, 0.5, 0.25 ],
[1, 0.5, 0.5, 2, 0.7, 1, 0.3, 0.6, 0.5, 0.8, 0, 0.4])
) * 1000 + 200
)
}.play
)

``````

Question: Why does the following not work?

``````
(
{
SinOsc.ar(freq:
EnvGen.kr(Env.new(
[ 0, 0.5, 0, 0.3, 0.3, 0, 0.1, 0, 0.2, 0.3, 0.15, 0.5, 0.25 ],
[1, 0.5, 0.5, 2, 0.7, 1, 0.3, 0.6, 0.5, 0.8, 0, 0.4])
), mul: 1000, add: 200    )
}.play
)

``````

SinOsc outputs samples between -1 and +1 at the sample rate of your system (typically 44100 Hz). The freq argument specifies how many times per second you will get a sine shape (full cycle). Mul and add are applied to the range of output samples (btwn -1 +1), not to the frequency.

In your second example, you are outputting samples in the range -800 +1200, and the frequency of your sine follows the (unchanged) values coming out of the envelope, so your freq is constrained between 0 and 1.

1 Like

At the moment you think you grab the idea, you loose it againâŚ

I thought using multiplication (*) and addition (+), was just a other way of writing down the mul and add value.

So, my assumption that 1000 is the mul and 200 is the add of the SinOsc in this code is wrong?

The values in the array are used for frequency in this patch right? Those values are multiplied by 1000 and 200 is added. So the lowest or base frequency is 200 (0 * 1000 + 200). That is my understanding of it.

The example does `EnvGen.kr(...) * 1000 + 200`, not SinOsc.ar(âŚ) * 1000 + 200.

Those math ops are inside the SinOscâs parentheses, so they apply to an input to the UGen. With the given syntax, they cannot apply to the result.

hjh

1 Like

No worries! You are right that mul and add are literally just doing a (*) and a (+). The source of confusion here is what exactly is being multiplied and added. As James explained above, it has to do with what you are multiplying and adding to.

Watch in the Post window what numbers SinOsc is generating. They are between -1 and +1. We know that SinOsc.ar is generating 44100 samples per second (because of the .ar, audio rate; and assuming thatâs the sample rate of your system), but poll prints only 10 of those per second to display:

``````{ SinOsc.ar(freq: 1).poll }.play;
``````

Notice that changing the freq only alters how many full cycles of up and down take place; the range of the output (-1 +1) remains the same.

Here, one full cycle every 10 seconds:

``````{ SinOsc.ar(freq: 1/10).poll }.play;
``````

When you use mul and add, you are multiplying and adding that output range, it has nothing to do with the freq of the oscillator itself:

``````{ SinOsc.kr(freq: 1/10, mul: 1000, add: 200).poll }.play;
``````

I changed from .ar to .kr above because didnât want this to make a loud pop going to the speakers. A huge mul in a sound-producing oscillator effectively means a huge amplitude).

The above is the same as:

``````{ (SinOsc.kr(freq: 1/10) * 1000 + 200).poll }.play;
``````

âŚwhich is also the same as:

``````{ SinOsc.kr(freq: 1/10).range(-800, 1200).poll }.play;
``````

Again, the point of all the above is, the mul and add have nothing to do with freq. Back to your other example with EnvGen, the reason the first one does what you expect and the second does not is that in the first you do the scaling (* and +) applied to the EnvGen, so that it is the EnvGenâs original output range 0-1 that is converted to a new range (desired audible frequencies). In the second example, by using mul and add of the SinOsc, you are effectively altering the amplitude of the SinOsc (its output), not its frequency.

Hope this helps.

Bruno

1 Like

Thanks a lot for the clear answers. Helps me a lot!

edit:

``````{SinOsc.ar(400 + SinOsc.ar(7, mul: 5))}.play

{SinOsc.ar(SinOsc.ar(7, mul: 5) + 400)}.play

``````

These seems to be the same. Hmm
So mul and add has nothing to do with frequency. Itâs scales the output range. Hmm.

I just realized what the confusion might be:

``````// mul/add *inside* parens

=

// math ops *outside* parens
SinOsc.ar(freq, 0) * mul + add
``````

âmulâ and âaddâ apply to the result of the associated UGen â so the corresponding math operators must be outside the unit.

So mul and add has nothing to do with frequency. It scales the output range.

Yes, the output range of the UGen to which the specific mul/add belong.

âHas nothing to do with frequencyâ â consider, though, that SinOsc has two inputs (frequency and phase): `SinOsc.ar(freq, phase, mul, add)` â so, if you had expected mul and add perhaps to apply to the frequency, why would it necessarily be frequency and not phase? OK, âI wasnât using phaseâ but the SynthDef compiler canât change the meaning of mul/add based on what you used/didnât use (`SinOsc.ar(mul: 2, add: 1)` â then what?). Phase is actually closer in the argument list, so thatâs just as good a guess as any.

That relationship at best would be syntactically ambiguous â and so, unacceptable. It just canât work that way.

A mul/add applying to frequency would have to be 100% contained within the âfreqâ slot (between open-paren and comma): `SinOsc.ar(SomeUGen.kr(blah, blah, mul: freqmul, add: freqadd), ...)`.

Itâs been proposed, btw, to discourage the use of âmulâ and âaddâ in favor of the math operators. This thread provides some evidence in favor of that proposal: it can be confusing to have two ways to write the same thing. (TL;DR: You could save some of these headaches by forgetting about mul/add altogether and just using â*â, â+â, and range mapping methods like range, exprange, linlin, linexp.)

hjh

Mul and add do seem like relicsâŚ * and + make for more legible codeâŚ

Thanks again!

Is Amplitude Modulation done with multiplication and Frequency Modulation with addition?

I did also start with puredata, I hope it helps me understand sound manipulation better. The fact that it is more visual might help me.

Oversimplifying a bit, but yes â amplitude scaling (volume control) is always multiplication, and the standard definition of frequency modulation is:

carrier freq = freq + (freq * modulator * index)

But I usually reduce that to:

carrier freq = freq * (1 + (modulator * index))

(In SC, it doesnât make much difference â I believe both of these expressions compile to one â*â and one âMulAddâ â but in Pd/Max, the 1+ formulation means one fewer connection to make.)

hjh

``````{SinOsc.ar(SinOsc.kr(5, 400, 800), 0, 0.3)}.play
``````

This is more a âsoftware trickâ then a sound modulation right?
There are not two oscillators here modulating each other. Itâs the OOSE software which makes use of itâs objects to generate frequencies from 400 to 800 hz, 5 times per second. Thatâs why this is a common sound in SuperCollider and less so in Pure Data. Itâs a result of how the software is designed and worksâŚ (?)
Put it differently, the second SinOsc has more a sequencing role, then a sound modulation role.

no Idea what you mean by software trick! the whole enterprise is a software trick I supposeâŚ

FWIW you have indeed specified two oscillators and the inner one is modulating the outer one (around a center frequency of zero though!

Try it like this:

{SinOsc.ar( freq: 400+ SinOsc.kr( freq:37, mul:100), mul:0.1))}.play

a quick correction: when using SinOsc with positional parameters the second is phase (in radians) which you had set to 400 in your example which doesnât make a ton of sense!

a nice way to set the range of a modulator is using the â.rangeâ method like so (note that weâre multiplying here)

(SinOsc.ar(freq: 400 * SinOsc.kr(25).range(0.9,0.9.reciprocal), mul:0.1)

for full on FM sound

(SinOsc.ar(freq: 400* SinOsc.ar(137).range(0.9,1.1),mul:0.1)}

The reference to Pure Data suggests perhaps some confusion about the term âcontrol rate.â

In Pure Data, âcontrolâ objects are driven by messages coming into an input (except objects like [metro] that generate control messages). No input message, no result â control objects can sit idle when not triggered. These are analogous to âlanguage-sideâ operations in SC.

In SC, a âcontrol-rateâ UGen is a unit generator, just running at a slower rate. With default settings, a kr unit produces one output sample per 64 audio samples. But it isnât different in kind, only in speed.

SuperColliderâs kr UGens have no analogue in Pure Data:

• SC language side ops â Pd control messages
• SC .ar units â Pd audio signals
• SC .kr units â donât exist in Pd

So you canât use Pd concepts to read them.

Anyway this is just confirming what semiquaver said: your expression does contain two oscillators, running at different rates. It does not consist of an audio oscillator and a message-driven control object masquerading as an oscillator.

Put it differently, the second SinOsc has more a sequencing role, then a sound modulation role.

If you say that, then you would have to say itâs âsequencingâ 689 values every second (which you wonât hear as discrete values) â and then you would also have to say that SinOsc.ar in some input is âsequencingâ 44,100 values every second. But you wouldnât say that. Both are modulation.

Btw SC applies some smoothing when you put a kr unit into the input of an ar unit, so that you donât hear step noise.

hjh

I suppose in Pd you could use a subpatch with a [block~] object to run at slower than audio rate, and this would be an analogue to kr units. But Iâve never seen anyone actually do that ([block~] is often used for oversampling, where kr is âunder-samplingâ).

Except for the 400-radian phase offset (which semiquaver noted), this should be a literal translation into Pd of `SinOsc.ar(SinOsc.kr(5, /*40*/0, 800), 0, 0.1)`.

hjh