Тёмный

SuperCollider Tutorial: 6. Iteration 

Eli Fieldsteel
Подписаться 12 тыс.
Просмотров 30 тыс.
50% 1

A cursory look at iteration and its use in the language and on the audio server.

Опубликовано:

 

28 сен 2024

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 43   
@UnfinishedIdeas
@UnfinishedIdeas 2 года назад
I know supercollider tutorials are pretty niche, but how are these not viewed more? These tutorials singlehandedly made me decide to pick up this language
@dotsonpaper
@dotsonpaper Год назад
Chuckled at the sneaky Tristan chord. Great tutorial.
@elifieldsteel
@elifieldsteel 11 лет назад
I sure did. Nicely spotted!
@Alextkirk
@Alextkirk 11 лет назад
Wow... did you made the Tristan Chord on purpose? haha
@elifieldsteel
@elifieldsteel 9 лет назад
***** Thanks. That's not a mistake, however. [0,1,2,3,4].do and 5.do perform the same action. The difference is that do returns its receiver. In the first case, the array is returned after the five integers are posted. In the second case, the integer 5 is returned instead. This behavior is really just a side effect of how do works, so I understand why it seems like a mistake at first glance.
@andrewfriend4210
@andrewfriend4210 11 лет назад
Yes, great work Eli - thank you very much"
@christiancasey4080
@christiancasey4080 7 лет назад
Hey, quick question. In the construction: [ array ].do{ arg item, count; ... } how does the interpreter know what to do with "item" and "count"? Or rather, how does it know what information it should assign to each argument? Thanks in advance, I really appreciate these tutorials.
@elifieldsteel
@elifieldsteel 7 лет назад
Chris Casey Simply, it's designed that way. The first argument in a 'do' function always represents the ordered content in the collection, and the second is always an integer index, starting at zero.
@christiancasey4080
@christiancasey4080 7 лет назад
Eli Fieldsteel Thank you so much! :)
@warpirecords3767
@warpirecords3767 2 года назад
Why is "Rand" piled up in brackets? I've always been curious about the meaning of these brackets.
@elifieldsteel
@elifieldsteel 2 года назад
The curly braces delineate a function, and in combination with duplication (the ! symbol) it's a technique for creating an array of random numbers. When a function is duplicated, it is evaluated once for each item to be created in the resulting array. When the contents of the function are random, we get multiple uniquely-generated random numbers: {rrand(0,9)}!6; If the curly braces are removed, we create one random number, and then duplicate it: rrand(0,9)!6; The implications for synthesis should be obvious here - if we want a cluster of different random tones, the curly braces are necessary.
@filippogonteri4439
@filippogonteri4439 9 лет назад
Hi Eli, thank you for your tutorials now I can see the light! Funny mistake on 4.22 you had to type 4.do{arg item; item.postln};
@filippogonteri4439
@filippogonteri4439 9 лет назад
shame on me ;-)
@cardboardmusic
@cardboardmusic 8 лет назад
Hi Eli, I'm curious to ask why you use Rand within {curly brackets}, is it really important, or just a 'sugar' syntax, as you sometimes say. I tried with/without and couldn't hear any difference, but did wonder if I was missing something. I also looked up Rand in the help-files and see that its used - in the example - in the same way as yours, but without curly brackets. Thanks in advance.
@elifieldsteel
@elifieldsteel 8 лет назад
I discuss the difference between rrand and Rand, as well as the use of curly braces, in tutorial 5, between 8:10 and 10:35.
@cardboardmusic
@cardboardmusic 8 лет назад
Thanks Eli, I just checked those references out, much clearer now (and very interesting also).
@mudithead
@mudithead 10 лет назад
Hi Eli, I had a basic question! I want to make a gated synth, like take for the example you create around 8:20 in this video.. How can I do that?
@elifieldsteel
@elifieldsteel 10 лет назад
Instead of Env.perc, use Env.adsr, which will sustain as long as the gate argument is positive.
@enricoborba9259
@enricoborba9259 8 лет назад
At 13:15, couldn't you iterate over something like 0.dup(num) as a solution to a variable number of synths passed as an argument? It's not nearly as efficient (i presume; i'm not sure how supercollider implements this).
@elifieldsteel
@elifieldsteel 8 лет назад
+Enrico Borba No, that would run into the same problem. It is not possible to modulate the size of UGen Arrays while the Synth which contains these Arrays is running on the server. UGen Array size is fixed and determined at SynthDef load time. Solutions include either creating different SynthDefs with different iteration counts (one with 10.do, one with 20.do, etc), or eliminate the iteration within the SynthDef altogether, and instead use iteration to create multiple Synths from a non-iterative SynthDef.
@largo17
@largo17 7 лет назад
I am extremely new to this so I might be babbling but wouldn't it be possible to have a argument that limits the volume of the iteration if bigger than? as in: if ( count
@elifieldsteel
@elifieldsteel 7 лет назад
It's a good thought, certainly, but because SC is an audio programming language, familiar problems can't always be solved by approaches that might be applied in more traditional programming languages. By design, the server (scsynth) can't dynamically change the size of arrays in UGen functions, once these functions are built and sent from the language (sclang). One apparent consequence of this design is that numChannels arguments within buffer-playing UGens can't be modulated. So, the operation 0.dup(num) gets evaluated once, when the UGen function is built, but can't be updated with set commands while playing on scsynth. Operations like if, do, collect, etc. are similar. They're language-side methods that the server doesn't understand. There are relatively simple workarounds to this seemingly annoying design choice, e.g. create a SynthDef that plays one partial and dynamically create and remove multiple Synth instances, or create a SynthDef that plays a fixed number of partials with a literal Array argument for setting individual partial amplitudes, etc. I see users encounter problems related to this feature from time to time, and usually direct them here to start: supercollider.github.io/tutorials/If-statements-in-a-SynthDef.html From the SynthDef help file: "It is important to understand that although a single def can provide a great deal of flexibility through its arguments, etc., it is nevertheless a static entity. A def's UGen graph function (and the SC code within it) is evaluated only when the def is created. Thus statements like while, do, collect etc. will have no further effect at the time the def is used to create a Synth, and it is important to understand that a UGen graph function should not be designed in the same way as functions in the language, where multiple evaluations can yield different results. It will be evaluated once and only once."
@JosePablo24
@JosePablo24 6 лет назад
why do you sometimes choose .range but other times .exprange?
@elifieldsteel
@elifieldsteel 6 лет назад
It depends on the units of the mapped parameter and/or how I want to distribute values within the range. For example, our perception of pitch is logarithmic (octaves "sound" equally spaced and appear equally spaced on the keyboard, but have exponentially increasing frequency values). So, if we are using a UGen to generate MIDI note numbers, range typically makes sense for an equal (linear) distribution and a more "natural" distribution. But, for generating frequency values, exprange makes more sense. Consider what would happen if we used range for an LFNoise1 generating frequency values between 20 and 20,000. Approximately half the values would fall in the upper half of the range, which is only one octave (10-20k). But there are nine more octaves below! So we'd hear disproportionately more high frequencies and fewer low/mid frequencies. A similar phenomenon happens with decibels, for which range is usually appropriate, vs. normalized amplitude values, for which exprange is usually more appropriate. Try the difference for yourself and listen closely!
@duvanfernandoarcosguzman6120
@duvanfernandoarcosguzman6120 7 лет назад
hi, thanks for the tutorials, i have a question related to this topic, i have a code that works, but i have to write an argument that i actually dont use, but if i delete it, the code stops working i dont know why: can you explain why i have to write arg item in this case? ( SynthDef.new(\pulso, { arg freq = 440; var sig, env; env = EnvGen.kr(Env.perc(0.05,4)); sig = SinOsc.ar(freq,mul:env); sig = Splay.ar(sig); Out.ar(0,sig)*0.25; }).add; ) ( ~miSerie = Array.series(12,60); ~miSerie = ~miSerie.scramble; ~miDuraciones = Array.newClear(12); ~miDuraciones.collect{ arg item, count;//here ~miDuraciones[count] = (~miserie[count]-59)*0.5; }; ~miSerie.collect{ arg item, count;//here ~miSerie[count] = ~miSerie[count].midicps; }; ~miSerie.postln; ~miDuraciones.postln; ~serieDod = Pbind(\instrument, \pulso, \dur,Pseq(~miDuraciones,1), \freq, Pseq(~miSerie,1)).play; )
@elifieldsteel
@elifieldsteel 7 лет назад
Iteration methods (do, collect, select, reject, etc) operate on a receiver, usually a collection (e.g. Array) and pass up to two arguments into their function. These arguments represent the current object from the collection, and the current integer index of that object, and are interpreted *in that order*. We gain access to these two values by declaring two arguments (e.g. "arg item, count") at the start of the function. We can name the arguments whatever we like (e.g. arg a, b; arg foo, bar), but the important point here is that the arguments are interpreted to have these meanings in this particular order. So, if we only declare one argument, it is interpreted as the object within the collection, and we have no access to the index within our function. If we want access to the index, we must declare two arguments, even if we don't plan on using the first one. A bigger problem is that, based on your approach, you are using "collect" in a case where it would be more appropriate to use "do". Remember that "do" returns its receiver, but "collect" returns a modified receiver, based on what happens inside the function. So, it is usually inappropriate/wrong to use the equals sign to update values within a collect function. When using collect, usually you will store the result of the collect operation in a new variable. With the approach you've chosen, you can simply replace both instances of "collect" with "do", and everything seems to work just fine. If you replace collect with do, then the receivers of do are simply counters that make sure the correct number of iterations occur. The actual value modification process occurs within the function, so technically, you could replace do's receivers with the number 12 -- which I think looks a little clearer. I still think collect is a more sensible choice, because it allows your array-creating code to be written more succinctly. Here's how I would do it: ~miSerie = Array.series(12,60).scramble; ~miDuraciones = ~miSerie.collect({arg item, count; item-59*0.5}); For extra brevity, you can also use the underscore character for the so-called "partial application syntax": ~miSerie = Array.series(12,60).scramble; ~miDuraciones = ~miSerie.collect(_-59*0.5); You also have one instance of ~miserie where you meant to type ~miSerie.
@duvanfernandoarcosguzman6120
@duvanfernandoarcosguzman6120 7 лет назад
Very fast and clear answer, Thank you very much!
@dver89
@dver89 7 лет назад
Hey Eli, thank you so much for these videos! Quick question: If I want to sonify an array of data by iterating over my array and using the array values as frequency arguments for a SinOsc, can I do that with .do? I've done it using Pbind without much trouble, but when I try it with .do, I'm not having any luck.
@dver89
@dver89 7 лет назад
Just got it figured out using a task!
@snugglepuff33
@snugglepuff33 5 лет назад
These videos are a complete godsend. Thanks for doing these!
@m4tk4p
@m4tk4p 5 лет назад
Hi, at ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-bMGXYEg1gJo.html what's a better way than doing language side looping?
@poguri27
@poguri27 3 месяца назад
Great videos. If you want to change number of partials via parameter, such as for additive synthesis, i figured it would be easier to just set a max nimber of partials, like 64, and then have an argument which simply controls which partials are silent via multiplication by 0. Avoids having to update synthdefs or create array of synths. This way you can have a knob or something that dynamically changes partials over time which could be cool.
@elifieldsteel
@elifieldsteel 3 месяца назад
True, this is definitely a viable way to do additive synthesis!
@Alextkirk
@Alextkirk 11 лет назад
Clap, clap, clap... ok with this video you covered a lot of things I did not knew... I´ll probably have to really study what you showed here. Thanks a lot for the great service you´re providing us here! =D
@dschkn
@dschkn 3 года назад
didn't quite understand the line sum = sum + temp;
@elifieldsteel
@elifieldsteel 3 года назад
It's a common way of applying a sequence of operations to a value, while overwriting the named container each time an operation is performed. In this case, we're calculating the sum of 'sig' and 'temp,' and storing the result in the 'sig' container, overwriting the old value. A simpler example of the same syntax looks like this: ( x = 4; x = x.pow(3); x = x - 7; )
@REYFlorian31
@REYFlorian31 7 лет назад
A great THXXX for all your videos on SC3 !
@HighlandViolinist
@HighlandViolinist 6 лет назад
"WARNING: SynthDef iter2 too big for sending. Retrying via synthdef file"
@elifieldsteel
@elifieldsteel 6 лет назад
This message occurs when you include tons and tons of UGens in your SynthDef. I think it means that your SynthDef is too big to stream to the server via add/send, so it creates a SynthDef file on your computer instead. You can try making your SynthDefs smaller or more efficient, or just ignore the warning. I get this message from time to time, and it doesn't interfere with my ability to create Synths from the SynthDef.
@octaviogaspar2704
@octaviogaspar2704 10 лет назад
Thank You Very Much : )
@Λ.Κ
@Λ.Κ 3 года назад
Hello :) I build a synthDef in which I have many iteration blocks, similar to your examples. I then add all to one output variable. I wonder if I can attach to each iteration cycle, an envelope. Meaning, if I have 3 iteration cycles, with different UGens, I would like to have different attack,sustain,release for each one. It seems that when I add them all to one output(the one in your example called "sum") these envelope instances cannot occur in the output. I understand there is the option to have just one envelope, of the final signal. But I also search ways to maintain an envelope of each iteration block alive, even after the addition of them to one audible event. So as to have different fade In and fade Out times for each UGen, although I treat them as one final signal. This is what my examples looks like, but the separate envelopes don't work: ( SynthDef.new(\iterDef, { var env1,env2,env3,inst1,inst2,inst3,cycl1,cycl2,cycl3,output,freq = \freq.ir(130); env1 = Env.linen(\att1.ir(0.5), \sus1.ir(1), ls1.ir(4)).ar(doneAction:0); env2 = Env.linen(\att2.ir(5), \sus2.ir(1), ls2.ir(1)).ar(doneAction:0); env3 = Env.linen(\att3.ir(2), \sus3.ir(2), ls3.ir(3)).ar(doneAction:0); cycl1 = 0; cycl2 = 0; cycl3 = 0; 4.do { arg counter; var ampCtrl, freqCtrl; ampCtrl = LFNoise1.kr(0.5, \ampDev1.ir(0.05), \amp1.ir(0.2)); freqCtrl = LFNoise1.kr(2, \dev1.ir(0), atio1.ir(1)); inst1 = LFTri.ar(freq * (counter + 1) * freqCtrl); inst1 = inst1 * ampCtrl * env1; cycl1 = cycl1 + inst1; }; 2.do { arg counter; var ampCtrl, freqCtrl; ampCtrl = LFNoise1.kr(0.5, \ampDev2.ir(0.0), \amp2.ir(0.3)); freqCtrl = LFNoise1.kr(2, \dev2.ir(0), atio2.ir(1)); inst2 = LFSaw.ar(freq * (counter + 1) * freqCtrl); inst2 = inst2 * ampCtrl * env2; cycl2 = cycl2 + inst2 }; 3.do { arg counter; var ampCtrl, freqCtrl; ampCtrl = LFNoise1.kr(0.5, \ampDev3.ir(0.0), \amp3.ir(0.1)); freqCtrl = LFNoise1.kr(2, \dev3.ir(0), atio3.ir(1)); inst3 = LFSaw.ar(freq * (counter + 1) * freqCtrl); inst3 = inst3 * ampCtrl * env3; cycl3 = cycl3 + inst3; }; output = cycl1 + cycl2 + cycl3; output = LPF.ar(output, 1100); output = output * \amp.ir(0.01); output = output!2; output = Out.ar(\out.ir(0), output); }).add;
@elifieldsteel
@elifieldsteel 3 года назад
As far as I can tell, this SynthDef is working properly. If you make your three envelopes very different from each other (one very long, two very short), you should be able to hear the result. It may also become more audible if you remove the lowpass filter. You should also put a doneAction:2 on the longest envelope.
@Λ.Κ
@Λ.Κ 3 года назад
@@elifieldsteel thanks. I will consider supporting you and also try some of your applications. Keep on the good work
Далее
SuperCollider Tutorial: 7. Server Architecture
20:07
Просмотров 25 тыс.
Being Competent With Coding Is More Fun
11:13
Просмотров 80 тыс.
Меня Забанили в Steam CS2 / PUBG
19:19
Просмотров 220 тыс.
Pydantic Tutorial • Solving Python's Biggest Problem
11:07
SuperCollider Tutorial: 10. Patterns, Part I
28:13
Просмотров 43 тыс.
My Initial Impresson Of Go
12:39
Просмотров 91 тыс.
JavaScript Pro Tips - Code This, NOT That
12:37
Просмотров 2,5 млн
SuperCollider Tutorial: 4. Envelopes and doneAction
18:53
OpenAI’s New ChatGPT: 7 Incredible Capabilities!
6:27
ARRAYLIST VS LINKEDLIST
21:20
Просмотров 63 тыс.