├── README.md ├── audio-bus-channels-illustration.pdf ├── Quick_Recording.scd ├── Epic_Pad_Example.scd ├── SuperCollider_Tweets.scd ├── Control_Structures.scd ├── Groups.scd ├── Fun_with_Control_Buses.scd ├── Functions.scd ├── MIDI_demo.scd ├── Examples_of_Complete_Pieces.scd ├── MIDI_Keyboard_Sampler.scd ├── OSC_demo.scd ├── TGrains_Example.scd ├── Patterns_Exercise_1.scd ├── Envelopes.scd ├── GUI_demo.scd ├── Enclosures.scd ├── Basics_Tutorial.scd ├── SynthDefs_2013.scd ├── 12_Things_You_Probably_Want_To_Know.scd ├── 10_Things_You_Need_To_Know.scd ├── Basic_Computer_Music_Concepts.scd ├── Patterns_Tutorial.scd ├── UGens_Envelopes_Buses_Tutorial.scd └── hanon_2014-01-01.schelp /README.md: -------------------------------------------------------------------------------- 1 | SuperCollider_Tutorials 2 | ======================= 3 | 4 | SC tutorials -------------------------------------------------------------------------------- /audio-bus-channels-illustration.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunoruviaro/SuperCollider_Tutorials/HEAD/audio-bus-channels-illustration.pdf -------------------------------------------------------------------------------- /Quick_Recording.scd: -------------------------------------------------------------------------------- 1 | "SynthDefs_2013.scd".loadRelative; 2 | 3 | 4 | // -------------- // 5 | // QUICK RECORDING 6 | // -------------- // 7 | 8 | // Start recording all SC output 9 | // (Post window will indicate where file is saved) 10 | s.record; 11 | 12 | // Stop recording 13 | s.stopRecording; 14 | 15 | // IMPORTANT: 16 | // Do not press ctrl+period BEFORE evaluating the line above -- control period kills everything, including the recording!! -------------------------------------------------------------------------------- /Epic_Pad_Example.scd: -------------------------------------------------------------------------------- 1 | // Nice synth sound taken from the thread "Epic Pads" in the SC mailing list 2 | // http://new-supercollider-mailing-lists-forums-use-these.2681727.n2.nabble.com/Epic-Pads-td7487382.html 3 | 4 | ( 5 | { 6 | var freq = { [60,64,65,67].choose.midicps * LFNoise2.kr(1,0.01,1) }!24; 7 | var gen = LFSaw.ar(freq) * 0.1; 8 | var fmod = LFCub.kr(1/12).range(1, MouseX.kr(2,16)); 9 | var rqmod = LFNoise2.kr(1/8).range(0.1,1.0); 10 | var snd = RLPF.ar(gen, freq * fmod, rqmod); 11 | Splay.ar(snd) 12 | }.play; 13 | ) 14 | 15 | -------------------------------------------------------------------------------- /SuperCollider_Tweets.scd: -------------------------------------------------------------------------------- 1 | {LocalOut.ar(a=CombN.ar(BPF.ar(LocalIn.ar(2)*7.5+Saw.ar([32,33],0.2),2**LFNoise0.kr(4/3,4)*300,0.1).distort,2,2,40));a}.play//#supercollider 2 | 3 | 4 | play{VarSaw.ar((Hasher.ar(Latch.ar(SinOsc.ar((1..4)!2),Impulse.ar([5/2,5])))*300+300).round(60),0,LFNoise2.ar(2,1/3,1/2))/5}//#supercollider 5 | 6 | Ndef('x',{x=Ndef('x').ar+0.01;a=BPF.ar(x,6**Latch.ar(x,Dust.ar(x))*200,0.1).sin;9.do{a=AllpassN.ar(a,0.2,{0.2.rand}!2,9)};a+a.mean}).play; 7 | 8 | play{LFCub.ar(LFSaw.kr(LFPulse.kr(1/4,1/4,1/4)*2+2,1,-20,50))+(WhiteNoise.ar(LFPulse.kr(4,0,LFPulse.kr(1,3/4)/4+0.05))/8)!2} 9 | -------------------------------------------------------------------------------- /Control_Structures.scd: -------------------------------------------------------------------------------- 1 | // ------------------ // 2 | // CONTROL STRUCTURES // 3 | // ------------------ // 4 | 5 | 6 | 10.do({ arg count; [count, "Hello"].postln }); 7 | 8 | 9 | 10 | ["file1", "file2", "file3"].do({ arg item, count; [count, item.reverse].postln }); 11 | 12 | [1, 2, 3, 4, 5].collect({arg bananaItem; bananaItem*2 }) 13 | 14 | ~num = 100; 15 | 16 | if(~num > 10, { "true".postln }, { "false".postln }); 17 | 18 | ( 19 | case 20 | {~num == 0} { "WOW".postln } 21 | {~num == 1} { "ONE!".postln } 22 | {~num < 0 } { "negative".postln } 23 | { true } { "last case scenario".postln } 24 | ) -------------------------------------------------------------------------------- /Groups.scd: -------------------------------------------------------------------------------- 1 | // ------- // 2 | // GROUPS 3 | // ------- // 4 | 5 | 6 | // Using groups to organize order of execution 7 | 8 | 9 | 10 | // Watch everything in the NodeTree 11 | s.plotTree; 12 | 13 | 14 | // Define groups in proper order 15 | ~sources = Group.new 16 | ~effects = Group.new(~sources, \addAfter); 17 | ~master = Group.new(~effects, \addAfter); 18 | 19 | // Create some buses 20 | ~reverbBus = Bus.audio(s, 2); 21 | ~masterBus = Bus.audio(s, 2); 22 | 23 | 24 | // This is the source sound 25 | {Out.ar(~reverbBus, SinOsc.ar([800, 880])*LFPulse.ar(2)*0.1)}.play(~sources); 26 | 27 | // This is some reverb 28 | {Out.ar(~masterBus, FreeVerb.ar(In.ar(~reverbBus, 2), mix: 0.5, room: 0.9))}.play(~effects); 29 | 30 | // Some silly master volume control with mouse 31 | {Out.ar(0, In.ar(~masterBus) * MouseY.kr(0, 1))}.play(~master) 32 | 33 | -------------------------------------------------------------------------------- /Fun_with_Control_Buses.scd: -------------------------------------------------------------------------------- 1 | // LOAD SOUND FILES 2 | ~buf1 = Buffer.read(s, "/home/ruviaro/Music/SuperCollider/wheels-mono.wav"); 3 | ~buf2 = Buffer.read(s, "/home/ruviaro/Music/Sounds/mussorgsky-mono.wav"); 4 | 5 | 6 | // CREATE CONTROL BUSES 7 | ( 8 | ~kbus1 = Bus.control; // a control bus 9 | ~kbus2 = Bus.control; // a control bus 10 | ) 11 | 12 | 13 | // START THE CONTROLS 14 | ( 15 | a = {Out.kr(~kbus1, LFNoise1.kr(1).range(0.5, 1))}.play; 16 | b = {Out.kr(~kbus2, LFClipNoise.kr(1/5))}.play; 17 | ) 18 | 19 | e = Synth("control", [\par1, 1, \par2, 3]); 20 | 21 | e.set(\par1, 15) 22 | 23 | a.free; b.free; 24 | 25 | // GRANDMASTER FLASH (LEFT), MUSSORGSKY (RIGHT) 26 | ( 27 | { 28 | var speed, direction; 29 | speed = In.kr(~kbus1); 30 | direction = In.kr(~kbus2); 31 | Out.ar(0, PlayBuf.ar(1, [~buf1, ~buf2], (speed * direction), loop: 1) * 0.3); 32 | }.play; 33 | ) 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Functions.scd: -------------------------------------------------------------------------------- 1 | // ------ // 2 | // SIMPLE 3 | // ------ // 4 | 5 | // Define the function 6 | f = { 2 + 2 }; 7 | 8 | // Use it 9 | f.value; 10 | 11 | 12 | // --------------- // 13 | // WITH ARGUMENTS 14 | // -------------- // 15 | 16 | // Functions with Arguments 17 | 18 | // Define a function with input arguments 19 | f = { arg myNumberOne, myNumberTwo; myNumberOne + myNumberTwo }; 20 | 21 | // Use it 22 | f.value(1, 5); 23 | 24 | 25 | // --------- // 26 | // EXERCISE 27 | // --------- // 28 | 29 | // 1. Write a function that takes a string of characters as input and reverses it. For example, 30 | 31 | f.value("Pira"); 32 | 33 | // should return ariP 34 | 35 | 36 | 37 | // 2. Write a function that takes a low and a high frequency values and plays a random note within that range: 38 | 39 | ~playNote.value(lo: 100, hi: 1000); 40 | 41 | 42 | // TIP 43 | (freq: 1000).play; 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /MIDI_demo.scd: -------------------------------------------------------------------------------- 1 | // --------- // 2 | // MIDI demo // 3 | // --------- // 4 | 5 | 6 | // Quick way to connect all available devices to SC 7 | MIDIIn.connectAll; 8 | 9 | 10 | // Quick way to see all incoming MIDI messages 11 | MIDIFunc.trace(true); 12 | MIDIFunc.trace(false); // stop it 13 | 14 | // Quick way to listen for a specific controller number 15 | MIDIdef.cc(\someCC, {arg a, b; [a, b].postln}); 16 | 17 | // A Synth for quick tests 18 | ( 19 | SynthDef("quick", {arg freq, amp; 20 | Out.ar(0, SinOsc.ar(freq, 0, amp) * EnvGen.kr(Env.perc, doneAction: 0)); 21 | }).add; 22 | ) 23 | 24 | // Play from a keyboard 25 | ( 26 | MIDIdef.noteOn(\someKeyboard, { arg vel, note; 27 | Synth("quick", [\freq, note.midicps, \amp, vel.linlin(0, 127, 0, 1)]); 28 | }); 29 | ) 30 | 31 | // Create a pattern and play that from the keyboard 32 | ( 33 | a = Pbind( 34 | \instrument, "quick", 35 | \degree, Pwhite(0, 10, 5), 36 | \amp, Pwhite(0, 0.1), 37 | \dur, 0.1 38 | ); 39 | ) 40 | 41 | // test 42 | a.play 43 | 44 | // Trigger pattern from keyboard 45 | MIDIdef.noteOn(\quneo, { arg vel, note; a.play }); 46 | 47 | 48 | // Listen only for cc #7 from channel 0 49 | MIDIdef.cc(\someSpecificControl, {arg a, b; [a, b].postln}, ccNum: 7, chan: 0); 50 | 51 | 52 | 53 | MIDIdef.cc(\quneoCCtyui, {arg value, ccNumber; "WOW".postln}, 22); 54 | 55 | 56 | -------------------------------------------------------------------------------- /Examples_of_Complete_Pieces.scd: -------------------------------------------------------------------------------- 1 | 2 | // ================= 3 | // Example pieces 4 | // ================= 5 | 6 | // James Harkins on Blue Rondo Phase by Dave Brubeck: 7 | "https://soundcloud.com/dewdrop_world/blue-rondo-phase".openDocument; 8 | 9 | // Kraftwerk's SpaceLab 10 | "https://github.com/supercollider/supercollider/blob/master/examples/pieces/spacelab.scd".openDocument; 11 | 12 | // More example pieces 13 | "https://github.com/supercollider/supercollider/tree/master/examples/pieces".openDocument; 14 | 15 | // ================= 16 | // Student pieces 17 | // ================= 18 | 19 | // Created just after a few weeks of learning SuperCollider 20 | 21 | // Reilly Farrell's "Waltz of the Cabypara" (AM synthesis) 22 | "https://soundcloud.com/capybarrage-reilly/waltz-of-the-capybara".openDocument 23 | 24 | // Reilly Farrell's Robo-Enya (Sampling) 25 | "https://soundcloud.com/capybarrage-reilly/robo-enya-updated".openDocument; 26 | 27 | // Jonathan Coon's "Avant Gard" (Pluck) 28 | "https://soundcloud.com/jonathan-coon/avant-garde-assignment-4".openDocument; 29 | 30 | // Tina Traboulsi's "Pachelbel's Canon" 31 | "https://soundcloud.com/tina-traboulsi/pachabels-canon".openDocument; 32 | 33 | 34 | // =========================== 35 | // Synthesis Techniques Demo 36 | // =========================== 37 | 38 | "http://sccode.org/bruno".openDocument; 39 | -------------------------------------------------------------------------------- /MIDI_Keyboard_Sampler.scd: -------------------------------------------------------------------------------- 1 | // ============================================ 2 | // Simple MIDI-controlled sampler 3 | // 2011-11-07, BTR 4 | // 2014-07-02 (update), BTR 5 | // ============================================ 6 | 7 | // ************ 8 | // LOAD SAMPLES 9 | // ************ 10 | 11 | ~buffers = "/home/ruviaro/Music/Sounds/caminhos-cruzados-short-samples/stereo/*/*.wav".pathMatch.collect({ arg i; Buffer.read(s, i)}); 12 | 13 | ~buffers[3] 14 | ~buffers.size 15 | 16 | // Buffer.freeAll 17 | 18 | // Quick test 19 | {PlayBuf.ar(2, ~buffers[rrand(0, (~buffers.size-1))])}.play; // number of channels and buffer 20 | 21 | 22 | // ********* 23 | // SYNTHDEF 24 | // ********* 25 | 26 | // Create a SynthDef 27 | 28 | ( 29 | SynthDef("midi-sample-playback", { 30 | arg bufnum = 0, rate = 1, amp = 1, gate = 1; 31 | var snd, env; 32 | env = EnvGen.ar( 33 | envelope: Env.adsr( 34 | attackTime: 0.01, 35 | decayTime: 0.3, 36 | sustainLevel: 0.5, 37 | releaseTime: 0.3), 38 | gate: gate, 39 | doneAction: 2); 40 | snd = PlayBuf.ar(2, bufnum, BufRateScale.kr(bufnum) * rate, loop: 1); 41 | snd = snd * env * amp; 42 | Out.ar(0, snd) 43 | }).add; 44 | ) 45 | 46 | // test 47 | f = Synth("midi-sample-playback", [\bufnum, rrand(0, 30).postln, \rate, 1.1]); 48 | 49 | f.set(\rate, 2); 50 | f.set(\gate, 0); 51 | f.release; // shortcut to .set(\gate, 0) 52 | 53 | 54 | // MIDI STUFF 55 | 56 | MIDIIn.connectAll; 57 | 58 | // note on becomes bufnum 59 | // note off sends gate=0 --> release envelope 60 | 61 | ( 62 | var midiSamplerArray = Array.newClear(128); // array has one slot per possible MIDI note 63 | 64 | MIDIdef.noteOn(key: \sampleOn, 65 | func: { arg velocity, noteNumber; 66 | midiSamplerArray[noteNumber] = Synth("midi-sample-playback", [ 67 | \bufnum, noteNumber.linlin(41, 72, 0, ~buffers.size-1), 68 | \rate, velocity.linlin(0, 127, 0.95, 1.5), 69 | \amp, velocity.linlin(0, 127, 0, 1) 70 | ])}); 71 | 72 | MIDIdef.noteOff(key: \sampleOff, 73 | func: { arg velocity, noteNumber; 74 | midiSamplerArray[noteNumber].set(\gate, 0)}); 75 | 76 | ) 77 | 78 | // Note that in my sampler I chose to use velocity not only for amplitude, but also to change sample playback rate. 79 | 80 | 81 | 82 | /* 83 | http://new-supercollider-mailing-lists-forums-use-these.2681727.n2.nabble.com/Using-SC-with-MIDI-sequencer-or-keyboard-td3684526.html#a3686405 84 | */ 85 | -------------------------------------------------------------------------------- /OSC_demo.scd: -------------------------------------------------------------------------------- 1 | // ====== 2 | // OSCdef 3 | // ====== 4 | 5 | /* 6 | Simplest demo: 7 | 8 | GOAL: to send OSC messages from a smartphone to your computer (or computer to computer) 9 | 10 | - Install any free OSC app on your phone (for example, gyrosc); 11 | - Find the IP address of your computer; 12 | - Set the IP address of your computer into the OSC app (as 'target'); 13 | - Set the SC receiving port into the OSC app; (usually 57120) 14 | - Check the exact message path that the app uses to send OSC to. 15 | - In the example below I take a 0 or 1 message from a button on the phone. 16 | 17 | If doing computer to computer, simply have SuperCollider running on both sides to receive and send. 18 | See end of this file for an example of how to send a message to another computer. 19 | */ 20 | 21 | 22 | // SET UP OSCdef ON THE RECEIVING COMPUTER 23 | // Simple OSCdef: 24 | ( 25 | OSCdef( 26 | key: \postargs, 27 | func: {arg ...args; args.postln}, 28 | path: '/stuff') 29 | ) 30 | 31 | // Adjust the path above to whatever you want. 32 | // On the OSC app, indicate the IP address of your computer and the port. 33 | // This is the port SC uses to receive. Normally 57120 (sometimes 57121): 34 | NetAddr.langPort; // evaluate to see 35 | 36 | 37 | // As long as your phone is sending messages to the proper path, you should see them arriving on the computer. 38 | // If sending messages from another computer (not a phone), SuperCollider can send OSC messages like this: 39 | 40 | // USE THIS ON THE MACHINE SENDING MESSAGES 41 | ~destination = NetAddr("127.0.0.1", 57120); // use correct IP address of destination computer 42 | 43 | ~destination.sendMsg("/stuff", "heelloooo"); 44 | 45 | 46 | 47 | 48 | // ******************************************** 49 | // If you don't have another machine to send OSC messages from, try this: 50 | 51 | ( 52 | OSCdef( 53 | key: \soliloquy, 54 | func: {arg ...args; args.postln}, 55 | path: '/stuff') 56 | ) 57 | 58 | ~self = NetAddr("127.0.0.1", 57120); // send messages internally (from this computer to this computer) 59 | 60 | ~self.sendMsg("/stuff", "heelloooo"); 61 | 62 | // Here's a message being sent every 2 seconds: 63 | SystemClock.sched(0,{~self.sendMsg("/stuff", "hello-from-me", 8888.rand); 2.0}); 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | // ================================================ 72 | // since SC 3.5 you can open up additional ports: 73 | thisProcess.openUDPPort(1121); // attempt to open 1121 74 | // return true when it succeeded, false if not 75 | 76 | thisProcess.openPorts; // list all open custom ports 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /TGrains_Example.scd: -------------------------------------------------------------------------------- 1 | ////////////////////// 2 | // GRANULAR SYNTHESIS 3 | ////////////////////// 4 | 5 | 6 | // ************************** 7 | // FIRST STEP 8 | // 9 | // Copy the sound file of a song into /Music/SuperCollider/. 10 | // Important: it has to be a mono wave file. 11 | // If you have a stereo file, use Audacity to convert it to mono. 12 | 13 | 14 | // ************************** 15 | // SECOND STEP 16 | // 17 | // Load the sound file into a buffer in memory: 18 | 19 | ( 20 | Buffer.freeAll; 21 | 22 | ~mybuffer = Buffer.read( 23 | server: s, 24 | path: "~/Music/SuperCollider/wheels-mono.wav".standardizePath, 25 | bufnum: 55); // just an arbitrary number I chose for this buffer 26 | ) 27 | 28 | // You can retrieve some info from the buffer just to double check: 29 | 30 | ~mybuffer.numChannels; // how many channels are stored in the buffer 31 | ~mybuffer.numFrames/44100; // duration of buffer in seconds (= duration of sound file) 32 | ~mybuffer.bufnum; // buffer number 33 | 34 | // You can also simply play back the sound that was loaded into the buffer: 35 | 36 | {PlayBuf.ar(1, 55)}.play; 37 | 38 | // ************************** 39 | // THIRD STEP 40 | // 41 | // Granulate it! 42 | 43 | ( 44 | { 45 | var buffer = ~mybuffer.bufnum; 46 | var bufdur = BufDur.kr(buffer); 47 | TGrains.ar( 48 | numChannels: 2, 49 | trigger: Impulse.ar(1), 50 | bufnum: buffer, 51 | rate: 1, 52 | centerPos: 3, 53 | dur: 0.5, 54 | amp: 1) 55 | }.play; 56 | ) 57 | 58 | // Play around with the various grain parameters. 59 | 60 | /* 61 | 62 | Explanation of the important parameters: 63 | 64 | TRIGGER is how many grains to play per second; 65 | BUFNUM is what buffer to granulate; 66 | RATE is transposition factor ("playback rate"): 1 is normal, 2 is octave above, 0.5 is octave below, etc. 67 | CENTERPOS stands for "center position". It is the time location in the sound to pick a grain from. 68 | DUR is duration of a grain. 69 | AMP is amplitude of a grain. 70 | 71 | Instead of having fixed numbers for these parameters, you can try using other functions, like random number generators or mouse control. For example: 72 | 73 | MouseX.kr(0, bufdur) // control centerPos with the horizontal movement of mouse 74 | 75 | LFNoise0.kr(1).range(0, bufdur) // control centerPos randomly 76 | 77 | Impulse.ar(MouseY.kr(0.1, 0.5)) // control number of grains per second with mouse 78 | 79 | etc... 80 | 81 | */ 82 | 83 | 84 | 85 | ///////////////////////////////////// 86 | 87 | // The structure below will let you change the values and re-evaluate in real time, without having to stop the sounds and start again. It will cross fade between each evaluation. 88 | 89 | Ndef(\a).play; // start; 90 | Ndef(\a).fadeTime = 1; // fadeTime (crossfade between changes) 91 | 92 | // Evaluate the grains, 93 | // Change them as you like, 94 | // Evaluate again, 95 | // etc 96 | 97 | ( 98 | Ndef(\a, 99 | { 100 | var buffer = ~mybuffer.bufnum; 101 | var bufdur = BufDur.kr(buffer); 102 | Mix(TGrains.ar( 103 | numChannels: 2, 104 | trigger: Impulse.ar(MouseX.kr(1/4,10)), // change these numbers 105 | bufnum: buffer, 106 | rate: MouseX.kr(1,2), // change these numbers 107 | centerPos: 3, // LFNoise0.kr(1).range(0,5), 108 | dur: MouseY.kr(0.5,5), 109 | amp: 0.5)) 110 | }) 111 | ) 112 | 113 | // Try any of these: 114 | // LFNoise0.kr(a).range(b, c), 115 | // MouseX.kr(a, b), 116 | // MouseY.kr(a, b), 117 | 118 | -------------------------------------------------------------------------------- /Patterns_Exercise_1.scd: -------------------------------------------------------------------------------- 1 | // ********************************************** 2 | // DAY 1 LAB - CCRMA SUPERCOLLIDER WORKSHOP 2012 3 | // ********************************************** 4 | 5 | // LAB EXERCISE 1 6 | // Analyze the Pbind below. Find out: 7 | // a) The exact frequencies being played (rounded to one decimal only); 8 | // b) The closest integer MIDI note of these frequencies. 9 | // Hint: look for MIDI in the SimpleNumbers help file. 10 | 11 | ( 12 | Pbind( 13 | \freq, Pseq(([ 1/1, 3/2, 4/3, 9/8, 16/9, 5/4, 8/5 ] * 440) + 10, 1), 14 | \dur, Prand([0.2, 0.4, 0.3], inf) 15 | ).play 16 | ) 17 | 18 | /* 19 | Pbind above and some examples borrowed from Charles Céleste Hutchins: 20 | http://sc3howto.blogspot.com/2010/06/pbinds.html 21 | */ 22 | 23 | 24 | // LAB EXERCISE 2 25 | /* 26 | Take the Pbind from Exercise #1. Modify it in the following way: 27 | a) Add an offset to each frequency, randomly chosen between 10 and 20 Hz each time 28 | b) Durations: 29 | [0.2, 0.4] played twice 30 | [0.3, 0.3, 0.6] played once 31 | ...and repeat the whole thing 3 times. 32 | 33 | */ 34 | 35 | 36 | // LAB EXERCISE 3 37 | // Copy once more the original Pbind from Exercise #1. 38 | // Make it play a bass line with the following durations: 39 | // [1/2, 1/2, 1/2, 1/2, 3/4, 3/4, 1/2] 40 | // Then, changing only one value, make it play twice as fast. 41 | // Finally, using the \sustain key, make it staccato (i.e., sustain values shorter than duration). 42 | 43 | 44 | // LAB EXERCISE 4 45 | // Listen to the audio file Day_1_LAB_Exercise_4.flac. 46 | // Your goal is to create a Pattern that sounds like that. 47 | // Use your solution to Exercise 3 as point of departure. 48 | 49 | // http://ccrma.stanford.edu/~ruviaro/temp/Day_1_LAB_Exercise_4.flac 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | // Scroll down to see the solutions. 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | //////////////////////////////////////////////////////////////////////////// 83 | // LAB EXERCISE 1 SOLUTION 84 | // Frequencies: 85 | Pseq(([ 1/1, 3/2, 4/3, 9/8, 16/9, 5/4, 8/5 ] * 440) + 10, 1).asStream.all.round(0.1); 86 | // MIDI notes: 87 | Pseq(([ 1/1, 3/2, 4/3, 9/8, 16/9, 5/4, 8/5 ] * 440) + 10, 1).asStream.all.cpsmidi.round; 88 | 89 | 90 | //////////////////////////////////////////////////////////////////////////// 91 | // LAB EXERCISE 2 SOLUTION 92 | ( 93 | Pbind( 94 | \freq, Pseq([ 1/1, 3/2, 4/3, 9/8, 16/9, 5/4, 8/5 ] * 440, inf) + Pwhite(10,20,inf), 95 | \dur, Pseq([Pseq([0.2, 0.4], 2), Pseq([0.3, 0.3, 0.6], 1)], 3) 96 | ).play 97 | ) 98 | 99 | //////////////////////////////////////////////////////////////////////////// 100 | // LAB EXERCISE 3 SOLUTION 101 | ( 102 | Pbind( 103 | \freq, Pseq([ 1/1, 3/2, 4/3, 9/8, 16/9, 5/4, 8/5 ] * 40 , inf), 104 | \dur, Pseq( [1/2, 1/2, 1/2, 1/2, 3/4, 3/4, 1/2] * 0.5, 10), 105 | \sustain, Pseq( [1/2, 1/2, 1/2, 1/2, 3/4, 3/4, 1/2] * 0.1, 10) 106 | ).play 107 | ) 108 | // Another solution, this time using a local variable. 109 | // Ask your instructors about local variables... :-) 110 | ( 111 | var durs = [1/2, 1/2, 1/2, 1/2, 3/4, 3/4, 1/2]; 112 | Pbind( 113 | \freq, Pseq([ 1/1, 3/2, 4/3, 9/8, 16/9, 5/4, 8/5 ] * 40 , inf), 114 | \dur, Pseq(durs * 0.5, 10), 115 | \sustain, Pseq(durs * 0.1, 10) 116 | ).play 117 | ) 118 | 119 | 120 | //////////////////////////////////////////////////////////////////////////// 121 | // LAB EXERCISE 4 SOLUTION 122 | Pseq( 123 | [Pbind( 124 | \freq, Pseq([ 1/1, 3/2, 4/3, 9/8, 16/9, 5/4, 8/5 ] * 40 , inf), 125 | \dur, Pseq( [1/2, 1/2, 1/2, 1/2, 3/4, 3/4, 1/2] * 0.5, 1), 126 | \sustain, Pseq( [1/2, 1/2, 1/2, 1/2, 3/4, 3/4, 1/2] * 0.1, 1) 127 | ), 128 | Pbind( 129 | \freq, Prand([ 1/1, 3/2, 4/3, 9/8, 16/9, 5/4, 8/5 ] * 60 , inf), 130 | \dur, Pseq( [1/2, 1/2, 1/2, 1/2, 3/4, 3/4, 1/2] * 0.5, 1), 131 | \sustain, Pseq( [1/2, 1/2, 1/2, 1/2, 3/4, 3/4, 1/2] * 0.1, 1) 132 | )], inf).play; 133 | 134 | 135 | -------------------------------------------------------------------------------- /Envelopes.scd: -------------------------------------------------------------------------------- 1 | // ----------- // 2 | // Envelopes 3 | // ----------- // 4 | 5 | 6 | /* Use Env.new to define an envelope from scratch using breakpoints. The first three arguments to Env.new are: levels, times, and curve (there are two more args, see help file). 7 | 8 | levels - an array of levels. The first level is the initial value of the envelope. 9 | times - an array of durations of segments in seconds. There should be one fewer duration than there are levels. 10 | curve - choose from 'step', 'linear', 'exponential', 'sine', 'welch' (see help file for details). */ 11 | 12 | Env.new([0, 1, 0.3, 0.8, 0], [2, 3, 1, 4],'linear').test.plot; // this will play a test tone with envelope 13 | 14 | // Here's how to "read" the two arrays above: 15 | // "Go from 0 to 1 in two seconds; 16 | // then go from 1 to 0.3 in 3 seconds; 17 | // then go from 0.3 to 0.8 in 1 second; 18 | // finally go from 0.8 to 0 in 4 seconds." 19 | 20 | // If you choose 'exponential', you can't use zero in the first array, but something like 0.001 will do: 21 | 22 | Env.new([0.001, 1, 0.3, 0.8, 0.001], [2, 3, 1, 4],'exponential').test.plot; 23 | 24 | // You can also create some standard frequently-used envelope shapes by supplying durations to the following methods: 25 | 26 | 27 | // LINE ENVELOPE 28 | // *linen(attackTime, sustainTime, releaseTime, level, curve) 29 | 30 | Env.linen(attackTime: 1, sustainTime: 2, releaseTime: 3, level: 0.6).test.plot; 31 | Env.linen(0.1, 0.2, 0.1, 0.6).test.plot; 32 | Env.linen(1, 2, 3, 0.6, 'sine').test.plot; 33 | Env.linen(1, 2, 3, 0.6, 'welch').test.plot; 34 | 35 | 36 | // TRIANGLE ENVELOPE 37 | // *triangle(duration, level) 38 | 39 | Env.triangle(1, 1).test.plot; 40 | 41 | 42 | // HANNING ENVELOPE (BELL SHAPE) 43 | // *sine(duration, level) 44 | 45 | Env.sine(1,1).test.plot; 46 | 47 | // PERCUSSIVE ENVELOPE 48 | // *perc(attackTime, releaseTime, peakLevel, curve) 49 | 50 | Env.perc(0.05, 1, 1, 0).test.plot; // linear 51 | Env.perc(0.05, 1, 1, -4).test.plot; // exponential 52 | Env.perc(0.001, 1, 1, -4).test.plot; // sharper attack 53 | Env.perc(0.001, 1, 1, -8).test.plot; // change curvature 54 | Env.perc(1, 0.01, 1, 4).test.plot; // reverse envelope 55 | 56 | 57 | // --------------------------------------- // 58 | // USING ENVELOPES IN SYNTH DEFINITIONS 59 | // --------------------------------------- // 60 | 61 | // In order to "perform" an Env inside a Synth, you need EnvGen, the Envelope Generator: 62 | 63 | {SinOsc.ar(800, 0, 0.5) * EnvGen.kr(Env.perc(0.001, 1))}.play; 64 | 65 | // In a SynthDef: 66 | ( 67 | SynthDef("env-example", { arg freq = 440, amp = 0.2; 68 | var snd, env; 69 | snd = SinOsc.ar(freq: freq, mul: amp); 70 | env = EnvGen.kr(Env.perc(attackTime: 0.001, releaseTime: 1), doneAction: 2); 71 | Out.ar(0, snd * env); 72 | }).add; 73 | ) 74 | 75 | Synth("env-example"); 76 | 77 | 78 | // There is a shortcut to EnvGen. Here are the examples above rewritten using the shortcut: 79 | 80 | { SinOsc.ar(800, 0, 0.5) * Env.perc(0.001, 1).kr }.play; 81 | 82 | // In a SynthDef: 83 | ( 84 | SynthDef("env-example-2", { arg freq = 440, amp = 0.2; 85 | var snd, env; 86 | snd = SinOsc.ar(freq: freq, mul: amp); 87 | env = Env.perc(attackTime: 0.001, releaseTime: 1).kr(doneAction: 2); 88 | Out.ar(0, snd * env); 89 | }).add; 90 | ) 91 | 92 | Synth("env-example-2"); 93 | 94 | 95 | s.plotTree 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | // Example using a trigger. 114 | // The "gate" argument of EnvGen triggers the envelope and holds it open while value is > 0. 115 | // See EnvGen Help file for more info. 116 | 117 | ( 118 | { 119 | var freq, trigger, env; 120 | freq = 800; 121 | trigger = Impulse.kr(1/2); // trigger a note every 2 seconds 122 | env = EnvGen.kr(Env.perc(0.001, 1), gate: trigger); 123 | SinOsc.ar(freq, mul: env); 124 | }.play; 125 | ) 126 | 127 | 128 | // Similar, now using an envelope to control frequency as well. 129 | ( 130 | { 131 | var trigger, freqEnv, ampEnv; 132 | trigger = Impulse.kr(1/2); // trigger a note every 2 seconds 133 | freqEnv = EnvGen.kr(Env.linen(0.1, 0.5, 0.4), gate: trigger).range(440, 880); 134 | ampEnv = EnvGen.kr(Env.perc(0.001, 1), gate: trigger); 135 | SinOsc.ar(freq: freqEnv, mul: ampEnv); 136 | }.play; 137 | ) 138 | 139 | 140 | -------------------------------------------------------------------------------- /GUI_demo.scd: -------------------------------------------------------------------------------- 1 | 2 | // Summary of Nick Collins' tutorial 4.2 - Graphical User Interfaces 3 | 4 | // ******** 5 | // WINDOW 6 | // ******** 7 | 8 | // How to make a window 9 | // The Rect(angle) takes the initial screen position and the window size 10 | // as screenx, screeny, windowwidth, windowheight, where screeny is 0 at the bottom 11 | 12 | ( 13 | var w; 14 | w = Window("My Window", Rect(100, 500, 200, 200)); 15 | // How the read the line above: "at screen co-ordinates (100, 500), create a 200x200 window" 16 | w.front; // this line is needed to make the window actually appear 17 | ) 18 | 19 | // A big window 20 | ( 21 | var w; 22 | w = Window("BIG Window", Rect(left: 100, top: 500, width: 600, height: 500)); 23 | w.front; 24 | ) 25 | 26 | // ******* 27 | // SLIDER 28 | // ******* 29 | 30 | ( 31 | var w, slid; 32 | 33 | w = Window("My Window", Rect(100, 500, 200, 100)); 34 | slid = Slider(w, Rect(10, 10, 180, 40)); // a basic slider object of size 180 by 40 appears 10 pixels in from the left, and 10 pixels down from the top 35 | 36 | slid.action_({slid.value.postln;}); // this is the callback: the function is called whenever you move the slider. 37 | // action_ means to set up the slider object to use the function passed in as its argument. 38 | 39 | w.front; 40 | ) 41 | 42 | // Note how the default slider range is from 0.0 to 1.0 43 | // In order to rescale slider range, remember linlin, linexp: 44 | 45 | 1.0.rand.linlin(0.0, 1.0, 14.7, 68.12) 46 | 1.0.rand.linexp(0.0, 1.0, 14.7, 68.12) 47 | 48 | 49 | // ************ 50 | // ControlSpec 51 | // ************ 52 | 53 | // Rather than doing these remappings yourself, an alternative is to take advantage of a ControlSpec, a helpful class which can be used to turn input into any desired range through various available precanned mappings 54 | 55 | ( 56 | var w, slid, cs; 57 | 58 | w = Window("My Window", Rect(100, 500, 200, 100)); 59 | slid = Slider(w, Rect(10, 10, 180, 40)); 60 | 61 | cs = ControlSpec( 62 | minval: 20, 63 | maxval: 20000, 64 | warp: \exponential, 65 | step: 10, 66 | default: 1000); 67 | 68 | slid.action_({cs.map(slid.value).postln;}); // map to the desired range 69 | 70 | w.front; 71 | ) 72 | 73 | // *************************************** 74 | // Demo of using 2D-Slider for synthesis 75 | // *************************************** 76 | 77 | ( 78 | SynthDef(\filterme, {arg freq = 500, rq = 0.01; 79 | Out.ar(0, Pan2.ar( 80 | BPF.ar(Impulse.ar(LFNoise0.kr(15, 500, 1000), 0.1, WhiteNoise.ar(2)), freq, rq)))}).add; 81 | ) 82 | 83 | ( 84 | var w, slid2d, syn, csfreq, csrq; 85 | 86 | w = Window("My Window", Rect(100, 300, 200, 200)); 87 | slid2d = Slider2D(w, Rect(5, 5, 190, 190)); 88 | 89 | syn = Synth(\filterme); // create synth 90 | 91 | csfreq = ControlSpec( 92 | minval: 500, 93 | maxval: 10000, 94 | warp: \exponential, 95 | step: 1); 96 | 97 | csrq = ControlSpec( 98 | minval: 0.01, 99 | maxval: 0.10, 100 | warp: \exponential, 101 | step: 0.01); 102 | 103 | slid2d.action_({ 104 | syn.set(\freq, csfreq.map(slid2d.y), \rq, csrq.map(slid2d.x)); 105 | [csfreq.map(slid2d.y), csrq.map(slid2d.x)].postln; 106 | }); 107 | 108 | w.front; 109 | 110 | w.onClose = {syn.free}; // stops running synth when the window close button is pressed 111 | ) 112 | 113 | // ********************* 114 | // KNOBs and FlowLayout 115 | // ********************* 116 | 117 | ( 118 | w = Window("decoration", Rect(200, 200, 400, 500)); 119 | // set up decorator. FlowLayout needs to know the size of the parent window, the outer borders (10 pixels in on horizontal and vertical here) and the standard gap to space GUI views (20 in x, 20 in y) 120 | w.view.decorator = FlowLayout(w.view.bounds, 10@10, 20@20); 121 | 122 | // now, when GUI views are added to the main view, they are automatically arranged, and you only need to say how big each view is 123 | k = Array.fill(10, {Knob(w.view,100@100).background_(Color.rand)}); 124 | 125 | w.front; // make GUI appear 126 | ) 127 | 128 | // they were stored in an array, held in global variable k, so we can access them all easily via one variable 129 | k[0].background_(Color.rand) 130 | 131 | 132 | ///////////////// 133 | // Fun with GUI: 134 | 135 | ( 136 | w = Window(); 137 | w.front; 138 | { 139 | inf.do({ 140 | {Slider(w, Rect( 141 | left: rrand(10, 290), 142 | top: rrand(10, 250), 143 | width: rrand(50, 150), 144 | height: rrand(50, 200)))}.defer; 145 | rrand(0.1, 0.3).wait; 146 | {w.view.children[0].remove}.defer; 147 | rrand(0.3, 0.5).wait; 148 | }); 149 | }.fork; 150 | ) 151 | -------------------------------------------------------------------------------- /Enclosures.scd: -------------------------------------------------------------------------------- 1 | // ============ 2 | // ENCLOSURES 3 | // ============ 4 | // This tutorial is adapted from the SuperCollider Book -- chapter 1 by David Cottle. 5 | 6 | /* 7 | 8 | There are four types of enclosures (parentheses), [brackets], {braces}, and "quotation marks". 9 | 10 | It is important to keep these pairs matched or “balanced” as you write code: everything that you open needs to be closed at some point later in the code. Nesting can make balancing difficult and confusing. (...) The SuperCollider language editor has some tools to help you keep track. For example, the SuperCollider IDE automatically indicates matching parentheses when you close a pair (they show up in red); and you can double click a parenthesis to select everything within. If you click on a parenthesis that lacks an opening/closing match, you will see a dark red selection telling you your are missing something. Balancing is a quick way to select large sections of code for evaluation, deletion, or copy/paste operations. 11 | 12 | */ 13 | 14 | 15 | 16 | 17 | 18 | //////////////////// 19 | // Quotation marks 20 | //////////////////// 21 | 22 | /* Quotation marks are used to enclose a string of characters (including spaces) as a single unit. These are aptly called strings. Single quotes create symbols, which are similar to strings. You can also make a symbol by preceding some text with a backslash. Thus 'aSymbol' and \aSymbol are equivalent. Symbols are often used as labels for parameters and are sometimes, but not always, interchangeable with strings. See the Symbol and String help files for more information. */ 23 | 24 | "Here's a nice string"; 25 | 26 | 'aSymbol'; 27 | 28 | 29 | 30 | 31 | 32 | /////////////// 33 | // Parentheses 34 | /////////////// 35 | 36 | // Parentheses can be used to enclose argument lists, as in this example: 37 | 38 | rrand(0, 10); 39 | 40 | // They can also force precedence, which is the order in which things get done. 41 | // For example, compare 42 | 43 | 5 + 10 * 4; 44 | 5 + (10 * 4); 45 | 46 | // Parentheses are also used to cread code blocks 47 | // (multiple lines of code to be evaluated together) 48 | ( 49 | "first line".postln; 50 | (9 + 9).postln; 51 | "last line".postln; 52 | ) 53 | 54 | 55 | 56 | 57 | ///////////// 58 | // Brackets 59 | ///////////// 60 | 61 | // Brackets define a collection of items. 62 | 63 | [1, 2, 3, 4, "hello"]; 64 | 65 | /* An Array (one type of collection) can contain numbers but also text, functions, or entire patches. You can even mix data types within an array. Arrays can receive messages such as reverse, scramble, mirror, rotate , midicps, choose, and permute, to name a few. You can also perform mathematical operations on arrays. */ 66 | 67 | [1, 2, 3, 4, "hello"].scramble; 68 | 69 | [1, 2, 3, 4, "hello"].mirror; 70 | 71 | [1, 2, 3, 4] + 10; 72 | 73 | // convert midi to frequency in Hz 74 | [60, 62, 64, 65, 67, 69, 71].midicps.round(0.1); 75 | 76 | 77 | 78 | 79 | 80 | ///////////////// 81 | // Curly Braces 82 | ///////////////// 83 | 84 | /* Braces (or “curly braces”), the last enclosure, define functions. Functions perform specific tasks that are usually repeated, often millions of times and often with different results. Try these lines, one at a time. */ 85 | 86 | exprand(1, 1000.0); 87 | 88 | { exprand(1, 1000.0) }; 89 | 90 | /* The first line picks a random number, which is displayed in the post window. The second prints a very different result: a function. What does the function do? It picks a random number. How can that difference affect code? Consider the lines below. The first chooses a random number and duplicates it. The second executes the random-number-picking function 5 times and collects the results in an array. */ 91 | 92 | dup(rand(1000.0), 5); // picks a number, duplicates it 93 | 94 | dup({rand(1000.0)}, 5); // duplicates the function of picking a number 95 | 96 | // essentially, this (which has a similar result) 97 | [rand(1000.0), rand(1000.0), rand(1000.0), rand(1000.0), rand(1000.0)] 98 | 99 | 100 | { LFNoise0.ar }.play; // play a series of random numbers (careful: loud) 101 | { LFNoise0.ar(10000) }.plot; // plot those numbers 102 | 103 | { 100.rand }.dup(10) // pick 10 random numbers 104 | { 100.rand } ! 10 // same as above 105 | { 100.rand }.dup(10).postln.plot // pick 10 numbers, post, plot 106 | { 100.rand }.dup(100).sort.plot // pick 100 numbers, sort, plot 107 | 108 | 109 | 110 | 111 | 112 | /* In Summary: 113 | Receivers, messages, and argument lists: object.message(arglist) 114 | Collections: [list, of, items] 115 | Functions: { often multiple lines of code } 116 | Strings: "words inside quotes" 117 | Symbols: 'words inside single quotes' or preceded by a backslash (\) 118 | -------------------------------------------------------------------------------- /Basics_Tutorial.scd: -------------------------------------------------------------------------------- 1 | //////////////////////// 2 | // SUPERCOLLIDER BASICS 3 | //////////////////////// 4 | 5 | // HOW TO EVALUATE 6 | // START AND STOP SOUND 7 | // WRITING COMMENTS 8 | // MESSAGES AND ARGUMENTS 9 | // NESTING 10 | // RECEIVER NOTATION, FUNCTIONAL NOTATION 11 | // ENCLOSURES 12 | // VARIABLES 13 | 14 | // How to evaluate a line: 15 | // [ctrl+Enter] 16 | 17 | // Use a semicolon to end your lines. 18 | // Evaluate lines below and see the results. 19 | 20 | "Hello World".postln; 21 | 22 | // Get yourself some GUI: 23 | 24 | s.meter; // same as [ctrl+M] 25 | 26 | // Make some sound: 27 | 28 | {SinOsc.ar(440, 0, 0.1)}.play; // simple oscillator; how to stop it? 29 | 30 | // How to stop all sounds: 31 | // [ctrl+period] 32 | 33 | // WRITING COMMENTS 34 | // This is a comment. 35 | // Anything written after two slashes is a comment. 36 | // Write a lot of comments in your code. You will not regret. 37 | 38 | "This is not a comment".postln; // THIS is a comment! 39 | 40 | rrand(1, 10); // generate a random number between 1 and 10 41 | 42 | // The word "rrand" above is what we call a "method." 43 | // The numbers in parenthesis are the arguments. 44 | 45 | x = rrand(1, 10); // store into a variable 46 | x.postln; // there it is 47 | 48 | y = ["hello", 90, 44, 11, x, 945]; // this is an array (a collection of items) 49 | 50 | y.postln; 51 | 52 | y.at(1); // retrive an item by index (starting at 0) 53 | y[1]; // shortcut, same as line above 54 | 55 | 56 | // Help files! Use them. Also available online: http://doc.sccode.org/ 57 | // Also, select any valid word in SC code and type ctrl + D. The corresponding help file will appear. 58 | 59 | // Below, more "grammar" stuff to understand the language... read on! 60 | // NOTE: 61 | // If the section below seems to dry for you at this point, try 62 | // the more friendly tutorial "Patterns_Intro" and come back here later on. 63 | 64 | ////////////////////////// 65 | // MESSAGES AND ARGUMENTS 66 | ////////////////////////// 67 | 68 | // Message: a lowercase word followed by a pair of parentheses containing a list of arguments. 69 | // Arguments: lists of items separated by commas, inside parentheses, next to a message. 70 | 71 | rand(100); // evaluate this several times, and watch the post window 72 | 73 | rand(100.0); // if you use a float, a float is returned 74 | 75 | exprand(1.0, 100.0); // a message with 2 arguments 76 | 77 | // More examples of messages with arguments: 78 | 79 | dup("fun", 20); // Rebecca Black 80 | round([3.365, 5.923, 1009.19]); 81 | round([3.365, 5.923, 1009.199], 0.1); 82 | sort([56, 12, 62, 71, 1, 53, 4]); 83 | rrand(440, 880); // different from rand; this allows you to specify a range 84 | 85 | /////////// 86 | // NESTING 87 | /////////// 88 | 89 | round(dup({exprand(1,10)}, 25), 0.1); 90 | 91 | // To "read" nested code, start from innermost nested message and work your way outwards: 92 | 93 | exprand(1,10); 94 | dup({exprand(1,10)}, 25); 95 | round(dup({exprand(1,10)}, 25)); 96 | 97 | // Notice the difference between these two nested statements, by the way: 98 | 99 | dup( exprand(1,10), 25); 100 | dup( {exprand(1,10)}, 25); // more on this later 101 | 102 | 103 | ////////////////////////////////////////// 104 | // RECEIVER NOTATION, FUNCTIONAL NOTATION 105 | ////////////////////////////////////////// 106 | 107 | // Functional notation: 108 | 109 | rand(100) 110 | 111 | // Object.receiver notation: 112 | 113 | 100.rand 114 | 115 | // The two statements above mean the same thing. You can use either way of writing. 116 | // Another example, now with more than one argument: 117 | 118 | dup("hello", 20); // functional notation 119 | "hello".dup(20); // same thing in receiver notation 120 | 121 | // The first argument of a message can be placed in front of the message, separated by a dot. 122 | 123 | // EXERCISE: How would you rewrite these statements? 124 | 125 | {SinOsc.ar(440, 0, 0.2)}.play; // rewrite this using functional notation only 126 | 127 | dup(round(rand(1000.0), 0.1), 5); // rewrite this using receiver notation only 128 | 129 | // It's a matter of style and convention. Sometimes one method can be clearer than the other. 130 | // "Things that begin with uppercase letters (like SinOsc) are nearly always written as receiver.messages" [p. 11] 131 | 132 | ////////////// 133 | // ENCLOSURES 134 | ////////////// 135 | 136 | // Four types: (parentheses), [brackets], {braces}, and "quotation marks" 137 | 138 | // (PARENTHESES) 139 | // Parentheses are used to enclose argument lists, as seen before. 140 | // They can also be used to force precedence: 141 | 5 + 10 * 4 // SuperCollider goes left to right, regardless of operators. 142 | 5 + (10 * 4) // Want the multiplication first? You have to be explicit. 143 | 144 | // Messages normally take precedence: 145 | 5 + 10.squared // 10 is squared first, then result is added to 5 146 | (5 + 10).squared // 5 and 10 are added first, then the result is squared 147 | 148 | // [BRACKETS] 149 | // Brackets define a collection of items. One common type of collection is called an Array. 150 | [10, 11, 12, 13, 14, 15, 16, 17, "wow"] 151 | [10, 11, 12, 13, 14, 15, 16, 17, "wow"].reverse 152 | [10, 11, 12, 13, 14, 15, 16, 17, "wow"].choose 153 | [10, 11, 12, 13, 14, 15, 16, 17, "wow"].mirror 154 | [10, 11, 12, 13, 14, 15, 16, 17, "wow"].size 155 | [10, 11, 12, 13, 14, 15, 16, 17, "wow"].at(0) 156 | [10, 11, 12, 13, 14, 15, 16, 17, "wow"] ++ 999 157 | [60, 72, 70, 65].midicps.round(0.1) 158 | ["cat", "mouse", "dog"].choose.dup(10) 159 | 160 | (1..10) // ...dirty trick to quickly generate an array. 161 | (1, 3 .. 20) // Yes, maybe confusing in the beginning. Better face it early on though! :-) 162 | 163 | // For a handy summary of common Array operations, see 164 | "https://ccrma.stanford.edu/wiki/SuperCollider_Quick_Reference#Arrays".openDocument; 165 | 166 | // {BRACES} 167 | // Curly braces define functions. Functions perform specific tasks that are usually repeated several times, normally with different results. 168 | 169 | rand(1000.0) // This just picks a random number and returns it 170 | {rand(1000.0)} // This creates and a function and returns the function 171 | 172 | // This line says: "pick a random number, duplicate it 5 times" 173 | dup(rand(1000.0), 5) 174 | 175 | // Whereas this line says: "pick this function, duplicate it 5 times" 176 | dup({rand(1000.0)}, 5) 177 | 178 | // The line above has a similar result as this: 179 | [rand(1000.0), rand(1000.0), rand(1000.0), rand(1000.0), rand(1000.0)] 180 | 181 | {100.rand}.dup(10); // Here's a function receiving the message dup 182 | {100.rand}.dup(10).plot; // Here's a function receiving the message dup; the result of it receives the message plot 183 | {100.rand}.dup(50).sort.plot; 184 | {rrand(1, 10)}.dup(12).sort.reverse; 185 | {SinOsc.ar(440, 0, 0.2)}.plot; // Here's a function receiving the message plot 186 | {SinOsc.ar(440, 0, 0.2)}.play; // Here's a function receiving the message play 187 | {SinOsc.ar(440, 0, 0.2)}.scope; // Here's a function receiving the message scope 188 | 189 | // "QUOTATION MARKS" 190 | 191 | "Hello Again, World".postln; 192 | 193 | "Quotation marks are used to enclose a string of characters (including spaces) as a single unit".postln 194 | 195 | {"All work and no play makes Jack a dull boy".postln}.dup(1000); "".postln; 196 | 197 | // These are called "strings." If you use single quotes, you will create a symbol. 198 | 199 | 'symbol' 200 | 201 | \symbol // same as above 202 | 203 | [1, 2, 3, \symbol, 4, "a string", 5]; 204 | 205 | // Symbols may be interchangeable with strings (though not always). 206 | 207 | "This is a great string".size 208 | "This is a great string".at(5) 209 | 'This is a great symbol'.size 210 | 211 | ///////////// 212 | // VARIABLES 213 | ///////////// 214 | 215 | // Default set of SC variables: lowercase letters a through z. 216 | 217 | ( 218 | a = 440; 219 | b = 3; 220 | c = "math operations"; 221 | [c, a, b, a*b, a+b, a.pow(b), a.mod(b)]; 222 | ) 223 | 224 | // Once you assign a default variable, it remains valid until you close SC or re-assign it. 225 | // Evaluate the lines below in order: 226 | 227 | a; // evaluate this; it is still 440 228 | a = "not anymore"; 229 | a; // evaluate this; 440 is gone, string "not anymore" replaced it 230 | 231 | // Compare these two assignments (variables e and f): 232 | e = {100.rand}.dup(10).plot; 233 | e; // What is stored here? 234 | 235 | f = {{100.rand}.dup(10).plot}; 236 | f; // What is stored here? 237 | 238 | e.value; // To whom is the message "value" being sent to? What is being returned? 239 | f.value; // To whom is the message "value" being sent to? What is being returned? 240 | 241 | // You can define variables in other ways as well. 242 | 243 | // You can use the key word "var" to define a variable with limited scope: 244 | ( 245 | var ccrma = [660, "Lomita", "Drive", "The Knoll"]; 246 | ccrma.postln; 247 | ccrma = ccrma.reverse; 248 | ccrma.postln; 249 | ) 250 | 251 | // You can also declare variables of less limited scope using a tilde: 252 | 253 | ~knoll = [660, 1910, 1946, 1986, 1975, 1989, 2005, 2012, "you won't get rid of me so easily"]; 254 | ~knoll.reverse; 255 | ~knoll; 256 | 257 | /* 258 | This will remain valid until you restart SuperCollider. They are called "environment variables." 259 | From the SC Book: "(...) similar to the a to z ones, but user defined. They will work anywhere in your patch, in other patches, even in other windows. By contrast, the [local] variables [as ccrma above] will work only inside the function where they are declared." (p. 25) 260 | */ 261 | 262 | -------------------------------------------------------------------------------- /SynthDefs_2013.scd: -------------------------------------------------------------------------------- 1 | SynthDef("PMCrotale", { 2 | arg freq = 261, tone = 3, art = 1, amp = 0.8, pan = 0; 3 | var env, out, mod; 4 | 5 | env = Env.perc(0, art); 6 | mod = 5 + (1/IRand(2, 6)); 7 | 8 | out = PMOsc.ar(freq, mod*freq, 9 | pmindex: EnvGen.kr(env, timeScale: art, levelScale: tone), 10 | mul: EnvGen.kr(env, timeScale: art, levelScale: 0.3)); 11 | 12 | out = Pan2.ar(out, pan); 13 | 14 | out = out * EnvGen.kr(env, timeScale: 1.3*art, 15 | levelScale: Rand(0.1, 0.5), doneAction:2); 16 | Out.ar(0, out*amp); //Out.ar(bus, out); 17 | 18 | }).add; 19 | 20 | 21 | SynthDef("marimba", {arg freq= 220, amp = 0.4; 22 | var snd, env; 23 | env = EnvGen.kr(Env.linen(0.015, 1, 0.5), levelScale: amp, doneAction: 2); 24 | snd = BPF.ar( 25 | in: Saw.ar(0), 26 | freq: freq, 27 | rq: 0.02); 28 | snd = BLowShelf.ar(snd, 220, 0.81, 6); 29 | Out.ar(0, Splay.ar(snd*env*0.3)); 30 | }).add; 31 | 32 | SynthDef("blips", { 33 | arg freq = 25, numharm = 10, rel = 1, amp = 0.1; 34 | var snd, env; 35 | env = EnvGen.ar(Env.perc(releaseTime: rel), doneAction: 2); 36 | snd = Blip.ar([freq, freq*1.01], numharm, 0.3); 37 | snd = snd*env*amp; 38 | Out.ar(0, snd); 39 | }).add; 40 | 41 | SynthDef("noisy", { 42 | arg freq = 110, amp = 0.2; 43 | var snd, env; 44 | env = EnvGen.ar(Env.perc(0.02, 0.1), doneAction: 2); 45 | snd = Mix(LFPulse.ar( 46 | freq: freq*[1, 5/2], 47 | iphase: 0.0, 48 | width: 0.5, 49 | mul: amp)); 50 | snd = snd * env * amp; 51 | Out.ar(0, Pan2.ar(snd, 0.0)); 52 | }).add; 53 | 54 | SynthDef("hihat", {arg amp = 0.5, att = 0.01, rel = 0.2, ffreq = 6000; 55 | var snd = WhiteNoise.ar(amp); 56 | var env = EnvGen.kr(Env.perc(att, rel), doneAction: 2); 57 | snd = HPF.ar(snd * env, ffreq); 58 | Out.ar(0, snd!2); 59 | }).add; 60 | 61 | SynthDef("snare", {arg amp = 0.1, sinfreq = 180, att = 0.01, rel = 0.2, ffreq = 2000; 62 | var snd1 = WhiteNoise.ar(amp); 63 | var snd2 = SinOsc.ar(sinfreq,0,amp); 64 | var env = EnvGen.kr(Env.perc(att, rel), doneAction: 2); 65 | var sum = HPF.ar(snd1 * env, ffreq)+(snd2*env); 66 | Out.ar(0, sum!2); 67 | }).add; 68 | 69 | SynthDef("kick", {arg amp = 0.3, sinfreq = 60, glissf = 0.9, att = 0.01, rel = 0.45; 70 | var gliss = XLine.kr(sinfreq, sinfreq*glissf, rel); 71 | var snd = SinOsc.ar(gliss); 72 | var env = EnvGen.kr(Env.perc(att, rel), doneAction: 2); 73 | snd = snd * env * amp; 74 | Out.ar(0, snd!2); 75 | }).add; 76 | 77 | SynthDef("kick3", {arg punch = 1, amp = 1; 78 | var freq = EnvGen.kr(Env([400, 66], [0.08], -3)), 79 | sig = Normalizer.ar(SinOsc.ar(freq, 0.5pi, punch).distort, 1) * amp 80 | * EnvGen.kr(Env([0, 1, 0.8, 0], [0.01, 0.1, 0.2]), doneAction: 2); 81 | Out.ar(0, sig ! 2); 82 | }).add; 83 | 84 | SynthDef("kick808", {arg freq1 = 240, freq2 = 60, amp = 1, ringTime = 10, rel = 1, dist = 0.5, pan = 0; 85 | var snd, env; 86 | snd = Ringz.ar( 87 | in: Impulse.ar(0), // single impulse 88 | freq: XLine.ar(freq1, freq2, 0.1), 89 | decaytime: ringTime); 90 | env = EnvGen.ar(Env.perc(0.001, rel), doneAction: 2); 91 | snd = (1.0 - dist) * snd + (dist * (snd.distort)); 92 | snd = snd * env * amp; 93 | Out.ar(0, Pan2.ar(snd, pan)); 94 | }).add; 95 | 96 | SynthDef("ringkick", {arg freq = 40, decay = 0.25, amp = 1; 97 | var snd; 98 | snd = Ringz.ar( 99 | in: LPF.ar( 100 | in: Impulse.ar(0), 101 | freq: 1000), 102 | freq: freq, 103 | decaytime: decay, 104 | mul: 7 * amp).tanh.sin*2; 105 | Out.ar(0, snd!2); 106 | }).add; 107 | 108 | SynthDef("harpsi", { |outbus = 0, freq = 440, amp = 0.1, gate = 1| 109 | var out; 110 | out = EnvGen.ar(Env.adsr, gate, doneAction: 2) * amp * 111 | Pulse.ar(freq, 0.25, 0.75); 112 | Out.ar(outbus, out ! 2); 113 | }).add; 114 | 115 | SynthDef("beating", {arg freq = 440, amp = 0.1, art = 1; 116 | var env, snd1, snd2; 117 | env = EnvGen.ar(Env.perc(0.01, art), doneAction: 2); 118 | snd1 = SinOsc.ar(freq); 119 | snd2 = SinOsc.ar(Line.kr(freq+15, freq, art)); 120 | Out.ar(0, Pan2.ar(Mix([snd1, snd2]), 0, amp*env)) 121 | }).add; 122 | 123 | SynthDef("bass", { |freq = 440, gate = 1, amp = 0.5, slideTime = 0.17, ffreq = 1100, width = 0.15, detune = 1.005, preamp = 4| 124 | var sig, env; 125 | env = Env.adsr(0.01, 0.3, 0.4, 0.1); 126 | freq = Lag.kr(freq, slideTime); 127 | sig = Mix(VarSaw.ar([freq, freq * detune], 0, width, preamp)).distort; 128 | sig = sig * amp * EnvGen.kr(env, gate, doneAction: 2); 129 | sig = LPF.ar(sig, ffreq); 130 | Out.ar(0, sig ! 2) 131 | }).add; 132 | 133 | SynthDef("kik", { |basefreq = 50, ratio = 7, sweeptime = 0.05, preamp = 1, amp = 1, decay1 = 0.3, decay1L = 0.8, decay2 = 0.15, out| 134 | var fcurve = EnvGen.kr(Env([basefreq * ratio, basefreq], [sweeptime], \exp)), 135 | env = EnvGen.kr(Env([1, decay1L, 0], [decay1, decay2], -4), doneAction: 2), 136 | sig = SinOsc.ar(fcurve, 0.5pi, preamp).distort * env * amp; 137 | Out.ar(out, sig ! 2) 138 | }).add; 139 | 140 | SynthDef("kraftySnr", { |amp = 1, freq = 2000, rq = 3, decay = 0.3, pan, out| 141 | var sig = PinkNoise.ar(amp), 142 | env = EnvGen.kr(Env.perc(0.01, decay), doneAction: 2); 143 | sig = BPF.ar(sig, freq, rq, env); 144 | Out.ar(out, Pan2.ar(sig, pan)) 145 | }).add; 146 | 147 | SynthDef("sillyVoice", { arg 148 | freq = 220, 149 | amp = 0.5, 150 | vibratoSpeed = 6, 151 | vibratoDepth = 4, 152 | vowel = 0, 153 | att = 0.01, 154 | rel = 0.1, 155 | lag = 1, 156 | gate = 1; 157 | 158 | var in, vibrato, env, va, ve, vi, vo, vu, snd; 159 | 160 | vibrato = SinOsc.kr(vibratoSpeed, mul: vibratoDepth); 161 | in = Saw.ar(Lag.kr(freq, lag) + vibrato); 162 | env = EnvGen.kr(Env.asr(att, 1, rel), gate, doneAction: 2); 163 | 164 | va = BBandPass.ar( 165 | in: in, 166 | freq: [ 600, 1040, 2250, 2450, 2750 ], 167 | bw: [ 0.1, 0.067307692307692, 0.048888888888889, 0.048979591836735, 0.047272727272727 ], 168 | mul: [ 1, 0.44668359215096, 0.35481338923358, 0.35481338923358, 0.1 ]); 169 | 170 | ve = BBandPass.ar( 171 | in: in, 172 | freq: [ 400, 1620, 2400, 2800, 3100 ] , 173 | bw: [ 0.1, 0.049382716049383, 0.041666666666667, 0.042857142857143, 0.038709677419355 ], 174 | mul: [ 1, 0.25118864315096, 0.35481338923358, 0.25118864315096, 0.12589254117942 ]); 175 | 176 | vi = BBandPass.ar( 177 | in: in, 178 | freq: [ 250, 1750, 2600, 3050, 3340 ] , 179 | bw: [ 0.24, 0.051428571428571, 0.038461538461538, 0.039344262295082, 0.035928143712575 ], 180 | mul: [ 1, 0.031622776601684, 0.15848931924611, 0.079432823472428, 0.03981071705535 ] ); 181 | 182 | vo = BBandPass.ar( 183 | in: in, 184 | freq:[ 400, 750, 2400, 2600, 2900 ] , 185 | bw: [ 0.1, 0.10666666666667, 0.041666666666667, 0.046153846153846, 0.041379310344828 ], 186 | mul: [ 1, 0.28183829312645, 0.089125093813375, 0.1, 0.01 ]); 187 | 188 | vu = BBandPass.ar( 189 | in: in, 190 | freq: [ 350, 600, 2400, 2675, 2950 ], 191 | bw: [ 0.11428571428571, 0.13333333333333, 0.041666666666667, 0.044859813084112, 0.040677966101695 ], 192 | mul: [ 1, 0.1, 0.025118864315096, 0.03981071705535, 0.015848931924611 ]); 193 | 194 | snd = SelectX.ar(Lag.kr(vowel, lag), [va, ve, vi, vo, vu]); 195 | snd = Mix.new(snd); 196 | Out.ar(0, snd!2 * env * amp); 197 | }).add; 198 | 199 | SynthDef("plucking", {arg amp = 0.1, freq = 440, decay = 5, coef = 0.1; 200 | var env, snd; 201 | env = EnvGen.kr(Env.linen(0, decay, 0), doneAction: 2); 202 | snd = Pluck.ar( 203 | in: WhiteNoise.ar(amp), 204 | trig: Impulse.kr(0), 205 | 206 | maxdelaytime: 0.1, 207 | delaytime: freq.reciprocal, 208 | decaytime: decay, 209 | coef: coef); 210 | Out.ar(0, [snd, snd]); 211 | }).add; 212 | 213 | SynthDef("defaultB", { arg out=0, freq=440, amp=0.1, pan=0, gate=1; 214 | var z; 215 | z = LPF.ar( 216 | Mix.new(VarSaw.ar(freq + [0, Rand(-0.4,0.0), Rand(0.0,0.4)], 0, 0.3)), 217 | XLine.kr(Rand(4000,5000), Rand(2500,3200), 1) 218 | ) * Linen.kr(gate, 0.01, 0.7, 0.3, 2); 219 | OffsetOut.ar(out, Pan2.ar(z, pan, amp)); 220 | }, [\ir]).add; 221 | 222 | SynthDef("trig_demo", { |freq = 440, gate = 1, t_trig = 1| 223 | var env, sig; 224 | env = Decay2.kr(t_trig, 0.01, 0.1); 225 | sig = SinOsc.ar(freq, 0, env); 226 | sig = sig * Linen.kr(gate, 0.01, 0.1, 0.1, doneAction: 2); 227 | Out.ar(0, sig ! 2) 228 | }).add; 229 | 230 | SynthDef("waveguideFlute", { arg scl = 0.2, pch = 72, ipress = 0.9, ibreath = 0.09, ifeedbk1 = 0.4, 231 | ifeedbk2 = 0.4, dur = 1, gate = 1, amp = 2; 232 | 233 | var kenv1, kenv2, kenvibr, kvibr, sr, cr, block; 234 | var poly, signalOut, ifqc; 235 | var aflow1, asum1, asum2, afqc, atemp1, ax, apoly, asum3, avalue, atemp2, aflute1; 236 | var fdbckArray; 237 | 238 | sr = SampleRate.ir; 239 | cr = ControlRate.ir; 240 | block = cr.reciprocal; 241 | 242 | ifqc = pch.midicps; 243 | 244 | // noise envelope 245 | kenv1 = EnvGen.kr(Env.new( 246 | [ 0.0, 1.1 * ipress, ipress, ipress, 0.0 ], [ 0.06, 0.2, dur - 0.46, 0.2 ], 'linear' ) 247 | ); 248 | // overall envelope 249 | kenv2 = EnvGen.kr(Env.new( 250 | [ 0.0, amp, amp, 0.0 ], [ 0.1, dur - 0.02, 0.1 ], 'linear' ), doneAction: 2 251 | ); 252 | // vibrato envelope 253 | kenvibr = EnvGen.kr(Env.new( [ 0.0, 0.0, 1, 1, 0.0 ], [ 0.5, 0.5, dur - 1.5, 0.5 ], 'linear') ); 254 | 255 | // create air flow and vibrato 256 | aflow1 = LFClipNoise.ar( sr, kenv1 ); 257 | kvibr = SinOsc.ar( 5, 0, 0.1 * kenvibr ); 258 | 259 | asum1 = ( ibreath * aflow1 ) + kenv1 + kvibr; 260 | afqc = ifqc.reciprocal - ( asum1/20000 ) - ( 9/sr ) + ( ifqc/12000000 ) - block; 261 | 262 | fdbckArray = LocalIn.ar( 1 ); 263 | 264 | aflute1 = fdbckArray; 265 | asum2 = asum1 + ( aflute1 * ifeedbk1 ); 266 | 267 | //ax = DelayL.ar( asum2, ifqc.reciprocal * 0.5, afqc * 0.5 ); 268 | ax = DelayC.ar( asum2, ifqc.reciprocal - block * 0.5, afqc * 0.5 - ( asum1/ifqc/cr ) + 0.001 ); 269 | 270 | apoly = ax - ( ax.cubed ); 271 | asum3 = apoly + ( aflute1 * ifeedbk2 ); 272 | avalue = LPF.ar( asum3, 2000 ); 273 | 274 | aflute1 = DelayC.ar( avalue, ifqc.reciprocal - block, afqc ); 275 | 276 | fdbckArray = [ aflute1 ]; 277 | 278 | LocalOut.ar( fdbckArray ); 279 | 280 | signalOut = avalue; 281 | 282 | OffsetOut.ar( 0, [ signalOut * kenv2, signalOut * kenv2 ] ); 283 | 284 | }).add; 285 | 286 | 287 | "SynthDefs_2013.scd loaded".postln; 288 | "".postln; 289 | 290 | 291 | /* 292 | x = Synth.new("hihat"); 293 | x = Synth.new("snare"); 294 | x = Synth.new("kick"); 295 | x = Synth.new("kick2"); 296 | */ -------------------------------------------------------------------------------- /12_Things_You_Probably_Want_To_Know.scd: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////// 2 | // 12 Tips for SuperCollider Beginners 3 | //////////////////////////////////////////////////////////////// 4 | 5 | 6 | // ***************************************** 7 | // 1. MULTICHANNEL EXPANSION 8 | // ***************************************** 9 | 10 | // Open the Server meter to see what's going on in these examples. 11 | 12 | s.meter; // same as Ctrl + M shortcut 13 | 14 | // This sends sound to the left channel (which is bus 0 in SC): 15 | {SinOsc.ar(440, 0, 0.1)}.play; 16 | 17 | // This sends sound to the left and right channels (bus 0 and 1): 18 | {SinOsc.ar([440, 550], 0, 0.1)}.play; 19 | 20 | // This sends sound to outputs 0, 1, and 2: 21 | {SinOsc.ar([440, 550, 660], 0, 0.1)}.play; 22 | 23 | // This sends sound to outputs 0, 1, 2, 3, 4, 5: 24 | {SinOsc.ar([440, 550, 660, 100, 412, 1067], 0, 0.1)}.play; 25 | 26 | /* 27 | In the example above, you probably only hear the first two channels, unless you have a multichannel soundcard and more than 2 loudspeakers in your studio. 28 | 29 | In a nutshell: everytime you provide an Array of arguments to a UGen, it will 30 | "multichannel expand" to the number of elements in the array. 31 | 32 | From the manual: 33 | "When an Array is given as an input to a unit generator it causes an array of multiple copies of that unit generator to be made, each with a different value from the input array. This is called multichannel expansion." 34 | */ 35 | 36 | // Read more about it here: 37 | "Multichannel Expansion".help 38 | 39 | // Or read online: http://doc.sccode.org/Guides/Multichannel-Expansion.html 40 | 41 | 42 | 43 | // *********************************** 44 | // 2. THAT FUNNY EXCLAMATION MARK 45 | // *********************************** 46 | 47 | // What is this? 48 | 49 | 30!4 50 | 51 | // It simply creates an array containing 52 | // the same item a number of times. 53 | // Evaluate and see the results. 54 | // Essentially, this is the same as writing: 55 | 56 | Array.fill(4, 30); 57 | 58 | 59 | // ****************************************** 60 | // 3. TWO DOTS WITHIN PARENTHESES 61 | // ****************************************** 62 | 63 | // What is this? 64 | 65 | (10..15); 66 | 67 | // It's a shortcut to generate an array with an arithmetic series. 68 | // The above is a shortcut to: 69 | 70 | series(10, 11, 15); 71 | 72 | // For a step different than 1, you can do this: 73 | 74 | (10, 13 .. 20); // step of 3 75 | 76 | // Which is a shortcut for this: 77 | 78 | series(10, 13, 20); 79 | 80 | // Another way to explicitly create an Array and fill it with an arithmetic series is this: 81 | 82 | Array.series(size: 6, start: 10, step: 3); 83 | 84 | /* 85 | 86 | Note that the Array.series method implies a different way of thinking. 87 | 88 | The (10..15) allows you to think this way: "just start from 10 then go all the way up to (and including) 15". You don't necessarily think about how many items the array will end up having. 89 | 90 | The Array.series allows you to think this way: "just give me an array with 6 items total, starting from 10". You don't necessarily think about who is going to be the last number in the series. 91 | 92 | Also note that the shortcut uses parentheses, not square brackets. The resulting array, of course, will be within square brackets. 93 | 94 | */ 95 | 96 | // In summary: all the lines below give the same result 97 | 98 | (100, 97 .. 81); 99 | series(100, 97, 81); 100 | Array.series(7, 100, -3); 101 | 102 | // If you need geometric series, use this: 103 | Array.geom(size: 10, start: 1, grow: 2); 104 | 105 | 106 | 107 | 108 | // *********************************** 109 | // 4. VOLUME CONTROL GUI 110 | // *********************************** 111 | 112 | s.volume.gui; // why not, right? 113 | 114 | 115 | 116 | 117 | // ****************** 118 | // 5. QUICK RECORD 119 | // ****************** 120 | 121 | // Start recording: 122 | s.record; 123 | 124 | // Stop recording: 125 | s.stopRecording; 126 | 127 | // GUI with record button, volume control, mute button: 128 | s.makeWindow 129 | 130 | // The Post Window will show the path of the folder 131 | // where the file will be saved. For more info, 132 | 133 | Server.help; // scroll down to "Recording Support" 134 | 135 | // Same doc file online: 136 | // http://doc.sccode.org/Classes/Server.html 137 | 138 | 139 | 140 | 141 | // ************************************************** 142 | // 6. BEING VERBOSE CAN BE HELPFUL IN THE BEGINNING 143 | // ************************************************** 144 | 145 | // This: 146 | {SinOsc.ar(440, 0, 0.1)}.play; 147 | // ...can also be written like this: 148 | {SinOsc.ar(freq: 440, phase: 0, mul: 0.1)}.play; 149 | 150 | // This: 151 | Array.series(4, 70, 1.5); 152 | // ...can also be written like this: 153 | Array.series(size: 4, start: 70, step: 1.5); 154 | 155 | // This: 156 | {Decay2.ar(Impulse.ar(8, 0, XLine.kr(1, 0.01, 5, 1, 0, 2)), 0.01, 0.15, Mix.ar(Pulse.ar([90,92], 0.3)))}.play; 157 | // ...can also be written like this: 158 | ( 159 | {Decay2.ar( 160 | in: Impulse.ar( 161 | freq: 8, 162 | mul: XLine.kr(start: 1, end: 0.01, dur: 5, doneAction: 2)), 163 | attackTime: 0.01, 164 | decayTime: 0.15, 165 | mul: Mix.ar(Pulse.ar(freq: [90,92], width: 0.3))) 166 | }.play; 167 | ) 168 | 169 | // Note that when you do use the green argument names explicitly, you are allowed 170 | // to skip arguments that you do not want to use. In the first example, 171 | 172 | {SinOsc.ar(440, 0, 0.1)}.play; 173 | 174 | // the "zero" in the middle is for phase. You have to write it in in order to 175 | // set the third argument (mul), even though the default value for phase 176 | // would be zero anyway. On the other hand, you can skip it if you use argument names: 177 | 178 | {SinOsc.ar(freq: 440, mul: 0.1)}.play; 179 | 180 | // As you begin to learn SuperCollider, you may find it useful to explicitly use the argument 181 | // names of your methods. Not only your code will be more readable for yourself the next day or 182 | // the next month, but it will help you memorize what are the arguments. 183 | 184 | 185 | 186 | 187 | // ******************** 188 | // 7. SIMPLE SYNTHDEF 189 | // ******************** 190 | 191 | // Define it: 192 | ( 193 | SynthDef("testing", {arg freq = 440, amp = 0.1; 194 | var env, snd; 195 | env = EnvGen.kr(Env.perc(attackTime: 0.01, releaseTime: 1, level: amp), doneAction: 2); 196 | snd = SinOsc.ar(freq: freq, mul: env); 197 | Out.ar(0, snd); 198 | }).add; 199 | ) 200 | 201 | // Use it: 202 | Synth("testing"); 203 | 204 | Synth("testing", [\freq, 1200, \amp, 0.4]); 205 | 206 | 207 | 208 | 209 | // ****************** 210 | // 8. DONEACTION: 2 211 | // ****************** 212 | 213 | // What is this "doneAction: 2" that we see in many SynthDefs? 214 | // Take a look at the following example. 215 | 216 | // First open this server window: 217 | 218 | s.plotTree; // it's called the "node tree" 219 | 220 | // Now play a simple sine wave: 221 | 222 | x = {SinOsc.ar(440, 0, 0.1)}.play; 223 | 224 | // The rectangle that showed up in the node tree 225 | // represents the synth you are playing. It has 226 | // some generic name like "temp_1000" or similar. 227 | // Now stop the sound with this line: 228 | 229 | x.free; // you will see the rectangle disappear. 230 | 231 | // Now let's add a simple envelope to the sine wave: 232 | 233 | x = {SinOsc.ar(freq: 440, mul: Line.kr(start: 0.1, end: 0, dur: 4))}.play; 234 | 235 | // The sound fades out in 4 seconds, but notice 236 | // that the synth is STILL running -- the rectangle 237 | // did NOT disappear from the Node Tree window. 238 | // The synth is simply sending out a continuous signal 239 | // of zeros after Line.kr reached the end. 240 | // That node will only be freed only when you do it explicitly: 241 | 242 | x.free; 243 | 244 | // Now evaluate this same line repeated times in sequence: 245 | 246 | {SinOsc.ar(rrand(440, 1200), 0, Line.kr(0.1, 0, 3))}.play; 247 | 248 | // You will see the synths piling up in the Node Tree window. 249 | // Because we didn't assign each to a different variable, 250 | // the only way to free them now is to hit ctrl + period. 251 | 252 | // Finally: how to make a synth to FREE itself automatically? 253 | // A very common example is: "please free this synth as soon 254 | // as this envelope reaches the end". This is where doneAction: 2 255 | // comes in. Run the example below and watch the Node Tree window: 256 | 257 | {SinOsc.ar(440, 0, Line.kr(0.1, 0, 4, doneAction: 2))}.play; 258 | 259 | // Learn more about this here: 260 | "UGen done-actions".help 261 | 262 | 263 | 264 | // ********************************** 265 | // 9. PBINDS WITH CUSTOM INSTRUMENTS 266 | // ********************************** 267 | 268 | // Once you know how to create your own Synth Definitions, 269 | // you can use Pbind to play them. For example, let's use the 270 | // "testing" SynthDef that we created above at #7: 271 | 272 | ( 273 | Pbind( 274 | \instrument, "testing", 275 | \freq, Pseq([440, 880, 650], 3), 276 | \amp, Pwhite(0.1, 0.5), 277 | \dur, Pwhite(0.3, 1) 278 | ).play; 279 | ) 280 | 281 | // Notice that this works nicely since each note created 282 | // by the SynthDef "testing" knows how to stop itself (doneAction: 2) 283 | 284 | 285 | 286 | 287 | // ****************** 288 | // 11. FUNCTIONS 289 | // ****************** 290 | 291 | // When you find yourself doing the same task several times, 292 | // it may be a good time to create a reusable function. 293 | // A function is something enclosed within curly braces. 294 | 295 | f = { 2 + 2 }; // define the function 296 | 297 | f.value; // put the function to work 298 | 299 | // The function above is not that useful, as it only knows how to do one thing. 300 | // Normally you will want to define functions that can accept arguments. 301 | 302 | g = { arg a, b; ["a plus b", a+b, "a times b", a*b].postln }; 303 | 304 | g.value(3, 7); // now you can give any two numbers as arguments to the function 305 | g.value(10, 14); 306 | 307 | // Compare the difference: 308 | 309 | ~sillyRand = rrand(0, 10); 310 | ~sillyRand.value; // evaluate several times 311 | 312 | ~sillyRand2 = { rrand(0, 10) }; 313 | ~sillyRand2.value; // evaluate several times 314 | 315 | 316 | 317 | 318 | 319 | // ************************ 320 | // 12. HOW TO "DO" AN ARRAY 321 | // ************************ 322 | 323 | // Often you will need to do some action over all items of a collection. 324 | // Suppose you have a list of frequencies: 325 | 326 | ~myFreqs = Array.fill(10, { rrand(440, 880) }); 327 | 328 | // Now let's do some simple action on every item of the list: 329 | 330 | ~myFreqs.do({ arg item, count; ["Freq", item, "Closest midinote is", item.cpsmidi.round].postln }); 331 | 332 | 333 | 334 | 335 | 336 | 337 | // ************************************************************** 338 | // end of this tutorial 339 | // ************************************************************** -------------------------------------------------------------------------------- /10_Things_You_Need_To_Know.scd: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////// 2 | // 10 Things You Need To Know (SuperCollider Total Beginner) // 3 | //////////////////////////////////////////////////////////////// 4 | 5 | // ***************************** 6 | // 1. EVALUATE: CONTROL + ENTER 7 | // ***************************** 8 | 9 | // Use Ctrl + Enter to evaluate a line or a code block in SuperCollider. 10 | // On a Mac, use the "command" key instead of control. 11 | // Try it: put your cursor anywhere on the line below and hit Ctrl + Enter. 12 | // Watch the post window for the result. 13 | 14 | 2 + 2 + 10; 15 | 16 | // ******************************** 17 | // 2. BOOT THE SERVER: CONTROL + B 18 | // ******************************** 19 | 20 | // SuperCollider will only make sound if its "sound engine" (the Server) 21 | // is turned on. Hit Ctrl + B to boot the Server. Alternatively, you can 22 | // right-click on the numbers on the bottom right of the SC window (where 23 | // it says "Server"), and choose "Boot Server" from the menu. When the 24 | // Server is on, those numbers will be in green color. After booting 25 | // the Server, run the line below to test that you can make sound: 26 | 27 | { SinOsc.ar(freq: MouseY.kr(300, 2000), mul: MouseX.kr(0, 1)) }.play; 28 | 29 | // (Move your mouse up and down just for fun. Sound will fade out in 10 seconds.) 30 | 31 | 32 | // ************************************** 33 | // 3. STOP ALL SOUNDS: CONTROL + PERIOD. 34 | // ************************************** 35 | 36 | // Before blasting more sounds through the speakers, it's a good idea to 37 | // know how to stop all sounds. The shortcut you will be using all 38 | // the time is Ctrl + period. Run the two lines below to make some 39 | // white noise that starts soft and slowly gets louder. Stop the 40 | // sounds with Ctrl + period when you feel like. 41 | 42 | {WhiteNoise.ar(Line.kr(start: 0, end: 1, dur: 60))}.play; 43 | 44 | // *********************************** 45 | // 4. END YOUR LINES WITH A SEMICOLON 46 | // *********************************** 47 | 48 | // Indicate the end of a line of code by putting a semicolon at the end: 49 | 50 | "This is a line of code".postln; 51 | 52 | [1, 2, 3, 4, 5].reverse; // line ends at semicolon 53 | 54 | // Yes, you will occasionally see lines that do not use it; 55 | // Yes, you will see that in some situations it works just the 56 | // same without the semicolon, and then you may be tempted to 57 | // leave some semicolons out, or you may just forget it sometimes. 58 | // Still, if you get into the habit of always ending your lines 59 | // with semicolons, you may save yourself from some trouble in the future. 60 | 61 | // ************** 62 | // 5. CODE BLOCK 63 | // ************** 64 | 65 | // Often you will want to run several lines of code with a single 66 | // keystroke. You can always select all lines and then evaluate them, 67 | // but a handy alternative is to enclose all your code within parentheses. 68 | // The "code block" between those parentheses will be evaluated at once 69 | // (line by line, in order). You can have your cursor *anywhere* inside 70 | // the block and Ctrl + Enter will work just the same! 71 | 72 | ( 73 | "This is the first line".postln; 74 | "This is the second line".postln; 75 | 8 + 8; // this is the third line 76 | "This is my last line".postln; 77 | ) 78 | 79 | // ************** 80 | // 6. PRECEDENCE 81 | // ************** 82 | 83 | // Multiplication and division will NOT go first. 84 | // SuperCollider goes in order. 85 | 86 | 5 + 2 * 2; // equals 14, not 9. 87 | 88 | // If you want to force a specific order of operations, use parentheses: 89 | 90 | 5 + (2 * 2); // equals 9. 91 | 92 | // ************ 93 | // 7. COMMENTS 94 | // ************ 95 | 96 | // All text that you see in RED is a "comment". 97 | // If you are new to programming languages, comments are a very 98 | // useful way to document your code, both for yourself and for 99 | // others who may have to read your code. Any line that starts 100 | // with a double slash is a comment. You can write comments 101 | // right after valid lines of code; the comment part will be 102 | // ignored when you evaluate. 103 | 104 | 2 + 5 + 10 - 5; // just doing some math 105 | rrand(10, 20); // generate a random number between 10 and 20 106 | 107 | // You can evaluate a line even if your cursor is in the middle 108 | // of the comment after that line. The comment part is ignored. 109 | 110 | // If you write a really long comment, your text may break into what looks like a new line that does *not* start with a double slash. That still counts as a single line of comment: as you can see, this whole bunch of text is all in RED because it belongs to the same line that begins with a double slash above. I am being verbose here just to make the point... ;-) 111 | 112 | /* Now, if you would like to "comment out" a big section of code comprising several lines, you can use the combination "slash + asterisk" to open and "asterisk + slash" to close a big chunk of code that you want to comment out. A nice trick to comment and "un-comment" a big section is to select the whole thing and hit Ctrl + slash. Try it with this paragraph. */ 113 | 114 | // *********************************** 115 | // 8. HOW TO CLEAN UP THE POST WINDOW 116 | // *********************************** 117 | 118 | // Sometimes it is a good idea to clean up the Post Window 119 | // of all the stuff that got printed there. There is a great 120 | // shortcut for that: Ctrl + Shift + P. Try it now. 121 | 122 | // Now evaluate this line and clean the Post Window afterwards: 123 | 124 | 1000.do({"Print this line over and over...".postln}); 125 | 126 | 127 | // ************************* 128 | // 9. POSTLN IS YOUR FRIEND 129 | // ************************* 130 | 131 | // You can explicitly request to SuperCollider to "post" 132 | // some data on the Post Window. This is useful to debug 133 | // your code or just to learn more about what is going on. 134 | // Evaluate the code block below and watch the Post Window. 135 | 136 | ( 137 | 138 | "You will not see on the Post Window the result of 76 + 10".postln; 139 | 140 | 76 + 10; // the calculation is made, but there's no request to post it. 141 | 142 | "...but you WILL see the result of 7 + 7 in the following line".postln; 143 | 144 | (7 + 7).postln; // this line says, "post the result of seven plus seven" 145 | 146 | "This is a line of this block that it won't get posted"; 147 | 148 | "Bye".postln; 149 | 150 | ) 151 | 152 | // All lines above are evaluated in order. We get to see the results 153 | // of those that end with "postln", but not those without a postln. 154 | // "So why does the following line gets posted anyway?", you may ask. 155 | 156 | 76 + 10; // no postln; why do we see the results posted? 157 | 158 | // Answer: by default, SuperCollider always "dumps" on the Post Window 159 | // whatever was the last result of the last evaluated function. So the 160 | // result of the sum above is the last thing that results, thus it 161 | // gets posted. Try evaluating the small code block below: 162 | 163 | ( 164 | 100 + 11; 165 | 7 + 7; 166 | ) 167 | 168 | // Both lines are evaluated, but the last thing that "returns" 169 | // is the number 14. This is what gets posted. This is way the 170 | // word "Bye" in the earlier example was printed twice on the 171 | // Post Window (did you notice it?): the first time as the 172 | // result of a explicit call to postln, the second time because 173 | // it was the very last thing that resulted from that evaluated block. 174 | // Understanding this, and understanding how to use postln effectively 175 | // is VERY helpful to debug and better understand your code. 176 | 177 | 178 | // ************** 179 | // 10. VARIABLES 180 | // ************** 181 | 182 | // You can store numbers, words, UGens, functions, or entire blocks of code in VARIABLES. 183 | // Use the equal (=) sign to "assign" a variable. 184 | // Lowercase letters a through z can be used anytime: 185 | 186 | a = [1, 2, 3, 4, 5, 6].scramble; 187 | 188 | a; // now "a" holds the result of the line above. 189 | 190 | b = a + 10; // and "b" holds the result of a plus 10. 191 | 192 | // Do not use the letter "s" for a variable. It is normally reserved to represent the SuperCollider server. 193 | 194 | // Often it will make more sense to give better names to your variables, 195 | // to help you remember what they stand for in your code. 196 | // You can use a ~ to declare a variable with a longer name. 197 | 198 | ~myList = [10, 22, 33, 44, 55, 66].scramble; 199 | 200 | ~myList * 100; 201 | 202 | // Variable names must begin with the lowercase letters a through z (you can use numbers or underscores within the name, just not as the first character), and they must be contiguous (no spaces or punctuation). 203 | 204 | // There are two types of variables that you can create: environment variables and local variables. 205 | 206 | // ENVIRONMENT VARIABLES 207 | // The ones seen up to now are environment variables (some people loosely call them “global variables”): they are the single lowercase letters a through z, or those starting with the tilde (~) character. They will work anywhere in the patch, in other patches, even in other SC documents. 208 | 209 | // LOCAL VARIABLES 210 | // Local variables are declared with the reserved keyword var at the beginning of your code. Note that you can assign a value to a variable at declaration (var rate = 4). Variables declared this way only exist within the scope of that code block. 211 | 212 | // Here's a simple example comparing the two types of variables: 213 | 214 | // Environment variables 215 | ~galaApples = 4; 216 | ~bloodOranges = 5; 217 | ~limes = 2; 218 | ~plantains = 1; 219 | 220 | ["Citrus", ~bloodOranges + ~limes]; 221 | ["Non-citrus", ~plantains + ~galaApples]; 222 | 223 | // Local variables 224 | ( 225 | var apples = 4, oranges = 3, lemons = 8, bananas = 10; 226 | ["Citrus fruits", oranges + lemons].postln; 227 | ["Non-citrus fruits", bananas + apples].postln; 228 | "End".postln; 229 | ) 230 | 231 | ~galaApples; // still exists 232 | apples; // gone 233 | 234 | 235 | 236 | 237 | // ************************ 238 | // 11. (BONUS) USE THE HELP FILES 239 | // ************************ 240 | 241 | // Find help by selecting an Object or method in the code 242 | // and hitting Ctrl + D (for "Documentation"). This also works 243 | // if you simply place your cursor within the word to be looked up. 244 | // Sometimes you end up directly on a Help file, sometimes on a menu with 245 | // links to relevant Help files. Evaluate the line below just to see what it 246 | // does, then try placing your cursor anywhere inside the word "scramble", 247 | // then hit Ctrl + D: 248 | 249 | [1, 2, 3, 4, 5, 6].scramble; 250 | 251 | // You will see that "scramble" is a method that can be used with Objects 252 | // such as Array, List, or String. In the example above we have an Array. 253 | // Click on the word "Array" on the Help browser and you will be taken 254 | // directly to the explanation. 255 | 256 | // Not sure what an Array is? Either use the "Search" function in the Help 257 | // Browser or, often, you can just type the Object name and Ctrl + D: 258 | 259 | Array // try Ctrl + D on this word, see help browser 260 | SinOsc // try Ctrl + D on this word, see help browser 261 | 262 | 263 | // ************************************************************** 264 | // end of this tutorial 265 | // ************************************************************** -------------------------------------------------------------------------------- /Basic_Computer_Music_Concepts.scd: -------------------------------------------------------------------------------- 1 | // keep moving. 2 | // nothing to see here. 3 | 4 | // this tutorial file is unfinished. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ///////////////////////////////////// 32 | // Basic Computer Music Concepts 33 | // Sound Design class 34 | // After reading Hosken's chapter 2 35 | // Bruno Ruviaro, 2013-09-28 36 | ///////////////////////////////////// 37 | 38 | // Control + B to boot the server. 39 | // Control + M to open the meter window. 40 | // Use Control + Period to stop all sounds at anytime. 41 | 42 | 43 | // Just listen to a sine wave first. 44 | {SinOsc.ar(freq: 440, mul: 0.1)}.play; 45 | 46 | // A little more fun: 47 | {SinOsc.ar(freq: MouseX.kr(440, 2000), mul: 0.1)}.play; 48 | 49 | // Simplest example of a plot in SuperCollider: 50 | 51 | [1, 2, 3, 6, -6, 9].plot; 52 | 53 | // Customize it a bit: 54 | 55 | [1, 2, 3, 6, -6, 9].plot(minval: -100, maxval: 100); 56 | 57 | // Scroll down to begin... 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | // ****************************************** 88 | // FREQUENCY (physical), PITCH (perceptual) 89 | // ****************************************** 90 | 91 | { SinOsc.ar(freq: 440, mul: 0.1) }.play; 92 | 93 | // "freq" specifies the frequency of this Sine Wave Oscillator (SinOsc). 94 | // From the reading: 95 | // f (frequency) is measured in Hz, or cycles per second (cps). 96 | // T is the period of the waveform (measured in seconds) 97 | 98 | // f = 1/T 99 | 100 | // Suppose a very long period T of 1 second. 101 | // Then frequency is also 1 second (one cycle per second). 102 | // Here's a plot just for visualization (this won't play): 103 | 104 | {SinOsc.ar(freq: 1)}.plot(duration: 1); // default plot duration is 0.01; here we request 1 second instead. 105 | 106 | // Note that the horizontal axis (X) of the plot is the number of samples. 107 | // The vertical axis (-1 to +1) represents amplitude range (more on this later). 108 | // We can't hear 1 Hz; it's too low. 109 | // Human hearing range is 20 to 20000 Hz approximately. 110 | // 111 | // The lowest note on the piano is 27.5 Hz (low A). 112 | // The highest note on the piano is 4,186.01 Hz (high C). 113 | // Middle C is 261.63 Hz (AKA as "C4", or midi note 60). 114 | // Tuning fork A is 440 Hz (AKA as "A4", or midi note 69). 115 | 116 | // Exercise: play these notes (type the whole line to practice) 117 | 118 | // { SinOsc... etc. 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | // Let's plot one second of a 261.63 Hz sine wave (middle C). 147 | 148 | {SinOsc.ar(freq: 261.63)}.plot(duration: 1); 149 | 150 | // The plot above shows 261.63 cycles crammed into one second. 151 | // How do we find out the period (T)? 152 | // Remember, the period is the duration of a SINGLE cycle. 153 | // Just use the formula f = 1/T. 154 | 155 | // When you find the T value, 156 | // plug it into the "duration" of the plot: 157 | 158 | {SinOsc.ar(freq: 261.63)}.plot(duration: /*replace this by T*/ ); 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | // Frequency relationships in a nutshell: 180 | // Doubling or halving frequencies corresponds to OCTAVES in musical terms. 181 | 182 | // A4 183 | { SinOsc.ar(freq: 440, mul: 0.1) }.play; 184 | // A3 185 | { SinOsc.ar(freq: 220, mul: 0.1) }.play; 186 | // A2 187 | // ... type expression and play 188 | // A5 189 | // ... type expression and play 190 | 191 | 192 | 193 | // Compare two plots: 100 Hz and 200 Hz 194 | 195 | { [SinOsc.ar(freq: 100), SinOsc.ar(freq: 200)] }.plot(duration: 1/100); 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | // ******************************************** 229 | // AMPLITUDE (physical), LOUDNESS (perceptual) 230 | // ******************************************** 231 | 232 | // The text mentions "db SPL": starts from zero and goes up, 233 | // 0 = threshold of hearing, 120 = threshold of pain, etc. 234 | // For our purposes here and now, we will be just using a 235 | // linear scale between 0 and 1 to mean min and max amplitude. 236 | // In SC that's the "mul" input you will find in many places. 237 | // Thus, a "mul" of 1 means full amplitude, and 0 means silence. 238 | 239 | {SinOsc.ar(freq: 440, mul: 1)}.play // careful: LOUD! 240 | 241 | // All plot examples so far had Y axis between -1 and +1 (minval, maxval). 242 | // Sine waves shown above reached those two extremes (full amplitude). 243 | // Here's a sine wave that is half of maximum amplitude: 244 | 245 | {SinOsc.ar(freq: 440, mul: 0.5)}.plot(minval: -1, maxval: 1); 246 | 247 | // Compare two plots. Both 100 Hz, but one full amplitude, the other half amplitude: 248 | 249 | { [SinOsc.ar(freq: 440, mul: 1), SinOsc.ar(freq: 440, mul: 0.5)] }.plot(minval: -1, maxval: 1); 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | // ********** 300 | // TIMBRE 301 | // ********** 302 | 303 | // Here's a few artificial waveforms beyond the simple sine wave. 304 | // ("a sort of rudimentary timbre vocabulary" -- Hosken p. 25) 305 | 306 | // SINE WAVE 307 | { SinOsc.ar(100) }.plot; // see 308 | { SinOsc.ar(MouseY.kr(50, 1000), mul: 0.1) }.play; // listen 309 | 310 | // TRIANGLE WAVE 311 | { LFTri.ar(100) }.plot; // see 312 | { LFTri.ar(MouseY.kr(50, 1000), mul: 0.1) }.play; // listen 313 | 314 | // SAWTOOTH WAVE 315 | { LFSaw.ar(100) }.plot; // see 316 | { LFSaw.ar(MouseY.kr(50, 1000), mul: 0.05) }.play; // listen 317 | 318 | // SQUARE WAVE 319 | { LFPulse.ar(100) }.plot; // see 320 | { LFPulse.ar(MouseY.kr(50, 1000), mul: 0.05) }.play; // listen 321 | 322 | // PULSE WAVE 323 | { LFPulse.ar(100, width: 0.2) }.plot; // basically a square with different "width" value 324 | { LFPulse.ar(MouseY.kr(50, 1000), width: 0.2, mul: 0.05) }.play; // sounds a bit different 325 | 326 | 327 | 328 | // WHITE NOISE 329 | { WhiteNoise.ar(mul: 1) }.plot; // see 330 | { WhiteNoise.ar(mul: 1) }.plot(duration: 0.001); // see (zoomed in) 331 | { WhiteNoise.ar(mul: MouseY.kr(0, 0.2)) }.play; // listen 332 | 333 | // PINK NOISE 334 | { PinkNoise.ar(mul: 1) }.plot; // see 335 | { PinkNoise.ar(mul: MouseY.kr(0, 0.2)) }.play; // listen 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | // ************* 391 | // ARTICULATION 392 | // ************* 393 | 394 | // Amplitude Envelope 395 | 396 | // Plot 1 second of a sine wave at full amplitude (no change in amp): 397 | { SinOsc.ar(freq: 440, mul: 1) }.plot(duration: 1); 398 | 399 | // Plot a sine wave starting at full amplitude (1) 400 | // and decreasing to 0 amplitude over 2 seconds: 401 | { SinOsc.ar(freq: 440, mul: Line.kr(start: 1, end: 0, dur: 2)) }.plot(duration: 2); 402 | 403 | // Listen: 404 | { SinOsc.ar(freq: 440, mul: Line.kr(start: 1, end: 0, dur: 2)) }.play; 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | ////////////////// 433 | // Envelope Types 434 | ////////////////// 435 | 436 | 437 | 438 | 439 | // Plot a sine wave starting at full amplitude (1) 440 | // and decreasing to 0 amplitude over 2 seconds: 441 | { SinOsc.ar(freq: 440, mul: Line.kr(start: 1, end: 0, dur: 2)) }.plot(duration: 2); 442 | // Listen: 443 | { SinOsc.ar(freq: 440, mul: Line.kr(start: 1, end: 0, dur: 2)) }.play; 444 | 445 | 446 | 447 | 448 | 449 | // Typical AR envelope ("struck or plucked" model) 450 | // Attack and Release 451 | 452 | // Meet Env, a handy tool to create envelopes 453 | Env.perc.plot; // see 454 | Env.perc.test; // listen 455 | 456 | // Lines of an envelope can be straight or curved: 457 | Env.perc(curve: 0).test.plot; // straight lines 458 | Env.perc(curve: -4).test.plot; // curved lines (generally 'sound more natural') 459 | 460 | // Customize it a bit: 461 | Env.perc(attackTime: 0.5, releaseTime: 2).test.plot; 462 | 463 | // Typical ADSR envelope ("bowed or blown" model) 464 | // Attack, Decay, Sustain, Release 465 | 466 | Env.adsr.plot; 467 | Env.adsr.test; 468 | 469 | // Straight or curved lines: 470 | Env.adsr(curve: 0).plot; 471 | Env.adsr(curve: -4).plot; 472 | 473 | // Customize it a bit: 474 | Env.adsr(attackTime: 0.1, decayTime: 1, sustainLevel: 0.1, releaseTime: 1, curve: 0).test.plot; 475 | Env.adsr(attackTime: 0.01, decayTime: 0.2, sustainLevel: 0.2, releaseTime: 1, curve: 0).test.plot; 476 | 477 | // BTW... 478 | // You don't have to be verbose all the time. With time 479 | // and practice, you can get rid of the keywords in green. 480 | // SC knows the order of arguments. 481 | 482 | Env.perc(0.01, 4).test.plot; 483 | Env.adsr(0.1, 0.5, 0.4, 2).test.plot; 484 | 485 | // Other envelopes: 486 | Env.triangle.test.plot; 487 | Env.linen.test.plot; 488 | Env.pairs(pairs: [[0, 0], [0.1, 1], [0.2, 0.2], [1.5, 1], [2, 0]], curve: 0).test.plot; 489 | 490 | // BTW... 491 | // Feel free to format lines in other ways 492 | // to make them more readable: 493 | 494 | ( 495 | Env.pairs( 496 | pairs: [ 497 | [0.0, 0.0], // time, level 498 | [0.1, 1.0], // time, level 499 | [0.2, 0.2], // etc... 500 | [1.5, 1.0], 501 | [2.0, 0.0] 502 | ], 503 | curve: 0).test.plot; 504 | ) 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | // Finally.. how to use Env with a SinOsc, WhiteNoise, LFSaw, etc... 540 | // Just put it inside an EnvGen.kr 541 | // Note: do not try with adsr just yet. It will need a bit more to work. 542 | 543 | // PERC 544 | // See 545 | { SinOsc.ar(freq: 440, mul: EnvGen.kr(Env.perc)) }.plot(duration: 1); 546 | // Listen 547 | { SinOsc.ar(freq: 440, mul: EnvGen.kr(Env.perc)) }.play; 548 | 549 | // TRIANGLE 550 | // See 551 | { SinOsc.ar(freq: 100, mul: EnvGen.kr(Env.triangle)) }.plot(duration: 1); 552 | // Listen 553 | { SinOsc.ar(freq: 100, mul: EnvGen.kr(Env.triangle)) }.play; 554 | 555 | // LINEN 556 | // See 557 | { SinOsc.ar(freq: 440, mul: EnvGen.kr(Env.linen)) }.plot(duration: 2); 558 | // Listen 559 | { SinOsc.ar(freq: 440, mul: EnvGen.kr(Env.linen)) }.play; 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | // Last: 590 | // Simultaneous oscillators! 591 | 592 | // Ouch! Why does the following comes out distorted? 593 | ( 594 | { SinOsc.ar(freq: 140, mul: EnvGen.kr(Env.perc(attackTime: 0.01, releaseTime: 1))) }.play; 595 | { SinOsc.ar(freq: 440, mul: EnvGen.kr(Env.perc(attackTime: 0.01, releaseTime: 1))) }.play; 596 | { SinOsc.ar(freq: 1449, mul: EnvGen.kr(Env.perc(attackTime: 0.01, releaseTime: 1))) }.play; 597 | { SinOsc.ar(freq: 2245, mul: EnvGen.kr(Env.perc(attackTime: 0.01, releaseTime: 1))) }.play; 598 | ) 599 | 600 | // Solution: 601 | ( 602 | { SinOsc.ar(freq: 140, mul: EnvGen.kr(Env.perc(attackTime: 0.01, releaseTime: 1, level: 0.1))) }.play; 603 | { SinOsc.ar(freq: 440, mul: EnvGen.kr(Env.perc(attackTime: 0.01, releaseTime: 1, level: 0.1))) }.play; 604 | { SinOsc.ar(freq: 1449, mul: EnvGen.kr(Env.perc(attackTime: 0.01, releaseTime: 1, level: 0.1))) }.play; 605 | { SinOsc.ar(freq: 2245, mul: EnvGen.kr(Env.perc(attackTime: 0.01, releaseTime: 1, level: 0.1))) }.play; 606 | ) 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | // ********** 615 | // EXERCISES 616 | // ********** 617 | 618 | // 1) 619 | // Choose an oscillator (SinOsc, LFTri, etc). 620 | // Use the horizontal motion of your mouse to control frequency (choose min and max). 621 | // Use the vertical motion of your mouse to control amplitude (0-1). 622 | // Play it. 623 | 624 | 625 | 626 | // 2) 627 | // Now create a few more lines of code like the above. 628 | // Choose a different oscillator for each line. 629 | // Choose different min and max frequency values for each line. 630 | // TIP: keep amplitudes low, between 0 and 0.2 for example 631 | // (remember: all oscillators will add up in amplitude) 632 | // Evaluate all lines at the same time. 633 | // Play and hear the results. 634 | 635 | 636 | // 3) 637 | // Write a chord of 4 or more sine waves, but: 638 | // Use a different frequency for each sine wave (no mouse control); 639 | // Use a percussive Amplitude Envelope for each sine wave; 640 | // Use a different attackTime and releaseTime for each note of the chord. 641 | // Make sure that all levels do not add up to more than 1. 642 | // Hear the results. 643 | 644 | -------------------------------------------------------------------------------- /Patterns_Tutorial.scd: -------------------------------------------------------------------------------- 1 | ////////////// 2 | // PATTERNS // 3 | ////////////// 4 | 5 | // QUICK INTRO TO PATTERNS 6 | // The goal is to start making sound right away as you learn to use Patterns. 7 | // Don't worry about every single detail of what is happening under the hood. 8 | // Later on, you can learn a lot more about Patterns by reading the 9 | // excellent "Pattern Guide" (by James Harkins) in the Documentation. 10 | 11 | // A Pattern is like a blueprint for a building, or a recipe. 12 | // The Pattern below specifies the following recipe: "take the list provided and read through it twice". 13 | 14 | Pseq([1, 2, 3, 4, 5], 2); 15 | 16 | // If you evaluate the line above, you see no actual result: it's just a recipe. 17 | // In order to quickly see the results of the recipe, we do this: 18 | 19 | Pseq([1, 2, 3, 4, 5], 2).asStream.all; 20 | 21 | // This is just to understand what the Pattern will do when 'streamed' in time. 22 | // Supose we want to play a simple scale: C minor, ascending, each note lasting 0.3 seconds: 23 | 24 | ( 25 | Pbind( 26 | \midinote, Pseq([60, 62, 63, 65, 67, 68, 71, 72], 2), 27 | \dur, 0.3, 28 | ).play; 29 | ) 30 | 31 | // Pbind is the structure that 'binds it all together'. It's a collection of key/value pairs. 32 | // Inside, you can specify sequences of values for pitches, durations, amplitude, etc. 33 | // Normally you will use Patterns to define these sequences of values. 34 | // IMPORTANT: Pbind will use default values for parameteres you do not explicitly specify. 35 | // If you omit "dur", for example, it will use a default 1-second duration for each note: 36 | 37 | ( 38 | Pbind( 39 | \midinote, Pseq([60, 62, 63, 65, 67, 68, 71, 72]), 40 | ).play; 41 | ) 42 | 43 | // Here's another example using Pseq for notes, durations, and amplitudes: 44 | ( 45 | Pbind( 46 | \midinote, Pseq([60, 67, 78, 71, 40], 2), 47 | \dur, Pseq([0.1, 0.1, 0.5, 0.4, 1], inf), 48 | \amp, Pseq([0.1, 0.2, 0.3, 0.2, 0.2], inf); 49 | ).play; 50 | ) 51 | 52 | // The examples below will use that C minor scale a lot, so let's store it into a variable: 53 | h = [60, 62, 63, 65, 67, 68, 71, 72]; 54 | 55 | 56 | h 57 | 58 | 59 | // We'll now go through a bunch of different Patterns. 60 | // Like when we learn to speak a new language, this is the moment 61 | // of learning new words to expand our vocabulary. 62 | // If you can't figure out what the Pattern is doing, 63 | // put your cursor on the Pattern name and type ctrl + D (help file) 64 | 65 | // Pseq 66 | ( 67 | Pbind( 68 | \midinote, Pseq(list: h, repeats: 4), 69 | \dur, 0.15; 70 | ).play; 71 | ) 72 | 73 | // Pser 74 | ( 75 | Pbind( 76 | \midinote, Pser(list: h, repeats: 15), 77 | \dur, 0.15; 78 | ).play; 79 | ) 80 | 81 | // Pslide 82 | ( 83 | Pbind( 84 | \midinote, Pslide(list: h, repeats: 7, len: 3, step: -1), 85 | \dur, 0.15; 86 | ).play; 87 | ) 88 | 89 | // Prand 90 | ( 91 | p = Pbind( 92 | \midinote, Prand(h, inf), 93 | \dur, 0.2; 94 | ).play; 95 | ) 96 | 97 | // Pxrand 98 | ( 99 | p = Pbind( 100 | \midinote, Pxrand(h, inf), 101 | \dur, 0.2; 102 | ).play; 103 | ) 104 | 105 | // Pwhite 106 | ( 107 | p = Pbind( 108 | \midinote, Pseq(h, 3), 109 | \dur, Pwhite(0.1, 0.3); 110 | ).play; 111 | ) 112 | 113 | 114 | // Pshuf 115 | ( 116 | p = Pbind( 117 | \midinote, Pshuf(h, 6), 118 | \dur, Prand([0.1, 0.2], inf); 119 | ).play; 120 | ) 121 | 122 | // Define some chords 123 | c = [[60, 63, 67], [62, 65, 68], [63, 67, 70], [65, 68, 72]]; 124 | 125 | // Chords with Pseq 126 | ( 127 | Pbind( 128 | \midinote, Pseq(list: c, repeats: 3), 129 | \dur, 0.15; 130 | ).play; 131 | ) 132 | 133 | // Place 134 | ( 135 | Pbind( 136 | \midinote, Place(list: c, repeats: 3), 137 | \dur, 0.15; 138 | ).play; 139 | ) 140 | 141 | // Check it out what Place does: 142 | Place(c, 3).asStream.all; 143 | 144 | // Now let's create new series of numbers from scratch 145 | // (no use of pre-defined sets of numbers like h and c above) 146 | 147 | // Pseries 148 | ( 149 | p = Pbind( 150 | \midinote, Pseries(start: 90, step: -2, length: 21), 151 | \dur, 0.1; 152 | ).trace.play; 153 | ) 154 | 155 | // Pgeom 156 | ( 157 | p = Pbind( 158 | \midinote, Pseries(start: 90, step: -2, length: 21), 159 | \dur, Pgeom(start: 0.1, grow: 1.1); 160 | ).trace.play; 161 | ) 162 | 163 | // Ppatlace 164 | ( 165 | p = Pbind( 166 | \midinote, Ppatlace([ 167 | Pseries(60, 2, 8), // first, third etc. notes 168 | Pseries(72, 2, 8) // second, fourth etc. notes 169 | ], inf), 170 | \dur, 0.25 171 | ).play; 172 | ) 173 | 174 | 175 | // Check it out what it does: 176 | Ppatlace([Pseries(60, 2, 8), Pseries(72, 2, 8)], inf).asStream.all 177 | 178 | // You can use freq instead of midinote 179 | ( 180 | Pbind( 181 | \freq, Pseries(220, 220, 8), // first 8 partials of 220Hz 182 | \dur, 0.25; 183 | ).play; 184 | ) 185 | // Another option: degree (as in scale degree) 186 | ( 187 | Pbind( 188 | \note, Pseq([0, 1, 2, 3, 4, 5, 6, 7], 1), 189 | \dur, 0.25; 190 | ).play; 191 | ) 192 | 193 | 194 | 195 | // Major scale is default for degrees. How to change scales: 196 | ( 197 | Pbind( 198 | \scale, Scale.zamzam, // try minor, chromatic, dorian, etc 199 | \degree, Pseq([0, 1, 2, 3, 4, 5, 6, 7], 1), 200 | \dur, 0.25; 201 | ).play; 202 | ) 203 | 204 | 205 | // Ptuple 206 | ( 207 | Pbind( 208 | \scale, Scale.phrygian, 209 | \degree, Ptuple([ 210 | Prand([5, 6, 5, 6, 8, 7, 6, 7], inf), 211 | Prand([3, 4, 3, 5, 5, 5, 5, 4], inf), 212 | Pseq([0, 0, 0, 2, 3, 4, 3, 1], inf)], inf), 213 | \dur, Pseq([0.15, 0.15, 0.25, 0.25, 0.15, 0.15, 0.25], inf), 214 | \legato, 0.1; 215 | ).play; 216 | ) 217 | 218 | /* See more at 219 | "http://doc.sccode.org/Tutorials/A-Practical-Guide/PG_02_Basic_Vocabulary.html" 220 | */ 221 | 222 | // "Flock of seagulls" example by James Harkins 223 | ( 224 | Pbind( 225 | \degree, Pslide((-6, -4 .. 12), 8, 3, 1, 0), 226 | \dur, Pseq([0.1, 0.1, 0.2], inf), 227 | \legato, 0.7 228 | ).play; 229 | ) 230 | 231 | // So far we have been using the "default" instrument. Let's load a few more interesting ones. 232 | // "PMCrotale" 233 | // "blips" 234 | // "noisy" 235 | // "hihat" 236 | // "snare" 237 | // "kick" 238 | // "kick2" 239 | 240 | /* 241 | From this point on we need the file SynthDefs_2013.scd to be evaluated, so that these new synths are loaded into memory. If you have copied the entire folder of tutorials from https://github.com/brunoruviaro/SuperCollider_Tutorials, you should already have this file. In this case you can simply run the following line to evaluate it: 242 | */ 243 | 244 | "SynthDefs_2013.scd".loadRelative; 245 | 246 | // Now try a Pbind with a different instrument: 247 | ( 248 | Pbind( 249 | \instrument, "PMCrotale", 250 | \midinote, Pseq([60, 67, 78, 71, 40], 4), 251 | \dur, Pseq([0.1, 0.1, 0.5, 0.4, 1], inf), 252 | \art, 1, 253 | \amp, 0.2, 254 | \awesome, 10 255 | ).play; 256 | ) 257 | 258 | // How about the Ptuple example again with crotales? 259 | ( 260 | Pbind( 261 | \instrument, "PMCrotale", 262 | \scale, Scale.phrygian, 263 | \degree, Ptuple([ 264 | Pseq([5, 6, 5, 6, 8, 7, 6, 7], inf), 265 | Pseq([3, 4, 3, 5, 5, 5, 5, 4], inf), 266 | Pseq([0, 0, 0, 2, 3, 4, 3, 1], inf)], inf), 267 | \dur, Pseq([0.15, 0.15, 0.25, 0.25, 0.15, 0.15, 0.25], inf), 268 | \art, 0.5, 269 | \amp, 0.3; 270 | ).play; 271 | ) 272 | 273 | // "PMCrotale" instrument has a specific "art" (Attack and Release Time) parameter. Try changing it. 274 | // Each instrument will have its own specific parameters. Some (like dur) are very common. 275 | // We will see later how to create your own instruments. 276 | // For now, just use the ones provided to practice writing Pbinds and Patterns. 277 | 278 | // Write a Pattern for the \art parameter. Suggestion: try Prand or Pwhite. 279 | ( 280 | Pbind( 281 | \instrument, "PMCrotale", 282 | \scale, Scale.phrygian, 283 | \degree, Ptuple([ 284 | Pseq([5, 6, 5, 6, 8, 7, 6, 7], inf), 285 | Pseq([3, 4, 3, 5, 5, 5, 5, 4], inf), 286 | Pseq([0, 0, 0, 2, 3, 4, 3, 1], inf)], inf), 287 | \dur, Pseq([0.15, 0.15, 0.25, 0.25, 0.15, 0.15, 0.25], inf), 288 | \art, // WRITE A PATTERN HERE (don't forget the comma at the end) 289 | \amp, 0.3; 290 | ).play; 291 | ) 292 | 293 | // Keep going: for every example below, change existing Patterns, create new ones, etc. 294 | 295 | // "blips" instrument example. Relevant parameters: freq, dur, numharm, release, amp. 296 | ( 297 | Pbind( 298 | \instrument, "blips", 299 | \freq, Pseq([20, 27, 38, 21, 30], inf) * 3, // try other multipliers 300 | \dur, Pseq([1, 1, 0.5, 0.4, 1], inf), 301 | \numharm, Pwhite(10, 50), 302 | \rel, 1, 303 | \amp, Pwhite(0.1, 1); 304 | ).play; 305 | ) 306 | 307 | // "noisy" instrument example. Relevant parameters: freq, dur, amp. 308 | ( 309 | Pbind( 310 | \instrument, "noisy", 311 | \freq, Pseries(110, 10, 20), 312 | \dur, Pseq([1/4, 1/4, 1/8], inf), 313 | \amp, Pwhite(0.1, 0.4); 314 | ).play; 315 | ) 316 | 317 | // "hihat" instrument example. Relevant parameters: duration, filter frequency, release. 318 | ( 319 | Pbind( 320 | \instrument, "hihat", 321 | \dur, Pseq([1/2, 1/2, 1/2, 1/4, 1/4] * 0.35, inf), // + Prand([0, 0.001],inf), 322 | \ffreq, 9000, // Prand([15000, 9000, 8000], inf), 323 | \rel, 0.1; // Prand( (0.01, 0.02 .. 0.1), inf) 324 | ).play; 325 | ) 326 | 327 | // "snare" instrument example. 328 | ( 329 | Pbind( 330 | \instrument, "snare", 331 | \dur, Pseq([Pseq([1, 1/3, 1/3, 1/3], 3), 1, 1], 4) * 0.3, 332 | \sinfreq, Pwhite(100,110,inf), 333 | \amp, 0.05; 334 | ).play; 335 | ) 336 | 337 | // "kick" instrument example. 338 | ( 339 | Pbind( 340 | \instrument, "kick", 341 | \dur, Pseq([1, Rest, 1, Rest], inf) * 1/4, 342 | \amp, Pseq([0.5, Rest, 1, Rest], inf), 343 | \rel, 0.5, // Pwhite(0.5, 0.7, inf), 344 | \glissf, 0.2; // Pwhite(0.1, 0.9, inf) 345 | ).play; 346 | ) 347 | 348 | // "kick3" instrument example 349 | 350 | ( 351 | p = Pbind( 352 | \instrument, "kick3", 353 | \punch, Pseq([ 354 | Pseq([0.01], 4), 355 | Pseq([0.1], 4), 356 | Pseq([1], 4), 357 | Pseq([10], 4), 358 | Pseq([100], 4), 359 | Pseq([1000], 4)], inf), 360 | \amp, 0.1, // careful: can be loud! 361 | \dur, 0.5, 362 | ).play; 363 | ) 364 | 365 | // A shorter way of writing the above: 366 | ( 367 | p = Pbind( 368 | \instrument, "kick3", 369 | \punch, Place([0.01, 0.1, 1, 10, 100, 1000]!4, inf), 370 | \amp, 0.1, // careful: can be loud! 371 | \dur, 0.5, 372 | ).play; 373 | ) 374 | 375 | Place([0.01, 0.1, 1, 10, 100, 1000]!4, 40).asStream.all; 376 | 377 | // Chords (note the "tone" parameter) 378 | ( 379 | Pbind( 380 | \instrument, "PMCrotale", 381 | \midinote, Pseq([[57,60,64]-3, [57,60,64]-6], inf)-Prand([0,1,3],inf), 382 | \dur, Prand([1/2, 1/2, 1/4, 1/3, 1/3, 1/5], inf), 383 | \art, Prand([1.5, 1.75, 2], inf), 384 | \tone, Pwhite(2, 3), 385 | \amp, Prand([0.2, 0.3, 0.1],inf), 386 | ).play; 387 | ) 388 | 389 | 390 | // ********************************** 391 | 392 | 393 | // Remember, you can always use .asStream.all to quickly check what the ouput of a pattern will look like. 394 | 395 | Pseq([1, 2, 3], 4).asStream.all; // 12 items = 4 repeats * 3 items 396 | Pser([1, 2, 3, 4, 5, 6, 7], 5).asStream.all; // 5 items only 397 | Pwhite(0, 7, 10).asStream.all; // 10 random numbers between 0 and 7 (including 7) 398 | Pslide([1, 2, 3, 4, 5, 6, 7, 8], 10, 3, 1, 0, false).asStream.all; 399 | 400 | // Another way of writing the same thing (declaring arguments explicitly may be helpful): 401 | ( 402 | Pslide( 403 | list: [1, 2, 3, 4, 5, 6, 7, 8], 404 | repeats: 10, // number of segments 405 | len: 3, // length of each segment 406 | step: 1, // step between segments 407 | start: 0, // what index to start at 408 | wrapAtEnd: false // do not wrap around 409 | ).asStream.all.clump(3) // clump -- easier to see the groupings 410 | ) 411 | 412 | // Dirty trick to generate an Array: 413 | 414 | (-5, -4 .. 12); 415 | 416 | // So this is what we get: 417 | 418 | Pslide((-5, -4 .. 12), 20, 3, 1, 0, false).asStream.all.clump(3); 419 | 420 | 421 | // ------------------------------------------------ // 422 | // DIFFERENCE BETWEEN PBIND AND EVENT STREAM PLAYER 423 | // ------------------------------------------------ // 424 | 425 | // The Pbind is like the actual "SCORE". It is not the "PLAYER" 426 | // When you call "play" on a Pbind, it returns a 427 | // Event Stream Player. THAT is the actual "player" of the score. 428 | 429 | a = Pbind(\degree, Pwhite(10, 14), \dur, 1/2); 430 | b = Pbind(\note, Pseq([[0, 4, 9], [-1, 3, 8]], inf), \dur, 1); 431 | 432 | ~player1 = a.play; 433 | ~player2 = b.play; 434 | 435 | // NOW you can ask the PLAYERS to stop: 436 | ~player1.stop; 437 | ~player2.stop; 438 | 439 | // Notice that it doesn't make sense to ask the "score" to stop 440 | 441 | Pbind( /* blah blah blah */).stop; // wrong 442 | 443 | 444 | // ----------- // 445 | // SEQUENCING 446 | // ----------- // 447 | 448 | // Simple way (not very flexible): 449 | 450 | ( 451 | { 452 | ~player1 = a.play; 453 | 1.wait; 454 | ~player2 = b.play; 455 | 5.wait; 456 | ~player1.stop; 457 | 5.wait; 458 | ~player2.stop; 459 | }.fork; 460 | ) 461 | 462 | // For more sophisticated control, look up Pspawner 463 | 464 | 465 | // --------------- // 466 | // CHANGING TEMPO 467 | // --------------- // 468 | 469 | ( 470 | t = TempoClock.new(72/60); 471 | 472 | Pbind( 473 | \degree, Pseq([7, 8, 9, 10], inf), 474 | \legato, 0.1 475 | ).play(t); 476 | ) 477 | 478 | // change it as it plays 479 | t.tempo = 120/60; 480 | 481 | // --------- // 482 | // QUANTIZE 483 | // --------- // 484 | 485 | a.play(quant: 1); 486 | b.play(quant: 1); 487 | 488 | 489 | // -------------- // 490 | // NEED SOME REST? 491 | // -------------- // 492 | 493 | ( 494 | Pbind( 495 | \note, Pseq([0, 1, 2, 3, 4, 5], inf), 496 | \dur, Pseq([0.1, 1, 0.4, Rest(0.1), 1), // 497 | ).play; 498 | ) 499 | 500 | /* 501 | 502 | EXERCISE: use Pseries and Pgeom to write a simple Pbind achieving the following musical goals: 503 | 504 | a) Create a three-octave descending scale using Pseries; 505 | b) Create a decreasing series of durations (accel) using Pgeom. First note played should have a duration of 1 second, and each successive note should have a shorter duration. 506 | c) Make every third note accented -- something like mf, p, p, mf, p, p, ... etc. 507 | 508 | */ 509 | 510 | // Scroll down for solution 511 | 512 | Pgeom(1, 0.9, 22).asStream.all; 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | // One solution for the exercise above 545 | ( 546 | p = Pbind( 547 | \degree, Pseries(7, -1, 22), 548 | \dur, Pgeom(1, 0.9, inf), 549 | \amp, Pser([0.3, 0.1, 0.1], inf), 550 | \legato, 0.3) 551 | ) 552 | 553 | p; 554 | 555 | p.play; 556 | 557 | 558 | 559 | 560 | 561 | 562 | -------------------------------------------------------------------------------- /UGens_Envelopes_Buses_Tutorial.scd: -------------------------------------------------------------------------------- 1 | // ****************************************** 2 | // UGens, Envelopes, Busses, etc. 3 | // ****************************************** 4 | 5 | // LANGUAGE VS. SERVER; UGENS 6 | // UGENS CONTROLLING OTHER UGENS; SCALING RANGES 7 | // OTHER CONVENIENT MESSAGES: PLOT AND SCOPE 8 | // AUDIO RATE, CONTROL RATE 9 | // MOUSE INPUT 10 | // MULTICHANNEL EXPANSION 11 | // MIC INPUT 12 | // PLAYING AN AUDIO FILE 13 | // SYNTH DEFINITIONS 14 | // A SIMPLE ENVELOPE; DONEACTION; PANNING 15 | // BUSES, NODES 16 | 17 | 18 | 19 | // --------------------------- // 20 | // LANGUAGE VS. SERVER; UGENS 21 | // --------------------------- // 22 | 23 | rrand(-1, 1.0); // Generates a random number between -1 and +1 every time you evaluate. No sound involved. This happens in the SuperCollider Language. 24 | 25 | // Caution: bring your volume down before evaluating the next line... 26 | 27 | { LFNoise0.ar(1) }.play; 28 | 29 | // LFNoise0 (through the Server) sends out 44100 samples per second to your sound card. Each sample is a number between -1 and +1. LFNoise0.ar(1) picks a new random number every second. The .ar means that the output is at AUDIO RATE (commonly 44100 Hz). This is done by the Server. 30 | 31 | // You can inspect what LFNoise0 is doing by using the message "poll": 32 | 33 | { LFNoise0.ar(1).poll }.play; 34 | 35 | /* poll is polling the output of the UGen 10 times per second, by default, and posting the results in the post window. In other words, out of the 44100 samples being generated every second, poll picks only 10 per second. Try changing the frequency of LFNoise0 and watch (and hear) the results. If it's too loud, you can multiply it by 0.1, for example. */ 36 | 37 | { (LFNoise0.ar(1) * 0.1).poll }.play; 38 | 39 | // LFNoise0 is what we call a UGen (Unit Generator). 40 | 41 | // From the Help file on UGen: 42 | /* "UGens represent calculations with signals. They are the basic building blocks of synth definitions on the server, and are used to generate or process both audio and control signals. The many subclasses of UGen are the client-side representations of unit generators, and are used to specify their parameters when constructing synth definitions (see SynthDef). */ 43 | 44 | // Let's take a look at a less noisy UGen: SinOsc. 45 | 46 | { Out.ar(0, SinOsc.ar(440, 0, 0.01)) }.play; 47 | 48 | { SinOsc.ar(440, 0, 0.1) }.play; // parameters are: freq, phase, mul. Take a look at the Help file. 49 | 50 | // You can be more explicit with the parameters: 51 | 52 | { SinOsc.ar(freq: 440, mul: 0.1) }.play; // note that I can omit a parameter if I'm using this method 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | // --------------------------------------------- // 61 | // UGENS CONTROLLING OTHER UGENS; SCALING RANGES 62 | // --------------------------------------------- // 63 | 64 | /* Most UGens have the "mul" and "add" parameters. They are used to multiply (mul) and offset (add) the default range of numbers being generated. The default output range of UGens is either bipolar (-1 to +1) or unipolar (0 to 1). Suppose you want to have a SinOsc to play a new random note every second. Let's say you want the notes to be between 440 Hz and 880 Hz. This means you need to find a way to change the SinOsc's frequency value randomly, once every second, and make sure that value is within the 440-880 range. */ 65 | 66 | // LFNoise0 can do that. However, by default LFNoise0 outputs numbers between -1 and +1... 67 | 68 | LFNoise0.ar.signalRange; // check it out 69 | 70 | // There are two ways to change that, in order to rescale the values to the desired range. 71 | // First let's take a look at the hard way... 72 | 73 | // The "hard" way: 74 | // Simply figure out the math to scale the range -1 to +1 to the new range 440 to 880. 75 | // Then use the built-in mul and add parameters accordingly (most UGens have those parameters) 76 | // Here is the code, indented and 'verbose' to make it more clear: 77 | 78 | ( 79 | {SinOsc.ar( 80 | freq: LFNoise0.ar(freq: 5, mul: 220, add: 660), 81 | phase: 0, 82 | mul: 0.1)}.play 83 | ) 84 | 85 | // Now the easy way: 86 | 87 | ( 88 | {SinOsc.ar( 89 | freq: LFNoise0.ar(1).range(440, 880), 90 | phase: 0, 91 | mul: 0.1)}.play 92 | ) 93 | 94 | // It is still good to understand how to use mul and add to scale things. You will need them at some point. 95 | // Now, just for fun, try changing the frequency of the LFNoise0 above. 96 | // Also try LFNoise1 and LFNoise2 instead of LFNoise0. What is the difference? 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | // --------------------------------------------- // 105 | // OTHER CONVENIENT MESSAGES: PLOT AND SCOPE 106 | // --------------------------------------------- // 107 | 108 | // We just saw "range" -- a handy message to rescale the output of UGens. 109 | // We also saw that "poll" is useful to inspect the output of UGens in the post window. 110 | // Two other convenient messages are "plot" and "scope": 111 | 112 | { SinOsc.ar(400, 0, 0.1) }.plot; // a snapshot of the UGen output; no sound involved. 113 | { LFNoise0.ar(1000) }.plot; 114 | { LFNoise1.ar(1000) }.plot; 115 | { LFNoise2.ar(1000) }.plot; 116 | 117 | // Tip: with the Plotter window active, hit "N" to Normalize the vertical axis. 118 | 119 | {SinOsc.ar(440, 0, 0.1)}.scope; // play and visualize "stethoscope" window; 120 | {LFNoise0.ar(freq: 2, mul: 0.5)}.scope; 121 | 122 | // As you explore new UGens, use these messages to better understand what each UGen does. 123 | 124 | 125 | 126 | 127 | 128 | 129 | // -------------------------- // 130 | // AUDIO RATE, CONTROL RATE 131 | // -------------------------- // 132 | 133 | /* 134 | In one of the examples above we used a .ar message to LFNoise0. As a result, the output of the LFNoise0 was at audio rate, i.e., 44100 samples per second (or 48k, 96k, etc, depending on your system). All we wanted in that case, though, was to change a value once or a few times per second. It is an overkill to use audio rate for that. That's where the notion of CONTROL RATE comes in. Take a look at the .kr message below: 135 | */ 136 | 137 | ( 138 | {SinOsc.ar( 139 | freq: LFNoise0.kr(1).range(440, 880), 140 | phase: 0, 141 | mul: 0.1)}.play 142 | ) 143 | 144 | // SinOsc remains with .ar, because that's the sound generator. 145 | // LFNoise0 gets a .kr, more "economic" (outputs less numbers per second). 146 | // Since LFNoise0 here is just generating control data, we don't need .ar 147 | 148 | 149 | 150 | 151 | 152 | 153 | // ------------ // 154 | // MOUSE INPUT 155 | //------------- // 156 | 157 | // Try this: 158 | 159 | ( 160 | {SinOsc.ar( 161 | freq: MouseX.kr(440, 880), 162 | phase: 0, 163 | mul: MouseY.kr(0.1, 0.5))}.play 164 | ) 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | // ----------------------- // 173 | // MULTICHANNEL EXPANSION 174 | // ----------------------- // 175 | 176 | {SinOsc.ar([500, 1111], 0, 0.1)}.play // stereo 177 | 178 | // Examples from SC Book, p. 14: 179 | 180 | {Blip.ar(25, LFNoise0.kr(5, 12, 14), 0.3)}.play // mono 181 | 182 | {Blip.ar(25, LFNoise0.kr([5, 10], 12, 14), 0.3)}.play // stereo 183 | 184 | {Blip.ar(25, LFNoise0.kr([5, 10, 12, 25], 12, 14), 0.3)}.play // quad 185 | 186 | // Check your level meters (s.meter); 187 | 188 | 189 | 190 | 191 | // --------- // 192 | // MIC INPUT 193 | // --------- // 194 | 195 | // Use headphones to avoid feedback 196 | {SoundIn.ar(0)}.play; 197 | 198 | // Stereo version 199 | {SoundIn.ar([0, 1])}.play; 200 | 201 | // Some reverb just for fun? 202 | {FreeVerb.ar(SoundIn.ar([0, 1]), mix: 0.5, room: 0.9)}.play; 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | // ---------------------- // 211 | // PLAYING AN AUDIO FILE 212 | // ---------------------- // 213 | 214 | // First, read the file into a buffer: 215 | 216 | ~buf1 = Buffer.read(s, "/home/ruviaro/Music/SuperCollider/wheels-mono.wav"); // one sound file 217 | ~buf2 = Buffer.read(s, "/home/ruviaro/Music/Sounds/mussorgsky-mono.wav"); // another sound file 218 | 219 | // Play back: 220 | {PlayBuf.ar(1, ~buf1)}.play; // number of channels and buffer 221 | {PlayBuf.ar(1, ~buf2)}.play; 222 | 223 | [~buf1.bufnum, ~buf1.numChannels, ~buf1.path, ~buf1.numFrames]; 224 | [~buf2.bufnum, ~buf2.numChannels, ~buf2.path, ~buf2.numFrames]; 225 | 226 | // Varying playback speed 227 | // (numChannels, bufnum: 0, rate: 1, trigger: 1, startPos: 0, loop: 0, doneAction: 0) 228 | 229 | {PlayBuf.ar(numChannels: 1, 230 | bufnum: ~buf1, 231 | rate: 2, 232 | loop: 0)}.play; // play 2x faster 233 | 234 | {PlayBuf.ar(1, ~buf1, 0.5, loop: 1)}.play; // play at half the speed 235 | {PlayBuf.ar(1, ~buf1, Line.kr(0.5, 2, 10), loop: 1)}.play; // speeding up 236 | {PlayBuf.ar(1, ~buf1, MouseY.kr(0.5, 3), loop: 1)}.play; // mouse control 237 | 238 | // Changing direction (reverse) 239 | {PlayBuf.ar(1, ~buf2, -1, loop: 1)}.play; // reverse sound 240 | {PlayBuf.ar(1, ~buf2, -0.5, loop: 1)}.play; // play at half the speed AND reversed 241 | 242 | 243 | 244 | 245 | 246 | // ------------------ // 247 | // SYNTH DEFINITIONS 248 | // -------------------// 249 | 250 | {SinOsc.ar(440, 0, 0.3)}.play; // notice what appears in the post window. 251 | 252 | // "A SynthDef (Synth Definition) describes which UGens are used and how they are plugged together. The Server can then use this definition to make running synths based on that synthesis recipe. When you use {}.play, SuperCollider does the work for you under the hood, such as autonaming the associated SynthDef and using it straight away to create a running synth, with the assigned temporary name." [SC Book, p. 22] 253 | 254 | // Here's how to create a SynthDef without playing it immediately. The "add" message adds the SynthDef to the "list of SynthDefs SuperCollider knows about," allowing you to later run synths based on that recipe. 255 | 256 | s.meter; 257 | 258 | ( 259 | SynthDef("one_tone_only", { 260 | var out, freq = 440; 261 | out = SinOsc.ar(freq); 262 | Out.ar(0, out) // identify the output bus (more on that later) 263 | }).add; 264 | ) 265 | 266 | // Now we can use it at anytime, calling it by its name: 267 | 268 | Synth("one_tone_only"); 269 | 270 | // This simple recipe can only make one note. Let's make something more flexible: 271 | 272 | ( 273 | SynthDef("different_tones", { 274 | arg freq = 440; 275 | Out.ar(0, SinOsc.ar(freq)*0.1) 276 | }).add; 277 | ) 278 | 279 | // Now this synth recipe accepts arguments: 280 | 281 | Synth("different_tones", ["freq", 550]); // how to pass an argument 282 | Synth("different_tones", [\freq, 660]); // another way of writing 283 | Synth("different_tones", ["freq", 770]); 284 | Synth("different_tones"); // no argument? Default is used. 285 | 286 | // Combine synths with variables for more independent control: 287 | 288 | a = Synth("different_tones", [\freq, 64.midicps]); 289 | b = Synth("different_tones", [\freq, 67.midicps]); 290 | c = Synth("different_tones", [\freq, 71.midicps]); 291 | 292 | a.set(\freq, 63.midicps); 293 | c.set(\freq, 72.midicps); 294 | 295 | ( 296 | a.set(\freq, 64.midicps); 297 | b.set(\freq, 68.midicps); 298 | c.set(\freq, 71.midicps); 299 | ) 300 | 301 | b.set(\freq, 69.midicps); c.set(\freq, 73.midicps); 302 | 303 | a.free; // "freeing" the synth 304 | b.free; 305 | c.free; 306 | 307 | 308 | 309 | 310 | // -------------------------------------- // 311 | // A SIMPLE ENVELOPE; DONEACTION; PANNING 312 | // -------------------------------------- // 313 | 314 | ( 315 | SynthDef("percussive_tones", { 316 | arg freq = 440, amp = 0.2, pan = 0; 317 | var snd; 318 | snd = SinOsc.ar(freq) * EnvGen.kr(Env.perc, doneAction: 2) * amp; 319 | snd = Pan2.ar(snd, pan); 320 | Out.ar(0, snd) 321 | }).add; 322 | ) 323 | 324 | // Watch synth nodes in the NodeTree window as you play notes: 325 | s.plotTree; 326 | 327 | // Run this several times: 328 | Synth("percussive_tones", [\freq, rrand(400, 1200), \amp, rrand(0.1, 0.9), \pan, rrand(-1, 1.0)]); 329 | Synth("percussive_tones", [\freq, 1000, \amp, 1, \pan, 1]); 330 | 331 | a = Synth("different_tones", [\freq, 64.midicps]); 332 | b = Synth("different_tones", [\freq, 67.midicps]); 333 | c = Synth("different_tones", [\freq, 71.midicps]); 334 | 335 | a.set(\freq, 63.midicps); 336 | 337 | a.free; // watch plotTree window 338 | b.free; 339 | c.free; 340 | 341 | // See how Env.perc & friends look like: 342 | Env.perc.plot; 343 | Env.linen.plot; 344 | Env.triangle.plot; 345 | 346 | /* 347 | Understanding the SynthDef "percussive_tones": 348 | Env creates a breakpoint envelope, and EnvGen plays it back at audio or control rate. 349 | In the example above, we rely on all default values, but you can of course change them. 350 | The "doneAction: 2" part means: "when this note is done, free the enclosing synth". 351 | This way you don't have to free the synth manually with .free 352 | See "UGen done-actions" Help file for more info. 353 | Pan2 is a stereo panner. It takes a mono signal and pans it between -1 (left) to +1 (right). 354 | */ 355 | 356 | 357 | 358 | 359 | 360 | 361 | // ------------- // 362 | // BUSES, NODES 363 | // ------------- // 364 | 365 | // Buses are used for routing audio or control signals. 366 | 367 | {Out.ar(0, SinOsc.ar(440, 0, 0.1))}.play; // left channel 368 | 369 | {Out.ar(1, SinOsc.ar(1240, 0, 0.1))}.play; // right channel 370 | 371 | ( 372 | SynthDef("test_out", { arg outBus = 0; 373 | Out.ar(outBus, SinOsc.ar(MouseX.kr(440, 880), 0, 0.2)) 374 | }).add; 375 | ) 376 | 377 | 378 | 379 | a = Synth("test_out"); // plays default 380 | a.set(\outBus, 1); 381 | a.set(\outBus, 2); // see level meter 382 | a.set(\outBus, 6); // see level meter 383 | 384 | b = Synth("test_out", [\outBus, 1]); 385 | a.free; b.free; 386 | 387 | // AUDIO BUS EXAMPLE 388 | // An example of audio bus used for an effect is shown below. 389 | 390 | // busy tone (just listen for a couple seconds, then stop): 391 | a = {Out.ar(0, SinOsc.ar([800, 880])*LFPulse.ar(2)*0.1)}.play 392 | a.free; 393 | 394 | s.plotTree 395 | // Now run this ('turn reverb on' -- you won't hear anything at first) 396 | r = {Out.ar(0, FreeVerb.ar(In.ar(55, 2), mix: 0.5, room: 0.9))}.play; 397 | 398 | // Now run this second ('feed the busy tone into the reverb bus') 399 | a = {Out.ar(55, SinOsc.ar([800, 880])*LFPulse.ar(2)*0.1)}.play; 400 | a.free; 401 | 402 | // Example using a sound file 403 | ~buf1 = Buffer.read(s, "/home/ruviaro/Music/SuperCollider/wheels-mono.wav"); 404 | ~buf2 = Buffer.read(s, "/home/ruviaro/Music/Sounds/mussorgsky-mono.wav"); 405 | 406 | // Direct out: 407 | a = {Out.ar(0, PlayBuf.ar(1, ~buf1))}.play; 408 | a.free; 409 | 410 | b = {Out.ar(0, PlayBuf.ar(1, ~buf2))}.play; 411 | b.free; 412 | 413 | // Reverb 414 | a = {Out.ar(55, PlayBuf.ar(1, ~buf1) * 0.1)}.play; 415 | a.free 416 | 417 | b = {Out.ar(55, PlayBuf.ar(1, ~buf2))}.play; 418 | b.free; 419 | 420 | // Sine tones and audio file playback all go through same audio bus (~reverbIn). 421 | // This audio bus is given as input to a reverb UGen. 422 | // The final result is sent out to the speakers. 423 | 424 | // The BUS Object 425 | // In the examples above an arbitrary bus number was used for demonstration. 426 | // In reality you don't have to be keeping track of bus numbers in this way. 427 | // You can just use the Bus object to take care of that for you. 428 | 429 | ~reverbIn = Bus.audio(s, 1); // Bus audio choose an available bus; we store it into a variable. 430 | 431 | // Same examples as before 432 | // "Turn reverb on" 433 | {Out.ar(0, FreeVerb.ar(In.ar(~reverbIn, 2), mix: 0.5, room: 0.9))}.play; 434 | // "Send stuff to that bus" 435 | {Out.ar(~reverbIn, SinOsc.ar([800, 880])*LFPulse.ar(2)*0.1)}.play 436 | 437 | 438 | 439 | 440 | // CONTROL BUS EXAMPLE 441 | // Below, an example of control buses (from SC Book, p. 27). 442 | // Make sure you still have the audio files loaded on buffers 443 | // (from PlayBuf examples), otherwise load them again. 444 | 445 | 446 | ( 447 | ~kbus1 = Bus.control; // a control bus 448 | ~kbus2 = Bus.control; // a control bus 449 | ) 450 | 451 | ~kbus1 452 | 453 | ( 454 | { 455 | var speed, direction; 456 | speed = In.kr(~kbus1); 457 | direction = In.kr(~kbus2); 458 | Out.ar(0, PlayBuf.ar(0, ~buf1, (speed * direction), loop: 1)); 459 | }.play; 460 | ) 461 | 462 | // Start the controls 463 | ( 464 | {Out.kr(~kbus1, LFNoise1.kr(1).range(0.5, 2))}.play; 465 | {Out.kr(~kbus2, LFClipNoise.kr(1/4))}.play; 466 | ) 467 | 468 | // Start the second buffer with the same control input buses, 469 | // but send it to the right channel using Out.ar(1, etc.) 470 | ( 471 | { 472 | var speed, direction; 473 | speed = In.kr(~kbus1, 1); 474 | direction = In.kr(~kbus2); 475 | Out.ar(1, PlayBuf.ar(0, ~buf1, (speed * direction), loop: 1)); 476 | }.play; 477 | ) 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | // NODES 493 | // Run the audio bus example again. 494 | // Inspect what is happening in the server using the code below. 495 | 496 | s.queryAllNodes; // same as 'ctrl + T" 497 | s.plotTree; 498 | 499 | /* The order of execution of nodes are important. From the Help files: 500 | "Order of execution is a crucial issue when creating Synths which interact with each other. 501 | If a sound is to be passed through a filter, the synth that does the filtering must be later in the order of execution than the synth which is its input. The computer must calculate a buffer's worth of sound, and then the computer moves on to calculate a buffer's worth of the filtered version of that sound." 502 | See Help file "Order of Execution" for more info. 503 | */ 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | -------------------------------------------------------------------------------- /hanon_2014-01-01.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: Hanon 2 | SUMMARY:: The Virtuoso Computer Musician 3 | CATEGORIES:: Other 4 | RELATED:: Tutorials/Getting-Started/00-Getting-Started-With-SC, Tutorials/A-Practical-Guide/PG_01_Introduction 5 | 6 | SECTION::Description 7 | A collection of short exercises for your daily practice of SuperCollider. 8 | 9 | by Bruno Ruviaro 10 | 11 | December, 2013 12 | 13 | 14 | 15 | SECTION::1 16 | Here's a simple sine wave that plays on the left channel only: 17 | CODE:: 18 | {SinOsc.ar(freq: 440, mul: 0.1)}.play; 19 | :: 20 | Change the line above so that you hear an additional sine wave at 444 Hz on the right channel. 21 | 22 | See footnote for one solution. 23 | footnote:: 24 | CODE:: 25 | {SinOsc.ar(freq: [440, 444], mul: 0.1)}.play; 26 | :: 27 | See link::Guides/Multichannel-Expansion:: for more info. 28 | :: 29 | 30 | 31 | 32 | SECTION::2 33 | The Pbind below plays a sequence of notes only once. Make it play forever. 34 | CODE:: 35 | Pbind( 36 | \note, Pseq([4, 6, 11, 13, 14, 6, 4, 13, 11, 6, 14, 13], 1), 37 | \dur, 0.15 38 | ).play; 39 | :: 40 | See footnote for one solution. 41 | footnote:: 42 | CODE:: 43 | // Replace 1 by inf: 44 | Pbind( 45 | \note, Pseq([4, 6, 11, 13, 14, 6, 4, 13, 11, 6, 14, 13], inf), 46 | \dur, 0.15 47 | ).play; 48 | :: 49 | :: 50 | 51 | 52 | 53 | SECTION::3 54 | The line below plays a simple note using SuperCollider's built-in default instrument: 55 | CODE:: 56 | (freq: 440).play; 57 | :: 58 | Modify the code above so that every time you evaluate this line you get a different frequency, randomly chosen between a low and a high boundary. Use A4 (440 Hz) as the lower boundary, and A5 (one octave above 440 Hz) as the upper boundary. 59 | 60 | See footnote for one solution. 61 | footnote:: 62 | The method rrand does the job: 63 | CODE:: 64 | (freq: rrand(440, 880)).play; 65 | :: 66 | Watch the Post window to see the different 'freq' values chosen every time. 67 | :: 68 | 69 | 70 | SECTION::4 71 | Write a line of code that plays one note using SuperCollider's default instrument. Every time you evaluate the line, the code should randomly choose a note from a C minor triad (midinotes 60, 63, 67). 72 | 73 | See footnote for one solution. 74 | footnote:: 75 | A few solutions are presented below: 76 | CODE:: 77 | (freq: [60, 63, 67].choose.midicps).play; 78 | // or 79 | (midinote: [60, 63, 67].choose).play; 80 | // or 81 | (note: [0, 3, 7].choose).play; 82 | :: 83 | :: 84 | 85 | SECTION::5 86 | Select the entire block of code below, evaluate it, and take a look at what shows up in the Post window. If you like, use Control + Shift + P to clean up the Post window first. 87 | CODE:: 88 | ( 89 | "This is the first line".postln; 90 | 8 + 8; 91 | "This is the last line".postln; 92 | ) 93 | :: 94 | Without changing the order of the lines, change the code above so that: 95 | LIST:: 96 | ## the result of the second line (16) gets posted too; 97 | ## the last line is posted only once. 98 | :: 99 | 100 | See footnote for one solution. 101 | footnote:: 102 | When you execute code in SC, it always posts whatever was the last thing executed, and that's why the last line was posted twice. Because the last line is last thing to be executed in that block of code, it will get posted anyway; so we can remove the postln. 103 | As for the middle line (8 + 8), it did get evaluated, we just didn't see the result. Simply add a postln to it to print its result in the Post window. Note that parentheses are needed: we want the addition 8 + 8 to happen first, and the postln (of the result) to happen second. 104 | CODE:: 105 | ( 106 | "Hello World".postln; 107 | (8 + 8).postln; 108 | "This is the last line"; 109 | ) 110 | :: 111 | :: 112 | 113 | 114 | 115 | SECTION::6 116 | The code below plays the notes of a Cm7 chord always in the same order. Make it play the same notes in any random order. Also, change the legato value to make all notes EMPHASIS::staccato:: (detached from each other). 117 | CODE:: 118 | Pbind( 119 | \midinote, Pseq([60, 63, 67, 70], inf), 120 | \dur, 0.2, 121 | \legato, 1 122 | ).play; 123 | :: 124 | See footnote for one solution. 125 | footnote:: 126 | Just use Prand instead of Pseq. 127 | CODE:: 128 | Pbind( 129 | \midinote, Prand([60, 63, 67, 70], inf), 130 | \dur, 0.2, 131 | \legato, 0.1 132 | ).play; 133 | :: 134 | :: 135 | 136 | SECTION::7 137 | Write a simple function to play a note of any given frequency. 138 | Assign the function to the variable 'f', so that lines such as 139 | CODE:: 140 | f.value(440); 141 | f.value(512); 142 | f.value(950); 143 | :: 144 | will play a short note with the provided frequency in Herz. 145 | 146 | See footnote for one solution. 147 | footnote:: 148 | CODE:: 149 | f = { arg myfreq; (freq: myfreq).play }; 150 | :: 151 | :: 152 | 153 | 154 | SECTION::8 155 | Here's a short ascending scale going up by quarter tones from midinote 60 to midinote 63. 156 | CODE:: 157 | ( 158 | var scale = [60, 60.5, 61, 61.5, 62, 62.5, 63]; 159 | 160 | Pbind( 161 | \midinote, Pseq(scale, 1), 162 | \dur, 1/3 163 | ).play; 164 | ) 165 | :: 166 | Make it play the same scale, but EMPHASIS::descending:: instead of ascending. 167 | 168 | See footnote for one solution. 169 | footnote:: 170 | Simply apply the method .reverse to the list of MIDI notes. 171 | CODE:: 172 | ( 173 | var scale = [60, 60.5, 61, 61.5, 62, 62.5, 63].reverse; 174 | 175 | Pbind( 176 | \midinote, Pseq(scale, 1), 177 | \dur, 1/3 178 | ).play; 179 | ) 180 | :: 181 | :: 182 | 183 | 184 | SECTION::9 185 | Write one line of code that plays a continuous sine wave. Use your mouse or trackpad to control frequency and amplitude of the sound (horizontal motion controlling frequency, vertical motion controlling amplitude). 186 | 187 | See footnote for one solution. 188 | footnote:: 189 | CODE:: 190 | { SinOsc.ar(freq: MouseX.kr(100, 1000), mul: MouseY.kr(0.1, 0.5)) }.play; 191 | :: 192 | :: 193 | 194 | SECTION::10 195 | Suppose you are trying to create a collection of 11 notes picked randomly between C4 (midinote 60) and C5 (midinote 72). The code below is almost there, but still doesn't do the job. What is wrong? How to fix it? 196 | CODE:: 197 | Array.fill(11, rrand(60, 72)); 198 | :: 199 | 200 | See footnote for one solution. 201 | footnote:: Enclose rrand in curly braces, making it a function. This way it is evaluated again 11 times, instead of just once at the beginning. 202 | CODE:: 203 | Array.fill(11, { rrand(60, 72) } ); 204 | :: 205 | :: 206 | 207 | SECTION::11 208 | Just like in the previous exercise, you are trying to create a sequence of 11 notes picked randomly between C4 (midinote 60) and C5 (midinote 72). This time, however, you don't want to use the Array.fill construction. You are writing a Pbind, and there is a straightforward way to accomplish this task with one of the members of the Pattern family. Write it in below. 209 | CODE:: 210 | Pbind( 211 | \midinote, /*what do you write here?*/, 212 | \dur, 0.2 213 | ).play; 214 | :: 215 | 216 | See footnote for the solution. 217 | footnote:: Use Pwhite: 218 | CODE:: 219 | Pbind( 220 | \midinote, Pwhite(60, 72, 11), 221 | \dur, 0.2 222 | ).play;:: 223 | :: 224 | :: 225 | 226 | SECTION::12 227 | Create a continuous stream of pink noise. Use Pan2 to slowly move the sound between the left and right channels. Use a sine oscillator as your control for panning the noise. 228 | 229 | See footnote for one solution. 230 | footnote:: 231 | CODE:: 232 | { Pan2.ar(PinkNoise.ar(0.2), SinOsc.kr(0.5)) }.play; 233 | :: 234 | :: 235 | 236 | SECTION::13 237 | Use Mix.fill to create a cluster of sine tones. Use the code below as a starting point. The variable 'n' specifies the number of sine tones to be mixed together. The frequency of each tone should be randomly chosen between 100 and 1000 Hz. Make sure to scale amplitudes accordingly so that the sum is never greater than 1. 238 | CODE:: 239 | ( 240 | var n = 8; 241 | { Mix.fill( ) }.play; 242 | ) 243 | :: 244 | See footnote for one solution. 245 | footnote:: 246 | See LINK::Tutorials/Getting-Started/07-Mix-it-Up##this page:: of the EMPHASIS::Getting Started:: tutorial. 247 | CODE:: 248 | ( 249 | var n = 8; 250 | { Mix.fill(n, { SinOsc.ar(freq: rrand(100, 1000), mul: 1/n) }) }.play; 251 | ) 252 | :: 253 | :: 254 | 255 | 256 | 257 | SECTION::14 258 | Write a simple SynthDef so that the block of code below will produce a chord of sawtooth waves. The SynthDef should output the same signal on both channels (left and right). 259 | CODE:: 260 | ( 261 | Synth("mySynth", [\freq, 41.midicps, \amp, 0.05]); 262 | Synth("mySynth", [\freq, 48.midicps, \amp, 0.07]); 263 | Synth("mySynth", [\freq, 60.1.midicps, \amp, 0.03]); 264 | Synth("mySynth", [\freq, 62.midicps, \amp, 0.04]); 265 | Synth("mySynth", [\freq, 69.midicps, \amp, 0.03]); 266 | ) 267 | :: 268 | In addition, the line below should also work, producing a single default tone: 269 | CODE:: 270 | Synth("mySynth"); 271 | :: 272 | 273 | See footnote for one solution. 274 | footnote:: 275 | CODE:: 276 | SynthDef("mySynth", {arg freq = 440, amp = 0.05; 277 | Out.ar(0, Saw.ar([freq, freq], amp)); 278 | }).add; 279 | :: 280 | :: 281 | 282 | 283 | 284 | SECTION::15 285 | Using the Pbind below as a starting point, make the following changes: 286 | LIST:: 287 | ##add a frequency offset to each note. The offset should be randomly chosen between 10 and 20 Hz each time; 288 | ##durations should be: [0.2, 0.4] played twice, then [0.3, 0.3, 0.6] played once; and repeat this whole sequence 3 times, then stop. 289 | :: 290 | CODE:: 291 | Pbind( 292 | \freq, Pseq( [1/1, 3/2, 4/3, 9/8, 16/9, 5/4, 8/5], 1) * 440, 293 | \dur, Prand([0.2, 0.4, 0.3], inf) 294 | ).play; 295 | :: 296 | 297 | The Pbind above has been borrowed from LINK::http://sc3howto.blogspot.com/2010/06/pbinds.html 298 | ##this online tutorial::. 299 | 300 | See footnote for one solution. 301 | footnote:: 302 | CODE:: 303 | Pbind( 304 | \freq, Pseq( [1/1, 3/2, 4/3, 9/8, 16/9, 5/4, 8/5], inf) * 440 + Pwhite(10, 20), 305 | \dur, Pseq( [Pseq([0.2, 0.4], 2), Pseq([0.3, 0.3, 0.6], 1) ], 3) 306 | ).play; 307 | 308 | /* 309 | Note that it stops after 3 repeats thanks to the number 3 inside the main Pseq in \dur. The solution above uses 'inf' for the Pseq in \freq; one could write '3' there as well, and the result would be the same; but I tend to prefer having only one clear terminating condition inside a Pbind. 310 | */ 311 | :: 312 | :: 313 | 314 | 315 | SECTION::16 316 | Start with the same Pbind from the previous exercise: 317 | CODE:: 318 | Pbind( 319 | \freq, Pseq( [1/1, 3/2, 4/3, 9/8, 16/9, 5/4, 8/5], 1) * 440, 320 | \dur, Prand([0.2, 0.4, 0.3], inf) 321 | ).play; 322 | :: 323 | Make it play a bass line with the following durations: 324 | 325 | [1/2, 1/2, 1/2, 1/2, 3/4, 3/4, 1/2] 326 | 327 | Then make it play twice as fast. 328 | 329 | Finally, using the \legato key, make all notes staccato. 330 | 331 | NOTE::If you are using laptop speakers, you may not hear your bass notes. Use good headphones or loudspeakers instead.:: 332 | 333 | See footnote for one solution. 334 | footnote:: 335 | CODE:: 336 | Pbind( 337 | \freq, Pseq([1, 3/2, 4/3, 9/8, 16/9, 5/4, 8/5] * 40 , inf), 338 | \dur, Pseq([1/2, 1/2, 1/2, 1/2, 3/4, 3/4, 1/2] * 0.5, 10), 339 | \amp, 0.5, 340 | \legato, 0.1 341 | ).play; 342 | :: 343 | :: 344 | 345 | 346 | SECTION::17 347 | Play an ascending scale that: 348 | 349 | LIST:: 350 | ##starts one octave below middle C 351 | ##has a total of 24 notes 352 | ##goes up mostly by stepwise motion 353 | ##all notes have a duration of 0.1 354 | :: 355 | Use Pbind with the default instrument. 356 | 357 | See footnote for one solution. 358 | footnote:: 359 | One octave below middle C is midinote 48. 360 | Pseries creates the arithmetic series of numbers. 361 | Pwrand controls the step. "Mostly stepwise" can have several interpretations; below it is interpreted as whole tone or semitone 80% of the time, and a minor third 20% of the time. 362 | CODE:: 363 | Pbind( 364 | \midinote, Pseries( 365 | start: 48, 366 | step: Pwrand([1, 2, 3], [0.4, 0.4, 0.2], inf), 367 | length: 24), 368 | \dur, 0.1 369 | ).play; 370 | :: 371 | :: 372 | 373 | 374 | SECTION::18 375 | Write a Pbind to achieve the following musical goals: 376 | LIST:: 377 | ##play a three-octave descending scale (use Pseries); 378 | ##scale type: harmonic minor; 379 | ##scale has a decreasing series of durations (EMPHASIS::accelerando::): the first note to be played should have a duration of 1 second, and each successive note should have a shorter duration (use Pgeom); 380 | ##create the following accent pattern for the entire scale: STRONG::mf::, p, p, STRONG::mf::, p, p, STRONG::mf::, p, p, etc (use Pser); 381 | ##all notes staccato. 382 | :: 383 | 384 | See footnote for one solution. 385 | footnote:: 386 | Using \degree, we need 22 notes to complete 3 octaves (7 + 7 + 7 + 1). The Pbind stops when Pser is done with its 22 values. 387 | CODE:: 388 | Pbind( 389 | \scale, Scale.harmonicMinor, 390 | \degree, Pseries(7, -1), 391 | \dur, Pgeom(1, 0.9), 392 | \amp, Pser([0.3, 0.1, 0.1], 22), 393 | \legato, 0.3 394 | ).play; 395 | :: 396 | :: 397 | 398 | SECTION::19 399 | Suppose you need a SynthDef that picks a random frequency and random amplitude for every new Synth that is created. You might be tempted to write this: 400 | CODE:: 401 | SynthDef("random", { 402 | Out.ar(0, SinOsc.ar( 403 | freq: rrand(440, 880), 404 | mul: rrand(0.1, 0.5))); 405 | }).add; 406 | :: 407 | The problem is that this doesn't work as desired, because it always plays the same note every time a new Synth is called: 408 | CODE:: 409 | x = Synth("random"); 410 | y = Synth("random"); 411 | z = Synth("random"); 412 | x.free; y.free; z.free; 413 | :: 414 | 415 | What is the problem? How to fix it? (Remember, the goal is to have a new random frequency and amplitude selected every time a new Synth is created). 416 | 417 | See footnote for one solution. 418 | footnote:: 419 | The problem has to do with the differences between Server and Language. In the first SynthDef, rrand is evaluated only once when the SynthDef is created. In order to have a new random number being generated for every Synth, we have to use the UGen Rand: 420 | CODE:: 421 | SynthDef("random2", { 422 | Out.ar(0, SinOsc.ar( 423 | freq: Rand(440, 880), 424 | mul: Rand(0.1, 0.5))); 425 | }).add; 426 | x = Synth("random2"); 427 | y = Synth("random2"); 428 | z = Synth("random2"); 429 | x.free; y.free; z.free; 430 | :: 431 | Example adapted from Getting Started Tutorial - LINK::Tutorials/Getting-Started/10-SynthDefs-and-Synths::. Check it out for more information. 432 | :: 433 | 434 | 435 | SECTION::20 436 | The SynthDef below defines an instrument that plays a continuous sound (a mix of three slightly detuned sawtooth waves). 437 | CODE:: 438 | SynthDef("ex20", {arg freq = 440, amp = 0.1; 439 | var snd; 440 | snd = Saw.ar(freq*[1, 0.99, 1.015], amp); 441 | snd = Splay.ar(snd); 442 | Out.ar(0, snd); 443 | }).add; 444 | 445 | x = Synth("ex20"); 446 | x.free; 447 | y = Synth("ex20", [\freq, 200, \amp, 0.05]); 448 | y.free; 449 | :: 450 | Add a percussive envelope to the SynthDef, so that you can play it like this (arguments \att and \rel stand for attack and release times): 451 | CODE:: 452 | x = Synth("ex20", [\freq, 200, \rel, 4]); 453 | y = Synth("ex20", [\att, 0.3, \rel, 3.1]); 454 | z = Synth("ex20"); // this one should fade out in 2s. 455 | :: 456 | Make sure each synth frees itself when the percussive envelope is finished. 457 | 458 | See footnote for one solution. 459 | footnote:: 460 | Adding a percussive envelope with doneAction: 2 to a SynthDef: 461 | CODE:: 462 | SynthDef("ex20b", {arg freq = 440, amp = 0.1, att = 0.01, rel = 2; 463 | var snd, env; 464 | env = EnvGen.kr(Env.perc(att, rel, amp), doneAction: 2); 465 | snd = Saw.ar(freq*[1, 0.99, 1.015], env); 466 | snd = Splay.ar(snd); 467 | Out.ar(0, snd); 468 | }).add; 469 | 470 | x = Synth("ex20b", [\freq, 200, \rel, 4]); 471 | y = Synth("ex20b", [\att, 0.2, \rel, 9.1]); 472 | z = Synth("ex20b"); // default release of 2s. 473 | :: 474 | :: 475 | 476 | 477 | SECTION::21 478 | Take the following sequence of MIDI notes: 479 | CODE:: 480 | ~notes = [60, 64, 67, 60, 62, 65, 59, 61, 65, 59, 60, 64]; 481 | :: 482 | First, write a Pbind to play these notes in sequence once. Then, always using the same notes, change the code to play a sequence of three-note chords (every three sequential notes forming one chord). 483 | 484 | See footnote for one solution. 485 | footnote:: 486 | Playing chords from a Pbind. 487 | CODE:: 488 | // First play notes one after the other: 489 | Pbind( 490 | \midinote, Pseq(~notes), 491 | \dur, 1/2 492 | ).play; 493 | 494 | // Now, play as chords (grouping every 3 adjacent notes together): 495 | Pbind( 496 | \midinote, Pseq(~notes.clump(3)), 497 | \dur, 1/2 498 | ).play; 499 | 500 | // The above is the same as: 501 | Pbind( 502 | \midinote, Pseq([[60, 64, 67], [60, 62, 65], [59, 61, 65], [59, 60, 64]]), 503 | \dur, 1/2 504 | ).play; 505 | :: 506 | :: 507 | 508 | 509 | 510 | SECTION::22 511 | Here's a simple SynthDef that plays a steady tone: 512 | CODE:: 513 | // Define the synth 514 | SynthDef("steady", {arg midinote = 60, amp = 0.1; 515 | var freq = midinote.midicps; 516 | var snd = SinOsc.ar(freq: freq, mul: amp); 517 | Out.ar(0, [snd, snd]); 518 | }).add; 519 | :: 520 | Try it: 521 | CODE:: 522 | x = Synth("steady"); 523 | x.free; 524 | :: 525 | Write a new SynthDef (let's call it "vib") using a sine oscillator at control rate to add vibrato to the sound. You should be able to specify vibrato speed (in cycles per second) and vibrato amount (in semitones) like this: 526 | CODE:: 527 | x = Synth("vib", [\midinote, 62, \vspeed, 4, \vamount, 1]); 528 | y = Synth("vib", [\vspeed, 8.3, \vamount, 0.5]); 529 | x.free; y.free; 530 | :: 531 | STRONG::How to interpret the vibrato amount parameter for this exercise::: for example, a D (midinote 62) with a vibrato amount of 1 means the note should deviate one semitone up (from D to D#) and one semitone down (from D to Db). A vibrato amount of 12 should make the note deviate one full octave up and one full octave down, etc. 532 | 533 | See footnote for one solution. 534 | footnote:: 535 | Creating vibrato: 536 | CODE:: 537 | SynthDef("vib", {arg midinote = 60, amp = 0.1, vspeed = 10, vamount = 1; 538 | var deviation, freq, snd; 539 | deviation = SinOsc.kr(freq: vspeed, mul: vamount); 540 | freq = (midinote + deviation).midicps; 541 | snd = SinOsc.ar(freq: freq, mul: amp); 542 | Out.ar(0, [snd, snd]); 543 | }).add; 544 | 545 | x = Synth("vib", [\midinote, 76, \vspeed, 7]); 546 | x.free; 547 | y = Synth("vib", [\vspeed, 4.3, \vamount, 3.1]); 548 | y.free; 549 | :: 550 | By calculating the deviation in semitones and adding it directly to a midinote value EMPHASIS::before:: converting it to frequency, we make sure the deviation will be the same in both directions (up and down). Compare with the following: 551 | 552 | CODE:: 553 | SynthDef("vib-unequal", {arg midinote = 60, amp = 0.1, vspeed = 10, vamountHz = 20; 554 | var deviation, freq, snd; 555 | deviation = SinOsc.kr(freq: vspeed, mul: vamountHz); 556 | // add deviation to freq in Herz: 557 | freq = midinote.midicps + deviation; 558 | snd = SinOsc.ar(freq: freq, mul: amp); 559 | Out.ar(0, [snd, snd]); 560 | }).add; 561 | 562 | a = Synth("vib-unequal", [\vamountHz, 15, \vspeed, 7]); 563 | a.free; 564 | b = Synth("vib-unequal", [\midinote, 72, \vspeed, 5, \vamountHz, 100]); 565 | b.free; 566 | c = Synth("vib-unequal", [\midinote, 48, \vspeed, 5, \vamountHz, 100]); 567 | c.free; 568 | :: 569 | 570 | Implementing vibrato in this way (directly in Hz) has the following properties: 571 | LIST:: 572 | ##the deviation upwards will not be the same as downwards (for example, when oscillating 20 Hz up and down from a center frequency of 200 Hz, the pitch interval between 180 and 200 will be larger than the pitch interval between 200 and 220); 573 | ##one same deviation amount will result in wildly different amounts of vibrato depending on the register. Compare synths b and c just above. 574 | :: 575 | :: 576 | 577 | 578 | SECTION::23 579 | Here's a simple hihat SynthDef: 580 | CODE:: 581 | SynthDef("hihat", {arg amp = 0.5, att = 0.01, rel = 0.2, ffreq = 6000; 582 | var snd = WhiteNoise.ar(amp); 583 | var env = EnvGen.kr(Env.perc(att, rel, amp), doneAction: 2); 584 | snd = HPF.ar(snd * env, ffreq); 585 | Out.ar(0, [snd, snd]); 586 | }).add; 587 | :: 588 | Try it: 589 | CODE:: 590 | Synth("hihat"); 591 | Synth("hihat", [\rel, 0.05, \ffreq, 10000]); 592 | :: 593 | Write a Pbind to play this hihat instrument. Try to create a difference between a closed hihat and an open hihat. 594 | 595 | See footnote for one solution. 596 | footnote:: 597 | Playing a hihat SynthDef. 598 | CODE:: 599 | Pbind( 600 | \instrument, "hihat", 601 | \dur, 0.2, 602 | \ffreq, 9000, 603 | \rel, Pseq([0.05, 0.05, 0.05, 0.4], inf), 604 | \amp, 0.5 605 | ).play; 606 | :: 607 | :: 608 | 609 | 610 | SECTION::24 611 | The code below doesn't make any sound - it simply writes MouseX.kr values into control bus 20. The values are between 0 and 1 (the default, since no other values are provided). 612 | CODE:: 613 | { Out.kr(20, MouseX.kr) }.play; 614 | :: 615 | Write a single synth that does all of the following simultaneously: 616 | LIST:: 617 | ##plays a soft sine wave with frequency controlled by the horizontal motion of your mouse. The left side of the screen should be a low frequency of your choice, and the right side of the screen should be 1000 Hz; 618 | ##plays a second and softer sine wave that sounds significantly higher in pitch than the previous one; with its frequency also controlled by horizontal motion of the mouse; but with glissando direction reversed: the left side of the screen should be a very high frequency of your choice, and the right side of the screen should be 1000 Hz. 619 | ##plays an extremely soft stream of white noise, the amplitude of which is also controlled by the horizontal movement of the mouse. The white noise should fade out to silence as the cursor approaches either side of the screen (extreme left or right). 620 | :: 621 | 622 | See footnote for one solution. 623 | footnote:: 624 | Using a control bus, and scaling numbers accordingly. 625 | CODE:: 626 | { 627 | var a, b, c, mouse; 628 | mouse = In.kr(20); 629 | a = SinOsc.ar( 630 | freq: mouse.linlin(0, 1, 100, 1000), 631 | mul: 0.01); 632 | b = SinOsc.ar( 633 | freq: mouse.linlin(0, 1, 9999, 1000), 634 | mul: 0.005); 635 | c = WhiteNoise.ar(mouse.linlin(0, 1, -0.005, 0.005)); 636 | Out.ar(0, a + b + c); 637 | }.play; 638 | 639 | // If you don't hear the gliss when you move the cursor 640 | // it's probably because you forgot to run this: 641 | { Out.kr(20, MouseX.kr) }.play 642 | :: 643 | :: 644 | 645 | 646 | SECTION::25 647 | Take your solution to the previous exercise as starting point. Make the following changes: 648 | LIST:: 649 | ##make it play the same signal on both channels (left and right); 650 | ##multiply the final mix by a pulse oscillator so that we no longer hear a continuous sound but rather a sequence of short "notes" (pulses). A new random frequency of the pulses (between 1-20 pulses per second) should be chosen automatically every second. 651 | :: 652 | 653 | See footnote for one solution. 654 | footnote:: 655 | LFPulse used as amplitude envelope. 656 | LFNoise0 used to control the freq of LFPulse. 657 | LFPulse width of 0.1 makes "short notes". 658 | CODE:: 659 | { 660 | var a, b, c, mix, mouse; 661 | mouse = In.kr(20); 662 | a = SinOsc.ar(mouse.linlin(0, 1, 100, 1000), 0, 0.1); 663 | b = SinOsc.ar(mouse.linlin(0, 1, 9999, 1000), 0, 0.05); 664 | c = WhiteNoise.ar(mouse.linlin(0, 1, -0.05, 0.05)); 665 | mix = a + b + c * LFPulse.kr(LFNoise0.kr(1).range(1, 20), width: 0.1); 666 | Out.ar(0, [mix, mix]); 667 | }.play; 668 | 669 | // If you don't hear the gliss when you move the cursor 670 | // it's probably because you forgot to run this: 671 | { Out.kr(20, MouseX.kr) }.play 672 | :: 673 | :: 674 | 675 | 676 | SECTION::26 677 | Here you have two synth definitions. Both produce sequences of short staccato notes. 678 | CODE:: 679 | SynthDef("sinegrain", 680 | { arg out = 0, amp = 0.5; 681 | var env; 682 | env = EnvGen.kr(Env.perc(0.01, 0.1, amp), Dust.kr(1)); 683 | Out.ar(out, SinOsc.ar(LFNoise0.kr(1).range(1900, 2300), 0, env)) 684 | }).add; 685 | 686 | SynthDef("glisspulse", {arg out = 0, amp = 0.5; 687 | var mouse, a, b, c, env, snd; 688 | mouse = MouseX.kr; 689 | a = SinOsc.ar(mouse.linlin(0, 1, 500, 1000), 0, 0.2); 690 | b = SinOsc.ar(mouse.linlin(0, 1, 9999, 1000), 0, 0.1); 691 | c = WhiteNoise.ar(mouse.linlin(0, 1, -0.1, 0.1)); 692 | env = LFPulse.kr(LFNoise1.kr(1).range(1/2, 3), width: 0.05); 693 | snd = (a + b + c) * env * amp; 694 | Out.ar(out, snd); 695 | }).add; 696 | :: 697 | Try them out: 698 | CODE:: 699 | x = Synth("sinegrain") 700 | y = Synth("glisspulse"); 701 | x.free; y.free; 702 | :: 703 | Write a SynthDef that takes any incoming sound and adds reverb to it. Play the two instruments above ("sinegrain" and "glisspulse") through the reverb synth simultaneously. Do not change "sinegrain" and "glisspulse" in any way. 704 | 705 | See footnote for one solution. 706 | footnote:: 707 | CODE:: 708 | SynthDef("reverb", {arg inbus, mix = 0.4; 709 | var in, snd; 710 | in = In.ar(inbus); 711 | snd = FreeVerb.ar( 712 | in: in, 713 | mix: mix, 714 | room: 0.8, 715 | damp: 0.5); 716 | Out.ar(0, [snd, snd]); 717 | }).add; 718 | 719 | // Create a bus 720 | b = Bus.audio(s, 1); 721 | 722 | // Play synths into the new bus 723 | x = Synth("sinegrain", [\out, b]) 724 | y = Synth("glisspulse", [\out, b]); 725 | 726 | // Start the reverb effect: 727 | r = Synth("reverb", [\inbus, b], addAction: \addToTail); 728 | 729 | // Important: note that the reverb synth has to be placed 730 | // AFTER the two other synths. Take a look at this graph 731 | // (while all synths are running): 732 | 733 | s.plotTree; // read the graph from top to bottom 734 | 735 | x.free; y.free; r.free; 736 | :: 737 | For more info, see LINK::Guides/Order-of-execution::. 738 | :: 739 | 740 | 741 | 742 | 743 | SECTION::27 744 | Fill in the blanks of the synth below to create two glissando sine waves (one ascending, the other descending). The first sine wave should start at 100 Hz and arrive at 440 Hz over 5 seconds, playing on the left channel. The second sine wave should start at 900 Hz and arrive at 443 Hz over 6 seconds, playing on the right channel. The amplitude (same for both) should decrease from 0.4 to 0.0 in 9 seconds. 745 | 746 | CODE:: 747 | ( 748 | {SinOsc.ar( 749 | freq: Line.kr( 750 | start: 751 | end: 752 | dur: 753 | mul: 754 | )}.play; 755 | ) 756 | :: 757 | 758 | See footnote for one solution. 759 | footnote:: 760 | Using multichannel expansion. 761 | CODE:: 762 | ( 763 | {SinOsc.ar( 764 | freq: Line.kr( 765 | start: [100, 900], 766 | end: [440, 443], 767 | dur: [5, 6]), 768 | mul: Line.kr(0.4, 0, 10)) 769 | }.play; 770 | ) 771 | :: 772 | :: 773 | 774 | 775 | SECTION::28 776 | A basic kick drum sound can be created with a single sine wave burst in the low register, with a percussive amplitude envelope (very short attack, fairly short release time), and a slight glissando downwards. Create a SynthDef following these guidelines, and write a Pbind to play your new instrument. 777 | 778 | See footnote for one solution. 779 | footnote:: 780 | CODE:: 781 | // Synth definition: 782 | SynthDef("kick", {arg amp = 0.3, sinfreq = 60, glissfactor = 0.9, att = 0.001, rel = 0.3; 783 | var gliss = XLine.kr(sinfreq, sinfreq * glissfactor, rel); 784 | var snd = SinOsc.ar(gliss); 785 | var env = EnvGen.kr(Env.perc(att, rel, amp), doneAction: 2); 786 | snd = snd * env; 787 | Out.ar(0, snd!2); 788 | }).add; 789 | 790 | // Playing it with Pbind: 791 | Pbind( 792 | \instrument, "kick", 793 | \dur, Pseq([1/2, 1/2, 1/3], inf), 794 | \amp, 0.5, 795 | \sinfreq, 60 796 | ).play; 797 | :: 798 | :: 799 | 800 | SECTION::29 801 | Create a short "audio tester" program that sends short bursts of pink noise to any specified channel. Suppose you would like to test an 8-channel set up, and you want this instrument to play continuously going around the eight channels (i.e., noise bursts will keep circling from channel 1 through channel 8). You should also be able to specify speed (for example, one noise burst every 1/2 second) and amplitude. 802 | 803 | First write a short SynthDef that plays one single noise burst with a percussive envelope. The synth should free itself after the envelope is finished. 804 | 805 | Then write a Pbind to play the instrument and the 8-channel test. 806 | 807 | Bonus: rewrite the playing mechanism as a Routine instead of a Pbind. 808 | 809 | See footnote for one solution. 810 | footnote:: 811 | CODE:: 812 | // Audiotester 813 | // First, create the SynthDef: 814 | SynthDef("audiotester", { arg out = 0, amp = 0.1; 815 | var snd = PinkNoise.ar(amp) * EnvGen.kr(Env.perc, doneAction: 2); 816 | Out.ar(out, snd); 817 | }).add; 818 | 819 | 820 | // Play it going around 8 channels (SC busses 0 to 7): 821 | Pbind( 822 | \instrument, "audiotester", 823 | \dur, 0.6, 824 | \out, Pseq((0..7), inf) 825 | ).play; 826 | 827 | 828 | // Watch it here: 829 | s.meter; 830 | 831 | // Notice the shortcut notation for creating an Array 832 | // with an arithmetic series from 0 to 7: 833 | 834 | (0..8); // evaluate, check Post window 835 | 836 | // Now using a Routine instead of a Pbind. 837 | // Define the Routine: 838 | r = Routine({ 839 | loop({ 8.do( 840 | {arg i; 841 | Synth("audiotester", [\out, i]); 842 | 0.5.wait; 843 | }) 844 | }) 845 | }); 846 | // Play it: 847 | r.play; 848 | r.stop; 849 | 850 | // While we are at it, here's a shortcut notation for the above: 851 | { 852 | loop({ 8.do( 853 | {arg i; 854 | Synth("audiotester", [\out, i]); 855 | 0.5.wait; 856 | }) 857 | }) 858 | }.fork; 859 | :: 860 | :: 861 | 862 | SECTION::30 863 | Write a SynthDef called "adsr" made of white noise going through a very sharp band pass filter. Each note should have an ADSR envelope with default values att = 0.1, dec = 1, sus = 0.3, rel = 2. Create a short phrase with this instrument to play 4 notes following this descriptive score: 864 | LIST:: 865 | ##first note: frequency = 1700 Hz, amplitude = 0.5, attack = 0.1 seconds; 866 | ##half a second later comes the second note: frequency = 900 Hz, amplitude = 0.9, attack = 1.2 seconds, release = 5 seconds; 867 | ##half a second later comes the third note: frequency = 2300 Hz, amplitude = 0.8, release = 5 seconds; 868 | ##0.8 second later comes the fourth note: frequency = 300 Hz, amplitude = 0.9, attack = 4.5 seconds, sustain = 0.2, release = 5 seconds; 869 | ##wait one second, release second note; 870 | ##wait 2 seconds, release fourth note; 871 | ##wait 1 more second, release second note; 872 | ##wait another half a second, release first note. 873 | :: 874 | 875 | 876 | 877 | 878 | See footnote for one solution. 879 | footnote:: 880 | CODE:: 881 | // Evaluate SynthDef: 882 | SynthDef("adsr", { 883 | arg freq = 440, 884 | amp = 0.5, 885 | gate = 1, 886 | att = 0.1, dec = 1, sus = 0.3, rel = 2; 887 | 888 | var env = EnvGen.ar( 889 | envelope: Env.adsr(att, dec, sus, rel), 890 | gate: gate, 891 | levelScale: amp * 5, 892 | doneAction: 2); 893 | 894 | var snd = BPF.ar( 895 | in: WhiteNoise.ar, 896 | freq: freq, 897 | rq: 0.005); 898 | 899 | snd = snd * env; 900 | 901 | Out.ar(0, [snd, snd]); 902 | }).add; 903 | 904 | // Play score with a Routine: 905 | { 906 | w = Synth("adsr", [\freq, 1700, \amp, 0.5, \att, 0.1]); 907 | 0.5.wait; 908 | 909 | x = Synth("adsr", [\freq, 900, \amp, 0.9, \att, 1.2, \rel, 5]); 910 | 0.5.wait; 911 | 912 | y = Synth("adsr", [\freq, 2300, \amp, 0.8, \rel, 5]); 913 | 0.8.wait; 914 | 915 | z = Synth("adsr", [\freq, 300, \amp, 0.9, \att, 4.5, \sus, 0.2, \rel, 8]); 916 | 1.wait; 917 | 918 | x.release; 919 | 2.wait; 920 | z.release; 921 | 1.wait; 922 | y.release; 923 | 0.5.wait; 924 | w.release; 925 | 926 | }.fork; 927 | 928 | 929 | :: 930 | :: 931 | 932 | 933 | SECTION::31 934 | Here's a Pbind taken from the tutorial EMPHASIS::A Practical Guide To Patterns::, LINK::Tutorials/A-Practical-Guide/PG_02_Basic_Vocabulary::: 935 | CODE:: 936 | // Flock of Seagulls! 937 | ( 938 | p = Pbind( 939 | \degree, Pslide((-6, -4 .. 12), 8, 3, 1, 0), 940 | \dur, Pseq([0.1, 0.1, 0.2], inf), 941 | \sustain, 0.15 942 | ).play; 943 | ) 944 | :: 945 | Record the output of this pattern into a short audio file (wave of aiff). Then play it back in five simultaenous layers: 946 | LIST:: 947 | ##one with no transposition, but in reverse; 948 | ##another one speeding up to 1.5 over 4 seconds; 949 | ##another one speeding up to 1.5 over 5 seconds; 950 | ##another one slowing down to 0.5 over 7 seconds; 951 | ##another one an octave down and in reverse. 952 | :: 953 | Loop playback for all four layers. 954 | Spread the layers evenly over the stereo field. 955 | 956 | See footnote for one solution. 957 | footnote:: Recording the output of SuperCollider into a sound file, then playing back the sound file at different speeds. Taking advantage of LINK::Guides/Multichannel-Expansion:: to create the five layers. 958 | CODE:: 959 | // Start recording: 960 | s.record; 961 | 962 | // Start the pattern: 963 | Pbind( 964 | \degree, Pslide((-6, -4 .. 12), 8, 3, 1, 0), 965 | \dur, Pseq([0.1, 0.1, 0.2], inf), 966 | \sustain, 0.15 967 | ).play; 968 | 969 | // When it's done, stop recording: 970 | s.stopRecording; 971 | 972 | // Find the file in the right directory, 973 | // then load it into a buffer. 974 | // (SC printed the path of the folder 975 | // when you evaluated the line s.record) 976 | 977 | b = Buffer.read(s, "/home/ruviaro/.local/share/SuperCollider/Recordings/SC_131227_171915.aiff"); 978 | 979 | ( 980 | { 981 | var snd, reversed, speeding, slowing, octave; 982 | reversed = -1; 983 | speeding = Line.kr(1, 1.5, [4, 5]); 984 | slowing = Line.kr(1, 0.5, 7); 985 | octave = -0.5; 986 | snd = PlayBuf.ar( 987 | numChannels: 2, 988 | bufnum: b, 989 | rate: [reversed, slowing, speeding, octave], 990 | loop: 1); 991 | Out.ar(0, Splay.ar(snd)); 992 | }.play 993 | ) 994 | 995 | // Same as above, but condensed into one long line: 996 | 997 | { Splay.ar(PlayBuf.ar(2, b, [-1, -0.5, Line.kr(1, [0.5, 1.5, 1.5], [7, 4, 5])], loop: 1)) }.play; 998 | 999 | :: 1000 | :: 1001 | 1002 | 1003 | 1004 | SECTION::32 1005 | Create a synthetic "wind chimes" texture with the following characteristics: 1006 | LIST:: 1007 | ##a total of 16 chimes are triggered randomly and independently; 1008 | ##each chime plays a single harmonic of the note D2 (MIDI note 38), from the first sixteen partials; 1009 | ##density of chimes (number of notes per second) is controlled by a MouseX.kr; 1010 | ##basic chime sound is a simple sine wave with a percussive envelope (release time of 4 seconds); 1011 | ##chimes are spread over the stereo field: 1st chime panned to the extreme left position, 16th chime to extreme right position; all other chimes evenly spaced in between. 1012 | :: 1013 | 1014 | See footnote for one solution. 1015 | footnote:: 1016 | Harmonic series wind chimes with percussive sine waves: 1017 | CODE:: 1018 | ( 1019 | var n = 16; // number of chimes 1020 | 1021 | { 1022 | Splay.ar(Array.fill(n, { arg i; 1023 | SinOsc.ar( 1024 | freq: 38.midicps * (i+1), 1025 | mul: 0.6) 1026 | * EnvGen.kr( 1027 | envelope: Env.perc(0.01, 4, curve: -8), 1028 | gate: Dust.kr(MouseX.kr(0.01, 0.5)))})) 1029 | }.play; 1030 | ) 1031 | 1032 | // Array.fill takes care of creating an array of 16 sine oscillators, each tuned to a partial of D2. 1033 | 1034 | // Dust.kr triggers the amplitude envelopes of each chime. Note that the envelope multiplication happens inside the Array.fill function, so in fact it expands to sixteen independent Dust.kr controls. 1035 | 1036 | // MouseX.kr controls the density of Dust.kr. 1037 | 1038 | // Splay.ar takes care of spreading the sixteen oscillators on the stereo field. 1039 | :: 1040 | :: 1041 | 1042 | 1043 | SECTION::33 1044 | Here's an example from the Place help file: 1045 | CODE:: 1046 | ( 1047 | SynthDef("help-sinegrain", 1048 | { arg out = 0, freq = 440, sustain = 0.05; 1049 | var env; 1050 | env = EnvGen.kr(Env.perc(0.01, sustain, 0.2), doneAction: 2); 1051 | Out.ar(out, SinOsc.ar(freq, 0, env)) 1052 | }).add; 1053 | ) 1054 | 1055 | ( 1056 | c = Place([0, 0, [0, 4, 7], [1, 5, 8], [2, 6, 9]], inf) + 67; 1057 | x = c.asStream; 1058 | Routine({ 1059 | loop({ 1060 | Synth("help-sinegrain", [\freq, x.next.midicps]); 1061 | 0.17.wait; 1062 | }) 1063 | }).play; 1064 | ) 1065 | :: 1066 | Rewrite the second block of code (the Routine) as a Pbind that plays exactly the same thing. 1067 | 1068 | Bonus challenges: 1069 | LIST:: 1070 | ##change the SynthDef so that you can control the amplitude of each note from inside the Pbind; 1071 | ##modify your Pbind so that each note is randomly sent to either left or right channel. 1072 | :: 1073 | 1074 | See footnote for one solution. 1075 | footnote:: 1076 | Rewriting a given Routine as a Pbind: 1077 | CODE:: 1078 | Pbind( 1079 | \instrument, "help-sinegrain", 1080 | \midinote, Place([0, 0, [0, 4, 7], [1, 5, 8], [2, 6, 9]], inf) + 67, 1081 | \dur, 0.17 1082 | ).play; 1083 | :: 1084 | Bonus challenges: 1085 | CODE:: 1086 | // Add amp argument: 1087 | ( 1088 | SynthDef("help-sinegrain-2", 1089 | { arg out = 0, freq = 440, sustain = 0.05, amp = 0.2; 1090 | var env; 1091 | env = EnvGen.kr(Env.perc(0.01, sustain, amp), doneAction: 2); 1092 | Out.ar(out, SinOsc.ar(freq, 0, env)) 1093 | }).add; 1094 | ) 1095 | // Amp control, plus out bus control (left/right choice): 1096 | Pbind( 1097 | \instrument, "help-sinegrain-2", 1098 | \midinote, Place([0, 0, [0, 4, 7], [1, 5, 8], [2, 6, 9]], inf) + 67, 1099 | \amp, Pwhite(0.1, 0.5), 1100 | \dur, 0.17, 1101 | \out, Pwhite(0, 1) 1102 | ).play; 1103 | :: 1104 | :: 1105 | 1106 | 1107 | SECTION::34 1108 | The two identical Pinds below will play the 12-note pattern from Steve Reich's Piano Phase. Leave the first Pbind as is, and modify the second Pbind so that it speeds up very gradually (phasing effect). In other words, the durations in the second Pbind have to become gradually shorter. 1109 | CODE:: 1110 | ( 1111 | 1112 | var notes = [4, 6, 11, 13, 14, 6, 4, 13, 11, 6, 14, 13]; 1113 | 1114 | Pbind( 1115 | \note, Pseq(notes, 30), 1116 | \dur, 0.15 1117 | ).play; 1118 | 1119 | Pbind( 1120 | \note, Pseq(notes, 30), 1121 | \dur, 0.15, 1122 | ).play; 1123 | 1124 | ) 1125 | :: 1126 | See footnote for one solution. 1127 | footnote:: 1128 | The solution below uses Pseries to generate a descending series of numbers like [1, 0.995, 0.990, 0.985, etc]. The initially fixed duration of the second Pbind (0.15) will now be, in time, multiplied by each of these numbers. As a result, the duration becomes slightly shorter every time. 1129 | CODE:: 1130 | ( 1131 | 1132 | var notes = [4, 6, 11, 13, 14, 6, 4, 13, 11, 6, 14, 13]; 1133 | 1134 | // This one remains constant: 1135 | Pbind( 1136 | \note, Pseq(notes, 30), 1137 | \dur, 0.15 1138 | ).play; 1139 | 1140 | // This one speeds up gradually: 1141 | Pbind( 1142 | \note, Pseq(notes, 30), 1143 | \dur, 0.15 * Pstutter(notes.size * 2, Pseries(1, -0.005, inf)), 1144 | ).play; 1145 | 1146 | ) 1147 | :: 1148 | Notice the use of Pstutter: it repeats ("stutters") 24 times the value coming out of Pseries, and only then requests the next Pseries value. This way the second Pbind player waits 2 bars before moving on to the next level of speeding up. Without this Pstutter trick, the incremental decrease in duration would happen EMPHASIS::every note:: instead of every 2 bars. Listen to a version without Pstutter -- the phasing increases note by note: 1149 | CODE:: 1150 | ( // without Pstutter 1151 | 1152 | var notes = [4, 6, 11, 13, 14, 6, 4, 13, 11, 6, 14, 13]; 1153 | 1154 | // This one remains constant: 1155 | Pbind( 1156 | \note, Pseq(notes, 30), 1157 | \dur, 0.15 1158 | ).play; 1159 | 1160 | // This one speeds up gradually: 1161 | Pbind( 1162 | \note, Pseq(notes, 30), 1163 | \dur, 0.15 * Pseries(1, -0.005, inf), 1164 | ).play; 1165 | 1166 | ) 1167 | :: 1168 | :: 1169 | 1170 | 1171 | SECTION::35 1172 | Here's a SynthDef that uses Pluck (Karplus-Strong synthesis) to simulate a plucked string instrument: 1173 | CODE:: 1174 | SynthDef("plucking", {arg out = 0, amp = 0.1, freq = 440, decay = 5, coef = 0.1; 1175 | var env, snd; 1176 | env = EnvGen.kr(Env.linen(0, decay, 0), doneAction: 2); 1177 | snd = Pluck.ar( 1178 | in: WhiteNoise.ar(amp), 1179 | trig: Impulse.kr(0), 1180 | maxdelaytime: 0.1, 1181 | delaytime: freq.reciprocal, 1182 | decaytime: decay, 1183 | coef: coef); 1184 | Out.ar(out, snd); 1185 | }).add; 1186 | :: 1187 | Write a Pbind to play this instrument. Use all the available controls of this SynthDef in your Pbind. In particular, explore decay and coef in order to understand what they do. Use any combination of Pwhite, Pseq, and Prand inside your Pbind. 1188 | 1189 | NOTE:: 1190 | For coef, keep your values between -1 and +1. 1191 | :: 1192 | 1193 | See footnote for one solution. 1194 | footnote:: 1195 | Writing a Pbind for "plucking" instrument: 1196 | CODE:: 1197 | Pbind( 1198 | \instrument, "plucking", 1199 | \freq, Pseq([220, 440, 550, 415, 100, 900], inf), 1200 | \dur, Pwhite(0.2, 0.4), 1201 | \amp, Prand([0.1, 0.2], inf), 1202 | \decay, 6, // decay of note (see Pluck help file for details) 1203 | \coef, 0.5, // closer to 0, bright and resonant; closer to 1, muted; 1204 | \out, Pseq([0, 1], inf) // alternate left and right channel 1205 | ).play; 1206 | :: 1207 | :: 1208 | 1209 | SECTION::36 1210 | Take as starting point the Pbind you wrote in the previous exercise (with the "plucking" SynthDef). Use \degree instead of \freq, and play any scale of your choice from the list of available ones: 1211 | CODE:: 1212 | Scale.directory; 1213 | :: 1214 | Play the scale up or down continuously at approximately 60 bpm (each note one beat), with decay times long enough so that notes slightly overlap. 1215 | 1216 | Write a separate synth that takes the sound of the ongoing scale and applies a pitch shifter to it. The ratio of pitch change should oscillate randomly between 0.99 and 1.01. Have the original sound play in the left channel, and the pitch shifted version play in the right channel. 1217 | 1218 | See footnote for one solution. 1219 | footnote:: 1220 | Writing a Pbind for "plucking" instrument: 1221 | CODE:: 1222 | // Evaluate this first: 1223 | x = { Out.ar(1, PitchShift.ar(in: In.ar(19), pitchRatio: LFNoise2.kr(13).range(0.99, 1.01))) }.play; 1224 | 1225 | // Then start playing the scale: 1226 | Pbind( 1227 | \instrument, "plucking", 1228 | \scale, Scale.scriabin, 1229 | \degree, Pseq([0, 1, 2, 3, 4, 5, 6, 7], inf), 1230 | \dur, Pwhite(0.9, 1), 1231 | \amp, 0.2, 1232 | \decay, 7, 1233 | \coef, 0.2, 1234 | \out, [0, 19] // play on left channel & send output to bus 19 as well 1235 | ).play; 1236 | 1237 | // Notice that the temp synth (the pitch shifter) is at end of order or execution: 1238 | s.plotTree; 1239 | 1240 | // Have fun and experiment some wilder values for pitchRatio. 1241 | 1242 | :: 1243 | :: 1244 | 1245 | 1246 | 1247 | SUBSECTION::Answers 1248 | 1249 | Hello. --------------------------------------------------------------------------------