From 8d85626f648cf5a99b0f9d860468d10f9ccc7ea3 Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 12:51:13 +0100 Subject: [PATCH 01/16] Change dead link Gitbook disxussion link has been subject to bitrot, changed it to github issues. --- preface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preface.md b/preface.md index 0dd81eb..2adc259 100644 --- a/preface.md +++ b/preface.md @@ -8,6 +8,6 @@ This book is the outcome of teaching SuperCollider in various British higher edu The aim with this book is the same as my initial tutorials written in 2005, i.e., to serve as a good undergraduate introduction to SuperCollider programming, audio synthesis, algorithmic composition, and interface building of innovative creative audio systems. I do hope that my past and future students will find this work useful, and I sincerely hope that it also is beneficial to anyone who decides to embark upon the exciting expedition into the fantastic and enticing world of SuperCollider: the ideal workshop for people whose creative material is sound. -I encourage any reader who finds bugs, errors, or simply would like a better explanation of a topic to give me feedback through this book’s [feedback channel]: (https://www.gitbook.com/book/thormagnusson/scoring/discussions). +I encourage any reader who finds bugs, errors, or simply would like a better explanation of a topic to give me feedback through this book’s [feedback channel]: (https://github.com/thormagnusson/scoring/issues). Brighton, February, 2016. From e546928706300fb638553f3fa0d243a9a2147587 Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 13:06:40 +0100 Subject: [PATCH 02/16] Note about images. --- imgs/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/imgs/README.md b/imgs/README.md index e69de29..42421d0 100644 --- a/imgs/README.md +++ b/imgs/README.md @@ -0,0 +1 @@ +The images were missing whe I made this fork (and are missing on Gitbook, so presumably unrecovereable?). From 41167b8bab3147555484073cdeee8887e620f088 Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 13:09:03 +0100 Subject: [PATCH 03/16] =?UTF-8?q?Replace=20all=20=E2=80=9Ccurly=20quotes?= =?UTF-8?q?=E2=80=9D=20in=20code=20blocks=20with=20ascii=20"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (and these single ones ‘ ’ with ') --- PartI/chapter_1.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/PartI/chapter_1.md b/PartI/chapter_1.md index 4682b3d..0600153 100644 --- a/PartI/chapter_1.md +++ b/PartI/chapter_1.md @@ -125,7 +125,7 @@ Comments are red by default, but can be any colour (in the Format menu choose Here is a mantra to memorise: Variables are containers of some value. They are names or references to values that could change (their value can vary). So we could create a variable that is a property of yourself called age. Every year this variable will increase by one integer (a whole number). So let us try this now: var age = 33; - age = age + 1; // here the variable ‘age’ gets a new value, or 33 + 1 + age = age + 1; // here the variable 'age' gets a new value, or 33 + 1 age.postln; // and it posts 34 SuperCollider is not strongly typed so there is no need to declare the data type of variables. Data types (in other languages) include : integer, float, double, string, custom objects, etc... But in SuperCollider you can create a variable that contains an integer at one stage, but later contains reference to a string or a float. This can be handy, but one has to be careful as this can introduce bugs in your code. @@ -145,9 +145,9 @@ SuperCollider has scope, so if you declare a variable within a certain scope, su var v, a; v = 22; a = 33; - “The value of a is : “.post; a.postln; + "The value of a is : ".post; a.postln; ) - “The value of a is now : ”.post; a.postln; // then run this line + "The value of a is now : ".post; a.postln; // then run this line So ‘a’ is a global variable. This is good for prototyping and testing, but not recommended as a good software design. A variable with the name ‘myvar’ could not be global – only single lowercase characters. @@ -237,12 +237,12 @@ SuperCollider contains quite a lot of examples of “syntax sugar”, i.e., wher You will see the following f = { arg string; string.postln; } // we will post the string that comes into the function - f.value(“hi there") // and here we call the function passing “hi there” as the argument. + f.value("hi there") // and here we call the function passing "hi there" as the argument. Often written in this form: - f = {|string| string.postln;} // arguments can be defined within two pipes ‘|’ + f = {|string| string.postln;} // arguments can be defined within two pipes '|' f.("hi there") // and you can skip the .value and just write a dot (.) ## Arrays, Lists and Dictionaries @@ -274,7 +274,7 @@ What happened here is that we tell the Array class to fill a new array with five We can now play a little bit with that function that we pass to the array creation: - a = Array.fill(5, { arg i; i }); // create a function with the iterator (‘i’) argument + a = Array.fill(5, { arg i; i }); // create a function with the iterator ('i') argument a = Array.fill(5, { arg i; (i+1)*11 }); // the same as the first array we created a = Array.fill(5, { arg i; i*i }); a = Array.series(5, 10, 2); // a new method (series). @@ -291,28 +291,28 @@ m is here an array with the following values: [ 0, 2, 3, 5, 7, 8, 10 ]. So in a m = m.add(12); // you might want to add the octave (12) into your array m = m+60 // here we simply add 60 to all the values in the array m = m.midicps // and here we turn the MIDI notes into their frequency values - m = m.cpsmidi // but let’s turn them back to MIDI values for now + m = m.cpsmidi // but let's turn them back to MIDI values for now We could now play with the ‘m’ array a little. In an algorithmic composition, for example, you might want to pick a random note from the minor scale - n = m.choose; // choose a random MIDI note and store it in the variable ’n’ + n = m.choose; // choose a random MIDI note and store it in the variable 'n' x = m.scramble; // we could create a melody by scrambling the array x = m.scramble[0..3] // scramble the list and select the first 4 notes p = m.mirror // mirror the array (like an ascending and descending scale) You will note that in ‘x = m.scramble’ above, the ‘x’ variable contains an array with a scrambled version of the ‘m’ array. The ‘m’ array is still intact: you haven’t scrambled that one, you’ve simply said “put a scrambled version of ‘m’ into variable ‘x’.” So the original ‘m’ is still there. If you really wanted to scramble ‘m’ you would have to do: - m = m.scramble; // a scrambled version of the ‘m’ array is put back into the ‘m’ variable - // But now it’s all scrambled up. Let’s sort it into ascending numbers again: + m = m.scramble; // a scrambled version of the 'm' array is put back into the 'm' variable + // But now it's all scrambled up. Let's sort it into ascending numbers again: m = m.sort Arrays can contain anything, and in SuperCollider, they can contain values of mixed types, such as integers, strings, floats, and so on. - a = [1, “two”, 3.33, Scale.minor] // we mix types into the array. + a = [1, "two", 3.33, Scale.minor] // we mix types into the array. // This can be dangerous as the following a[0]*10 // will work - a[1]*10 // but this won’t, as you cant multiply the word “two” with 10 + a[1]*10 // but this won't, as you cant multiply the word "two" with 10 Arrays can contain other arrays, containing other arrays of any dimensions. @@ -337,7 +337,7 @@ Above we added 12 to the minor scale. m = Scale.minor.degrees; - m.add(12) // but try to run this line many times, the array won’t grow forever + m.add(12) // but try to run this line many times, the array won't grow forever ### Lists @@ -407,7 +407,7 @@ So to really understand a class like Array or List you need to read the document Array.openHelpFile // get the documentation of the Array class Array.dumpInterface // get the interface or the methods of the Array class - Array.dumpFullInterface // get the methods of Array’s superclasses as well. + Array.dumpFullInterface // get the methods of Array's superclasses as well. You can see that in the .dumpFullInterface method will tell you all the methods Array *inherits* from its superclasses. @@ -430,8 +430,8 @@ if( hungry, { eat } ); So let’s play with this: - if( true, { "condition is TRUE".postln;}, {"condition is FALSE”.postln;}); - if( false, { "condition is TRUE".postln;}, {"condition is FALSE”.postln;}); + if( true, { "condition is TRUE".postln;}, {"condition is FALSE".postln;}); + if( false, { "condition is TRUE".postln;}, {"condition is FALSE".postln;}); You can see that true and false are keywords in SuperCollider. They are so called Boolean values. You should not use those as variables (well, you can’t). In digital systems, we operate in binary code, in 1s and 0s. True is associated with 1 and false with 0. @@ -483,8 +483,8 @@ And we also use comparison operators You might not realise it yet, but knowing what you now know is very powerful and it is something you will use all the time for synthesis, algorithmic composition, instrument building, sound installations, and so on. So make sure that you understand this properly. Let's play with this a bit more in if-statements: - if( 3==3, { "condition is TRUE".postln;}, {"condition is FALSE”.postln;}); - if( 3==4, { "condition is TRUE".postln;}, {"condition is FALSE”.postln;}); + if( 3==3, { "condition is TRUE".postln;}, {"condition is FALSE".postln;}); + if( 3==4, { "condition is TRUE".postln;}, {"condition is FALSE".postln;}); // and things can be a bit more complex: if( (3 < 4) && (true != false), {"TRUE".postln;}, {"FALSE".postln;}); From 83f519c6466e37cb6cbf368bab88f6f40cc829fe Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 14:05:42 +0100 Subject: [PATCH 04/16] Changed a few ``` type code blocks to 4-space indented code blocks This makes it easier to find & replace characters like the problem quotes. Added a coupld of TODO notes where some of the text seems wrong, but I don't don't understand enough to fix it yet. --- PartI/chapter_1.md | 125 ++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 65 deletions(-) diff --git a/PartI/chapter_1.md b/PartI/chapter_1.md index 0600153..d2dcf4a 100644 --- a/PartI/chapter_1.md +++ b/PartI/chapter_1.md @@ -162,18 +162,18 @@ But typically we just declare the variable (var) in the beginning of the program But why use variables at all? Why not simply write the numbers or the value wherever we need it? Let’s take one example that should demonstrate clearly why they are useful: -```{ - // declare the variables -var freq, oscillator, filter, signal; -freq = 333; // set the frequency variable - // create a Saw wave oscillator with two channels -oscillator = Saw.ar([freq, freq+2]); -// use a resonant low pass filter on the oscillator -filter = RLPF.ar(oscillator, freq*4, 0.25); -// multiply the signal by 0.5 to lower the amplitude -signal = filter * 0.5; -}.play; -``` + + { + // declare the variables + var freq, oscillator, filter, signal; + freq = 333; // set the frequency variable + // create a Saw wave oscillator with two channels + oscillator = Saw.ar([freq, freq+2]); + // use a resonant low pass filter on the oscillator + filter = RLPF.ar(oscillator, freq*4, 0.25); + // multiply the signal by 0.5 to lower the amplitude + signal = filter * 0.5; + }.play; As you can see, the ‘freq’ variable is used in various places in the above synthesizer. You can now change the value of the variable to something like 500, and it the frequency will ‘automatically’ be turned into 500 Hz in the left channel, 502 Hz in the right, and the cutoff frequency will be 2000 Hz. So instead of changing these variables throughout the code, you change it in one place and its value magically plugged into every location where that variable is used. @@ -288,7 +288,9 @@ You might wonder why this is so fantastic or important. The fact is that arrays m is here an array with the following values: [ 0, 2, 3, 5, 7, 8, 10 ]. So in a C scale, 0 would be C, 2 would be D (two half notes above C), 3 would be E flat, and so on. We could represent those values as MIDI notes, where 60 is the C note (~ 261Hz). And we could even look at the actual frequencies in Hertz of those MIDI notes. (Those frequencies would be passed to the oscillators as they are expecting frequencies and not MIDI notes as arguments). m = Scale.minor.degrees; // Scale class returns the degrees of the minor scale - m = m.add(12); // you might want to add the octave (12) into your array + m = m.add(12); // you might want to add the octave (12) into your array +[ TODO : In SuperCollider 3.12.2 this doesn't work at this point, you get ERROR: Primitive '_ArrayAdd' failed. Attempted write to immutable object. - after running one of the lines below it then works. I don't understand why well enough to explain, but have an inkling (the array contains a refernce to Scale.minor.degrees rather than the values [ 0, 2, 3, 5, 7, 8, 10 ] until something forces it to, right ? ) - Andy ] + m = m+60 // here we simply add 60 to all the values in the array m = m.midicps // and here we turn the MIDI notes into their frequency values m = m.cpsmidi // but let's turn them back to MIDI values for now @@ -317,28 +319,27 @@ Arrays can contain anything, and in SuperCollider, they can contain values of mi Arrays can contain other arrays, containing other arrays of any dimensions. -``` -// a function that will create a 5 item array with random numbers from 0 to 10 -f = { Array.fill(5, { 10.rand }) }; // array generating function -a = Array.fill(10, f.value); // create another array with 10 items of the above array -// But the above was evaluated only once. Why? -// Because, you need to pass it a function to get a different array every time. Like this: -a = Array.fill(10, { f.value } ); // create another array with 10 items of the above array -// We can get at the first array and see it’s different from the second array -a[0] -a[1] -// We could put a new array into a[0] (that slot contains an array) -a[0] = f.value -// We could put a new array into a[0][0] (an integer) -a[0][0] = f.value -``` + // a function that will create a 5 item array with random numbers from 0 to 10 + f = { Array.fill(5, { 10.rand }) }; // array generating function + a = Array.fill(10, f.value); // create another array with 10 items of the above array + // But the above was evaluated only once. Why? + // Because, you need to pass it a function to get a different array every time. Like this: + a = Array.fill(10, { f.value } ); // create another array with 10 items of the above array + // We can get at the first array and see it's different from the second array + a[0] + a[1] + // We could put a new array into a[0] (that slot contains an array) + a[0] = f.value + // We could put a new array into a[0][0] (an integer) + a[0][0] = f.value Above we added 12 to the minor scale. - m = Scale.minor.degrees; m.add(12) // but try to run this line many times, the array won't grow forever +[ TODO : In SuperCollider 3.12.2 this gives "ERROR: Primitive '_ArrayAdd' failed. Attempted write to immutable object." as above - Andy ] + ### Lists It is here that the List class becomes useful. @@ -353,54 +354,48 @@ Lists are like arrays - and implement many of the same methods - but the are sli A dictionary is a collection of items where *keys* are mapped to *values*. Here, keys are keywords that are identifiers for slots in the collection. You can think of this like names for values. This can be quite useful. Let's explore two examples: -``` -a = Dictionary.new -a.put(\C, 60) -a.put(\Cs, 61) -a.put(\D, 62) -a[\Ds] = 63 // same as .put -// and now, let's get the values -a.at(\D) -a[\D#] // same as .at - -a.keys -a.values -a.getPairs -a.findKeyForValue(60) -``` + a = Dictionary.new + a.put(\C, 60) + a.put(\Cs, 61) + a.put(\D, 62) + a[\Ds] = 63 // same as .put + // and now, let's get the values + a.at(\D) + a[\D#] // same as .at + + a.keys + a.values + a.getPairs + a.findKeyForValue(60) Imagine how you would do this with an Array. One way would be -``` -a = [\C, 60, \Cs, 61, \D, 62, \Ds, 63] -// we find the slot of a key: -x = a.indexOf(\D) // 4 -a[x+1] -// or simply -a[a.indexOf(\D)+1] -``` + + a = [\C, 60, \Cs, 61, \D, 62, \Ds, 63] + // we find the slot of a key: + x = a.indexOf(\D) // 4 + a[x+1] + // or simply + a[a.indexOf(\D)+1] + but using an array you need to keep track of the how things are organised and indexed. Another Dictionary example: -``` -b = Dictionary.new -b.put(\major, [ 0, 2, 4, 5, 7, 9, 11 ]) -b.put(\minor, [ 0, 2, 3, 5, 7, 8, 10 ]) -b[\minor] -``` + b = Dictionary.new + b.put(\major, [ 0, 2, 4, 5, 7, 9, 11 ]) + b.put(\minor, [ 0, 2, 3, 5, 7, 8, 10 ]) + b[\minor] ## Methods? We have now seen things as 100.rand and a.reverse. How does .rand and .reverse work? Well, SuperCollider is an [Object Orientated language](https://en.wikipedia.org/wiki/Object-oriented_programming) and these are *methods* of the respective classes. So an integer (like 100), has methods like .rand, .midicps, or .neg. It does not have a .reverse method. Why not? Because you can’t reverse a number. However, an array (like [11,22,33,44,55]) can be reversed or added to. We will explore this later in the chapter about Object Orientated programming in SC, but for now it is enough to think that the object (an instantiation of the class) has relevant methods. Or to use an analogy: let’s say we have a class called Car. This class is the information needed to build the car. When we build a Car, we instantiate the class and we have an actual Car. This car can then have some methods, for instance: start, drive, turn, putWipersOn. And these methods could have arguments, like speed(60), or turn(-60). You could think about the object as the noun, the method as the verb, and the argument as the adjective. (As in: John (object) walks (method) fast (adjective)). -``` -// we create a new car. 4 indicating for example number of seats -c = Car.new(4); -c.start; -c.drive(40); // the car drives 40 miles per hour -c.turn(-60); // the car turns 60 degrees to the left -``` + // we create a new car. 4 indicating for example number of seats + c = Car.new(4); + c.start; + c.drive(40); // the car drives 40 miles per hour + c.turn(-60); // the car turns 60 degrees to the left So to really understand a class like Array or List you need to read the documentation and explore the methods available. Note also that the Array is subclassing (or getting methods from its superclass) the ArrayedColldection class. This means that it has all the methods of its superclass. Like a class “Car” might have a superclass called “Vehicle” of which a “Motorbike” would also be a subclass (a sibling to “Car”). You can explore this by peeking under the hood of SC a little: From 9182e3e9be21b629e1e993b9d67556ed764d93d4 Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 14:11:22 +0100 Subject: [PATCH 05/16] Changed the final array example before ###Lists section It didn't work before & this is more equivalant of the lists example it's contrasted with. --- PartI/chapter_1.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/PartI/chapter_1.md b/PartI/chapter_1.md index d2dcf4a..3fbd180 100644 --- a/PartI/chapter_1.md +++ b/PartI/chapter_1.md @@ -333,19 +333,16 @@ Arrays can contain other arrays, containing other arrays of any dimensions. // We could put a new array into a[0][0] (an integer) a[0][0] = f.value -Above we added 12 to the minor scale. +Above we added 12 to an array, the minor scale in the previous instance. - m = Scale.minor.degrees; + m = [] // Start with an new, empty array m.add(12) // but try to run this line many times, the array won't grow forever -[ TODO : In SuperCollider 3.12.2 this gives "ERROR: Primitive '_ArrayAdd' failed. Attempted write to immutable object." as above - Andy ] - ### Lists It is here that the List class becomes useful. - - l = List.new; + l = List.new; // Start with an new, empty list l.add(100.rand) // try to run this a few times and watch the list grow Lists are like arrays - and implement many of the same methods - but the are slightly more expensive than arrays. In the example above you could simply do ‘a = a.add(100.rand)’ if ‘a’ was an array, but many people like lists for reasons we will discuss later. From b8054e2dce32a7ea32b393cb891e1356ab8c3cb0 Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 14:14:07 +0100 Subject: [PATCH 06/16] Fix \Ds key instead \D# --- PartI/chapter_1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PartI/chapter_1.md b/PartI/chapter_1.md index 3fbd180..090c65c 100644 --- a/PartI/chapter_1.md +++ b/PartI/chapter_1.md @@ -358,7 +358,7 @@ A dictionary is a collection of items where *keys* are mapped to *values*. Here, a[\Ds] = 63 // same as .put // and now, let's get the values a.at(\D) - a[\D#] // same as .at + a[\Ds] // same as .at a.keys a.values From c23ccac8c9222cac681cbf7ad316327047418e3f Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 14:20:08 +0100 Subject: [PATCH 07/16] No space between [] and () in link --- PartI/chapter_1.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/PartI/chapter_1.md b/PartI/chapter_1.md index 090c65c..3b6f396 100644 --- a/PartI/chapter_1.md +++ b/PartI/chapter_1.md @@ -394,8 +394,7 @@ We have now seen things as 100.rand and a.reverse. How does .rand and .reverse w c.drive(40); // the car drives 40 miles per hour c.turn(-60); // the car turns 60 degrees to the left -So to really understand a class like Array or List you need to read the documentation and explore the methods available. Note also that the Array is subclassing (or getting methods from its superclass) the ArrayedColldection class. This means that it has all the methods of its superclass. Like a class “Car” might have a superclass called “Vehicle” of which a “Motorbike” would also be a subclass (a sibling to “Car”). You can explore this by peeking under the hood of SC a little: - +So to really understand a class like Array or List you need to read the documentation and explore the methods available. Note also that the Array is subclassing (or getting methods from its superclass) the ArrayedCollection class. This means that it has all the methods of its superclass. Like a class “Car” might have a superclass called “Vehicle” of which a “Motorbike” would also be a subclass (a sibling to “Car”). You can explore this by peeking under the hood of SC a little: Array.openHelpFile // get the documentation of the Array class Array.dumpInterface // get the interface or the methods of the Array class @@ -403,7 +402,7 @@ So to really understand a class like Array or List you need to read the document You can see that in the .dumpFullInterface method will tell you all the methods Array *inherits* from its superclasses. -Now, this might give you a bit of a brainache, but don’t worry, you will gradually learn this terminology and what it means for you in your musical or sound practice with SuperCollider. Wikipedia is good place to start reading about [Object Oriented Programming] (https://en.wikipedia.org/wiki/Object-oriented_programming). +Now, this might give you a bit of a brainache, but don’t worry, you will gradually learn this terminology and what it means for you in your musical or sound practice with SuperCollider. Wikipedia is good place to start reading about [Object Oriented Programming](https://en.wikipedia.org/wiki/Object-oriented_programming). ## Conditionals, data flow and control From 5a2f40d4a65d8de6ad335ee7b977856dcc57e9ea Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 14:34:35 +0100 Subject: [PATCH 08/16] Add Windows Path (Can't remember on Linux) --- introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/introduction.md b/introduction.md index 9eec4e2..be4fb28 100644 --- a/introduction.md +++ b/introduction.md @@ -49,7 +49,7 @@ You have now installed and explored SuperCollider on your system. This book does Mac: ~/Library/Application Support/SuperCollider Linux: -Windows: +Windows: %LocalAppData%\SuperCollider From 47514c4adb204f4c8223e0003ec3d35cf8741fb8 Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 14:38:42 +0100 Subject: [PATCH 09/16] I finished following Chapter 1 --- PartI/chapter_1.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/PartI/chapter_1.md b/PartI/chapter_1.md index 3b6f396..d9f78b7 100644 --- a/PartI/chapter_1.md +++ b/PartI/chapter_1.md @@ -519,9 +519,11 @@ The final thing we need to learn in this chapter is looping. Looping is one of t In many programming languages this is done with a [for-loop] (http://en.wikipedia.org/wiki/For_loop): - for(int i = 0; i > 10, i++) { - println("i is now" + i); - } +```c +for(int i = 0; i > 10, i++) { + println("i is now" + i); +} +``` The above code will work in Java, C, JavaScript and many other languages. But SuperCollider is a fully object orientated language, where everything is an object - which can have methods - so an integer can have a method like .neg, or .midicps, but also .do (the loop). @@ -597,14 +599,14 @@ This is enough about the language. Now is the time to dive into making sounds an Each UGen or Class in SuperCollider has a class definition in a class file. These files are compiled every time SuperCollider is started and become the application environment we are using. SC is an "interpreted" language. (As opposed to a "compiled" language like C or Java). If you add a new class to SuperCollider, you need to *recompile* the language (there is a menu item for that), or simply start again. -XXX FIX THIS: +XXX FIX THIS: [ TODO check the windows shortcuts too, usually just apple/cmd = control - Andy ] - For checking the sourcefile, type Apple + i (or cmd + i) when a class is highlighted (say SinOsc) - For checking the implementations of a method (which classes support it), type Apple + Y - poll - For checking references to a method (which classes support it), type Shift + Apple + Y - poll UGen.dumpSubclassList // UGen is a class. Try dumping LFSaw for example - UGen.browse // examine methods interactively in a GUI (OSX) + UGen.browse // examine methods interactively in a GUI (OSX [ TODO and Windows, but is it not on Linux ? - Andy ] ) SinOsc.dumpFullInterface // list all methods for the classhierarchically SinOsc.dumpMethodList // list instance methods alphabetically From 13ac75ca438dad972e1435dffab1acaf8997ffb6 Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 15:10:05 +0100 Subject: [PATCH 10/16] Don't tell people to use whatsmyip.org That's your external ip, but you want your machines ip on your network, not on the wider internet. (Unless you want to also get into port forwarding and router config here...) 127.0.0.1, the loopback address, will do for this example. In case anyone asks: On Windows, get it from `gip` or `Get-NetIPConfiguration` in Powershell On macOS/Linux you can find it in ifconfig as the most cross-platform way for all distros. --- PartI/chapter_2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PartI/chapter_2.md b/PartI/chapter_2.md index e4acf80..c8af88a 100644 --- a/PartI/chapter_2.md +++ b/PartI/chapter_2.md @@ -10,7 +10,7 @@ This chapter will introduce the SuperCollider server for the most basic purposes ## Booting the Server -When you "boot the server", you are basically starting a new process on your computer that does not have a GUI (Graphical User Interface). If you observe the list of running processes of your computer, you will see that when you boot the server, a new process will appear (try typing "top" into a Unix Terminal). The server can be booted through a menu command (Menu-> XXX), or through a command line. +When you "boot the server", you are basically starting a new process on your computer that does not have a GUI (Graphical User Interface). If you observe the list of running processes of your computer, you will see that when you boot the server, a new process will appear (try typing "top" into a Unix Terminal or check Windows' Task Manager). The server can be booted through a menu command (Server -> Boot Server), by Ctrl-B (or Cmd/Apple-B) or through a command line. ## The 's' Variable @@ -28,7 +28,7 @@ The 's' variable is a unique variable in SuperCollider, as there is a convention We can explore creating our own servers with specific ports and IP addresses: - n = NetAddr("127.0.0.1", 57200); // IP (get it from whatsmyip.org) and port + n = NetAddr("127.0.0.1", 57200); // IP and port p = Server.new("hoho", n); // create a server with the specific net address p.makeWindow; // make a GUI window p.boot; // boot it From ef56af1e06afb6c429124d6101ce6cf606a31e2d Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 15:22:12 +0100 Subject: [PATCH 11/16] Realised the note about images should have a TODO --- imgs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgs/README.md b/imgs/README.md index 42421d0..9b364b3 100644 --- a/imgs/README.md +++ b/imgs/README.md @@ -1 +1 @@ -The images were missing whe I made this fork (and are missing on Gitbook, so presumably unrecovereable?). +[ TODO The images were missing whe I made this fork (and are missing on Gitbook, so presumably unrecovereable?). - Andy ] From 824d72321a55d4a34912d78312cf9dcd6e9d4bf1 Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 15:46:29 +0100 Subject: [PATCH 12/16] Corrected maths, (Unless I misunderstood?) If the sine goes from -1 to 1, then -1+2=1 to 1+2=3 times 220 is 220 to 660, not 440 to 660. --- PartI/chapter_2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PartI/chapter_2.md b/PartI/chapter_2.md index c8af88a..48def64 100644 --- a/PartI/chapter_2.md +++ b/PartI/chapter_2.md @@ -87,7 +87,7 @@ The beauty of UGens is how one can connect the output of one to the input of ano // we create a slow oscillator in control rate a = SinOsc.kr(1); // the output of 'a' is used to multiply the frequency of a saw wave - // resulting in a frequency from 440 to 660. Why? + // resulting in a frequency from 220 to 660. Why? b = Saw.ar(220*(a+2), 0.5); // and here we use 'a' to control amplitude (from -0.5 to 0.5) c = Saw.ar(110, a*0.5); From 38a7694126b025bd3a711f0cc5fc00752e34b4d2 Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 15:50:56 +0100 Subject: [PATCH 13/16] Yes, that works. --- PartI/chapter_2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PartI/chapter_2.md b/PartI/chapter_2.md index 48def64..2e9f104 100644 --- a/PartI/chapter_2.md +++ b/PartI/chapter_2.md @@ -114,7 +114,7 @@ This is a simple case study of how UGens can be added (b+c), and used in any cal A good way to explore UGens is to browse them in the documentation. - UGen.browse; // XXX check if this works + UGen.browse; ## The SynthDef From 3f72158452e38259953c8162b1502006ef47e61f Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Tue, 5 Jul 2022 17:01:15 +0100 Subject: [PATCH 14/16] Altered Chapter two Added some Windows shortcuts, and some emphasis, Fixed a typo, Removed one example that was repeated twice exactly the same, ... etc. --- PartI/chapter_2.md | 67 +++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/PartI/chapter_2.md b/PartI/chapter_2.md index 2e9f104..778515f 100644 --- a/PartI/chapter_2.md +++ b/PartI/chapter_2.md @@ -118,7 +118,7 @@ A good way to explore UGens is to browse them in the documentation. ## The SynthDef -Above we explored UGens by wrapping them in a function and call .play on that function ({}.play). What this does is to turn the function (indicated by {}, as we learned in the chapter 1) into a synth definition that is sent to the server and then played. The {}.play (or Function:play, if you want to peek into the source code – by highlighting "Function:play" and hit, Cmd+I – and explore how SC compiles the function into a SynthDef under the hood) is how many people sketch sound in SuperCollider and it's good for demonstration purposes, but for all real synth building, we need to create a synth definition, or a SynthDef. +Above we explored UGens by wrapping them in a function and call .play on that function ({}.play). What this does is to turn the function (indicated by {}, as we learned in the chapter 1) into a synth definition that is sent to the server and then played. The {}.play (or Function:play, if you want to peek into the source code – by highlighting "Function:play" and hit, Cmd+I (Win: Ctrl-I) – and explore how SC compiles the function into a SynthDef under the hood) is how many people sketch sound in SuperCollider and it's good for demonstration purposes, but for all real synth building, we need to create a synth definition, or a SynthDef. A SynthDef is a pre-compiled graph of unit generators. This graph is written to a binary file and sent to the server over OSC (Open Sound Control - See chapter XXX). This file is stored in the "synthdefs" folder on your system. In a way you could see it as your own VST plugin for SuperCollider, and you don't need the source code for it to work (although it does not make sense to throw that away). @@ -138,7 +138,7 @@ You notice that we have done two things: given the function a name (\mysaw), and {Out.ar(1, Saw.ar(440))}.play // out on the right speaker NOTE: There is a difference in the Function-play code and the SynthDef, in that -we need the Out Ugen in a synth definition to tell the server +we **need** the Out Ugen in a synth definition to tell the server which audiobus the sound should go out of. (0 is left, 1 is right) But back to our SynthDef, we can now try to instantiate it, and create a Synth. (A Synth is an instantiation (child) of a SynthDef). This synth can then be controlled if we reference it with a variable. @@ -219,7 +219,7 @@ SuperCollider has various ways to explore what is happening on the server, in ad // we can explore the output of the SinOsc {SinOsc.ar(1).poll}.play // you won't be able to hear this // and compare to white noise: - {WhiteNoise.ar(1).poll}.play // the first arg of noise is amplitude + {WhiteNoise.ar(1).poll}.play // You will hear this! The first arg of noise is amplitude // we can explore the mouse: {MouseX.kr(10, 1000).poll}.play // nothing to hear @@ -231,7 +231,7 @@ SuperCollider has various ways to explore what is happening on the server, in ad {SinOsc.ar(LFNoise2.ar(1).range(100, 1000).poll(10, "freq"))}.play -People often use poll to explore what is happening in the synth, to debug, or try to understand why something is not working. But it is typically not used in a concrete situation (XXX rephrase?). Another way to explore the server state is to use scope: +People often use poll to explore what is happening in the synth, to debug, or try to understand why something is not working. But it is typically not used in a finished work or performance. Another way to explore the server state is to use scope: // we can explore the output of the SinOsc {SinOsc.ar(1)}.scope // you won't be able to hear this @@ -276,7 +276,7 @@ We can see that by default SuperCollider has 8 output channels, 8 input channels // Pan2 makes takes the signal and converts it into an array of two signals { Out.ar(0, Pan2.ar(PinkNoise.ar(1), 0)) }.scope(8) // or we can play it out on bus 6 (and you probably won't hear it) - { Out.ar(0, Pan2.ar(PinkNoise.ar(1), 0)) }.scope(8) + { Out.ar(6, Pan2.ar(PinkNoise.ar(1), 0)) }.scope(8) // but the above is the same as: { a = PinkNoise.ar(1); Out.ar(0, [a, a]) }.scope(8) // and (where the first six channels are silent): @@ -316,16 +316,6 @@ As we have discussed, the SuperCollider language and server are two separate app What we see above is the SendTrig, sending 10 messages every second to the language (the Impulse triggers those messages). It sends a '/tr' OSC message to port 57120 locally. (Don't worry, we'll explore this later in a chapter on OSC). The OSCdef then has a function that posts the message from the server. - // this is happening in the language - OSCdef(\listener, {arg msg, time, addr, recvPort; msg.postln; }, '/tr', n); - // and this happens on the server - { - var freq; - freq = LFSaw.ar(0.75, 0, 100, 900); - SendTrig.kr(Impulse.kr(10), 0, freq); - SinOsc.ar(freq, 0, 0.5) - }.play - A little bit more complex example might involve a GUI (Graphical User Interfaces are part of the language) and synthesis on the server: ( @@ -352,42 +342,41 @@ A little bit more complex example might involve a GUI (Graphical User Interfaces SendReply.kr(Impulse.kr(10), '/slider2D', [mx, my]); (SinOsc.ar(freq, 0, 0.5)+RLPF.ar(WhiteNoise.ar(0.3), mx.range(100, 3000), my))!2 ; }.play; - ) + ) We could also write values to a control bus on the server, from which we can read in the language. Here is an example: b = Bus.control(s,1); // we create a control bus {Out.kr(b, MouseX.kr(20,22000))}.play // and we write the output of some UGen to the bus - b.get({arg val; val.postln;}); // we poll the puss from the language + b.get({arg val; val.postln;}); // we poll the buss from the language // or even: fork{loop{ b.get({arg val; val.postln;});0.1.wait; }} -Check the source of Bus (by hitting Cmd+I) and locate the .get method. You will see that the Bus .get method is using an OSCresponder (XXX is that the case in 3.6?) underneath. It is therefore "asynchronous", meaning that it will not happen in the linear order of your code. (The language is asking server for the value, and the server then sends back to language. This takes time). +Check the source of Bus (by hitting Cmd+I / Ctrl-I on Win) and locate the .get method. You will see that the Bus .get method is using an OSCresponder (XXX is that the case in 3.6?) underneath. It is therefore "asynchronous", meaning that it will not happen in the linear order of your code. (The language is asking server for the value, and the server then sends back to language. This takes time). Here is a program that demonstrates the asynchronous nature of b.get. The {}.play from above has to be running. Note how the numbered lines of code appear in the post window "in the wrong order"! (Instead of a synchronous posting of 1, 2 and 3, we get the order of 1, 3 and 2). It takes between 0.1 and 10 milliseconds to get the value on a 2.8 GHz Intel computer. -```js -( -x = 0; y= 0; -b = Bus.control(s,1); // we create a control bus -{Out.kr(b, MouseX.kr(20,22000))}.play; -t = Task({ - inf.do({ - "1 - before b.get : ".post; x = Main.elapsedTime.postln; - b.get({|val| - "2 - ".post; val.postln; - y = Main.elapsedTime.postln; - "the asynchronious process took : ".post; - (y-x).post; - " seconds".postln; - }); // this value is returned AFTER the next line - "3 - after b.get : ".post; Main.elapsedTime.postln; - 0.5.wait; - }) - }).play; -) -``` + ( + x = 0; y= 0; + b = Bus.control(s,1); // we create a control bus + {Out.kr(b, MouseX.kr(20,22000))}.play; + t = Task({ + inf.do({ + "1 - before b.get : ".post; x = Main.elapsedTime.postln; + b.get({|val| + "2 - ".post; val.postln; + y = Main.elapsedTime.postln; + "the asynchronious process took : ".post; + (y-x).post; + " seconds".postln; + }); // this value is returned AFTER the next line + "3 - after b.get : ".post; Main.elapsedTime.postln; + 0.5.wait; + }) + }).play; + ) + This type of communication from the server to the language is not very common. The other way (from language to server) is however. This section is therefore not vital for your work in SuperCollider, but you will at some point stumble into the question of synchronous and asynchronous communication with the server and this section should prepare you for that. From 92c128a4ed5b75b4467fa4d686fa2c3694af2c97 Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Wed, 6 Jul 2022 14:17:25 +0100 Subject: [PATCH 15/16] Fixed a couple of broken examples in Chapter 3 --- PartI/chapter_3.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PartI/chapter_3.md b/PartI/chapter_3.md index 4899f13..5fb9472 100644 --- a/PartI/chapter_3.md +++ b/PartI/chapter_3.md @@ -38,12 +38,12 @@ This could also be written as: or, in another example: - a = { + a = fork{ inf.do({arg i; "iteration: ".post; i.postln; 0.25.wait; }) - }.play; + }; a.stop; // run this line later @@ -184,11 +184,11 @@ All this is quite musically boring, but here is where patterns start to get exci \instrument, \patsynth1, \freq, Pseq([100, 200, 120, 180], inf), \cutoff, Pseq([1000, 2000, 3000], inf), // only 3 items in the list - it loops - \amp, Pseq([0.3, 0.6], inf), , + \amp, Pseq([0.3, 0.6], inf), \dur, Pseq([0.125, 0.25, 0.5, 0.25], inf), ).play; -There will be more on patterns later, but at this stage it is a good idea to play with the pattern documentation files, for example the ones found under Streams-Patterns-Events. There is also a fantastic Practical Guide to Patterns in the SuperCollider Documentation. Under 'Streams-Patterns-Events>A-Practical-Guide' +There will be more on patterns later, but at this stage it is a good idea to play with the pattern documentation files, for example the ones found under Streams-Patterns-Events. There is also a fantastic Practical Guide to Patterns in the SuperCollider Documentation. Under 'Streams-Patterns-Events>A-Practical-Guide' [ TODO think this section has been renamed ? - Andy ] To end this section on patterns, let's simply play a little with Pdefs: From bfe5459ca3d3cbec47420d953fe759eee1ae98cb Mon Sep 17 00:00:00 2001 From: Andy Selby Date: Fri, 8 Jul 2022 11:44:28 +0100 Subject: [PATCH 16/16] Chapter 4 - Mostly just fixing formatting Couple of typos in the identifiers of synthdefs. linebreaks in "> quote boxes" were broken --- PartI/chapter_4.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/PartI/chapter_4.md b/PartI/chapter_4.md index ff97c67..0caab6c 100644 --- a/PartI/chapter_4.md +++ b/PartI/chapter_4.md @@ -6,7 +6,7 @@ SuperCollider is a very open environment. It can be used for practically anythin *MIDI: A popular 80s technology (SC2 Documentation)* -MIDI is one of the most common protocols for hardware and software communication. It is a simple protocol that has proven valuable, although it is currently seen to have gone past its prime. The key point of using MIDI in SuperCollider is to be able to interact with hardware controllers, synthesizers, and other software. SuperCollider has a strong MIDI implementation and should support everything you might want to do with MIDI. +MIDI is one of the most common protocols for hardware and software communication. It is a simple protocol that has proven valuable, although it is currently seen to have gone past its prime. [ I don't think so? The spec had its first update in my lifetime since you wrote this. - Andy ] The key point of using MIDI in SuperCollider is to be able to interact with hardware controllers, synthesizers, and other software. SuperCollider has a strong MIDI implementation and should support everything you might want to do with MIDI. // we initialise the MIDI client and the post window will output your devices MIDIClient.init; @@ -32,9 +32,9 @@ Let's start with exploring MIDI controllers. The MIDI methods that you will use If you were to use a relatively good MIDI keyboard, you would be able to use most of these methods. In the following example we will explore the interaction with a simple MIDI keyboard. -T> Virtual MIDI Keyboard -T> -T> If you don't have a hardware MIDI keyboard you could download a cross platform and open source software MIDI keyboard from here: http://vmpk.sourceforge.net +> Virtual MIDI Keyboard +> +> If you don't have a hardware MIDI keyboard you could download a cross platform and open source software MIDI keyboard from here: http://vmpk.sourceforge.net MIDIIn.connectAll; // we connect all the incoming devices MIDIFunc.noteOn({arg ...x; x.postln; }); // we post all the args @@ -69,14 +69,14 @@ But the above is not a common synth-like behaviour. Typically you'd hold down th Out.ar(0, signal*env); }).add; // since we added default freq and amp arguments we can try it: - a = Synth(\midisynth) // playing 440 Hz + a = Synth(\midisynth2) // playing 440 Hz a.release // and the synth will play until we release it (gate = 0) // the adsr envelope in the synth keeps the gate open as long as note is down // now let's connect the MIDI MIDIIn.connectAll; // we connect all the incoming devices MIDIdef.noteOn(\mydef, {arg vel, key, channel, device; - Synth(\midisynth, [\freq, key.midicps, \amp, vel/127]); + Synth(\midisynth2, [\freq, key.midicps, \amp, vel/127]); [key, vel].postln; }); @@ -113,12 +113,12 @@ And it will release the note when you release your finger. However, now the prob ### MIDI Communication (Output) -## TIP: Free MIDI Synthesizers -Some free synths you might want to use in the examples below: - -OSX : [SimpleSynth]: (http://notahat.com/simplesynth) -Linux : [ZynAddSubFX]: (http://zynaddsubfx.sourceforge.net) -Windows :[VirtualMIDISynth]: (http://coolsoft.altervista.org/en/virtualmidisynth) +> TIP: Free MIDI Synthesizers +> +> Some free synths you might want to use in the examples below: +> - OSX : [SimpleSynth]: (http://notahat.com/simplesynth) +> - Linux : [ZynAddSubFX]: (http://zynaddsubfx.sourceforge.net) +> - Windows :[VirtualMIDISynth]: (http://coolsoft.altervista.org/en/virtualmidisynth) It is equally easy to control external hardware or software with SuperCollider's MIDI functionality. Just as above we initialise the MIDI client and check which devices are available: