Pipe Operator | and 3 dots and 2 greater than

@jamshark70 From vividsnow on sccode. May have to fix something when pasting. Not sure what I’m doing wrong with formatting

({var
r = MouseX.kr(1,10).linexp(1,10,1/10,10), d = 1/r, l = 8, // tempo, bar dur, beat levels
i = Impulse.ar(r*(2**l)), // base beat
pd = {|...a|PulseDivider.ar(*a)},
q = {|n,s=0,p=0| n=n+l; pd.(i, 2**n, s*2.pow(n-(p?0)-1)) }, // quantizer
e = {|p,d,a|EnvGen.ar(Env.perc(a?0.01,d?0.5),p)}, // perc envelope
s = {|...a|Stepper.ar(*a)},
z=DC.ar(0)
+Ringz.ar(LPF.ar(q.(0),1800),50,d).tanh
+SinOsc.ar(60,0,e.(q.(0)bitXor:q.(1))*3).tanh.madd(0.3)
+Ringz.ar(LPF.ar(q.(-2)bitXor:q.(0),[400,800]),90,d/2,1/2).mean.tanh
+BrownNoise.ar(e.(q.(0,1),d/4)/12*s.(q.(1),0,1,2))
+WhiteNoise.ar(e.(q.(1,1,2),d/4)/30)
+Blip.ar(360,4,e.(q.(-3)bitXor:q.(-2,1),d/8)/14)
+Formlet.ar(LPF.ar(q.(0)bitXor:q.(1,1,2),2300),350,d/8,d/2,1/2)
+Formlet.ar(LPF.ar(q.(-2,1)bitXor:q.(1,1,3),s.(q.(2),0,80,105,3).midicps),s.(q.(3),0,50,80,5).midicps,d/6,d/4,1/6)
+Saw.ar(400,e.(q.(2,(1..4),3), d/8)*s.(q.(-2),0,0,3)/2)
+Ringz.ar(LPF.ar(q.(-2),[400,800])/2,160,d,s.(q.(2),0,7,14)/14).mean.tanh
+PMOsc.ar(300,490,2,0,e.(q.(2))/6)
+PMOsc.ar(200,890,3,0,e.(q.(2,1))/8)
+Pulse.ar(2900,mul:e.(q.(3)bitXor:q.(2,1),d/3)/12)
+LFPar.ar(1900,mul:e.(q.(0,[1,2],[1,3])bitXor:q.(1,1,2),d/3,5e-3)/7)
+SelectX.ar(s.([q.(1),q.(0)].choose,0,0,3.rrand(7)), Saw.ar(Array.rand(7,500,4500),mul:e.(q.(-2)bitXor:q.(0,3,1), d/3))/19)
+Pulse.ar(s.(q.(3),0,60,80,3).midicps,0.5,e.(q.(-1,2,1),d/4)*2).tanh.madd(1/24)
+LFCub.ar(s.(q.(3),0,80,108,6).midicps).madd(Saw.ar(2490)*e.(pd.(q.(-4)bitXor:q.(-3,1),8,1), d/3)/16)
+Pluck.ar(Impulse.ar(s.(q.(4),0,80,108,6).midicps), q.(-2)bitXor:q.(0,3,1), d, d/18, d/2, -0.4).madd(e.(q.(2),d*8)/3)
;z!2/8}.play)

and that bitXor stuff…no idea

When pasting code put three bacticks ``` before and after!

1 Like

bitXor is “bitwise Exclusive OR”

1 bitXor: 1 = 0
1 bitXor 0 = 1
0 bitXor 1 = 1
0 bitXor 0 = 0

bitXor gives 1 when both bits are different and 0 when they are the same.

it does this for each bit in a binary number

so 3 bitXor: 4 (decimal) =
11 bitXor: 10 (binary) =
01 (binary) = 1 (decimal

9 bitXor: 4 = 0

That was a chunk!

Some thoughts about readability in this example, apart from those noted in code comments:

  • Whitespace, whitespace, whitespace. If you’ll indulge a bit of “grumpy old guy” talk – I honestly do not understand why people think it’s a good idea to eliminate as much white space as possible. It’s a good idea only in 140-char-max sctweets. ---- I find it stressful to look at a solid wall of characters. It makes me just want to stop reading. Do you want people to stop reading your code? No? Then: Use spaces.

    • Spaces after commas. LPF.ar(sig,1000) vs LPF.ar(sig, 1000). Do you see how it breathes better? It’s more relaxed to look at and it’s easier to see where one argument ends and the next begins.
    • Spaces around binary operators. a+b :-1:a + b :+1: in general (although, for some very simple expressions at the lowest level of the formula, I might write e.g. a-1). There’s never a reason to write keyword binary ops without spaces: q.(0)bitXor:q.(1) is an abomination :laughing: – really. Just don’t. q.(0) bitXor: q.(1)
    • I prefer spaces inside function braces – which looks better to you? {|...a|Stepper.ar(*a)} or { |...a| Stepper.ar(*a) }?
    • Vertical whitespace: Use blank lines to divide conceptual chunks.
  • Line length. Once you get to about 50-60 characters, that’s enough. Stop. Break the line. If it’s a deeply nested method call, indent the arguments on separate lines (and be careful that the closing parens/brackets’ indentation levels match the corresponding opening brackets). Then you can parse the structure visually, instead of having to sift out the brackets from an undifferentiated no-space mass of characters.

  • Descriptive variable names. I guess the author felt it was important to save typing. Well, OK. But when you come back to this code 5 years later, e.(q.(0)) will be a lot harder to read than percEnvGen.(quantizer.(0)). Descriptive names are an investment for the future.

(
{
	// separate variable declarations
	// use words for variables
	// If r is the tempo, just call it 'tempo'!
	// then you don't have to explain it, or mentally translate below

	var tempo = MouseX.kr(1, 10).linexp(1, 10, 1 / 10, 10);
	var bar = 1 / tempo;
	var levels = 8; // tempo, bar dur, beat levels
	var i = Impulse.ar(tempo * (2 ** levels)); // base beat

	var quantizer = { |num, s = 0, p = 0|
		var divLevel = num + levels;
		// removed (p?0) bc p is already non-nil by the arg default = 0
		PulseDivider.ar(i, 2 ** divLevel, s * (2 ** (divLevel - p - 1)))
	};
	
	// isn't it clearer to give arg defaults in the arg list?
	// instead of (a?0.01) in the expression
	var percEnvGen = { |pulse, decay = 0.5, attack = 0.01|
		EnvGen.ar(Env.perc(attack, decay), pulse)
	};
	
	// deleting DC.ar(0) because it (by definition)
	// can have no impact on the output
	// I actually wondered if the DC.ar(0) might be a private joke or such
	
	z = Ringz.ar(LPF.ar(quantizer.(0), 1800), 50, bar).tanh
	
	+ (0.3 * SinOsc.ar(60, 0, percEnvGen.(quantizer.(0) bitXor: quantizer.(1)) * 3).tanh)
	
	+ Ringz.ar(
		LPF.ar(quantizer.(-2) bitXor: quantizer.(0), [400, 800]),
		90,
		bar / 2, 1 / 2
	).mean.tanh
	
	// moved this block up b/c it's very much like the previous term
	+ Ringz.ar(
		LPF.ar(quantizer.(-2), [400, 800]) / 2,
		160,
		bar,
		Stepper.ar(quantizer.(2), 0, 7, 14) / 14
	).mean.tanh
	
	+ BrownNoise.ar(percEnvGen.(quantizer.(0, 1), bar / 4) / 12 * Stepper.ar(quantizer.(1), 0, 1, 2))
	
	+ WhiteNoise.ar(percEnvGen.(quantizer.(1, 1, 2), bar / 4) / 30)
	
	+ Blip.ar(360, 4, percEnvGen.(quantizer.(-3) bitXor: quantizer.(-2, 1), bar / 8) / 14)
	
	+ Formlet.ar(
		LPF.ar(
			quantizer.(0) bitXor: quantizer.(1, 1, 2),
			2300
		),
		350, bar / 8, bar / 2, 1 / 2
	)
	
	+ Formlet.ar(
		LPF.ar(
			quantizer.(-2, 1) bitXor: quantizer.(1, 1, 3),
			Stepper.ar(quantizer.(2), 0, 80, 105, 3).midicps
		),
		Stepper.ar(quantizer.(3), 0, 50, 80, 5).midicps,
		bar / 6, bar / 4, 1 / 6
	)
	
	+ Saw.ar(
		400,
		percEnvGen.(
			quantizer.(2, (1..4), 3), bar / 8
		) * Stepper.ar(quantizer.(-2), 0, 0, 3) / 2
	)
	
	+ PMOsc.ar(300, 490, 2, 0, percEnvGen.(quantizer.(2)) / 6)
	
	+ PMOsc.ar(200, 890, 3, 0, percEnvGen.(quantizer.(2, 1)) / 8)
	
	+ (Pulse.ar(2900) * percEnvGen.(quantizer.(3) bitXor: quantizer.(2, 1), bar / 3) / 12)
	
	+ (LFPar.ar(1900) * percEnvGen.(
		quantizer.(0, [1, 2], [1, 3]) bitXor: quantizer.(1, 1, 2),
		bar / 3, 5e-3)
	/ 7)
	
	+ (SelectX.ar(
		Stepper.ar(
			[quantizer.(1), quantizer.(0)].choose,
			0, 0, 3.rrand(7)
		),
		Saw.ar(Array.rand(7, 500, 4500))
	) * percEnvGen.(quantizer.(-2) bitXor: quantizer.(0, 3, 1), bar / 3) / 19)
	
	// x.madd(1/24) is the same as x / 24
	// why not write the simple way?
	+ (Pulse.ar(
		Stepper.ar(quantizer.(3), 0, 60, 80, 3).midicps,
		0.5,
		percEnvGen.(quantizer.(-1, 2, 1), bar / 4) * 2
	).tanh / 24)
	
	+ (LFCub.ar(Stepper.ar(quantizer.(3), 0, 80, 108, 6).midicps)
		* (Saw.ar(2490)
			* percEnvGen.(
				PulseDivider.ar(quantizer.(-4) bitXor: quantizer.(-3, 1), 8, 1),
				bar / 3
			) / 16
		)
	)
	
	+ (Pluck.ar(
		Impulse.ar(Stepper.ar(quantizer.(4), 0, 80, 108, 6).midicps), 
		quantizer.(-2) bitXor: quantizer.(0, 3, 1),
		bar, bar / 18, bar / 2, -0.4
	) * percEnvGen.(quantizer.(2), bar * 8) / 3);
	
	(z / 8).dup(2)
	
}.play;
)
2 Likes

Man…I can’t explain how much this made my day. Had a rough, ROUGH week, and this is like a birthday present. Gonna chew on this all weekend.

Seriously thanks!

I used to do the exact same thing, starting out… I would ‘study’ one of the two competing champions of all esoteric & complex:

SCtweets, or, from sccode, probably one of vividsnow’s tightly knit masterpieces…

Why? Because they’re on the front page of -made with SC-, in one way or another… where else whould one possibly know where to start?

After some years of experience, I would have to second the notion of readability over difficulty, when it comes to studying a language as a beginner somewhat… the front page of SC is mostly filled with artist pieces, not in in any way intended to be clear or considerate of the beginner … quite the opposite, in most cases.

After years of experience, starting from zero in math, production, and programming, and working mostly in SC, here is my god-honest advice:

Start with the Saw wave. Keep everything you study in a journal, every session study one Ugen / method / technique, save the file as " Date.getStamp ugen / method / etc. "

… the point is, save your work, keep everything clean and organized. Don’t dive straight into complex works, instead build your own.

Start with the Saw wave, it’s universal, and it’s a classic. Use the Saw wave without thinking about complex synthesis just yet.

Using only 1-2 (no more than 3) basic oscillators, learn how to use envelopes… the env / envGen implementation requires a little practice, and there are many different ways to make them… but you can map an envelope to anything, an envelope is simply a way to make any variable more dynamic.

(Side note - I myself, personally only use non-gated, self-releasing Env’s made using Env.xyc… just my taste, but you should become familiar with the entire class, as nothing is more fundamental, it’s the most common method of making a parameter more dynamic)

After envelopes, practice with LFO’s, particularly sine wave oscillators… envelopes and LFO’s can be universally replaced with each other, in any situation… except for when you need the env to free the synth node for you, using doneAction: 2.

Master these both, w/ with only triangle / saw wave, envelopes, LFO’s, and then, make sure your excellent with filters (LPF, HPF), and then go on from there, however you will, it’s all free exploration at this point… but I cant accept that this isn’t the best way to go about it, because these are the basic building blocks of synthesis in general, and nothing could be more fundamental.

  • Envelopes
  • LFO’s
  • Filters (Low-pass & High-pass)
  • Reverb, Delay, and Chorus, etc.

Always remember your end game… don’t lose yourself in the fascination.

Hi semiquaver

I am a bit confused by the bitXor thing.

So my assumption is that if the number is decimal like 4, 9, and so on it gets converted to a binary number i.e. 100 or 1001 and then these numbers are compared. What exactly is compared?

If I enter “9 bitXor: 4” in SC I receive 13 and not 0.

Thanks!

My error - apologies.

Each bit of the number on the left is compared to each bit of the number on the right. If both bits are the same then the result for that position is 0, else 1.

So 9 is 1001; 4 is 100; result is 1101

12 bitXor: 4: 12 is 1100; 4 is 100; result should be 1000 or 8.

1 Like

Now I get it :slight_smile:
Thanks!

I can’t find any method to convert decimal numbers to binary in SC.
The most convenient that I find is 9.asBinaryDigits which return an array of 8 elements ([0, 0, 0, 0, 1, 0, 0, 1]).
Is there a method wich return 1001 with 9 as a receiver ?
Thanks in advance

Yeah I looked yesterday too and couldn’t find anything. Found EVRYTHING else though, ha.

the algorithm is pretty simple

if a is a decimal number …

divide by two and add the remainder to an array
recurse
then reverse the array

12 / 2 = 6 r 0 // [0]
6/2 = 3 r 0 // [0,0]
3/2 = 1 r 1 // [0,0,1]
1/2 = 0 r 1 // [0,0,1,1]

result = 1100

fun assignment: write the method you seek and add it to the Integer class…

You’d have to define first what data type you actually expect. Integer, Float, String, Symbol ?

asBinaryDigits is also a bit dangerous as it limits to 8 digits by default, consequently it gives erroneous results with larger numbers or you calculate the length yourself:

17359.asBinaryDigits

// not ok as result and data type ...

-> [ 1, 1, 0, 0, 1, 1, 1, 1 ]

// you can check the length though

17359.asBinaryDigits(log2(17359).ceil.asInteger)

-> [ 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 ]

So better take asDigits:

9.asDigits(2).join
9.asDigits(2).join.asSymbol
9.asDigits(2).join.asInteger
1 Like

Thank you Daniel and Semiquaver for your explanations.
9.asDigits(2).join is ok

Hello Semiquaver.
I tried to do it but I’m not very familiar with class extension.
First I come up with this simple function:

(
var result;
var decToBinary = { arg dec, div = 2;
	var quotient;
	
	if (dec % div > 0, {
		result = result.add(1);
	}, {
		result = result.add(0);
	});
	
	quotient = (dec / div).floor.asInteger;
	
	if (quotient > 0, {
		decToBinary.value(quotient, div);
	});
	
	result.reverse.join;
};

decToBinary.value(12);
)

I initialise an array outside of the function to store binary, that’s the first line: var result;
Then I use the function recursively.
I made very quick test and it seems to work so far.
If I forgot something or misunderstood your post, feel free to tell me.

I tried to transform this function into a method of the Integer class but I think I need to add an instance variable to store binary like in the function above and I don’t know how to do this with class extension.
I come up with this (it throw an error at compilation because of the var result; declaration):

+ Integer {
	var result; // not allowed

	decToBinary { arg div = 2;
		var dec, quotient;

		dec = this;

		if (dec % div > 0, {
			result = result.add(1);
		}, {
			result = result.add(0);
		});

		quotient = (dec / div).floor.asInteger;

		if (quotient > 0, {
			quotient.decToBinary(div);
		});

		^result.reverse.join;
	}
}

Any help will be much appreciated.
Thank you

Very close!

You simply need one additional pair of braces (and rename your inner function:

+ Integer {
    decToBinary { arg div = 2;
        var result = List.new();
        var innerFunction {
            Etc !

Reminder that the set of braces after Integer is not a function declaration - it simply groups the methods declared within.

+ Class {
    method function
    method function
}

Thank you for your answer Semiquaver.
I assume you are trying to make me do a closure ? But I can’t figure out what is the syntax with your example.
Could you be a little more specific please ?
for example:
var innerfunction {}
throw an error
do you mean:
var innerFunction = {}
I can’t understand the need to rename the innerFunction ?
I don’t know what the method should return, how to use the innerFunction ?
… I’m a bit lost.
I can’t find how to make a closure inside a method.
Any help will be much appreciated.

I found another way which is working so far but maybe not very elegant, maybe totally disgusting in fact:

+ Integer {

	decToBinary { arg div = 2, result = List.new;
		var dec, quotient;

		dec = this;

		if (dec % div > 0, {
			result = result.add(1);
		}, {
			result = result.add(0);
		});

		quotient = (dec / div).floor.asInteger;

		if (quotient > 0, {
			quotient.decToBinary(div, result);
		});

		^result.reverse.join;
	}
}

use it like this:

12.decToBinary; // 1100
9.decToBinary; // 1001

Thank you

Your solution seems ok!

result should be a var not an arg though!

Also no need for the line
var dec = this;
you can simply use this throughout.

About your question though, there’s no syntactic difference between Functions and methods (other than use of this and super etc) - just as your original Function contained a Function bound to a variable within it, your method could have worked exactly the same way. I just thought you probably wanted ‘decToBinary’ as your method name rather than as the name of the variable your inner function was bound to, sorry for confusion.

Thank you for taking the time to answer me.
I use this method recursively, if result is a var initiate like this: var result = List.new; each time I recall the method I loose all the content of result so your advice breaks the functionnality of this method.

I’m sorry but I still don’t get it, I still don’t knox how to write it with the innerFunction.
I tryed this:

+ Integer {

	decToBinary { arg div = 2;
		var result = List.new, innerFunction;
		
		innerFunction = {
			var quotient;
			
			if (this % div > 0, {
				result = result.add(1);
			}, {
				result = result.add(0);
			});
			
			quotient = (this / div).floor.asInteger;
			
			if (quotient > 0, {
				quotient.decToBinary(div);
			});
			
			^result.reverse.join;
		}
	}
}

but it’s not working,
Should the method return the innerFunction ?
Should I keep ^result.reverse.join; outside ?
Thank you for your help