Difference between revisions of "SuperCollider Tweets"
From CCRMA Wiki
(→micromoog) |
|||
Line 109: | Line 109: | ||
</pre> | </pre> | ||
− | == | + | ==headcube== |
<pre> | <pre> | ||
// ===================================================================== | // ===================================================================== |
Revision as of 13:58, 1 July 2011
This pages analyses and explains some "tweet-size" SuperCollider examples.
"A popular way to share SuperCollider code is to post it to the social networking site Twitter. This site imposes a restriction on the length of posts, limiting them to 140 characters or less. Creating an interesting sound or piece of music within this constraint has proved a popular challenge."
[quoted from http://swiki.hfbk-hamburg.de:8888/MusicTechnology/899]
For more 140-character SuperCollider code, visit the link above. Feel free to contribute to this page with more analyses! [if you don't have edit access to this wiki, send it to ruviaro at stanford edu and I'll post it]
Contents
micromoog
// SuperCollider code analysis // by Bruno Ruviaro, 2011-06-12 Server.default = s = Server.local.boot g = SwingOSC.default // Original SuperCollider tweet by 'micromoog' // http://swiki.hfbk-hamburg.de:8888/MusicTechnology/899 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} // I'll start with the second part of the code, after the 'plus' sign. // // SECOND HALF OF THE PATCH: right side of + sign // // Listen to a LFPulse turning on and off some white noise. // Frequency is 1 Hz, so one noise burst per second ("quarter notes"). // Notice that we actually have half a second of noise, and half second of silence. // This is because the default "width" of LFPulse is 1/2. {WhiteNoise.ar(LFPulse.kr(1))}.play // By controlling the width parameter we can then control the note duration. // The examples below are still 1 Hz ("quarter notes"), but the actual durations are different: {WhiteNoise.ar(LFPulse.kr(freq: 1, width: 1/10))}.play // note dur is 1/10 of the beat (very "staccato") {WhiteNoise.ar(LFPulse.kr(freq: 1, width: 0.9))}.play // note dur is 9/10 of the beat ("non legato") // Now let's make this white noise pulsate 4 times per beat ("sixteenth notes") {WhiteNoise.ar(LFPulse.kr(4))}.play // standard staccato (width default = 0.5) {WhiteNoise.ar(LFPulse.kr(4, width: 0.05))}.play // much more staccato for the hi-hats // What if I wanted to have one of the white noise bursts to simulate a snare drum? // One out of ever four would have to be longer. The width parameter has to change accordingly. // We can use another LFPulse to do just that. {LFPulse.kr(1, mul: 1/4, add: 0.05).poll(8, label: "out")}.play // outputs 0.05 and 0.3 for half a second each // Simply plug the line above into the white noise: {WhiteNoise.ar(LFPulse.kr(4, width: LFPulse.kr(1, mul: 1/4, add: 0.05)))}.play // The beginning was a little off, so we adjust the iphase of inner LFPulse: {WhiteNoise.ar(LFPulse.kr(4, width: LFPulse.kr(1, iphase: 3/4, mul: 1/4, add: 0.05)))}.play // Abbreviate this to make it "twitter friendly". Note that we add '0' as the iphase of first // LFPulse in order to avoid having to declare the keyword 'width' after, saving several characters... // Also, the original code divides the whole thing by 8 to scale its global amplitude in the mix. {WhiteNoise.ar(LFPulse.kr(4,0,LFPulse.kr(1,3/4)/4+0.05))/8}.play // // FIRST HALF OF THE PATCH: left side of + sign // // This inner LFPulse outputs number 2 for three 'beats', then 4 for one beat (bpm: 60) // LFPulse(freq, iphase, width, mul, add) {(LFPulse.kr(1/4,1/4,1/4)*2+2).poll(4)}.play // The LFPulse above controls the frequency of this LFSaw (2 or 4 Hz). // With the mul/add, the LFSaw outputs a downward ramp from 70 to 30. {LFSaw.kr(freq: LFPulse.kr(1/4,1/4,1/4)*2+2, iphase: 1, mul: -20, add: 50).poll}.play // The LFSaw above controls the frequency of this LFCub. LFCub is more or less like a sine wave, different timbre. // Thus we have a bass line glissando downwards from 70 Hz to 30 Hz; That LFPulse is controlling rhythm, then. {LFCub.ar(LFSaw.kr(LFPulse.kr(1/4,1/4,1/4)*2+2,1,-20,50))}.play // Compare how the same thing sounds using a SinOsc instead: {SinOsc.ar(LFSaw.kr(LFPulse.kr(1/4,1/4,1/4)*2+2,1,-20,50))}.play // // MIXING THE TWO PARTS TOGETHER // // Here's the first and second half of the code, still isolated: {LFCub.ar(LFSaw.kr(LFPulse.kr(1/4,1/4,1/4)*2+2,1,-20,50))}.play {WhiteNoise.ar(LFPulse.kr(4,0,LFPulse.kr(1,3/4)/4+0.05))/8}.play // A simple '+' puts them together: {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)}.play // Note that the WhiteNoise has been enclosed in parentheses to force its division by 8 to happen *before* the sum. // Listen to it without parentheses: {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}.play // without parentheses: white noise too loud! // Now add a !2 at the end of the line to make it stereo... {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}.play // Finally, to save ONE more character, put the 'play' in the beginning: 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)} // Done! // Outstanding unanswered question: how come the inner LFPulse of WhiteNoise.ar produces THREE short notes // and ONE longer note? Why is it not two short and two long? (even with the phase change...)
headcube
// ===================================================================== // Bruno Ruviaro, 2011-07-01 // SuperCollider code analysis // ===================================================================== // Original tweet by Nathaniel Virgo (headcube) // http://swiki.hfbk-hamburg.de:8888/MusicTechnology/899 {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 // ******************************************************** // FIRST PART: INSIDE THE BPF // ******************************************************** // Starting from the BPF, a bandpass filter. // Below is a simple BPF filtering white noise. // BPF.ar(in, freq, rq) --> input, center frequency, rq // This example uses a fixed center frequency of 1000 Hz and a rq of 0.1: {BPF.ar(WhiteNoise.ar(1), 1000, 0.01)}.play // Now instead of a fixed center frequency, let's use a // LFNoise0 to generate new center frequencies for the BPF. // The LFNoise0 will output a new value between 500 and 5000 // twice per second in this example: {BPF.ar(WhiteNoise.ar(1), LFNoise0.kr(2).range(500, 5000), 0.1)}.play // Same thing as above, but now using 'poll' so we // can see the new freqs values being generated: {BPF.ar(WhiteNoise.ar(1), LFNoise0.kr(2).range(500, 5000).poll(2, label: "bpf-freq"), 0.1)}.play // Same thing but now using single impulses (Impulse.ar) // as the input of the BPF, instead of WhiteNoise. We use the *5 // at the end just to make it louder (too soft otherwise). Listen: {BPF.ar(Impulse.ar(2), LFNoise0.kr(2).range(500, 5000).poll(2, label: "bpf-freq"), 0.1)*5}.play // filtered impulses // Now let's step back for a moment. Compare the sound // of a single impulse to that of a slow sawtooth wave: {Impulse.ar(1)}.play // hear it {Impulse.ar(1000)}.plot // see it {Saw.ar(1)}.play // hear it (you hear the dip of the sawtooth from +1 to -1) {Saw.ar(1000)}.plot // see it // Let's use a 1 Hz sawtooth wave as the sound input for the BPF filter. // These filtered 'saw pops' have a very different timbre from // the filtered impulse pops created earlier with Impulse.ar: {BPF.ar(Saw.ar(4), LFNoise0.kr(4).range(500, 5000).poll(4, label: "bpf-freq"), 0.1)}.play // "saw pops" BPF // compare to {BPF.ar(Impulse.ar(4), LFNoise0.kr(4).range(500, 5000).poll(4, label: "bpf-freq"), 0.1)*5}.play // "impulse pops" BPF // Getting a bit closer to the original tweet... // Let's have get the Saw.ar at 32 and 33 Hz: {Saw.ar(33)}.play // the raw saw, mono [CAREFUL: LOUD!] {BPF.ar(Saw.ar(33), LFNoise0.kr(2).range(500, 5000).poll(2, label: "bpf-freq"), 0.1)}.play // raw saw thru BPF {Saw.ar([32, 33])}.play // two raw saws, stereo [CAREFUL: LOUD!] {BPF.ar(Saw.ar([32, 33]), LFNoise0.kr(2).range(500, 5000).poll(2, label: "bpf-freq"), 0.1)}.play // raw saws thru BPF // Now let's say we want the BPF frequencies to be // in the range 18.75 Hz to 4800 Hz, which is the // range of the original tweet (more on that later): {BPF.ar(Saw.ar([32, 33]), LFNoise0.kr(2).range(18.75, 4800).poll(2, label: "bpf-freq"), 0.1)}.play // Note that whenever the output of LFNoise0 produces a big leap from a very low to a // very high number (center frequency for BPF), there's a loud "pop" as a consequence. // These are the interesting "accented notes" we often hear in the final result. // Watch the Post window as you listen, and check when the louder pops happen. // One more step closer to the original tweet: // Let's make the LFNoise0 freq to be 4/3, that is, // 4 new values every 3 seconds (note that we change // the poll frequency accordingly): {BPF.ar(Saw.ar([32, 33]), LFNoise0.kr(4/3).range(18.75, 4800).poll(4/3, label: "bpf-freq"), 0.1)}.play /* What does this mean? If you think of this rhythm in a Tempo of quarter note = 60, which will be the case in the final result, you have something like this (imagine these are representations of 16th notes): > > > > |||| |||| |||| In other words: the frequency of change of the BPF center-freqs (4/3 Hz) actually promotes a certain "syncopated" feel of the final result, especially when the louder pops appear (created by the occasional sudden change from a very low to a very high BPF center-freq) */ // Now take a look at how the scaling of the output of LFNoise0 // is actually accomplished in the original tweet. The author does // use .range(18.75, 4800). Instead we see this: 2**LFNoise0.kr(4/3,4)*300 // output range is 18.75 to 4800 Hz // With 4 as the "mul", the range of this LFNoise0 becomes -4 to + 4. // "2 to the power of (-4 up to + 4), and this result multiplied by 300" 2**(-4)*300 // Evaluate this: if LFNoise0 outputs its lowest -4, result is 18.75 2**(0)*300 // Evaluate this: if LFNoise0 outputs its middle value 0, the result is 300 2**(4)*300 // Evaluate this: if LFNoise0 outputs its highest +4, result is 4800 // So, in fact, the range boundaries are the same as in our earlier examples // using .range(18.75, 4800), but, unlike before, the distribution is NOT linear. // Compare how often you see numbers below 300 appearing in these two examples: {LFNoise0.kr(4/3).range(18.75, 4800).poll(4/3, label: "linear")}.play // Watch the Post window {(2**LFNoise0.kr(4/3,4)*300).poll(4/3, label: "exponential")}.play // Watch the Post window // You can see from the math above that, in the second case, the distribution // of random values is not anymore linear. Basically, there's 50% of chance that // the selected value will be BELOW 300; and 50% of chance that it will be above 300. // In other words, lower center-freqs for the BPF are now being favored. // This is how the linear distribution sounds like with the rest of our code so far: {BPF.ar(Saw.ar([32, 33]), LFNoise0.kr(4/3).range(18.75, 4800).poll(4/3, label: "bpf-freq"), 0.1)}.play // And this is how the exponential one sounds like: {BPF.ar(Saw.ar([32,33]),(2**LFNoise0.kr(4/3,4)*300).poll(2, label: "bpf-freq"),0.1)}.play // The exponential one not only favors lower notes in general, but also // increases the likelihood of even louder pops to appear (leaps from // very low to very high center-freq). That's probably why the original tweet // uses a multiplier of 0.2 for the Saw: {BPF.ar(Saw.ar([32,33],0.2),(2**LFNoise0.kr(4/3,4)*300).poll(4/3, label: "bpf-freq"),0.1)}.play // Finally, a 'distort' method is added to this to smooth things out a bit: {BPF.ar(Saw.ar([32,33],0.2),(2**LFNoise0.kr(4/3,4)*300).poll(4/3, label: "bpf-freq"),0.1).distort}.scope // A few visual examples of what distort does: {LFSaw.ar(300)}.plot // sawtooth {LFSaw.ar(300).distort}.plot // sawtooth with distort {SinOsc.ar(300)}.plot // sine {SinOsc.ar(300).distort}.plot // sine with distort {LFTri.ar(300)}.plot // triangle {LFTri.ar(300).distort}.plot // triangle with distort // Back to the tweet, inside the BPF. Here's the original tweet again: {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 // There is still this LocalIn.ar(2)*7.5+ in the code, // preceding the Saw, and which we have not analyzed yet. // Forget about it for now. Let's look at the the CombN, // which has the entire BPF code as its first argument. // ******************************************************** // SECOND PART: CombN // ******************************************************** // This is a comb delay line. // CombN.ar(in, maxdelaytime, delaytime, decaytime, mul, add) // Simple example with Impulses: {CombN.ar(Impulse.ar(1/2), 2, 0.25, 3)}.play // Input signal: one impulse every 2 seconds (1/2 Hz) // Maximum delay time: 2 seconds // Delay time: 0.25 seconds // Decay time: 3 // You can hear the 'echoing' impulse fading out. // Now let's plug that BPF code into a CombN: {CombN.ar( BPF.ar(Saw.ar([32,33],0.2),(2**LFNoise0.kr(4/3,4)*300),0.1).distort, // input signal 2, // max delay time 0.25, // delay time 3) // decay time }.play // Same thing, with longer decay time (10s), and delay of 1 second: {CombN.ar( BPF.ar(Saw.ar([32,33],0.2),(2**LFNoise0.kr(4/3,4)*300),0.1).distort, // input signal 2, // max delay time 1, // delay time 10) // decay time }.play // Now with the actual values used in the original tweet, // that is, very long decay time (40s) and delay = 2 seconds {CombN.ar( BPF.ar(Saw.ar([32,33],0.2),(2**LFNoise0.kr(4/3,4)*300),0.1).distort, // input signal 2, // max delay time 2, // delay time 40) // decay time }.play // Because the delay is now 2 seconds, we hear it more as 'meter' // rathern than 'echo'. The result is a kind of 2-beat metric structure // (say, a 2/4 with quarter note = 60), which gradually gets filled // with "sixteenth notes" (the 4:3 pattern of the LFNoise0) // as the CombN accumulates decaying echoes of these attacks. // Remember the earlier example with a linear distribution of the // random numbers between 18.75 and 4800? If we use THAT one now, // a lot LESS pops (attacks) are generated, and the whole thing is // much less rhythmic as a result. The exponential distribution // of random numbers is then directly relevant to the // effectiveness of the final rhythmic result. Here's how // this same bit of code sounds with the linear distribution: {CombN.ar( BPF.ar(Saw.ar([32,33],0.2),LFNoise0.kr(4/3).range(18.75, 4800),0.1).distort, // input signal 2, // max delay time 2, // delay time 40) // decay time }.play // Another variation below. A version with white noise instead of the // sawtooth reveals an interesting aspect about the sawtooth: {CombN.ar( BPF.ar(WhiteNoise.ar([1,1]),(2**LFNoise0.kr(4/3,4)*300),0.1).distort, // input signal 2, // max delay time 2, // delay time 40) // decay time }.play // We still get occasional "rhythmic" pops; but note that the distance // in amplitude between the pops and the 'sustained' portions of // the texture is smaller; the continuous white noise // competes for the foreground with the pops. In the original Saw // version, the dynamic distance between 'explosive' pops and the // continuous notes is much bigger, so that the pops (thus the rhythm) // becomes clearly the foreground within an overall 'quieter' texture. // Here's another version, back with Saw, but with a more limited // range of center freqs for the BPF. The attacks (pops) disappear, since // there are no more big leaps from low to high center-freqs. A more continuous // texture becomes prevalent, but you can still hear the underlying rhythm: {CombN.ar( BPF.ar(Saw.ar([32,33],0.2),LFNoise0.kr(4/3).range(500, 1500),0.1).distort, // input signal 2, // max delay time 2, // delay time 40) // decay time }.play // ******************************************************** // THIRD PART: LocalIn and LocalOut // ******************************************************** // LocalIn.ar and LocalOut.ar are internal buses (see help file). // In the example below, we feed one impulse every 3 seconds // into this local bus. LocalIn is inside LocalOut, thus a feedback // is created. The impulse is repeated every 64 samples (block size), // each time multiplied by 0.99 (so it fades out quickly). {LocalOut.ar(a=LocalIn.ar(1)+Impulse.ar(1/3)*0.99);a}.play // Assuming the current sampling rate (sr) is 44100, we can find out the // frequency of this "note" by dividing sr by 64 (block size): 44100/64 // result is 689.0625 Hz if your sr = 44100 // Check it with a sine wave, they should sound the same pitch: {SinOsc.ar(44100/64, mul: 0.5)}.play // freq = sampling rate / block size {LocalOut.ar(a=LocalIn.ar(1)+Impulse.ar(1/3)*0.99);a}.play // freq = sampling rate / block size // The variable 'a' above works like in the simpler example below. // There are two statements separated by a semicolon: {a=SinOsc.ar(440);a}.play // Finally, this is how the original tweet // uses the LocalIn LocalOut structure: {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 // The variable 'a' is the CombN code we analyzed earlier: {CombN.ar(BPF.ar(Saw.ar([32,33],0.2),2**LFNoise0.kr(4/3,4)*300,0.1).distort,2,2,40)}.play /* I still haven't figured out: a) What exactly happens when the LocalIn / LocalOut structure is used? b) Why is LocalIn.ar(2) multiplied by 7.5? */
mathk
// Original tweet by mathk // http://swiki.hfbk-hamburg.de:8888/MusicTechnology/899 {k=LFNoise1.kr(8.0.rand+2,0.5,0.5);SinOsc.ar([[333,444],[222,555]]*(k+(rrand(1.0,5.0))),0,k).sum.cubed * 0.1}.play // #supercollider #babies
mutantsounds
// Original tweet by mutantsounds // http://swiki.hfbk-hamburg.de:8888/MusicTechnology/899 {x=Array.fill(5,{[0.00001,0.03].asSpec.map(LFNoise2.kr(3))});Splay.ar(Friction.ar(LFTri.ar(50),friction:x,mass:x*30000))}.play