Routines and multiple conditionals / OOE

I am trying to play with recent ideas and methods brought up like this little micro tracker, and I’m trying to understand Routines better in general.

Here I have 2 if statements. I am trying to learn how to do 2 things mainly:

  1. how to have multiple conditional statements: if, for, switches, etc. inside the same routine, using the same variables (here rhythm = "string").

and 2) the type of structure I have to use. This is hard to explain for me, but for instance, do I need to put the conditional statements in different fork / loops? Especially if I want to have them yield at different .wait times?

I have been going through the docs, and have looked through these forums and the mailing list, but haven’t found much on this.

(
Routine {
	var s, bpm, beat, map, rhythm;

	map = IdentityDictionary[$k -> \kick, $s -> \snare, $h -> \hat,];

	s = Server.default;
	bpm = 160 ;
	beat = 60/bpm;
	rhythm = "d.hk.hk.s.h.h.s.";

	fork {
		loop {
			rhythm.do { |char|
				var instrument;
				if (char == $d) {
					s.makeBundle(s.latency, {Synth(\sine);});
					(beat*8).wait;	
				
				if (char != $.) {
					instrument = map[char];
					s.makeBundle(s.latency, {Synth(instrument);	});
					(beat*2).wait;
				
				}};
			};
		};
	}
}
)

In this example, I want the $d, when selected to do something different, and not already mapped in the IdentityDictionary, just for learning’s sake. And the == $d and != $. statements to have different wait times. I guess my main problem is understanding how to wrap the functions properly in loops and forks probably. I’m sorry if I am unclear in any way, and thank you.

We should have a different thread category just for my incessant noob questions…

And if I try case, it says syntax error at the “is equal”, == :

(
Routine {
	var s, bpm, beat, map, rhythm;

	map = IdentityDictionary[$k -> \kick, $s -> \snare, $h -> \hat,];

	s = Server.default;
	bpm = 160 ;
	beat = 60/bpm;
	rhythm = "d.hk.hk.s.h.h.s.";
	
	fork { loop {
		rhythm.do{ |char|
			case
			{char != $.} {instrument = map[char]; s.makeBundle(s.latency, {Synth(instrument)})};
			{char == $d} {s.makeBundle(s.latency, {Synth(\sine)})}};
			
			(beat * 2).wait;
	}};
}.play;
)

EDIT: I found the semicolon in the wrong place…after 2 hours. But the case still does not work. I guess it’s not working passing the argument. I can either do the char == $d, OR char!= $. …I’m sure this just has to do with syntax at this point.

Even after fixing the semicolon it is saying char doesn’t exist even though there is the argument: |char|. And I cannot see the proper way to separate these multiple conditions. Should they be in separate loops? forks? If I put the char argument after loop: loop { |char| then I still have to put the argument in the rhythm.do function: rhythm.do{ |char| and this is as far as I get, and it only plays the \sine over and over.

If I switch the {char == $d} and {char!= $.} order in the “case”, then it will play a sine over and over, but not play any of the other sounds at that point.

Ok. After…5 hours today and 3 yesterday, I figured it out, trial and error. Sorry if I was too hasty asking here. Now all I have to do is figure out how to do multiple conditionals in one loop, and when to use fork vs. loop, etc.

(
Routine {
	var map, rhythm, instrument;
	map = IdentityDictionary[$k -> \kick, $s -> \snare, $h -> \hat,];
	rhythm = "d.hk.hk.s.h.h.s.";
	loop { rhythm.do{ |character|
		case
		{character == $d} {s.makeBundle(s.latency, {Synth(\sine, [\freq, 400])})}
		{character != $.} {instrument = map[character]; s.makeBundle(s.latency, {Synth(instrument)}
		)};
		0.125.wait;
	}};
}.play;
)

DONT PLAY THIS next one. It will crash SC. Maybe the .wait time can’t be dynamically changed… :slight_smile:

(
Routine {
	var s, bpm, beat, map, rhythm, instrument, waitv;
	s = Server.default;
	bpm = 160 ;
	beat = 60/bpm;
	
	map = IdentityDictionary[$k -> \kick, $s -> \snare, $h -> \hat,];
	rhythm = "d.hk.hk.s.h.h.s.";
	waitv = (beat*2).wait;
	loop { rhythm.do{ |character|
		case
		{character == $d} {s.makeBundle(s.latency, {Synth(\sine, [\freq, 400])})}
		{character == $d} {beat/2}
		{character != $.} {instrument = map[character]; s.makeBundle(s.latency, {Synth(instrument)};
			
		)};
		waitv;
	}};
}.play;
)

I’m thinking this should work, but it doesn’t:

(
var time = 4;
var ar = [$x, $y, $x, $x];
Routine {
	
		ar.do{ |char|
			switch( char,
				$x, {time;   "pickle x".postln},
				$y, {time/4; "pepper y".postln}
			);
			time.wait;
	};
}.play;
)

I thought it was that “time” wasn’t in scope, but it still doesn’t work if I make it global: ~time

I haven’t had a chance to look through the code in your first two posts, but the reason it isn’t working in the third post is that when you divide time by 4, you don’t save the value anywhere. So the calculation is performed, but when you call time.wait; afterward, time is still equal to 4.

And with that in mind, in the $x case, you’ll also need to set time = 4; or else after the first $y, time will be equal to 1.

(
var time = 4;
var ar = [$x, $y, $x, $x];
Routine {
	
		ar.do{ |char|
			switch( char,
				$x, {time = 4;   "pickle x".postln},
				$y, {time = time/4; "pepper y".postln}
			);
			time.wait;
	};
}.play;
)

Neither branch modifies the time variable, so the wait will always be for the same time.

A side-effect-free expression by itself makes sense at the end of a function, as a return value. Otherwise it’s not useful.

Perhaps:

(
var time = 4;
var ar = [$x, $y, $x, $x];
Routine {
	ar.do{ |char|
		var thisTime;
		switch( char,
			$x, { thisTime = time;   "pickle x".postln },
			$y, { thisTime = time/4; "pepper y".postln }
		);
		thisTime.wait;
	};
}.play;
)

hjh

Ah I see. I think I’m starting to understand these return and side-effect concepts now.

Thank you both. I feel like the majority of my problems are from simply not understanding basic programming concepts. Maybe I should learn an easier language with similar paradigm at the same time.

I think your problem is less that SC is hard, but more that we have no tutorials on basic programming concepts (or maybe there is something out there somewhere?).

Having dabbled in many languages over the past decades, i like SC a lot, and am convinced it would be an excellent first programming language – if only someone invested the effort in devising a programming concepts 101 script. The hurdle there is we’re all here to make noise, not learn comp sci : )

That goes both ways – people starting with SC want to hear something fast. There may be few takers for a pure programming concepts tutorial …

But all is not lost. You’re getting good help here, keep asking!

Cheers,
eddi
https://alln4tural.bandcamp.com

I agree, the help here has been indispensable. That’s also interesting to hear that you think it makes a good beginner language.

I have the MIT book which does a decent job of explaining some programming concepts, but I do wish there was a more hands-on tutorial. Maybe when I get good enough…