diff --git a/README.md b/README.md deleted file mode 100644 index 9476158..0000000 --- a/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# rsc3 for Racket - - -This is a port of the [rsc3](http://rd.slavepianos.org/?t=rsc3) super collider client to Racket, organized as a Racket package. Used the Thu Jun 27 2013 version. - -rsc3 is Written by [Rohan Drape](http://rd.slavepianos.org/), © 2008-2013 - -Licensed under GPL (2 or 3? FIXME) - -## Install - -Install with `raco pkg install rsc3`, assuming racket/bin is in PATH. - -## Using - -`(require rsc3)` - -This also exports `send`, which is from the sosc collection. (TODO - need to fix sosc exports) - - diff --git a/README.org b/README.org new file mode 100644 index 0000000..6c88892 --- /dev/null +++ b/README.org @@ -0,0 +1,20 @@ +* rsc3 for Racket + +This is a port of the [[http://rd.slavepianos.org/?t=rsc3][rsc3]] [[https://supercollider.github.io/][SuperCollider]] client to Racket. rsc3 is written by [[http://rd.slavepianos.org/][Rohan Drape]] and ported to Racket by [[https://khafateh.com/][Mustafa Khafateh]] based on the Thu Jun 27 2013 version. The current version of rd--rsc3 is available at [[https://gitlab.com/rd--/rsc3][gitlab]] + +** Install + +If you have raco installed you can Install rsc3 with =raco pkg install rsc3= + +** Using + +Require the module, open SuperCollider or sclang and write some synthetic audio… +#+BEGIN_SRC racket :lang racket +(require rsc3) +#+END_SRC + +The names of ugens have been adapted to scheme style (e.g. =FSinOsc= to =f-sin-osc= ) but are usually obvious. Specifics can be checked in =rsc3/main.rkt= and there are some useful examples in the =rsc3/examples/= folder (some may need tweaking to work with Racket) + +* Documentation + +The scheme files in file:doc/help/ correspond closely to the SuperCollider help files included in the standard distribution and can easily be used alongside them (the schelp source files can be found at [[https://github.com/supercollider/supercollider/tree/develop/HelpSource][github]]). Examples from the help files, tutorials and other sources are included in the [[file:doc/][doc folder]]. diff --git a/doc-schelp/Help-3.12.2/BrokenLink.html b/doc-schelp/Help-3.12.2/BrokenLink.html new file mode 100644 index 0000000..8e27c02 --- /dev/null +++ b/doc-schelp/Help-3.12.2/BrokenLink.html @@ -0,0 +1,32 @@ + + + +
++ Could not find: +
+ +A Schroeder allpass filter is given by the difference equations
s(t) = x(t) + k * s(t - D) +y(t) = -k * s(t) + s(t - D)+ +
where x(t)
is the input signal, y(t)
is the output signal, D
is the delay time, and k
is the allpass coefficient.
+
In this UGen, k
is computed as k == 0.001 ** (delay / decay.abs) * decay.sign
(0.001 is -60 dBFS).
+
This UGen quantizes the delay time to the nearest sample period, and will produce aliasing artifacts if the delay time is modulated. If these are undesirable properties, the more CPU-expensive alternatives are AllpassL which uses linear interpolation, and AllpassC which uses cubic interpolation.
in |
+ The input signal. |
maxdelaytime |
+ The maximum delay time in seconds. Used to initialize the delay buffer size. |
delaytime |
+ Delay time in seconds. |
decaytime |
+ Time for the echoes to decay by 60 decibels. If this time is negative, then the feedback coefficient will be negative, thus emphasizing only odd harmonics at an octave lower. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
// Since the allpass delay has no audible effect as a resonator on +// steady state sound ... + +{ AllpassN.ar(WhiteNoise.ar(0.1), 0.01, XLine.kr(0.0001, 0.01, 20), 0.2) }.play; + +// ...these examples add the input to the effected sound and compare variants so that you can hear +// the effect of the phase comb: + +( +{ + z = WhiteNoise.ar(0.2); + z + AllpassN.ar(z, 0.01, XLine.kr(0.0001, 0.01, 20), 0.2) +}.play) + +( +{ + z = WhiteNoise.ar(0.2); + z + AllpassL.ar(z, 0.01, XLine.kr(0.0001, 0.01, 20), 0.2) +}.play) + +( +{ + z = WhiteNoise.ar(0.2); + z + AllpassC.ar(z, 0.01, XLine.kr(0.0001, 0.01, 20), 0.2) +}.play) + +// used as an echo - doesn't really sound different than Comb, +// but it outputs the input signal immediately (inverted) and the echoes +// are lower in amplitude. +{ AllpassN.ar(Decay.ar(Dust.ar(1,0.5), 0.2, WhiteNoise.ar), 0.2, 0.2, 3) }.play;+
//This file is part of MLfftwUGens. Copyright (C) 2006 Nicholas M.Collins distributed under the terms of the GNU General Public License full notice in file MachineListening.license + +//This file is part of The BBCut Library. Copyright (C) 2001 Nick M.Collins distributed under the terms of the GNU General Public License full notice in file BBCutLibrary.help+ +
On-the-fly event analyser, based on onset detection/on-the-fly analysis described in my academic papers. Best for percussive events. Recommended that you go via the Segmentation (for one-pass) and AnalyseEventsDatabase (for on-the-fly) classes in standard usage, don't use this directly.
in |
+ Audio input to track |
bufnum |
+ A buffer within which results of the analysis are place |
threshold |
+ A parameter acting as the onset detector threshold, default of 0.34 was determined as the best performing over a database of percussive onset, but you might want to change this to change the sensitivity (though you always increase the risk of false positives or false negatives) |
triggerid |
+ A trigger ID number used for communication from the UGen to the Lang to mark that a new event was received. Only passed for on-the-fly analysis. |
circular |
+ A flag to note on-the-fly analysis assuming a circular buffer. If you only need a one-pass analysis on a file, you won't use this. |
pitch |
+ Can take a .kr pitch detection UGen as input. Will take the median fundamental frequency over a note event from values recorded from this pitch input. |
No examples are given, to discourage solo use.
SystemClock is more accurate, but cannot call GUI primitives. +
You will need to use the SystemClock to get accurate/musical scheduling. +
See Clock for general explanation of how clocks operate.
The float you return specifies the delta to resched the function for. Returning nil will stop the task from being rescheduled.
( +AppClock.sched(0.0,{ arg time; + ["AppClock has been playing for ",time].postln; + rrand(0.1,0.9); +}); +)+ +
( +AppClock.sched(2.0,{ + "2.0 seconds later".postln; + nil; +}); +)+
Clear the AppClock's scheduler to stop it.
AppClock.clear;+
The Routine (or Task) yields a float value indicating the delta (secs) for the AppClock to wait until resuming the Routine.
( +var w, r; +w = Window.new("trem", Rect(512, 256, 360, 130)); +w.front; +r = Routine({ arg appClockTime; + ["AppClock has been playing for secs:",appClockTime].postln; + 60.do({ arg i; + 0.05.yield; + w.bounds = w.bounds.moveBy(10.rand2, 10.rand2); + w.alpha = cos(i*0.1pi)*0.5+0.5; + }); + 1.yield; + w.close; +}); +AppClock.play(r); +)+
AppClock.tick is called periodically by the SuperCollider language interpreter. This updates the Scheduler and causes any scheduled tasks to be executed. You should never call this method yourself.
name | |
layout | |
minWidth |
Arrays are ArrayedCollections whose slots may contain any object. Arrays have a fixed maximum size beyond which they cannot grow. For expandable arrays, use the List class. +
Literal Arrays can be created at compile time, and are very efficient. See Literals for information. +
For handling multidimensional arrays, there are specific methods which are covered in the helpfile J concepts in SC.
add
method may or may not return the same Array object. It will add the argument to the receiver if there is space, otherwise it returns a new Array object with the argument added. Thus the proper usage of add
with an Array is to always assign the result as follows:
+
+This allows an efficient use of resources, only growing the array when it needs to. The List class manages the Array internally, and in many cases is more suitable.
Elements can be put into an existing slot with a.put(2,obj)
and accessed with a.at(2)
or a[2]
+
See ArrayedCollection for the principal methods: at, put, clipAt, wrapAt, etc...
Create a new array with size 0 that can grow up to the fixed size.
maxSize |
+ The maximum size of the array. |
Create a new array with all slots filled with nils.
indexedSize |
+ The size of the array. |
Create a new Array whose slots are filled with the given arguments. This is the same as the method in ArrayedCollection, but is reimplemented here to be more efficient. +
Creates a Collection of the given size, the elements of which are determined by evaluation the given function. The function is passed the index as an argument. +
size |
+ The size of the collection which is returned. If nil, it returns an empty collection. If an array of sizes is given, the resulting collection has the appropriate dimensions (see: *fillND). + |
function |
+ The function which is called for each new element - the index is passed in as a first argument. The function be anything that responds to the message "value". + |
Creates a 2 dimensional Collection of the given sizes. The items are determined by evaluation of the supplied function. The function is passed row and column indexes as arguments. See J concepts in SC +
Creates a N dimensional Collection where N is the size of the array dimensions. The items are determined by evaluation of the supplied function. The function is passed N number of indexes as arguments. See J concepts in SC +
Creates a new Collection from another collection. This supports the interface for the method "as". +
Fill an ArrayedCollection with a geometric series. +
Fill an ArrayedCollection with an arithmetic series. +
Fills an ArrayedCollection with a counter. See J concepts in SC for more examples. +
Fill a SequenceableCollection with the interpolated values between the start and end values. +
Fill a SequenceableCollection with random values in the range minVal to maxVal. +
Fill a SequenceableCollection with random values in the range -val to +val. +
Fill a SequenceableCollection with random values in the range minVal to maxVal with a linear distribution. +
Fill a SequenceableCollection with random values in the range minVal to maxVal with exponential distribution. +
Fill a SequenceableCollection with a fibonacci series. + +
size |
+ the number of values in the collection |
a |
+ the starting step value |
b |
+ the starting value |
Return the item at index. +
The index can also be an Array of indices to extract specified elements. Example: +
Put item at index, replacing what is there.
Inserts the item into the contents of the receiver. This method may return a new ArrayedCollection. For this reason, you should always assign the result of insert
to a variable - never depend on add changing the receiver.
+
Same as -at, but values for index greater than the size of the ArrayedCollection will be clipped to the last index. +
Same as -at, but values for index greater than the size of the ArrayedCollection will be wrapped around to 0. +
Same as -at, but values for index greater than the size of the ArrayedCollection will be folded back. +
Same as -put, but values for index greater than the size of the ArrayedCollection will be clipped to the last index.
Same as -put, but values for index greater than the size of the ArrayedCollection will be wrapped around to 0.
Same as -put, but values for index greater than the size of the ArrayedCollection will be folded back.
Swap the values at indices i and j. +
Return a new array in which a number of elements have been replaced by another. + +
this method is inherited by String : +
Concatenate the contents of the two collections into a new ArrayedCollection. +
Adds an item to an ArrayedCollection if there is space. This method may return a new ArrayedCollection. For this reason, you should always assign the result of add to a variable - never depend on add
changing the receiver.
+
Adds all the elements of aCollection to the contents of the receiver. This method may return a new ArrayedCollection. For this reason, you should always assign the result of addAll
to a variable - never depend on add changing the receiver.
+
Inserts the item before the contents of the receiver, possibly returning a new collection. +
Remove and return the element at index, shrinking the size of the ArrayedCollection. +
Answer a new collection which consists of the results of function evaluated for each item in the collection. The function is passed two arguments, the item and an integer index. See Collection helpfile for examples.
Iterate over the elements in order, calling the function for each element. The function is passed two arguments, the element and an index. +
Iterate over the elements in reverse order, calling the function for each element. The function is passed two arguments, the element and an index. +
The same as -collect, but can look inside sub-arrays up to the specified depth. +
For a multidimensional array, rearranges the data using the desired number of elements along each dimension. The data may be extended using wrapExtend if needed. +
Interprets the array as a list of probabilities which should sum to 1.0 and returns a random index value based on those probabilities. +
Return the number of elements the ArrayedCollection.
Returns a new Array with the receiver items normalized between min and max. +
Returns the Array resulting from : + +
so that the array will sum to 1.0. +
This is useful for using with windex or wchoose. +
Plot values in a GUI window. See plot for more details. When the receiver contains nil
items, the plot fails with an error.
Returns a new Array whose elements are reversed. The receiver is unchanged. +
Returns a new Array whose elements have been scrambled. The receiver is unchanged. +
Return a new Array which is the receiver made into a palindrome. The receiver is unchanged. +
Return a new Array which is the receiver made into a palindrome with the last element removed. This is useful if the list will be repeated cyclically, the first element will not get played twice. The receiver is unchanged. +
Return a new Array which is the receiver concatenated with a reversal of itself. The center element is duplicated. The receiver is unchanged. +
Return a new Array whose elements are repeated n times. The receiver is unchanged. + +
n |
+ Number of repeats. |
Return a new Array whose elements are in rotated order. The receiver is unchanged. + +
n |
+ Number of elements to rotate. Negative n values rotate left, positive n values rotate right. |
Return a new Array whose elements have been reordered via one of 10 "counting" algorithms. Run the examples to see the algorithms. + +
patternType |
+ Choose counting algorithm. The algorithms are numbered 1 through 10. |
Like pyramid, but keep the resulting values grouped in subarrays. +
Return a new Array of length maxlen with the items partly repeated (random choice of given probability). + +
probability |
+ Probability of repeat. |
maxlen |
+ The length of the new Array. |
Returns a new Array whose elements are interlaced sequences of the elements of the receiver's subcollections, up to size length. The receiver is unchanged. +
Returns a new Array whose elements are the nthPermutation of the elements of the receiver. The receiver is unchanged. +
Returns a new Array whose elements contain all possible combinations of the receiver's subcollections. +
Returns a new Array whose elements are repeated sequences of the receiver, up to size length. The receiver is unchanged. +
Same as wrapExtend but the sequences fold back on the list elements. +
Same as wrapExtend but the sequences "clip" (return their last element) rather than wrapping. +
Return a new Array whose elements are repeated subsequences from the receiver. Easier to demonstrate than explain. +
Shift the values of the array n steps to the right (n positive) or to the left(n negative), dropping the excess and filling empty space with zero. +
Returns true if the receiver Array contains any instance of SequenceableCollection +
Returns all possible combinations of the array's elements. + +
powerset is also supported in Collection: +
Given an array of symbols, this returns an array of pairs of (symbol, value) from the current environment. This can then be used as arguments for a Synth, or in an OSC message. +
Invert rows and columns in a two dimensional Array (turn inside out). See also: Function, SequenceableCollection. +
Used by UGens to perform multi channel expansion. Same as flop.
Some UGens return Arrays of OutputProxy when instantiated. This method allows you to get at the source UGen. +
Used within Routines and assumes an array of functions, from which subroutines are created. The subroutines are played while the outer Routine carries on. The join parameter expresses after how many subroutines complete the outer Routine is allowed to go on. By default this happens after all subroutines have completed. +
apply an array of Dpoll units to an array of UGens (see those helpfiles for more details).
This method is used by IdentitySet to search for a key among its members.
This method is used by IdentityDictionary to search for a key among its members.
Returns a string representing the Array. May not be compilable due to elision (...) of excessive arguments.
Returns a string that will compile to return an Array equal to the receiver.
Returns true. Arrays are valid UGen inputs.
Returns the OSC message as an Int8Array. Receiver must be a bundle. +
Astrocade custom 'IO' chip sound chip driver by Aaron Giles and Frank Palazzolo.
reg0 |
| ||||||
reg1 |
| ||||||
reg2 |
| ||||||
reg3 |
| ||||||
reg4 |
| ||||||
reg5 |
| ||||||
reg6 |
| ||||||
reg7 |
|
This help file is for the BBCut2 class. For an overview of the BBCut2 library, see BBCut. +
BBCut2 provides the central scheduling mechanisms for algorithmic audio cutting running on an external clock. +
BBCut2 is passed a cut renderer, an algorithmic cutting routine, and an optional quantisation routine. The clock is then passed in via play. +
BBCut2 sends OSC messages ahead of rendering time to the Server so as to maintain perfect time lock without the jitter caused by variable network latency. This is superior to BBCut's 'send message at moment of decision' paradigm. The price is that the events during the next beat are determined a beat in advance and thus interaction with the cutting routines may show a beat or more's delay in response.
cutgroups |
+ an Array of CutGroups, though this argument can also cope with being passed a CutSynth directly, a single CutGroup, an Array of CutSynths and an Array of Arrays of CutSynths. Now, a proc can have multiple renderers. The CutGroup is a necessary abstraction to select the execution graph and bus requirements of SC3, and you can think of BBCut2 making Group Nodes on the Server for each distinct CutGroup (=cut synthesiser chain). |
proc |
+ an instance of a class derived from BBCutProc. This is the algorithmic composition routine that will generate cuts. |
quantiser |
+ An optional quantisation class which imposes a template on the output of the algorithmic cutter. Leave this nil for default behaviour. |
Frees any group and bus resources. If you passed in explicit Group and Bus objects they will not be deleted and are your own responsibility (see CutGroup).
Pause the bbcut2- it will not send further messages to the Server (but may have scheduled some already which can't be taken back).
Restart after a pause.
Stop playing, removing the cutter from the current clock.
Stop and free.
Start playing on a given clock.
clk |
+ A clock class derived from ExternalClock for the event scheduling. ExternalClock is a base class which wraps a TempoClock in SCLang. ServerClock is used for beat induction control from Server side UGens. If a TempoClock is passed in, it gets wrapped in an ExternalClock. If a bps is passed in (eg .play(2)) then a TempoClock is created at that bps and wrapped in an ExternalClock. |
s=Server.default; + +//shortest possible example, defaults to BBCutProc11 cut procedure, generates a default clock for you at 2 bps +//will cut up the first audio input stream on your system (use with headphones to avoid feedback) +b=BBCut2(CutStream1.new).play + +b.pause; //silent mode + +b.resume; //hear again + +b.stop; //finish on clock + +c=ExternalClock(TempoClock(2.7)).play; + +b.play(c); //start on a new running clock + +b.stop; + +b.free; //free any resources left + + +//better use, explicit passing of clock, buffer +( +var clock= ExternalClock(TempoClock(2)); +var buffer= Buffer.alloc(s,44100,1); + +clock.play; + +//runs on first available input channel +BBCut2(CutStream1(s.options.numOutputBusChannels,buffer),BBCutProc11.new).play(clock); +) + + +( //the same for a soundfile: you must allow time for the soundfile to load +var sf; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + +s.sync; //this forces a wait for the Buffer to load + +BBCut2(CutBuf1(sf)).play; //will use TempoClock.default so depends what tempo that is at +}); + +) + +TempoClock.default.tempo_(2.3) + + +//you would usually load soundfiles separately (perhaps before performing) before running cutters + + +//better practise, more explicit +( +var sf, clock; + +clock= ExternalClock(TempoClock(2.5)); + +clock.play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + +s.sync; //this forces a wait for the Buffer to load + +BBCut2(CutBuf1(sf), BBCutProc11.new).play(clock); +}); + +) + +//sharing clocks, and demonstrating what happens with tempo changes + +( +var sf, tempoclock, bbcutclock; + +SynthDef(\beep,{Out.ar(0,SinOsc.ar(440,0,0.1)*Line.kr(1,0,0.1,doneAction:2))}).load(s); + +tempoclock= TempoClock(2.3); + +bbcutclock= ExternalClock(tempoclock); + +Routine.run({ + + sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + + s.sync; //this forces a wait for the Buffer to load + + BBCut2([CutBuf1(sf),CutMixer(0,1.0,1.0,1.0)], BBCutProc11.new).play(bbcutclock); +}); + +bbcutclock.play; + +Task({ + + inf.do({ + + Synth(\beep); + + if(0.1.coin,{tempoclock.tempo_(rrand(1.7,2.7))}); + + 1.0.wait; + }); + +}).play(tempoclock); + +) + + + + + +//creating CutGroup to choose rendering bus and Group, renders on bus 16 but output on bus 0 due to a hidden CutMixer +( +var clock= ExternalClock(TempoClock(2)); +var buffer= Buffer.alloc(s,44100,1); +var group= Group.head(Node.basicNew(s,1)); +var bus= Bus.audio(s,1); //mono rendering bus + +clock.play; + +//runs on first available input channel +BBCut2(CutGroup(CutStream1(nil,buffer),group,bus),BBCutProc11.new).play(clock); +) + + +//also specifying a CutMixer to choose volume and outbus, but CutGroup will allocate a rendering Bus for you +( +var clock= ExternalClock(TempoClock(2)); +var buffer= Buffer.alloc(s,44100,1); +var group= Group.head(Node.basicNew(s,1)); + +clock.play; + +BBCut2(CutGroup([CutStream1(nil,buffer),CutMixer(0,0.5,1.0,{1.0.rand2})],group),BBCutProc11.new).play(clock); +) + + + + + + +//showing off the five input possibilities for the cutgroup array argument +//five options for shortcuts- all work because of code in BBCut2-initBBCut2 + + + +//run this first to load the samples: +( +var filenames, bpsound; + +//Put in the locations of your samples +filenames= [Platform.resourceDir +/+ "sounds/break.aiff",Platform.resourceDir +/+ "sounds/break2.aiff"]; +//how many beats in your samples? +bpsound= [8,4]; + +f= BBCutBuffer.array(filenames,bpsound); +) + +//test soundfile parameters +f.do({arg val; Post <<[val.path, val.numChannels, val.numFrames, val.sampleRate, val.bufnum]<<nl;}); + +//start a BBCut scheduling clock +c= ExternalClock(TempoClock(2)).play; + +//run one line at a time, each BBCut instance +a=BBCut2(CutFunc.new(blockfunc:{Synth(\beep)}),BBCutProc11.new).play(c); + +a.end; //end stops and frees the cutter + +//makes one running BBCutGroup containing two renderers +a= BBCut2([CutBuf1(f[0]),CutBuf1(f[1])],BBCutProc11.new).play(c); + +a.end; + +//makes two running groups, first with default cut synth, second with sample based renderers +a= BBCut2([[CutFunc.new(blockfunc:{Synth(\beep)})], [CutBuf1(f[0]),CutBuf1(f[1])]],BBCutProc11.new).play(c); + +a.end; + +//create explicitly a single BBCutGroup (BBCG is a shortcut name for one) +a= BBCut2(CutGroup(CutBuf1(f[0])),BBCutProc11.new).play(c); + +a.end; + +//explicitly create an array of BBCutGroups, like the 'makes two running groups' option above but here more explicitly done +a= BBCut2([CutGroup(CutFunc.new(blockfunc:{Synth(\beep)})), CutGroup([CutBuf1(f[0]),CutMixer(0,0.7,1.0,{1.0.rand2})]),CutGroup(CutBuf1(f[1]))],BBCutProc11.new).play(c); + +c.tempoclock.tempo_(2.3); + +a.end; + + + +//tempo ratio of 20 to 27 +( +var sf, clock1, clock2; + +clock1= ExternalClock(TempoClock(2)); +clock2= ExternalClock(TempoClock(2.7)); + +Routine.run({ + +sf=BBCutBuffer.array([Platform.resourceDir +/+ "sounds/break.aiff",Platform.resourceDir +/+ "sounds/break2.aiff"],[8,4]); + +s.sync; //this forces a wait for the Buffer to load + +BBCut2([CutBuf1(sf[0]),CutMixer(0,0.5,1.0,-1.0)], ChooseCutProc(0.25,{[4,2].choose})).play(clock1); +BBCut2([CutBuf1(sf[1]),CutMixer(0,0.5,1.0,1.0)], ChooseCutProc(0.25,{[4,2].choose})).play(clock2); +}); + +clock1.play; +clock2.play; +) + + + + +//adding/removing cutsynths + +//better practise, more explicit +( +var sf, clock; + +clock= ExternalClock(TempoClock(2.5)); + +clock.play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + +s.sync; //this forces a wait for the Buffer to load + +g=CutGroup(CutBuf1(sf)); + +a=BBCut2(g, BBCutProc11.new).play(clock); +}); + +) + +//run these one at a time +g.cutsynths.postln; //default CutMixer was added + +g.add(CutComb1({arg i; (i.clip2(10)*0.005)+0.01},0.5)); + +g.cutsynths.postln; + +g.removeAt(2); //remove comb + +g.cutsynths.postln; + +a.end+
Holds data on a buffer including any segmentation (event positions). Derived from the Buffer class and has methods for choosing playback segments.
filename |
+ File location of a soundfile to load. Default path is to the SC3 source folder. |
beatlength |
+ Number of beats in the soundfile, also used to estimate the source tempo in beats per second. |
eventlist |
+ Array giving a segmentation of the sound file measured in sample frames. The class can construct one for you if you pass in a granularity, ie 0.5 for eighth note cuts imposed throughout the soundfile length- see below. If left nil the default is an even segmentation in eighth notes. |
action |
+ A function to be evaluated when the buffer is done loading. This instance of BBCutBuffer is passed as an argument. |
filenames |
+ Array of sound file locations to load |
beatlengths |
+ Number of beats in the soundfile, also used to estimate the source tempo in beats per second. |
eventlists |
+ Array of Arrays of event positions. |
action |
+ A function to be evaluated when all buffers are done loading. This instance of BBCutBuffer is passed as an argument. |
//run one line at a time +f= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + +Post << [\length,f.length, \beatlength, f.beatlength, \events, f.eventstarts] << nl; + + +f= BBCutBuffer.array([Platform.resourceDir +/+ "sounds/break.aiff",Platform.resourceDir +/+ "sounds/break2.aiff"],[8,4],[[0,10000],[3500,6700,9000]]); + +f.do({ arg val; Post << [\length,val.length, \beatlength, val.beatlength, \events, val.eventstarts] << nl;}); + + +//will only select playback positions from the events list given above, using an offset choice function +( +BBCut2(CutGroup([CutBuf1(f[1],{arg buffer; buffer.eventstarts.choose}), CutBPF1(1000,drqfunc:0.5)]), ChooseCutProc(0.125,4)).play(2) +) + +//change the event positions allowed +f[1].events_([10000,20000,40000]);+
The original automatic breakbeat cutting algorithm as refined in the BreakBeatx series of classes. This cut procedure favours small odd number length cuts with respect to some subdiv integer for a phrase. A block consists of an original cut plus some number of repeats. A special fast 'stutter' or 'roll' can occur only to finish off a given phrase. +
As is normal for cut procedures and synths, the value message is used to get the current value of any input parameter, so functions can be passed. Defaults are provided for all arguments. +
For additional data on the algorithm see +
Nick Collins, "Algorithmic Composition Methods for BreakBeat Science", +
Proceedings of Music Without Walls, ISBN 1857213319 +
reproduced at http://www.cus.cam.ac.uk/~nc272
Create a BBCutProc11 object with the given parameters.
sdiv |
+ sub division. A single measure is cut up into sdiv primitive units. So sdiv=8 over 4 beats gives eighth note resolution cutting. |
barlength |
+ Normally set to 4.0 beats, for 4/4 bars, this can be altered to allow cuts over a different number of beats. So barlength= 3 gives cuts respecting 3/4. |
phrasebars |
+ The length of the current phrase is barlength*phrasebars. |
numrepeats |
+ Total number of repeats for normal cuts. So 2 corresponds to a particular size cut at one offset plus one exact repetition. |
stutterchance |
+ the tail of a phrase has this chance of becoming a repeating one unit cell stutter (0.0 to 1.0) |
stutterspeed |
+ the stutter can be an integer multiple of the subdivision speed. For instance, if subdiv is 8 (quavers) and stutterspeed is 2, then the stutter is in semiquavers (subdiv 16). |
stutterarea |
+ a stutter is permissible within this proportion of the last bar of a phrase. Use values larger than 1 for stutters across multiple bars. The default is 0.5, for a half bar at 4/4. |
Called internally after a new. +
Other methods are overrides of BBCutProc base class behaviour. BBCutProc11 will flag a roll when stuttering.
You'll have to substitute your own break sample to hear the rhythmic aspects of this procedure properly
//must have run this line before any of the examples +f= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + +//default cutups +BBCut2(CutBuf1(f),BBCutProc11.new).play(2.4); + + + +( //demonstrating tempo following from Server Clock control +var bbcut, pulse, clock; + +clock= ServerClock.new.play(100); + +bbcut= BBCut2(CutBuf1(f),BBCutProc11.new(8,4.0,2,3,0.5,3,1.0)); + +pulse= SynthDef(\pulse, { +var rate, impulse; + +rate= MouseX.kr(2,3); + +impulse= Impulse.kr(rate); + +SendTrig.kr(impulse, 100, rate); + +Out.ar(0,Decay.kr(impulse,0.1)*SinOsc.ar(440,0,0.5)) + +}).play; + +bbcut.play(clock); + +) + + + + +( + //showing use of all parameters +var w; +var slider,string; +var num; +var names,data, specs, lastval; +var bbcutter, clock; + +names= ["tempo","cut","phrasebars","stutterchance","stutterspeed","restchance","ampvariation","panvariation","numrepeats","beatsperbar","randomoffset"]; + +data= //control spec data for ui controls +[ +[2.0,3.0,\exp, 0.0,2.5], //tempo +[4,16,\lin,1,8], //cuts +[1,5,\lin,1,2], //phrasebars +[0.0,1.0,\lin,0.0,0.2], //stutterchance +[1,3,\lin,1,2], //stutterspeed +[0.0,1.0,\lin, 0.0,0.0], //restchance +[0.0,1.0,\amp, 0.0,1.0], //ampvariation +[0.0,1.0,\lin, 0.0,0.0], //panvariation +[1,5,\lin,1,2], //numrepeats +[2.0,6.0,\lin,1.0, 4.0], //subdivision over x beats +[0.0,1.0,\lin,0.0, 0.0] //offsetchance +]; + +specs= Array.fill(data.size, {arg i; ControlSpec.performList(\new, data.at(i))}); + +num= names.size; + +w = SCWindow("BBCutProc11 demo N.M.Collins 23/08/03", Rect.new(100, 500, 500, (30*num))); + +string= Array.fill(num); +slider= Array.fill(num); +//Fill with defaults. This array will hold the updated last value of any GUI element +lastval= Array.fill(num, {arg i; data.at(i).at(4);}); + +num.do( +{ +arg i; + +SCStaticText( w, Rect.new(5, (30*i)+5, 95,20)).string_(names.at(i)); +slider.put(i,SCSlider.new( w, Rect.new(105, (30*i)+5, 95,20))); +//set slider to default value, else will default to 0.0 +slider.at(i).value_(specs.at(i).unmap(data.at(i).at(4))); +string.put(i, SCStaticText( w, Rect.new(205, (30*i)+5, 95,20)).string_(data.at(i).at(4))); + +slider.at(i).action_({arg sl; +var val; val = specs.at(i).map(sl.value); string.at(i).string_(val); +lastval.put(i, val); //set associated variable to this value, bbcut code will poll this rather than the slider directly +}); + +}); + +w.front; + +bbcutter=BBCut2([ +CutBuf2(f,{lastval.at(10)}), +CutMixer(0,1.0,{ +if(lastval.at(5).coin, //restchance +{0}, +{ +rrand(lastval.at(6),1.0)} //amp variation +)}, +{rrand(-1.0*(lastval.at(7)),lastval.at(7))}) //panvariation +], +BBCutProc11.new( +{lastval.at(1)}, //cut +{lastval.at(9)}, //sdbeats +{lastval.at(2)}, //phrasebars +{lastval.at(8)}, //numrepeats +{lastval.at(3)}, //stutterchance +{lastval.at(4)} //stutterspeed +)); + +clock=ExternalClock(TempoClock(lastval[0])).play; + +bbcutter.play(clock); + +//update tempo ten times a second +SystemClock.sched(0.0,{clock.tempoclock.tempo_(lastval.at(0)); 0.1}); + +) + + + +//If you want fast varying time signatures set phrasebars=1, +//and provide interconnected functions updating barlength and sdiv. +( //default eighth note cutups +var sf,newbarl,phrasecount; + +phrasecount=0; + +BBCut2(CutBuf1(f),BBCutProc11.new( +{ +newbarl=[4.0,3.0,3.5].wrapAt(phrasecount); //4/4 then 3/4 then 7/8 looping +phrasecount=phrasecount+1; +newbarl*2 //always eighth note cuts +}, +{newbarl}, +1 +)).play(2.4); + +)+
The B equalization suite is based on the Second Order Section (SOS) biquad UGen.
in |
+ input signal to be processed. |
freq |
+ center frequency. WARNING: due to the nature of its implementation frequency values close to 0 may cause glitches and/or extremely loud audio artifacts! |
bw |
+ the bandwidth in octaves between -3 dB frequencies. |
mul | |
add |
s.boot; +( +z = { +BBandStop.ar( + SoundIn.ar([0,1]), + MouseX.kr(20, 20000, \exponential), + MouseY.kr(0.0, 10.0, \linear), // bw + 0.5); // mul +}.play) +z.release; + +( +z = { +BBandStop.ar( + WhiteNoise.ar ! 2, + MouseX.kr(20, 20000, \exponential), + MouseY.kr(0.0, 10.0, \linear), // bw + 0.1); // mul +}.play) +z.release; + +( +z = { // drill... +BBandStop.ar( + SinOsc.ar(1000) ! 2, + MouseX.kr(800, 1200, \exponential), + MouseY.kr(0.0, 10.0, \linear), // bw + 0.1); // mul +}.play) +z.release; + +( +z = { // nada +BBandStop.ar( + SinOsc.ar(1000) ! 2, + 1000, + MouseY.kr(0.0, 10.0, \linear), // bw + 0.5); // mul +}.play) +z.release;+ +
Convenience class to create and store assembly programs for the BetaBlocker VChip. +
See http://tai-studio.org/portfolio/chip-interpretations.html for details.
+creates a new BBlocker program: +
program |
+ Mixed array of instructions (Symbols) and Integers (will be interpreted either as instructions or as values for unary instructions like |
all instructions
all descriptions
print description for each instruction
search for command
identifier |
+ search term |
array of commands
find description for command
key |
+ command |
description string
fill Buffer with compiled code.
buffer |
+ buffer to be filled. |
the program
create a Buffer and fill it with conpiled code.
server |
+ instance of Server on which the Buffer is created |
action |
+ action to be evaluated after successful buffer creation |
the Buffer
array with 256
entries containing the codeified program.
force |
+ if |
compiled program as a LocalBuf object
play program.
rate |
+ rate of operation |
using |
+ instance of Server or Buffer. If a buffer is given, this is used to load the program into. |
leak |
+ if |
vol |
+ attenuation |
force |
+ force re-filling of collection with program (plus possible randomness) |
Synth evaluating and playing the program.
server playback.
rate |
+ rate of operation |
using |
+ instance of Server or Buffer. If a buffer is given, this is used to load the program into. |
leak |
+ if |
vol |
+ attenuation |
force |
+ force re-filling of collection with program (plus possible randomness) |
if true
, fill remaining space in collection/Buffer with random numbers between 0
and 255
.
run program and scope it.
rate |
+ rate of operation |
using |
+ instance of Server or Buffer. If a buffer is given, this is used to load the program into. |
leak |
+ if |
vol |
+ attenuation |
force |
+ force re-filling of collection with program (plus possible randomness) |
run program and plot its results.
rate |
+ rate of operation |
using |
+ instance of Server or Buffer. If a buffer is given, this is used to load the program into. |
leak |
+ if |
duration |
+ time to run in seconds. |
force |
+ force re-filling of collection with program (plus possible randomness) |
decompile content of collection.
force |
+ if |
program
Band Limited ImPulse generator. All harmonics have equal amplitude. This is the equivalent of 'buzz' in MusicN languages. +
Synth-O-Matic (1990) had an impulse generator called blip, hence that name here rather than 'buzz'. +
It is improved from other implementations in that it will crossfade in a control period when the number of harmonics changes, so that there are no audible pops. It also eliminates the divide in the formula by using a 1/sin table (with special precautions taken for 1/0). The lookup tables are linearly interpolated for better quality.
freq |
+ Frequency in Hertz. |
numharm |
+ Number of harmonics. This may be lowered internally if it would cause aliasing. |
mul | |
add |
Generates noise whose spectrum falls off in power by 6 dB per octave.
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
compare:
{ BrownNoise.ar(0.1) }.play; +{ WhiteNoise.ar(0.1) }.play;+ +
brownian noise as a frequency modulator:
{ SinOsc.ar(BrownNoise.ar(100, 200)) * 0.1 }.play;+ +
filtered brown noise:
{ BPF.ar(BrownNoise.ar(0.1.dup), MouseX.kr(40, 17000, 1), 0.2) }.play;+ +
//SLUGens released under the GNU GPL as extensions for SuperCollider 3, by Nick Collins, http://composerprogrammer.com/index.html
Euler ODE solver implementation of the Brusselator equations (http://en.wikipedia.org/wiki/Brusselator). +
x' = x^2*y - (mu+1)*x + gamma +
y' = -x^2*y + mu*x +
All inputs can have .kr rate UGens plugged in. +
Nonlinear oscillators can blow up, treat with caution. This one is relatively stable however, converging to a fixed point, or a limit cycle, in the upper positive quadrant. for some reasonable values. Just be careful if mu gets much bigger than gamma (though making it larger is necessary to get some chaotic oscillation behaviour); you can retrigger to get back to normal, and keep the rate lower to avoid Euler integration blow-ups. You may just need to scale and push down around zero to avoid a DC offset. Fixed point is at (x,y) = (gamma, mu/gamma)
reset |
+ If > 0.0, restart with new initial conditions sampled from initx, inity |
rate |
+ update rate for a sample step |
mu |
+ equation constant. Set mu > (gamma**2 + 1.0) for the more fun limit cycle regions |
gamma |
+ equation constant |
initx |
+ reset value for x |
inity |
+ reset value for y |
//listen to x and y output +{Out.ar(0,Brusselator.ar(Impulse.kr(MouseY.kr(1,50,'exponential')),MouseX.kr(0.01,1,'exponential')))}.play + +//avoid DC offset (default gamma is 1.0, listening to x only, so take off gamma +{Out.ar(0,Pan2.ar((Brusselator.ar(Impulse.kr(MouseY.kr(1,50,'exponential')),MouseX.kr(0.01,1,'exponential')))[0] - 1.0) )}.play + + +//fun time, stable oscillation with these parameters +{var mu = MouseY.kr(1.0,1.5); Out.ar(0,Pan2.ar(Brusselator.ar(0.0,MouseX.kr(0.01,0.1,'exponential'),mu,0.25)[0] ) )}.play + + +{ Out.ar(0,Pan2.ar(Brusselator.ar(Impulse.kr(10),MouseX.kr(0.01,0.1,'exponential'),1.5,0.25,MouseY.kr(-1.0,1.0),0.0)[0]) )}.play + + +{ Out.ar(0,Brusselator.ar(Impulse.kr(MouseY.kr(1,500,'exponential')),MouseX.kr(0.01,0.1,'exponential'),1.15,0.5,0.5,1.0) )}.play+ +
Returns a ratio by which the playback of a soundfile is to be scaled.
bufnum |
+ Buffer index. |
a ratio by which the playback of a soundfile is to be scaled.
.ir
method is not the safest choice. Since a buffer can be reallocated at any time, using .ir
will not track the changes.b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); + +( +x = { arg rate=1; + BufRd.ar(1, b, Phasor.ar(0, BufRateScale.kr(b) * rate, 0, BufFrames.kr(b))) +}.play; +)+
A Buffer object is a client-side abstraction for a server-side buffer. (SuperCollider's server-client architecture is a common source of confusion when working with Buffer objects, so please see Client vs Server.) +
A buffer is most often used to hold sampled audio, such as a soundfile loaded into memory, but can be used to hold other types of data as well. Technically speaking, a buffer on the server is a globally available, multichannel array of 32-bit floating-point numbers. It also has an associated sample rate, represented in Hertz as a 64-bit float. +
The Buffer class encapsulates a number of common tasks, OSC messages, and capabilities related to server-side buffers – see the examples lower down this document for many examples of using Buffers for sound playback and recording. +
Buffers are commonly used with PlayBuf, RecordBuf, DiskIn, DiskOut, BufWr, BufRd, and other UGens. (See their individual help files for more examples.) Buffers can be freed or altered even while being accessed. See Server Architecture for some technical details. +
Buffer objects should not be created or modified within a SynthDef. If this is needed, see LocalBuf.
Although the number of buffers on a server is set at the time it is booted, memory must still be allocated within the server app before they can hold values. (At boot time all buffers have a size of 0.) +
Server-side buffers are identified by number, starting from 0. When using Buffer objects, buffer numbers are automatically allocated from the Server's bufferAllocator. When you call .free
on a Buffer object it will release the buffer's memory on the server, and free the buffer number for future reallocation. See ServerOptions for details on setting the number of available buffers.
+
Normally you should not need to supply a buffer number (see Buffer: *alloc). You should only do so if you are sure you know what you are doing. Similarly, in normal use you should not need to access the buffer number, since instances of Buffer can be used directly as UGen inputs or Synth args.
Multichannel buffers interleave their data. Thus the actual number of available values when requesting or setting values by index using methods such as set, setn, get, getn
, etc., is equal to numFrames * numChannels
. Indices start at 0 and go up to (numFrames * numChannels) - 1
. In a two channel buffer for instance, index 0 will be the first value of the first channel, index 1 will be the first value of the second channel, index 2 will be the second value of the first channel, and so on.
+
In some cases it is simpler to use multiple single channel buffers instead of a single multichannel one.
Many buffer operations (such as reading and writing files) are asynchronous, meaning that they will take an arbitrary amount of time to complete. Asynchronous commands are passed to a background thread on the server so as not to steal CPU time from the audio synthesis thread. Since they can last an arbitrary amount of time it is convenient to be able to specify something else that can be done immediately on completion. The ability to do this is implemented in two ways in Buffer's various methods: completion messages and action functions. +
A completion message is a second OSC command which is included in the message which is sent to the server. (See Node Messaging for a discussion of OSC messages.) The server will execute this immediately upon completing the first command. An action function is a Function which will be evaluated when the client receives the appropriate reply from the server, indicating that the previous command is done. Action functions are therefore inherently more flexible than completion messages, but slightly less efficient due to the small amount of added latency involved in message traffic. Action functions are passed the Buffer object as an argument when they are evaluated. +
With Buffer methods that take a completion message, it is also possible to pass in a function that returns an OSC message. As in action functions this will be passed the Buffer as an argument. It is important to understand however that this function will be evaluated after the Buffer object has been created (so that its bufnum and other details are accessible), but before the corresponding message is sent to the server.
Many of the methods below have two versions: a regular one which sends its corresponding message to the server immediately, and one which returns the message in an Array so that it can be added to a bundle. It is also possible to capture the messages generated by the regular methods using Server's automated bundling capabilities. See Server and Bundled Server Messages for more details.
Create and return a Buffer and immediately allocate the required memory on the server. The buffer's values will be initialised to 0.0.
server |
+ The server on which to allocate the buffer. The default is the default Server. |
numFrames |
+ The number of frames to allocate. Actual memory use will correspond to numFrames * numChannels. |
numChannels |
+ The number of channels for the Buffer. The default is 1. |
completionMessage |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
bufnum |
+ An explicitly specified buffer number. Supplying a number bypasses the server's buffer allocator, which can lead to conflicts. The best practice is to leave this unset and let the buffer number allocator choose the next available bufnum. |
// Allocate 8 second stereo buffer +s.boot; +b = Buffer.alloc(s, s.sampleRate * 8.0, 2); +b.free;+
Allocates a range of consecutively-numbered buffers, for use with UGens like VOsc and VOsc3 that require a contiguous block of buffers, and returns an array of corresponding Buffer objects.
numBufs |
+ The number of consecutively indexed buffers to allocate. |
server |
+ The server on which to allocate the buffers. The default is the default Server. |
numFrames |
+ The number of frames to allocate in each buffer. Actual memory use will correspond to numFrames * numChannels. |
numChannels |
+ The number of channels for each buffer. The default is 1. |
completionMessage |
+ A valid OSC message or a Function which will return one. A Function will be passed each Buffer and its index in the array as arguments when evaluated. |
bufnum |
+ An explicitly specified buffer number. Supplying a number bypasses the server's buffer allocator, which can lead to conflicts. The best practice is to leave this unset and let the buffer number allocator choose the next available bufnum. |
N.B. You must treat the array of Buffers as a group. Freeing them individually or reusing them can result in allocation errors. You should free all Buffers in the array at the same time by iterating over it with do.
s.boot; +// allocate an array of Buffers and fill them with different harmonics +( +b = Buffer.allocConsecutive(8, s, 4096, 1, { |buf, i| + buf.sine1Msg((1..((i+1)*6)).reciprocal) // completion Messages +}); + +a = { VOsc.ar(SinOsc.kr(0.5, 0).range(b.first.bufnum + 0.1, b.last.bufnum - 0.1), + [440, 441], 0, 0.2) }.play; +) +a.free; + +// iterate over the array and free it +b.do(_.free);+
Allocate a buffer and immediately read a soundfile into it. This method sends a query message as a completion message so that the Buffer's instance variables will be updated automatically.
server |
+ The server on which to allocate the buffer. |
path |
+ A String representing the path of the soundfile to be read. |
startFrame |
+ The first frame of the soundfile to read. The default is 0, which is the beginning of the file. |
numFrames |
+ The number of frames to read. The default is -1, which will read the whole file. |
action |
+ A Function to be evaluated once the file has been read and this Buffer's instance variables have been updated. The function will be passed this Buffer as an argument. |
bufnum |
+ An explicitly specified buffer number. Supplying a number bypasses the server's buffer allocator, which can lead to conflicts. The best practice is to leave this unset and let the buffer number allocator choose the next available bufnum. |
N.B. You cannot rely on the buffer's instance variables being instantly updated, as there is a small amount of latency involved. action will be evaluated upon receipt of the reply to the query, so use this in cases where access to instance variables is needed.
// read a soundfile +s.boot; +p = Platform.resourceDir +/+ "sounds/a11wlk01.wav"; +b = Buffer.read(s, p); + +// now play it +( +x = SynthDef(\help_Buffer, { arg out = 0, bufnum; + Out.ar( out, + PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum)) + ) +}).play(s,[\bufnum, b]); +) +x.free; b.free; + +// with an action function +// note that the vars are not immediately up-to-date +( +b = Buffer.read(s, p, action: { arg buffer; + ("After update:" + buffer.numFrames).postln; + x = { PlayBuf.ar(1, buffer, BufRateScale.kr(buffer)) }.play; +}); +("Before update:" + b.numFrames).postln; +) +x.free; b.free;+
As *read above, but takes an Array of channel indices to read in, allowing one to read only the selected channels.
server |
+ The server on which to allocate the buffer. |
path |
+ A String representing the path of the soundfile to be read. |
startFrame |
+ The first frame of the soundfile to read. The default is 0, which is the beginning of the file. |
numFrames |
+ The number of frames to read. The default is -1, which will read the whole file. |
channels |
+ An Array of channels to be read from the soundfile. Indices start from zero. These will be read in the order provided. |
action |
+ A Function to be evaluated once the file has been read and this Buffer's instance variables have been updated. The function will be passed this Buffer as an argument. |
bufnum |
+ An explicitly specified buffer number. Supplying a number bypasses the server's buffer allocator, which can lead to conflicts. The best practice is to leave this unset and let the buffer number allocator choose the next available bufnum. |
s.boot; +// first a standard read so we can see what's in the file +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/SinedPink.aiff"); +// Platform.resourceDir +/+ "sounds/SinedPink.aiff" contains SinOsc on left, PinkNoise on right +b.plot; +b.free; + +// Now just the sine +b = Buffer.readChannel(s, Platform.resourceDir +/+ "sounds/SinedPink.aiff", channels: [0]); +b.plot; +b.free; + +// Now just the pink noise +b = Buffer.readChannel(s, Platform.resourceDir +/+ "sounds/SinedPink.aiff", channels: [1]); +b.plot; +b.free; + +// Now reverse channel order +b = Buffer.readChannel(s, Platform.resourceDir +/+ "sounds/SinedPink.aiff", channels: [1, 0]); +b.plot; +b.free;+
As *read above, but without the automatic update of instance variables. Call updateInfo
(see below) to update the vars.
server |
+ The server on which to allocate the buffer. |
path |
+ A String representing the path of the soundfile to be read. |
startFrame |
+ The first frame of the soundfile to read. The default is 0, which is the beginning of the file. |
numFrames |
+ The number of frames to read. The default is -1, which will read the whole file. |
bufnum |
+ An explicitly specified buffer number. Supplying a number bypasses the server's buffer allocator, which can lead to conflicts. The best practice is to leave this unset and let the buffer number allocator choose the next available bufnum. |
completionMessage |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
// with a completion message +s.boot; +( +SynthDef(\help_Buffer,{ arg out=0, bufnum; + Out.ar( out, + PlayBuf.ar(1,bufnum,BufRateScale.kr(bufnum)) + ) +}).add; + +y = Synth.basicNew(\help_Buffer); // not sent yet +b = Buffer.readNoUpdate(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav", + completionMessage: { arg buffer; + // synth add its s_new msg to follow + // after the buffer read completes + y.newMsg(s,[\bufnum, buffer],\addToTail) + }); +) +// note vars not accurate +b.numFrames; // nil +b.updateInfo; +b.numFrames; // 188893 +// when done... +y.free; +b.free;+
Allocate a buffer and preload a soundfile for streaming in using DiskIn.
server |
+ The server on which to allocate the buffer. |
path |
+ A String representing the path of the soundfile to be read. |
startFrame |
+ The frame of the soundfile that DiskIn will start playing at. |
numChannels |
+ The number of channels in the soundfile. |
bufferSize |
+ This must be a multiple of (2 * the server's block size). 32768 is the default and is suitable for most cases. |
completionMessage |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
s.boot; +( +SynthDef(\help_Buffer_cue,{ arg out=0,bufnum; + Out.ar(out, + DiskIn.ar( 1, bufnum ) + ) +}).add; +) + +( +s.makeBundle(nil, { + b = Buffer.cueSoundFile(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav", 0, 1); + y = Synth(\help_Buffer_cue, [\bufnum, b], s); +}); +) +b.free; y.free;+
Load a large collection into a buffer on the server. Returns a Buffer object.
server |
+ The server on which to create the buffer. |
collection |
+ A subclass of Collection (i.e. an Array) containing only floats and integers. Multi-dimensional arrays will not work. |
numChannels |
+ The number of channels that the buffer should have. Note that buffers interleave multichannel data. You are responsible for providing an interleaved collection if needed. Multi-dimensional arrays will not work. |
action |
+ A Function to be evaluated once the file has been read and this Buffer's instance variables have been updated. The function will be passed this Buffer as an argument. |
This is accomplished through writing the collection to a SoundFile and loading it from there. For this reason this method will only work with a server on your local machine. For a remote server use sendCollection
, below. The file is automatically deleted after loading. This allows for larger collections than setn, below, and is in general the safest way to get a large collection into a buffer. The sample rate of the buffer will be the sample rate of the server on which it is created.
s.boot; +( +a = FloatArray.fill(44100 * 5.0, {1.0.rand2}); // 5 seconds of noise +b = Buffer.loadCollection(s, a); +) + +// test it +b.get(20000,{|msg| (msg == a[20000]).postln}); +// play it +x = { PlayBuf.ar(1, b, BufRateScale.kr(b), loop: 0) * 0.5 }.play; +b.free; x.free; + +// interleave a multi-dimensional array +( +l = Signal.sineFill(16384, Array.fill(200, {0}).add(1)); +r = Array.fill(16384, {1.0.rand2}); +m = [Array.newFrom(l), r]; // a multi-dimensional array +m = m.lace(32768); // interleave the two collections +b = Buffer.loadCollection(s, m, 2, {|buf| + x = { PlayBuf.ar(2, buf, BufRateScale.kr(buf), loop: 1) * 0.5 }.play; +}); +) +b.plot; +x.free; b.free;+
Stream a large collection into a buffer on the server using multiple setn messages. Returns a Buffer object.
server |
+ The server on which to create the buffer. |
collection |
+ A subclass of Collection (i.e. an Array) containing only floats and integers. Multi-dimensional arrays will not work. |
numChannels |
+ The number of channels that the buffer should have. Note that buffers interleave multichannel data. You are responsible for providing an interleaved collection if needed. Multi-dimensional arrays will not work. See the example in *loadCollection above, to see how to do this. |
wait |
+ An optional wait time between sending setn messages. In a high traffic situation it may be safer to set this to something above zero, which is the default. |
action |
+ A Function to be evaluated once the file has been read and this Buffer's instance variables have been updated. The function will be passed this Buffer as an argument. |
This allows for larger collections than setn, below. This is not as safe as *loadCollection above, but will work with servers on remote machines. The sample rate of the buffer will be the sample rate of the server on which it is created.
s.boot; +( +a = Array.fill(2000000,{ rrand(0.0,1.0) }); // a LARGE collection +b = Buffer.sendCollection(s, a, 1, 0, {arg buf; "finished".postln;}); +) +b.get(1999999, {|msg| (msg == a[1999999]).postln}); +b.free;+
As *read above, but gives you a load dialog window to browse for a file. Cocoa and SwingOSC compatible.
server |
+ The server on which to allocate the buffer. |
startFrame |
+ The first frame of the soundfile to read. The default is 0, which is the beginning of the file. |
numFrames |
+ The number of frames to read. The default is -1, which will read the whole file. |
action |
+ A Function to be evaluated once the file has been read and this Buffer's instance variables have been updated. The function will be passed this Buffer as an argument. |
bufnum |
+ An explicitly specified buffer number. Supplying a number bypasses the server's buffer allocator, which can lead to conflicts. The best practice is to leave this unset and let the buffer number allocator choose the next available bufnum. |
s.boot; +( +b = Buffer.loadDialog(s, action: { arg buffer; + x = { PlayBuf.ar(buffer.numChannels, buffer, BufRateScale.kr(buffer)) }.play; +}); +) +x.free; b.free;+
Create and return a new Buffer object, without immediately allocating the corresponding memory on the server. This combined with 'message' methods can be flexible with bundles.
server |
+ The server on which to allocate the buffer. The default is the default Server. |
numFrames |
+ The number of frames to allocate. Actual memory use will correspond to numFrames * numChannels. |
numChannels |
+ The number of channels for the Buffer. The default is 1. |
bufnum |
+ An explicitly specified buffer number. Supplying a number bypasses the server's buffer allocator, which can lead to conflicts. The best practice is to leave this unset and let the buffer number allocator choose the next available bufnum. |
s.boot; +b = Buffer.new(s, 44100 * 8.0, 2); +c = Buffer.new(s, 44100 * 4.0, 2); +b.query; // numFrames = 0 +s.sendBundle(nil, b.allocMsg, c.allocMsg); // sent both at the same time +b.query; // now it's right +c.query; +b.free; c.free;+
To assist with automatic updates of buffer information (see updateInfo
and read
), buffer objects are cached in a collection associated with the Server object hosting the buffers. Freeing a buffer removes it from the cache; quitting the server clears all the cached buffers. (This also occurs if the server crashes unexpectedly.)
+
You may access cached buffers using the following methods. +
It may be simpler to access them through the server object:
myServer.cachedBufferAt(bufnum) +myServer.cachedBuffersDo(func) + +b = Buffer.alloc(s, 2048, 1); +Buffer.cachedBufferAt(s, 0); // assuming b has bufnum 0 +s.cachedBufferAt(0); // same result +s.cachedBuffersDo({ |buf| buf.postln });+
Access a buffer by its number.
Iterate over all cached buffers. The iteration is not in any order, but will touch all buffers.
The following variables have getter methods.
Returns the Buffer's Server object.
Returns the buffer number of the corresponding server-side buffer. In normal use you should not need to access this value, since instances of Buffer can be used directly as UGen inputs or Synth args.
s.boot; +b = Buffer.alloc(s,44100 * 8.0,2); +b.bufnum.postln; +b.free;+
Returns the number of sample frames in the corresponding server-side buffer. Note that multichannel buffers interleave their samples, so when dealing with indices in methods like get and getn, the actual number of available values is numFrames * numChannels.
Returns the number of channels in the corresponding server-side buffer.
Returns the sample rate of the corresponding server-side buffer. +
Changing this value only changes what the buffer thinks its sample rate is. It does not resample the buffer's content.
Returns a string containing the path of a soundfile that has been loaded into the corresponding server-side buffer.
These methods allocate the necessary memory on the server for a Buffer previously created with *new.
completionMessage |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
s.boot; +b = Buffer.new(s, 44100 * 8.0, 2); +b.query; // numFrames = 0 +b.alloc; +b.query; // numFrames = 352800 +b.free;+
Read a soundfile into a buffer on the server for a Buffer previously created with *new. Note that this will not autoupdate instance variables. Call updateInfo
in order to do this.
argpath |
+ A String representing the path of the soundfile to be read. |
startFrame |
+ The first frame of the soundfile to read. The default is 0, which is the beginning of the file. |
numFrames |
+ The number of frames to read. The default is -1, which will read the whole file. |
completionMessage |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
s.boot; +b = Buffer.new(s); +b.allocRead(Platform.resourceDir +/+ "sounds/a11wlk01.wav"); +x = { PlayBuf.ar(1, b, BufRateScale.kr(b), loop: 1) * 0.5 }.play; +x.free; b.free;+
As -allocRead above, but allows you to specify which channels to read.
argpath |
+ A String representing the path of the soundfile to be read. |
startFrame |
+ The first frame of the soundfile to read. The default is 0, which is the beginning of the file. |
numFrames |
+ The number of frames to read. The default is -1, which will read the whole file. |
channels |
+ An Array of channels to be read from the soundfile. Indices start from zero. These will be read in the order provided. |
completionMessage |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
s.boot; +b = Buffer.new(s); +// read only the first channel (a Sine wave) of a stereo file +b.allocReadChannel(Platform.resourceDir +/+ "sounds/SinedPink.aiff", channels: [0]); +x = { PlayBuf.ar(1, b, BufRateScale.kr(b), loop: 1) * 0.5 }.play; +x.free; b.free;+
Read a soundfile into an already allocated buffer.
argpath |
+ A String representing the path of the soundfile to be read. |
fileStartFrame |
+ The first frame of the soundfile to read. The default is 0, which is the beginning of the file. |
numFrames |
+ The number of frames to read. The default is -1, which will read the whole file. |
bufStartFrame |
+ The index of the frame in the buffer at which to start reading. The default is 0, which is the beginning of the buffer. |
leaveOpen |
+ A boolean indicating whether or not the Buffer should be left 'open'. For use with DiskIn you will want this to be true, as the buffer will be used for streaming the soundfile in from disk. (For this the buffer must have been allocated with a multiple of (2 * synth block size). A common number is 32768 frames. cueSoundFile below, provides a simpler way of doing this.) The default is false which is the correct value for all other cases. |
action |
+ A Function to be evaluated once the file has been read and this Buffer's instance variables have been updated. The function will be passed this Buffer as an argument. |
(completionMessage) |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
construct the message for a read command. args are like those for read, except that last arg is completionMessage.
Note that if the number of frames in the file is greater than the number of frames in the buffer, it will be truncated. Note that readMsg will not auto-update instance variables. Call updateInfo in order to do this.
As -read above, but allows you to specify which channels to read.
argpath |
+ A String representing the path of the soundfile to be read. |
fileStartFrame |
+ The first frame of the soundfile to read. The default is 0, which is the beginning of the file. |
numFrames |
+ The number of frames to read. The default is -1, which will read the whole file. |
bufStartFrame |
+ The index of the frame in the buffer at which to start reading. The default is 0, which is the beginning of the buffer. |
leaveOpen |
+ A boolean indicating whether or not the Buffer should be left 'open'. For use with DiskIn you will want this to be true, as the buffer will be used for streaming the soundfile in from disk. (For this the buffer must have been allocated with a multiple of (2 * synth block size). A common number is 32768 frames. cueSoundFile below, provides a simpler way of doing this.) The default is false which is the correct value for all other cases. |
channels |
+ An Array of channels to be read from the soundfile. Indices start from zero. These will be read in the order provided. The number of channels requested must match this Buffer's numChannels. |
action |
+ A Function to be evaluated once the file has been read and this Buffer's instance variables have been updated. The function will be passed this Buffer as an argument. |
(completionMessage) |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
as above for single channel, with last arg being completionMessage.
A convenience method to cue a soundfile into the buffer for use with a DiskIn. The buffer must have been allocated with a multiple of (2 * the server's block size) frames. A common size is 32768 frames.
path |
+ A String representing the path of the soundfile to be read. |
startFrame |
+ The first frame of the soundfile to read. The default is 0, which is the beginning of the file. |
completionMessage |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
s.boot; +//create with cueSoundFile class method +b = Buffer.cueSoundFile(s, Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff", 0, 1); +x = { DiskIn.ar(1, b) }.play; +b.close; // must call close in between cueing +// now use like named instance method, but different arguments +b.cueSoundFile(Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff"); +// have to do all this to clean up properly! +x.free; b.close; b.free;+
Write the contents of the buffer to a file. See SoundFile for information on valid values for headerFormat and sampleFormat.
path |
+ A String representing the path of the soundfile to be written. If no path is given, Buffer writes into the default recording directory with a generic name. |
headerFormat |
+ A String. |
sampleFormat |
+ A String. |
numFrames |
+ The number of frames to write. The default is -1, which will write the whole buffer. |
startFrame |
+ The index of the frame in the buffer from which to start writing. The default is 0, which is the beginning of the buffer. |
leaveOpen |
+ A boolean indicating whether or not the Buffer should be left 'open'. For use with DiskOut you will want this to be true. The default is false which is the correct value for all other cases. |
completionMessage |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
Release the buffer's memory on the server and return the bufferID back to the server's buffer number allocator for future reuse.
completionMessage |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
Sets all values in this buffer to 0.0.
completionMessage |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
Set the value in the buffer at index to be equal to float. Additional pairs of indices and floats may be included in the same message.
Note that multichannel buffers interleave their sample data, therefore the actual number of available values is equal to numFrames * numChannels
. Indices start at 0.
s.boot; +b = Buffer.alloc(s, 4, 2); +b.set(0, 0.2, 1, 0.3, 7, 0.4); // set the values at indices 0, 1, and 7. +b.getn(0, 8, {|msg| msg.postln}); +b.free;+
Set a contiguous range of values in the buffer starting at the index startAt to be equal to the Array of floats or integers, values. The number of values set corresponds to the size of values. Additional pairs of starting indices and arrays of values may be included in the same message.
Note that multichannel buffers interleave their sample data, therefore the actual number of available values is equal to numFrames * numChannels
. You are responsible for interleaving the data in values if needed. Multi-dimensional arrays will not work. Indices start at 0.
+
N.B. The maximum number of values that you can set with a single setn message is 1633 when the server is using UDP as its communication protocol. Use -loadCollection and -sendCollection to set larger ranges of values.
s.boot; +b = Buffer.alloc(s,16); +b.setn(0, Array.fill(16, { rrand(0,1) })); +b.getn(0, b.numFrames, {|msg| msg.postln}); +b.setn(0, [1, 2, 3], 4, [1, 2, 3]); +b.getn(0, b.numFrames, {|msg| msg.postln}); +b.free;+
Load a large collection into this buffer. This is accomplished through writing the collection to a SoundFile and loading it from there. For this reason this method will only work with a server on your local machine. For a remote server use sendCollection, below. The file is automatically deleted after loading.
collection |
+ A subclass of Collection (i.e. an Array) containing only floats and integers. Multi-dimensional arrays will not work. |
startFrame |
+ The index of the frame at which to start loading the collection. The default is 0, which is the start of the buffer. |
action |
+ A Function to be evaluated once the file has been read and this Buffer's instance variables have been updated. The function will be passed this Buffer as an argument. |
This allows for larger collections than setn, above, and is in general the safest way to get a large collection into a buffer. The sample rate of the buffer will be the sample rate of the server on which it was created. The number of channels and frames will have been determined when the buffer was allocated. You are responsible for making sure that the size of collection is not greater than numFrames, and for interleaving any data if needed.
s.boot; +( +v = Signal.sineFill(128, 1.0/[1,2,3,4,5,6]); +b = Buffer.alloc(s, 128); +) +( +b.loadCollection(v, action: {|buf| + x = { PlayBuf.ar(buf.numChannels, buf, BufRateScale.kr(buf), loop: 1) + * 0.2 }.play; +}); +) +x.free; b.free; + +// interleave a multi-dimensional array +( +l = Signal.sineFill(16384, Array.fill(200, {0}).add(1)); +r = Array.fill(16384, {1.0.rand2}); +m = [Array.newFrom(l), r]; // a multi-dimensional array +m = m.lace(32768); // interleave the two collections +b = Buffer.alloc(s, 16384, 2); +) +( +b.loadCollection(m, 0, {|buf| + x = { PlayBuf.ar(2, buf, BufRateScale.kr(buf), loop: 1) * 0.5 }.play; +}); +) +b.plot; +x.free; b.free;+
Stream a large collection into this buffer using multiple setn messages.
collection |
+ A subclass of Collection (i.e. an Array) containing only floats and integers. Multi-dimensional arrays will not work. |
startFrame |
+ The index of the frame at which to start streaming in the collection. The default is 0, which is the start of the buffer. |
wait |
+ An optional wait time between sending setn messages. In a high traffic situation it may be safer to set this to something above zero, which is the default. |
action |
+ A Function to be evaluated once the file has been read and this Buffer's instance variables have been updated. The function will be passed this Buffer as an argument. |
This allows for larger collections than setn. This is not as safe as loadCollection, above, but will work with servers on remote machines. The sample rate of the buffer will be the sample rate of the server on which it is created.
s.boot; +( +a = Array.fill(2000000,{ rrand(0.0,1.0) }); +b = Buffer.alloc(s, 2000000); +) +b = b.sendCollection(a, action: {arg buf; "finished".postln;}); +b.get(1999999, {|msg| (msg == a[1999999]).postln}); +b.free;+
Send a message requesting the value in the buffer at index. action is a Function which will be passed the value as an argument and evaluated when a reply is received.
s.boot; +b = Buffer.alloc(s,16); +b.setn(0, Array.fill(16, { rrand(0.0, 1.0) })); +b.get(0, {|msg| msg.postln}); +b.free;+
Send a message requesting the a contiguous range of values of size count starting from index. action is a Function which will be passed the values in an Array as an argument and evaluated when a reply is received. See setn above for an example.
N.B. The maximum number of values that you can get with a single getn message is 1633 when the server is using UDP as its communication protocol. Use -loadToFloatArray and -getToFloatArray to get larger ranges of values.
Write the buffer to a file and then load it into a FloatArray.
index |
+ The index in the buffer to begin writing from. The default is 0. |
count |
+ The number of values to write. The default is -1, which writes from index until the end of the buffer. |
action |
+ A Function which will be passed the resulting FloatArray as an argument and evaluated when loading is finished. |
This is safer than getToFloatArray but only works with a server on your local machine. In general this is the safest way to get a large range of values from a server buffer into the client app.
s.boot; +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); +// same as Buffer.plot +b.loadToFloatArray(action: { arg array; a = array; {a.plot;}.defer; "done".postln;}); +b.free;+
Stream the buffer to the client using a series of getn messages and put the results into a FloatArray.
index |
+ The index in the buffer to begin writing from. The default is 0. |
count |
+ The number of values to write. The default is -1, which writes from index until the end of the buffer. |
wait |
+ The amount of time in seconds to wait between sending getn messages. Longer times are safer. The default is 0.01 seconds which seems reliable under normal circumstances. A setting of 0 is not recommended. |
timeout |
+ The amount of time in seconds after which to post a warning if all replies have not yet been received. the default is 3. |
action |
+ A Function which will be passed the resulting FloatArray as an argument and evaluated when all replies have been received. |
This is more risky than loadToFloatArray but does works with servers on remote machines. In high traffic situations it is possible for data to be lost. If this method has not received all its replies by timeout it will post a warning saying that the method has failed. In general use loadToFloatArray instead wherever possible.
s.boot; +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); +// like Buffer.plot +b.getToFloatArray(wait:0.01,action:{arg array; a = array; {a.plot;}.defer;"done".postln;}); +b.free;+
Normalizes the buffer so that the peak absolute value is newmax (which defaults to 1). If your buffer is in Wavetable format then set the asWavetable argument to true.
Starting at the index startAt, set the next numFrames to value. Additional ranges may be included in the same message.
Starting at the index srcStartAt, copy numSamples samples from this to the destination buffer buf starting at dstStartAt. If numSamples is negative, the maximum number of samples possible is copied. The default is start from 0 in the source and copy the maximum number possible starting at 0 in the destination.
Note: This method used to be called copy, but this conflicts with the method for object-copying. Therefore Buffer:copy is now intended to create a copy of the client-side Buffer object. Its use for copying buffer data on the server is deprecated. If you see a deprecation warning, the data will still be copied on the server and your code will still work, but you should update your code for the new method name.
s.boot; +( +SynthDef(\help_Buffer_copy, { + arg out = 0, buf; + Line.ar(0, 0, dur: BufDur.kr(buf), doneAction: Done.freeSelf); + Out.ar(out, PlayBuf.ar(1, buf)); +}).add; +) + +( +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); +c = Buffer.alloc(s, 120000); +) + +Synth(\help_Buffer_copy, [\buf, b]); + +// copy the whole buffer +b.copyData(c); +Synth(\help_Buffer_copy, [\buf, c]); + +// copy some samples +c.zero; +b.copyData(c, numSamples: 4410); +Synth(\help_Buffer_copy, [\buf, c]); + +// buffer "compositing" +c.zero; +b.copyData(c, numSamples: 4410); +b.copyData(c, dstStartAt: 4410, numSamples: 15500); +Synth(\help_Buffer_copy, [\buf, c]); + +b.free; +c.free;+
After using a Buffer with a DiskOut or DiskIn, it is necessary to close the soundfile. Failure to do so can cause problems.
completionMessage |
+ A valid OSC message or a Function which will return one. A Function will be passed this Buffer as an argument when evaluated. |
Plot the contents of the Buffer in a GUI window.
name |
+ The name of the resulting window. |
bounds |
+ An instance of Rect determining the size of the resulting view. |
minval |
+ the minimum value in the plot |
maxval |
+ the maximum value in the plot |
separately |
+ a boolean whether to use separate display ranges or a single shared range. |
s.boot; +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); +b.plot; +b.free;+
Plays the contents of the buffer on the server and returns a corresponding Synth.
loop |
+ A Boolean indicating whether or not to loop playback. If false the synth will automatically be freed when done. The default is false. |
mul |
+ A value by which to scale the output. The default is 1. |
s.boot; +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); +b.play; // frees itself +x = b.play(true); +x.free; b.free;+
Sends a b_query message to the server, asking for a description of this buffer. The results are posted to the post window. Does not update instance vars.
Sends a b_query message to the server, asking for a description of this buffer. Upon reply this Buffer's instance variables are automatically updated.
action |
+ A Function to be evaluated once the file has been read and this Buffer's instance variables have been updated. The function will be passed this Buffer as an argument. |
s.boot; +b = Buffer.readNoUpdate(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); +b.numFrames; // incorrect, shows nil +b.updateInfo({|buf| buf.numFrames.postln; }); // now it's right +b.free;+
These correspond to the various b_gen OSC Commands, which fill the buffer with values for use. See Server Command Reference for more details.
This is a generalized version of the commands below.
genCommand |
+ A String indicating the name of the command to use. See Server-Command-Reference for a list of valid command names. |
genArgs |
+ An Array containing the corresponding arguments to the command. |
normalize |
+ A Boolean indicating whether or not to normalize the buffer to a peak value of 1.0. The default is true. |
asWavetable |
+ A Boolean indicating whether or not to write to the buffer in wavetable format so that it can be read by interpolating oscillators. The default is true. |
clearFirst |
+ A Boolean indicating whether or not to clear the buffer before writing. The default is true. |
Fill this buffer with a series of sine wave harmonics using specified amplitudes.
amps |
+ An Array containing amplitudes for the harmonics. The first float value specifies the amplitude of the first partial, the second float value specifies the amplitude of the second partial, and so on. |
normalize |
+ A Boolean indicating whether or not to normalize the buffer to a peak value of 1.0. The default is true. |
asWavetable |
+ A Boolean indicating whether or not to write to the buffer in wavetable format so that it can be read by interpolating oscillators. The default is true. |
clearFirst |
+ A Boolean indicating whether or not to clear the buffer before writing. The default is true. |
s.boot; +( +b = Buffer.alloc(s, 512, 1); +b.sine1(1.0 / [1, 2, 3, 4], true, true, true); +x = { Osc.ar(b, 200, 0, 0.5) }.play; +) +x.free; b.free;+
Fill this buffer with a series of sine wave partials using specified frequencies and amplitudes.
freqs |
+ An Array containing frequencies (in cycles per buffer) for the partials. |
amps |
+ An Array containing amplitudes for the partials. This should contain the same number of items as freqs. |
normalize |
+ A Boolean indicating whether or not to normalize the buffer to a peak value of 1.0. The default is true. |
asWavetable |
+ A Boolean indicating whether or not to write to the buffer in wavetable format so that it can be read by interpolating oscillators. The default is true. |
clearFirst |
+ A Boolean indicating whether or not to clear the buffer before writing. The default is true. |
s.boot; +( +b = Buffer.alloc(s, 512, 1); +b.sine2([1.0, 3], [1, 0.5]); +x = { Osc.ar(b, 200, 0, 0.5) }.play; +) +x.free; b.free;+
Fill this buffer with a series of sine wave partials using specified frequencies, amplitudes, and initial phases.
freqs |
+ An Array containing frequencies (in cycles per buffer) for the partials. |
amps |
+ An Array containing amplitudes for the partials. This should contain the same number of items as freqs. |
phases |
+ An Array containing initial phase for the partials (in radians). This should contain the same number of items as freqs. |
normalize |
+ A Boolean indicating whether or not to normalize the buffer to a peak value of 1.0. The default is true. |
asWavetable |
+ A Boolean indicating whether or not to write to the buffer in wavetable format so that it can be read by interpolating oscillators. The default is true. |
clearFirst |
+ A Boolean indicating whether or not to clear the buffer before writing. The default is true. |
Fill this buffer with a series of Chebyshev polynomials, which can be defined as: cheby(n) = amplitude * cos(n * acos(x))
. To eliminate a DC offset when used as a waveshaper, the wavetable is offset so that the center value is zero.
+
Similar functionality can be found in Signal.chebyFill and Wavetable.chebyFill. If you require Chebyshev polynomials that do not include the offset compensation, it is recommended to use one of these.
amplitudes |
+ An Array containing amplitudes for the harmonics. The first float value specifies the amplitude for n = 1, the second float value specifies the amplitude for n = 2, and so on. |
normalize |
+ A Boolean indicating whether or not to normalize the buffer to a peak value of 1.0. The default is true. |
asWavetable |
+ A Boolean indicating whether or not to write to the buffer in wavetable format so that it can be read by interpolating oscillators. The default is true. |
clearFirst |
+ A Boolean indicating whether or not to clear the buffer before writing. The default is true. |
s.boot; +b = Buffer.alloc(s, 512, 1, {arg buf; buf.chebyMsg([1,0,1,1,0,1])}); +( +x = { + Shaper.ar( + b, + SinOsc.ar(300, 0, Line.kr(0,1,6)), + 0.5 + ) +}.play; +) +x.free; b.free;+
The clientside representation of an audio or control bus on a server. Encapsulates all the OSC messages a Bus can receive. Manages allocation and deallocation of bus indices so that you don't need to worry about conflicts. The number of control busses, audio busses, and input and output busses is fixed and cannot be changed after the server has been booted. +
For more information see Client vs Server and Server Architecture. +
Note that using the Bus class to allocate a multichannel bus does not 'create' a multichannel bus, but rather simply reserves a series of adjacent bus indices with the bus' Server object's bus allocators. abus.index
simply returns the first of those indices. When using a Bus with an In or Out ugen there is nothing to stop you from reading to or writing from a larger range, or from hardcoding to a bus that has been allocated. You are responsible for making sure that the number of channels match and that there are no conflicts.
+
Bus objects should not be created or modified within a SynthDef.
n
bus indices are reserved for hardware output and input, wheren = server.options.numOutputBusChannels + server.options.numInputBusChannels+ +
0 .. (numOutputBusChannels - 1)
numOutputBusChannels .. (numOutputBusChannels + numInputBusChannels - 1)
numOutputBusChannels + numInputBusChannels
Do not try to use hardware I/O buses as private buses.
Allocate a control bus on the server.
server |
+ The Server. Defaults to Server.default. |
numChannels |
+ Number of channels to allocate |
Allocate an audio bus on the server.
server |
+ The Server. Defaults to Server.default. |
numChannels |
+ Number of channels to allocate |
Allocate a bus of either rate as specified by rate
.
rate |
+ Rate symbol: \control or \audio |
server |
+ The Server. Defaults to Server.default. |
numChannels |
+ Number of channels to allocate |
This method does not allocate a bus index, but assumes that you already have allocated the appropriate bus index and can supply it yourself.
This method creates a new Bus that is a subset of the bus. The bus will be at the same rate as the input bus. offset is the index into the given bus. numChannels is the desired number of channels. If the combination of offset and numChannels is outside the input bus' range, an error will be thrown.
Get the Bus' index. Normally you should not need to do this since instances of Bus can be passed directly as UGen inputs or Synth args.
Return the bus' indices to the server's bus allocator so they can be reallocated.
Get the Bus' rate. This is a symbol, either \control or \audio.
Get the Bus' number of channels.
Get the Bus' server object.
a symbol consisting of the letter 'c' or 'a' (for control or audio) followed by the bus's index. This may be used when setting a synth node's control inputs to map the input to the control bus.
See the Node help file for more information on mapping controls to buses.
( +a = Bus.control(s, 1).set(440); +b = Bus.control(s, 1).set(0.01); +) +( +SynthDef(\rlpf, { |ffreq, rq| + Out.ar(0, RLPF.ar(WhiteNoise.ar(0.2), ffreq, rq)) +}).play(s, [\ffreq, a.asMap, \rq, b.asMap]); +)+
Get a new Bus that is a subset of this bus (see newFrom
).
The following commands apply only to control buses and are asynchronous. For synchronous access to control busses please consult Synchronous%20Control%20Bus%20Methods.
Set all channels to this float value. This command is asynchronous.
A list of values for each channel of the control bus. The list of values supplied should not be greater than the number of channels. This command is asynchronous.
As set but takes an array as an argument.
Get the current value of this control bus. This command is asynchronous.
action |
+ a function that will be evaluated when the server responds, with the current value of the bus passed as an argument. This will be a float for a single channel bus, or an array of floats for a multichannel bus. The default action posts the bus values. |
Get the current values of this control bus. This command is asynchronous.
count |
+ the number of channels to read, starting from this bus' first channel. |
action |
+ a function that will be evaluated when the server responds, with the current values of the bus in an array passed as an argument. |
Synchronous access to control busses only works for servers with a shared memory interface. You can check with hasShmInterface if the server provides these methods.
Get the current value of this control bus. This command is synchronous.
Value of the control bus.
Get the current values of this control bus. This command is synchronous.
count |
+ The number of channels to read, starting from this bus' first channel. |
Array of values.
A list of values for each channel of the control bus. The list of values supplied should not be greater than the number of channels. This command is synchronous.
As setSynchronous but takes an array as an argument.
set the bus value(s) beginning at offset. asynchronous.
set the bus to the list of values supplied. asynchronous.
set the bus values by pairs of index, value, ... asynchronous
use a bus like a UGen. The numChannels and offset arguments can be used to get a subset of the bus.
By default, all the bus channels are used. E.g. in an 8 channel bus,
b.kr
will return an In ugen reading from all the 8 channels of the bus;b.kr(4)
will return the first four channels, andb.kr(2, 5)
will return two channels, starting from the bus's channels at index 5 and 6.Returns a msg of the type /c_get for use in osc bundles.
Returns a msg of the type /c_getn for use in osc bundles.
count |
+ the number of channels to read, starting from this bus' first channel. The default is this bus' numChannels. |
Returns a msg of the type /c_set for use in osc bundles.
Returns a msg of the type /c_setn for use in osc bundles.
values |
+ an array of values to which adjacent channels should be set, starting at this bus' first channel. |
Returns a msg of the type /c_fill for use in osc bundles.
value |
+ the value to which this bus' channels will be set. |
Displays a bus in a Stethoscope, using the Bus' -numChannels, -index, and -rate properties.
s.boot +b=Bus.audio(s, 2); +a={SinOsc.ar([330,440], 0, 0.4)}.play(s, b) //you won't hear this if you only have two channels +b.scope + +a.free; +b.free;+
s.boot; + +( +// something to play with +SynthDef(\help_Bus, { arg out=0,ffreq=100; + var x; + x = RLPF.ar(LFPulse.ar(SinOsc.kr(0.2, 0, 10, 21), [0,0.1], 0.1), + ffreq, 0.1) + .clip2(0.4); + Out.ar(out, x); +}).add; + +) + +x = Synth(\help_Bus); + +// get a bus +b = Bus.control(s); + +// map the synth's second input (ffreq) to read +// from the bus' output index +x.map(1, b); + +// By setting the bus' value you send a /c_fill message +// to each channel of the bus setting it to supplied float value +b.value = 100; +b.value = 1000; +b.value = 30; + +// Since this is a single channel bus this has the same effect +b.set(300); +b.numChannels.postln; + +// multi-channel: b.set(300,350); +// Get the current value. This is asynchronous so you can't rely on it happening immediately. +( +a = "waiting"; +b.get({arg value; a = value; ("after the server responds a is set to:" + a).postln;}); +("a is now:" + a).postln; +) + +x.free; + + // buses can also be used with kr or ar like UGens: +( + +SynthDef(\help_Bus, { + var ffreq = b.kr; + Out.ar(0, + RLPF.ar( + LFPulse.ar(SinOsc.kr(0.2, 0, 10, 21), [0,0.1], 0.1), + ffreq, 0.1 + ).clip2(0.4) + ); +}).play; +) + +b.free; // release it so it may be reallocated! + + +// using and setting multichannel buses: + +( +b = Bus.control(s, 4); + +x = SynthDef(\helpBusMulti, { + var freqs = b.kr; + Out.ar(0, Splay.ar(SinOsc.ar(freqs) * Decay2.ar(Dust.ar(10 ! 4), 0.001, 0.1)) * 0.5); +}).play; +) + + // set bus beginning at index 0: + // none of these methods checks whether the indexes remain + // within the bus's range. + +b.set(234, 345, 456, 567); +b.getn; +b.setn([100, 200, 300, 400]); +b.getn; + + // get to individual channels +b.setAt(3, 500); +b.getn; +b.setAt(1, 300, 400); +b.getn; +b.setnAt(1, [250, 350]); +b.getn; + // set by pairs of index, value ... +b.setPairs(3, 600, 0, 200); +b.getn; + +b.set(300, 500, 700, 900); + +( // just get the first 2 channels +x = SynthDef(\helpBusMulti, { + Out.ar(0, SinOsc.ar(b.kr(2)) * 0.2); +}).play; +) +b.set(300, 303); +x.free; + +( // just channels[[2, 3]]; +y = SynthDef(\helpBusMulti, { + Out.ar(0, LFNoise2.ar(b.kr(2, 2)) * 0.2); +}).play; +) +b.setAt(2, 1200); +b.setAt(3, 2400); + +y.free; +b.free;+ +
Chorusing wavetable lookup oscillator. Produces sum of two signals at + +
Due to summing, the peak amplitude is not the same as the wavetable and can be twice of that.
bufnum |
+ The number of a buffer filled in wavetable format. |
freq |
+ Frequency in Hertz. |
beats |
+ Beat frequency in Hertz. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
CSVFileReader reads comma-separated text files into 2D arrays line by line. +
For tab delimited files use TabFileReader. For semi-colon-delimited files use SemiColonFileReader. For space-delimited files, or custom delimiters, use FileReader.
"ChaosGen" is an abstract class - in other words, a class that you do not use directly. Instead, use one of its subclasses. Various things inherit from this abstract class, including HenonN, LinCongL, LatoocarfianL, GbmanL, CuspL, StandardL, and more. +
These chaotic UGens generally each represent a deterministic set of equations, which can take different starting parameters. The equations define a system whose evolution over time is highly sensitive to initial conditions, and can exhibit highly intricate behaviour. +
To learn more, start here: http://en.wikipedia.org/wiki/Chaos_theory +
To see all classes which derive from the ChaosGen class, run this line:
ChaosGen.allSubclasses.do(_.postln)+ +
A chromagram, measuring the energy at particular chroma within an nTET tuning system. +
Possible extension: TODO: Could have arbitrary tuning systems if precalculated the exact fft bin + interpolation data.
fft |
+ input fft chain, that is, from an FFT UGen |
fftsize |
+ FFT size, required for initialisation |
n |
+ Equal divisions of an octave, e.g. n=12 is 12TET, 12 steps in an octave |
tuningbase |
+ Base frequency or tuning; will correspong to index 0 in results (conventionally, this would be a 'C' in 12TET, but its an arbitrary reference) |
octaves |
+ Number of octaves considered from tuning base up |
integrationflag |
+ Whether to integrate from frame to frame, off by default |
coeff |
+ Coefficient of integration |
octaveratio |
+ Default of 2 is a 'normal' octave; other ratios are possible, e.g. Bohlen-Pierce scale uses 13 equal divisions over the ratio of 3 for a 'tritave' |
perframenormalize |
+ Defaults to off, but if set to 1 will normalize each frame with respect to itself (as long as there is non-negligible power), potentially providing a more robust measure for comparison between frames (each frame will have then a normalized distribution over chroma, independent of volume) |
( +{ + +var in, fft, chroma; + +//in = SinOsc.ar(440,0,0.1); +in= SoundIn.ar; + +fft = FFT(LocalBuf(2048), in); + +chroma=Chromagram.kr(fft); + +chroma.poll; + +Out.ar(0,Pan2.ar(in)); +}.play +) + + + +//n TET display + +n= 12; //19, 24 + +( +x = { + +var in, fft, chroma; + +//in = SinOsc.ar(440,0,0.1); +in= SoundIn.ar; + +fft = FFT(LocalBuf(2048), in); + +chroma=Chromagram.kr(fft, 2048, n); + +//chroma=Chromagram.kr(fft, 2048, n, 36.midicps, 7, 1, 0.9); + +Out.kr(0,chroma); +}.play; + +c= Bus.new('control', 0, n); + +) + + + +//poll coefficients snapshot +c.getn(n,{arg val; {val.plot;}.defer}); + + + +//Continuous graphical display of Chromagram values; free routine before closing window + +( +var ms; + +w=Window.new((n.asString)++" chroma coefficients", Rect(200,400,n*20+50,300)); + +ms= MultiSliderView.new(w, Rect(10,10,n*20,280)); + +ms.value_(Array.fill(n,0.0)); +ms.valueThumbSize_(20.0); +ms.indexThumbSize_(20.0); +ms.gap_(0); + +w.front; + +r= { + +inf.do{ + +c.getn(n,{arg val; {ms.value_(val)}.defer}); + +0.04.wait; //25 frames per second +}; + +}.fork; + +w.onClose = { +r.stop; +c.free; +x.free; +}; + +) + + + +b.free; + + + +//Bohlen-Pierce over 3 tritaves, normalized results per frame +( +{ + +var in, fft, chroma; + +//in = SinOsc.ar(440,0,0.1); +in= SoundIn.ar; + +fft = FFT(LocalBuf(2048), in); + +chroma=Chromagram.kr(fft,2048,13,octaves:3,octaveratio:3,perframenormalize:1); + +chroma.poll; + +Out.ar(0,Pan2.ar(in)); +}.play +)+ +
Clock is an abstract class: it only defines an abstract set of methods that all clocks should implement. See its subclasses: SystemClock, TempoClock, AppClock for specific implementations.
A Clock keeps track of time and allows tasks to be scheduled for some time in the future (e.g. using sched, schedAbs or play methods). A task can be any Object. When the time at which a task was scheduled is up, the task is awoken, i.e. its awake method is evaluated. If the value returned by this method is a number, the task is automatically rescheduled for the time equal to its last scheduled time plus the return value (in beats).
Objects of different classes may do different things in response to being scheduled on a clock by having own implementation of the awake
method. The Object: -awake method that all classes inherit simply calls the same object's next method, forwarding the beats
argument as well as the return value, so subclasses may implement either one to equivalent effect, as far as clock scheduling is concerned. 1
+
Examples of useful objects to be scheduled on clocks:
next
method do something useful aside from returning a new value in a stream.Whenever a task is awaken, its awake
method is called in the context of the main thread. Just before that, the main thread's logical time is set to the scheduling time of the awaken task, and its clock is set to the scheduling clock. Note however that if the task is a Routine it will then immediately start or resume its Function, setting itself as the current thread.
Comb delay line with no interpolation. See also CombLwhich uses linear interpolation, and CombC which uses cubic interpolation. Cubic and linear interpolation are more computationally expensive, but more accurate. +
This UGen will create aliasing artifacts if you modulate the delay time, which is also quantized to the nearest sample period. If these are undesirable properties, use CombL or CombC. But if your delay time is fixed and sub-sample accuracy is not needed, this is the most CPU-efficient choice with no loss in quality. +
The feedback coefficient is given by the equation fb == 0.001 ** (delay / decay.abs) * decay.sign
where 0.001 is -60 dBFS.
in |
+ The input signal. |
maxdelaytime |
+ The maximum delay time in seconds. Used to initialize the delay buffer size. |
delaytime |
+ Delay time in seconds. |
decaytime |
+ Time for the echoes to decay by 60 decibels. If this time is negative, then the feedback coefficient will be negative, thus emphasizing only odd harmonics at an octave lower. + Large decay times are sensitive to DC bias, so use a LeakDC if this is an issue. + Infinite decay times are permitted. A decay time of |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
// These examples compare the variants, so that you can hear the difference in interpolation + +// Comb used as a resonator. The resonant fundamental is equal to +// reciprocal of the delay time. +{ CombN.ar(WhiteNoise.ar(0.01), 0.01, XLine.kr(0.0001, 0.01, 20), 0.2) }.play; + +{ CombL.ar(WhiteNoise.ar(0.01), 0.01, XLine.kr(0.0001, 0.01, 20), 0.2) }.play; + +{ CombC.ar(WhiteNoise.ar(0.01), 0.01, XLine.kr(0.0001, 0.01, 20), 0.2) }.play; + +// with negative feedback: +{ CombN.ar(WhiteNoise.ar(0.01), 0.01, XLine.kr(0.0001, 0.01, 20), -0.2) }.play; + +{ CombL.ar(WhiteNoise.ar(0.01), 0.01, XLine.kr(0.0001, 0.01, 20), -0.2) }.play; + +{ CombC.ar(WhiteNoise.ar(0.01), 0.01, XLine.kr(0.0001, 0.01, 20), -0.2) }.play; + +// used as an echo. +{ CombN.ar(Decay.ar(Dust.ar(1,0.5), 0.2, WhiteNoise.ar), 0.2, 0.2, 3) }.play;+
Create a new instance, set the test variable.
Answer whether the condition will block or not (boolean).
Wait until the condition is true and signalled. This only works in a Routine. This method yields a symbol (\hang), so that the clock doesn't reschedule the Routine.
c = Condition(false); fork { 0.5.wait; "started ...".postln; c.wait; "... and finished.".postln }; +c.test = true; +c.signal;+
Wait for value time, regardless of test. This only works in a Routine. This method yields a symbol (\hang), so that the clock doesn't reschedule the Routine.
c = Condition.new; fork { 0.5.wait; "started ...".postln; c.hang; "... and finished.".postln }; +c.unhang;+
If -test is true, reschedule blocked threads.
Resume threads.
( +c = Condition.new(false); + +Routine { + 1.wait; + "waited for 1 second".postln; + 1.wait; + "waited for another second, now waiting for you ... ".postln; + c.wait; + "the condition has stopped waiting.".postln; + 1.wait; + "waited for another second".postln; + "waiting for you ... ".postln; + c.test = false; + c.wait; + "the condition has stopped waiting.".postln; + 1.wait; + "the end".postln; +}.play; +) + +// continue +( +c.test = true; +c.signal; +) + +// a typical use is a routine that can pause under certain conditions: +( +c = Condition.new; +fork { loop { 1.wait; "going".postln; c.wait } }; +) +c.test = true; c.signal; +c.test = false;+
// the same, using hang + +( +c = Condition.new; + +Routine { + 1.wait; + "waited for 1 second".postln; + 1.wait; + "waited for another second, now waiting for you ... ".postln; + c.hang; + "the condition has stopped waiting.".postln; + 1.wait; + "waited for another second".postln; + "waiting for you ... ".postln; + c.hang; + "the condition has stopped waiting.".postln; +}.play; +) + +// continue +c.unhang;+ +
Waiting for Synths to end (waitForFree) uses a Condition implicitly:
( +SynthDef(\help, { |out| + var mod = LFNoise2.kr(ExpRand(0.5, 2)) * 0.5; + var snd = mod * Blip.ar(Rand(200, 800) * (mod + 1)); + Out.ar(out, snd); + FreeSelf.kr(mod < 0); // free the synth when amplitude goes below 0. +}).add; +) + +( +fork { + 10.do { + "started a synth".postln; + Synth(\help).waitForFree; + "This one ended. Wait a second, I will start the next one.".postln; + 1.wait; + }; + "This is it.".postln; +} +);+
Get the current control rate of the server.
The current control rate of the server. +
equivalent to 1 / ControlDur
play a sine tone at control rate +
A linear-interpolating sound generator based on the difference equation:
x(n+1) = a - b * sqrt(|x(n)|)+
sclang code translation:
( +var a = 1.0, b = 1.9, xi = 0, size = 64; +plot(size.collect { xi = a - (b * sqrt(abs(xi))) }); +)+
freq |
+ Iteration frequency in Hertz |
a |
+ Equation variable |
b |
+ Equation variable |
xi |
+ Initial value of x |
// vary frequency +{ CuspL.ar(MouseX.kr(20, SampleRate.ir), 1.0, 1.99) * 0.3 }.play(s); + +// mouse-controlled params +{ CuspL.ar(SampleRate.ir/4, MouseX.kr(0.9,1.1,1), MouseY.kr(1.8,2,1)) * 0.3 }.play(s); + +// as a frequency control +{ SinOsc.ar(CuspL.ar(40, MouseX.kr(0.9,1.1,1), MouseY.kr(1.8,2,1))*800+900)*0.4 }.play(s);+ +
A non-interpolating sound generator based on the difference equation:
x(n+1) = a - b * sqrt(|x(n)|)+
sclang code translation:
( +var a = 1.0, b = 1.9, xi = 0, size = 64; +plot(size.collect { xi = a - (b * sqrt(abs(xi))) }); +)+
freq |
+ Iteration frequency in Hertz |
a |
+ Equation variable |
b |
+ Equation variable |
xi |
+ Initial value of x |
mul | |
add |
// vary frequency +{ CuspN.ar(MouseX.kr(20, SampleRate.ir), 1.0, 1.99) * 0.3 }.play(s); + +// mouse-controlled params +{ CuspN.ar(SampleRate.ir/4, MouseX.kr(0.9,1.1,1), MouseY.kr(1.8,2,1)) * 0.3 }.play(s); + +// as a frequency control +{ SinOsc.ar(CuspN.ar(40, MouseX.kr(0.9,1.1,1), MouseY.kr(1.8,2,1))*800+900)*0.4 }.play(s);+ +
Playback for a buffer, using a single fixed PlayBuf UGen with jump in playback position- cheap for CPU but no enveloping, so it may click, and no individual parameters for different grains are supported.
bbcutbuf |
+ An instance of BBCutBuffer representing the buffer to be cut-up. |
offset |
+ A parameter to be passed to any cut playback position determining routine. The default chooseoffset method is in BBCutBuffer and the parameter is a single number from 0.0 to 1.0 giving the chance of a jump to a random event in the source. |
( +var sf, clock; + +clock= ExternalClock(TempoClock(2.1)); + +clock.play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + +s.sync; //this forces a wait for the Buffer to load + +//0.3.coin chance of random offset starting position for cut playback +BBCut2(CutBuf1(sf,0.3), BBCutProc11.new).play(clock); +}); + +)+
Playback for a buffer, with individual grains spawned for each cut. Each grain may have associated parameters for playback speed, enveloping and dutycycle (ratio of duration to inter-onset-interval).
All of the arguments apart from bbcutbuf can have more complex objects than SimpleNumbers passed in. You can pass in anything which responds to value (like a Function) or even objects which respond to the updateblock method such as CutPBS1 (see examples below).
bbcutbuf |
+ An instance of BBCutBuffer representing the buffer to be cut-up. |
offset |
+ A parameter to be passed to any cut playback position determining routine. The default chooseoffset method is in BBCutBuffer and the parameter is a single number from 0.0 to 1.0 giving the chance of a jump to a random event in the source. |
pbsfunc |
+ Playback speed (rate) control for each cut. This can be a constant, or some other object that responds to .value, which is called for every repeat in a block. The first argument is the repeat number in the block, and the second argument is the block itself. If pbsfunc responds to .updateblock, that will also be called every block with the current block passed as an argument. |
dutycycle |
+ Ratio of duration to inter-onset-interval (IOI). 0.5 would mean that the duration of grains is only half the length between cut start times. |
atkprop |
+ Enveloping parameter for attack speed. Rated as a proportion of the overall envelope (0.0 to 1.0) |
relprop |
+ Enveloping parameter for release speed. Rated as a proportion of the overall envelope (0.0 to 1.0) |
curve |
+ Envelope curve |
( +var sf, clock; + +s=Server.default; + +clock= ExternalClock(TempoClock(2.1)).play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + +s.sync; //this forces a wait for the Buffer to load + +BBCut2(CutBuf2(sf,0.3), BBCutProc11.new).play(clock); +}); + +) + + +//dutycycle and envelope manipulations, CutPBS1 instance used to control repitching of rolls +( +var sf, clock; + +clock= ExternalClock(TempoClock(2.1)).play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + +s.sync; //this forces a wait for the Buffer to load + +BBCut2(CutBuf2(sf,0.3, CutPBS1(0.97,{1.rand2}), 0.5, 0.001, 0.5, -4), WarpCutProc1.new).play(clock); +}); + +) + + +//showing passing of functions, the block argument passed to the dutycycle function is a BBCutBlock object +( +var sf, clock; + +clock=ExternalClock(TempoClock(3.3)).play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break2.aiff",4); + +s.sync; //this forces a wait for the Buffer to load + +BBCut2(CutBuf2(sf,0.3, {rrand(0.9,1.1)}, {arg i, block; var ioi; ioi= block.iois[i]; ioi.min(0.75)/ioi},{exprand(0.001,0.5)}), ChooseCutProc({[0.5,1.0,1.25].choose},{rrand(1,4)})).play(clock); +}); + +) + + +( +var sf, clock; + +clock=ExternalClock(TempoClock(3.3)).play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break2.aiff",4); + +s.sync; //this forces a wait for the Buffer to load + +BBCut2(CutBuf2(sf, relprop:0.8, curve:{rrand(-16,-1)}), ChooseCutProc({[0.5,1.0,0.25].choose},{rrand(1,4)})).play(clock); +}); + +)+
Playback for a segmented buffer, with individual grains spawned for each event. Cuts play back any events within their scope. The scheduling system for bbcut2 takes account of groove based time deviations from quantised template positions for the events, and perceptual attack times. Note that FX units may switch exactly on the cuts, which may or may not match up with the event playback. There are options to constrain the amount of groove deviation. +
Because CutBuf3 works by scheduling individual events in a soundfile, there is no repitch for different tempi. So you may get different plkayback speeds from a CutBuf2 or 1- ie, with CutBuf3 you should hear the sample's original pitch at whatever tempo you explore.
bbcutbuf |
+ An instance of BBCutBuffer representing the buffer to be cut-up. |
offset |
+ A parameter to be passed to any cut playback position determining routine. The default chooseoffset method is in BBCutBuffer and the parameter is a single number from 0.0 to 1.0 giving the chance of a jump to a random event in the source. You can pass an Array in that has two parameters, being [randomoffsetchance, quantise grid of offsetcutting in beats]. Ie, [0.3, 0.5] would have a 30% chance of jumping to a random eighth note position. |
deviationmult |
+ Multiplies the groove based time deviations of events. Set to 0.0 for rigid quantise based playback, 1.0 for full original timing properties. |
pretrim |
+ If playing back a cut, play any events within the cut even if there tming deviation puts them ahead of the cut start. Ie, play anticipatory events. |
posttrim |
+ The same for events whose deviation puts them after the end of the cut, but whose quantised position is within the cut. |
pbsfunc |
+ Playback speed (rate) control for each cut. This can be a constant, or some other object that responds to .value, which is called for every repeat in a block. The first argument is the repeat number in the block, and the second argument is the block itself. If pbsfunc responds to .updateblock, that will also be called every block with the current block passed as an argument. |
dutycycle |
+ Ratio of duration to inter-onset-interval (IOI). 0.5 would mean that the duration of grains is only half the length between cut start times. |
atk |
+ Enveloping parameter for attack speed of an event grain, in seconds. You may set to zero to play back source events exactly as in the original, assuming the source is proprely segmented and there are no clicks. |
rel |
+ Enveloping parameter for release speed in seconds. |
curve |
+ Envelope curve parameter. + The cut synthesis arguments apart from bbcutbuf can have more complex objects than SimpleNumbers passed in. You can pass in anything which responds to value (like a Function) or even objects which respond to the updateblock method such as CutPBS1 (see examples below). |
s=Server.default; + + +//default segmentation into eighth note events applied to the buffer + +( +var sf, clock; + +clock= ExternalClock(TempoClock(2.1)); + +clock.play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + +s.sync; //this forces a wait for the Buffer to load + +BBCut2(CutBuf3(sf,0.3), BBCutProc11.new).play(clock); +}); + +) + + +//segment into even 16ths, apply swing on event playback, dutycycle manipulation, no enveloping +( +var sf, clock; + +clock= ExternalClock(TempoClock(1.8)); + +clock.play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8, 0.25); //segment into 16ths so 16th swing works + +s.sync; //this forces a wait for the Buffer to load + +sf.setgroove; //defaults to UK garage swing + +BBCut2(CutBuf3(sf,0.3,0.0,true,true,1.0,1.0,0.0,0.0,0), BBCutProc11.new).play(clock); +}); + +) + + + +//passing in event positions, event lengths will be taken as distance between event onsets +//demonstrating repitch, keeping all anticipations and postevents with original groove deviation +( +var sf, clock; + +clock= ExternalClock(TempoClock(3.1)); + +clock.play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break2.aiff",4, [ 0, 16789, 28721, 37166, 41389, 49042, 56783 ]); + +s.sync; //this forces a wait for the Buffer to load + +BBCut2(CutBuf3(sf,0.3,1.0, false, false), BBCutProc11.new).play(clock); +}); + +) + + + +//preserving time deviations but events realigned to a UK garage swing grid +//also shows interaction with FX unit +( +var sf, clock; + +clock= ExternalClock(TempoClock(2.8)); + +clock.play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break2.aiff",4, [ 0, 16789, 28721, 37166, 41389, 49042, 56783 ]); //segment into 16ths so 16th swing works + +s.sync; //this forces a wait for the Buffer to load + +sf.setgroove; //defaults to UK garage swing + +BBCut2([CutBuf3(sf,0.3,1.0,false,false,1.0,1.0,0.0,0.0,0),CutComb1.new], ChooseCutProc(1.0,2)).play(clock); +}); + +) + + +//use the GUI to find onsets for a loaded sample- use post to output the data array +Segmentation.new //also see the help file for this GUI + + +//you could substitute your data here, remember to change the sound filename too +( +~data=[ [ 380, 0.21049886621315, 0.005 ], [ 9663, 0.16013605442177, 0.005 ], [ 16725, 0.28975056689342, 0.005 ], [ 29503, 0.085555555555556, 0.005 ], [ 33276, 0.067482993197279, 0.005 ], [ 41243, 0.18616780045351, 0.005 ], [ 49453, 0.29065759637188, 0.005 ], [ 62271, 0.085532879818594, 0.005 ], [ 66043, 0.081360544217687, 0.005 ], [ 74014, 0.18480725623583, 0.005 ], [ 82164, 0.28897959183673, 0.005 ], [ 94908, 0.08421768707483, 0.005 ], [ 98622, 0.096734693877551, 0.005 ], [ 106492, 0.2931746031746, 0.005 ], [ 119421, 0.08562358276644, 0.005 ], [ 123197, 0.0740589569161, 0.005 ] ]; +) + +//passing in event data in general form +( +var sf, clock; + +clock= ExternalClock(TempoClock(2.1)); + +clock.play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8, ~data); + +s.sync; //this forces a wait for the Buffer to load + +BBCut2(CutBuf3(sf,0.3,1.0, false, true), BBCutProc11.new).play(clock); +}); +) + + +///////////////////////////////////////////////////////////////////// +//test code for finding events for wraparound cuts +//default segmentation into eighth note events applied to the buffer +f= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); +a=f.findevents(7,8.4).postln; +b=f.trimevents(7,8.4,a,true, true,2); +//////////////////////////////////////////////////////////////////////+
CutComb1 adds a comb filter to BBCut2. +
Both arguments can be constant values, but they can also be anything that responds to .value. In the latter case, .value is called every repeat, with the repeat number as the first argument and the block as the second argument.
deltime |
+ Delay time. |
dectime |
+ Decay time. |
~buf = BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff", 8); + +BBCut2(CutGroup([CutBuf3(~buf, 0.3), CutComb1(0.01, 0.5)]), BBCutProc11()).play(2.5); + +BBCut2(CutGroup([CutBuf3(~buf, 0.3), CutComb1({ exprand(100, 200).reciprocal }, 0.5)]), BBCutProc11()).play(2.5); + +r = Routine({ loop { [48, 55, 62, 65].scramble.midicps.reciprocal.do(_.yield) } }); +BBCut2(CutGroup([CutBuf3(~buf, 0.3), CutComb1(r, 0.5)]), BBCutProc11()).play(2.5);+
The CutMixer organises the final output from a given rendering CutGroup. It defaults to outputting a stereo centred signal at full volume on busses 0 and 1. Whilst the main rendering in bbcut2 is mono, the final output is thus rendered to stereo. A stereo buffer cutup would use a simpler CutMixer where no panning occurs.
outbus |
+ Bus number for final output. The stereo output will be placed on this bus plus the one above. The default is thus 0 and 1. |
volume |
+ output volume multiplier. Defaults to 1.0. |
ampfunc |
+ Something that responds to .value and returns a cut's amplitude. This is called for every repeat in a BBCutBlock. The first argument is the repeat number in the block, and the second argument is the block itself. If ampfunc responds to .updateblock, that will also be called every block with the current block passed as an argument. |
panfunc |
+ Something that responds to .value and returns a cut's pan position. This is called for every repeat in a BBCutBlock. The first argument is the repeat number in the block, and the second argument is the block itself. If panfunc responds to .updateblock, that will also be called every block with the current block passed as an argument. |
TempoClock.default.tempo_(2); + +( +BBCut2(CutGroup([CutStream1.new,CutMixer(0,1.0,{1.0.rand},{1.0.rand2})]), ChooseCutProc(0.25,4)).play +) + + +( +BBCut2(CutGroup([CutStream1.new,CutMixer(0,1.0,RollAmplitude.new,CutPan1.new)]), WarpCutProc1(0.25,4)).play; +)+
Playback for a stream of audio which can be cut-up. The stream can be any bus on the Server, so might be a file streamed off disk, a current audio input or some synthesised data. +
Each grain may have associated parameters for enveloping and dutycycle (ratio of duration to inter-onset-interval). +
Note that CutStream1 uses In.ar rather than InFeedback.ar, for reaction speed, so execution order is important. you cannot cut-up a stream created later in the execution order. Change the SynthDefs in the class file to InFeedback if you want no execution order worries, at the expense of an audio block's delay (usually 64 samples).
inbus |
+ Bus on the Server to be cut-up. |
bbcutbuf |
+ A Server side buffer must exist for the use of the UGen- by default one is allocated for you. |
dutycycle |
+ Ratio of duration to inter-onset-interval (IOI). 0.5 would mean that the duration of grains is only half the length between cut start times. |
atkprop |
+ Enveloping parameter for attack speed. Rated as a proportion of the overall envelope (0.0 to 1.0) |
relprop |
+ Enveloping parameter for release speed. Rated as a proportion of the overall envelope (0.0 to 1.0) |
curve |
+ Envelope curve |
s=Server.default; + + +a=BBCut2(CutStream1.new).play; + +a.end; + +//dutycycle and envelope manipulations of first audio in channel +( +var buf, clock; + +clock= ExternalClock(TempoClock(2.1)); + +clock.play; + +Routine.run({ + +buf= BBCutBuffer.alloc(s,44100,1); + +s.sync; //this forces a wait for the Buffer to be allocated + +BBCut2(CutStream1(s.options.numOutputBusChannels, buf, 0.5, 0.001, 0.5, -4), WarpCutProc1.new).play(clock); +}); + +) + + + +//dutycycle and envelope manipulations cutting up a stream being synthesised +( +var buf, clock, synthgroup, bbcutgroup, synthbus; + +clock= ExternalClock(TempoClock(2.1)); + +synthgroup= Group.head(Node.basicNew(s,1)); +bbcutgroup= Group.after(synthgroup); + +synthbus= Bus.audio(s,1); + +Routine.run({ + +SynthDef(\sourcesound,{Out.ar(synthbus.index,Gendy1.ar(1,3,0.2,0.5,LFNoise1.kr(1,100,300),550,0.05,0.07))}).play(synthgroup); +buf= BBCutBuffer.alloc(s,44100,1); + +s.sync; //this forces a wait for the Buffer to be allocated + +BBCut2(CutGroup(CutStream1(synthbus.index, buf, 0.75, 0.01, 0.5, -4),bbcutgroup), WarpCutProc1.new).play(clock); +}); + +clock.play; +) + +//source on its own for comparison +SynthDef(\sourcesound,{Out.ar(0,Gendy1.ar(1,3,0.2,0.5,LFNoise1.kr(1,100,300),550,0.05,0.07)*0.1)}).play; + + + +//cutup of stereo sound- note use of stereo Bus and creation of stereo buffer, CutGroup last argument is numChannels +( +var buf, clock, synthgroup, bbcutgroup, synthbus; + +clock= ExternalClock(TempoClock(2.1)); + +synthgroup= Group.head(Node.basicNew(s,1)); +bbcutgroup= Group.after(synthgroup); + +synthbus= Bus.audio(s,2); + +Routine.run({ + +SynthDef(\sourcesound,{Out.ar(synthbus.index,0.25*Gendy1.ar(1,3,0.2,0.5,LFNoise1.kr([1.1,1.3],100,300),[650,750],0.05,0.07))}).play(synthgroup); +buf= BBCutBuffer.alloc(s,44100,2); + +s.sync; //this forces a wait for the Buffer to be allocated + +BBCut2(CutGroup(CutStream1(synthbus.index, buf, 0.75, 0.01, 0.5, -4),bbcutgroup,nil,2), WarpCutProc1.new).play(clock); +}); + +clock.play; +)+
Playback for a stream of audio which can be cut-up, with offsetting relative to the last beat marked by the clock. The stream can be any bus on the Server, so might be a file streamed off disk, a current audio input or some synthesised data. +
Each grain may have associated parameters for playback speed, enveloping and dutycycle (ratio of duration to inter-onset-interval). +
Note that CutStream3 uses In.ar rather than InFeedback.ar, for reaction speed, so execution order is important. you cannot cut-up a stream created later in the execution order. Change the SynthDefs in the class file to InFeedback if you want no execution order worries, at the expense of an audio block's delay (usually 64 samples).
aed |
+ a running AnalyseEventsDatabase object, using the same ExternalClock as the cutter. |
offset |
+ value to offset from the last recorded beat of the clock. 0 keeps this reference point, 2 would be a reference start time of three beats ago (last recorded, then back another 2). The default is 1. However, the meaning of this parameter changes when setoffset is applied by certain cut procedures; here it determines the region of offsetting in beats. |
swing |
+ Number of beats of delay to apply for offbeat semiquavers, ie 0.0 is no swing, 0.08 is UK garage swing. For strict quantisation on-the-fly deviationmult must also be 0.0. |
deviationmult |
+ Multiplies the groove based time deviations of events. Set to 0.0 for rigid quantise based playback, 1.0 for full original timing properties. |
pretrim |
+ If playing back a cut, play any events within the cut even if there tming deviation puts them ahead of the cut start. Ie, play anticipatory events. |
posttrim |
+ The same for events whose deviation puts them after the end of the cut, but whose quantised position is within the cut. |
pbsfunc |
+ Manipulate playback speed, would usually be 1.0. |
dutycycle |
+ Ratio of duration to inter-onset-interval (IOI). 0.5 would mean that the duration of grains is only half the length between cut start times. |
atk |
+ Enveloping parameter for attack speed. Rated as a proportion of the overall envelope (0.0 to 1.0) |
rel |
+ Enveloping parameter for release speed. Rated as a proportion of the overall envelope (0.0 to 1.0) |
curve |
+ Envelope curve |
s=Server.default; + +c=ExternalClock(2.3).play; + +e=AnalyseEventsDatabase.new; + +e.analyse(clock:c); + +//must be same clock +a=BBCut2(CutStream3(e,1,0.0), ChooseCutProc(0.5,2)).play(c); + +a.end; + +e.stop; + +c.stop; + + +//sing along to the beat and have the events found in the audio stream in time, auto quantised +( +var b,buf,e, clock; + +clock= ExternalClock(TempoClock(2.1)); + +clock.play; + +e=AnalyseEventsDatabase.new; + +e.analyse(clock:clock); + +Routine.run({ + +b=BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + +s.sync; //this forces a wait for the Buffer to be allocated + +//3 beats into the past +BBCut2(CutStream3(e,3,0.0,0.0, false, false), ChooseCutProc({[1.5,1.0].choose},2)).play(clock); + +BBCut2(CutBuf1(b), BBCutProc11.new).play(clock); +}); + +) + + + +//beat induction and event capture on an existing track +( +var trackbus, trackgroup; + +s.latency=0.05; + +//clear any existing OSCresponder +OSCresponder.all.do({arg val; if(val.cmdName=='/tr',{OSCresponder.remove(val)}); }); + +//run a line at a time +~clock= ServerClock.new; + +~clock.play(100,s); //will wait on trigID 100 + +~database=AnalyseEventsDatabase.new; + +Routine.run({ + +//choose some file you want to track off your hard drive +~source=Buffer.read(s,"/Volumes/data/stevebeattrack/samples/100.wav"); + +s.sync; + +~trackbus=Bus.audio(s,1); + +trackgroup= Group.before(Node.basicNew(s,1)); + +//run a beat tracker on the Server which sends the appropriate OSC message +~tracksynth= SynthDef(\help_cutstream3,{arg vol=1.0, beepvol=0.0, lock=0; +var trackb,trackh,trackq,tempo; +var source, beep; + +source= PlayBuf.ar(1,~source.bufnum,1.0,1,0,1); + +//see AutoTrack help file +#trackb,trackh,trackq,tempo=AutoTrack.kr(source, lock); + +beep= SinOsc.ar(1000,0.0,Decay.kr(trackb,0.1)); + +Out.ar(~trackbus.index,source); + +Out.ar(0,Pan2.ar((vol*source)+(beepvol*beep),0.0)); + +SendTrig.kr(trackb,100,tempo); //sends with ID of 100 matching what clock expects + +}).play(trackgroup); + +//creates at tail of trackgroup +~database.analyse(~trackbus.index, 101, trackgroup, 0.34, ~clock); //trigID 101 is default + +}); + +) + +//default is add to head of group at Node 1 CutGroup +a=BBCut2(CutStream3(~database, 4, 0.0, 0.0,false,false), ChooseCutProc(1,2)).play(~clock); + +b=BBCut2(CutStream3(~database, 4, 0.0, 0.0,false,false), BBCutProc11.new).play(~clock); + +c=BBCut2([CutMod1.new,CutRev1.new,CutStream3(~database, 4, 0.0, 0.0,false,false)], ChooseCutProc(0.5,4)).play(~clock); + +~tracksynth.set(\vol,0.0); + +~tracksynth.set(\beepvol,1.0); + +~database.threshold_(0.1); //make it more event trigger happy + +a.end; +b.end; +c.end; + +~clock.stop; +~database.stop;+
DFM1 is a digitally modelled analog filter.1 It provides low-pass and high-pass filtering. The filter can be overdriven and will self-oscillate at high resonances.
in |
+ input signal |
freq |
+ cutoff frequency |
res |
+ resonance |
inputgain |
+ gain applied to the input signal |
type |
+ set to 0.0 for low-pass or 1.0 for high-pass |
noiselevel |
+ amplitude of noise added to the model |
For questions regarding the function of DFM1 within SuperCollider contact Jonny Stutters <jstutters@jeremah.co.uk>
Create a new date with the given properties (all numerical values). These arguments are also the names of instance variables of the date object.
Get current date from system and create a date object from it. +
Get current localized time from system and create a date object from it.
Get current Greenwich Mean Time (GMT) from system and create a date object from it.
Get current date and return a value suitable for seeding a random number generator. See also randomSeed. +
Set the receiver's time to current localtime.
Set the receiver's time to current Greenwich Mean Time (GMT).
Obtain a string with the year, month and day in the format YYMMDD.
Obtain a string in the format H:M:S.
Obtain a string with the seconds.
Obtain a string in the format YYMMDD_HHMMSS.
Obtain a string in an alphabetically sortable standard database format.
Obtain a string in the format WeekdayName MonthName Time Year.
Returns asctime.
Obtain a date string with a given format. The character % is replaced by the appropriate value, which is derived from the letter that follows. +
A list of formats can be found here: http://www.opengroup.org/onlinepubs/009695399/functions/strftime.html
If a conversion specification does not correspond to any of the above, the behavior is undefined.
Delays the input by 1 audio frame or control period.
in |
+ Input signal. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
For audio-rate signals the delay is 1 audio frame, and for control-rate signals the delay is 1 control period.
Continuously play a longer soundfile from disk. This requires a buffer to be preloaded with one buffer size of sound.
numChannels |
+ Number of channels. This must match the number of channels in the buffer. |
bufnum |
+ Buffer number NOTE: The Buffer's numFrames must be a power of two and is recommended to be at least 65536 -- preferably 131072 or 262144. Smaller buffer sizes mean more frequent disk access, which can cause glitches. |
loop |
+ If set to 1, the soundfile will loop. NOTE: If the buffer has a larger number of frames than the sound file there will be a noticeable gap between the first and the following loop iterations. In that case chose a smaller buffer size or use PlayBuf instead |
This UGen will set the 'done' flag when finished playing.
s.boot; // start the server + +// examples below will use this synthdef +( +SynthDef("help-Diskin", { |out, bufnum = 0| + Out.ar(out, DiskIn.ar(1, bufnum)); +}).add +)+
b = Buffer.cueSoundFile(s, Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff", 0, 1); + +x = { DiskIn.ar(1, b.bufnum) }.play; + +b.close; + +// again +// note the like named instance method, but different arguments +b.cueSoundFile(Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff", 0); + +x.free; b.close; b.free; + + + +// loop it (for better looping use PlayBuf!) +( +p = Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff"; +a = SoundFile.new; +a.openRead(p); +d = a.numFrames/s.sampleRate; // get the duration +a.close; // don't forget +b = Buffer.cueSoundFile(s, p, 0, 1); +f = { DiskIn.ar(1, b.bufnum) }; +x = f.play; +r = Routine({ + loop({ d.wait; x.free; x = f.play; b.close( b.cueSoundFileMsg(p, 0)) }); +}).play; ) +r.stop; x.free; b.close; b.free; // you need to do all these to properly cleanup + + + +// cue and play right away +( +SynthDef("help-Diskin", { |out, bufnum = 0| + Out.ar(out, DiskIn.ar(1, bufnum)); +}).add; +) + +( +x = Synth.basicNew("help-Diskin"); +m = { arg buf; x.addToHeadMsg(nil, [\bufnum, buf.bufnum])}; + +b = Buffer.cueSoundFile(s,Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff", 0, 1, completionMessage: m); + +)+
// allocate a disk i/o buffer +s.sendMsg("/b_alloc", 0, 65536, 1); + +// open an input file for this buffer, leave it open +s.sendMsg("/b_read", 0, Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff", 0, 65536, 0, 1); + +// create a diskin node +s.sendMsg("/s_new", "help-Diskin", x = s.nextNodeID, 1, 1); + +s.sendMsg("/b_close", 0); // close the file (very important!) + + +// again +// don't need to reallocate and Synth is still reading +s.sendMsg("/b_read", 0, Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff", 0, 0, 0, 1); + +s.sendMsg("/n_free", x); // stop reading + +s.sendMsg("/b_close", 0); // close the file. + +s.sendMsg("/b_free", 0); // frees the buffer+
Some UGens set a 'done' flag when they are finished playing. This UGen echoes that flag when it is set to track a particular UGen. +
The UGens trackable by Done are:
+src |
+ The UGen to monitor |
The 'done' flag can be used to trigger other things in the same synth:
( +SynthDef("Done-help", { arg out, t_trig; + var line, a, b; + + line= Line.kr(1,0,1); + + a= SinOsc.ar(440,0,0.1*line); //sound fading out + b= WhiteNoise.ar(Done.kr(line)*0.1); //noise starts at end of line + + Out.ar(out, Pan2.ar(a+b)); +}).add; +) + +Synth("Done-help"); //note that this synth doesn't have it's own doneAction, so you'll need to manually deallocate it+ +
The 'done' flag can be used to trigger a delayed freeing of the current synth, which is not possible by using doneActions alone:
play { + var env = Line.kr(1,0,2); + var sig = PinkNoise.ar(env); + FreeSelf.kr(TDelay.kr(Done.kr(env),3)); + GVerb.ar(sig,70,7); +}+ +
A number of UGens implement doneActions. These allow one to optionally free or pause the enclosing synth and other related nodes when the UGen is finished. You can use the constants in this class to name doneActions, which can be clearer than using the number codes alone. The available doneActions are as follows:
name | value | description |
none | 0 | do nothing when the UGen is finished |
pauseSelf | 1 | pause the enclosing synth, but do not free it |
freeSelf | 2 | free the enclosing synth |
freeSelfAndPrev | 3 | free both this synth and the preceding node |
freeSelfAndNext | 4 | free both this synth and the following node |
freeSelfAndFreeAllInPrev | 5 | free this synth; if the preceding node is a group then do g_freeAll on it, else free it |
freeSelfAndFreeAllInNext | 6 | free this synth; if the following node is a group then do g_freeAll on it, else free it |
freeSelfToHead | 7 | free this synth and all preceding nodes in this group |
freeSelfToTail | 8 | free this synth and all following nodes in this group |
freeSelfPausePrev | 9 | free this synth and pause the preceding node |
freeSelfPauseNext | 10 | free this synth and pause the following node |
freeSelfAndDeepFreePrev | 11 | free this synth and if the preceding node is a group then do g_deepFree on it, else free it |
freeSelfAndDeepFreeNext | 12 | free this synth and if the following node is a group then do g_deepFree on it, else free it |
freeAllInGroup | 13 | free this synth and all other nodes in this group (before and after) |
freeGroup | 14 | free the enclosing group and all nodes within it (including this synth) |
For information on freeAll
and deepFree
, see Group and Server Command Reference.
Another way to free a synth when some UGen is done playing is to use FreeSelfWhenDone, or FreeSelf in combination with Done. For example, this can be used to delay the freeing to let reverb tails fade out, etc.
DynKlank is a bank of frequency resonators which can be used to simulate the resonant modes of an object. Each mode is given a ring time, which is the time for the mode to decay by 60 dB. +
Unlike Klank, all parameters in DynKlank can be changed in real-time after it has been started.
specificationsArrayRef |
+ A Ref to an Array of three Arrays:
All subarrays, if not nil, should have the same length. |
input |
+ The excitation input to the resonant filter bank. |
freqscale |
+ A scale factor multiplied by all frequencies at initialization time. |
freqoffset |
+ An offset added to all frequencies at initialization time. |
decayscale |
+ A scale factor multiplied by all ring times at initialization time. |
Four resonators each at maximum amplitude of 1.0 and ring times of 1 second, different exciters and no scaling:
{ DynKlank.ar(`[[800, 1071, 1153, 1723], nil, [1, 1, 1, 1]], Impulse.ar(2, 0, 0.1)) }.play; + +{ DynKlank.ar(`[[800, 1071, 1353, 1723], nil, [1, 1, 1, 1]], Dust.ar(8, 0.1)) }.play; + +{ DynKlank.ar(`[[800, 1071, 1353, 1723], nil, [1, 1, 1, 1]], PinkNoise.ar(0.007)) }.play; + +{ DynKlank.ar(`[[200, 671, 1153, 1723], nil, [1, 1, 1, 1]], PinkNoise.ar([0.007, 0.007])) }.play;+ +
Changing parameters in realtime:
( +// change freqs and ringtimes with mouse +{ var freqs, ringtimes; + freqs = [800, 1071, 1153, 1723] * MouseX.kr(0.5, 2, 1); + ringtimes = [1, 1, 1, 1] * MouseY.kr(0.1, 10, 1); + DynKlank.ar(`[freqs, nil, ringtimes ], Impulse.ar(2, 0, 0.1)) +}.play; +) + +( +// set them from outside later: +SynthDef('help-dynKlank', { |out| + var freqs, ringtimes, signal; + freqs = Control.names([\freqs]).kr([800, 1071, 1153, 1723]); + ringtimes = Control.names([\ringtimes]).kr([1, 1, 1, 1]); + signal = DynKlank.ar(`[freqs, nil, ringtimes ], Impulse.ar(2, 0, 0.1)); + Out.ar(out, signal); +}).add; +) + +a = Synth('help-dynKlank'); + +a.setn(\freqs, Array.rand(4, 500, 2000)); +a.setn(\ringtimes, Array.rand(4, 0.2, 4) ); +a.setn(\ringtimes, Array.rand(4, 0.02, 0.4) ); + +// create multichannel controls directly with literal arrays: +( +SynthDef('help-dynKlank', { |out, + freqs (#[100, 200, 300, 400]), + amps (#[1, 0.3, 0.2, 0.05]), + rings (#[1, 1, 1, 2])| + + Out.ar(out, DynKlank.ar(`[freqs, amps, rings], WhiteNoise.ar * 0.001)) +}).add +) + +a = Synth('help-dynKlank'); + +a.setn(\freqs, Array.rand(4, 500, 2000)); +a.setn(\amps, Array.exprand(4, 0.01, 1)); + +{ Out.kr(102, MouseX.kr(1, 2) * Array.rand(4, 500, 2000)) }.play; +a.mapn(\freqs, 102, 4);+
Users will not normally directly create instances of EZGui, but only use it through its subclasses. It provides the basic mechanisms for various EZ widget wrappers. It also provides a standard for EZ GUI Classes, and new EZ Classes should subclass EZGUI to help keep a consistent user interface.
Returns the enclosing CompositeView.
Returns the bounds of the enclosing CompositeView.
Sets/gets it the label. Will add the label view if none was initially created.
string |
+ An Instance of String. |
Returns the window if you used the popUp window function.
Makes the popup window always on top, if there is one.
bool |
+ An Instance of Boolean. Default is false. |
Sets/gets it the component views are visible.
bool |
+ An Instance of Boolean. Default is true. |
Sets/gets the onClose function of either view
or window
, depending on whether the EZ view used a popup window.
func |
+ An Instance of Function or FunctionList. |
font |
+ An instance of Font. |
EZGui provides a standard and basic tools for most EZ classes. If you make a new EZ class, then subclass EZGui, and override the necessary methods. If your class only has a label and a widget, chances are, you need to override nothing, but only need to write the new and init class methods. See existing subclasses of EZGui for examples of this. You may also want to override the following:
Returns the active widget. Subclasses will typically refer to it or ignore it, e.g.:
MyEZClass{ + myOtherMethods{} + .... + listView{ ^widget } +}+
Gets/sets the action of the EZ class instance.
(func) |
+ An Instance of Function or FunctionList. |
Gets/sets the value of the widget
. Does not perform the action.
val |
+ An integer. |
Gets/sets the value of the widget. Performs do action.
val |
+ An integer. |
Performs this.action.value(this)
.
This calculates the bounds of the subviews and the gaps. It returns an array of Rects, which depends on how many subview there are. Subclasses override this if they have more than one widget.
Called by init. Returns [view, bounds]
. The container is either the enclosing Container, or a pop up window with a container.
Only defined by some subclasses. Sets the resize
and align
of all the views, according to the state of layout
.
Called in the init method of all subclasses. Sets the margin and gap of view
. By default, it tries to get its parent's gap, otherwise it defaults to 2@2
. Setting argGap
overrides these.
EZPopUpMenu is wrapper class which creates an (optional) label and a popUpMenu. It includes per item actions as well as a global action which are both evaluated upon selection of an item. Convenience methods for inserting and deleting menu items are also included . If the parent is nil, then EZPopUpMenu will create its own window.See EZGui and EZLists for all of the options.
The convenience methods for EZPopUpMenu require that the items array is an array of associations of labels and functions, not like in PopUpMenu, where items is simply an array of strings. If label
is nil
, then no staticText is created.
parentView |
+ The parent view or window. If the parent is nil, then EZPopUpMenu will create its own Window, and place it conveniently on the screen if the bounds are a Point. If the bounds are a Rect, then the Rect determines the window bounds. |
bounds | + |
label |
+ The label. Default value is |
items |
+ Default value is |
globalAction |
+ A global function to be performed in addition to the item functions |
initVal |
+ Initial value of the menu, i.e. the index selected. Default value is 0. |
initAction |
+ An instance of Boolean. Performs the action at |
labelWidth |
+ Default value is 80. |
labelHeight |
+ Default value is 20. Not used if layout is |
layout |
+
|
gap |
+ A Point. By default, the view tries to get its parent's |
margin |
+ A Point. This will inset the bounds occupied by the subviews of view. |
( +w = Window.new.front; +w.view.decorator = FlowLayout(w.view.bounds); +g = EZPopUpMenu.new( + w, + 230@22, + "A PopUpMenu: ", + [ + \item0 ->{|a| ("this is item 0 of " ++ a).postln}, + \item1 ->{|a| ("this is item 1 of " ++ a).postln}, + \item2 ->{|a| ("this is item 2 of " ++ a).postln}, + ], + globalAction: {|a| ("this is a global action of "++a.asString ).postln}, + initVal: 1, + initAction: true, + labelWidth: 120, + labelHeight: 20, + layout: \horz, + gap: 2@2 +); +) + +// or a more simple syntax: +( +w = Window.new.front; +w.view.decorator = FlowLayout(w.view.bounds); +g = EZPopUpMenu.new(w, 200@22, "Menu: "); +g.addItem(\item0, { |a| ("this is item 0 of " ++ a).postln }); +g.addItem(\item1, { |a| ("this is item 1 of " ++ a).postln }); +g.addItem(\item2, { |a| ("this is item 2 of " ++ a).postln }); +g.value = 0; +)+
stringBackground |
+ An instance of Color. The |
stringColor |
+ An instance of Color. The |
menuBackground |
+ An instance of Color. The |
menuStringColor |
+ An instance of Color. The |
background |
+ An instance of Color. The |
// try several examples together +( + +// many menus +// inherits the parent's decorator gap + +( +w=Window.new("oscillators", Rect(200,500,200,160)).front; +w.view.decorator = FlowLayout(w.view.bounds).gap_(2@2); +5.do{|i| + g = EZPopUpMenu.new(w,190@22, "Oscillator % : ".format(i+1)); + g.addItem(\off, {"off". postln}); + g.addItem(\sine, {"sine". postln}); + g.addItem(\saw, {"saw". postln}); + g.addItem(\pulse, {"pulse". postln}); + g.setColors(Color.grey,Color.white); + g.value=0; +}; +w.bounds=w.bounds.moveBy(300,60); +); + + +// Creates its own window if parentView is nil: +( +g = EZPopUpMenu.new(nil,250@22 ," Select : "); +g.addItem(\item0, {"this is item 0". postln}); +g.addItem(\item1, {"this is item 1". postln}); +g.addItem(\item2, {"this is item 2". postln}); +g.setColors(Color.grey,Color.white); +g.value=0; + +); + +// layout vertical: +( +g = EZPopUpMenu.new(nil,200@42, " Choose",layout:\vert); +g.addItem(\item0, {"this is item 0". postln}); +g.addItem(\item1, {"this is item 1". postln}); +g.addItem(\item2, {"this is item 2". postln}); +g.setColors(Color.grey,Color.white); +g.window.bounds=g.window.bounds.moveBy(300,-200); +g.value=0; +); + +// No labelView created, so set the window title; +( +g = EZPopUpMenu.new(bounds:180@22); // no label +g.addItem(\item0, {"this is item 0". postln}); +g.addItem(\item1, {"this is item 1". postln}); +g.addItem(\item2, {"this is item 2". postln}); +g.value=0; +g.window.name=" choose item"; +g.window.bounds=g.window.bounds.moveBy(0,-200); +); +) +// insertItem; + +( +g = EZPopUpMenu.new(nil,200@22, "Menu:"); +g.addItem(\item0, {"this is item 0". postln}); +g.addItem(\item1, {"this is item 1". postln}); +g.addItem(\item2, {"this is item 2". postln}); +g.addItem(\item4, {"this is item 4". postln}); +g.value=0; +); + +g.insertItem(3, \item3, {"this is item 3". postln}); + + +// remove Item ; + +( +w=Window.new.front; +w.view.decorator = FlowLayout(w.view.bounds); +g = EZPopUpMenu.new(w,200@22, "Menu:"); +g.addItem(\item0, {"this is item 0". postln}); +g.addItem(\item1, {"this is item 1". postln}); +g.addItem(\item2, {"this is item 2". postln}); +g.addItem(\item4, {"this is item 4". postln}); +g.insertItem(3, \item3, {"this is item 3". postln}); +g.value=0; +) + +g. removeItemAt(0); + + + +// replace item; +( +g = EZPopUpMenu.new(nil,200@22, "List:"); +g.addItem(\item0, {"this is item 0". postln}); +g.addItem(\item1, {"this is item 1". postln}); +g.addItem(\item2, {"this is item 2". postln}); +g.addItem(\item3, {"this is item 3". postln}); +) + +g.replaceItemAt(2, \item2_replaced, {"this is item 2 replaced". postln});+ +
EZText is a wrapper class which creates an (optional) StaticText, and a TextField. The value is displayed as a compileString in the text field for editing.
If the parent is nil
, then EZText will create its own Window. See EZGui for more options.
parent |
+ The parent view or window. If the parent is nil, then EZText will create its own Window, and place it conveniently on the screen if the bounds are a Point. If the bounds are a Rect, then the Rect determines the window bounds. |
bounds | + |
label |
+ The label. Default value is |
action |
+ A Function called when the value changes. The function is passed the EZText instance as its argument. |
initVal |
+ The value to initialize the EZText with. |
initAction |
+ A Boolean indicating whether the action function should be called when setting the initial value. The default is false. |
labelWidth |
+ Number of pixels width for the label. The default is 60. In the |
textWidth |
+ Number of pixels width for the number box. The default is 45. In |
labelHeight |
+ Default is 20. |
layout |
+
|
gap |
+ A Point. By default, the view tries to get its parent's gap, otherwise it defaults to |
margin |
+ A Point. This will inset the bounds occupied by the subviews of view. |
Example:
( +w = Window("EZText", Rect(300, 300, 260, 60)).front; +g = EZText( w, // parent + 250@50, // bounds + "testing", // label + { |ez| (ez.value.asString ++" is the value of " ++ ez).postln }, // action + [1, 2, 3], // initValue + true // initAction +); +g.setColors(Color.grey,Color.white); +); + +// Simplest version, no parent view, so a window is created +( + g = EZText(label:" test "); + g.action_({ |ez| (ez.value.asString ++" is the value of " ++ ez).postln }); +); + +( + g = EZText(bounds: Rect( 100, 200, 150, 50), label:" test ", layout: \vert); + g.action_({ |ez| (ez.value.asString ++" is the value of " ++ ez).postln }); +);+ +
The contained views can be accessed via the EZText instance variables: labelView
, textField
.
Returns the textField.
A Function to be evaluated when the value changes. Typical use is to type in a new value, and interpret it by hitting the evaluation shortcut. The first argument to the function will be the EZText.
Gets/sets the value of the ezText. Does not perform the action.
inval |
+ Any object. |
Sets the value and performs the action.
val |
+ Any object. |
Performs the action.
Sets/gets whether the textfield is enabled.
bool |
+ An instance of Boolean. Default is |
stringBackground |
+ An instance of Color. The |
stringColor |
+ An instance of Color. The |
textBackground |
+ An instance of Color. The |
textStringColor |
+ An instance of Color. The |
textNormalColor |
+ An instance of Color. The |
textTypingColor |
+ An instance of Color. The |
background |
+ An instance of Color. The |
// Simplest version +( // basic use + w=Window("ez", Rect(300, 300, 300, 50)).front; + g=EZText(w, 290@40," test ", textWidth: 220,layout:\horz); + g.setColors(Color.grey,Color.white); +); + + +// lots of textFields on one window +( +w=Window.new.front; +w.view.decorator=FlowLayout(w.view.bounds); +w.view.decorator.gap=2@2; + +40.do{ + g=EZText(w, 170@16," test ", textWidth: 120,layout:\horz); + g.setColors(Color.grey, Color.white, Color.grey(0.8)); +}; +); + + +// click these parentheses to see three variants +( + +m=nil; +m=2@2; //comment for no margin + +///////////////// +/// Layout \horz + +( // with label + g=EZText(nil, 170@20," freq ", textWidth:120,layout:\horz,margin:m); + g.setColors(Color.grey,Color.white,background: Color.grey(0.7)); + g.window.bounds = g.window.bounds.moveBy(-180,50); +); + +( // no label. use window name as label + g=EZText(nil, 120@20, layout:\horz,margin:m); + g.setColors(Color.grey,Color.white,background: Color.grey(0.7)); + g.window.bounds = g.window.bounds.moveBy(-180, -90); + g.window.name="Freq"; +); + +///////////////// +/// Layout \vert + +( // all features + g=EZText(nil, 120@60," freq ", textWidth: 120,layout: \vert, margin:m); + g.setColors(Color.grey,Color.white,background: Color.grey(0.7)); + g.window.bounds = g.window.bounds.moveBy(100,50); +); + +) + + + +// Simplest sound example +( +Tdef(\text).set(\note, [0, 2, 7], \dur, { [0.1, 0.2].choose }); + +w = Window("EZTexts", Rect(200, 400, 304, 120)).front; +w.addFlowLayout; + +TdefGui(Tdef(\text), 0, w); +Tdef(\text).envir.keysValuesDo { |k, v| + EZText(w, Rect(0,0,300,40), k, { |ez| + Tdef(\text).envir.put(*[k, ez.value].postcs); + }, v); +}; + +Tdef(\text, { |ev| + var mydur; + loop { + mydur = ev.dur; + (note: ev.note, dur: mydur).postln.play; + mydur.wait; + } +}).play; +) + +// type these or similar functions into dur and note fields and evaluate: + +{ [0.1, 0.2, 0.3].choose } +{ [ 0, 2, 7, 10 ].scramble.keep(rrand(0, 4)) }+ +
An Env is a specification for a segmented envelope. Envs can be used both server-side, by an EnvGen or an IEnvGen within a SynthDef, and clientside, with methods such as -at and -asStream, below. +
An Env can have any number of segments which can stop at a particular value or loop several segments when sustaining. It can have several shapes for its segments. +
The envelope is conceived as a sequence of nodes (not to be confused with a synthesis-Node) : the first node gives the initial level of the envelope, and the following have three parameters: a target level, a time duration from the previous node, and a shape. The three parameters for each node are kept in separate arrays as explained below. + +
In this envelope, there are four nodes :
Close attention must be paid when retriggering envelopes. Starting from their value at the moment of retrigger, envelopes will cycle through all of their nodes, with the exception of the first. The first node is an envelope's initial value and is only output prior to the initial trigger. + +
In the above example, the initial level (0) is never repeated. When retriggered, the envelope moves from its current value (0.3), to the value of the second node (0.1), and so forth.
Create a new envelope specification.
levels |
+ an array of levels. The first value is the initial level of the envelope. When the envelope is used with an EnvGen, levels can be any UGen (new level values are updated only when the envelope has reached that point). When the array of levels contains itself an array, the envelope returns a multichannel output (for a discussion, see Multichannel expansion) | ||||||||||||||||||||||||||||||
times |
+ an array of durations of segments in seconds. There should be one fewer duration than there are levels, but if shorter, the array is extended by wrapping around the given values. | ||||||||||||||||||||||||||||||
curve |
+ a Symbol, Float, or an Array of those. Determines the shape of the envelope segments. + The possible values are:
| ||||||||||||||||||||||||||||||
releaseNode |
+ an Integer or nil. The envelope will sustain at the releaseNode until released. + + In the above example, the release node is set to the third node, which means it will sustain at the level of 0.5 until it is released. The envelope will then continue on until its last node is reached. | ||||||||||||||||||||||||||||||
loopNode |
+ an Integer or nil. Creates a segment of looping nodes. You must specify a releaseNode in order for loopNode to have any effect. The loopNode is the initial node of the loop and is never repeated. Upon reaching the releaseNode, the envelope will move back to the node that immediately follows loopNode. The envelope will loop until its gate is closed. When released, a looping envelope will move from its current position to the node that immediately follows releaseNode and continue until the end. + + In this example :
| ||||||||||||||||||||||||||||||
offset |
+ an offset to all time values (only applies in IEnvGen). |
Creates a new envelope specification with numSegments and numChannels for filling in later.
This can be useful when passing Env parameters as args to a Synth. Note that the maximum number of segments is fixed and cannot be changed once embedded in a SynthDef. Trying to set an Env with more segments than then this may result in other args being unexpectedly set. +
returns the dictionary containing the available shapes for the envelopes' curves
returns the index in the dictionary of the given curve shape
shapeName |
+ name of the shape. e.g. \lin, \cub ... |
The following class methods create some frequently used envelope shapes based on supplied durations.
Creates a new envelope specification which has a trapezoidal shape.
attackTime |
+ the duration of the attack portion. |
sustainTime |
+ the duration of the sustain portion. |
releaseTime |
+ the duration of the release portion. |
level |
+ the level of the sustain portion. |
curve |
+ the curvature of the envelope. |
Creates a new envelope specification which has a triangle shape.
dur |
+ the duration of the envelope. |
level |
+ the peak level of the envelope. |
Creates a new envelope specification which has a hanning window shape.
dur |
+ the duration of the envelope. |
level |
+ the peak level of the envelope. |
Creates a new envelope specification which (usually) has a percussive shape.
attackTime |
+ the duration of the attack portion. |
releaseTime |
+ the duration of the release portion. |
level |
+ the peak level of the envelope. |
curve |
+ the curvature of the envelope. |
Creates a new envelope specification from coordinates / control points
pairs |
+ an array of pairs [[time, level], ...] + if possible, pairs are sorted regarding their point in time |
curve |
+ the curvature of the envelope. |
Creates a new envelope specification from coordinates / control points with curvature.
xyc |
+ an array of triplets [[time, level, curve], ...] + if possible, pairs are sorted regarding their point in time |
The following methods create some frequently used envelope shapes which have a sustain segment. They are typically used in SynthDefs in situations where at the time of starting the synth it is not known when it will end. Typical cases are external interfaces, midi input, or quickly varying TempoClock. +
Creates a new envelope specification where all the segments are horizontal lines. Given n values of times only n levels need to be provided, corresponding to the fixed value of each segment.
levels |
+ an array of levels. Levels can be any UGen (new level values are updated only when the envelope has reached that point). When the array of levels contains itself an array, the envelope returns a multichannel output (for a discussion, see Multichannel expansion) |
times |
+ an array of durations of segments in seconds. It should be the same size as the levels array. |
releaseNode |
+ an Integer or nil. The envelope will sustain at the release node until released. |
loopNode |
+ an Integer or nil. If not nil the output will loop through those nodes starting at the loop node to the node immediately preceding the release node, before back to the loop node, and so on. Note that the envelope only transitions to the release node when released. Examples are below. The loop is escaped when a gate signal is sent, when the output transitions to the release node, as described below. |
offset |
+ an offset to all time values (only applies in IEnvGen). |
Creates a new envelope specification which is shaped like traditional analog attack-decay-sustain-release (adsr) envelopes.
attackTime |
+ the duration of the attack portion. |
decayTime |
+ the duration of the decay portion. |
sustainLevel |
+ the level of the sustain portion as a ratio of the peak level. |
releaseTime |
+ the duration of the release portion. |
peakLevel |
+ the peak level of the envelope. |
curve |
+ the curvature of the envelope. |
bias |
+ offset |
As *adsr above, but with its onset delayed by delayTime in seconds. The default delay is 0.1.
Creates a new envelope specification which is shaped like traditional analog attack-sustain-release (asr) envelopes.
attackTime |
+ the duration of the attack portion. |
sustainLevel |
+ the level of the sustain portion as a ratio of the peak level. |
releaseTime |
+ the duration of the release portion. |
curve |
+ the curvature of the envelope. |
Creates a new envelope specification which has no attack segment. It simply sustains at the peak level until released. Useful if you only need a fadeout, and more versatile than Line.
releaseTime |
+ the duration of the release portion. |
level |
+ the peak level of the envelope. |
curve |
+ the curvature of the envelope. |
Creates a new envelope specification which cycles through its values. For making a given envelope cyclic, you can use the instance method -circle
levels |
+ The levels through which the envelope passes. |
times |
+ The time between subsequent points in the envelope, which may be a single value (number), or an array of them. If too short, the array is extended. In difference to the *new method, the size of the times array is the same as that of the levels, because it includes the loop time. |
curve |
+ The curvature of the envelope, which may be a single value (number or symbol), or an array of them. If too short, the array is extended. In difference to the *new method, the size of the curve array is the same as that of the levels, because it includes the loop time. |
If one of the values within either levels, times, or curves is itself an array, the envelope expands to multiple channels wherever appropriate. This means that when such an envelope is passed to an EnvGen, this EnvGen will expand, and when the envelope is queried via the methods -at or -asSignal, it will return an array of values. +
Instead of using an EnvGen inside a UGen graph, this message does the same implicitly for convenience. Its argument order corresponds to the most common arguments.
doneAction |
+ An integer representing an action to be executed when the env is finished playing. This can be used to free the enclosing synth, etc. See Done for more detail. |
gate |
+ This triggers the envelope and holds it open while > 0. If the Env is fixed-length (e.g. Env.linen, Env.perc), the gate argument is used as a simple trigger. If it is an sustaining envelope (e.g. Env.adsr, Env.asr), the envelope is held open until the gate becomes 0, at which point is released. + If gate < 0, force release with time |
timeScale |
+ The durations of the segments are multiplied by this value. This value can be modulated, but is only sampled at the start of a new envelope segment. |
levelScale |
+ The levels of the breakpoints are multiplied by this value. This value can be modulated, but is only sampled at the start of a new envelope segment. |
levelBias |
+ This value is added as an offset to the levels of the breakpoints. This value can be modulated, but is only sampled at the start of a new envelope segment. |
Blend two envelopes. Returns a new Env. See blend example below.
argAnotherEnv |
+ an Env. |
argBlendFrac |
+ a number from zero to one. |
Returns a new Env based on the receiver in which the start value will be held for delay number of seconds.
delay |
+ The amount of time to delay the start of the envelope. |
Set the total duration of times, by stretching them.
Get the total duration of the envelope. In multi-channel envelopes, this is the duration of the longest one.
circle from end to beginning over the time specified, with the curve specified. See also the class method *circle
Test the envelope on the default Server with a SinOsc.
releaseTime |
+ If this is a sustaining envelope, it will be released after this much time in seconds. The default is 3 seconds. |
Plot this envelope's shape in a window.
size |
+ The size of the plot. The default is 400. |
bounds |
+ the size of the plot window. |
minval |
+ the minimum value in the plot. Defaults to the lowest value in the data. |
maxval |
+ the maximum value in the plot. Defaults to the highest value in the data. |
name |
+ the plot window's label name. If nil, a name will be created for you. |
Returns a Signal of size length created by sampling this Env at length number of intervals. If the envelope has multiple channels (see Multichannel expansion), this method returns an array of signals.
Converts the Env to an Array in a specially ordered format. This allows for Env parameters to be settable arguments in a SynthDef. See example under *newClear.
Converts the Env to an Array in a specially ordered format, like -asArray, however it always returns an array of these data sets, corresponding to the number of channels of the envelope.
Returns true if this is a sustaining envelope, false otherwise.
Returns a copy of the Env whose levels have been mapped onto the given linear, exponential or curve range.
Sustain and loop settings have no effect in the methods below.
Returns the value of the Env at time. If the envelope has multiple channels, this method returns an array of levels.
time |
+ A number or an array of numbers to specify a cut in the envelope. If time is an array, it returns the corresponding levels of each time value, and if the envelope has multiple channels, it returns an array of values. A combination of both returns a two-dimensional array. |
Embeds this Env within an enclosing Stream. Timing is derived from thisThread.beats
.
Creates a Routine and embeds the Env in it. This allows the Env to function as a Stream.
If a release node is given, and the gate input of the EnvGen is set to zero, it outputs the nodes after the release node: + +
If a loop node is given, the EnvGen outputs the nodes between the loop node and the release node (not including the release node itself) until it is released: +
There is an extra level at the beginning of the envelope to set the initial level. After that each node is a goal level and a duration, so node zero has duration equal to times[0] and goal level equal to levels[1]. +
The loop jumps back to the loop node. The endpoint of that segment is the goal level for that segment and the duration of that segment will be the time over which the level changed from the current level to the goal level.
Convenience class for an envelope generator combining fadeTime and gate arguments.
Returns an EnvGen.
i_level |
+ initial level of envelope (if set to 1, it starts open) |
gate |
+ a gate input. if nil, EnvGate creates a NamedControl named 'gate' |
fadeTime |
+ an input for both attack and decay time. if nil, EnvGate creates a NamedControl named 'fadeTime' (default time: 0.02) |
doneAction |
+ doneAction of the EnvGen |
curve |
+ envelope curve |
a = { LPF.ar(Saw.ar(200), 600) * EnvGate.new }.play; +a.set(\fadeTime, 2); +a.release; + +// the same as: +a.set(\gate, 0); + +// several env gates can coexist in one synth def. +( +a = { + var sound1 = LPF.ar(Saw.ar(80), 600) * EnvGate.new; + var sound2 = RLPF.ar(Saw.ar(200) * 0.5, 6000 * EnvGate.new + 60, 0.1) * EnvGate.new; + sound1 + sound2 +}.play; +) +a.set(\fadeTime, 5); +a.release;+ +
Plays back break point envelopes. The envelopes are instances of the Env class. The envelope and the arguments for levelScale
, levelBias
, and timeScale
are polled when the EnvGen is triggered, and at the start of a new envelope segment. All values remain constant for the duration of each segment.
+
envelope |
+ An Env instance, or an Array of Controls. (See Control and the example below for how to use this.) + The envelope is polled when the EnvGen is triggered, and at the start of a new envelope segment. The Env inputs can be other UGens. |
gate |
+ This triggers the envelope and holds it open while > 0. If the Env is fixed-length (e.g. Env.linen, Env.perc), the gate argument is used as a simple trigger. If it is an sustaining envelope (e.g. Env.adsr, Env.asr), the envelope is held open until the gate becomes 0, at which point is released. + If gate < 0, force release with time |
levelScale |
+ The levels of the breakpoints are multiplied by this value. This value can be modulated, but is only sampled at the start of a new envelope segment. |
levelBias |
+ This value is added as an offset to the levels of the breakpoints. This value can be modulated, but is only sampled at the start of a new envelope segment. |
timeScale |
+ The durations of the segments are multiplied by this value. This value can be modulated, but is only sampled at the start of a new envelope segment. |
doneAction |
+ An integer representing an action to be executed when the env is finished playing. This can be used to free the enclosing synth, etc. See Done for more detail. |
If the gate of an EnvGen is set to -1 or below, then the envelope will cutoff immediately. The time for it to cutoff is the amount less than -1, with -1 being as fast as possible, -1.5 being a cutoff in 0.5 seconds, etc. The cutoff shape and final value are read from the Env's last node. + +
If the synthDef has an arg named "gate", the convenience method of Node can be used: node.release(releaseTime)
+
+
Forced release ignores multi-node release stages, always performing a one-node release, reading curve and end value from the Env's last node, and overwriting its duration. +
For more information about the control bus mapping used in the line a = Synth(\sine, [freq: f.asMap]);
, see Node: -map and Bus: -asMap.
+
+
An Event is an Environment that specifies an action to be taken in response to a -play message. The key/value pairs within the Event specify the parameters of that action. Most methods, Event inherits from its superclasses, especially from Dictionary.
An IdentityDictionary of useful parent events.
An IdentityDictionary of Events that define the default values and functions for different aspects of note generation (timing, volume, pitch, server to use, etc).
create an event with initial size n.
n |
+ Initial size. |
proto |
+ May be provided as another event which is used to override keys in the event. |
parent |
+ May be provided as another event which is used to provide default keys for the event without modifying it. |
know |
+ If know is set to True, the event will respond to appropriate message calls. See Environment for more details. |
Returns an empty event with .defaultParentEvent as parent.
Returns an event that describes a pause of dur duration.
Event types define alternate play functions that are selected by the value of ~type.
type |
+ A name (usually a symbol) for the event type, which can be used to select it |
func |
+ A function which optionally takes the server as a first argument Event.addEventType(\happyEvent, { ("I am so happy to be silent sometimes, says" + ~who).postln; }); +Pbind(\type, \happyEvent, \who, Prand(["Alice", "Bob", "Eve"], inf), \dur, Pwhite(0.1, 1.0, inf)).play; + +// To a certain degree, it is possible to reuse some of another event type's functionality: +( +Event.addEventType(\happyEvent, { |server| + ~octave = [5, 6, 7]; // always play three octaves + ~detune = 10.0.rand2; // always play a bit out of tune + ~type = \note; // now set type to a different one + currentEnvironment.play; +}); +Pbind(\type, \happyEvent, \degree, Pseq([0, 1, 2, 3, 4, 4, 5, 5, 5, 5, 4, 2, 3, 2, 3, 1], inf), \dur, Pwhite(0.1, 1.0, inf)).play; +);+ |
This method is called in order to build the default SynthDef, which is stored under the key \default
SynthDef(\default, { Out.ar(0, Line.kr(0.3, 0, 0.5) * SinOsc.ar(Rand(300, 500.0)) ) }).add; // overwrite default +(freq: 600).play; +Event.makeDefaultSynthDef; // reset default +(freq: 600).play;+
Play the event. This evaluates the function at \play.
(freq: 800).play; +(play: { "I rather do something else: ".post; ~x.postln; }, x: 800.rand).play;+
Returns the inter onset time - the time delay until the next event in a sequence. This usually depends on \dur and \stretch, but may be overridden by specifying \delta directly.
Pn((dur: 2, freq:8000)).play;+
Combines an event given in the argument with the current event. This is used to enable events to be composed.
(a: 6, b: 7).next((c: 100));+
event |
+ The inval, usually in an event stream. See also Event. + If the event is not nil, yields a copy, adding all the elements of the receiver event (this leaves the receiver unchanged). If it is nil, return the receiver. a = (note: 2); +b = (note: [3, 5]); +Pseq([a, b]).play;+ + If a key "embedInStream" is given, use this function instead. The behaviour of the event can be configured easily this way. + The arguments event (the receiver) and inevent (the inevent) are passed to the function. NOTE: In infinite patterns, you must call yield or embedInStream in the function, otherwise it will loop forever. ( +a = ( + pattern: Pbind(\note, Pgeom(1, 1.1, { 20.rand }), \dur, 0.05), + embedInStream: { |event, inevent| event[\pattern].embedInStream(inevent) } +); +b = (note: [3, 5]); +c = (freq: 402, dur: 0.3); +Prand([a, b, c], inf).trace.play; +) + +// change the events while playing +c[\freq] = [900, 1002, 1102]; +c[\freq] = [200, 101, 1102];+ + A generator for dictionaries: ( +d = ( + a: 5, b: 7, c: 1, + rout: Routine { |inval| + inf.do { |i| + var event = d.copy.put(\count, i); + inval = event.embedInStream(inval); + } + } +); +) + +// draw new values +d.rout.((z:999)); +d.rout.((z:1, a:0)); +d.rout.(());+ |
Used by EventStreamPlayer to play Events and obtain a time increment.
Returns true if the event will be played as a rest, and false otherwise. See Rest for a more complete discussion of rests in event patterns.
Calls -asControlInput.
Enables events to represent the server resources they created in an Event.
Makes the event a control interface to the resultant Synth when played.
Makes the event a control interface to the resultant Group when played. This is experimental, does not work consistently yet.
Set a control value in the Synth or Group. (key1, val1, ....)
a = (note: 2).play; +a.set(\freq, 700); +a.release;+
Events can be written as a series of key value pairs enclosed in parentheses. Empty parentheses will create an empty event. They may be also used for object prototyping - see Environment for more details.
Because of this simple syntax, Events are often used as name space for keeping objects:
// using an event to store values +q = (n: 10, x: [1, 2, 3]); +q[\y] = q[\x] * q[\n]; // similar to ~y = ~x * ~n, but in a separate name space +q.y = q.x * q.n; // shorter way to do the same (pseudo-methods) +q.y; // [ 100, 200, 300 ]+
Event provides a .defaultParentEvent that defines a variety of different event types and provides a complete set of default key/value pairs for each type. The type is determined by the value of the key \type which defaults to \note. Note events create synths on the server.
( ).play; // the default note +(freq: 500, pan: -1) .play; // 500 Hz, panned left +(play: { ~what.postln }, what: "hello happening").play; // something else+ +
Per default, the play message derives its behaviour from the .defaultParentEvent, which provides many default values, such as default instrument (\default), note (0), legato (0.8) and so on. Depending on the event type, these may differ completely and need not even represent a sound.
The key used to select what synthdef is to be played is \instrument. In order to use a SynthDef with an Event, send it an add message. This creates a description of the SynthDef that the event can consult to determine its control names. The values of these names in the event are used when the event is played. (See SynthDesc for details.)
( +SynthDef(\pm, { |out=0, freq=440, amp=0.1, pan=0, gate=1, ratio = 1, index = 1, ar = 0.1, dr = 0.1| + var z; + z = LPF.ar( + PMOsc.ar(freq, freq * ratio, Linen.kr(gate, ar,index, dr), 0, 0.3), + XLine.kr(Rand(4000, 5000), Rand(2500, 3200), 1) + ) * Linen.kr(gate, 0.01, 0.7, dr, 2); + OffsetOut.ar(out, Pan2.ar(z, pan, amp)); +}).add; +); + +(instrument: \pm).play; + +(instrument: \pm, ratio: 3.42, index: 12, freq: 150, ar: 8, dr: 3, sustain: 10).play;+
If a key relevant to the action is assigned an Array, the action is repeated on each element of the array:
(degree: (0..12)).play; // a diatonic cluster+ +
If several keys are assigned arrays, the action is repeated for each element of the largest array. Smaller arrays are rotated through repeatedly. Here are some examples:
// every other note of the diatonic cluster: stacked thirds +(degree: (0..12), amp: [0, 0.1]).play; + +// every other note of the semitone cluster: a wholetone cluster again +(note: (0..12), amp: [0, 0.1]).play; + +// every third note of the semitone cluster: a diminished chord +(note: (0..12), amp: [0, 0, 0.1]).play; + +// the same with different sustain times +(note: (0..12), amp: [0, 0, 0.1], sustain:[0.1, 0.3, 1.3, 2.5]).play; + +// timingOffset gives a tempo-relative offset time to each synth +(instrument: \pm, ratio: [2.3, 4.5, 1.7], timingOffset: [0, 1.2, 3], sustain: [0.2, 2, 1]).play;+ +
In the \note event, all keys multichannel expand apart from: \instrument, \dur, \delta, \strum.
It is possible to assign an array to one of a SynthDef's control names. For example:
( +SynthDef(\test, { | out = 0, amp = 0.01, freq = #[300,400,400], pan, gate = 1 | + var audio, env; + audio = Mix.ar(Pulse.ar(freq, 0.5)); // this is a mixture of three oscillators + env = Linen.kr(gate, susLevel: amp , doneAction: 2); // envelope deletes the synt when done + audio = audio * env; + OffsetOut.ar(0, audio ); +}).add; +)+ +
Within an event, arrayed arguments of this sort must be enclosed within an additional array to distinguish them from arguments intended for multi-channel expansion.
// one synth, use enclosing array to prevent multi-channel expansion +(instrument: \test, note: [[0, 2, 4]]).play; + +// two synths +(instrument: \test, note: [[0, 2, 4], [6, 8, 10]]).play;+
Events are closely integrated with the Patterns library. Different patterns can be bound to different keys (or collections of keys) to specify the resultant music. See the help files Pattern and Pbind and the tutorials Understanding Streams, Patterns and Events - Part 4 and Understanding Streams, Patterns and Events - Part 5 for more details on Patterns. +
Patterns that return events may be played on a clock: dur specifies the time between two subsequent events.
// Pseq returns one item in the list after the other +( +Pseq([ + (note: 2, sustain: 1, dur: 1.5), + (note: [5, 7], sustain: 0.5, dur: 0.8), + (note: [2, 6], sustain: 1, dur: 0.8) +]).play; +) + +// Pbind binds parameters to events: +( +Pbind( + \note, Pseq([2, [5, 7], [2, 6]]), + \sustain, Pseq([1, 0.5, 1]), + \dur, Pseq([1.5, 0.8, 0.8]) +).play; +) + +// per-event timing may be specified: +( +Pbind( + \note, Pseq([[0, 9], [5, 7], [2, 6]], inf), + \sustain, Pseq([1, 0.5, 1], inf), + \dur, Pseq([1.5, 0.8, 0.8], inf), + \timingOffset, Pseq([[0, 0.3], [0, 0.01]], inf) +).play; +)+ +
Here is an example that illustrates some more of the keys defined by the .defaultParentEvent. Note that it is equivalent to write Pbind(\key, val, ...)
and Pbind(*[key: val, ...])
.
( +Pbind(*[ + stepsPerOctave: Pstep(Pseq((2..12).mirror, inf),12), // 3 - 12 tone e.t. scales + note: Pseq((0..12).mirror, inf), // play full notes up and down + ctranspose: Pwhite(-0.2, 0.2), // detune up to +-20 cents + detune: Pwhite(-1.0, 1.0), // detune up to 1 Hz + sustain: Prand([0.2, 0.2, 0.2, 4], inf), // notes last 0.2 or 4 seconds + // 1 in 6 chance waits 0.8 seconds: + dur: Prand([0.2, 0.2, 0.2, 0.2, 0.2, 0.8], inf), + db: Pstep(Pseq([-15, -25, -20, -25], inf), 0.8)// 4 beat accent structure +]).play; +)+
When an Event (or any other Environment) receives a use(function)
message, it sets itself to be currentEnvironment, evaluates the function, and restores the original value of currentEnvironment. This allows the function to access and alter the contents of the event using the following shortcuts: ~keyName
which is equivalent to currentEnvironment.at(keyName)
and ~keyName = value
which is equivalent to currentEnvironment.put(keyName, value)
.
+
We will write ~keyName
whenever referring to the value stored at the key keyName in the event.
+
Here is the definition of Event's play method:
play { + if (parent.isNil) { parent = defaultParentEvent }; + this.use { ~play.value }; +}+ +
Thus we can see that the .defaultParentEvent is used unless otherwise specified and the function stored in ~play
is executed in the context of the Event. It can be replaced in a given event for different behavior:
(a: 6, b: 7, play: { (~a * ~b).postln }).play;+
Events also specify timing within a Pattern. Event's delta
method returns the value of ~delta
or, if that is nil, ~dur * ~stretch
.
+
Patterns are played by TempoClocks, which have their own tempo controls. This tempo which can be controlled through ~tempo
in the event. Changes to the tempo affect everything else scheduled by the TempoClock, so tempo
provides a global tempo control while stretch
provides a control limited to the one pattern.
The default event used in most cases. This is a private class variable. See *default. +
The default parent event provides the collection of default values and functions needed for the different uses of an Event. These defaults are defined in partialEvents that specify distinct aspects of default parent Event:
playerEvent // defines ~play, ~type and ~eventTypes +serverEvent // server, group, addAction +durEvent // duration, tempo and articulation +ampEvent // volume, pan, MIDI velocity +pitchEvent // pitch specified in many different ways +bufferEvent // buffers on the server +midiEvent // defines the sending of midi messages+
Using Events is largely a matter of overwriting keys. Here is a list of keys useful for defining notes with their default values, grouped by the partialEvent within which they are defined.
The keys in serverEvent provide the values needed to identify the server to be used and where in the tree of nodes to place the group.
server: nil, // if nil, Server.default is used +instrument: \default, // this is the name of a SynthDef +group: 1, // nodeID of group on the server + // when adding before or after a node + // this could be the nodeID of a synth instead of a group +addAction: 0, // 0, 1, 2, 3 or \addToHead, \addToTail, \addBefore, \addAfter +out: 0, // usually an output bus number, but depends on the SynthDef+
The ampEvent determines volume. Notice that ~amp
is a function that determines its value from ~db
. The user can choose to specify the amplitude directly by overwriting ~amp
or to use a decibel specification by overwriting ~db
.
amp: #{ ~db.dbamp }, // the amplitude +db: -20.0, // the above described in decibel +pan: 0.0, // pan position: -1 left 1 right +velocity: 64 // midi velocity +trig: 0.5 // trigger value+
The durEvent has keys that determine the timing of a note. Notice that ~sustain
is a function that uses ~legato
to determine the sustain. Like ~amp
this can be overwritten to set the sustain directly.
tempo: nil, // changes tempo of a TempoClock +dur: 1.0, // time until next note (inter-onset time) +stretch: 1.0, // inverse of tempo control, specific to the Event's stream +legato: 0.8, // ratio of sustain to duration +sustain: #{ ~dur * ~legato * ~stretch }, +lag: 0.0, // delay (in seconds) relative to current time position of Stream +timingOffset: 0.0, // delay (in beats) relative to current time position of Stream +strum: 0.0 // "breaks" a chord. May be negative, playing the chord backward +strumEndsTogether: false // if true, the strummed notes end together (with gated synths) +sendGate: nil // override: true == always send a gate-release message; false == never send+
The pitchEvent has the most complex system of functions that provide a variety of useful ways to determine pitch:
freq (->440) // determines the pitch directly as a frequency in Hertz +midinote (-> 60) // determines pitch as a fractional MIDI note (69 -> 440) +note (-> 0) // determines pitch as a scale degree in an ~stepsPerOctave equal tempered scale +degree: 0 // determines pitch as a scale degree within the scale ~scale+ +
The event also provides a set of transposition keys:
mtranspose: 0 // modal transposition of degree within a scale +root: 0.0 // transposes root of the scale +gtranspose: 0.0 // gamut transposition within the ~stepsPerOctave equal tempered scale +ctranspose: 0.0 // chromatic transposition within the 12 tone equal tempered scale +harmonic: 1.0 // multiplies the frequency determined by ~midinote, typically to an overtone +detune: 0.0 // directly offsets frequency by adding this value +midiToCps // a function taking a MIDI note number and turning it into frequency + // Normally this is _.midicps, but you can override it for non-ET tunings + +mtranspose: 0, // modal transposition of degree +gtranspose: 0.0 // gamut transposition of note within a ~stepsPerOctave e.t. scale +ctranspose: 0.0 // chromatic transposition of midinote within 12 tone e.t. scale + +octave: 5.0 // octave offest of note +root: 0.0 // root of the scale +degree: 0 // degree in scale +scale: #[0, 2, 4, 5, 7, 9, 11] // diatonic major scale +stepsPerOctave: 12.0 // +detune: 0.0, // detune in Hertz +harmonic: 1.0 // harmonic ratio +octaveRatio: 2.0 // size of the octave (can be used with the Scale class)+ +
The event calculates with these keys to derive parameters needed for the synth:
note: #{ // note is the note in halftone steps from the root + (~degree + ~mtranspose).degreeToKey(~scale, ~stepsPerOctave); +} +midinote: #{ // midinote is the midinote (continuous intermediate values) + ((~note.value + ~gtranspose + ~root) / ~stepsPerOctave + ~octave) * 12.0; +} +freq: #{ + (~midinote.value + ~ctranspose).midicps * ~harmonic; +} +detunedFreq: #{ // finally sent as "freq" to the synth as a parameter, if given + ~freq.value + ~detune +}+ +
An Event responds to a play message by evaluating ~play
in the event, which by default uses the event's type to define the action to be performed. See Event types.
Wrapper for a TempoClock that deals with the special scheduling queue required for externally driven scheduling. Subclasses of ExternalClock may deal with the case of a ServerClock (server side clock, as for instance from beat tracking UGens), or control from external applications. +
Because of Server latency, perceptual attack time of events and timing groove deviations, the next beat's worth of music must be generated before that beat, and cannot be rescheduled with changes of tempo. You add instances (individual bbcutters, patterns etc) to the clock. +
Note that the scheduler compensates for latency- it is built for real-time reactions, so the latency is taken into account to avoid network jitter in communicating with the Server, but there is no delay from the required time of an event. Also, perceptual attack time and time deviations (possibly due to groove/expressive timing) of events are factored in. PAT necessitates pre-scheduling, expressive timing can go either way. +
warnings of undefined behaviour: +
Tempo is assumed in a moderate range 1-4 bps say. +
s.latency must be small, ie 0.05 sec. +
Using with Patterns- you can play EventStreams only. See EventStreamPlayer2 class file. +
Using as a general scheduler: this scheduler assumes that everything it schedules is an Event that has a \play field, within which is a Function. For default Events this will be the same result as Event.play. For your own special Events you require at least (play:{"something".postln;})
tempoclock |
+ A TempoClock object. If a SimpleNumber gets passed in, a new TempoClock is created at that bps. The default is to use TempoClock.default; |
s=Server.default; + +s.latency=0.05; + +c=ExternalClock(TempoClock(2.1)).play; + +//playExt= play on External Clock +p=Pbind.new.playExt(c); + +q= Pbind(\dur,Pseq([0.25,0.25,0.5],inf),\freq,Pseq([330,330,550],inf),\sustain, 0.1).playExt(c); + +q.mute; +q.unmute; + +p.stop; +q.stop; + +~buffers= BBCutBuffer.array([Platform.resourceDir +/+ "sounds/break.aiff",Platform.resourceDir +/+ "sounds/break2.aiff"],[8,4]); + +//will be added to run on this clock as soon as made +a=BBCut2(CutBuf2(~buffers[0],0.3), BBCutProc11.new).play(c); + +b=BBCut2(CutBuf2(~buffers[1],0.5,dutycycle:0.4), BBCutProc11.new).play(c); + +//stream cut audio in +g=BBCut2(CutStream1(8), ChooseCutProc(0.5,4)).play(c); + + +//remove the first from the clock- don't worry if there are FAILURE messages, some latency queued OSC messages +//are still waiting that were already sent to the server, they shouldn't cause too much trouble +a.end + +c.tempoclock.tempo_(1.5); + +b.end + +//remove everything and terminate TempoClock +c.stop;+
A non-interpolating sound generator based on the difference equations:
x(n+1) = sin(im * y(n) + fb * x(n)) + y(n+1) = (a * y(n) + c) % 2pi+
This uses a linear congruential function to drive the phase indexing of a sine wave. For im = 1
, fb = 0
, and a = 1
a normal sinewave results.
+
sclang code translation:
( +var im = 1, fb = 0.1, a = 1.1, c = 0.5, xi = 0.1, yi = 0.1, size = 64; +plot(size.collect { xi = sin((im * yi) + (fb * xi)); yi = (a * yi + c) % 2pi; xi }); +)+
freq |
+ Iteration frequency in Hertz |
im |
+ Index multiplier amount |
fb |
+ Feedback amount |
a |
+ Phase multiplier amount |
c |
+ Phase increment amount |
xi |
+ Initial value of x |
yi |
+ Initial value of y |
mul | |
add |
// default initial params +{ FBSineN.ar(SampleRate.ir/4) * 0.2 }.play(s);+
// increase feedback +{ FBSineN.ar(SampleRate.ir, 1, Line.kr(0.01, 4, 10), 1, 0.1) * 0.2 }.play(s);+
// increase phase multiplier +{ FBSineN.ar(SampleRate.ir, 1, 0, XLine.kr(1, 2, 10), 0.1) * 0.2 }.play(s);+
// modulate frequency and index multiplier +{ FBSineN.ar(LFNoise2.kr(1, 1e4, 1e4), LFNoise2.kr(1,16,17), 1, 1.005, 0.7) * 0.2 }.play(s);+
// randomly modulate params +( +{ FBSineN.ar( + LFNoise2.kr(1, 1e4, 1e4), + LFNoise2.kr(1, 32, 33), + LFNoise2.kr(1, 0.5), + LFNoise2.kr(1, 0.05, 1.05), + LFNoise2.kr(1, 0.3, 0.3) +) * 0.2 }.play(s); +)+ +
FM7 implements a 6x6 oscillator matrix, where each oscillator's phase can be modulated by any of the other oscillators' output.
The UGen expects two matrices: one for oscillator parameter control, one for phase modulation. It outputs six channels, one for each oscillator. To select specific outputs, you can use array methods like at(indices)
or slice(indices)
.
ctlMatrix |
+ specifies the three parameters frequency (can be modulated at control rate), phase (set at initialization only) and amplitude.(control rate): + |
modMatrix |
+ The modulation matrix specifies the amount of modulation each oscillator output has on another oscillator's phase. Row i in the matrix refer to oscillator i's phase input and the columns denote the amount of phase modulation in radians. + The UGen outputs the six individual oscillator signals. + |
Provided a number for a wiring (called 'algorithm' in the FM-world), returns an instance that has this setup. All 32 algorithms from the DX7 are implemented.
algo |
+ The id of the algorithms [0..31] + the algo argument chooses the algorithm when the ugen is instantiated. It cannot be modulated. |
ctlMatrix |
+ See above. |
feedback |
+ One global parameter used in all places where the interconnections result in feedback. + |
Very fast sine wave generator (2 PowerPC instructions per output sample!) implemented using a ringing filter. This generates a much cleaner sine wave than a table lookup oscillator and is a lot faster. However, the amplitude of the wave will vary with frequency. Generally the amplitude will go down as you raise the frequency and go up as you lower the frequency.
freq |
+ Frequency in Hertz. |
iphase |
+ Initial phase offset. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
{ FSinOsc.ar(800) * 0.2 }.play; + +{ FSinOsc.ar(XLine.kr(200, 4000, 1)) * 0.2 }.play; + +// loses amplitude towards the end +{ FSinOsc.ar(FSinOsc.ar(XLine.kr(4, 401, 8), 0.0, 200, 800)) * 0.2 }.play;+
Generates a set of harmonics around a formant frequency at a given fundamental frequency.
fundfreq |
+ Fundamental frequency in Hertz. (control rate) |
formfreq |
+ Formant frequency in Hertz. (control rate) |
bwfreq |
+ Pulse width frequency in Hertz. Controls the bandwidth of the formant. (control rate) + Must be greater than or equal to |
mul | |
add |
The frequency inputs are read at control rate only, so if you use an audio rate UGen as an input, it will only be sampled at the start of each audio synthesis block.
Some UGens set a 'done' flag when they are finished playing. FreeSelfWhenDone will free the enclosing synth when this flag is set to true. +
See Done for a complete list of these UGens. +
Note that many of these UGens have doneActions, which are another way of accomplishing the same thing. See Done for more detail.
src |
+ the UGen to check for done. |
s.boot; + +// simple example +( +{ var env; +env = Line.kr(0, 1, 1); +FreeSelfWhenDone.kr(env); // free synth at end of line +SinOsc.ar(200, 0, 0.5) * env +}.play; +) + +// the previous example works, because FreeSelfWhenDone operates on the Line +// this version won't work +( +{ var env, output; +env = Line.kr(0, 1, 1); +output = SinOsc.ar(200, 0, 0.5) * env; +output.postln; // output is a BinaryOpUGen, which has no 'done' flag +FreeSelfWhenDone.kr(output); // won't ever be done +output +}.play; +) + +// record for four seconds +b = Buffer.alloc(s, 44100 * 4.0, 1); +( +SynthDef("help-RecordBuf",{ arg out=0,bufnum=0; + var formant, recbuf; + formant = Formant.ar(XLine.kr(400,1000, 4), 2000, 800, 0.125); + recbuf = RecordBuf.ar(formant, bufnum, recLevel: Line.kr(1, 1), loop: 0); + // The RecordBuf doesn't loop, so you can check it for 'done' status + FreeSelfWhenDone.kr(recbuf); +}).play(s,[\out, 0, \bufnum, b]); +) + +// play it back +( +SynthDef("help-RecordBuf play",{ arg out=0,bufnum=0; + var playbuf; + playbuf = PlayBuf.ar(1,bufnum); + FreeSelfWhenDone.kr(playbuf); // frees the synth when the PlayBuf is finished + Out.ar(out, playbuf); +}).play(s,[\out, 0, \bufnum, b]); +)+
Coded from experiments with faust.
in |
+ input signal channel 1. |
in2 |
+ input signal channel 2. |
mix |
+ dry/wet balance. range 0..1. |
room |
+ room size. rage 0..1. |
damp |
+ Reverb HF damp. range 0..1. |
mul | |
add |
Valid parameter range from 0 to 1. Values outside this range are clipped by the UGen.
s.boot; + +// FreeVerb2 - demo synthdef +( +SynthDef(\FreeVerb2x2, { |out, mix = 0.25, room = 0.15, damp = 0.5, amp = 1.0| + var signal; + + signal = In.ar(out, 2); + + ReplaceOut.ar(out, + FreeVerb2.ar( // FreeVerb2 - true stereo UGen + signal[0], // Left channel + signal[1], // Right Channel + mix, room, damp, amp + ) + ); // same params as FreeVerb 1 chn version + +}).add; +) + +// 2ch source +( +a = SynthDef(\src2x2, { |out| + Out.ar(out, + Decay.ar(Impulse.ar(1), 0.25, LFCub.ar(1200, 0, 0.1)) ! 2 + + + Pan2.ar( + Decay.ar(Impulse.ar(1, pi), 0.1, WhiteNoise.ar(0.1)), + LFNoise1.kr(0.5).range(-1, 1) + ) + ) +}).play +) + +// kick it in +z = Synth(\FreeVerb2x2, [\outbus, 0], addAction:\addToTail) +// experiment with some settings +z.set(\room, 0.7) +z.set(\mix, 0.33) +z.set(\damp, 0.9) + +// silence +[a, z].do(_.free)+ +
A Function is a reference to a FunctionDef and its defining context Frame. When a FunctionDef is encountered in your code it is pushed on the stack as a Function. A Function can be evaluated by using the 'value' method. See the Functions help file for a basic introduction. +
Because it inherits from AbstractFunction, Functions respond to math operations by creating a new Function.
// example +( +var a, b, c; +a = { [100, 200, 300].choose }; // a Function +b = { 10.rand + 1 }; // another Function +c = a + b; // c is a Function. +c.value.postln; // evaluate c and print the result +)+ +
See AbstractFunction for function composition examples. +
Because Functions are such an important concept, here some examples from related programming languages with functions as first class objects:
// returning the first argument itself: +{ |x| x }.value(1) // SuperCollider +[:x | x ] value: 1 // Smalltalk +((lambda (x) x) 1) // Lisp+
The global pseudo-variable thisFunction
always evaluates to the current enclosing Function.
See also: thisFunctionDef
Get the definition ( FunctionDef ) of the Function.
returns true if the function is closed, i.e. has no external references and can thus be converted to a compile string safely.
Evaluates the FunctionDef referred to by the Function. The Function is passed the args given.
{ |a, b| (a * b).postln }.value(3, 10); +{ arg a, b; (a * b).postln }.value(3, 10); // different way of expressing the same+
Evaluates the FunctionDef referred to by the Function. If the last argument is an Array or List, then it is unpacked and appended to the other arguments (if any) to the Function. If the last argument is not an Array or List then this is the same as the 'value' method.
{ |a, b, c| ((a * b) + c).postln }.valueArray([3, 10, 7]); + +{ |a, b, c, d| [a, b, c, d].postln }.valueArray([1, 2, 3]); + +{ |a, b, c, d| [a, b, c, d].postln }.valueArray(9, [1, 2, 3]); + +{ |a, b, c, d| [a, b, c, d].postln }.valueArray(9, 10, [1, 2, 3]);+ +
A common syntactic shortcut:
{ |a, b, c| ((a * b) + c).postln }.value(*[3, 10, 7]);+
As value above. Unsupplied argument names are looked up in the current Environment.
( +Environment.use({ +~a = 3; +~b = 10; +{ |a, b| (a * b).postln }.valueEnvir; +}); +)+
Evaluates the FunctionDef referred to by the Function. If the last argument is an Array or List, then it is unpacked and appended to the other arguments (if any) to the Function. If the last argument is not an Array or List then this is the same as the 'value' method. Unsupplied argument names are looked up in the current Environment.
Evaluate the function, using arguments from the supplied environment This is slightly faster than valueEnvir and does not require replacing the currentEnvironment
( +e = Environment.make({ ~a = 3; ~b = 10 }); +{ |a, b| (a * b) }.valueWithEnvir(e); +)+
For Function, this behaves the same as valueArray(arglist). It is used where Functions and other objects should behave differently to value, such as in the object prototyping implementation of Environment.
a = { |a, b, c| postf("% plus % plus % is %\n", a, b, c, a + b + c); "" }; +a.performWithEnvir(\value, (a: 1, c: 3, d: 4, b: 2));+
selector |
+ A Symbol representing a method selector. |
envir |
+ The remaining arguments derived from the environment and passed as arguments to the method named by the selector. |
a = { |a, b, c| postf("% plus % plus % is %\n", a, b, c, a + b + c); "" }; +a.performKeyValuePairs(\value, [\a, 1, \b, 2, \c, 3, \d, 4]);+
selector |
+ A Symbol representing a method selector. |
pairs |
+ Array or List with key-value pairs. |
Repeat this function. Useful with Task and Clocks.
t = Task({ { "I'm loopy".postln; 1.wait;}.loop }); +t.start; +t.stop;+
Delay the evaluation of this Function by delta
in seconds on AppClock.
+
This is equivalent to AppClock.sched(0, function)
unless delta
is nil
. In that case the function is only scheduled if current code is not running on AppClock, otherwise the function is evaluated immediately.
{ "2 seconds have passed.".postln; }.defer(2); + +( +{ "chicken".postln }.defer(0); // schedules on the AppClock +{ "egg".postln }.defer // evaluates immediately +) + +( +fork { // schedules on a TempoClock + { "chicken".postln }.defer // schedules on the AppClock +}; +{ "egg".postln }.defer // evaluates immediately +)+
Return an Array consisting of the results of n evaluations of this Function.
x = { 4.rand; }.dup(4); +x.postln;+
equivalent to dup(n)
x = { 4.rand } ! 4; +x.postln;+
return the sum of n values produced.
{ 4.rand }.sum(8);+
evaluates the function. This makes it polymorphic to SequenceableCollection, Bag and Set.
[{ 100.rand }, [20, 30, 40]].collect(_.choose);+
Returns the amount of time this function takes to evaluate. print is a boolean indicating whether the result is posted. The default is true.
{ 1000000.do({ 1.0.rand }); }.bench;+
Returns a Routine using the receiver as it's function, and plays it in a TempoClock.
{ 4.do({ "Threading...".postln; 1.wait;}) }.fork;+
If needed, creates a new Routine to evaluate the function in, if the message is called within a routine already, it simply evaluates it.
f = { 4.do({ "Threading...".postln; 1.wait;}) }; +f.forkIfNeeded; +{ "we are now in a routine".postln; 1.wait; f.forkIfNeeded }.fork;+
Break from a loop. Calls the receiver with an argument which is a function that returns from the method block. To exit the loop, call .value on the function passed in. You can pass a value to this function and that value will be returned from the block method.
block {|break| + 100.do {|i| + i.postln; + if (i == 7) { break.value(999) } + }; +}+
Return a Thunk, which is an unevaluated value that can be used in calculations
x = thunk { 4.rand }; +x.value; +x.value;+
Return a function that, when evaluated with nested arguments, does multichannel expansion by evaluating the receiver function for each channel. A flopped function responds like the "map" function in languages like Lisp.
f = { |a, b| if(a > 0) { a + b } { -inf } }.flop; +f.value([-1, 2, 1, -3.0], [10, 1000]); +f.value(2, 3);+
like flop, but implements an environment argument passing (valueEnvir). Less efficient in generation than flop, but not a big difference in evaluation.
f = { |a| if(a > 0) { a + 1 } { -inf } }.envirFlop; +e = (a: [20, 40]); +e.use { f.value }+
returns an "environment-safe" function. See Environment for more details.
// prints nil because ~a is read from topEnvironment, not e +e = (a: "got it", f: { { ~a.postln }.defer(0.5) }); +e.use { e.f }; + +// prints "got it" because { ~a.postln } is now bound to the e environment +e = (a: "got it", f: { { ~a.postln }.inEnvir.defer(0.5) }); +e.use { e.f };+
Function implements a case method which allows for conditional evaluation with multiple cases. Since the receiver represents the first case this can be simply written as pairs of test functions and corresponding functions to be evaluated if true. Unlike Object-switch, this is inlined and is therefore just as efficient as nested if statements.
( +var i, x, z; +z = [0, 1, 1.1, 1.3, 1.5, 2]; +i = z.choose; +x = case + { i == 1 } { \no } + { i == 1.1 } { \wrong } + { i == 1.3 } { \wrong } + { i == 1.5 } { \wrong } + { i == 2 } { \wrong } + { i == 0 } { \true }; +x.postln; +)+
Interface shared with other classes that implements pattern matching. See also: matchItem. Function.matchItem evaluates the function with the item as argument, expecting a Boolean as reply. +
See also matchItem.
{ |x| x > 5 }.matchItem(6); // true+
use a function as a conversion from scale degree to note number. See also SequenceableCollection and Scale
// a strange mapping +( +var f = {|degree, stepsPerOctave, acc| + (1.8 ** (degree % stepsPerOctave) + acc).postln +}; +Pbind( + \scale, f, + \degree, Pseq([0, 1, 2b, 3s, 4s, 6, 14, [0, 2, 4], [1, 3, 6]], inf) +).play +)+
For the following two methods a return ^ inside of the receiver itself cannot be caught. Returns in methods called by the receiver are OK.
Executes the receiver. If an exception is thrown the catch function handler is executed with the error as an argument. handler itself can rethrow the error if desired.
Executes the receiver. The cleanup function handler is executed with an error as an argument, or nil if there was no error. The error continues to be in effect.
This is probably the simplest way to get audio in SC3. It wraps the Function in a SynthDef (adding an Out ugen if needed), creates and starts a new Synth with it, and returns the Synth object. A Linen is also added to avoid clicks, which is configured to allow the resulting Synth to have its \gate argument set, or to respond to a release message. Args in the function become args in the resulting def.
x = { |freq = 440| SinOsc.ar(freq, 0, 0.3) }.play; // this returns a Synth object; +x.set(\freq, 880); // note you can set the freq argument +x.defName; // the name of the resulting SynthDef (generated automatically in a cycle of 512) +x.release(4); // fadeout over 4 seconds+ +
Many of the examples make use of the Function.play syntax. Note that reusing such code in a SynthDef requires the addition of an Out ugen.
// the following two lines produce equivalent results +{ SinOsc.ar(440, 0, 0.3) }.play(fadeTime: 0.0); +SynthDef(\help_FuncPlay, { Out.ar(0, SinOsc.ar(440, 0, 0.3))}).play;+ +
Function.play is often more convenient than SynthDef.play, particularly for short examples and quick testing. The latter does have some additional options, such as lagtimes for controls, etc. Where reuse and maximum flexibility are of greater importance, SynthDef and its various methods are usually the better choice.
target |
+ a Node, Server, or Nil. A Server will be converted to the default group of that server. Nil will be converted to the default group of the default Server. |
outbus |
+ the output bus to play the audio out on. This is equivalent to Out.ar(outbus, theoutput). The default is 0. |
fadeTime |
+ a fadein time. The default is 0.02 seconds, which is just enough to avoid a click. This will also be the fadeout time for a release if you do not specify. |
addAction |
+ see Synth for a list of valid addActions. The default is \addToHead. |
args |
+ arguments |
As play above, and calls Server-scope to open a scope window in which to view the output.
{ FSinOsc.ar(440, 0, 0.3) }.scope(1)+
numChannels |
+ The number of channels to display in the scope window, starting from outbus. The default is 2. |
outbus |
+ The output bus to play the audio out on. This is equivalent to Out.ar(outbus, theoutput). The default is 0. |
fadeTime |
+ A fadein time. The default is 0.02 seconds, which is just enough to avoid a click. |
bufsize |
+ The size of the buffer for the ScopeView. The default is 4096. |
zoom |
+ A zoom value for the scope's X axis. Larger values show more. The default is 1. |
Calculates duration in seconds worth of the output of this function asynchronously, and plots it in a GUI window. Unlike play and scope it will not work with explicit Out Ugens, so your function should return a UGen or an Array of them. The plot will be calculated in realtime.
{ SinOsc.ar(440) }.plot(0.01, bounds: Window.screenBounds); + +{ {|i| SinOsc.ar(1 + i)}.dup(7) }.plot(1);+
duration |
+ The duration of the function to plot in seconds. The default is 0.01. |
server |
+ The Server on which to calculate the plot. This must be running on your local machine, but does not need to be the internal server. If nil the default server will be used. |
bounds |
+ An instance of Rect or Point indicating the bounds of the plot window. |
minval |
+ the minimum value in the plot. Defaults to -1.0. |
maxval |
+ the maximum value in the plot. Defaults to 1.0. |
separately |
+ For multi channel signals, set whether to use separate value display ranges or not. a window to place the plot in. If nil, one will be created for you. |
Calculates duration in seconds worth of the output of this function asynchronously, and returns it in a Buffer of the number of channels. This method immediately returns a buffer, which takes duration
seconds to become filled with data asynchronously. Then the action function is called.
duration |
+ The duration of the function to plot in seconds. The default is 0.01. |
server |
+ The server on which the function is calculated. The default is |
action |
+ A function that is called when the buffer is filled. It is passed the buffer as argument. |
fadeTime |
+ A fade in and out time in seconds. Only when greater than zero, an envelope is applied to the signal to avoid clicks (default: 0). |
// record a buffer +b = { Blip.ar(XLine.kr(10000, 4, 3) * [1, 1.2], 20) * 0.1 }.asBuffer(3, fadeTime:0.1) +b.plot; // after 3 seconds, you can see it. +// play the soundfile back +{ PlayBuf.ar(b.numChannels, b, LFNoise2.kr(2 ! 8).exprange(0.15, 1), loop:1).sum }.play;+
Returns a SynthDef based on this Function, adding a Linen and an Out ugen if needed.
rates |
+ An Array of rates and lagtimes for the function's arguments (see SynthDef for more details). |
prependArgs |
+ arguments |
outClass |
+ The class of the output ugen as a symbol. The default is \Out. |
fadeTime |
+ a fadein time. The default is 0. |
name |
+ the name of the SynthDef |
Performs asSynthDef (see above), sends the resulting def to the local server and returns the SynthDefs name. This is asynchronous.
x = { SinOsc.ar(440, 0, 0.3) }.asDefName; // this must complete first +y = Synth(x);+
Returns a Routine using this as its func argument.
Returns a Routine using this as its func argument.
a = r { 5.do { |i| i.rand.yield } }; +a.nextN(8);+
Returns a Prout using this as its func argument.
a = p { 5.do { |i| i.rand.yield } }; +x = a.asStream; +x.nextN(8);+ +
This is useful for using ListComprehensions in Patterns:
Pbind(\degree, p {:[x, y].postln, x<-(0..10), y<-(0..10), (x + y).isPrime }, \dur, 0.3).play;+
// no exception handler +value { 8.zorg; \didnt_continue.postln; } + +try { 8.zorg } {|error| error.postln; \cleanup.postln; }; \continued.postln; + +protect { 8.zorg } {|error| error.postln; }; \didnt_continue.postln;+
try { 123.postln; 456.throw; 789.postln } {|error| [\catch, error].postln }; + +try { 123.postln; 789.postln } {|error| [\catch, error].postln }; + +try { 123.postln; nil.throw; 789.postln } {|error| [\catch, error].postln }; + +protect { 123.postln; 456.throw; 789.postln } {|error| [\onExit, error].postln }; + +protect { 123.postln; 789.postln } {|error| [\onExit, error].postln }; + +( +try { + protect { 123.postln; 456.throw; 789.postln } {|error| [\onExit, error].postln }; +} {|error| [\catch, error].postln }; +) + +value { 123.postln; 456.throw; 789.postln } + +value { 123.postln; Error("what happened?").throw; 789.postln }+
( +a = [\aaa, \bbb, \ccc, \ddd]; +a[1].postln; +a[\x].postln; +a[2].postln; +) + +( +try { + a = [\aaa, \bbb, \ccc, \ddd]; + a[1].postln; + a[\x].postln; + a[2].postln; +} {|error| \caught.postln; error.dump } +) + +( +try { + a = [\aaa, \bbb, \ccc, \ddd]; + a[1].postln; + a[\x].postln; + a[2].postln; +} {|error| \caught.postln; error.dump; error.throw } +) + +( +protect { + a = [\aaa, \bbb, \ccc, \ddd]; + a[1].postln; + a[\x].postln; + a[2].postln; +} {|error| \caught.postln; error.dump } +)+
The global pseudo-variable thisFunctionDef
always evaluates to the current enclosing FunctionDef.
+
See also: thisFunction
Even though it is possible to change the values in the various arrays that define the FunctionDef, you should not do it, unless you like to crash.
Get the byte code array.
{ |a = 9, b = 10, c| a + b }.def.code;+
Get the source code string.
{ |a = 9, b = 10, c| a + b }.def.sourceCode.postcs;+
Get the enclosing FunctionDef or Method.
return a list of all references to a given symbol.
Get the Array of Symbols of the argument names.
{ |a = 9, b = 10, c| a + b }.def.argNames;+ +
Get the array of default values for argument and temporary variables.
{ |a = 9, b = 10, c| a + b }.def.prototypeFrame;+ +
Get the Array of Symbols of the local variable names.
{ |a = 9, b = 10, c| var x = 9; a + b + x }.def.varNames;+ +
Return a string that contains arguments and their default values for embedding in a string
{ |a = 9, b = 10, c| a + b }.def.argumentString;+
Get the Array of Symbols of the local variable names.
{ |a = 9, b = 10, c| a + b }.def.makeEnvirFromArgs;+
"Disassemble" and post the FunctionDef's byte code instructions to the text window.
SuperCollider currently supports three operating system platforms: Macintosh OSX, UNIX (Linux and FreeBSD) and Windows (with some limitations).
Graphical User Interface (GUI) code, for the most part, does not need to worry about which platform is executing the code because of the view redirect system. At any time, one and only one GUI kit is active. This determines which GUI classes will be used for rendering. These classes, the active views, have prefixes for the GUI kit that created the view object: SCWindow vs. JSCWindow vs. QWindow.
GUI kit | Code to activate | Supported platform(s) | Framework | Prefix |
Cocoa | GUI.cocoa | Mac OSX only | Cocoa | SC- |
SwingOSC | GUI.swing | All | Java + Swing | JSC- |
Qt | GUI.qt | All | Qt | Q- |
In general, users should not concern themselves with the prefixes. Instead, work with the redirect classes, which have no prefix: Window, Button, Slider, etc. The redirect class will ask the currently-selected kit which implementation class should be used. +
The GUI kit (CocoaGUI, QtGUI, SwingGUI) maps the generic view names to the implementing classes: Window --> SCWindow, QWindow or JSCWindow
. These schemes are in turn used by ViewRedirect to provide a simple cross-platform gui syntax. The GUI class provides utilities for switching kits and other cross platform tasks.
+
You can get your available schemes (depending on what you installed) with:
GUI.schemes;+ +
For a complete list of gui classes and redirects, see List of GUI classes.
As of this writing, three GUI kits are available through the GUI class: QtGUI (Qt framework), CocoaGUI (Mac OS X native) and SwingGUI (Java). Note that SwingOSC is not part of every SuperCollider distribution, so you may have to install it separately. +
You can switch the GUI kit by calling one of the following class methods:
GUI.qt; // use Qt in subsequent GUI creation procedures +GUI.cocoa; // use cocoa in subsequent GUI creation procedures +GUI.swing; // use swing in subsequent GUI creation procedures + // NOTE: If you do not have SwingOSC installed, you get + // a warning only, and do not switch; so you cannot + // accidentally disable your gui system.+ +
These methods return the new GUI kit implementation. The current implementation can be queried by calling
GUI.current; // returns the current GUI kit implementation+ +
If you want to make a GUI kit specific switch (e.g. in a class), then you should use the following instead, as on non-OSX systems the class CocoaGUI is not in the class library path, and you cannot check for an undefined class:
GUI.id; // returns the current GUI kit implementation id; this is currently either \cocoa or \swing+ +
For persistency, you can store the identifier of the kit implementation and recall the kit through the class method fromID
:
x = GUI.cocoa; +y = x.id; // store the identifier of a kit implementation +y.postln; // the id could be stored in a preferences file for example +GUI.swing; +// now switch back to the kit implementation with identifier y +GUI.fromID( y ); +GUI.current.id.postln; // --> cocoa+ +
The *use
and *useID
methods allow you to temporarily switch the kit, so as to use it only for a dedicated block of statements:
GUI.cocoa; +GUI.useID(\swing, { Array.rand( 1000, 0.0, 1.0 ).plot }); +GUI.current.id.postln; // --> still cocoa+ +
You can get a particular kit using the *get
method. You can switch to a particular kit using the *set
method:
x = GUI.get( \swing ); // note: unlike *swing and *cocoa, this does not _switch_ the current kit! +GUI.current.id.postln; // --> still cocoa +GUI.set( x ); // now we make SwingOSC the current kit +GUI.window.viewPalette;+
GUI Kits can be extended with custom classes by using their respective put
methods:
GUI.get( \cocoa ).put( \myText, SCStaticText ); +GUI.get( \swing ).put( \myText, JSCStaticText ); + +GUI.cocoa; +GUI.swing; +( + w = GUI.window.new; + GUI.myText.new( w, w.view.bounds.insetBy( 20, 20 )).string_( "schoko" ).background_( Color.red ); + w.front; +)+ +
If you intend to add extensions from within your own classes upon class library initialization time, the preferred way is to do this in the startup process:
MyGUIExtension { + *initClass { + StartUp.add({ + var scheme; + + scheme = GUI.get( \cocoa ); + if( scheme.notNil, {scheme.put( \myText, SCStaticText )}); + scheme = GUI.get( \swing ); + if( scheme.notNil, {scheme.put( \myText, JSCStaticText )}); + }); + } +}+
Sets the skin
to default values on compile.
fontSpecs: ["Helvetica", 10], +fontColor: Color.black, +background: Color(0.8, 0.85, 0.7, 0.5), +foreground: Color.grey(0.95), +onColor: Color(0.5, 1, 0.5), +offColor: Color.clear, +gap: 0@0, +margin: 2@2, +buttonHeight: 16+
Makes Cocoa (Mac OS X GUI) the current scheme and returns it. Subsequent GUI object calls to GUI are delegated to cocoa. Returns the current (cocoa) scheme.
Makes Swing (Java GUI) the current scheme and returns it. Subsequent GUI object calls to GUI are delegated to swing. Returns the current (swing) scheme.
Changes the current scheme and returns the new scheme.
id |
+ A Symbol. The identifier of the scheme to use. |
Returns the current scheme. This is useful for objects that, upon instantiation, wish to store the then-current scheme, so as to be able to consistently use the same scheme in future method calls.
Returns the scheme for a given identifier. Does not switch the current scheme.
id |
+ A Symbol. The identifier of the scheme to retrieve, such as returned by calling |
Changes the current scheme.
aScheme |
+ An instance of Symbol. The scheme to use as current scheme. |
Executes a function body, temporarily setting the current GUI scheme. This is useful inside view's action functions in order to make this function use the GUI scheme that was originally used for the view of the action, even if the scheme has been switched meanwhile.
aScheme |
+ The scheme to use during the function execution. |
func |
+ An Instance of Function. |
Same as use
but using a scheme's id as first argument.
id |
+ The id of the scheme to use during the function execution. |
func |
+ A body to execute. |
Registers a new scheme. This is typically called by external libraries in their startup procedure. If a scheme with the same identifier (scheme.id
) exists, it is overwritten.
aScheme |
+ The scheme to add. |
All method calls are mapped to the current scheme, so that for example GUI.button
can be used and is delegated to the button association of the current scheme.
selector | |
... args |
skinName |
A class variable. Returns the current scheme.
A class variable. Returns an IdentityDictionary of registered schemes.
A class variable. Returns the current skin.
A class variable. Returns an IdentityDictionary of registered skins.
A two-channel reverb UGen, based on the "GVerb" LADSPA effect by Juhana Sadeharju (kouhia at nic.funet.fi).
in |
+ mono input. |
roomsize |
+ in squared meters. |
revtime |
+ in seconds. |
damping |
+ 0 to 1, high frequency rolloff, 0 damps the reverb signal completely, 1 not at all. |
inputbw |
+ 0 to 1, same as damping control, but on the input signal. |
spread |
+ a control on the stereo spread and diffusion of the reverb signal. |
drylevel |
+ amount of dry signal. |
earlyreflevel |
+ amount of early reflection level. |
taillevel |
+ amount of tail level. |
maxroomsize |
+ to set the size of the delay lines. Defaults to roomsize + 1. |
mul | |
add |
( +SynthDef(\test, { |out, roomsize, revtime, damping, inputbw, spread = 15, drylevel, earlylevel, + taillevel| + + var a = Resonz.ar( + Array.fill(4, { Dust.ar(2) }), + 1760 * [1, 2, 4, 8], + 0.01 + ).sum * 10; + + // var a = SoundIn.ar(0); + // var a = PlayBuf.ar(1, 0); + + Out.ar(out, + GVerb.ar( + a, + roomsize, + revtime, + damping, + inputbw, + spread, + drylevel.dbamp, + earlylevel.dbamp, + taillevel.dbamp, + roomsize, 0.3) + + a + ) +}).add +) + + +s.scope(2); + +// bathroom +a = Synth(\test, [\roomsize, 5, \revtime, 0.6, \damping, 0.62, \inputbw, 0.48, \drylevel -6, \earlylevel, -11, \taillevel, -13]); +a.free; + +//living room +a = Synth(\test, [\roomsize, 16, \revtime, 1.24, \damping, 0.10, \inputbw, 0.95, \drylevel -3, \earlylevel, -15, \taillevel, -17]); +a.free; + +//church +a = Synth(\test, [\roomsize, 80, \revtime, 4.85, \damping, 0.41, \inputbw, 0.19, \drylevel -3, \earlylevel, -9, \taillevel, -11]); +a.free; + +// cathedral +a = Synth(\test, [\roomsize, 243, \revtime, 1, \damping, 0.1, \inputbw, 0.34, \drylevel -3, \earlylevel, -11, \taillevel, -9]); +a.free + +// canyon +a = Synth(\test, [\roomsize, 300, \revtime, 103, \damping, 0.43, \inputbw, 0.51, \drylevel -5, \earlylevel, -26, \taillevel, -20]); +a.free; + +s.quit;+ +
An implementation of the dynamic stochastic synthesis generator conceived by Iannis Xenakis and described in Formalized Music (1992, Stuyvesant, NY: Pendragon Press) chapter 9 (pp 246-254) and chapters 13 and 14 (pp 289-322). +
The BASIC program in the book was written by Marie-Helene Serra so I think it helpful to credit her too. +
The program code has been adapted to avoid infinities in the probability distribution functions. +
The distributions are hard-coded in C but there is an option to have new amplitude or time breakpoints sampled from a continuous controller input.
X's plan as described in chapter 13 allows the 12 segments in the period to be successively modified with each new period. Yet the period is allowed to vary as the sum of the segment durations, as figure 1 demonstrates. We can setup some memory of n (conventionally 12) points, or even simply vary successively a single point's ordinate and duration. There are thus various schemes available to us. In one, fix period T and only move the (ti, Ei) within the period. In another, have a memory of 12 segments but allow continuous modification of the inter point intervals and the amplitudes. In yet another, just have one point and random walk its amplitude and duration based on the probability distribution. In this implementation I allow the user to initialise a certain number of memory points which is up to them. To restrict the period to be unchanging, you must set rate variation to zero (dscale=0). +
SuperCollider implementation by Nick Collins.
ampdist |
+ Choice of probability distribution for the next perturbation of the amplitude of a control point. + The distributions are (adapted from the GENDYN program in Formalized Music):
Where the sinus (Xenakis' name) is in this implementation taken as sampling from a third party oscillator. See example below. | ||||||||||||||
durdist |
+ Choice of distribution for the perturbation of the current inter control point duration. | ||||||||||||||
adparam |
+ A parameter for the shape of the amplitude probability distribution, requires values in the range 0.0001 to 1 (there are safety checks in the code so don't worry too much if you want to modulate!). | ||||||||||||||
ddparam |
+ A parameter for the shape of the duration probability distribution, requires values in the range 0.0001 to 1. | ||||||||||||||
minfreq |
+ Minimum allowed frequency of oscillation for the Gendy1 oscillator, so gives the largest period the duration is allowed to take on. | ||||||||||||||
maxfreq |
+ Maximum allowed frequency of oscillation for the Gendy1 oscillator, so gives the smallest period the duration is allowed to take on. | ||||||||||||||
ampscale |
+ Normally 0.0 to 1.0, multiplier for the distribution's delta value for amplitude. An ampscale of 1.0 allows the full range of -1 to 1 for a change of amplitude. | ||||||||||||||
durscale |
+ Normally 0.0 to 1.0, multiplier for the distribution's delta value for duration. An ampscale of 1.0 allows the full range of -1 to 1 for a change of duration. | ||||||||||||||
initCPs |
+ Initialise the number of control points in the memory. Xenakis specifies 12. There would be this number of control points per cycle of the oscillator, though the oscillator's period will constantly change due to the duration distribution. | ||||||||||||||
knum |
+ Current number of utilised control points, allows modulation. | ||||||||||||||
mul | |||||||||||||||
add |
All parameters can be modulated at control rate except for initCPs
which is used only at initialisation.
//defaults +{Pan2.ar(Gendy1.ar)}.play + +//wandering bass/ powerline +{Pan2.ar(Gendy1.ar(1,1,1.0,1.0,30,100,0.3,0.05,5))}.play + +//play me +{Pan2.ar(RLPF.ar(Gendy1.ar(2,3,minfreq:20,maxfreq:MouseX.kr(100,1000),durscale:0.0,initCPs:40),500,0.3,0.2),0.0)}.play + +//scream! - careful with your ears for this one! +( +{ + var mx, my; + + mx= MouseX.kr(220,440); + my= MouseY.kr(0.0,1.0); + + Pan2.ar(Gendy1.ar(2,3,1,1,minfreq:mx, maxfreq:8*mx, ampscale:my, durscale:my, initCPs:7, mul:0.3), 0.0) +}.play +) + + +//1 CP = random noise effect +{Pan2.ar(Gendy1.ar(initCPs:1))}.play + +//2 CPs = suudenly an oscillator (though a fast modulating one here) +{Pan2.ar(Gendy1.ar(initCPs:2))}.play + + +//used as an LFO +( +{ + Pan2.ar( + SinOsc.ar( + Gendy1.kr(2, 4, + SinOsc.kr(0.1,0,0.49,0.51), + SinOsc.kr(0.13,0,0.49,0.51), + 3.4, 3.5, + SinOsc.kr(0.17,0,0.49,0.51), + SinOsc.kr(0.19,0,0.49,0.51), + 10,10,50, 350), + 0, 0.3), + 0.0) +}.play +) + +//wasp +{Pan2.ar(Gendy1.ar(0, 0, SinOsc.kr(0.1, 0, 0.1, 0.9),1.0, 50,1000, 1,0.005, 12, 12, 0.2), 0.0)}.play + + +//modulate distributions +//change of pitch as distributions change the duration structure and spectrum +{Pan2.ar(Gendy1.ar(MouseX.kr(0,7),MouseY.kr(0,7),mul:0.2), 0.0)}.play + + +//modulate num of CPs +{Pan2.ar(Gendy1.ar(knum:MouseX.kr(1,13),mul:0.2), 0.0)}.play + + +(//Gendy into Gendy...with cartoon side effects +{Pan2.ar(Gendy1.ar( + maxfreq:Gendy1.kr(5,4,0.3, 0.7, 0.1, MouseY.kr(0.1,10), 1.0, 1.0, 5,5, 500, 600), + knum:MouseX.kr(1,13),mul:0.2), 0.0) +}.play +) + +//use SINUS to track any oscillator and take CP positions from it, use adparam and ddparam as the inputs to sample +{Pan2.ar(Gendy1.ar(6,6,LFPulse.kr(100, 0, 0.4, 1.0), SinOsc.kr(30, 0, 0.5),mul:0.2), 0.0)}.play + + +//try out near the corners especially +( +{Pan2.ar(Gendy1.ar(6,6,LFPulse.kr(MouseX.kr(0,200), 0, 0.4, 1.0), + SinOsc.kr(MouseY.kr(0,200), 0, 0.5),mul:0.2), 0.0)}.play +) + +//texture +( +{ +Mix.fill(10,{ +var freq; + +freq= rrand(130,160.3); +Pan2.ar(SinOsc.ar(Gendy1.ar(6.rand,6.rand,SinOsc.kr(0.1,0,0.49,0.51), + SinOsc.kr(0.13,0,0.49,0.51),freq ,freq, SinOsc.kr(0.17,0,0.49,0.51), + SinOsc.kr(0.19,0,0.49,0.51), 12, 12, 200, 400), 0, 0.1), 1.0.rand2) +}); +}.play +) + +//wahhhhhhhh- try durscale 10.0 and 0.0 too +( +{Pan2.ar( +CombN.ar( +Resonz.ar( +Gendy1.ar(2,3,minfreq:1, maxfreq:MouseX.kr(10,700), durscale:0.1, initCPs:10), +MouseY.kr(50,1000), 0.1) +,0.1,0.1,5, 0.6 +) +, 0.0)}.play +) + +//overkill +( +{ +var n; +n=10; + +Mix.fill(n,{ +var freq, numcps; + +freq= rrand(130,160.3); +numcps= rrand(2,20); +Pan2.ar(Gendy1.ar(6.rand,6.rand,1.0.rand,1.0.rand,freq ,freq, 1.0.rand, 1.0.rand, numcps, + SinOsc.kr(exprand(0.02,0.2), 0, numcps/2, numcps/2), 0.5/(n.sqrt)), 1.0.rand2) +}); +}.play +) + +//another traffic moment +( +{ + var n; + n=10; + + Resonz.ar( + Mix.fill(n,{ + var freq, numcps; + + freq= rrand(50,560.3); + numcps= rrand(2,20); + Pan2.ar(Gendy1.ar(6.rand,6.rand,1.0.rand,1.0.rand,freq ,freq, 1.0.rand, 1.0.rand, numcps, + SinOsc.kr(exprand(0.02,0.2), 0, numcps/2, numcps/2), 0.5/(n.sqrt)), 1.0.rand2) + }) + ,MouseX.kr(100,2000), MouseY.kr(0.01,1.0)) + ; +}.play +) + +( +{ +var n; +n=15; + +Out.ar(0, +Resonz.ar( +Mix.fill(n,{ +var freq, numcps; + +freq= rrand(330,460.3); +numcps= rrand(2,20); +Pan2.ar(Gendy1.ar(6.rand,6.rand,1.0.rand,1.0.rand,freq,MouseX.kr(freq,2*freq), 1.0.rand, 1.0.rand, numcps, + SinOsc.kr(exprand(0.02,0.2), 0, numcps/2, numcps/2), 0.5/(n.sqrt)), 1.0.rand2) +}) +,MouseX.kr(100,2000), MouseY.kr(0.01,1.0)) +) + +}.play; +)+
Generates noise which results from flipping random bits in a word. This type of noise has a high RMS level relative to its peak to peak level. The spectrum is emphasized towards lower frequencies.
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
( +SynthDef("help-GrayNoise", { arg out=0; + Out.ar(out, + GrayNoise.ar(0.1) + ) +}).play; +)+
Outputs non-bandlimited single sample impulses.
freq |
+ Frequency in Hertz. |
phase |
+ Phase offset in cycles (0..1). |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
An Impulse with frequency 0 returns a single impulse.
{ Impulse.ar(800, 0.0, 0.5, 0) }.play + +{ Impulse.ar(XLine.kr(800,100,5), 0.0, 0.5, 0) }.play+ +
modulate phase:
{ Impulse.ar(4, [0, MouseX.kr(0, 1)], 0.2) }.play;+ +
an Impulse with frequency 0 returns a single impulse:
SynthDef(\imp, { OffsetOut.ar(0, Impulse.ar(0)); FreeSelf.kr(Impulse.kr(0)); }).add; +fork { (1 / (1..60).scramble).do { |dt| Synth.grain(\imp); dt.wait } };+
When the various output UGens ( Out , OffsetOut , XOut ) write data to a bus, they mix it with any data from the current cycle, but overwrite any data from the previous cycle. ( ReplaceOut overwrites all data regardless.) Thus depending on node order and what synths are writing to the bus, the data on a given bus may be from the current cycle or be one cycle old at the time of reading. In.ar checks the timestamp of any data it reads in and zeros any data from the previous cycle (for use within that node; the data remains on the bus). This is fine for audio data, as it avoids feedback, but for control data it is useful to be able to read data from any place in the node order. For this reason In.kr also reads data that is older than the current cycle. +
In some cases we might also want to read audio from a node later in the current node order. This is the purpose of InFeedback. The delay introduced by this is one block size, which equals about 0.0014 sec at the default block size and sample rate. (See the resonator example below to see the implications of this.) +
The variably mixing and overwriting behaviour of the output UGens can make order of execution crucial. (No pun intended.) For example with a node order like the following the InFeedback UGen in Synth 2 will only receive data from Synth 1 (→ = write out; ← = read in):
If Synth1 were moved after Synth2 then Synth2's InFeedback would receive a mix of the output from Synth1 and Synth3. This would also be true if Synth2 came after Synth1 and Synth3. In both cases data from Synth1 and Synth3 would have the same time stamp (either current or from the previous cycle), so nothing would be overwritten. +
Because of this it is often useful to allocate a separate bus for feedback. With the following arrangement Synth2 will receive data from Synth3 regardless of Synth1's position in the node order:
The second example below demonstrates this issue.
bus |
+ The index of the bus to read in from. |
numChannels |
+ The number of channels (i.e. adjacent buses) to read in. The default is 1. You cannot modulate this number by assigning it to an argument in a SynthDef. |
audio feedback modulation:
( +SynthDef("help-InFeedback", { arg out=0, in=0; + var input, sound; + input = InFeedback.ar(in, 1); + sound = SinOsc.ar(input * 1300 + 300, 0, 0.4); + Out.ar(out, sound); + +}).play; +)+ +
this shows how a node can read audio from a bus that is being written to by a synth following it:
( +SynthDef("help-InFeedback", { arg out=0, in=0; + Out.ar(out, + InFeedback.ar(in, 1) + ); +}).add; +SynthDef("help-SinOsc", { arg out=0, freq=440; + Out.ar(out, SinOsc.ar(freq, 0, 0.1)) +}).add; +) + +x = Bus.audio(s, 1); + +// read from bus n play to bus 0 (silent) +a = Synth("help-InFeedback",[\in, x.index, \out, 0]); + +// now play a synth after this one, playing to bus x +b = Synth.after(a, "help-SinOsc", [\out, x.index]); + +// add another synth before a which also writes to bus x +// now you can't hear b, as its data is one cycle old, and is overwritten by c +c = Synth.before(a, "help-SinOsc", [\out, x.index, \freq, 800]); + +// free c and you can hear b again +c.free; +x.free; + +a.free; b.free;+ +
The example below implements a resonator. Note that you must subtract the blockSize in order for the tuning to be correct. See LocalIn for an equivalent example.
( +var play, imp, initial; +SynthDef("testRes", { + +play = InFeedback.ar(10, 1); // 10 is feedback channel +imp = Impulse.ar(1); + +// feedback +OffsetOut.ar(10, DelayC.ar(imp + (play * 0.995), 1, + 440.reciprocal - ControlRate.ir.reciprocal)); // subtract block size + +OffsetOut.ar(0, play); + +}).play(s); + +// Compare with this for tuning +{ SinOsc.ar(440, 0, 0.2) }.play(s, 1); +)+
A 32 bit integer. Integer inherits most of its behaviour from its superclass.
Executes function for all integers from zero to this minus one.
function |
+ a Function which is passed two arguments, both of which are the same integer from zero to this minus one. The reason two arguments are passed is for symmetry with the implementations of do in Collection. |
Executes function for all integers from this minus one to zero.
Executes function for all integers from this to endval, inclusive.
endval |
+ an Integer. |
function |
+ a Function which is passed two arguments, the first which is an integer from this to endval, and the second which is a number from zero to the number of iterations minus one. |
Executes function for all integers from this to endval, inclusive, stepping each time by stepval.
endval |
+ an Integer. |
stepval |
+ an Integer. |
function |
+ a Function which is passed two arguments, the first which is an integer from this to endval, and the second which is a number from zero to the number of iterations minus one. |
an Array of this size filled by objects generated from evaluating the function.
a Collection of class of this size filled by objects generated from evaluating the function.
an Interval from this to hi.
an array with a geometric series of this size from start.
an array with a fibonacci series of this size beginning with a and b.
the prime factors as array.
the factorial of this.
See also: Randomness
exclude |
+ an Integer. |
a random value from zero to this, excluding the value exclude.
exclude |
+ an Integer. |
a random value from this.neg to this, excluding the value exclude.
a Char which has the ASCII value of the receiver.
a Char which represents the receiver as an ASCII digit.
For example 5.asDigit
returns $5
.
an array with the binary digits (integer 0 or 1).
an array with the n-ary digits.
See also the complementary method SequenceableCollection: -convertDigits.
2007.asDigits; +2007.asDigits(2);+
a string with the binary digits (0 or 1).
a string with the hexadecimal digits (integer 0 to F).
a string in IP format.
Interpret this as index into a scale with a given number of steps per ocatve.
2.degreeToKey([0, 2, 5, 7, 11]);+
the gray code for the number.
2.grayCode+
set nth bit to zero (bool = false) or one (bool = true)
{ _CLZ }
{ _CTZ }
number of required bits
true if dividable by 2 with no rest
true if not dividable by 2 with no rest
the next power of two greater than or equal to the receiver.
13.nextPowerOfTwo.postln; +64.nextPowerOfTwo.postln;+
the whether the receiver is a power of two.
13.isPowerOfTwo.postln; +64.isPowerOfTwo.postln;+
the nth prime number. The receiver must be from 0 to 6541.
[0,1,2,3,4,5].collect({ arg i; i.nthPrime; }).postln;+
the next prime less than or equal to the receiver up to 65521.
25.prevPrime.postln;+
the next prime less than or equal to the receiver up to 65521.
25.nextPrime.postln;+
whether the receiver is prime.
25.isPrime.postln; +13.isPrime.postln;+
the index of a prime number less than or equal to the receiver up to 65521. If the receiver is not a prime, the answer is nil.
23.indexOfPrime; +25.indexOfPrime;+
a Boolean for whether or not the specified pid is running.
p = "cat".unixCmd; +p.pidRunning; // cat will stay alive +("kill" + p).unixCmd +p.pidRunning;+ +
This sets up an environment in which you can build a player, build a gui for that player, and respond to midi and keyboard control. +
The gui is quite optional, and in fact non-screen-staring is one of its primary goals.
interfaceDef | |
args |
the control change handler is installed (via CCResponder) when play starts and unistalled when play stops. +
It can be a simple function: + +
a CCResponder that responds on a specific number. (note: tell it NOT to install itself, because the Interface will install and uninstall it when play starts or stops) + +
or a custom class: + +
whatever it is will be asked to respond to 'value' : +
i | |
a |
argargs |
agroup | |
bundle | |
private | |
bus | |
defWasLoaded |
b |
f |
... args |
function |
function |
stream |
You can set a custom gui function. This can use any combination of .gui style and normal Views The Interface can be placed on any other windows of any style. You may decline to customize your gui.
If you set any of these handler functions: onNoteOn onNoteOff onPitchBend onCC then appropriate midi responders will be enabled when the player starts to play and disabled when it stops. This includes if the player is being started/stopped by external mixers, PlayerPool etc. +
KeyDown/KeyUp keyDownAction keyUpAction (only when guied, only when focus is within the MetaPatch's views) +
Interface is great for having no gui at all. Personally I use the gui stuff to set up parameters for performing, and then when performing I use no gui, only MIDI controllers and key commands. +
The function that builds the player is actually an InterfaceDef. These can be created and stored in the same fashion as Instr and kept in the same folder. You can then address them by name, supply paramaters as you do for Patch and you will get an Interface which will use the gui and midi functions from the InterfaceDef.
The interpreter is an object that handles the translation and execution of code at runtime. It is that what runs any program code and defines a context for it.
( +a = 5 + 7; +this.cmdLine.postln; +)+
In the interpreter, this
refers to the interpreter itself, e.g.: this.postln
+
The interpreter defines global variables (a
… z
), that can be used for interactive programming. Except these single letter variables ("interpreter variables"), all variables have to be defined by the keyword var
(see: Assignment Statements, and Scoping and Closure).
// typical usage +a = 4; +b = 3; +b = b + a; + +// some sound +a = Synth(\default); +g = fork { loop { 0.1.wait; a.set(\freq, 200 + 20.0.rand2.postln) } }; +g.stop; a.free; + +// an overview of all the variables +this.inspect;+
s
is by convention bound to the default server (Server) and should not be changed.set the values of the variables a
through z
to nil.
x = 123; +x.postln; +this.clearAll; +x.postln;+
Compile and execute a String.
this.interpret("(123 + 4000).postln");+
Compile and execute a String, printing the result.
this.interpretPrint("123 + 4000");+
Compile a String and return a Function.
( +z = this.compile("(123 + 4000).postln"); +z.postln; +z.value; +)+
Reads the file at pathName, compiles it and returns a Function. The file must contain a valid SuperCollider expression, naturally. This will not compile class definitions, only expressions.
Reads the file at pathName, compiles it and executes it, returning the result. The file must contain a valid SuperCollider expression, naturally. This will not compile class definitions, only expressions.
Returns the previously interpreted code.
1 + 2; +this.cmdLine+
this interpreter variable can be set to evaluate a function with any successfully compiled code. see e.g. the class History.
a = [ ]; // store all the code evaluated in a +this.codeDump = { |code| a = a.add(code) }; +1 + 3; +f = { "hallo" }; +a.postcs; +codeDump = nil; // reset to nil.+
can be used to modify code before it is interpreted. Given appropriate delimiters, this can be used to implement little languages.
// silly but simple: understand a Saw for every SinOsc +this.preProcessor = { |code| code.replace("SinOsc", "Saw") }; + +{ SinOsc.ar(200) * 0.1 }.play; + +preProcessor = nil; // reset to nil.+
Global variables ("interpreter variables") for interactive programming (see Accessing).
Klang is a bank of fixed frequency sine oscillators. Klang is more efficient than creating individual oscillators but offers less flexibility.
specificationsArrayRef |
+ A Ref to an Array of three Arrays:
|
freqscale |
+ A scale factor multiplied by all frequencies at initialization time. |
freqoffset |
+ An offset added to all frequencies at initialization time. |
The parameters in specificationsArrayRef
can't be changed after it has been started. For a modulatable but less efficient version, see DynKlang.
Klank is a bank of fixed frequency resonators which can be used to simulate the resonant modes of an object. Each mode is given a ring time, which is the time for the mode to decay by 60 dB.
Klank is a bank of Ringz filters. Formlet is equivalent to Ringz.ar(... decay...) - Ring.ar(... attack...)
. Therefore, a more efficient way to make a bank of fixed-parameter Formlet filters is Klank(`decaySpecs, ...) - Klank.ar(`attackSpecs, ...)
or Klank.ar(`specs, ..., decayscale: decay) - Klank.ar(`specs, ..., decayscale: attack)
.
specificationsArrayRef |
+ A Ref to an Array of three Arrays:
All subarrays, if not nil, should have the same length. |
input |
+ The excitation input to the resonant filter bank. |
freqscale |
+ A scale factor multiplied by all frequencies at initialization time. |
freqoffset |
+ An offset added to all frequencies at initialization time. |
decayscale |
+ A scale factor multiplied by all ring times at initialization time. |
The parameters in specificationsArrayRef
can't be changed after it has been started. For a modulatable but less efficient version, see DynKlank.
Four resonators each at maximum amplitude of 1.0 and ring times of 1 second, different exciters and no scaling:
+ +
Three resonators at maximum amplitude of 1.0, random frequency and ring times. Excited by two pulses at 2 and 2.5 Hz: + +
Multichannel Expansion via an array of specs (note the ` before the opening bracket of the parameter array!): + +
A SynthDef that generates 4 partials used in different configurations: + +
Advanced examples: +
A non-band-limited pulse oscillator. Outputs a high value of one and a low value of zero.
freq |
+ frequency in Hertz |
iphase |
+ initial phase offset in cycles ( 0..1 ) |
width |
+ pulse width duty cycle from zero to one. |
mul | |
add |
a plot: + +
50 Hz wave: + +
modulating frequency: + +
amplitude modulation: + +
used as both Oscillator and LFO: + +
scope: + +
compare with band limited Pulse UGen: +
A non-band-limited sawtooth oscillator. Output ranges from -1 to +1.
freq |
+ Frequency in Hertz. |
iphase |
+ Initial phase offset. For efficiency reasons this is a value ranging from 0 to 2. NOTE: enter "1" for an initial phase of 0 radians. A value of 0 would start the cycle at pi radians. See the example below. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
Display the special behaviour of the initial phase parameter: + +
Linear predictive coding analysis on any arbitrary input signal. The spectrum of the input signal is modeled, and used to filter the source. This works most successfully if the source is spectrally flat to begin with, ie, an impulse train ( Impulse UGen ) or white noise ( WhiteNoise UGen ). +
The two big hits to CPU costs here are large n, causing peaky calculation hits once per window as autocorrelation coefficients are calculated, and p as determining the order of the filter which is fitted, and thus the per sample output calculation cost. +
(For more technicalities see John Makhoul (1975) "Linear Prediction: A Tutorial Review". Proceedings of the IEEE 63(4).)
input |
+ Original signal to analyse with the LPC model |
source |
+ Excitation to run through LPC derived filter |
n |
+ Windowsize for analysis in samples; limits of 1<=n<=1024 |
p |
+ Number of poles used to model spectrum of input within one window |
testE |
+ Whether to test for meeting of error condition, for variable number of poles solution up to p |
delta |
+ Test value, close to but just below 1. |
windowtype |
+ Windowing. 0 is rectangular window, abrupt swap of filter coefficients after every n samples. 1 is triangular window, and runs two LPCAnalyzers crossfading, for a smoother ride, at expense of double CPU cost. |
{LPCAnalyzer.ar(SoundIn.ar,Impulse.ar(440,0,0.2), 256, 50)}.play + +{LPCAnalyzer.ar(SoundIn.ar,Impulse.ar(440,0,0.2), 256, 50, windowtype:1)}.play + +{LPCAnalyzer.ar(SoundIn.ar,Impulse.ar(440,0,0.2), 128, MouseX.kr(1,128))}.play + +{LPCAnalyzer.ar(SoundIn.ar,Impulse.ar(440,0,0.2), 1024, MouseX.kr(1,128), windowtype:1)}.play + + +{LPCAnalyzer.ar(SoundIn.ar,0.1*WhiteNoise.ar, 256, MouseX.kr(1,256))}.play + +//keep number of poles lower if pushing harder with bigger windows +{LPCAnalyzer.ar(SoundIn.ar,Impulse.ar(MouseY.kr(100,1000),0,0.2), 1024, MouseX.kr(1,128),1,0.9999, 1)}.play + + +( +{ +var source,amplitude, freq, hasFreq; + +source= SoundIn.ar; + +//no need, fitted filter tracks amplitudes, essentially +//amplitude= Amplitude.kr(source); + +#freq,hasFreq= Pitch.kr(source); + +LPCAnalyzer.ar(source,if(hasFreq,Impulse.ar(freq),0.25*WhiteNoise.ar), 256,MouseX.kr(1,256), windowtype:1) + +}.play +) + + +//singing with myself after three seconds +( +{ +var source,amplitude, freq, hasFreq; + +source= SoundIn.ar; + +amplitude= Amplitude.kr(source); + +LPCAnalyzer.ar(source,CombC.ar(source,3.0,3.0,10.0), 256,MouseX.kr(1,256)).dup(2) + +}.play +) + +( +{ +var input,source; + +input= Resonz.ar(Saw.ar(SinOsc.kr([0.1,0.11],500,600)),LFNoise1.kr(LFNoise1.kr(4,3.5,7),700,2500),LFNoise2.kr(0.5,0.3,0.4)); + +source= SoundIn.ar; + +LPCAnalyzer.ar(input,source, 64,MouseX.kr(1,64), windowtype:1) + +}.play +) + + +//test variable order +{LPCAnalyzer.ar(SoundIn.ar,Impulse.ar(MouseX.kr(10,1000,'exponential'),0,0.2), 256, 256,1,0.999)}.play + +{LPCAnalyzer.ar(SoundIn.ar,Impulse.ar(MouseX.kr(10,1000,'exponential'),0,0.2), 256, 256,1,MouseY.kr(0.9,1.0).sqrt)}.play + + + + + + + + + +////////////////////////////////////// + +//residual test assumes windowtype 0) +( +{ +var input,source; +var delaytime= 1024.0/SampleRate.ir; + +input= SoundIn.ar; + +source= Impulse.ar(delaytime.reciprocal); + +(DelayN.ar(input,delaytime, delaytime)- LPCAnalyzer.ar(input,source,1024,MouseX.kr(1,256))).poll(10000) + +}.play +)+ +
A second order low pass filter.
in |
+ The input signal. |
freq |
+ Cutoff frequency in Hertz. WARNING: due to the nature of its implementation frequency values close to 0 may cause glitches and/or extremely loud audio artifacts! |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
The LanguageConfig class provides access to the interpreter configuration.
Store the current configuration to file.
file |
+ Path to the configuration file to store. If the value is |
The language configuration mechanism provides a way to add or exclude specific paths for the class library.
Return the class library include paths.
Add new class library include path.
Remove path from class library include paths.
Return the class library exclude paths.
Add new class library exclude path.
Remove path from class library exclude paths.
Return the current config file path.
Get or set the compiler flag, whether warnings should be posted if a FunctionDef cannot be inlined.
The configuration file is stored in YAML format, which contains one dictionary. The semantics of the dictionary is listed in the following table:
includePaths
excludePaths
includePaths
).postInlineWarnings
x(n+1) = sin(by(n)) + c*sin(bx(n)) +y(n+1) = sin(ay(n)) + d*sin(ax(n))+ +
x values determine frequencies; y values determine amplitudes. Stable ranges for a & b tend to be between -3 to + 3. c & d between 0.5 and 1.5. There are combinations within these ranges that are unstable, so be prepared to tweak this oscillator.
minfreq, maxfreq |
+ iteration frequency in Hertz |
a, b, c, d |
+ equation variables |
x0 |
+ initial value of x |
y0 |
+ initial value of y |
( +{ Latoocarfian2DC.ar( + SampleRate.ir/8, + SampleRate.ir/2, + LFNoise2.kr(2.dup, 1.5, 1.5), + d:LFNoise2.kr(2.dup, 0.5, 1.5), + mul:0.2 +) }.play(s); +)+ +
A linear-interpolating sound generator based on a function given in Clifford Pickover's book Chaos In Wonderland, pg 26. The function is:
x(n+1) = sin(b * y(n)) + c * sin(b * x(n)) + y(n+1) = sin(a * x(n)) + d * sin(a * y(n))+
According to Pickover, parameters a
and b
should be in the range from -3 to +3, and parameters c
and d
should be in the range from 0.5 to 1.5. The function can, depending on the parameters given, give continuous chaotic output, converge to a single value (silence) or oscillate in a cycle (tone).
+
sclang code translation:
( +var a = 1, b = 3, c = 0.5, d = 0.5, xi = 0.5, yi = 0.5, size = 64; +plot(size.collect { var x = xi; +xi = sin(b * yi) + (c * sin(b * xi)); +yi = sin(a * x) + (d * sin(a * yi)); +xi +}); +)+
freq |
+ Iteration frequency in Hertz |
a |
+ Equation variable |
b |
+ Equation variable |
c |
+ Equation variable |
d |
+ Equation variable |
xi |
+ Initial value of x |
yi |
+ Initial value of y |
// default initial params +{ LatoocarfianL.ar(MouseX.kr(20, SampleRate.ir)) * 0.2 }.play(s);+
// randomly modulate all params +( +{ LatoocarfianL.ar( + SampleRate.ir/4, + LFNoise2.kr(2,1.5,1.5), + LFNoise2.kr(2,1.5,1.5), + LFNoise2.kr(2,0.5,1.5), + LFNoise2.kr(2,0.5,1.5) +) * 0.2 }.play(s); +)+
( +{ LatoocarfianL.ar( + SampleRate.ir/4, + [LFDNoise0,LFClipNoise,LFDNoise1,LFDNoise3, + LFNoise0,LFNoise1,LFNoise2].choose.kr(rrand(2,20),rrand(2,20)*0.1,rrand(2,20)*0.2), + [LFDNoise0,LFClipNoise,LFDNoise1,LFDNoise3, + LFNoise0,LFNoise1,LFNoise2].choose.kr(rrand(2,20),rrand(2,20)*0.1,rrand(2,20)*0.2), + [LFDNoise0,LFClipNoise,LFDNoise1,LFDNoise3, + LFNoise0,LFNoise1,LFNoise2].choose.kr(rrand(2,20),rrand(2,20)*0.1,rrand(2,20)*0.2), + [LFDNoise0,LFClipNoise,LFDNoise1,LFDNoise3, + LFNoise0,LFNoise1,LFNoise2].choose.kr(rrand(2,20),rrand(2,20)*0.1,rrand(2,20)*0.2) +) * 0.2 !2}.play; +)+ +
This class is missing documentation.
Copy and paste the text below and save to HelpSource/Classes/LatoocarfianTrig.schelp
TITLE:: LatoocarfianTrig +summary:: (put short description here) +categories:: Undocumented classes, UGens>Undocumented +related:: Classes/SomeRelatedClass, Reference/SomeRelatedStuff, etc. + +DESCRIPTION:: +(put long description here) + + +CLASSMETHODS:: + +METHOD:: kr +(describe method here) + +ARGUMENT:: minfreq +(describe argument here) + +ARGUMENT:: maxfreq +(describe argument here) + +ARGUMENT:: a +(describe argument here) + +ARGUMENT:: b +(describe argument here) + +ARGUMENT:: c +(describe argument here) + +ARGUMENT:: d +(describe argument here) + +ARGUMENT:: x0 +(describe argument here) + +ARGUMENT:: y0 +(describe argument here) + +ARGUMENT:: mul +(describe argument here) + +ARGUMENT:: add +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: ar +(describe method here) + +ARGUMENT:: minfreq +(describe argument here) + +ARGUMENT:: maxfreq +(describe argument here) + +ARGUMENT:: a +(describe argument here) + +ARGUMENT:: b +(describe argument here) + +ARGUMENT:: c +(describe argument here) + +ARGUMENT:: d +(describe argument here) + +ARGUMENT:: x0 +(describe argument here) + +ARGUMENT:: y0 +(describe argument here) + +ARGUMENT:: mul +(describe argument here) + +ARGUMENT:: add +(describe argument here) + +returns:: (describe returnvalue here) + + +INSTANCEMETHODS:: + + +EXAMPLES:: + +code:: +(some example code) +:: ++
Library is a global MultiLevelIdentityDictionary. The Library can be used as a place to store data that you want globally accessible. It is an alternative to using class variables. It is a nice place to store menus, annotations, and commonly reusable functions.
Post a formatted description of the entire library.
Library.postTree;+
The last argument to put is the object being inserted:
Library.put(\multi, \level, \addressing, \system, "i'm the thing you are putting in here"); +Library.at(\multi, \level, \addressing, \system).postln; +Library.atList([\multi, \level, \addressing, \system]).postln;+
A cubic-interpolating sound generator based on the difference equation:
x(n+1) = (a * x(n) + c) % m+
The output signal is automatically scaled to a range of [-1, 1]. +
sclang code translation:
( +var a = 1.1, c = 0.13, m = 1, xi = 0, size = 64; +plot(size.collect { xi = (a * xi + c) % m }); +)+
freq |
+ Iteration frequency in Hertz |
a |
+ Multiplier amount |
c |
+ Increment amount |
m |
+ Modulus amount |
xi |
+ Initial value of x |
// default initial params +{ LinCongC.ar(MouseX.kr(20, SampleRate.ir)) * 0.2 }.play(s);+
// randomly modulate params +( +{ LinCongC.ar( + LFNoise2.kr(1, 1e4, 1e4), + LFNoise2.kr(0.1, 0.5, 1.4), + LFNoise2.kr(0.1, 0.1, 0.1), + LFNoise2.kr(0.1) +) * 0.2 }.play(s); +)+
// as frequency control... +( +{ +SinOsc.ar( + LinCongC.ar( + 40, + LFNoise2.kr(0.1, 0.1, 1), + LFNoise2.kr(0.1, 0.1, 0.1), + LFNoise2.kr(0.1), + 0, 500, 600 + ) +) * 0.4 }.play(s); +)+ +
Maps a linear range of values to another linear range of values.
in |
+ The input signal to convert. |
srclo |
+ Lower limit of input range. |
srchi |
+ Upper limit of input range. |
dstlo |
+ Lower limit of output range. |
dsthi |
+ Upper limit of output range. |
// examples: + +( +{ + var mod = SinOsc.kr(Line.kr(1, 10, 10)); + SinOsc.ar(LinLin.kr(mod, -1,1, 100, 900)) * 0.1 +}.play; +) + +// modulating destination values. +( +{ + var mod = LFNoise2.ar(80); + SinOsc.ar(LinLin.ar(mod, -1,1, MouseX.kr(200, 8000, 1), MouseY.kr(200, 8000, 1))) * 0.1 +}.play; +) + +// modulating source and destination values. +( +{ + var mod = LFNoise2.ar(80); + SinOsc.ar( + LinLin.ar(mod, + SinOsc.kr(0.2), SinOsc.kr(0.2543), + MouseX.kr(200, 8000, 1), MouseY.kr(200, 8000, 1) + ) + ) * 0.1 +}.play; +)+ +
linlin and range can be used to create a LinLin implicitly from a ugen, mapping its output values from linear range to an exponential one. The rate is derived from the ugen.
// linlin +( +{ + var mod = LFNoise2.ar(80); + SinOsc.ar(mod.linlin(-1,1, MouseX.kr(200, 8000, 1), MouseY.kr(200, 8000, 1))) * 0.1 +}.play; +) + +// range +( +{ + var mod = LFNoise2.ar(80).range(MouseX.kr(200, 8000, 1), MouseY.kr(200, 8000, 1)); + SinOsc.ar(mod) * 0.1 +}.play; +)+
Generates a line from the start value to the end value.
start |
+ Starting value. |
end |
+ Ending value. |
dur |
+ Duration in seconds. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
doneAction |
+ A doneAction to be evaluated when the Line is completed. See + Done for more detail. |
This class is missing documentation.
Copy and paste the text below and save to HelpSource/Classes/Link.schelp
TITLE:: Link +summary:: (put short description here) +categories:: Undocumented classes, UGens>Undocumented +related:: Classes/SomeRelatedClass, Reference/SomeRelatedStuff, etc. + +DESCRIPTION:: +(put long description here) + + +CLASSMETHODS:: + +METHOD:: enable +(describe method here) + +ARGUMENT:: tempo +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setTempo +(describe method here) + +ARGUMENT:: tempo +(describe argument here) + +ARGUMENT:: lagTime +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: kr +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: quantum +(describe method here) + +returns:: (describe returnvalue here) + + +INSTANCEMETHODS:: + + +EXAMPLES:: + +code:: +(some example code) +:: ++
This class is missing documentation.
Copy and paste the text below and save to HelpSource/Classes/LinkLane.schelp
TITLE:: LinkLane +summary:: (put short description here) +categories:: Undocumented classes, UGens>Undocumented +related:: Classes/SomeRelatedClass, Reference/SomeRelatedStuff, etc. + +DESCRIPTION:: +(put long description here) + + +CLASSMETHODS:: + +METHOD:: kr +(describe method here) + +ARGUMENT:: div +(describe argument here) + +ARGUMENT:: max +(describe argument here) + +ARGUMENT:: lane +(describe argument here) + +returns:: (describe returnvalue here) + + +INSTANCEMETHODS:: + + +EXAMPLES:: + +code:: +(some example code) +:: ++
This class is missing documentation.
Copy and paste the text below and save to HelpSource/Classes/LinkTempo.schelp
TITLE:: LinkTempo +summary:: (put short description here) +categories:: Undocumented classes, UGens>Undocumented +related:: Classes/SomeRelatedClass, Reference/SomeRelatedStuff, etc. + +DESCRIPTION:: +(put long description here) + + +CLASSMETHODS:: + +METHOD:: kr +(describe method here) + +ARGUMENT:: tempo +(describe argument here) + +ARGUMENT:: env +(describe argument here) + +returns:: (describe returnvalue here) + + +INSTANCEMETHODS:: + + +EXAMPLES:: + +code:: +(some example code) +:: ++
This class is missing documentation.
Copy and paste the text below and save to HelpSource/Classes/LinkTrig.schelp
TITLE:: LinkTrig +summary:: (put short description here) +categories:: Undocumented classes, UGens>Undocumented +related:: Classes/SomeRelatedClass, Reference/SomeRelatedStuff, etc. + +DESCRIPTION:: +(put long description here) + + +CLASSMETHODS:: + +METHOD:: kr +(describe method here) + +ARGUMENT:: division +(describe argument here) + +returns:: (describe returnvalue here) + + +INSTANCEMETHODS:: + + +EXAMPLES:: + +code:: +(some example code) +:: ++
List is a subclass of SequenceableCollection with unlimited growth in size. Although not a subclass of Array or its superclass ArrayedCollection it uses an Array in its implementation and is in many cases interchangeable with one. (List implements many of the same methods as Array.) +
Arrays have a fixed maximum size. If you add beyond that size a new Array is created and returned, but you must use an assignment statement or the new array will be lost. (See the Array helpfile.) List has no size limitation and is thus more flexible, but has slightly more overhead.
( +x = Array.new(3); +y = List.new(3); +5.do({ arg i; z = x.add(i); y.add(i); }); +x.postln; z.postln; y.postln; +)+ +
Many of List's methods are inherited from SequenceableCollection or Collection and are documented in those helpfiles.
Creates a List with the initial capacity given by size.
Creates a List with the initial capacity given by size and slots filled with nil.
Creates a List by copying aList's array variable.
Creates a List using anArray.
Returns a new Array based upon this List.
Returns the List's Array, allowing it to be manipulated directly. This should only be necessary for exotic manipulations not implemented in List or its superclasses.
( +x = List[1, 2, 3]; +x.array.add("foo"); +x.postln; +)+
Sets the List's Array.
Return the item at index.
List[ 1, 2, 3 ].at(0).postln;+
Same as -at, but values for index greater than the size of the List will be clipped to the last index.
y = List[ 1, 2, 3 ]; +y.clipAt(13).postln;+
Same as -at, but values for index greater than the size of the List will be wrapped around to 0.
y = List[ 1, 2, 3 ]; +y.wrapAt(3).postln; // this returns the value at index 0 +y.wrapAt(4).postln; // this returns the value at index 1+
Same as -at, but values for index greater than the size of the List will be folded back.
y = List[ 1, 2, 3 ]; +y.foldAt(3).postln; // this returns the value at index 1 +y.foldAt(4).postln; // this returns the value at index 0 +y.foldAt(5).postln; // this returns the value at index 1+
Put item at index, replacing what is there.
Same as -put, but values for index greater than the size of the List will be clipped to the last index.
Same as -put, but values for index greater than the size of the List will be wrapped around to 0.
Same as -put, but values for index greater than the size of the List will be folded back.
Adds an item to the end of the List.
Inserts the item at the beginning of the List.
Inserts the item into the contents of the List at the indicated index.
Remove and return the last element of the List.
Increase the size of the List by sizeIncrease number of slots.
Remove and return the element at index, shrinking the size of the List.
y = List[ 1, 2, 3 ]; +y.removeAt(1); +y.postln;+
Inserts the item into the contents of the receiver, possibly returning a new collection.
( +var z; +z = List[1, 2, 3, 4]; +z.fill(4).postln; +z.fill([1,2,3,4]).postln; +)+
Iterate over the elements in order, calling the function for each element. The function is passed two arguments, the element and an index.
List['a', 'b', 'c'].do({ arg item, i; [i, item].postln; });+
Iterate over the elements in reverse order, calling the function for each element. The function is passed two arguments, the element and an index.
List['a', 'b', 'c'].reverseDo({ arg item, i; [i, item].postln; });+
Calls function for each subsequent pair of elements in the List. The function is passed the two elements and an index.
List[1, 2, 3, 4, 5, 6].pairsDo({ arg a, b; [a, b].postln; });+
Return a new List which is a copy of the indexed slots of the receiver from start to end.
( +var y, z; +z = List[1, 2, 3, 4, 5]; +y = z.copyRange(1,3); +z.postln; +y.postln; +)+
Return a new List consisting of the values starting at first, then every step of the distance between first and second, up until last.
( +var y, z; +z = List[1, 2, 3, 4, 5, 6]; +y = z.copySeries(0, 2, 5); +y.postln; +)+
Put value at every index starting at first, then every step of the distance between first and second, up until last.
( +var y, z; +z = List[1, 2, 3, 4, 5, 6]; +y = z.putSeries(0, 2, 5, "foo"); +y.postln; +)+
Return a new List whose elements are reversed.
( +var y, z; +z = List[1, 2, 3, 4]; +y = z.reverse; +z.postln; +y.postln; +)+
Returns a new List whose elements have been scrambled. The receiver is unchanged.
List[1, 2, 3, 4, 5, 6].scramble.postln;+
Return a new List which is the receiver made into a palindrome. The receiver is unchanged.
List[1, 2, 3, 4].mirror.postln;+
Return a new List which is the receiver made into a palindrome with the last element removed. This is useful if the list will be repeated cyclically, the first element will not get played twice. The receiver is unchanged.
List[1, 2, 3, 4].mirror1.postln;+
Return a new List which is the receiver concatenated with a reversal of itself. The center element is duplicated. The receiver is unchanged.
List[1, 2, 3, 4].mirror2.postln;+
Return a new List whose elements are repeated n times. The receiver is unchanged.
List[1, 2, 3].stutter(2).postln;+ +
rotate Return a new List whose elements are in rotated order. Negative n values rotate left, positive n values rotate right. The receiver is unchanged.
List[1, 2, 3, 4, 5].rotate(1).postln; +List[1, 2, 3, 4, 5].rotate(-1).postln; +List[1, 2, 3, 4, 5].rotate(3).postln;+
Return a new List whose elements have been reordered via one of 10 "counting" algorithms. The algorithms are numbered 1 through 10. Run the examples to see the algorithms.
List[1, 2, 3, 4].pyramid(1).postln; + +( +10.do({ arg i; + List[1, 2, 3, 4].pyramid(i + 1).postcs; +}); +)+
Returns a new List whose elements are interlaced sequences of the elements of the receiver's subcollections, up to size length. The receiver is unchanged.
( +x = List[ [1, 2, 3], 6, List["foo", 'bar']]; +y = x.lace(12); +x.postln; +y.postln; +)+
Returns a new List whose elements are the nthPermutation of the elements of the receiver. The receiver is unchanged.
( +x = List[ 1, 2, 3]; +6.do({|i| x.permute(i).postln;}); +)+
Returns a new List whose elements are repeated sequences of the receiver, up to size length. The receiver is unchanged.
( +x = List[ 1, 2, 3, "foo", 'bar' ]; +y = x.wrapExtend(9); +x.postln; +y.postln; +)+
Same as -wrapExtend but the sequences fold back on the list elements.
( +x = List[ 1, 2, "foo"]; +y = x.foldExtend(9); +x.postln; +y.postln; +)+
Return a new List whose elements are repeated subsequences from the receiver. Easier to demonstrate than explain.
List[1, 2, 3, 4, 5, 6].slide(3, 1).postcs; +List[1, 2, 3, 4, 5, 6].slide(3, 2).postcs; +List[1, 2, 3, 4, 5, 6].slide(4, 1).postcs;+
Dump the List's Array.
Replace the List's Array with a new empty one.
The functionality of MKtl relies on descriptions of the used devices. For some Controllers, and while many descriptions are already available, your device may not yet be among them. With MIDIExplorer, it is easy to create such a file for a MIDI device. For more info on how to use it, see How to create a description file. +
post info on currently available sources, mainly to access uid number.
start exploration, i.e. collecting statistics on all incoming MIDI messages.
srcID |
+ an optional source ID - if not nil, only messages from the identified source will be collected. |
post info on the current state of observation
stop exploration.
find the deviceName for the observed srcID
return instructions string to put on top of desc file. +
MIDIExplorer.instructions
compile the discovered elements as an evaluable string, which can be saved as a description file, and then read as a description dict.
(includeSpecs) |
a String containing a a dict which works as a description, and contains raw list of elements
opens a new document containing a description string made with MIDIExplorer.compile.
compile the elementsDesc from data collected by MIDIExplorer.
get and set flag whether to post info
set verbose flag, default flag is true
list of midi message types to catch
Main is the concrete instance of Process (the runtime environment for the virtual machine and interpreter). Main overrides some methods of Process. There are two methods of interest. One is named startup and is called after the class library has been compiled. The other is called shutdown which gets called when the library gets re-compiled.
The singleton instance of Main is available through the special keyword thisProcess. For example, to find out what platform you're on:
thisProcess.platform; // --> e.g. "an OSXPlatform", "a LinuxPlatform", ...+
These class methods tell you which version of SuperCollider you are running and whether that version complies to your required minimum / maximum settings:
the current version as a human readable string
check if we are running at least version maj.min
Main.versionAtLeast( 3, 1 );+ +
true or false
check if we are running version maj.min or older
Main.versionAtMost( 3, 1 );+ +
true or false
Called after the class library has been compiled.
This calls the superclass' startup, which among other things initializes the AppClock and the top-level Environment. +
Main's startup then stores Server.default in the interpreter variable s, sets the platform default's GUI kit, calls a Platform specific startup method (for example OSXPlatform's startup opens the server windows), and finally invokes StartUp.run. +
To add your own startup functionalities, you could either edit the special startup-file (discussed in Sclang Startup File), or use StartUp.add as discussed in the StartUp help file.
Called after SuperCollider is quit or the class library is about to be re-compiled.
This will quit all audio Server instances, perform a platform specific shutdown (e.g. the HID subsystem is released), finally Process' shutdown method is called, resulting in successive calls to UI.shutdown, NetAddr.disconnectAll, File.closeAll, and Archive.write. To register your own shutdown code, use a call like this:
ShutDown.add({ "Good bye!!".postln });+
Override this to do whatever you want, e. g. add a class extension file like this to the class library:
+ Main { + run { "myPatch.rtf".load } +}+
(newFunc) |
+ A Function or similar object to be set. When evaluated, this function will be passed the arguments time, replyAddr, and message, corresponding to the time the message was sent, the NetAddr of the sender, and the message itself as an Array. |
Register a Function to be evaluated whenever SuperCollider language (the client) receives an OSC message. This will not overwrite any previously registered functions.
func |
+ A Function or similar object to be added. When evaluated, this function will be passed the arguments msg, time, replyAddr, and recvPort, corresponding to the message itself as an Array, the time the message was sent, the NetAddr of the sender, and the port on which the message was received. Note that this order differs from that used by the deprecated method -recvOSCfunc. // post all incoming traffic except the server status messages +// basically the same as OSCFunc.trace +( +f = { |msg, time, replyAddr, recvPort| + if(msg[0] != '/status.reply') { + "At time %s received message % from % on port%\n".postf( time, msg, replyAddr, recvPort ) + } +}; +thisProcess.addOSCRecvFunc(f); +); + +// stop posting. +thisProcess.removeOSCRecvFunc(f);+ |
Remove a Function from the list of those evaluated whenever SuperCollider language (the client) receives an OSC message. This will leave any other registered functions in place.
func |
+ A Function or similar object to be removed. |
Replace a Function in the list of those evaluated whenever SuperCollider language (the client) receives an OSC message with a different one. This will leave any other registered functions in place.
func |
+ The Function or similar object to be replaced. |
newFunc |
+ A Function or similar object to be replace the one being removed. When evaluated, this function will be passed the arguments time, replyAddr, recvPort, and message, corresponding to the time the message was sent, the NetAddr of the sender, the port on which the message was received, and the message itself as an Array. |
Attempt to open a new UDP port for receiving OSC traffic. If another application has already bound to the requested port this will fail. Once opened, ports remain bound until SC is recompiled. +
If the port was already opened by SC it will return true directly without trying to open the port again.
portNum |
+ An Integer indicating the port to attempt to bind. |
A Boolean indicating whether the attempt was successful.
thisProcess.openUDPPort(3000); // will return true or false. +thisProcess.openPorts; // returns all open ports+
Get a collection of all active UDP ports, including the main sclang port NetAddr.langPort
.
A Set.
The operating system's pid (process ID) for the process.
Recompiles the class library. This is equivalent to restarting SC. Currently OSX (SuperCollider.app) only.
Get the current Platform
Get the command-line arguments passed to sclang.
Returns the minimum difference of two values in modulo arithmetics. On a circle, there are two distances between two points. This UGen returns the smaller value of the two.
{ var a = Line.ar(0, 4, 0.01), d = ModDif.ar(a); [a, d] }.plot; +{ var a = Line.ar(0, 4, 0.01); ModDif.ar(a, 0, (1..4)) }.plot; +{ var a = Line.ar(0, 4, 0.01); ModDif.ar(a, (0, 0.25 .. 1), 1) }.plot;+
x |
+ First input value |
y |
+ Second input value |
mod |
+ Modulo (maximum value, double of the maximal difference). // different moduli +( +{ + var sig = LFSaw.ar(10); + var dist = ModDif.kr(sig, 0, (0..8) * MouseX.kr(0, 1/5)); + Splay.ar(SinOsc.ar(dist * 4000 + 400)) * 0.1 +}.play; +) + +// wrapping amplitude crossfade +( +{ + var numChan = 12; + var x = SinOsc.ar({ rrand(300.0, 800.0) } ! numChan); + var dist = ModDif.kr(MouseX.kr(0, numChan * 2), (0..numChan-1), numChan); + x = x * max(0, 1 - dist); + Splay.ar(x) * 0.1 +}.play; +)+ |
in |
+ audio input |
ffreq |
+ cutoff freq |
res |
+ resonance (0-1) |
{ MoogLadder.ar(Mix(LFSaw.ar([120, 180], 0, 0.33)), LinExp.kr(LFCub.kr(0.1, 0.5*pi), -1, 1, 180, 8500), 0.75).dup }.play+ +
Cursor tracking UGen.
minval |
+ Value corresponding to the left edge of the screen. |
maxval |
+ Value corresponding to the right edge of the screen. |
warp |
+ Mapping curve. 0 is linear, 1 is exponential (e. g. for freq or times). Alternatively you can specify: 'linear' or 'exponential'. |
lag |
+ Lag factor to dezipper cursor movement. |
With this class you may select which of a number of cut procedures to use on the fly. The selection is from an array of BBCutProc derived objects (which might include a further MultiProc!). The index is chosen on a per phrase, or per block basis, by passed valid index returning functions. If a blockfunc is provided, then the selection is always per block. Otherwise it is per phrase. Note that for convoluted switches between many cut procedures at block rate it becomes very unpredictable when phrases start and end, since multiple phrases are bundled together at once.
procs |
+ An array of cut procedure objects. |
phrasefunc |
+ a function that returns a valid index into the procs array. |
blockfunc |
+ a function that returns a valid index into the procs array. + phrasefunc and blockfunc may be set to nil if unneeded. The default behaviour with all nil is a phrasefunc that randomly selects any index up to procs.size. |
Arguments as for new. +
All other methods override the base to pass messages to the currently selected procedure, and update that procedure based on the phrase or block func.
( //test- default behaviour (random choice between cutprocs after each phrase) +var sf, clock, cutsynth, cutproc; + +clock= ExternalClock(TempoClock(2.7725)); +clock.play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break2.aiff",4); + +s.sync; //this forces a wait for the Buffer to load + +cutsynth= CutBuf2(sf); +cutproc=MultiProc.new([WarpCutProc1.new,BBCutProc11.new]); + +g=BBCut2(cutsynth,cutproc).play(clock); +}); + +) + + +( //test- block speed choice of procedure +var sf, clock, cutsynth, cutproc; + +clock= ExternalClock(TempoClock(2.7725)); +clock.play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break2.aiff",4); + +s.sync; //this forces a wait for the Buffer to load + +cutsynth= CutBuf2(sf); +cutproc=MultiProc.new([BBCPPermute(4.0,8,{|i,n| (i**(i+1))%n},{[1,2].choose}),SQPusher1(0.7,2,0.5)],nil,{2.rand}); + +g=BBCut2(cutsynth,cutproc).play(clock); +}); + +) + + +( //fill every 4th bar- plain loop otherwise +var sf, clock, cutsynth, cutproc, pseq; + +pseq=Pseq([0,0,0,1],inf).asStream; + +clock= ExternalClock(TempoClock(2.7725)); +clock.play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break2.aiff",4); + +s.sync; //this forces a wait for the Buffer to load + +cutsynth= CutBuf1(sf); +cutproc=MultiProc.new([BBCutProc.new,WarpCutProc1(phraselength:4.0)],{pseq.next}); + +g=BBCut2(cutsynth,cutproc).play(clock); +}); + +) + + + +( //fills 2- more complex pattern +var sf, clock, cutsynth, cutproc, pseq; +var x,y; + +pseq=Pseq([0,0,0,1,0,0,0,2],inf).asStream; + +clock= ExternalClock(TempoClock(2.7725)); +clock.play; + +Routine.run({ + +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break2.aiff",4); + +s.sync; //this forces a wait for the Buffer to load + +cutsynth= CutBuf1(sf); +cutproc=MultiProc.new([ +BBCutProc.new, //for plain play +WarpCutProc1({0.5},phraselength:4.0), //for obvious fill +BBCPPermute(4.0,8,{arg i,n; ((i*y)%n).asInteger},{z}) +],{ + +z=[2,4,8].choose; y= rrand(3,12.3); + +pseq.next}); + +g=BBCut2(cutsynth,cutproc).play(clock); +}); + +)+
create new net address.
hostname |
+ a String, either an IP number (e.g. "192.168.34.56") or a hostname such as "otherHost.local". |
port |
+ a port number, like 57110. |
create new net address using an integer IP number.
Get the port sclang is currently listening on (may change after a recompile).
Get a NetAddr which corresponds to localhost and the port sclang is listening on.
close all TCP connections.
Get or set the broadcast flag (whether or not broadcast messages can be sent).
Test an IP address to see if it matches that of one of the NICs on this computer.
ipstring |
+ A String to test containing an IP number in dot decimal notation (e.g. "192.168.34.56"). |
A Boolean indicating whether a match was found.
send a message without timestamp to the addr.
send a bundle with timestamp to the addr.
send a raw message without timestamp to the addr.
open TCP connection.
disconnectHandler |
+ called when the connection is closed (either by the client or by the server). |
close TCP connection.
returns the ip number (as a String).
n = NetAddr("localhost", 57110); +n.ip;+
Test if this NetAddr ip number matches that of one of this hosts NICs, or the loopback address.
A Boolean.
n = NetAddr("127.0.0.1", 57120); // 57120 is sclang default port +r = OSCFunc({ arg msg, time; [time, msg].postln }, '/good/news', n); + +n.sendMsg("/good/news", "you", "not you"); +n.sendMsg("/good/news", 1, 1.3, 77); + + +n.sendBundle(0.2, ["/good/news", 1, 1.3, 77]); + +r.free; +n.disconnect; + +// note that different NetAddr objects with the same port and ip are independent. + +r = OSCFunc({ "message arrived".postln }, '/x'); + +n = NetAddr("127.0.0.1", 57120); +n.sendMsg("/x") + + +u = NetAddr("127.0.0.1", 57120); +u.sendMsg("/x"); + +n.disconnect + +u.sendMsg("/x"); + +r.free; +u.disconnect;+
This class is missing documentation.
Copy and paste the text below and save to HelpSource/Classes/Notch.schelp
TITLE:: Notch +summary:: (put short description here) +categories:: Undocumented classes, UGens>Undocumented +related:: Classes/SomeRelatedClass, Reference/SomeRelatedStuff, etc. + +DESCRIPTION:: +(put long description here) + + +CLASSMETHODS:: + +METHOD:: ar +(describe method here) + +ARGUMENT:: in +(describe argument here) + +ARGUMENT:: freq +(describe argument here) + +ARGUMENT:: rq +(describe argument here) + +ARGUMENT:: mul +(describe argument here) + +ARGUMENT:: add +(describe argument here) + +returns:: (describe returnvalue here) + + +INSTANCEMETHODS:: + + +EXAMPLES:: + +code:: +(some example code) +:: ++
Number represents a mathematical quantity.
Addition.
Subtraction.
Multiplication.
Division.
Integer division.
Modulo.
Exponentiation.
Answer the polar radius of the number.
Answer the polar angle of the number.
Answer the real part of the number.
Answer the imaginary part of the number.
Create a new Point whose x coordinate is the receiver and whose y coordinate is aNumber.
Create a new Complex number whose real part is the receiver with the given imaginary part.
Create a new Polar number whose radius is the receiver with the given angle.
Calls function for numbers from this up to endval, inclusive, stepping each time by 1.
endValue |
+ a Number. |
function |
+ a Function which is passed two arguments, the first which is an number from this to |
(endval, and the second which is a number from zero to the number of iterations minus one.) |
Calls function for numbers from this up to endval stepping each time by step.
endValue |
+ a Number. |
stepValue |
+ a Number. |
function |
+ a Function which is passed two arguments, the first which is an number from this to endval, and the second which is a number from zero to the number of iterations minus one. |
Calls function for numbers from this up to endval stepping each time by a step specified by second.
second |
+ a Number. |
last |
+ a Number. |
function |
+ a Function which is passed two arguments, the first which is an number from this to endval, and the second which is a number from zero to the number of iterations minus one. |
OSCFunc (and its subclass OSCdef) registers one or more functions to respond to an incoming OSC message which matches a specified OSC Address. Many of its methods are inherited from its superclass AbstractResponderFunc. OSCFunc supports pattern matching of wildcards etc. in incoming messages. For efficiency reasons you must specify that an OSCFunc will employ pattern matching by creating it with the *newMatching method, or by passing a matching dispatcher to *new. For details on the Open Sound Control protocol, see http://opensoundcontrol.org/spec-1_0
Get or set the default dispatcher object for OSCFuncs (this is what you get if you pass nil as the dispatcher argument to *new). This object will decide if any of its registered OSCFuncs should respond to an incoming OSC message.
By default this will be an OSCMessageDispatcher, but it can be set to any instance of an appropriate subclass of AbstractDispatcher.
Get or set the default matching dispatcher object for OSCFuncs (this is what you get if when you create an OSCFunc using *newMatching). This object will decide if any of its registered OSCFuncs should respond to an incoming OSC message using pattern matching.
By default this will be an OSCMessagePatternDispatcher, but it can be set to any instance of an appropriate subclass of AbstractDispatcher.
Create a new, enabled OSCFunc.
func |
+ A Function or similar object which will respond to the incoming message. When evaluated it will be passed the following arguments:
| ||||||||
path |
+ A Symbol indicating the path of the OSC address of this object. Note that OSCFunc demands OSC compliant addresses. If the path does not begin with a / one will be added automatically. | ||||||||
srcID |
+ An optional instance of NetAddr indicating the IP address of the sender. If set this object will only respond to messages from that source. | ||||||||
recvPort |
+ An optional Integer indicating the port on which messages will be received. If set this object will only respond to message received on that port. This method calls Main: -openUDPPort to ensure that the port is opened. | ||||||||
argTemplate |
+ An optional Array composed of instances of Integer or Function (or objects which respond to the method Methods: matchItem) used to match the arguments of an incoming OSC message. If a Function, it will be evaluated with the corresponding message arg as an argument, and should return a Boolean indicating whether the argument matches and this OSCFunc should respond (providing all other arguments match). Template values of nil will match any incoming argument value. | ||||||||
dispatcher |
+ An optional instance of an appropriate subclass of AbstractDispatcher. This can be used to allow for customised dispatching. Normally this should not be needed. |
A new instance of OSCFunc.
A convenience method to create a new, enabled OSCFunc whose dispatcher will perform pattern matching on incoming OSC messages to see if their address patterns match this object's path.
func |
+ A Function or similar object which will respond to the incoming message. When evaluated it will be passed the arguments msg, time, addr, and recvPort, corresponding to the message as an Array [OSCAddress, other args], the time that the message was sent, a NetAddr corresponding to the IP address of the sender, and an Integer corresponding to the port on which the message was received. |
path |
+ A Symbol indicating the path of the OSC address of this object. Note that OSCFunc demands OSC compliant addresses. If the path does not begin with a / one will be added automatically. Pattern matching will be applied to any incoming messages to see if they match this address. Note that according to the OSC spec, regular expression wildcards are only permitted in the incoming message's address pattern. Thus path should not contain wildcards. For more details on OSC pattern matching, see http://opensoundcontrol.org/spec-1_0 |
srcID |
+ An optional instance of NetAddr indicating the IP address of the sender. If set this object will only respond to messages from that source. |
recvPort |
+ An optional Integer indicating the port on which messages will be received. |
argTemplate |
+ An optional Array composed of instances of Integer or Function (or objects which respond to the method Methods: matchItem) used to match the arguments of an incoming OSC message. If a Function, it will be evaluated with the corresponding message arg as an argument, and should return a Boolean indicating whether the argument matches and this OSCFunc should respond (providing all other arguments match). Template values of nil will match any incoming argument value. |
A new instance of OSCFunc.
A convenience method which dumps all incoming OSC messages.
bool |
+ A Boolean indicating whether dumping is on or off. |
hideStatusMsg |
+ A Boolean indicating whether server status messages are excluded from the dump or not. |
This is used by OSCMessageDispatcher and OSCMessagePatternDispatcher to match incoming OSC messages to instances of OSCFunc or OSCdef using sender address. This class is private, and generally users should not need to address instances directly.
Make a new instance.
addr |
+ The NetAddr to attempt to match. |
func |
+ The Function to evaluate if a match is found. |
An OSCFuncAddrMessageMatcher.
Check to see if a message matches, and evaluate func if it does.
msg |
+ The OSC message as an Array in the form |
time |
+ A Float indicating the time the incoming message was sent. |
testAddr |
+ A NetAddr indicating the source of the message. |
recvPort |
+ An Integer indicating the port on which the message was received. |
OSCMon monitors captures incoming OSC messages, keeping recent ones around for analyis and display. It can show the message sizes, relative arrival times for multiple sources. +
some colors to use in the visualisation
make a new OSCMon
bufsize |
+ with a buffersize of how many messages to keep, |
timeWindow |
+ a length of time within which to keep messages |
action |
+ and an action to do when a new message comes in |
the maximum number of messages to store
the maximum time window in which to store messages
internal: the function with which OSCMon listen to osc traffic.
the list of recorded message entries
the addresses from which messages have been sent
the msgNames monitored
a dict containing various analyses of the osc data
post info on current recorded messages
enable and disable listening
get flag whether oscmon is listening
get or set flag whether to post info on incoming traffic
set verbose flag, default flag is true
get and set flag whether to record server status messages
a custom action to perform on all incoming messages
name | |
addr |
+ add a nickname for an incoming address |
name |
+ remove a nickname |
free the resources of this oscmon
refresh analysis of present addresses and names
addr |
+ add/remove an address to/from addrNames |
add or remove a messageName from msgNames
post all messages from address at index
make a named display window for the oscmon
its display window and userview
refresh display
clear msgNames or addresses
get and set flags whether to track msgNames and addresses
OSCdef provides a global reference to the functionality of its superclass OSCFunc. Essentially it stores itself at a key within a global dictionary, allowing replacement at any time. Most methods are inherited from its superclass.
Create a new, enabled OSCdef. If an OSCdef already exists at this key, its parameters will be replaced with the ones provided (args for which nil is passed will use the old values).
key |
+ The key at which to store this OSCdef in the global collection. Generally this will be a Symbol. | ||||||||
func |
+ A Function or similar object which will respond to the incoming message. When evaluated it will be passed the following arguments:
| ||||||||
path |
+ A Symbol indicating the path of the OSC address of this object. Note that OSCdef demands OSC compliant addresses. If the path does not begin with a / one will be added automatically. | ||||||||
srcID |
+ An optional instance of NetAddr indicating the IP address of the sender. If set this object will only respond to messages from that source. | ||||||||
recvPort |
+ An optional Integer indicating the port on which messages will be received. If set this object will only respond to message received on that port. This method calls Main: -openUDPPort to ensure that the port is opened. | ||||||||
argTemplate |
+ An optional Array composed of instances of Integer or Function (or objects which respond to the method Methods: matchItem) used to match the arguments of an incoming OSC message. If a Function, it will be evaluated with the corresponding message arg as an argument, and should return a Boolean indicating whether the argument matches and this OSCdef should respond (providing all other arguments match). Template values of nil will match any incoming argument value. | ||||||||
dispatcher |
+ An optional instance of an appropriate subclass of AbstractDispatcher. This can be used to allow for customised dispatching. Normally this should not be needed. |
An instance of OSCdef.
A convenience method to create a new, enabled OSCdef whose dispatcher will perform pattern matching on incoming OSC messages to see if their address patterns match this object's path.
key |
+ The key at which to store this OSCdef in the global collection. Generally this will be a Symbol. |
func |
+ A Function or similar object which will respond to the incoming message. When evaluated it will be passed the arguments msg, time, addr, and recvPort, corresponding to the message as an Array [OSC address, args1, arg2, ...], the time that the message was sent, a NetAddr corresponding to the IP address of the sender, and an Integer corresponding to the port on which the message was received. |
path |
+ A Symbol indicating the path of the OSC address of this object. Note that OSCdef demands OSC compliant addresses. If the path does not begin with a / one will be added automatically. Pattern matching will be applied to any incoming messages to see if they match this address. Note that according to the OSC spec, regular expression wildcards are only permitted in the incoming message's address pattern. Thus path should not contain wildcards. For more details on OSC pattern matching, see http://opensoundcontrol.org/spec-1_0 |
srcID |
+ An optional instance of NetAddr indicating the IP address of the sender. If set this object will only respond to messages from that source. |
recvPort |
+ An optional Integer indicating the port on which messages will be received. |
argTemplate |
+ An optional Array composed of instances of Integer or Function (or objects which respond to the method Methods: matchItem) used to match the arguments of an incoming OSC message. If a Function, it will be evaluated with the corresponding message arg as an argument, and should return a Boolean indicating whether the argument matches and this OSCFunc should respond (providing all other arguments match). Template values of nil will match any incoming argument value. |
An instance of OSCdef.
Clears and deactivates all OSCdefs from the global collection.
Clears this OSCdef from the global collection and deactivates it.
n = NetAddr("127.0.0.1", 57120); // local machine + +OSCdef(\test, {|msg, time, addr, recvPort| \unmatching.postln}, '/chat', n); // def style +OSCdef.newMatching(\test2, {|msg, time, addr, recvPort| \matching.postln}, '/chat', n); // path matching +OSCdef(\test3, {|msg, time, addr, recvPort| \oneShot.postln}, '/chat', n).oneShot; // once only + + +m = NetAddr("127.0.0.1", 57120); // loopback + +m.sendMsg("/chat", "Hello App 1"); +m.sendMsg("/chat", "Hello App 1"); // oneshot gone +m.sendMsg("/ch?t", "Hello App 1"); +m.sendMsg("/*", "Hello App 1"); +m.sendMsg("/chit", "Hello App 1"); // nothing + +// Introspection + +AbstractResponderFunc.allFuncProxies +AbstractResponderFunc.allEnabled +OSCdef(\test).disable; +AbstractResponderFunc.allDisabled + +// change funcs +OSCdef(\test).enable; +OSCdef(\test, {|msg, time, addr, recvPort| 'Changed Unmatching'.postln}, '/chat', n); // replace at key \test +m.sendMsg("/chat", "Hello App 1"); +OSCdef(\test).add(f = {\foo.postln}); // add another func +m.sendMsg("/chat", "Hello App 1"); +OSCdef(\test).clear; // remove all functions +m.sendMsg("/chat", "Hello App 1"); +OSCdef(\test).free; // unregister OSCdef + + +//////// Use an argTemplate for finer grained matching + +s.boot; +x = Synth(\default); +OSCdef(\watchForXEnd, { 'ended!'.postln }, '/n_end', s.addr, nil, [x.nodeID]).oneShot; +x.release(3); + +// Args for which nil is passed will use the old values. While this facilitates swapping only parts of an OSCdef, such as the function, it also means you will have to free and recreate an OSCdef to remove parts. + +OSCdef(\argtest, {|msg ... args| msg[1].postln}, '/hey', argTemplate: ['you']); +m.sendMsg("/hey", "you"); // oscdef will respond +m.sendMsg("/hey", "there"); // oscdef will not respond, filtered out by argTemplate + +OSCdef(\argtest, argTemplate: nil); // this has no effect +m.sendMsg("/hey", "you"); // oscdef will respond +m.sendMsg("/hey", "there"); // oscdef will not respond, still filtered out by argTemplate + +OSCdef(\argtest).free; // in order to get rid of the argTemplate, free the def... +OSCdef(\argtest, {|msg ... args| msg[1].postln}, '/hey'); // ... and recreate it. +m.sendMsg("/hey", "you"); // oscdef will respond +m.sendMsg("/hey", "there"); // oscdef will respond+ +
The time argument indicates the time the message was sent plus, if given, the latency added to the bundle:
( +OSCdef(\x, { |msg, time| + "reception time: %\nscheduling time: %\ndelta: %\n\n".postf(Main.elapsedTime, time, time - Main.elapsedTime) +}, \time); + +n = NetAddr("127.0.0.1", 57120); +) + +( +n.sendBundle(0.0, [\time]); +n.sendBundle(1.0, [\time]); +)+
This class is missing documentation.
Copy and paste the text below and save to HelpSource/Classes/OSCpathDispatcher.schelp
TITLE:: OSCpathDispatcher +summary:: (put short description here) +categories:: Undocumented classes +related:: Classes/SomeRelatedClass, Reference/SomeRelatedStuff, etc. + +DESCRIPTION:: +(put long description here) + + +CLASSMETHODS:: + +METHOD:: cmdPathIndices +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: new +(describe method here) + +ARGUMENT:: addr +(describe argument here) + +ARGUMENT:: cmdName +(describe argument here) + +ARGUMENT:: action +(describe argument here) + +ARGUMENT:: pathSize +(describe argument here) + +returns:: (describe returnvalue here) + + +INSTANCEMETHODS:: + +METHOD:: isEmpty +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: maxPathSize +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: pathResponders +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: initPathSize +(describe method here) + +ARGUMENT:: pathSize +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: value +(describe method here) + +ARGUMENT:: time +(describe argument here) + +ARGUMENT:: msg +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: addChild +(describe method here) + +ARGUMENT:: responder +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: removeChild +(describe method here) + +ARGUMENT:: responder +(describe argument here) + +returns:: (describe returnvalue here) + + +EXAMPLES:: + +code:: +(some example code) +:: ++
In the MVC architecture this is the Controller, which creates Views for manipulating the properties of your Model, and receives messages from the View and enacts the changes on the Model. +
Each class specifies its Gui class via the guiClass method. +
The default guiClass for an Object is ObjectGui. So if a class does not implement guiClass at all, then at least there is a default ObjectGui that will display the name. +
Many subclasses override the guiClass method to specify a different class, one that subclasses ObjectGui. +
It is the simplest display, it is just the object asString. +
The .gui method is called on your model:
// standard usage +theModel.gui( parent, bounds ) + +// this results in these steps: +guiClass = theModel.guiClass; +gui = guiClass.new( theModel ); +gui.gui( parent, bounds );+ +
In addition to those steps the model/gui dependencies are managed, defaults (nil parent or nil bounds) are managed and when the window or parent view is closed then dependencies are safely managed.
Create a gui controller object but does not yet create the views / window. Call .gui to create the views.
model |
+ The model is the object that the GUI is a graphical interface for. |
the ObjectGui or subclass object
When implementing subclasses this is the primary and often the only method that needs to be implemented. The ObjectGui parent class takes care of setting up all windows and dependencies and then the guiBody method adds views to the layout. It is normal to declare instance variables in the ObjectGui subclass that are used to store the widgets so they can be updated later.
layout |
+ Usually a FlowView : a parent view with a FlowLayout to add views to. |
bounds |
+ nil or a Rect. |
... args |
+ More args may be passed here. |
this
When the model is changed and the .changed method is called then .update is called on all dependants including this gui object. Update the views you have placed in the guiBody.
changed |
+ The model. Within your gui class the model is already in the instance variable 'model'. |
changer |
+ Depends on the conventions of how .changed was called. If an object called someModel.changed(this) then it is supplying itself as the changer and will be passed through here. Sometimes a flag is used: someModel.changed('points') and the gui may know of and participate in that convention. Sometimes no changer is passed in. |
The standard method to create a view / window. Usually you call yourModel.gui(parent,bounds) and this creates the gui (of the related ObjectGui subclass) and then theObjectGui.gui(parent,bounds) is called, forwarding the arguments. So this method is what receives the forwarded (parent,bounds) from the initial call to theModel.gui(parent,bounds). Usually you do not call this manually and would avoid reimplementing it.
parent |
+ parent view : nil, a Window, a FlowView or any other usable container view. |
bounds |
+ nil or a Rect. The default of nil will offer the entire bounds to the guiBody method and then shrink the view size afterwards to the exact size of the contents that were actually added. |
... args |
+ More args may be passed into theModel.gui(parent,bounds,anArg,moreArg) and will be forwarded to guiBody. |
this
This converts a supplied parent and bounds into a usable parent container view on a window. It creates a window if needed.
parent |
+ parent view or nil |
bounds |
+ desired bounds or nil |
title |
+ window title IF a new window is being created. if there is a parent view then title is ignored. |
converted parent
set a new model. This allows to use a single gui and dynamically swap in a different model of the same class. The old model releases the gui as a dependant and the new model adds the gui as a dependent. Then the views are updated.
newModel |
+ The new object |
(returnvalue)
The default implementation of writeName places a nameplate on the gui that is draggable. This method is an accessor for that dragSource object.
a GUi.dragSource
This is called when the parent view closes. It releases dependants.
this
Each ObjectGui subclass may implement a default background color.
a color
ObjectGui by default makes a nameplate with the name of the model. Implement this in subclasses if a different name style or no nameplate is desired. Note: this may change in the near future. So many classes override this to shut off the name.
layout |
+ The layout to place the nameplate on. Probably the same as is being passed to guiBody |
The default write name implementation. You could call this from a subclass if you are primarily implementing writeName to customize what name is shown or to add other items to that area.
layout |
+ the layout |
name |
+ the string to display |
YourSimpleGuiClass : ObjectGui { + + guiBody { arg layout; + + // we refer to the model and + // access its variable howFast. + // if its a simple number, it will display + // using the default ObjectGui class, which + // will simply show its value as a string. + model.howFast.gui(layout); + } +} + + +// more complex +YourGuiClass : ObjectGui { + + var numberEditor; + + //for example + guiBody { arg layout; + var r; + // the object you are making a gui for is referred to as the model + + // display some param on screen. + // here we assume that someParam is something that + // has a suitable gui class + // implemented, or that the default ObjectGui is sufficient. + model.someParam.gui(layout); + + // using non 'gui' objects + r = layout.layRight(300,300); // allocate yourself some space + Button(layout.win,r) + .action_({ arg butt; + model.goApeShit; + }); + + // note: NumberEditor is a cruciallib class + // which is itself a model (its an editor of a value) + // and has its own gui class that creates and manages the NumberBox view + numberEditor = NumberEditor(model.howFast,[0,100]) + .action_({ arg val; + model.howFast = val; + model.changed(this); + // tell the model that this gui changed it + }); + numberEditor.gui(layout); + } + + // your gui object will have update called any time the .changed message + // is sent to your model + update { arg changed,changer; + + if(changer !== this,{ + /* if it is this gui object that changed the value + using the numberEditor, then we already have a correct + display and don't need to waste cpu to update it. + if anyone else changed anything about the model, + we will update ourselves here. + */ + numberEditor.value = model.howFast; + /* + note that + numberEditor.value = model.howFast; + is passive, and does not fire the numberEditor's action. + + numberEditor.activeValue = model.howFast + would fire the action as well, resulting in a loop that would + probably crash your machine. + */ + } + } + +}+ +
Linear interpolating wavetable lookup oscillator with frequency and phase modulation inputs. +
This oscillator requires a buffer to be filled with a wavetable format signal. This preprocesses the Signal into a form which can be used efficiently by the Oscillator. The buffer size must be a power of 2. +
This can be achieved by creating a Buffer object and sending it one of the "b_gen" messages ( Buffer: -sine1, Buffer: -sine2, Buffer: -sine3 ) with the wavetable flag set to true. +
This can also be achieved by creating a Signal object and sending it the 'asWavetable' message, thereby creating a Wavetable object in the required format. Then, the wavetable data may be transmitted to the server using the Buffer: *sendCollection or Buffer: *loadCollection methods.
bufnum |
+ Buffer index. |
freq |
+ Frequency in Hertz. |
phase |
+ Phase offset or modulator in radians. (Note: phase values should be within the range +-8pi. If your phase values are larger then simply use |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
Write a signal to a bus. +
Note that using the Bus class to allocate a multichannel bus simply reserves a series of adjacent bus indices with the Server object's bus allocators. abus.index simply returns the first of those indices. When using a Bus with an In or Out UGen there is nothing to stop you from reading to or writing from a larger range, or from hardcoding to a bus that has been allocated. You are responsible for making sure that the number of channels match and that there are no conflicts.
See the Server Architecture and Bus helpfiles for more information on buses and how they are used.
bus |
+ The index of the bus to write out to. The lowest numbers are written to the audio hardware. |
channelsArray |
+ An Array of channels or single output to write out. You cannot change the size of this once a SynthDef has been built. |
Phase modulation sine oscillator pair.
carfreq |
+ Carrier frequency in cycles per second. |
modfreq |
+ Modulator frequency in cycles per second. |
pmindex |
+ Modulation index in radians. |
modphase |
+ A modulation input for the modulator's phase in radians. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
Clears bins above or below a cutoff point.
buffer |
+ FFT buffer. |
wipe |
+ Can range between -1 and +1. + If If If |
s.boot; + +( +{ + var in, chain; + in = WhiteNoise.ar(0.2); + chain = FFT(LocalBuf(2048), in); + chain = PV_BrickWall(chain, SinOsc.kr(0.1)); + IFFT(chain); +}.play +)+
PV_ChainUGen
is an abstract class, not used directly, but only its subclasses are. It represents phase-vocoder UGens - i.e. UGens which apply some kind of transformation to the frequency-domain signal produced by FFT.
+
It encompasses all unit generators whose output is an FFT chain. This is why FFT is in this group but IFFT is not - the IFFT ugen outputs ordinary time-domain audio. +
For more information on using these unit generators, see FFT Overview.
Returns the FFT chain buffer's size.
( +{ + var chain = FFT(LocalBuf(1024)); + chain.fftSize.poll; + 0.0 +}.play; +)+
pvcalc applies a function to the frequency-domain data of an FFT chain. See -pvcollect below for discussion of efficiency considerations. See also -pvcalc2 below, and UnpackFFT.
chain = chain.pvcalc(numframes, func, frombin, tobin, zeroothers)+
numframes |
+ Number of FFT frames to process |
func |
+ The function that takes two arrays as inputs ( // example function +{ | magnitudes, phases | + [mags.reverse, phases.reverse] // e.g. upside-down spectrum +}+ |
frombin |
+ Range start (optional) |
tobin |
+ Range end (optional) |
zeroothers |
+ If set to 1 then bins outside of the range being processed are silenced. |
The method pvcalc2 is just like -pvcalc but can combine two FFT chains.
chain = chain.pvcalc2(chain2, numframes, func, frombin, tobin, zeroothers)+
chain2 |
+ The scond FFT chain. |
numframes |
+ Number of FFT frames to process |
func |
+ The function that takes four arrays as inputs (magnitudes1, phases1, magnitudes2, phases2) and returns a resulting pair of arrays // example function +{ | magnitudes1, phases1, magnitudes2, phases2 | + [magnitudes1, phases2] // e.g. use the magnitudes of one, ane the phases of the other +}+ |
frombin |
+ Range start (optional) |
tobin |
+ Range end (optional) |
zeroothers |
+ If set to 1 then bins outside of the range being processed are silenced. |
Process each bin of an FFT chain, separately, by applying a function to each bin of an FFT chain.
chain = chain.pvcollect(numframes, func, frombin, tobin, zeroothers)+
numframes |
+ Number of FFT frames to process |
func |
+ The function that processes each bin. It should be a function that takes // example function +{ | magnitude, phase, bin, index | + [mags.reverse, phases.reverse] // e.g. upside-down spectrum +}+ + The bin is the integer bin number, starting at 0 for DC, while index is the iteration number, always starting with 0. You can optionally ignore the phase and only return a single (magnitude) value, in which case the phase is assumed to be left unchanged. |
frombin |
+ Range start (optional) |
tobin |
+ Range end (optional) |
zeroothers |
+ If set to 1 then bins outside of the range being processed are silenced. |
Note that this procedure can be relatively CPU-heavy, depending on how you use it. Using pvcollect (or its components, UnpackFFT & PackFFT) is usually less efficient than using a single "PV_" unit generator to process an FFT chain, because it involves the creation of quite a large graph of demand-rate unit generators. +
If you wish to reduce the CPU impact of using this approach, try the following:
// a sound file +c.free; c = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); + +( +{ + var in, chain, v; + in = PlayBuf.ar(1, c, BufRateScale.kr(c), loop: 1); + chain = FFT(LocalBuf(1024), in); + + chain = chain.pvcalc(1024, {|mags, phases| +//////// Try uncommenting each of these lines in turn and re-running the synth: + [mags * {1.5.rand}.dup(mags.size), phases + {pi.rand}.dup(phases.size)]; // Arbitrary filter, arbitrary phase shift + //[mags.reverse, phases.reverse]; // Upside-down! + //[mags.differentiate, phases.differentiate]; // Differentiate along frequency axis + //[mags[30..] ++ mags[..30], phases[30..] ++ phases[..30]]; // ".rotate" doesn't work directly, but this is equivalent + }, frombin: 0, tobin: 250, zeroothers: 0); + + 0.5 * IFFT(chain).dup +}.play +)+
c.free; c = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); + +( +x = { + var fftsize = 1024; + var in, chain, in2, chain2, out; + in = PlayBuf.ar(1, c, BufRateScale.kr(c), loop: 1); + chain = FFT(LocalBuf(fftsize), in); + + // JMcC babbling brook + in2 = ({ + RHPF.ar(OnePole.ar(BrownNoise.ar, 0.99), LPF.ar(BrownNoise.ar, 14) + * 400 + 500, 0.03, 0.003) }!2) + + ({ RHPF.ar(OnePole.ar(BrownNoise.ar, 0.99), LPF.ar(BrownNoise.ar, 20) + * 800 + 1000, 0.03, 0.005) }!2 + ) * 4; + chain2 = FFT(LocalBuf(fftsize), in2); + + chain = chain.pvcalc2(chain2, fftsize, { |mags, phases, mags2, phases2| + [ + mags * mags2 / 10, + phases2 + phases + ] + }, frombin: 0, tobin: 125, zeroothers: 0); + + out = IFFT(chain); + 0.5 * out.dup +}.play +)+
c.free; c = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); + + +( +{ + var in, chain, v; + in = PlayBuf.ar(1, c, BufRateScale.kr(c), loop: 1); + chain = FFT(LocalBuf(1024), in); + + v = LFPar.kr(0.5).range(0.1, 1); + + chain = chain.pvcollect(1024, {|mag, phase, index| + +//////// Try uncommenting each of these lines in turn and re-running the synth: + //mag; + //[mag, phase]; + //[mag, phase] / 3; + //[mag, phase].sqrt; + //[mag, 3.14.rand]; + //[mag, LFNoise0.kr.range(0, 3.14)]; + //[mag * Dseq([1, 0, 0, 1, 1, 0, 1, 0].stutter(8), 999999999999)]; // Can even use Demand ugens! One val demanded each frame + //[mag.sqrt, 3.14.rand]; + //if(index % 7 == 0, mag, 0); // Comb filter + //if(LFNoise0.kr(10) > 0.5, mag, 0); + //mag + DelayN.kr(mag, 1, v); // Spectral delay + + if((index - LFPar.kr(0.1).range(2, 1024/20)).abs < 10, mag, 0); // Swept bandpass + + }, frombin: 0, tobin: 250, zeroothers: 0); + + 0.5 * IFFT(chain).dup +}.play +)+ +
Adds a different constant random phase shift to each bin. When triggered, it selects a new set of random phases.
buffer |
+ FFT buffer. |
trig |
+ A trigger, that selects a new set of random values. |
( +// trig with MouseY crossing center of screen +{ + var in, chain; + in = Mix.ar(SinOsc.ar(200 * (1..10), 0, Array.fill(10, { rrand(0.1, 0.2) }) )); + chain = FFT(LocalBuf(2048), in); + chain = PV_Diffuser(chain, MouseY.kr > 0.5 ); + 0.5 * IFFT(chain).dup; +}.play +); + +( +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); + +// trig with MouseY crossing center of screen +{ + var in, chain; + in = PlayBuf.ar(1, b, BufRateScale.kr(b), loop: 1); + chain = FFT(LocalBuf(2048), in); + chain = PV_Diffuser(chain, MouseY.kr > 0.5 ); + 0.5 * IFFT(chain).dup; +}.play +);+
Freezes magnitudes at current levels when freeze
> 0.
buffer |
+ FFT buffer. |
freeze |
+ If > 0, then magnitudes are frozen at current levels. |
s.boot; +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); + + +( +SynthDef("help-magFreeze", { arg out=0; + var in, chain; + in = SinOsc.ar(LFNoise1.kr(5.2,250,400)); + chain = FFT(LocalBuf(2048), in); + // moves in and out of freeze + chain = PV_MagFreeze(chain, SinOsc.kr(0.2) ); + Out.ar(out, 0.1 * IFFT(chain).dup); +}).play(s); + +) + +( +//trig with MouseY +SynthDef("help-magFreeze2", { arg out=0, soundBufnum=2; + var in, chain; + in = PlayBuf.ar(1, soundBufnum, BufRateScale.kr(soundBufnum), loop: 1); + chain = FFT(LocalBuf(2048), in); + chain = PV_MagFreeze(chain, MouseY.kr > 0.5 ); + Out.ar(out, 0.1 * IFFT(chain).dup); +}).play(s,[\soundBufnum, b]); +) + +b.free+ +
Crossfades between two sounds by copying bins in a random order.
bufferA |
+ FFT buffer A. |
bufferB |
+ FFT buffer B. |
wipe |
+ Copies bins from bufferB in a random order as wipe goes from 0 to 1. |
trig |
+ A trigger, that selects a new random ordering. |
s.boot; + +( +//trig with MouseY +SynthDef("help-randWipe", { arg out=0; + var inA, chainA, inB, chainB, chain; + inA = Mix.arFill(6, { LFSaw.ar(exprand(400, 1000), 0, 0.1) }); + inB = Mix.arFill(6, { LFPulse.ar(exprand(80, 400), 0, 0.2, SinOsc.kr(8.0.rand, 0, 0.2).max(0)) }); + chainA = FFT(LocalBuf(2048), inA); + chainB = FFT(LocalBuf(2048), inB); + chain = PV_RandWipe(chainA, chainB, MouseX.kr.poll, MouseY.kr.poll > 0.5); + Out.ar(out, 0.5 * IFFT(chain).dup); +}).play(s); +)+
This class is missing documentation.
Copy and paste the text below and save to HelpSource/Classes/ParaSpace.schelp
TITLE:: ParaSpace +summary:: (put short description here) +categories:: Undocumented classes +related:: Classes/SomeRelatedClass, Reference/SomeRelatedStuff, etc. + +DESCRIPTION:: +(put long description here) + + +CLASSMETHODS:: + +METHOD:: new +(describe method here) + +ARGUMENT:: w +(describe argument here) + +ARGUMENT:: bounds +(describe argument here) + +returns:: (describe returnvalue here) + + +INSTANCEMETHODS:: + +METHOD:: setShape +(describe method here) + +ARGUMENT:: argshape +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setBackgrColor +(describe method here) + +ARGUMENT:: color +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: getNodeLoc +(describe method here) + +ARGUMENT:: index +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: getNodeString +(describe method here) + +ARGUMENT:: index +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: lazyRefresh +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: nodeUpAction +(describe method here) + +ARGUMENT:: func +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: createNode1 +(describe method here) + +ARGUMENT:: argX +(describe argument here) + +ARGUMENT:: argY +(describe argument here) + +ARGUMENT:: color +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: createNode +(describe method here) + +ARGUMENT:: x +(describe argument here) + +ARGUMENT:: y +(describe argument here) + +ARGUMENT:: color +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setMouseOverState +(describe method here) + +ARGUMENT:: state +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: deleteConnection +(describe method here) + +ARGUMENT:: node1 +(describe argument here) + +ARGUMENT:: node2 +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: getNodeStates +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: setNodeSize +(describe method here) + +ARGUMENT:: index +(describe argument here) + +ARGUMENT:: size +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: deleteNode +(describe method here) + +ARGUMENT:: nodenr +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: getNodeColor +(describe method here) + +ARGUMENT:: index +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setSelectFillColor +(describe method here) + +ARGUMENT:: color +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: nodeTrackAction +(describe method here) + +ARGUMENT:: func +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: connectAction +(describe method here) + +ARGUMENT:: func +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setNodeLoc +(describe method here) + +ARGUMENT:: index +(describe argument here) + +ARGUMENT:: argX +(describe argument here) + +ARGUMENT:: argY +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: initParaSpace +(describe method here) + +ARGUMENT:: w +(describe argument here) + +ARGUMENT:: argbounds +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setNodeStates +(describe method here) + +ARGUMENT:: array +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setOutlineColor +(describe method here) + +ARGUMENT:: color +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: createConnection +(describe method here) + +ARGUMENT:: node1 +(describe argument here) + +ARGUMENT:: node2 +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setFont +(describe method here) + +ARGUMENT:: f +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: reconstruct +(describe method here) + +ARGUMENT:: aFunc +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: nodeDownAction +(describe method here) + +ARGUMENT:: func +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: nodeOverAction +(describe method here) + +ARGUMENT:: func +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setNodeLocAction +(describe method here) + +ARGUMENT:: index +(describe argument here) + +ARGUMENT:: argX +(describe argument here) + +ARGUMENT:: argY +(describe argument here) + +ARGUMENT:: action +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setNodeColor +(describe method here) + +ARGUMENT:: index +(describe argument here) + +ARGUMENT:: color +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setFontColor +(describe method here) + +ARGUMENT:: fc +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setFillColor +(describe method here) + +ARGUMENT:: color +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setBackgrDrawFunc +(describe method here) + +ARGUMENT:: func +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: deleteConnections +(describe method here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: getNodeSize +(describe method here) + +ARGUMENT:: index +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: findNode +(describe method here) + +ARGUMENT:: x +(describe argument here) + +ARGUMENT:: y +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: refresh +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: setNodeLoc1Action +(describe method here) + +ARGUMENT:: index +(describe argument here) + +ARGUMENT:: argX +(describe argument here) + +ARGUMENT:: argY +(describe argument here) + +ARGUMENT:: action +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setNodeLoc1 +(describe method here) + +ARGUMENT:: index +(describe argument here) + +ARGUMENT:: argX +(describe argument here) + +ARGUMENT:: argY +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setNodeString +(describe method here) + +ARGUMENT:: index +(describe argument here) + +ARGUMENT:: string +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: bounds +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: getNodeLoc1 +(describe method here) + +ARGUMENT:: index +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: clearSpace +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: keyDownAction +(describe method here) + +ARGUMENT:: func +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: setSelectStrokeColor +(describe method here) + +ARGUMENT:: color +(describe argument here) + +ARGUMENT:: refresh +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: paraNodes +(describe method here) + +returns:: (describe returnvalue here) + + +EXAMPLES:: + +code:: +(some example code) +:: ++
PathName is a utility class for manipulating file names and paths. It expects a path to a file, and lets you access parts of that file path.
path |
+ a String which likely contains one or more / as typical for folder separation. ~ will be converted to your fully addressed home directory, as per String: -standardizePath. PathName.new("MyDisk/SC 2.2.8 f/Sounds/FunkyChicken");+ |
Get or set the global temp directory as a String. This is used by Buffer, etc. By default this is "/tmp/" for Linux and macOS, and "/WINDOWS/TEMP/" for Windows.
returns just the name of the file itself; i.e. everything after the last slash in the full path.
( +var myPath; +myPath = PathName.new("MyDisk/SC 2.2.8 f/Sounds/FunkyChicken"); +myPath.fileName.postln; +)+
returns the name of the file itself without the file extension.
returns the file extension, i.e. everything after the last full-stop in the -fileName.
returns the full path up to the file name itself; i.e. everything up to and including the last slash. This is handy e.g. for storing several files in the same folder.
( +var myPath; +myPath = PathName.new("MyDisk/SC 2.2.8 f/Sounds/FunkyChicken"); +myPath.pathOnly.postln; +)+
you MUST have correctly initialized the scroot classvar for this to know what it is relative to !
returns only the name of the folder that the file is in; i.e. everything in between the last but one and the last slash.
( +var myPath; +myPath = PathName.new("MyDisk/SC 2.2.8 f/Sounds/FunkyChicken"); +myPath.folderName.postln; +)+
returns the full path name that PathName contains.
( +var myPath; +myPath = PathName.new("MyDisk/SC 2.2.8 f/Sounds/FunkyChicken"); +myPath.fullPath.postln; +)+
returns a list of all the files+folders inside the folder represented by this path.
( +var myPath; +myPath = PathName.new("./"); +myPath.entries.postln; +)+
returns a list of all the files in the folder represented by this path.
( +var myPath; +myPath = PathName.new("./"); +myPath.files.postln; +)+
returns a list of all the subfolders of the folder represented by this path.
( +var myPath; +myPath = PathName.new("./"); +myPath.folders.postln; +)+
returns a Boolean indicating whether or not the path represents a file (not a folder).
( +var myPath; +myPath = PathName.new("./"); +myPath.isFile.postln; +)+
returns a Boolean indicating whether or not the path represents a folder (not a file).
( +var myPath; +myPath = PathName.new("./"); +myPath.isFolder.postln; +)+
Iterates over all files found in the pathname, including ones in subfolders.
( +var myPath; +myPath = PathName.new("./"); +myPath.filesDo{|afile| afile.postln}; +)+
returns a list of all the folder names contained in the pathname itself.
( +var myPath; +myPath = PathName.new("MyDisk/SC 2.2.8 f/Sounds/FunkyChicken"); +myPath.allFolders.postln; +)+
if path is an absolute path, returns the disk name; else a blank string.
( +var myPath; +myPath = PathName.new("MyDisk/SC 2.2.8 f/Sounds/FunkyChicken"); +myPath.diskName.postln; +) + +( // note the / at the start +var myPath; +myPath = PathName.new("/MyDisk/SC 2.2.8 f/Sounds/FunkyChicken"); +myPath.diskName.postln; +)+
Path concatenation operator - useful for avoiding doubling-up slashes unnecessarily.
(PathName("/somewhere") +/+ PathName("over/the/rainbow")).postln; +(PathName("/somewhere") +/+ PathName("/over/the/rainbow")).postln;+
returns a number at the end of PathName. Returns zero if there is no number.
PathName("floating1").endNumber.postln; +PathName("floating").endNumber.postln;+
returns -fullPath without any numbers at the end.
PathName("floating1").noEndNumbers.postln; +PathName("floating").noEndNumbers.postln;+
generates a sensible next name for a file by incrementing a number at the end of PathName, or by adding one if there is none. This is useful for recording files with consecutive names, and e.g. to generate a new filename when you don't want to overwrite an existing file with the current name.
PathName("floating34").nextName.postln; +PathName("floating").nextName.postln; +PathName("floating12_3A4X_56.7").nextName.postln;+
Here is an example that uses many instance methods. Just pick any file to see all the parts of its path.
( +GetFileDialog.new( + { arg ok, path; + var myPathName; + if (ok, + { + myPathName = PathName.new(path); + + "New PathName object: ".postc; + myPathName.postln; + + "fileName only: ".postc; + myPathName.fileName.postln; + + "path up to file only: ".postc; + myPathName.pathOnly.postln; + + "folder Name: ".postc; + myPathName.folderName.postln; + } + ) + } +) +)+ +
Choose a soundfile to put into the library, using its foldername and filename.
( +GetFileDialog.new( + { arg ok, path; + var myPathName, myFile; + if (ok, + { + myPathName = PathName.new(path); + + // read your file from disk, e.g. a soundFile/ + + myFile = SoundFile.new; + if (myFile.openRead(path), + { + Library.put( + [ myPathName.folderName.asSymbol, myPathName.fileName.asSymbol ], + myFile); + ("Check Library.global" + myPathName.folderName + "please.").postln; + }, + { ("Could not read soundfile" + path ++ ".").postln; } + ); + myFile.close; + } + ) + } +) +)+ +
Save three tables in the same folder. Note: The file name chosen in the dialog is ignored! The files are always named table1, table2, table3.
( +var table1, table2, table3; + +table1 = Wavetable.sineFill(1024, [1,2,3]); +table2 = Signal.newClear.asWavetable; +table3 = Wavetable.sineFill(1024, Array.rand(64, 0.0, 1.0)); + +GetFileDialog.new( + { arg ok, path; + var myPathName, myPathOnly; + if (ok, + { + myPathName = PathName.new(path); + myPathOnly = myPathName.pathOnly; + ("writing files tables1-3 to"+myPathOnly).postln; + table1.write(myPathOnly ++ "table1"); + table2.write(myPathOnly ++ "table2"); + table3.write(myPathOnly ++ "table3"); + } + ) + } +) +)+ +
When triggered, pauses a node.
gate |
+ When gate is 0, node is paused, when 1 it runs. |
id |
+ Node to be paused. |
s.boot; + +SynthDef(\a, { Out.ar(0, SinOsc.ar(800, 0, 0.2)) }).add; + +SynthDef(\b, { |gate = 1| Out.ar(1, PinkNoise.ar(0.3)); Pause.kr(gate, 1001) }).add; + +s.sendMsg(\s_new, \a, 1001, 0, 0); + +s.sendMsg(\s_new, \b, 1002, 0, 0); + +s.sendMsg(\n_set, 1002, \gate, 0); + +s.sendMsg(\n_set, 1002, \gate, 1);+
This is a better pitch follower than ZeroCrossing, but more costly of CPU. For most purposes the default settings can be used and only in
needs to be supplied. Pitch returns two values (via an Array of OutputProxys, see the OutputProxy help file), a freq
which is the pitch estimate and hasFreq
, which tells whether a pitch was found. Some vowels are still problematic, for instance a wide open mouth sound somewhere between a low pitched short 'a' sound as in 'sat', and long 'i' sound as in 'fire', contains enough overtone energy to confuse the algorithm.
in | |
initFreq | |
minFreq | |
maxFreq | |
execFreq | |
maxBinsPerOctave | |
median | |
ampThreshold | |
peakThreshold | |
downSample | |
clar |
The pitch follower executes periodically at the rate specified by execFreq
in cps. execFreq
is clipped to be between minFreq
and maxFreq
. First it detects whether the input peak to peak amplitude is above the ampThreshold
. If it is not then no pitch estimation is performed, hasFreq
is set to zero and freq
is held at its previous value. It performs an autocorrelation on the input and looks for the first peak after the peak around the lag of zero that is above peakThreshold
times the amplitude of the peak at lag zero.
+
If the clar
argument is greater than zero (it is zero by default) then hasFreq
is given additional detail. Rather than simply being 1 when a pitch is detected, it is a "clarity" measure in the range between zero and one. (Technically, it's the height of the autocorrelation peak normalised by the height of the zero-lag peak.) It therefore gives a kind of measure of "purity" of the pitched signal.
+
Using a peakThreshold
of one half does a pretty good job of eliminating overtones, and finding the first peak above that threshold rather than the absolute maximum peak does a good job of eliminating estimates that are actually multiple periods of the wave.
+
The autocorrelation is done coarsely at first using a maximum of maxBinsPerOctave
lags until the peak is located. Then a fine resolution search is performed until the peak is found. (Note that maxBinsPerOctave does NOT affect the final pitch resolution; a fine resolution search is always performed. Setting maxBinsPerOctave larger will cause the coarse search to take longer, and setting it smaller will cause the fine search to take longer.)
+
The three values around the peak are used to find a fractional lag value for the pitch. If the pitch frequency is higher than maxFreq
, or if no peak is found above minFreq
, then hasFreq
is set to zero and freq
is held at its previous value.
+
It is possible to put a median filter of length median
on the output estimation so that outliers and jitter can be eliminated. This will however add latency to the pitch estimation for new pitches, because the median filter will have to become half filled with new values before the new one becomes the median value. If median is set to one then that is equivalent to no filter, which is the default.
+
When an in range peak is found, it is inserted into the median filter, a new pitch is read out of the median filter and output as freq
, and hasFreq
is set to one.
+
It is possible to down sample the input signal by an integer factor downSample
in order to reduce CPU overhead. This will also reduce the pitch resolution.
+
Until Pitch finds a pitch for the first time, it will output initFreq
.
+
None of these settings are time variable.
(use headphones!)
( +SynthDef("pitchFollow1", { + var in, amp, freq, hasFreq, out; + in = Mix.new(SoundIn.ar([0,1])); + amp = Amplitude.kr(in, 0.05, 0.05); + # freq, hasFreq = Pitch.kr(in, ampThreshold: 0.02, median: 7); + //freq = Lag.kr(freq.cpsmidi.round(1).midicps, 0.05); + out = Mix.new(VarSaw.ar(freq * [0.5,1,2], 0, LFNoise1.kr(0.3,0.1,0.1), amp)); + 6.do({ + out = AllpassN.ar(out, 0.040, [0.040.rand,0.040.rand], 2) + }); + Out.ar(0,out); +}).play(s); +)+
( +SynthDef("pitchFollow2", { + var in, amp, freq, hasFreq, out; + in = Mix.new(SoundIn.ar([0,1])); + amp = Amplitude.kr(in, 0.05, 0.05); + # freq, hasFreq = Pitch.kr(in, ampThreshold: 0.02, median: 7); + out = CombC.ar(LPF.ar(in, 1000), 0.1, (2 * freq).reciprocal, -6).distort * 0.05; + 6.do({ + out = AllpassN.ar(out, 0.040, [0.040.rand,0.040.rand], 2) + }); + Out.ar(0,out); +}).play(s); +) + +/* + +RM octaver + +inSignal is RingModulated by a sinusoidal tone with half frequency. +The resulting spectrum is given by all the components of inSignal with +half freqs. +This means that the new spectrum is a one 8ve below version of the input signal's one, +with only odd partials. +As a consequence, if inSignal is added again, even partials are +recovered. + +See: +Miller Puckette, The Theory and Technique of Electronic Music, p. 126 +http://crca.ucsd.edu/~msp/techniques/latest/book.pdf +http://crca.ucsd.edu/~msp/techniques/latest/book-html/node77.html#sect5.ringmod + +andreavalle + +*/ + +( +SynthDef.new(\RmOctaver, { var in, out = 0, freq, hasFreq; + in = SoundIn.ar(0); + # freq, hasFreq = Pitch.kr(in); + Out.ar(out, SinOsc.ar(freq: freq*0.5)*in+in); +}).add; +) + +Synth.new(\RmOctaver);+ +
The Platform class (along with its subclasses) handles things which differ between operating systems (mac/linux/windows/...), to simplify cross-platform aspects of SuperCollider. +
Platform is an abstract class encapsulating various platform dependent constants and properties, such as directories, primitive features and startup files. The platform object is accessible through the platform
method of the main process instance:
thisProcess.platform+ +
Currently implemented platforms include: OSXPlatform, LinuxPlatform, WindowsPlatform, UnixPlatform.
Most of Platforms class methods are simply wrappers to thisProcess.platform.method
.
Perform actions depending on the current platform (name), just like Object:switch:
Platform.case( + \osx, { "OSX".postln }, + \linux, { "Linux".postln }, + \windows, { "Windows".postln } +);+
returns a String indicating which IDE the language believes it is running in. (Often this is determined via the "-i" option to the sclang executable.) This is determined when sclang starts and cannot be changed dynamically. +
The main purpose of this is to include/exclude folders from the class search patch depending on which IDE is in use: for example, if the value of ideName is "scapp" then folders named "scide_scapp" are included and all other folders beginning with "scide_" are excluded. The default value of this is "none". +
Known IDE names in use are "scapp" (SuperCollider.app on Mac), "scvim" (vim), "scel" (emacs). Others may be used.
location of the bundled class library
location of the bundled help files
system application support directory
system extension directory (see Using Extensions)
user home directory
user application support directory
directory for configuration files
user extension directory (see Using Extensions)
platform specific directory for class files (see Using Extensions)
platform specific path separator
platform specific resource directory
platform recordings directory
default directory for temporary files
path |
+ A path string. |
The input string formatted as a command-line argument. On Windows this method quotes the string. On Unix-based systems this method escapes space characters with a backslash.
Evaluate ifFunction if all features are present, otherwise evaluate elseFunction.
Platform.when(#[\Document, \SCWindow], { "yeehah!".postln });+
returns the platform name
recompile class library
location of the bundled class library
location of the bundled help files
system application support directory
system extension directory (see Using Extensions)
user home directory
user application support directory
directory for configuration files
user extension directory (see Using Extensions)
platform specific directory for class files (see Using Extensions)
platform specific path separator
platform recordings directory
platform specific resource directory
default directory for temporary files
files to be loaded on startup
(re)load startup files
kill all processes as defined by cmdLineArgs.
cmdLineArgs |
+ a string containing one or several process names. // e.g. kill all possibly running servers (scsynth or supernova) +thisProcess.platform.killAll("scsynth supernova");+ |
kill a single process as identified by its process ID.
pid |
+ an Integer which is the pid of the process to kill. // start a server program from the cmdLine for testing +~pid = unixCmd(Server.program + s.options.asOptionsString(57100)); +// kill just that server program by its pid: +thisProcess.platform.killProcessByID(~pid);+ |
Features are abstract symbols that can be declared by extension authors and be checked during runtime in user code. Apart from explicitly declared features, class and primitive names are implicitly declared.
Declare aSymbol to be a feature present in the runtime. Class names and primitive names cannot be declared as features.
Return true if the feature aSymbol is present in the runtime system. aSymbol can refer to explicitly declared features as well as class and primitive names.
thisProcess.platform.hasFeature(\Object); +thisProcess.platform.hasFeature('_SCWindow_BeginFullScreen'); +thisProcess.platform.hasFeature('_myFuncyPrimitive'); + +thisProcess.platform.declareFeature('superCrazyCompositionSystem'); +thisProcess.platform.hasFeature('superCrazyCompositionSystem');+
Evaluate ifFunction if all features are present, otherwise evaluate elseFunction.
thisProcess.platform.when(#[\Document, \SCWindow], { "yeehah!".postln });+ +
Plays back a sample resident in memory.
numChannels |
+ Number of channels that the buffer will be. This must be a fixed integer. The architecture of the SynthDef cannot change after it is compiled. |
bufnum |
+ The index of the buffer to use. NOTE: If you supply a bufnum of a buffer with a differing number of channels than the one specified in this PlayBuf, it will post a warning and output the channels it can. |
rate |
+ 1.0 is the server's sample rate, 2.0 is one octave up, 0.5 is one octave down -1.0 is backwards normal rate… etc. Interpolation is cubic. |
trigger |
+ A trigger causes a jump to the startPos. A trigger occurs when a signal changes from negative value to positive value. |
startPos |
+ Sample frame to start playback. |
loop |
+ 1 means true, 0 means false. This is modulateable. |
doneAction |
+ an integer representing an action to be executed when the buffer is finished playing. This can be used to free the enclosing synth, etc. See Done for more detail. |
s.boot // Boot the server, if you need to + +// read a whole sound into memory +// note: not *that* columbia, the first one +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); // remember to free the buffer later. + +SynthDef(\help_PlayBuf, {| out = 0, bufnum = 0 | + Out.ar(out, + PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum), doneAction: Done.freeSelf) + ) +}).play(s, [\out, 0, \bufnum, b]);+ +
In the above example, note how the doneAction: Done.freeSelf
causes the synth to free itself when the buffer reaches its end.
+
Note again that the number of channels must be fixed for the SynthDef. It cannot vary depending on which buffer you use.
// loop is true +SynthDef(\help_PlayBuf, {| out = 0, bufnum = 0 | + Out.ar(out, + PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum), loop: 1.0) + ) +}).play(s, [\out, 0, \bufnum, b]); + + +// trigger one shot on each pulse +SynthDef(\help_PlayBuf, {| out = 0, bufnum = 0 | + var trig; + trig = Impulse.kr(2.0); + Out.ar(out, + PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum), trig, 0, 0) + ) +}).play(s, [\out, 0, \bufnum, b]); + + +// trigger one shot on each pulse +SynthDef(\help_PlayBuf, {| out = 0, bufnum = 0 | + var trig; + trig = Impulse.kr(XLine.kr(0.1, 100, 30)); + Out.ar(out, + PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum), trig, 5000, 0) + ) +}).play(s, [\out, 0, \bufnum, b]); + + +// mouse control of trigger rate and startpos +SynthDef(\help_PlayBuf, { arg out=0, bufnum=0; + var trig; + trig = Impulse.kr(MouseY.kr(0.5, 200, 1)); + Out.ar(out, + PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum), trig, MouseX.kr(0, BufFrames.kr(bufnum)), 1) + ) +}).play(s, [\out, 0, \bufnum, b]); + + +// accelerating pitch +SynthDef(\help_PlayBuf, {| out = 0, bufnum = 0 | + var rate; + rate = XLine.kr(0.1, 100, 60); + Out.ar(out, + PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum)*rate, 1.0, 0.0, 1.0) + ) +}).play(s, [\out, 0, \bufnum, b]); + + +// sine wave control of playback rate. negative rate plays backwards +SynthDef(\help_PlayBuf, {| out = 0, bufnum = 0 | + var rate; + rate = FSinOsc.kr(XLine.kr(0.2, 8, 30), 0, 3, 0.6); + Out.ar(out, + PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum) * rate, 1, 0, 1) + ) +}).play(s, [\out, 0, \bufnum, b]); + + +// zig zag around sound +SynthDef(\help_PlayBuf, {| out = 0, bufnum = 0 | + var rate; + rate = LFNoise2.kr(XLine.kr(1, 20, 60), 2); + Out.ar(out, + PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum) * rate, 1, 0, 1) + ) +}).play(s, [\out, 0, \bufnum, b]); + +b.free;+
This class is missing documentation.
Copy and paste the text below and save to HelpSource/Classes/Plot.schelp
TITLE:: Plot +summary:: (put short description here) +categories:: Undocumented classes +related:: Classes/SomeRelatedClass, Reference/SomeRelatedStuff, etc. + +DESCRIPTION:: +(put long description here) + + +CLASSMETHODS:: + +METHOD:: new +(describe method here) + +ARGUMENT:: plotter +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: initClass +(describe method here) + +returns:: (describe returnvalue here) + + +INSTANCEMETHODS:: + +METHOD:: plotColor +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: zoomFont +(describe method here) + +ARGUMENT:: val +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: plines +(describe method here) + +ARGUMENT:: x +(describe argument here) + +ARGUMENT:: y +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: gridOnX +(describe method here) + +ARGUMENT:: bool +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: bounds +(describe method here) + +ARGUMENT:: rect +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: editDataLine +(describe method here) + +ARGUMENT:: pt1 +(describe argument here) + +ARGUMENT:: pt2 +(describe argument here) + +ARGUMENT:: plotIndex +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: editDataIndex +(describe method here) + +ARGUMENT:: index +(describe argument here) + +ARGUMENT:: x +(describe argument here) + +ARGUMENT:: y +(describe argument here) + +ARGUMENT:: plotIndex +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: drawBackground +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: getDataPoint +(describe method here) + +ARGUMENT:: x +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: levels +(describe method here) + +ARGUMENT:: x +(describe argument here) + +ARGUMENT:: y +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: spec +(describe method here) + +ARGUMENT:: sp +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: steps +(describe method here) + +ARGUMENT:: x +(describe argument here) + +ARGUMENT:: y +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: value +(describe method here) + +ARGUMENT:: array +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: backgroundColor +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: resampledSize +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: getIndex +(describe method here) + +ARGUMENT:: x +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: linear +(describe method here) + +ARGUMENT:: x +(describe argument here) + +ARGUMENT:: y +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: getRelativePositionY +(describe method here) + +ARGUMENT:: y +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: gridLineSmoothing +(describe method here) + +ARGUMENT:: bool +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: labelX +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: labelY +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: drawLabels +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: dataCoordinates +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: gridLinePattern +(describe method here) + +ARGUMENT:: pattern +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: font +(describe method here) + +ARGUMENT:: f +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: draw +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: copy +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: getRelativePositionX +(describe method here) + +ARGUMENT:: x +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: init +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: gridColorX +(describe method here) + +ARGUMENT:: c +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: hasSteplikeDisplay +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: drawGrid +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: gridColorY +(describe method here) + +ARGUMENT:: c +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: editData +(describe method here) + +ARGUMENT:: x +(describe argument here) + +ARGUMENT:: y +(describe argument here) + +ARGUMENT:: plotIndex +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: resampledDomainSpec +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: points +(describe method here) + +ARGUMENT:: x +(describe argument here) + +ARGUMENT:: y +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: drawData +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: plotter +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: plotBounds +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: gridOnY +(describe method here) + +ARGUMENT:: bool +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: fontColor +(describe method here) + +ARGUMENT:: c +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: domainCoordinates +(describe method here) + +ARGUMENT:: size +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: prResampValues +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: domainSpec +(describe method here) + +ARGUMENT:: sp +(describe argument here) + +returns:: (describe returnvalue here) + + +EXAMPLES:: + +code:: +(some example code) +:: ++
Plot data of up to three dimensions on a Window or UserView.
When the plotter window has focus, the following keyboard shortcuts can be used to change the display:
+ / - | vertical zoom |
= | compare plot channels |
n | toggle normalize display (0..1) / (-1..1), or fit range |
s | toggle superposition (see: superpose) |
m | switch plot mode (see: Plotter: plotMode) |
e | toggle editing (see: Plotter: editMode) |
g | toggle horizontal (domain) grid |
G | toggle vertical (codomain) grid |
p | print curve |
ctrl-+ / - | zoom font |
alt-click | post value |
Plotter extends other classes with methods. To see what classes implements plot, see Methods: plot
// plot array +[1, 6, 2, -5, 2].plot; +(0..100).normalize(0, 8pi).sin.plot; + +// nested arrays +{ (0..100).normalize(0, 15.0.rand).sin }.dup(3).plot; +{ { (0..17).normalize(0, 15.0.rand).sin }.dup(4) }.dup(3).plot; + +// UGen functions +{ SinOsc.ar([700, 357]) * SinOsc.ar([400, 476]) * 0.2 }.plot; + +// Buffer +Buffer.read(s, Platform.resourceDir +/+ "sounds/SinedPink.aiff").plot; + +// Env +Env.perc(0.4, 0.6).plot;+
{ |x| sin(x) }.plotGraph(300,0,2*pi); +{ |x| sin(1/x)*x }.plotGraph(from:0.0001,to:0.2);+
name |
+ Plot window title. |
bounds |
+ The window bounds (a Rect). |
parent |
+ Either a Window / View may be passed in - then the plot is embedded. Otherwise a new Window is created. |
( +a = Plotter("the plot", Rect(600, 30, 800, 250)); +a.value = (0..1000).normalize(0, 14pi).curdle(0.01).scramble.flat.sin; +)+
Open given plotter in a new window or within a given composite view.
argParent |
+ Either a Window or View may be passed in - then the plot is embedded. Otherwise a new Window is created. |
argBounds |
+ The window bounds (a Rect). |
Set the style of data display.
(symbol) |
+ Available modes:
|
a = (0..20).scramble.plot; +a.plotMode = \points; a.refresh; +a.plotMode = \plines; a.refresh; +a.plotMode = \levels; a.refresh; +a.plotMode = \steps; a.refresh; +a.plotMode = \linear; a.refresh;+
Set properties of all plot views. Defaults are taken from GUI.skin.at(\plot);
... pairs |
+ A list of symbol,value pairs. Supported properties: + + |
Example:
( +a = { (0..30).scramble }.dup(2).plot; +a.setProperties( + \fontColor, Color.red, + \plotColor, Color.blue, + \backgroundColor, Color.black, + \gridColorX, Color.white, + \labelX, "Humidity" +); +a.refresh; +); + +GUI.skin.at(\plot); // defaults+
If the edit mode is set to true, the data may be edited via cursor.
a = (0..20).plot; +a.editMode = true; // now edit the data by clicking into the plot.. +a.value; // the value+
Set the number of data points displayed maximally per pixel (default: 1)
a = (0..200).scramble.plot; +a.resolution = 8; a.refresh; // resizing the window shows interpolation +a.resolution = 1; a.refresh;+
If true (default: true
), specs are derived from new data (using min and max values) automatically.
If set to true, plotter displays channels on top of each other (keyboard shortcut: s)
a = { (0..30).scramble }.dup(2).plot; +a.superpose = true; a.refresh;+
Return or set the data values. Data may be numerical arrays of up to 3 dimensions.
a = [1, 4, 2, 7, 4].dup(2).plot; +a.value;+
Reference to the current internal data.
the last cursorPos (a Point).
the single subplots (a Plot).
Set or get the spec for the y-axis (codomain).
a = { (40..3000).scramble }.dup(2).plot; +a.specs = \freq.asSpec; a.refresh;+
Set or get the spec for the x-axis (domain).
a = { (40..300).scramble }.dup(2).plot; +a.domainSpecs = \freq.asSpec; a.refresh;+
Supply a function which is evaluated when editing data. The function is called with the arguments: plotter
, plotIndex
, index
, val
, x
, y
.
Example:
( +a = { (0..10).scramble.normalize }.dup(2).plot; +a.editMode = true; +a.editFunc = { |...args| args.postln }; +); + +// using plotter as a control interface +( +a = (0..10).scramble.normalize(300, 400).plot; +a.specs = \freq; a.plotMode = \points; +a.editMode = true; +x = { SinOsc.ar(\freq.kr(a.value)).mean * 0.1 }.play; +a.editFunc = { |plotter, plotIndex, i, val| + x.setn(\freq, a.value) +}; +a.parent.onClose = { x.release }; +); + +( +a = { (0..10).scramble.normalize(300, 400) }.dup.plot; +a.specs = \freq; a.plotMode = \levels; +a.editMode = true; +x = { + var phase = SinOsc.ar(\rate.kr(a.value[1])); + SinOsc.ar(\freq.kr(a.value[0]), phase).mean * 0.1 +}.play; +a.editFunc = { |plotter, plotIndex, i, val| + x.setn(\freq, a.value[0]); + x.setn(\rate, a.value[1]); +}; +a.parent.onClose = { x.release }; +);+
// embedding in another GUI +( +w = Window("plot panel", Rect(20, 30, 520, 450)); +Slider.new(w, Rect(10, 10, 490, 20)).resize_(2).action_ { |v| + a.value = (0..(v.value.linexp(0, 1, 5, 8000)).asInteger).scramble; + w.refresh; +}; +z = CompositeView(w, Rect(10, 35, 490, 400)).background_(Color.rand(0.7)).resize_(5); +a = Plotter("plot", parent: z).value_([0, 1, 2, 3, 4].scramble * 100); +w.front; +) + + +( +a = Plotter("the plot", Rect(600, 30, 600, 400)); +a.value = (0..100).normalize(0, 8pi).sin; +) + +a.value = { |i| (0..90) % (i + 12) + ( (0..90) % (i + 2 * 1) ) }.dup(3); +a.value = (0..12).squared; +a.plotMode = \points; a.refresh; +a.plotMode = \levels; a.refresh; +a.plotMode = \plines; a.refresh; + +a.domainSpecs = [[0, 115, \lin, 1]]; a.refresh; + +a.parent.close; // close window +a.makeWindow; // open it again + +a.value = { (0..70).scramble }.dup(3); +a.plotMode = \linear; a.refresh; +a.value = { |i| (0..2000).normalize(0, 4pi + i).sin } ! 4; // lots of values, test efficiency +a.value = { |i| (0..10000).normalize(0, 8pi + i).sin } ! 3; // lots of values, test efficiency +a.value = { (0..140).scramble } ! 7; + +a.value = { |i| (0..90).normalize(0, 8pi + (i*2pi)).sin } ! 2 * [400, 560] + 700; +a.value = { |i| (_ + 2.0.rand).dup(100).normalize(0, 8pi + i).sin } ! 2 * 400 + 700; + + +// multi channel expansion of single values +a.value = { |i| (_ + 2.0.rand).dup(100).normalize(0, 8pi + i).sin *.t [1, 2, 3] } ! 2 * 400 + 700; +a.value = { |i| (0..10) **.t [1, 1.2, 1.3, 1.5] * (3.5 ** i) }.dup(3); + +a.parent.bounds = Rect(400, 100, 500, 700); +a.parent.bounds = Rect(600, 30, 500, 300); + +a.superpose = true; +a.value = { |i| (0..20) * (3.5 ** i) }.dup(5); +a.superpose = false; + +// specs + +a.value = (50..90).midicps.scramble; +a.specs = \freq; a.refresh; +a.value = (1..60).scramble.neg; +a.specs = \db; a.refresh; + +a.value = { |i| { exprand(1e3, (10 ** (i + 8))) }.dup(90) }.dup(3); +a.value = { { exprand(1e3, 1e9) }.dup(90) }.dup(3); +a.specs = [[1e3, 1e10, \exp], [1e3, 1e20, \exp], [1e3, 1e30, \exp]]; a.refresh; +a.domainSpecs = [[0, 5], [-8, 100], [-1, 1]]; a.refresh; + + +// Array:plot +( +a = (4 ** (-5..0)).postln.plot; +a.specs = \delay; a.refresh; +a.domainSpecs = [0, 10, \lin, 0, 0, " Kg"].asSpec; a.refresh; +); + +a.domainSpecs = [0.1, 10, \exponential, 0, 0, " Kg"].asSpec; a.refresh; +a.domainSpecs = [-10, 10, \lin, 0, 0, " Kg"].asSpec; a.refresh; + + +a = [(0..100) * 9, (200..1300) * 2, (200..1000)/ 5].plot; +a.superpose = true; + +a = [[0, 1.2, 1.5], [0, 1.3, 1.5, 1.6], [0, 1.5, 1.8, 2, 6]].midiratio.plot; +a.plotMode = \levels; a.refresh; +a.superpose = false; + + +// Function:plot +a = { SinOsc.ar([700, 357]) * SinOsc.ar([400, 476]) * 0.2 }.plot; +a = { SinOsc.ar([700, 357] *0.02) * SinOsc.ar([400, 476]) * 0.3 }.plot(0.2, minval: -1); +a = { SinOsc.ar(440) }.plot(1); + + +// Env:plot +Env.perc(0.4, 0.6).plot; +Env.new({ 1.0.rand2 }! 8, { 1.0.rand } ! 7, \sin).plot; + +// Buffer:plot +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/SinedPink.aiff"); + // Platform.resourceDir +/+ "sounds/SinedPink.aiff" contains SinOsc on left, PinkNoise on right +b.plot; +b.free;+
The default styles are kept (and may be overridden) in GUI.skin.at(\plot)
. See also GUI help.
// specify plot layout +( +GUI.skin.plot.gridLinePattern = FloatArray[1, 0]; +GUI.skin.plot.fontColor = Color(0.5, 1, 0); +GUI.skin.plot.gridColorX = Color.yellow(0.5); +GUI.skin.plot.gridColorY = Color.yellow(0.5); +GUI.skin.plot.background = Color.black; +GUI.skin.plot.plotColor = (10..0).normalize(0.1, 1).collect { |i| Color.rand(i) }; +GUI.skin.plot.labelX = "X"; +GUI.skin.plot.labelY = "Y"; +); + +( +x = { |i| (0..60).scramble.clump(8) * (3.5 ** i) }.dup(3); +x.plot("ARRAY:PLOT", Rect(200, 300, 600, 500)); +) + +GUI.skin.plot.put(\plotColor, { Color.rand(0.0, 0.8) } ! 8); +[(0..100), (20..120), (40..140)].squared.flop.bubble.plot; + +// reset the defaults: +Plot.initClass;+
This class is missing documentation.
Copy and paste the text below and save to HelpSource/Classes/Pplayer.schelp
TITLE:: Pplayer +summary:: (put short description here) +categories:: Undocumented classes +related:: Classes/SomeRelatedClass, Reference/SomeRelatedStuff, etc. + +DESCRIPTION:: +(put long description here) + + +CLASSMETHODS:: + +METHOD:: new +(describe method here) + +ARGUMENT:: playerPattern +(describe argument here) + +ARGUMENT:: subPattern +(describe argument here) + +returns:: (describe returnvalue here) + + +INSTANCEMETHODS:: + +METHOD:: storeArgs +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: subPattern +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: playerPattern +(describe method here) + +ARGUMENT:: playerPattern +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: embedInStream +(describe method here) + +ARGUMENT:: event +(describe argument here) + +returns:: (describe returnvalue here) + + +EXAMPLES:: + +code:: +(some example code) +:: ++
Generally a proxy is a placeholder for something. A node proxy is a placeholder for something playing on a server that writes to a limited number of busses (e.g. a synth or an event stream). NodeProxy objects can be replaced and recombined while they play. Also they can be used to build a larger structure which is used and modified later on. Overview: JITLib +
When accessed, ProxySpace returns a NodeProxy. A similar class without environment: Ndef +
For more examples see: ProxySpace examples, jitlib_basic_concepts_01 +
For GUI overview, see ProxyMixer. See NodeProxy for many relevant methods.
s.boot; + +p = ProxySpace.new; +p.fadeTime = 2; // fadeTime specifies crossfade +p[\out].play; // monitor an empty placeholder through hardware output +// set its source +p[\out] = { SinOsc.ar([350, 351.3], 0, 0.2) }; +p[\out] = { Pulse.ar([350, 351.3] / 4, 0.4) * 0.2 }; +p[\out] = Pbind(\dur, 0.03, \freq, Pbrown(0, 1, 0.1, inf).linexp(0, 1, 200, 350)); + +// route one proxy through another: +p[\out] = { Ringz.ar(p[\in].ar, [350, 351.3] * 8, 0.2) * 4 }; +p[\in] = { Impulse.ar([5, 7]/2, [0, 0.5]) }; + +a.clear(3); // clear after 3 seconds +b.clear(3);+
server |
+ a Server object. Note that on remote computers the clock must be in sync. |
name |
+ a Symbol. If a name is given, the proxy space is stored in |
clock |
+ for event-based or beat-sync playing use a TempoClock. |
replace the currentEnvironment with a new ProxySpace and clear the current one, if it is a ProxySpace (this is to avoid piling up proxy spaces). +
In order to move to another ProxySpace while keeping the current, use pop and then push a new one. To have multiple levels of proxy spaces, use .new.push;
restore the previous currentEnvironment
clear all registered spaces
returns a group that plays the NodeProxy at that key.
key |
+ a Symbol |
out |
+ output channel offset |
numChannels |
+ play this number of channels. |
returns a RecNodeProxy that records the NodeProxy at that key.
returns a NodeProxy output that plays the NodeProxy at that key, to be used within a function used as input to a node proxy
when the proxyspace is created without a running server this method can be used. To run it (internally this is done by -play as well).
set the fadetime of all proxies as well as the default fade time
set the clock of all proxies as well as the default clock.
set the quant of all proxies as well as the default quant.
free all proxies (i.e. free also the groups, do not stop the monitors)
release all proxies (i.e. keep the groups running)
stop all proxies (stop only monitors, do not stop synths)
end all proxies (free and stop the monitors)
clear the node proxy and remove it from the environment. this frees all buses. If a fadeTime is given, first fade out, then clear.
add the ProxySpace to the repository (name required)
remove the ProxySpace from the repository
The rate and numChannels of the NodeProxy determined in a lazy way from the first object put into this environment (see helpfile). Once it is created it can only be set to a function that returns the same rate and a number of channels equal to the intial one or smaller. For details, see the_lazy_proxy.
Gets the NodeProxy at key (if none exists, returns a new one) and sets its source to obj. For how this works, see also LazyEnvir and NodeProxy.
Return the proxy source object at that key.
free and remove all proxies that are not needed in order to play the ones passed in with 'exclude'. if none are passed in, all proxies that are monitoring (with the .play message) are kept as well as their parents etc.
free all proxies that are not needed in order to play the ones passed in with 'to'. if none are passed in, all proxies that are monitored (with the play message) are kept as well as their parents etc.
Copies the environment into a new one, with each proxy being copied as well (See: NodeProxy: -copy). Also the instance variables that determine the ProxySpace behaviour are included, such as server, fadeTime, quant, reshaping (this happens in the copyState
method).
p = ProxySpace.push(s.boot); +p.reshaping = \elastic; +~out.play; +~out = { Blip.ar(~freq, ~numharm) }; +~freq = 70; +~numharm = { MouseX.kr(2, 100, 1) }; + +q = p.copy; p.pop; q.push; + +q.reshaping.postln; // also elastic +~out.play; +~freq = { MouseY.kr(2, 1000, 1) * [1, 1.2] }; + +p.end; q.end;+
creates a new document with the current proxyspace state. This does not allow open functions as proxy sources. see: jitlib_asCompileString.
keys |
+ list of keys to document a subset of proxies |
onlyAudibleOutput |
+ a boolean. |
includeSettings |
+ a boolean. |
// ProxySpace returns instances of NodeProxy: +a = NodeProxy(s) is equivalent to ~a; +a.source = ... is equivalent to ~a = ... +a[3] = ... is equivalent to ~a[3] = ... + +// the two expressions are equivalent: +~out = something; +currentEnvironment.put(\out, something);+
// examples + +p = ProxySpace.push(s.boot); // use ProxySpace as current environment. + +~out.play; + +~out = { SinOsc.ar([400, 407] * 0.9, 0, 0.2) }; + +~out = { SinOsc.ar([400, 437] * 0.9, 0, 0.2) * LFPulse.kr([1, 1.3]) }; + +~out = { SinOsc.ar([400, 437] * 0.9, 0, 0.2) * ~x.kr(2) }; + +~x = { LFPulse.kr([1, 1.3] * MouseX.kr(1, 30, 1)) }; + +~out = { SinOsc.ar([400, 437] * Lag.kr(0.1 + ~x, 0.3), 0, 0.2) * ~x }; + +p.fadeTime = 5; + +~out = { SinOsc.ar([400, 437] * 1.1, 0, 0.2) * ~x.kr(2) }; + +p.clear(8); // end and clear all in 8 sec. + + +p.pop; // move out.+ +
Pspawner allows a routine to dynamically start and stop subpatterns.
function |
+ The function defines a Routine that receives a Spawner as its sole argument. All control of subpatterns is through the spawner. + Spawner responds to the messages:
NOTE: We should move the documentation of above methods to the Spawner helpfile... |
// example 1: a simple Pspawner + +( +Pspawner({ | sp | + +// parallel in-c'ish pulses will run throughout the example + sp.par(Pbind(*[ degree: [0,7], octave: 7, dur: 0.2, db: Pseq([-20, -24, -22, -24], inf)]) ); + +// scales in sequence with pauses + sp.seq( + Pbind(*[ degree: Pseq((0..7).mirror), dur: 0.2]) + ); + sp.wait(0.4); + sp.seq( + Ppar([ + Pbind(*[ degree: Pseq((0..7).mirror), dur: 0.2, octave: 4]), + Pbind(*[ degree: Pseq((0..7).reverse.mirror), dur: 0.2]) + ]) + + ); + sp.wait(3); + sp.seq( + Pbind(*[ degree: Pseq((0..7).mirror), dur: 0.2, mtranspose: (0,2..14)]) + ); + +// scales overlaped at 0.4 second intervals + + 10.do { + sp.par( + Pbind(*[ degree: Pseq((0..7).mirror), dur: 0.2]) + ); + + sp.wait(0.4) + }; + sp.wait(1.6); + sp.suspendAll; +}).play +) + + +( +// example 2: create 6 streams at 4 second intervals +// then delete them in the order they were created + +Pspawner({ | sp | + var streams, stream; + // start patterns, collect the resultant event streams + streams = [2, 3, 4, 6, 7, 8].collect { | i | + stream = sp.par(Pbind(*[ + harmonic: i, + ctranspose: [0, 1, 3]/40, + octave: Pbrown(2,8,2), dur: 1/i, db: -30 + ]) ); + sp.wait(4); + stream; + }; + + // now stop those streams one by one + streams.do { | s | sp.suspend(s); sp.wait(4) }; +}).play +) + +( +// example 3: define a Pspawner and use Pattern manipulations +p = Pspawner({ | sp | + var pat = Pbrown( -7, 7, 3); + + sp.par(Pbind(*[octave: 5, degree: pat, dur: 1/4]) ); + sp.wait(2); + sp.par(Pbind(*[octave: 6, degree: pat, dur: 1/8]) ); + sp.wait(3); + sp.par(Pbind(*[octave: 7, degree: pat, dur: 1/6, db: -20]) ); + sp.wait(4); + sp.par(Pbind(*[octave: 4, degree: pat, dur: 1/4]) ); + + sp.wait(8); + sp.suspendAll; +}); +) + +// play the basic patten +p.play; + +( +// manipulate basic pattern with Pchain +Pchain( + Pbind(*[mtranspose: Pkey(\mtranspose) + Pstutter(8, Prand([0,[0,3,-2],[0,2,4]], inf)) ] ), //make some notes into triads + Pbind(*[ctranspose: Pwhite(-0.1, 0.1)]), //add random detuning to notes + Pbind(*[\scale, Scale.minor]), //alter the scale + Pn(Pseq([p, (type:\rest, dur: 1.0)]) ), //repeat the pattern after a 1 second pause + Pbind(*[ + db: Pstep(Pseq([-10, -13, -13, -11, -13, -13], inf), 0.1) - 10, + mtranspose: Pstep(Pwhite(-7,7), Prand([5,4,2],inf) ) //random modal transposition + ]) +).play(protoEvent: Event.default) +) + +// example 4: altering the contents of the Pspawner from separate code + +( +a = Pspawner({ |sp | + c = sp; // store the Spawner in a global variable + 100.do{ sp.wait(1) } +}); +a.play; +) + +( // c will not be valid until the Pspawner has run +b = c.par( // now start a pattern in spawner + Pbind(*[degree: Pseq((0..6) ++ (7..1), inf), dur: 0.2]) +); +) +c.suspend(b) // we can suspend and resume the stream +c.par(b) +c.suspend(b) +( +b = c.par( // or just start up a new pattern + Pbind(*[degree: Pseq((0..6) ++ (7..1), inf), dur: 0.2]) +); +) + +// example 5: Spawner can be used directly in the manner of Pspawner. +// This allows external code to access to the spawner whether or not it has run +( +c = Spawner({ |sp | + 100.do{ sp.wait(1) } +}); +b = c.par( // now start a pattern in spawner + Pbind(*[degree: Pseq((0..6) ++ (7..1), inf), dur: 0.2]) +); +c.play; // in this case, c is always valid +) +c.suspend(b) // we can suspend and resume the stream +c.par(b) +c.suspend(b) +( +b = c.par( // or just start up a new pattern + Pbind(*[degree: Pseq((0..6) ++ (7..1), inf), dur: 0.2]) +); +) + +( +Pspawner({ | sp | + (1..5).do { | i | + sp.par(Pbind(*[ + octave: i + 2, + degree: Pwhite(0,7), dur: 1/i, db: -30 + ]) ); + sp.wait(4); + }; + sp.suspendAll; +}).play +)+ +
Band limited pulse wave generator with pulse width modulation.
freq |
+ Frequency in Hertz. |
width |
+ Pulse width ratio from zero to one. 0.5 makes a square wave. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
// modulate frequency +{ Pulse.ar(XLine.kr(40, 4000, 6), 0.1, 0.2) }.play; + +// modulate pulse width +{ Pulse.ar(200, SinOsc.kr(0.2).range(0.01, 0.99), 0.2) }.play; + +// two band limited square waves thru a resonant low pass filter +{ RLPF.ar(Pulse.ar([100, 250], 0.5, 0.1), XLine.kr(8000, 400, 5), 0.05) }.play;+
A resonant low pass filter.
in |
+ The input signal. |
freq |
+ Cutoff frequency in Hertz. WARNING: due to the nature of its implementation frequency values close to 0 may cause glitches and/or extremely loud audio artifacts! |
rq |
+ The reciprocal of Q (bandwidth / cutoffFreq). |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
{ RLPF.ar(Saw.ar(200, 0.1), SinOsc.ar(XLine.kr(0.7, 300, 20), 0, 3600, 4000), 0.2) }.play; + + +( +{ var ctl = RLPF.ar(Saw.ar(5, 0.1), 25, 0.03); + SinOsc.ar(ctl * 200 + 400) * 0.1; +}.play; +) + + +( +{ var ctl = RLPF.ar(Saw.ar(5,0.1), MouseX.kr(2, 200, 1), MouseY.kr(0.01, 1, 1)); + SinOsc.ar(ctl * 200 + 400) * 0.1; +}.play; +)+
This class implements explicit recursive cutting, as described in LMJ13. The level of recursion can be set as a parameter of the cut procedure. The user chooses cut, repeat and offset functions which are applied through successive iteratinos to make new cut sequences. Only the final cut sequence of a cut sequence of a cut sequence... is rendered. +
For additional data on the algorithm see +
Nick Collins, "Recursive Audio Cutting", +
Leonardo Music Journal 13
Create a RecursiveCutProc1 object with the given parameters.
cutfunc |
+ A user specified function determining the next cut size, passed the arguments phrase length filled so far and current phraselength. |
repeatfunc |
+ A user specified function determining the number of repeats of the new cutsize, passed the arguments phrase length determined and current phraselength. |
offsetfunc |
+ A user specified function determining the offset for the new block, passed the arguments quantise level (= currphraselength/beats per sub division, so one 4/4 bar phrase with bpsd 0.5 gives quantise level of 8, ie eighth notes) ,current beats per sub division,phrase completed so far and current phrase length. The default method is {arg q,bpsubdiv; rrand(0,q - 1)*bpsubdiv}; //random offset into source+ |
reclevel |
+ Level of recursion, so 0 gives back the source exactly, 1 is one cutup, 4 is four recursive cutups. The higher the reclevel , the greater the performance hit at the start of the phrase. Outside realtime rendering is required for very high values, and the output will be increasingly set on one offset at a greatest common divisor cut size (see the paper for technical analysis). Notes however that the CPU cost is in the language app, not the server. In implicit cutting, the cost is in the Server. |
phraselength |
+ Next length of phrase in beats. |
bpsd |
+ beats per sub division. |
Called internally after a new.
this uses the setoffset method of BBCutSynth to specify offset jumps relative to the current phrase size. In normal use phrasesize will be the same as the beat length of the source you wish to cut up. Note that sources which don't respond to setoffset will not be effected by RecCutProc except for any enveloping/panning etc caused by blocks.
//you must have run this line before any of the examples below +f= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + + +//defaults +BBCut2(CutBuf2(f), RecursiveCutProc1.new).play(2); + + + + +(//20 recursion levels with simple user defined functions plus quick delay hack +var sf, group; + +group=Group.new; + +Routine.run({ + +//f=BBCutBuffer("sounds/a11wlk01.wav",4); + +s.sync; + +BBCut2(CutGroup(CutBuf2(f, pbsfunc:{rrand(0.98,1.02)}),Group.before(group)), RecursiveCutProc1.new({[4.0,2.5,1.5].choose},{rrand(1,3)},{[0.0,2.0].choose},20) +).play(4); + +{var in; + +in= In.ar(0,2); + +ReplaceOut.ar(0, CombN.ar(0.3*in, 0.25, 0.25, 20, 1, 0.7*in))}.play(group); + +}); + +) + + + +(//recursion level control +var w,slider; + +w = SCWindow("panel", Rect.new(200,200,200,100)); +slider=DDSlider( w, Rect.new(5,5,180,40), "recursion level", 0, 50, 'linear', 1, 0); + +w.front; + +BBCut2(CutBuf2(f), RecursiveCutProc1.new(reclevel:slider)).play(2); +) + + +(//user defined functions +var w,slider; +var cutfunc, repeatfunc, offsetfunc; + +cutfunc= {arg done, currpl; if((done<(currpl*0.7)), {[1.5,0.5].choose},{[0.25,1.0].choose});}; +repeatfunc= {arg done, currpl; if((done<(currpl*0.7)), {1},{rrand(1,3)});}; +//3/4 of the time let through what is there already, otherwise a random offset +offsetfunc= {arg q, bpsd, done, currphraselength; if(0.75.coin, {done%(q*bpsd)},{(q.rand)*bpsd});}; + +BBCut2(CutBuf2(f), RecursiveCutProc1.new(cutfunc, repeatfunc, offsetfunc,3, 8.0)).play(2); +) + + + +//as a comparison, here is the easier but more CPU expensive implicit recursive method +//implicit recursive cutting, nth order + +//this code assumes 8 out busses 0-7, 8 in (8-15 = 1-8 for AudioIn UGen) +//then spare audio busses from 16 up + +//There will be a delay in hearing output proportional to n since the +//various audio capture devices need to be capturing a flow before +//the next can capture a flow, passing down to the final one. +( +var clock, n; +var source, reccutters; +var group; + +group=Group.new; + +//number of levels of recursive audio cutting +//be careful, costs go up for large n... +n=4; + +clock= ExternalClock(TempoClock(2.6)).play; + +Routine.run({ + +source=BBCut2(CutGroup([CutBuf2(f),CutMixer(16,1.0,1.0,0.0)],Group.head(group),numChannels:1)); + +reccutters= Array.fill(n); + +n.do({arg i; +reccutters.put(i, BBCut2(CutGroup([CutStream1((2*i)+16, atkprop:0.02, relprop:0.02),CutMixer((2*i)+18,1.0,1.0,0.0)],Group.tail(group),numChannels:2))); +}); + +{Out.ar(0,In.ar((2*n)+16,2))}.play(Group.tail(group)); + +s.sync; +}); + + + +//give it all chance to set up- can do without this, just being safe +//Task({ +//1.0.wait; +source.play(clock); +reccutters.do({arg val; val.play(clock);}); +//nil +//}).start; + + +)+
This is the same as Ringz , except that it has a constant gain at 0 dB instead of being constant skirt. +
It is a two pole resonant filter with zeroes at
z = ±1+ +
Based on K. Steiglitz, "A Note on Constant-Gain Digital Resonators", Computer Music Journal, vol 18, no. 4, pp. 8-10, Winter 1994.
in |
+ The input signal. |
freq |
+ Resonant frequency in Hertz. WARNING: due to the nature of its implementation frequency values close to 0 may cause glitches and/or extremely loud audio artifacts! |
bwr |
+ Bandwidth ratio (reciprocal of Q). rq = bandwidth / centerFreq. + The reciprocal of Q is used rather than Q because it saves a divide operation inside the unit generator. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
{ Resonz.ar(WhiteNoise.ar(0.5), 2000, 0.1) }.play + +// modulate frequency +{ Resonz.ar(WhiteNoise.ar(0.5), XLine.kr(1000,8000,10), 0.05) }.play + +// modulate bandwidth +{ Resonz.ar(WhiteNoise.ar(0.5), 2000, XLine.kr(1, 0.001, 8)) }.play + +// modulate bandwidth opposite direction +{ Resonz.ar(WhiteNoise.ar(0.5), 2000, XLine.kr(0.001, 1, 8)) }.play+
A Routine runs a Function and allows it to be suspended in the middle and be resumed again where it left off. This functionality is supported by the Routine's superclass Thread. Effectively, Routines can be used to implement co-routines as found in Scheme and some other languages. +
A Routine is started the first time -next is called, which will run the Function from the beginning. It is suspended when it "yields" (using Object: -yield within the Function), and then resumed using -next again. When the Function returns, the Routine is considered stopped, and calling -next will have no effect - unless the Routine is reset using -reset, which will rewind the Function to the beginning. You can stop a Routine before its Function returns using -stop. +
When a Routine is scheduled on a Clock (e.g. using -play), it will be started or resumed at the scheduled time. The value yielded by the Routine will be used as the time difference for rescheduling the Routine. (See -awake). +
Since Routine inherits from Thread, it has its own associated logical time, etc. When a Routine is started or resumed, it becomes the current thread. +
Routine also inherits from Stream, and thus shares its ability to be combined using math operations and "filtered".
Creates an instance of Routine, passing it the Function with code to run.
func |
+ A Function with code for the Thread to run. |
stackSize |
+ Call stack size (an Integer). |
a = Routine.new({ 1.yield; 2.yield; }); +a.next.postln; +a.next.postln; +a.next.postln;+
This method performs differently according to the Routine's state:
inval
argument.inval
argument as the return value of yield
.Since Routine inherits from Thread, it will become the current thread when it is started or resumed; i.e. thisThread used in the Routine Function will return the Routine. It will inherit the parent thread's logical time and clock (see Thread: -parent). +
Synonyms for next
are -value and -resume.
nil
, if the Routine has stopped.When a Routine is started by a call to this method (or one of its synonyms), the method's argument is passed on as the argument to the Routine Function:
Routine { arg inval; + inval.postln; +}.value("hello routine");+ +
After the Routine has yielded (it has been suspended at the point in its Function where yield
is called on an Object), a call to this method (or its synonyms) resumes executing the Function and the argument to this method becomes the return value of yield
. To access that value within the Function, you have to assign it to a variable - typically, the argument of the Function is reused:
( +r = Routine { arg inval; + inval.postln; + inval = 123.yield; + inval.postln; +} +) + +r.value("hello routine"); +r.value("goodbye routine");+ +
Typically, a Routine yields multiple times, and each time the result of the yield is reassigning to the argument of its Function.
( +r = Routine { arg inval; + inval.postln; // Post the value passed in when started. + 5.do { arg i; + inval = (i + 10).yield; + inval.postln; // Post the value passed in when resumed. + } +} +) +( +5.do { + r.value("hello routine").postln; // Post the value that the Routine yields. +} +)+
Equivalent to -next.
Equivalent to -next.
Equivalent to the Routine Function reaching its end or returning: after this, the Routine will never run again (the -next method has no effect and returns nil
), unless -reset is called.
Causes the Routine to start from the beginning next time -next is called.
If a Routine is stopped (its Function has returned or -stop has been called), it will never run again (the -next method has no effect and returns nil
), unless this method is called.
+
A Routine cannot reset itself, except by calling Object: -yieldAndReset. +
See also: Object: -yield, Object: -alwaysYield
In the SuperCollider application, a Routine can be played using a Clock, as can any Stream. every time the Routine yields, it should do so with a float, the clock will interpret that, usually pausing for that many seconds, and then resume the routine, passing it the clock's current time.
clock |
+ a Clock, TempoClock by default |
quant |
+ see the Quant helpfile |
using Object:idle within a routine, return values until this time is over. Time is measured relative to the thread's clock.
// for 6 seconds, return 200, then continue +( +r = Routine { + 199.yield; + 189.yield; + 200.idle(6); + 199.yield; + 189.yield; +}; + +fork { + loop { + r.value.postln; + 1.wait; + } +} +); + +// the value can also be a stream or a function +( +r = Routine { + 199.yield; + 189.yield; + Routine { 100.do { |i| i.yield } }.idle(6); + 199.yield; + 189.yield; +}; + +fork { + loop { + r.value.postln; + 1.wait; + } +} +);+
This method is called by a Clock on which the Routine was scheduled when its scheduling time is up. It calls -next, passing on the scheduling time in beats as an argument. The value returned by next
(the value yielded by the Routine) will in turn be returned by this method, thus determining the time which the Routine will be rescheduled for.
inBeats |
+ The scheduling time in beats. This is equal to the current logical time (Thread: -beats). |
inSeconds |
+ The scheduling time in seconds. This is equal to the current logical time (Thread: -seconds). |
inClock |
+ The clock which awoke the Routine. |
Routine inherits from Thread, which allows access to some of its state:
( +r = Routine { arg inval; + loop { + // thisThread refers to the routine. + postf("beats: % seconds: % time: % \n", + thisThread.beats, thisThread.seconds, Main.elapsedTime + ); + 1.0.yield; + + } +}.play; +) + +r.stop; +r.beats; +r.seconds; +r.clock;+
The elapsed beats (logical time) of the routine. The beats do not proceed when the routine is not playing.
The elapsed seconds (logical time) of the routine. The seconds do not proceed when the routine is not playing, it is the converted beat value.
The thread's clock. If it has not played, it is the SystemClock.
( +var r, outval; +r = Routine.new({ arg inval; + ("->inval was " ++ inval).postln; + inval = 1.yield; + ("->inval was " ++ inval).postln; + inval = 2.yield; + ("->inval was " ++ inval).postln; + inval = 99.yield; +}); + +outval = r.next('a'); +("<-outval was " ++ outval).postln; +outval = r.next('b'); +("<-outval was " ++ outval).postln; +r.reset; "reset".postln; +outval = r.next('c'); +("<-outval was " ++ outval).postln; +outval = r.next('d'); +("<-outval was " ++ outval).postln; +outval = r.next('e'); +("<-outval was " ++ outval).postln; +outval = r.next('f'); +("<-outval was " ++ outval).postln; +)+
// wait + +( +var r; +r = Routine { + 10.do({ arg a; + a.postln; + // Often you might see Wait being used to pause a routine + // This waits for one second between each number + 1.wait; + }); + // Wait half second before saying we're done + 0.5.wait; + "done".postln; +}.play; +)+
// waitUntil + +( +var r; +r = Routine { + var times = { rrand(1.0, 10.0) }.dup(10) + thisThread.beats; + times = times.sort; + times.do({ arg a; + waitUntil(a); + a.postln; + }); + // Wait half second before saying we're done + 0.5.wait; + "done".postln; +}.play; +)+
// Using Routine to set button states on the fly. +( +var update, w, b; +w = SCWindow.new("State Window", Rect(150,SCWindow.screenBounds.height-140,380,60)); + +// a convenient way to set the button label +update = { + |but, string| but.states = [[string.asString, Color.black, Color.red]]; + but.refresh; +}; + +b = SCButton(w, Rect(10,10,360,40)); +b.font_(Font("Impact", 24)); + +update.value(b, "there is only one state"); + +// if an action should do something different each time it is called, a routine is the +// right thing to use. This is better than creating variables outside and setting them +// from the action function to keep state from one action to the next + +b.action_(Routine { |butt| + rrand(15, 45).do { |i| + update.value(butt, "%. there is still only 1 state".format(i + 2)); + 0.yield; // stop here + }; + w.close; +}); + +w.front; +)+
// drawing in a window dynamically with Pen +( +var w, much = 0.02, string, synth; + +w = Window.new("swing", Rect(100, 100, 300, 500)).front; +w.view.background_(Color.new255(153, 255, 102).vary); + +string = "swing ".dup(24).join; + +w.drawFunc = Routine { + var i = 0; + var size = 40; + var func = { |i, j| sin(i * 0.07 + (j * 0.0023) + 1.5pi) * much + 1 }; + var scale; + Pen.font = Font("Helvetica-Bold", 40); + loop { + i = i + 1; + string.do { |char, j| + + scale = func.value(i, j).dup(6); + + Pen.fillColor = Color.new255(0, 120, 120).vary; + Pen.matrix = scale * #[1, 0, 0, 1, 1, 0]; + Pen.stringAtPoint(char.asString, + ((size * (j % 9)) - 10) @ (size * (j div: 9)) + ); + }; + 0.yield // stop here, return something unimportant + } +}; + +fork { while { w.isClosed.not } { defer { w.refresh }; 0.04.wait; } }; + +w.front; + +)+
SFP plays long sound files from disk. It is not a sample player, it is to be used for playing recordings and long sound files.
( + +SFP.getNew({ arg v; + Sheet({ arg layout; + v.topGui(layout); + }) +}) + +) + + +( +// no path supplied +// click on the path (which is nil) to browse for a file +SFP.new.gui +)+ +
path |
+ Paths should be either fully specified or if a relative path is used it will be relative to the classvar : + AbstractSFP.dir + which by default is "~/SoundFiles/" + so test.aiff will be determined internally by using the standardizePath method: + SFP.standardizePath("test.aiff") + for me this would result in : + /Users/cruxxial/SoundFiles/test.aiff + You may set the AbstractSFP.dir = "wherever you want it" in your startup.rtf file. + or you may simply fully specify your paths. |
tempo | |
firstBeatIsAtFrame |
receivingFunction |
sfilePath |
sfilePath |
startAt | |
endAt | |
group | |
bundle | |
parentSegmentBuffers |
t |
An implementation of the sines+noise model first described by Xavier Serra in his 1989 PhD thesis; an input sound is analysed in terms of sinusoidal components by a peak tracking phase vocoder. The error between the sinusoidal reconstruction and the original signal (the residual) is then modeled by a noise model of filtered white noise. The sines part and the noise part are separately resynthesised, allowing independent transformations. +
For technical details see: +
Xavier Serra and Julius O. Smith (1990) "Spectral Modeling Synthesis: A Sound Analysis/Synthesis System Based on a Deterministic plus Stochastic Decomposition". Computer Music Journal 14(4): 12--24
input |
+ Audio rate input to be analysed |
maxpeaks |
+ Absolute maximum number of allowed peaks to be detected in the spectrum |
currentpeaks |
+ Current number of allowed peaks to be detected in the spectrum |
tolerance |
+ Search area for matching peaks; within tolerance spectral bins |
noisefloor |
+ Minimum magnitude for a candidate peak (measured as spectral magnitude) |
freqmult |
+ Resynthesis parameter to change frequency; currently causes a gross multiplication of frequency of all sinusoidal components |
freqadd |
+ Resynthesis parameter to change frequency; currently causes a gross addition of a frequency to all sinusoidal components |
formantpreserve |
+ Even if changing the frequencies of sinusoidal partial tracks, re-impose the original magnitude spectrum so as to keep the formants (spectral envelope preservation). 0 is off, otherwise on (there is a small performance hit). |
useifft |
+ Use IFFT based resynthesis, which is lower quality, but substantially more efficient |
ampmult |
+ amplitude multiplier for internal compensation for window power loss within algorithm. Usually leave as default of 1.0. |
graphicsbufnum |
+ Will fill a user provided buffer with sines + noise data; the buffer must be size 1 + 513 + 5*(maxsines). The first entry will be the number of sines active for that polled frame. Default for this argument is -1, meaning do not write any status data. See the example with live plotting at the base of this help file. |
SQPushFXAmp2 is intended to be used in the ampfunc parameter of CutMixer. It randomly drops repeats according to a table of probabilities depending on the current position in the bar. It is apparently inspired by Squarepusher. +
The alias CutAmp1 is available for backward compatibility.
ampchance |
+ Controls the overall probability that a given repeat will be dropped. |
amptemplate |
+ An array of 16 float values from 0 to 1, each value corresponding to one sixteenth of the bar. The decision to drop a repeat is done by the following process: look up the current time in the table, multiply that probability by ampchance, and then drop the repeat with that probability. |
~buf = BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff", 8); + +BBCut2(CutGroup([CutBuf3(~buf, 0.3), CutMixer(0, 1, SQPushFXAmp2(0.5))]), SQPusher1()).play(2.5);+
This cut procedure is derived from some analysis of the manic breaks work of Squarepusher, aka Tom Jenkinson. His innovation was to push the tempo range to around 200 bpm, and to create constantly varying beats with highly musical jazz inflected drum programming. +
His original work was with sampler/drum machine+sequencer but he uses many effects units and nowadays a laptop in his live shows. To my knowledge, all his programming is by hand, without algorithm. This is an automated routine inspired by his style, which is obviously my take on things and in no way to represent the great variety of great compositions Tom has created. The routine was inspired by transcribing drum patterns from Alive in Japan (disc 2, do you know squarepusher, warpcd97 2002). +
Note that the original Squarepusher material is created by specifying all the individual drum hits- this routine simulates that sequencing in one single line by cutting up a breakbeat sample, so it works on a sense of rhythmic aggregate, not an analysis of relative positions of kick/snare/hat.
Create a SQPusher1 object with the given parameters. +
Note that this cut procedure always works in 4/4 with one bar phrases.
activity |
+ Chance of semiquavers rather than quavers. |
fillfreq |
+ There are two types of bar, normal based on activity from the above parameter, and fills based on musical motifs lifted from Squarepusher tracks. The fills occur every fourth bar by default, but change their frequency with this parameter. |
fillscramble |
+ chance of a fill being scrambled, rather than played back exactly as stored. |
sqweights |
+ The chance of a semiquaver bias at each quaver of the bar. Used in combination with the activity parameter to make cut sequences for normal non fill bars. |
bpsd |
+ beats per sub division. Sets a primitive cut size resolution for choose offset messages. |
Use your rawest noisest breaks to hear this properly. You won't have all the breaks I use below. Start with something that is running quick so repitching doesn't cause too much chipmunkiness, or double the beats per sample value in BBCutBuffer. +
Otherwise you'll need to use CutBuf3 to preserve the original pitch and reslice.
( //defaults +var sf; + +Routine.run({ +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break2.aiff",4); + +s.sync; + +//3.33bps= 200 bpm +BBCut2(CutBuf1(sf),SQPusher1.new).play(3.33); + +}); +) + +( //fast cuts, half speed sample +var sf; + +Routine.run({ +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",16); + +s.sync; + +//3.33bps= 200 bpm +BBCut2(CutBuf1(sf),SQPusher1.new).play(3.33); + +}); +) + + +//UI controls +( +var sf; +var w,offset, activity; + +w = SCWindow("panel", Rect.new(200,200,230,130)); +offset=DDSlider( w, Rect.new(0,0,200,40), "offset", 0.0, 1.0, 'linear', 0.01, 0.0); +activity=DDSlider( w, Rect.new(0,50,200,40), "activity", 0.0, 1.0, 'linear', 0.01, 0.0); + +w.front; + +Routine.run({ +sf= BBCutBuffer("sounds/Sounds/BOGDAN/dillbeat1",8); + +s.sync; + +//3.33bps= 200 bpm +BBCut2(CutBuf1(sf, offset),SQPusher1(activity)).play(3.33); + +}); + + +) + + +//preserved original pitch via CutBuf3 +( +var sf; + +Routine.run({ +sf= BBCutBuffer("sounds/Sounds/BOGDAN/dillbeat1",8); + +s.sync; + +//3.33bps= 200 bpm +BBCut2(CutBuf3(sf),SQPusher1.new).play(3.33); +}); +) + + + + +( //two at once- sounds great on my machine at least! +var sf, clock; + +clock= ExternalClock(3.4).play; + +Routine.run({ +sf=BBCutBuffer.array(["sounds/Sounds/bogdan/dillbeat1","sounds/Sounds/bogdanunused/bogdanbeat1"],[8,8]); + +s.sync; + +BBCut2(CutBuf1(sf[0],0.3),SQPusher1(0.4)).play(clock); +BBCut2(CutBuf1(sf[1],0.45),SQPusher1(0.3)).play(clock); + +}); + +) + + +( //two run off same routine +var sf, clock; + +clock= ExternalClock(3.4).play; + +Routine.run({ +sf=BBCutBuffer.array(["sounds/Sounds/bogdan/dillbeat1","sounds/Sounds/bogdanunused/bogdanbeat1"],[8,8]); + +s.sync; +BBCut2([CutBuf1(sf[0],0.3),CutBuf1(sf[1],0.45)],SQPusher1(0.4)).play(clock); + +}); + +) + + + +( //virtuoso rhythms +var sf; + +Routine.run({ +sf=BBCutBuffer("sounds/a11wlk01.wav",8); + +s.sync; + +BBCut2( +[[CutBuf2(sf,0.2,0.7,0.3), CutMixer(0,1.0,1.0,-0.5)], +[CutBuf2(sf,0.5,0.8,0.1), CutMixer(0,1.0,1.0,0.5)]], +SQPusher1(0.5,1, 1.0) +).play(4.5); + +}); +) + + +( //alternate normal and fill bars, always scramble fills, sometimes silent +var sf; + +Routine.run({ +sf= BBCutBuffer("sounds/Sounds/BOGDAN/dillbeat1",8); + +s.sync; + +BBCut2([CutBuf2(sf, 0.3, dutycycle: 0.7), CutMixer(0,1.0,{if(0.2.coin,{0},{1})},{1.0.rand2})],SQPusher1(0.3,2, 1.0)).play(3.5); +}); +) + + + +( //SQPusher FX +var sf; + +Routine.run({ +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break2.aiff",4); + +s.sync; +BBCut2([CutBuf2(sf,0.3,SQPushFXPitch2.new(0.96,pbchance:0.8)),CutMixer(0,1.0,SQPushFXAmp2.new(0.15),0.0)],SQPusher1(0.4, 3, 0.4)).play(3.4); + +}); +) + + +( //SQPusher fx at original pitch of sample using BBCSKnownOffsets +var sf; + +Routine.run({ +sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break2.aiff",4); + +s.sync; + +BBCut2([CutBuf3(sf,0.3,pbsfunc:SQPushFXPitch2.new(0.96,pbchance:0.8)),CutMixer(0,1.0,SQPushFXAmp2.new(0.15),0.0)],SQPusher1(0.4, 3, 0.4)).play(3.4); + +}); + +)+
Returns the current sample rate of the server.
Band limited sawtooth wave generator.
freq |
+ Frequency in Hertz. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
If in > hi
, output 1. If in < lo
, output 0. Otherwise, repeat the last sample of output, assumed to be 0 at initialization. In sclang-flavored pseudocode:
out[i] = if(in[i] < lo[i]) { + 0 +} { + if(in[i] > hi[i]) { + 1 + } { + out[i-1] + } +};+
in |
+ Signal to be tested. |
lo |
+ Low threshold. |
hi |
+ High threshold. |
s.boot; + +{ Schmidt.kr(SinOsc.kr(1, 0, 0.2), -0.15, 0.15)}.scope; // see the trigger + +{ Schmidt.kr(MouseX.kr(0, 1), 0.2, 0.8)}.scope; // try it with the cursor + +// threshold octave jumps +( +{ + var in = LFNoise1.kr(3); + var octave = Schmidt.kr(in, -0.15, 0.15) + 1; + SinOsc.ar(in * 200 + 500 * octave, 0, 0.1) +}.scope; +)+
On receiving a trigger (a non-positive to positive transition), send a trigger message from the server back to the client. +
The trigger message sent back to the client is this:
/tr | A trigger message. |
int: | Node ID. |
int: | Trigger ID. |
float: | Trigger value. |
This command is the mechanism that synths can use to trigger events in clients. The node ID is the node that is sending the trigger. The trigger ID and value are determined by inputs to the SendTrig unit generator which is the originator of this message.
in |
+ The trigger. |
id |
+ An integer that will be passed with the trigger message. This is useful if you have more than one SendTrig in a SynthDef. |
value |
+ A UGen or float that will be polled at the time of trigger, and its value passed with the trigger message. |
s.boot; + +( +SynthDef("help-SendTrig",{ + SendTrig.kr(Dust.kr(1.0),0,0.9); +}).add; + +// register to receive this message +o = OSCFunc({ arg msg, time; + [time, msg].postln; +},'/tr', s.addr); +) + +Synth("help-SendTrig"); + +o.free;+ +
Sensory Dissonance model, measuring roughness between pairs of prominent spectral peaks. Follows the algorithm in William A. Sethares (1998) Consonance-Based Spectral Mappings. CMJ 22(1): 56-72. +
In usual use, you probably won't care about the other arguments; just pass an FFT in, assuming FFT size 2048 by default.
fft |
+ input fft chain, that is, from an FFT UGen |
maxpeaks |
+ Maximum number of spectral peaks detected; cannot be modulated, initialisation only. |
peakthreshold |
+ Minimum spectral power detection threshold for a peak |
norm |
+ Normalisation factor. Calculated for you in the UGen class if you don't provide one, but you can experiment here. In combination with the next argument and maxpeaks, allows you to have alternative range outputs if you so desire. |
clamp |
+ Clamps very high dissonances, in default mode will end up with sensory dissonance measure in range 0.0 to 1.0 |
( +{ + +var in, fft, dissonance; + +//in = SinOsc.ar(MouseX.kr(100,1000),0,0.1); +//in = Mix(SinOsc.ar([440,MouseX.kr(440,880)],0,0.1)); +in= SoundIn.ar; + +fft = FFT(LocalBuf(2048), in); + +dissonance=SensoryDissonance.kr(fft); + +dissonance.poll; + +Out.ar(0,Pan2.ar(0.1*Blip.ar(100,(dissonance.sqrt)*200))); +}.play +) + + + +//different fftsize, max num peaks, own normalisation, avoid clamping by setting high value (more CPU cost) +( +{ + +var in, fft, dissonance; + +//in = SinOsc.ar(MouseX.kr(100,1000),0,0.1); +//in = Mix(SinOsc.ar([440,MouseX.kr(440,880)],0,0.1)); +in= SoundIn.ar; + +fft = FFT(LocalBuf(4096), in); + +dissonance=SensoryDissonance.kr(fft,500,1.0,1.0,999999); + +dissonance.poll; + +Out.ar(0,SinOsc.ar(dissonance*0.1,0,0.1)); +}.play +)+ +
SequenceableCollection is a subclass of Collection whose elements can be indexed by an Integer. It has many useful subclasses; Array and List are amongst the most commonly used.
Creates a Collection of the given size, the elements of which are determined by evaluation the given function. The function is passed the index as an argument.
Array.fill(4, { arg i; i * 2 }); +Bag.fill(14, { arg i; i.rand });+
size |
+ The size of the collection which is returned. If nil, it returns an empty collection. If an array of sizes is given, the resulting collection has the appropriate dimensions (see: *fillND). Array.fill([2, 2, 3], { arg i, j, k; i * 100 + (j * 10) + k });+ |
function |
+ The function which is called for each new element - the index is passed in as a first argument. The function be anything that responds to the message "value". Array.fill(10, { arg i; 2 ** i }); +Array.fill(10, Pxrand([0, 1, 2], inf).iter); +Array.fill(10, 7); // an object that doesn't respond with a new value is just repeatedly added.+ |
Fill a SequenceableCollection with an arithmetic series.
Array.series(5, 10, 2);+
Fill a SequenceableCollection with a geometric series.
Array.geom(5, 1, 3);+
Fill a SequenceableCollection with a fibonacci series.
Array.fib(5); +Array.fib(5, 2, 32); // start from 32 with step 2.+ +
size |
+ the number of values in the collection |
a |
+ the starting step value |
b |
+ the starting value |
Fill a SequenceableCollection with random values in the range minVal to maxVal.
Array.rand(8, 1, 100);+
Fill a SequenceableCollection with random values in the range -val to +val.
Array.rand2(8, 100);+
Fill a SequenceableCollection with random values in the range minVal to maxVal with a linear distribution.
Array.linrand(8, 1, 100);+
Fill a SequenceableCollection with random values in the range minVal to maxVal with exponential distribution.
Array.exprand(8, 1, 100);+
Fill a SequenceableCollection with the interpolated values between the start and end values.
Array.interpolation(5, 3.2, 20.5);+
synonym for ArrayedCollection: -clipAt.
[3, 4, 5]|@|6;+
synonym for ArrayedCollection: -wrapAt.
[3, 4, 5]@@6; +[3, 4, 5]@@ -1; +[3, 4, 5]@@[6, 8]+
synonym for ArrayedCollection: -foldAt.
[3, 4, 5]@|@[6, 8];+
Return the first element of the collection.
[3, 4, 5].first;+
Return the last element of the collection.
[3, 4, 5].last;+
Place item at the first / last index in the collection. Note that if the collection is empty (and therefore has no indexed slots) the item will not be added.
[3, 4, 5].putFirst(100); +[3, 4, 5].putLast(100);+
Return the index of an item in the collection, or nil if not found.
[3, 4, 100, 5].indexOf(100); +[3, 4, \foo, \bar].indexOf(\foo);+
Return the index of something in the collection that equals the item, or nil if not found.
[3, 4, "foo", "bar"].indexOfEqual("foo");+
Return an array of indices of things in the collection that equal the item, or nil if not found.
y = [7, 8, 7, 6, 5, 6, 7, 6, 7, 8, 9]; +y.indicesOfEqual(7); +y.indicesOfEqual(5);+
Return the first index containing an item which is greater than item.
y = List[ 10, 5, 77, 55, 12, 123]; +y.indexOfGreaterThan(70);+
Return a new collection of same type as receiver which consists of all indices of those elements of the receiver for which function answers true
. The function is passed two arguments, the item and an integer index.
#[a, b, c, g, h, h, j, h].selectIndices({|item, i| item === \h})+ +
If you want to control what type of collection is returned, use -selectIndicesAs
Return a new collection of type class which consists of all indices of those elements of the receiver for which function answers true
. The function is passed two arguments, the item and an integer index.
#[a, b, c, g, h, h, j, h].selectIndicesAs({|item, i| item === \h}, Set)+
Return a new collection of same type as receiver which consists of all indices of those elements of the receiver for which function answers false
. The function is passed two arguments, the item and an integer index.
#[a, b, c, g, h, h, j, h].rejectIndices({|item, i| item === \h})+ +
If you want to control what type of collection is returned, use -rejectIndicesAs
Return a new collection of type class which consists of all indices of those elements of the receiver for which function answers false
. The function is passed two arguments, the item and an integer index.
#[a, b, c, g, h, h, j, h].rejectIndicesAs({|item, i| item === \h}, Set)+
Answer the index of the maximum of the results of function evaluated for each item in the receiver. The function is passed two arguments, the item and an integer index. If function is nil, then answer the maximum of all items in the receiver.
List[1, 2, 3, 4].maxIndex({ arg item, i; item + 10 }); +[3.2, 12.2, 13, 0.4].maxIndex;+
Answer the index of the minimum of the results of function evaluated for each item in the receiver. The function is passed two arguments, the item and an integer index. If function is nil, then answer the minimum of all items in the receiver.
List[1, 2, 3, 4].minIndex({ arg item, i; item + 10 }); +List[3.2, 12.2, 13, 0.4].minIndex;+
If the sublist exists in the receiver (in the specified order), at an offset greater than or equal to the initial offset, then return the starting index.
y = [7, 8, 7, 6, 5, 6, 7, 6, 7, 8, 9]; +y.find([7, 6, 5]);+
Similar to -find but returns an array of all the indices at which the sequence is found.
y = [7, 8, 7, 6, 5, 6, 7, 6, 7, 8, 9]; +y.findAll([7, 6]);+
Returns the closest index of the value in the collection (collection must be sorted).
[2, 3, 5, 6].indexIn(5.2);+
Returns a linearly interpolated float index for the value (collection must be sorted). Inverse operation is -blendAt.
x = [2, 3, 5, 6].indexInBetween(5.2); +[2, 3, 5, 6].blendAt(x);+
Returns a linearly interpolated value between the two closest indices. Inverse operation is -indexInBetween.
x = [2, 5, 6].blendAt(0.4);+
Return a new SequenceableCollection which is a copy of the indexed slots of the receiver from start to end. If end < start, an empty collection is returned.
( +var y, z; +z = [1, 2, 3, 4, 5]; +y = z.copyRange(1, 3); +z.postln; +y.postln; +)+ +
x.copyRange(a, b)
is not equivalent to x[a..b]
. The latter compiles to ArrayedCollection: -copySeries, which has different behavior when end < start.Return a new SequenceableCollection which is a copy of the indexed slots of the receiver from start to the end of the collection. x.copyToEnd(a)
can also be written as x[a..]
Return a new SequenceableCollection which is a copy of the indexed slots of the receiver from the start of the collection to end. x.copyFromStart(a)
can also be written as x[..a]
Remove item from collection.
Remove and return item from collection. The last item in the collection will move to occupy the vacated slot (and the collection size decreases by one). See also takeAt, defined for ArrayedCollection: -takeAt.
a = [11, 12, 13, 14, 15]; +a.take(12); +a;+
Retrieve an element from a given index (like SequenceableCollection: -at). This method is also implemented in Object, so that you can use it in situations where you don't want to know if the receiver is a collection or not. See also: SequenceableCollection: -instill
index |
+ The index at which to look for an element |
default |
+ If index exceeds collection size, or receiver is nil, return this instead ( +a = [10, 20, 30]; +b = [10, 20]; +c = 7; +); + + // obtain third element, if outside bounds return 1 +a.obtain(2, 1); +b.obtain(2, 1); +c.obtain(2, 1);+ |
Put an element at a given index (like SequenceableCollection: -put). This method is also implemented in Object, so that you can use it in situations where you don't want to know if the receiver is a collection or not. It will always return a new collection. See also: SequenceableCollection: -obtain
index |
+ The index at which to put the item |
item |
+ The object to put into the new collection |
default |
+ If the index exceeds the current collection's size, extend the collection with this element ( +a = [10, 20, 30, 40]; +b = [10, 20]; +c = 7; +); + +a.instill(2, -1); +b.instill(2, -1); +c.instill(2, -1); +// providing a default value +c.instill(2, -1, 0);+ |
Keep the first n items of the array. If n is negative, keep the last -n items.
a = [1, 2, 3, 4, 5]; +a.keep(3); +a.keep(-3);+
Drop the first n items of the array. If n is negative, drop the last -n items.
a = [1, 2, 3, 4, 5]; +a.drop(3); +a.drop(-3);+
Returns a String formed by connecting all the elements of the receiver, with joiner inbetween. See also String: -split as the complementary operation.
["m", "ss", "ss", "pp", ""].join("i").postcs; +"mississippi".split("i").postcs;+
Returns a collection from which all nesting has been flattened.
[[1, 2, 3], [[4, 5], [[6]]]].flat; // [ 1, 2, 3, 4, 5, 6 ] +[1, 2, [3, 4, [5, 6, [7, 8, [9, 0]]]]].flat; // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ]+
Returns a collection from which numLevels of nesting has been flattened.
numLevels |
+ Specifies how many levels downward (inward) to flatten. Zero returns the original. a = [1, 2, [3, 4, [5, 6, [7, 8, [9, 0]]]]]; +a.flatten(1); // [ 1, 2, [ 3, 4, [ 5, 6, [ 7, 8, [ 9, 0 ] ] ] ] ] +a.flatten(2); // [ 1, 2, 3, 4, 5, 6, [ 7, 8, [ 9, 0 ] ] ] +a.flatten(3); // [ 1, 2, 3, 4, 5, 6, 7, 8, [ 9, 0 ] ] +a.flatten(4); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ]+ |
A symmetric version of -flatten. For a negative numLevels
, it flattens starting from the innermost arrays.
numLevels |
+ Specifies how many levels downward (inward) or upward (outward) to flatten. a = [1, 2, [3, 4, [5, 6, [7, 8, [9, 0]]]]]; +a.flatten2(4); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ] +a.flatten2(3); // [ 1, 2, 3, 4, 5, 6, 7, 8, [ 9, 0 ] ] +a.flatten2(2); // [ 1, 2, 3, 4, 5, 6, [ 7, 8, [ 9, 0 ] ] ] +a.flatten2(1); // [ 1, 2, 3, 4, [ 5, 6, [ 7, 8, [ 9, 0 ] ] ] ] +a.flatten2(0); // [ 1, 2, [ 3, 4, [ 5, 6, [ 7, 8, [ 9, 0 ] ] ] ] ] +a.flatten2(-1); // [ 1, 2, [ 3, 4, [ 5, 6, [ 7, 8, 9, 0 ] ] ] ] +a.flatten2(-2); // [ 1, 2, [ 3, 4, [ 5, 6, 7, 8, 9, 0 ] ] ] +a.flatten2(-3); // [ 1, 2, [ 3, 4, 5, 6, 7, 8, 9, 0 ] ] +a.flatten2(-4); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ]+ |
Flatten all subarrays deeper than level.
level |
+ Specifies from what level onward to flatten. level 0 is outermost, so flatBelow(0) is like flat. a = [1, 2, [3, 4, [5, 6, [7, 8, [9, 0]]]]]; +a.flatBelow(0); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ] +a.flatBelow(1); // [ 1, 2, [ 3, 4, 5, 6, 7, 8, 9, 0 ] ] +a.flatBelow(2); // [ 1, 2, [ 3, 4, [ 5, 6, 7, 8, 9, 0 ] ] ] + +// to set the level below which to flatten from the deepest level up, +// one can use coll.maxDepth. E.g. to flatten only the innermost level: +a.flatBelow( (a.maxDepth - 1) - 1); +// for lowest two levels: +a.flatBelow( (a.maxDepth - 1) - 2);+ |
Invert rows and columns in a two dimensional Collection (turn inside out). See also: Function.
[[1, 2, 3], [4, 5, 6]].flop; +[[1, 2, 3], [4, 5, 6], [7, 8]].flop; // shorter array wraps +[].flop; // result is always 2-d.+ +
Note that the innermost arrays are not copied:
a = [1, 2]; +x = [[[a, 5], [a, 10]], [[a, 50, 60]]].flop; +a[0] = pi; +x // pi is everywhere+
Flop with a user defined function. Can be used to collect over several collections in parallel.
[[1, 2, 3], [4, 5, 6]].flopWith(_+_); +[[1, 2, 3], 1, [7, 8]].flopWith{ |a,b,c| a+b+c }; // shorter array wraps + +// typical use case (pseudocode) +[synths, buffers].flopWith{ |a,b| a.set(\buf, b) }+ +
func |
+ A function taking as many arguments as elements in the array. |
Invert rows and columns in a an array of dimensional Collections (turn inside out), so that they all match up in size, but remain separated.
( +a = flopTogether( + [[1, 2, 3], [4, 5, 6, 7, 8]] * 100, + [[1, 2, 3], [4, 5, 6], [7, 8]], + [1000] +) +); + +a.collect(_.size); // sizes are the same +a.collect(_.shape) // shapes can be different+
Fold dimensions in a multi-dimensional Collection (turn inside out).
rank |
+ The depth (dimension) from which the array is inverted inside-out. [[1, 2, 3], [[41, 52], 5, 6]].flopDeep(2); +[[1, 2, 3], [[41, 52], 5, 6]].flopDeep(1); +[[1, 2, 3], [[41, 52], 5, 6]].flopDeep(0); +[[1, 2, 3], [[41, 52], 5, 6]].flopDeep; // without argument, flop from the deepest level + +[[[10, 100, 1000], 2, 3], [[41, 52], 5, 6]].flopDeep(2); // shorter array wraps +[].flopDeep(1); // result is always one dimension higher. +[[]].flopDeep(4);+ NOTE: Note that, just like in flop, the innermost arrays (deeper than rank) are not copied. a = [1, 2]; +x = [[[a, 5], [a, 10]], [[a, 50, 60]]].flopDeep(1); +a[0] = pi; +x // pi is everywhere+ |
Returns the maximum size of all subarrays at a certain depth (dimension)
rank |
+ The depth at which the size of the arrays is measured [[1, 2, 3], [[41, 52], 5, 6], 1, 2, 3].maxSizeAtDepth(2); +[[1, 2, 3], [[41, 52], 5, 6], 1, 2, 3].maxSizeAtDepth(1); +[[1, 2, 3], [[41, 52], 5, 6], 1, 2, 3].maxSizeAtDepth(0); +[].maxSizeAtDepth(0); +[[]].maxSizeAtDepth(0); +[[]].maxSizeAtDepth(1);+ |
Returns the maximum depth of all subarrays.
max |
+ Internally used only. [[1, 2, 3], [[41, 52], 5, 6], 1, 2, 3].maxDepth+ |
Returns true if the collection is an arithmetic series.
step |
+ Step size to look for. If none is given, any step size will match. [ 0, 1, 2, 3, 4, 5 ].isSeries; // true +[ 1.5, 2.5, 3.5, 4.5, 5.5, 6.5 ].isSeries; // true +[ 0, 1, 4, 5 ].isSeries; // false +[ 0, 3, 6, 9, 12, 15 ].isSeries; // true +[ 0, 3, 6, 9, 12, 15 ].isSeries(1); // false +[ 2 ] // true +[ ] // true (empty sequence)+ |
Returns a new Collection of the desired length, with values resampled evenly-spaced from the receiver without interpolation.
[1, 2, 3, 4].resamp0(12); +[1, 2, 3, 4].resamp0(2);+
Returns a new Collection of the desired length, with values resampled evenly-spaced from the receiver with linear interpolation.
[1, 2, 3, 4].resamp1(12); +[1, 2, 3, 4].resamp1(3);+
Choose an element from the collection at random.
[1, 2, 3, 4].choose;+
Choose an element from the collection at random using a list of probabilities or weights. The weights must sum to 1.0.
[1, 2, 3, 4].wchoose([0.1, 0.2, 0.3, 0.4]);+
Sort the contents of the collection using the comparison function argument. The function should take two elements as arguments and return true if the first argument should be sorted before the second argument. If the function is nil, the following default function is used. { arg a, b; a < b }
[6, 2, 1, 7, 5].sort; +[6, 2, 1, 7, 5].sort({ arg a, b; a > b }); // reverse sort+
Sort the contents of the collection using the key key, which is assumed to be found inside each element of the receiver.
( +a = [ + Dictionary[\a->5, \b->1, \c->62], + Dictionary[\a->2, \b->9, \c->65], + Dictionary[\a->8, \b->5, \c->68], + Dictionary[\a->1, \b->3, \c->61], + Dictionary[\a->6, \b->7, \c->63] +] +) +a.sortBy(\b); +a.sortBy(\c);+
Return an array of indices that would sort the collection into order. function is treated the same way as for the -sort method.
[6, 2, 1, 7, 5].order;+
Swap two elements in the collection at indices i and j.
Calls function for each subsequent pair of elements in the SequentialCollection. The function is passed the two elements and an index.
[1, 2, 3, 4, 5].pairsDo({ arg a, b; [a, b].postln; });+
Calls function for every adjacent pair of elements in the SequenceableCollection. The function is passed the two adjacent elements and an index.
[1, 2, 3, 4, 5].doAdjacentPairs({ arg a, b; [a, b].postln; });+
Separates the collection into sub-collections by calling the function for each adjacent pair of elements. If the function returns true, then a separation is made between the elements.
[1, 2, 3, 5, 6, 8, 10].separate({ arg a, b; (b - a) > 1 }).postcs;+
Separates the collection into sub-collections by separating every groupSize elements.
[1, 2, 3, 4, 5, 6, 7, 8].clump(3).postcs;+
Separates the collection into sub-collections by separating elements into groupings whose size is given by integers in the groupSizeList.
[1, 2, 3, 4, 5, 6, 7, 8].clumps([1, 2]).postcs;+
Separates the collection into sub-collections by randomly separating elements according to the given probability.
[1, 2, 3, 4, 5, 6, 7, 8].curdle(0.3).postcs;+
Returns a collection with the incremental sums of all elements.
[3, 4, 1, 1].integrate;+
Returns a collection with the pairwise difference between all elements.
[3, 4, 1, 1].differentiate;+
Applies the method named by operator to the first and second elements of the collection, and then applies the method to the result and to the third element of the collection, then applies the method to the result and to the fourth element of the collection, and so on, until the end of the array. +
If the collection contains only one element, it is returned as the result. If the collection is empty, returns nil
.
operator |
+ May be a Function (taking two or three arguments) or a Symbol (method selector). [3, 4, 5, 6].reduce('*'); // this is the same as [3, 4, 5, 6].product +[3, 4, 5, 6].reduce(\lcm); // Lowest common multiple of the whole set of numbers +["d", "e", (0..9), "h"].reduce('++'); // concatenation +[3, 4, 5, 6].reduce({ |a, b| sin(a) * sin(b) }); // product of sines+ |
adverb |
+ An optional adverb to be used together with the operator (see Adverbs for Binary Operators). If the operator is a functions, the adverb is passed as a third argument. // compare: +[1, 2] *.x [10, 20, 30] +[[1, 2], [10, 20, 30]].reduce('*', 'x') +[[1, 2], [10, 20, 30], [1000, 2000]].reduce('+', 'x') // but you can combine more+ |
Returns an integer resulting from interpreting the elements as digits to a given base (default 10). See also asDigits in Integer: asDigits for the complementary method.
[1, 0, 0, 0].convertDigits; +[1, 0, 0, 0].convertDigits(2); +[1, 0, 0, 0].convertDigits(3);+
Returns the count of array elements that are not equal in identical positions. http://en.wikipedia.org/wiki/Hamming_distance +
The collections are not wrapped - if one array is shorter than the other, the difference in size should be included in the count.
[0, 0, 0, 1, 1, 1, 0, 1, 0, 0].hammingDistance([0, 0, 1, 1, 0, 0, 0, 0, 1, 1]); +"SuperMan".hammingDistance("SuperCollider");+
All of the following messages send the message -performUnaryOp to the receiver with the unary message selector as an argument.
Creates a new collection of the results of applying the selector to all elements in the receiver.
[1, 2, 3, 4].neg; +[1, 2, 3, 4].reciprocal;+
All of the following messages send the message -performBinaryOp to the receiver with the binary message selector and the second operand as arguments.
Creates a new collection of the results of applying the selector with the operand to all elements in the receiver. If the operand is a collection then elements of that collection are paired with elements of the receiver.
([1, 2, 3, 4] * 10); +([1, 2, 3, 4] * [4, 5, 6, 7]);+
All of the following messages are performed on the elements of this collection, using Object: -multiChannelPerform. +
The result depends on the objects in the collection, but the main use case is for UGens. +
See also Multichannel Expansion
Calls this.multiChannelPerform(selector, *args)
where selector is the name of the message.
This method is called internally on inputs to UGens that take multidimensional arrays, like Klank and it allows proper multichannel expansion even in those cases. For SequenceableCollection, this returns the collection itself, assuming that it contains already a number of Refs. See Ref for the corresponding method implementation.
rank |
+ The depth at which the list is expanded. For instance the Klank spec has a rank of 2. For more examples, see SequenceableCollection: -flopDeep `([[[100, 200], 500], nil, [[[0.01, 0.3], 0.8]]]).multichannelExpandRef(2); +[`[[100, 200], nil, [0.2, 0.8]], `[[130, 202], nil, [0.2, 0.5]]].multichannelExpandRef(2);+ |
Convert a rhythm-list to durations.
supports a variation of Mikael Laurson's rhythm list RTM-notation.1 +
The method converts a collection of the form [beat-count, [rtm-list], repeats]
to a List of Floats. A negative integer within the rtm-list equates to a value tied over to the duration following. The method is recursive in that any subdivision within the rtm-list can itself be a nested convertRhythm collection (see example below). The repeats integer has a default value of 1.
+
If the divisions in the rtm-list are events, the event durations are interpreted as relative durations, and a list of events is returned.
// using numbers as score +[3, [1, 2, 1], 1].convertRhythm; // List[ 0.75, 1.5, 0.75 ] +[2, [1, 3, [1, [2, 1, 1, 1]], 1, 3], 1].convertRhythm; +[2, [1, [1, [2, 1, 1, 1]]], 1].convertRhythm; +[2, [1, [1, [2, 1, 1, 1]]], 2].convertRhythm; // repeat +[2, [1, [1, [2, 1, 1, -1]]], 2].convertRhythm; // negative value is tied over. + +// sound example +Pbind(\degree, Pseries(0, 1, inf), \dur, Pseq([2, [1, [1, [2, 1, 1, -1]]], 2].convertRhythm)).play;+
A Server object is a representation of a server application. It is used to control scsynth (or supernova) from the SuperCollider language. (See Server Guide, as well as Client vs Server for more details on the distinction.) It forwards OSC messages and has a number of allocators that keep track of IDs for nodes, buses, and buffers. +
The server application represented by a Server object might be running on the same machine as the sclang, or it may be running on a remote machine. +
Most of a Server's options are controlled through its instance of ServerOptions. See the ServerOptions helpfile for more detail. +
A server also holds an instance of a Recorder (for recording output into a file) and a Volume (main output level).
s.boot
The server application may be in three different states: running, not running, or unresponsive (for example while loading large files from disk). + +
Some insights about common Server issues can be found on the FAQ Server Issues
Create a new Server instance.
name |
+ a symbol; each Server object is stored in one global classvariable under its name. |
addr |
+ an optional instance of NetAddr, providing host and port. The default is the localhost address using port 57110; the same as the local server. |
options |
+ an optional instance of ServerOptions. If |
clientID |
+ an integer. In multi-client situations, every client can be given separate ranges for Nodes, Buffers, or Busses. In normal usage, the server will supply an ID automatically when a client registers for the notifications so you should not need to supply one here. N.B. In multi-client situations, you will need to set the ServerOptions: -maxLogins to at least the number of clients you wish to allow. This must be the same in the Server instances on every client. |
Create a new Server instance corresponding to a server app running on a separate machine. This method assumes the remote app has been booted and starts listening immediately. You should not call -boot on an instance created using this method. Server Guide and Multi-client Setups contain further information on this and multiclient usage.
name |
+ a symbol; each Server object is stored in one global classvariable under its name. |
addr |
+ an optional instance of NetAddr, providing IP address of the remote machine and port the app is listening on. |
options |
+ an optional instance of ServerOptions. If NOTE: To enable remote connections you will need to change the option ServerOptions: -bindAddress as the default value only allows connections from the local machine. s.options.bindAddress = "0.0.0.0" will allow connections from any address. |
clientID |
+ an integer. In multi-client situations, every client can be given separate ranges for Nodes, Buffers, or Busses. In normal usage, the server will supply an ID automatically when a client registers for the notifications so you should not need to supply one here. N.B. In multi-client situations, you will need to set the ServerOptions: -maxLogins to at least the number of clients you wish to allow. This must be the same in the Server instances on every client. + |
get/set the local server, stored in classvar local
(created already on initClass)
get/set the internal server, stored in classvar internal
(created already on initClass) See: Server Guide
Get or set the default server. By default this is the local server (see above).
Setting this will also assign it to the Interpreter variable 's'. +
a Set containing all servers. +
a Set containing all running servers, according to the definition of -serverRunning. +
a Set containing all booted servers, according to the definition of -hasBooted.
An IdentityDictionary of all servers listed by their name +
quit all registered servers
kill all the processes called "scsynth" and "supernova" in the system
free all nodes in all registered servers
try to free all nodes in all registered servers, even if the server seems not to be running
If kept true (default), when the default server is changed, also the interpreter variable s is changed. +
Switches the server program to supernova. Check ParGroup how to make use of multicore hardware with the supernova server.
Switches the server program to scsynth. This is the default server.
send an OSC-message to the server.
send an OSC-bundle to the server.
Since the network may have irregular performance, time allows for the bundle to be evaluated at a specified point in the future. Thus all messages are synchronous relative to each other, but delayed by a constant offset. If such a bundle arrives late, the server replies with a late message but still evaluates it. +
as sendMsg, but takes an array as argument.
as sendBundle, but takes an array as argument.
This allows you to collect messages in an array and then send them. +
send a synthDef to the server that was written in a local directory
load a synthDef that resides in the remote directory
load all the SynthDefs in the directory dir.
dir |
+ a String which is a valid path. |
completionMsg |
get a unique nodeID.
get a permanent node ID. This node ID is in a reserved range and will be held until you explicitly free it.
free a permanent node ID for later reuse.
this can be used within a Routine to wait for a server reply
Evaluate "onComplete" as soon as the server has booted. This method will boot the server for you if it is not already running or booting. If the server is already running, "onComplete" is executed immediately.
onComplete |
+ A function to evaluate after the server has booted successfully. |
limit |
+ The number of times to check for a successful boot. (5 times/sec) |
onFailure |
+ A function to evaluate after the server fails to boot. If onFailure is not given, an error message is posted. Providing a function suppresses the error message. If you want to supply a function and print the normal error message, make sure that your function returns "false," e.g. |
Evaluate "onComplete" as soon as the server has booted. This method assumes the server is being booted explicitly through a separate boot
call. If the server is already running, "onComplete" is executed immediately.
onComplete |
+ A function to evaluate after the server has booted successfully. |
limit |
+ The number of times to check for a successful boot. |
onFailure |
+ A function to evaluate after the server fails to boot. If onFailure is not given, an error message is posted. Providing a function suppresses the error message. If you want to supply a function and print the normal error message, make sure that your function returns "false," e.g. |
boot the remote server, create new allocators.
startAliveThread |
+ If true, start a Routine to send a /status message to the server every so often. The interval between the messages is set by |
recover |
+ If true, create a new node ID allocator for the server, but use the old buffer and bus allocators. This is useful if the server process did not actually stop. In normal use, the default value "false" should be used. |
onFailure |
+ In this method, the onFailure argument is for internal use only. If you wish to take specific actions when the server boots or fails to boot, it is recommended to use -waitForBoot or -doWhenBooted. |
You cannot boot a server app on a remote machine, but you can initialize the allocators by calling this message.
quit the server application
onComplete |
+ A function that is called when quit has completed. |
onFailure |
+ A function that is called when quit has failed. |
watchShutDown |
+ a boolean to tell the server whether to watch status during shutdown. |
quit and restart the server application
func |
+ a function that is called between quit and (re-)boot. |
onFailure |
+ A function that is called when quit has failed. |
free all nodes in this server
query the server status
The server sends notifications, for example, if a node was created, a 'tr' message from a SendTrig, or a /done action. If flag
is set to false, these messages will not be sent to this client. The default is true. If true the server will respond with a clientID (scsynth only) which can be useful in multi-client situations. If this is different from any previously received ID new allocators will be created. See Local vs. Remote Servers, Multi-client Configurations for more information.
measure the time between server and client, which may vary. the func
is evaluated after n
number of times and is passed the resulting maximum.
Get or set this Server's ServerOptions object. Changes to options only take effect when the server is rebooted.
Return the default group on this Server for this client ID. This will be the same object as server.defaultGroups[server.clientID]
.
Return an array mapping client IDs to their associated default groups as Group objects.
Get an instance of Volume that runs after the default group, or sets the Volume of the Server's output to level. Level is in db.
mute the server's output. This can also be toggled from the Server window with the 'm' key.
unmute the server. This can also be toggled from the Server window with the 'm' key.
Move the nodes in nodeList
to the location specified by target
and addAction
, placing them there in the order indicated by nodeList.
Any nodes which have already been freed will be skipped. Passing nil for target and addAction will result in the location being the head of the default group. +
Return a Bus object that represents the input audio bus.
Return a Bus object that represents the output audio bus.
code |
NOTE: /status messages won't be posted, when dumping is enabled |
Post a representation of this Server's current node tree to the post window. See -plotTree for a graphical variant.
Very helpful for debugging. For local servers, this uses g_dumpTree and for remote g_queryTree. See Group and Server Command Reference for more info. +
Get peak and average CPU usage.
The current latency of the server. See Scheduling and Server timing for details.
An integer representing the nominal sample rate of the server; in other words, the sample rate that was requested of the server when it was booted.
A floating-point number representing the current hardware sample rate, which may drift.
Get number of running Synths.
Get the number of Groups.
Get the number of running UGens.
Get number of loaded SynthDefinitions.
Get process ID of the running server (if not internal).
The network address of the server as a NetAddr.
If known, the maximum number of clients allowed on the server. Otherwise, the value of ServerOptions: -maxLogins, which is what will be requested after the server boots. This number is not guaranteed to be correct until -serverRunning is true
.
The getter returns the client ID of this client on the remote process. nil
until the server is running.
+
The setter attempts to set the client ID of this client for the remote server process. Fails on invalid input or if the server is running. Valid inputs are in the range [0..(this.maxNumClients-1)]
.
Returns true if a ServerShmInterface is available. See also Bus: Synchronous control bus methods. The shared memory interface is initialized after first server boot.
true
if the server is booting, false
otherwise.
true
if the server has booted. The server is not guaranteed to have a correct clientID, nor is it guaranteed that actions in ServerTree will have run yet.
true
only if the server is fully ready. A server is fully ready once it has booted, received a reply to a /notify
command, been given a client ID, and after the ServerTree has run.
true
if the server is unresponsive (specifically, if it has failed to respond after ServerOptions: -pingsBeforeConsideredDead ping attempts); false
otherwise.
true
if the server is running on the same machine as sclang, false
otherwise.
true
if the server is not being controlled by the machine on which it is running, false
otherwise. This value is the same as isLocal
unless explicitly set.
Deprecated in 3.9. Returns false
. Server:clientID can now be set while a server is off, and is locked while the server is running. Thus, userSpecifiedClientID is no longer needed internally, and meaningless.
The server provides support for automatically bundling messages. This is quite convenient in object style and ensures synchronous execution. See also Bundled Server Messages
The Function func
is evaluated, and all OSC messages generated by it are deferred and added to a bundle.
time |
+ If set to nil or a number the bundle will be automatically sent and executed after the corresponding delay in seconds. If |
func |
+ The function to evaluate. |
bundle |
+ allows you to pass in a preexisting bundle and continue adding to it. |
The bundle so that it can be further used if needed.
Calling sync
inside func will split the bundle and wait for asynchronous actions to complete before continuing.
+
If an error is encountered while evaluating func
this method will throw an Error and stop message deferral.
+
Just as in makeBundle
, the Function func
is evaluated, and all OSC messages generated by it are deferred and added to a bundle, which is sent to the server, using the server default latency.
The internal server has a number of shared control buses. Their values can be set or polled using the methods below.
get the current value of a shared control bus. num is the index of the bus to poll. This command is synchronous and only works with the internal server.
set the current value of a shared control bus to value. num is the index of the bus to set. This command is synchronous and only works with the internal server.
set the number of shared control buses. Must be done before the internal server is booted. The default is 1024.
The class ServerTree can be used to store functions which will be evaluated after the server is booted, after all nodes are freed, and after cmd-. is pressed. This allows, for example, for one to create a persistent basic node structure. ServerTree is evaluated in the method initTree after the default group is created, so its existence can be relied upon.
This method initializes the Default Group and runs ServerTree.
This method is called automatically when you boot a Server from the language. N.B. If you started a server app from the command line you will have to call initTree manually if you need this functionality. + +
ServerBoot and ServerQuit provide similar functionality at boot and quit times.
Create and show the server window. The window responds to a number of keyboard shortcuts:
key | action |
n | Post a representation of this Server's current node tree to the post window. (See -queryAllNodes) |
N | As 'n' above but include controls. |
l | Show input/output level meters. (See -meter) |
p | Show graphical view of the node tree. (See -plotTree) |
(space) | Boot server if not already booted. (See -boot) |
s | Show scope window. (See -scope) |
f | Show frequency analyzer window. (See -freqscope) |
d | Toggle dumping of OSC messages. |
m | Toggle mute. |
0 | Reset volume to 0 db. |
On most platforms, this is equivalent to makeGui
. If you are running SuperCollider on Emacs, it makes a server view composed of Emacs widgets.
Open a scope window showing the output of the Server. see Stethoscope for further details.
numChannels |
+ the number of channels to be scoped out. The default is this server's options' numOutputBusChannels. |
index |
+ the first channel to be output. The default is 0. |
bufsize |
+ the size of the buffer for the ScopeView. The default is 4096. |
zoom |
+ a zoom value for the scope's X-axis. Larger values show more. The default is 1. |
rate |
+ whether to display audio or control rate buses (either \audio or \control) |
Show frequency analyzer window.
Show input/output level meters.
Plot the node/group tree. As -queryAllNodes but graphical.
interval |
+ Polling interval. |
Plot the node/group tree graphically on a given view.
interval |
+ Polling interval. |
parent |
+ Parent view. |
actionIfFail |
+ Function to be evaluated in case communication with the server cannot be established. |
Function to be evaluated in order to clean up all actions performed inside the method. To be called for instance by the onClose method of the enclosing window.
The following methods are for convenience use. For recording with sample-accurate start and stop times you should make your own nodes. See the DiskOut helpfile for more info. For non-realtime recording, see the Non-Realtime Synthesis (NRT) helpfile. +
This functionality is also available through the recording button on the server windows. Pressing it once calls record, and pressing it again calls stopRecording (see below). When doing so the file created will be in your recordings folder and be named for the current date and time. The default location of the recordings folder varies from platform to platform but is always stored in thisProcess.platform.recordingsDir
. Setting this variable allows you to change the default.
For more detail on this subject see Order of execution, Default Group, and Node Messaging. +
See SoundFile for information on the various sample and header formats. Not all sample and header formats are compatible. Note that the sampling rate of the output file will be the same as that of the server app. This can be set using the Server's ServerOptions. +
Example: + +
The recording is done via an of Recorder - a server holds one instance implicitly.
Allocates the necessary buffer, etc. for recording the server's output. (See record
below.)
path |
+ a String representing the path and name of the output file. If the directory does not exist, it will be created for you. (Note, however, that if this fails for any reason, the recording will also fail.) |
numChannels |
+ a String the number of output channels to record. |
If you do not specify a path than a file will be created in your recordings folder (see the note above on this) called SC_thisDateAndTime. Changes to the header or sample format, or to the number of channels must be made before calling this.
Starts or resumes recording the output.
path |
+ this is optional, and is passed to |
bus |
+ the bus to record - defaults to 0 |
numChannels |
+ the number of channels to record - defaults to server numChannels |
node |
+ the node to record - defaults to server rootnode |
duration |
+ duration to record - defaults to inf |
If you have not called prepareForRecord first (see above) then it will be invoked for you (but that adds a slight delay before recording starts for real).
Pauses recording. Can be resumed by executing record again.
Stops recording, closes the file, and frees the associated resources.
You must call this when finished recording or the output file will be unusable. Cmd-. while recording has the same effect.
Get/set the number of channels (int) to record. By default this is set to 2 (stereo) but can be increased to record soundfiles with many more channels. Must be called before prepareForRecord.
Get/set the header format (string) of the output file. The default is "aiff". Must be called before prepareForRecord.
Get/set the sample format (string) of the output file. The default is "float". Must be called before prepareForRecord.
Get/set the size of the Buffer to use with the DiskOut UGen. This must be a power of two. The default is the sampleRate.nextPowerOfTwo
or the first power of two number of samples longer than one second. Must be called before prepareForRecord.
The server provides support for waiting on the completion of asynchronous OSC-commands such as reading or writing sound files. N.B. The following methods must be called from within a running Routine. Explicitly passing in a Condition allows multiple elements to depend on different conditions. The examples below should make clear how all this works.
Boot the Server and wait until it has completed before resuming the thread.
condition |
+ an optional instance of Condition used for evaluating this. |
Send the following message to the wait until it has completed before resuming the thread.
condition |
+ an optional instance of Condition used for evaluating this. |
... args |
+ one or more valid OSC messages. |
Send a /sync
message to the server, which will reply with the message /synced
when all pending asynchronous commands have been completed.
condition |
+ an optional instance of Condition used for evaluating this. |
bundles |
+ one or more OSC messages which will be bundled before the sync message (thus ensuring that they will arrive before the /sync message). |
latency |
+ allows for the message to be evaluated at a specific point in the future. |
This may be slightly less safe then sendMsgSync under UDP on a wide area network, as packets may arrive out of order, but on a local network should be okay. Under TCP this should always be safe. +
Scope a number of channels from a Bus on this Server, to Bela's Oscilloscope (see BelaScope for required setup). It is required that this Server is running on a Bela, and that it is thus capable of using BelaScope.
scopeChannel |
+ Bela's oscilloscope channel to start scoping on. This has to be a non-negative number, and can't be changed after scoping starts. |
index |
+ This server's bus index to scope. Defaults to 0. |
numChannels |
+ Number of channels to send to BelaScope, starting from the index supplied. Defaults to 2, or to ServerOptions: -numOutputBusChannels if index is 0. |
A Synth, linking this Server's desired bus channels to BelaScope's bus.
A scheduling clock driven from OSC Triggers from the Server. Allows the use of a UGen as a beat tracker controlling scheduling. This class assumes a UGen running on the Server, sending OSC trig messages at each beat.
trigID |
+ Trigger OSC message ID number. |
s |
+ Server. |
A ServerClock must be stopped to remove the OSC responder that reacts to triggers sent from the Server.
s=Server.default; +s.latency=0.05; //must be small compared to half beat size in tempo + +( +//react to an Impulse driven clock on the Server, SendTrig with ID of 100 +SynthDef(\help_ServerClock,{arg tempo=2.3, beepvol=0.0, trigID=100; + var clocktick, beep; + + clocktick=Impulse.ar(tempo); + + beep= SinOsc.ar(440,0,0.1)*Decay.ar(clocktick,0.1); + + Out.ar(0,Pan2.ar(beepvol*beep,0.0)); + + //trig from clock, trigID 100, always pass current tempo as parameter + SendTrig.ar(clocktick,trigID,tempo); //sends with ID of 100 matching what clock expects +}).send(s); +) + +a=Synth(\help_ServerClock); + +//set up a ServerClock running off of these triggers +c=ServerClock.new.play(100,s); + +//run a bbcut from it +b=BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + +d=BBCut2(CutBuf2(b)).play(c); + +a.set(\tempo,2.5); +a.set(\tempo,1.7); + +//triggers stop +a.free; + +//continue triggers +a=Synth(\help_ServerClock) + +c.stop; //stop and free OSC responder. + +d.free; //free resources + +//frees clock +a.free; + + + + + +//now a beat tracking example +//change the ~source line with a path to some song file you want to track to load from your hard drive + +( +var trackbus, trackgroup; + +s.latency=0.05; + +//clear any existing OSCresponder +OSCresponder.all.do({arg val; if(val.cmdName=='/tr',{OSCresponder.remove(val)}); }); + +//run a line at a time +~clock= ServerClock.new; + +~clock.play(100,s); //will wait on trigID 100 + +Routine.run({ + +~buffers= BBCutBuffer.array([Platform.resourceDir +/+ "sounds/break.aiff",Platform.resourceDir +/+ "sounds/break2.aiff"],[8,4]); + +//choose some file you want to track off your hard drive +~source=Buffer.read(s,"/Volumes/data/stevebeattrack/samples/100.wav"); + +~trackbus=Bus.audio(s,1); + +trackgroup= Group.before(Node.basicNew(s,1)); + +//run a beat tracker on the Server which sends the appropriate OSC message +~tracksynth= SynthDef(\help_bbinduct,{arg vol=1.0, beepvol=0.0, lock=0; +var trackb,trackh,trackq,tempo; +var source, beep; + +source= PlayBuf.ar(1,~source.bufnum,1.0,1,0,1); + +//see AutoTrack help file +#trackb,trackh,trackq,tempo=AutoTrack.kr(source, lock); + +beep= SinOsc.ar(1000,0.0,Decay.kr(trackb,0.1)); + +Out.ar(~trackbus.index,source); + +Out.ar(0,Pan2.ar((vol*source)+(beepvol*beep),0.0)); + +SendTrig.kr(trackb,100,tempo); //sends with ID of 100 matching what clock expects + +}).play(trackgroup); + +}); + +) + +//cutters will run by default in Node.basicNew(s,1), so rendering order safe + +//will be added to run on this clock as soon as made, you'll hear from the next beat +a=BBCut2(CutBuf2(~buffers[0],0.3), ChooseCutProc(1,2)).play(~clock); + +b=BBCut2(CutBuf2(~buffers[1],0.5, dutycycle:0.3), BBCutProc11.new).play(~clock); + +~tracksynth.set(\vol,0.1); + +~tracksynth.set(\beepvol,0.0); + +~store=Buffer.alloc(Server.default,44100,1); + +//stream cut the source with respect to the inferred beat +c=BBCut2(CutStream1(~trackbus.index,~store), ChooseCutProc(0.25,4)).play(~clock); + +//lock in to a state that is working +~tracksynth.set(\lock,1.0); + +//remove lock (ie track again) +~tracksynth.set(\lock,0.0); + + + +a.end; +b.end; +c.stop; + +~store.free; + +//remove everything and terminate clock +~clock.stop; + +~tracksynth.free;+
This is more compact and conforms to the .gui convention in that it can be placed on any window or view. +
Its an MVC design so there can be many of them for a single server and they can be closed and will clean up after themselves.
layout |
layout | |
width |
layout | |
width |
layout | |
bounds |
layout |
changer | |
what |
ServerOptions encapsulates various options for a server app within an object. This makes it convenient to launch multiple servers with the same options, or to archive different sets of options, etc. Every Server has an instance of ServerOptions created for it if one is not passed as the options argument when the Server object is created. (This is the case for example with the local and internal Servers which are created at startup.) +
A Server's instance of ServerOptions is stored in its options instance variable, which can be accessed through corresponding getter and setter methods. +
N.B. A ServerOptions' instance variables are translated into commandline arguments when a server app is booted. Thus a running Server must be rebooted before changes will take effect. There are also a few commandline options which are not currently encapsulated in ServerOptions. See Server Architecture for more details.
Create and return a new instance of ServerOptions.
Return an Array of Strings listing the audio devices currently available on the system.
Return an Array of Strings listing the audio devices currently available on the system which have input channels.
Return an Array of Strings listing the audio devices currently available on the system which have output channels.
The number of samples in one control period. The default is 64.
A String that allows you to choose a sound device to use as input and output. The default, nil
, will use the system's default input and output device(s). See Audio device selection for more details.
A String that allows you to choose an input sound device. The default, nil
, will use the system's default input device. See Audio device selection for more details.
A String that allows you to choose an output sound device. The default, nil
, will use the system's default output device. See Audio device selection for more details.
The preferred hardware buffer size. If non-nil the server app will attempt to set the hardware buffer frame size. Not all sizes are valid. See the documentation of your audio hardware for details. Default value is nil.
By default, the Server object in the client begins allocating node IDs at 1000, reserving 0-999 for "permanent" nodes. You may change this default here.
A String which allows turning off input streams that you are not interested in on the audio device. If the string is "01100", for example, then only the second and third input streams on the device will be enabled. Turning off streams can reduce CPU load. The default value is nil.
A Boolean indicating whether or not to load the synth definitions in synthdefs/ (or anywhere set in the environment variable SC_SYNTHDEF_PATH) at startup. The default is true.
The maximum number of nodes. The default is 1024.
The maximum number of synthdefs. The default is 1024.
The number of kilobytes of real time memory allocated to the server. This memory is used to allocate synths and any memory that unit generators themselves allocate (for instance in the case of delay ugens which do not use buffers, such as CombN), and is separate from the memory used for buffers. Setting this too low is a common cause of 'exception in real time: alloc failed' errors. The default is 8192.
The number of audio rate busses, which includes input and output busses. The default is 1024.
The number of global sample buffers available. (See Buffer.) The default is 1024.
The number of internal control rate busses. The default is 16384.
The number of audio input bus channels. This need not correspond to the number of hardware inputs. The default is 2.
The number of audio output bus channels. This need not correspond to the number of hardware outputs (this can be useful for instance in the case of recording). The default is 2.
The number of seedable random number generators. The default is 64.
The maximum number of buffers that are allocated to interconnect unit generators. (Not to be confused with the global sample buffers represented by Buffer.) This sets the limit of complexity of SynthDefs that can be loaded at runtime. This value will be automatically increased if a more complex def is loaded at startup, but it cannot be increased thereafter without rebooting. The default is 64.
A String which allows turning off output streams that you are not interested in on the audio device. If the string is "11000", for example, then only the first two output streams on the device will be enabled. Turning off streams can reduce CPU load.
A Symbol representing the communications protocol. Either \udp
or \tcp
. The default is \udp
.
The IP address that the server's TCP or UDP socket is listening on. The default value is "127.0.0.1"
, meaning only listen to OSC messages on the host.
"0.0.0.0"
(listen on all network interfaces). However, this is a dangerous default configuration — for most users working on laptops connected to WiFi, this means that anyone on your local network can send OSC messages to the server. "0.0.0.0"
is only useful if you are running networked server/client, and only safe if your networking is properly configured.
+Before SuperCollider 3.12 supernova listened to all network interfaces and ignored the bindAddress
option. In later versions the behavior is identical to scsynth.
A Boolean indicating whether this server should allow its volume to be set remotely. The default value is false
.
The preferred sample rate. If non-nil the server app will attempt to set the sample rate of the hardware. The hardware has to support the sample rate that you choose.
sampleRate
as nil
for an ASIO
device will likely result in setting the hardware to run at 44100 Hz.Controls the verbosity of server messages. A value of 0 is normal behaviour. -1 suppresses informational messages. -2 suppresses informational and many error messages, as well as messages from Poll. The default is 0.
A Boolean indication whether or not the server should publish its port using zero configuration networking, to facilitate network interaction. This is true by default; if you find unacceptable delays (beachballing) upon server boot, you can try setting this to false.
A path or an Array of paths. If non-nil, the standard paths are NOT searched for plugins. This corresponds with the option "-U".
Allows you to restrict the system paths in which the server is allowed to read/write files during running. A nil value (the default) means no restriction. Otherwise, set it as a string representing a single path.
Number of audio threads that are spawned by supernova. For scsynth this value is ignored. If it is nil
or 0, it uses the one thread per CPU. Default is nil
.
Tells supernova whether to sync to the driver's sample clock, or to the system clock.
true
(default) -- Use the system clock. Timestamped messages will maintain consistent latency over long sessions, but may not be perfectly sample-accurate.false
-- Use the sample clock. This helps to support sample-accurate scheduling; however, messaging latency from the SuperCollider language will drift over long periods of time.A Boolean indicating whether the server should try to lock its memory into physical RAM. Default is false
.
An Integer indicating the maximum number of clients which can simultaneously receive notifications from the server. When using TCP this is also the maximum number of simultaneous connections. This is also used by the language to split ranges of Nodes, Buffers, or Busses. In multi-client situations you will need to set this to at least the number of clients you wish to allow. This must be the same in the Server instances on every client. The default is 1.
A Float indicating a safety threshold for output values to be clipped to. This is necessary on macOS because setting a low system volume doesn't prevent output values greater than +/- 1 from sounding extremely loud, which can happen by mistake, e.g. when sending a negative coefficient to a filter. With this threshold, values are clipped just before being written to hardware output busses, which does not affect the recording. However, the signal will be affected if it's above the threshold and the sound is routed to other apps using 3rd-party software. Defaults to a threshold of 1.26 (ca. 2 dB), to save some ears and still allow some headroom. Setting safetyClipThreshold to inf
, 0
, or a negative value, disables clipping altogether.
port |
+ The port number for the resulting server app. Default value is 57110. |
a String specifying the options in the format required by the command-line server app (scsynth or supernova).
the index of the first audio bus on this server which is not used by the input and output hardware.
Number of failed pings (attempts to contact server process) before server is considered dead. Default value is 5.
An object that observes a Server object by managing status requests and updates. ServerStatusWatcher is a client-side server implementation detail, and users are not expected to need to use it directly. A Server's status watcher can be accessed via Server: -statusWatcher.
Creates a new ServerStatusWatcher.
server |
+ An instance of Server. |
The constructed ServerStatusWatcher.
ServerView is a replacement for the standard SuperCollider server gui. It has a cleaner and more compact look, it supports multiple servers, and displays graphs of CPU usage, Synth counts, as well as a scope and input/output levels.
It will work without these installed, but layout and affordances may not be correct.
Create a new ServerView
key |
+ A Symbol or other IdentityDictionary-appropriate object used to identify the ServerView. See Singleton: -new for behavior. Unless you need multiple ServerView's, no argument is required. |
(widgetlist) |
+ A list of subclasses of ServerWidgetBase to be displayed in the ServerView. By default, it contains ServerStatusWidget, VolumeWidget, ScopeWidget. Set this in your startup file to enable alternative configurations of widgets. |
Close the view;
Sets or gets the object to be evaluated when the view is destroyed (i.e. closed or removed). It is passed the view as an argument.
Bring ServerView to front. If this ServerView has not been created yet, create it.
Add a keyboard action to the ServerView.
key |
+ A Char representing an ascii key. |
action |
+ A function to execute when key is detected. |
ServerView works as a singleton, so the default constructor will always refer to the same object. +
p | Show node hierarchy window |
n | Dump all node information to post window (s.queryAllNodes(true) ) |
l | Show level meters window. |
<space> | Boot server (if not already running) |
s | Show scope window |
f | Show frequency scope window |
d | Enable / disable dumping of OSC messages |
m | Mute server volume |
0 | Reset server volume to 0dB |
Horizontal / vertical mouse wheel or scroll actions over the scope will zoom horizontally and vertically. +
Clicking graph widgets will reset their bounds. +
Clicking the server name will boot / kill the server.
ServerView is populated by widgets that must be subclasses of ServerWidgetBase. To add a new widget type to the ServerView: +
1. Create a subclass of ServerWidgetBase. It should not implement a constructor, and must implement a view
method that returns a valid View.
+
2. Add the class to *widgets in your startup file, or any time before a ServerView is created.
Base class for numbers which can be represented by a single one dimensional value. +
Most of the Unary and Binary operations are also implemented by UnaryOpUGen and BinaryOpUGen, so you can get more examples by looking at the help for those.
allocates a new SimpleNumber.
Addition
Subtraction
Multiplication
Division
Modulo
Modulo
Integer Division
Exponentiation
Is not
greater than
greater than
greater or equal than
smaller or equal than
Least common multiple
Greatest common divisor
Round to multiple of aNumber
Round up to a multiple of aNumber. For roundDown use trunc.
Truncate to multiple of aNumber (e.g. it rounds numbers down to a multiple of aNumber).
Rounds the value to a multiple of resolution. By using margin and strength you can control which values will be rounded, and by how much. +
Conceptually this is the equivalent of MIDI quantization in a DAW/MIDI sequencer. In particular it allows a certain sloppiness close to the resolution value. +
Note: this method expects values >= 0.
resolution |
+ Round this value to a multiple of resolution. E.g. if you chose 1, then all values would be rounded to the nearest integer. |
margin |
+ Values that are within ±margin from a multiple of resolution will be left as they are. + E.g. if you chose a resolution value of 0.5 and a margin of 0.01, then the values 0.501 and 0.499 would be left as they are, but the value 0.502 would become 0.5. + This should be a value between 0 and resolution. |
strength |
+ Determines the degree to which this number will be changed. + If strength is 1, then this function will return the nearest resolution. If it is 0, then value of this number will be left unchanged. + E.g. If the resolution was 1 and strength was 0.5, then the value 0.6 would become 0.8. |
((0..10) / 5).collect { |num| [num, num.softRound(1, 0, 1)] }; +((0..10) / 5).collect { |num| [num, num.softRound(1, 0.3, 1)] }; +((0..10) / 5).collect { |num| [num, num.softRound(1, 0, 0.5)] };+
Rounds the values margin distance from resolution to a multiple of resolution. By using margin and strength you can control when values will be rounded, and by how much. +
Conceptually this is the equivalent of 'snap' in a graphics program. Values within a certain distance (margin) from a grid line are snapped to it. All other values are unchanged. +
Note: this method expects values >= 0.
resolution |
+ Snap this value to a multiple of resolution. E.g. if you chose 1, then all values would be rounded to the nearest integer. |
margin |
+ Only values that are greater ±margin from a multiple of resolution value will be changed. Values that are less than margin will be unchanged. + E.g. if you chose a resolution value of 0.5 and a margin of 0.01, then the values 0.501 and 0.499 would be snapped to 0.5, but the value 0.502 would be unchanged. + This should be a value between 0 and resolution. |
strength |
+ Determines the degree to which this number will be changed. + If strength is 1, then this function will return the nearest resolution. If it is 0, then value of this number will be left unchanged. + E.g. If the resolution was 1 and strength was 0.5, then the value 0.6 would become 0.8. |
((0..10) / 5).collect { |num| [num, num.snap(1, 0, 1)] }; +((0..10) / 5).collect { |num| [num, num.snap(1, 0.3, 1)] }; +((0..10) / 5).collect { |num| [num, num.snap(1, 0, 0.5)] };+
Minimum
Maximum
Arctangent of (this/aNumber)
Square root of the sum of the squares.
Base e logarithm.
Base 2 logarithm.
Base 10 logarithm.
negation
absolute value.
Answer -1 if negative, +1 if positive or 0 if zero.
next larger integer.
next smaller integer
Sine
Cosine
Tangent
Arcsine
Arccosine
Arctangent
Hyperbolic sine
Hyperbolic cosine
Hyperbolic tangent
fractional part
the square of the number
the cube of the number
the square root of the number.
e to the power of the receiver.
1 / this
this to the power of aNumber
the folded value, a bitwise or with aNumber
the number relative to this that is the previous power of aNumber
the next power of aNumber
the number relative to this that is the next power of 2
the next power of three
a hash value
the receiver. aNumber is ignored.
Bitwise And
Bitwise Or
Bitwise Exclusive Or
Binary Hamming distance: the count of bits that are not the same in the two numbers
true if bit at index aNumber is set.
ones complement
Binary shift left.
Binary shift right.
Unsigned binary shift right.
performs a binary right shift
performs an unsigned right shift
performs a binary left shift
performs a bitwise or with aNumber
performs a bitwise and with aNumber
(a * b) + a
((a*b) + a + b)
(a * a *b)
((a*a *b) - (a*b*b))
(a*a) - (b*b)
(a*a) + (b*b)
(a - b) ** 2
(a + b) ** 2
(a - b).abs
On a circle, there are two distances between two points. This operator returns the smaller value of the two.
moddif(0.75, 0, 1)+
0 when b <= 0, a*b when b > 0
a * b when a < 0, otherwise a.
clips receiver to +/- aNumber
Returns the difference of the receiver and its clipped form.
(a - clip2(a,b))+
this * a + b+
Answer if the number is >= 0.
Answer if the number is < 0.
Answer if the number is > 0.
true, if strictly positive ( > 0), otherwise false (see Boolean)
denominator | |
fasterBetter |
+ if true, asFraction may find a much closer approximation and do it faster. |
an array of denominator and divisor of the nearest and smallest fraction
Converts this into an audiorate input.
Produces a time string in the format ddd:hh:mm:ss.sss
, interpreting the receiver as time in seconds. See String: -asSecs for the inverse function.
precision |
+ accuracy of the millisecond format; the string will always be formatted with 3 decimal places for milliseconds. Minimum value: 0.001. |
maxDays |
+ maximum number of days |
dropDaysIfPossible |
+ a Boolean. If set to |
( +var start; +start = Main.elapsedTime; +{ + loop { + (Main.elapsedTime - start).asTimeString.postln; + 0.05.wait + } +}.fork; +)+
this as Point. x = y = this.
this as Point. x = y = this.
this as Float
a Rect with x = y = w = h = this.
this as a Boolean. this > 0
the values as Quant
this as Integer
within a routine, yield the number so that the clock can wait for this many beats. Outside a Routine, this trows an error (see also Routine for details).
Create a routine by a function fork
( +fork { + 1.wait; + "I did wait".postln; + 1.0.rand.wait; + "No you didn't".postln; + 2.wait; + (1..).do { |i| + "yes I did".postln; + i.asFloat.rand.wait; + "no you didn't".postln; + i.wait + } +} +)+
like wait, only specify a time (measured in beats of the current thread's clock). Outside a Routine, this trows an error (see also Routine for details).
make the current thread sleep, until woken up by re-scheduling. Outside a Routine, this trows an error (see also Routine for details).
clock |
the next possible multiple of the clock's beats.
the value in the list closest to this
( +l = [0, 0.5, 0.9, 1]; +(0, 0.05..1).collect { |i| i.nearestInList(l) } +)+
scale |
+ an array of SimpleNumbers each treated as a step in the octave. |
stepsPerOctave |
+ 12 by default |
the value in the collection closest to this, assuming an octave repeating table of note values.
( +l = [0, 1, 5, 9, 11]; // pentatonic scale +(60, 61..76).collect { |i| i.nearestInScale(l, 12) } +)+
return an arithmetic series from this over second to last.
This is used in the shortcuts:
(0..100); +(1, 3 .. 17)+ +
If second is nil, it is one magnitude step towards last (1 or -1). Examples:
series(5, 7, 10); +series(5, nil, 10); +(5, 7 .. 10)+
a Routine that iterates over the numbers from this to last.
Since this is a lazy operation, last may be inf, generating an endless series (see also List Comprehensions)
r = seriesIter(0, 5); +r.nextN(8); +r.nextN(8);+
a value for a rectangular window function between 0 and 1.
a value for a hanning window function between 0 and 1.
a value for a welsh window function between 0 and 1.
a value for a triangle window function between 0 and 1.
a nonlinear distortion function.
Distortion with a perfectly linear region from -0.5 to +0.5
Map receiver in the onto an S-curve.
((0..100) / 100 ).collect(_.scurve).plot+
Map receiver onto a ramp starting at 0.
((-100..100) / 100 ).collect(_.ramp).plot+
scale |
+ an array of SimpleNumbers each treated as a step in the octave. |
stepsPerOctave |
+ 12 is the standard chromatic scale. |
the value is truncated to an integer and used as an index into an octave repeating table of note values. Indices wrap around the table and shift octaves as they do.
( +l = [0, 1, 5, 9, 11]; // pentatonic scale +(1, 2..15).collect{|i| + i.degreeToKey(l, 12) +}; +)+
inverse of degreeToKey.
scale |
+ an array of SimpleNumbers each treated as a step in the octave. |
stepsPerOctave |
+ 12 is the standard chromatic scale. |
( +l = [0, 1, 5, 9, 11]; // pentatonic scale +(60, 61..75).collect { |i| i.keyToDegree(l, 12) } +)+ +
( +l = [0, 1, 5, 9, 11]; // pentatonic scale +(60, 61..75).postln.collect { |i| i.keyToDegree(l, 12).degreeToKey(l) } +)+
map the receiver onto a gauss function.
Uses the formula:
a * (exp(squared(this - b) / (-2.0 * squared(c)))) Default values: a = 1; b = 0; c = 1+ +
Example code
(0..1000).normalize(-10, 10).collect { |num| num.gaussCurve }.plot;+
that |
+ the number to compare with within precision |
precision |
+ The absolute precision, independent of the value compared |
relativePrecision |
+ The precision relative to the larger absolute of the values compared. |
true if receiver is closer to that than precision.
3.1.equalWithPrecision(3.0, 0.05); // false +3.1.equalWithPrecision(3.0, 0.1); // false +3.1.equalWithPrecision(3.0, 0.11); // true +3000.1.equalWithPrecision(3000.0, 0, 0.01); // true +3.1.equalWithPrecision(3.0, 0, 0.01); // false+
Deprecated. Round the receiver to the quantum. If you're looking for MIDI quantization type features use SimpleNumber#-softRound
quantum |
+ amount. |
tolerance |
+ allowed tolerance. |
strength |
+ Determines how much the value is allowed to differ in the tolerance range. |
((0..10) / 10).collect { |num| num.quantize(1, 0.3, 0.5) }.postcs.plot; +((0..10) / 10).collect { |num| num.quantize(1, 0.6, 0.5) }.postcs.plot; +((0..10) / 10).collect { |num| num.quantize(1, 1.0, 0.5) }.postcs.plot;+
map the receiver from an assumed linear input range to a linear output range. If the input exceeds the assumed input range, the behaviour is specified by the clip argument.
inMin |
+ assumed input minimum |
inMax |
+ assumed input maximum |
outMin |
+ output minimum |
outMax |
+ output maximum |
clip |
+ nil (don't clip) \max (clip ceiling) \min (clip floor) \minmax (clip both - this is default). |
(0..10).collect { |num| num.linlin(0, 10, -4.3, 100) }; +(0..10).linlin(0, 10, -4.3, 100); // equivalent.+
map the receiver from an assumed linear input range (inMin..inMax) to an exponential output range (outMin..outMax). The output range must not include zero. If the input exceeds the input range, the following behaviours are specified by the clip argument.
inMin |
+ assumed input minimum |
inMax |
+ assumed input maximum |
outMin |
+ output minimum |
outMax |
+ output maximum |
clip |
+ nil (don't clip) \max (clip ceiling) \min (clip floor) \minmax (clip both - this is default). |
(0..10).collect { |num| num.linexp(0, 10, 4.3, 100) }; +(0..10).linexp(0, 10, 4.3, 100); // equivalent.+
map the receiver from an assumed exponential input range (inMin..inMax) to a linear output range (outMin..outMax). If the input exceeds the assumed input range. The input range must not include zero. If the input exceeds the input range, the following behaviours are specified by the clip argument.
inMin |
+ assumed input minimum |
inMax |
+ assumed input maximum |
outMin |
+ output minimum |
outMax |
+ output maximum |
clip |
+ nil (don't clip) \max (clip ceiling) \min (clip floor) \minmax (clip both - this is default). |
(1..10).collect { |num| num.explin(0.1, 10, -4.3, 100) }; +(1..10).explin(0.1, 10, -4.3, 100); // equivalent.+
map the receiver from an assumed exponential input range (inMin..inMax) to an exponential output range (outMin..outMax). If the input exceeds the assumed input range. Both input range and output range must not include zero. If the input exceeds the input range, the following behaviours are specified by the clip argument.
inMin |
+ assumed input minimum |
inMax |
+ assumed input maximum |
outMin |
+ output minimum |
outMax |
+ output maximum |
clip |
+ nil (don't clip) \max (clip ceiling) \min (clip floor) \minmax (clip both - this is default). |
(1..10).collect { |num| num.expexp(0.1, 10, 4.3, 100) }; +(1..10).expexp(0.1, 10, 4.3, 100); // equivalent.+
map the receiver from an assumed linear input range (inMin..inMax) to an exponential curve output range (outMin..outMax). A curve is like the curve parameter in Env. Unlike with linexp, the output range may include zero. If the input exceeds the input range, the following behaviours are specified by the clip argument.
inMin |
+ assumed input minimum |
inMax |
+ assumed input maximum |
outMin |
+ output minimum |
outMax |
+ output maximum |
curve |
+ 0 (linear) <0 (concave, negatively curved) >0 (convex, positively curved) |
clip |
+ nil (don't clip) \max (clip ceiling) \min (clip floor) \minmax (clip both - this is default). |
(0..10).collect { |num| num.lincurve(0, 10, -4.3, 100, -3) }; +(0..10).lincurve(0, 10, -4.3, 100, -3); // equivalent.+ +
// different curves: +(-4..4).do { |val| + (0..100).collect(_.lincurve(0, 100, 0, 1, val)).plot +}+
map the receiver from an assumed curve-exponential input range (inMin..inMax) to a linear output range (outMin..outMax). If the input exceeds the assumed input range. A curve is like the curve parameter in Env. Unlike with explin, the input range may include zero. If the input exceeds the input range, the following behaviours are specified by the clip argument.
inMin |
+ assumed input minimum |
inMax |
+ assumed input maximum |
outMin |
+ output minimum |
outMax |
+ output maximum |
curve |
+ 0 (linear) <0 (concave, negatively curved) >0 (convex, positively curved) |
clip |
+ nil (don't clip) \max (clip ceiling) \min (clip floor) \minmax (clip both - this is default). |
(1..10).collect { |num| num.curvelin(0, 10, -4.3, 100, -3) }; +(1..10).curvelin(0, 10, -4.3, 100, -3); // equivalent.+ +
// different curves: +(-4..4).do { |val| + (0..100).collect(_.curvelin(0, 100, 0, 1, val)).plot +}+
map the receiver from two assumed linear input ranges (inMin..inCenter) and (inCenter..inMax) to two linear output ranges (outMin..outCenter) and (outCenter..outMax). If the input exceeds the input range, the following behaviours are specified by the clip argument.
inCenter | |
inMin |
+ assumed input minimum |
inMax |
+ assumed input maximum |
outCenter | |
outMin |
+ output minimum |
outMax |
+ output maximum |
clip |
+ nil (don't clip) \max (clip ceiling) \min (clip floor) \minmax (clip both - this is default). |
var center = 0.5, ctlCenter; +w = Window("bilin", Rect(100, 100, 200, 100)).front; +a = Slider(w, Rect(20, 20, 150, 20)).value_(0.5); +b = Slider(w, Rect(20, 45, 150, 20)).value_(0.5); +b.action = { center = b.value }; +a.mouseDownAction = { ctlCenter = a.value }; +a.action = { + b.value = a.value.bilin(ctlCenter, 0, 1, center, 0, 1); +};+
map the receiver from two assumed exponential input ranges (inMin..inCenter) and (inCenter..inMax) to two linear output ranges (outMin..outCenter) and (outCenter..outMax). The input range must not include zero. If the input exceeds the input range, the following behaviours are specified by the clip argument.
inCenter | |
inMin |
+ assumed input minimum |
inMax |
+ assumed input maximum |
outCenter | |
outMin |
+ output minimum |
outMax |
+ output maximum |
clip |
+ nil (don't clip) \max (clip ceiling) \min (clip floor) \minmax (clip both - this is default). |
// doesn't properly work yet. +( +var center = 0.5, ctlCenter; +w = Window("biexp", Rect(100, 100, 200, 100)).front; +a = Slider(w, Rect(20, 20, 150, 20)).value_(0.5); +b = Slider(w, Rect(20, 45, 150, 20)).value_(0.5); +b.action = { center = b.value }; +a.mouseDownAction = { ctlCenter = a.value + 0.05 }; +a.action = { + b.value = (a.value + 0.1).biexp(ctlCenter, 0.1, 1.1, center, 0, 1); +}; +)+
map the receiver onto an L-curve.
Uses the formula
a * (m * exp(x) * rTau + 1) / (n * exp(x) * rTau + 1)+ +
This is used for smoothing values and limiting them to a range.
(0..1000).normalize(-10, 10).collect { |num| num.lcurve }.plot;+
converts degree to radian
converts radian to degree
Convert MIDI note to cycles per second
cycles per second
Convert cycles per second to MIDI note.
midi note
Convert an interval in semitones to a ratio.
a ratio
Convert a ratio to an interval in semitones.
an interval in semitones
Convert a linear amplitude to decibels.
Convert a decibels to a linear amplitude.
Convert decimal octaves to cycles per second.
Convert cycles per second to decimal octaves.
stores this on the given stream
prints this on the given stream
Let x be the receiver clipped to the range [0, 1]. With probability x, return true. With probability 1 - x, return false.
Random number from zero up to the receiver, exclusive.
a random number from -this to +this.
aNumber |
+ the upper limit |
adverb |
a random number in the interval ]a, b[.
If both a and b are Integer then the result will be an Integer.
a linearly distributed random number from zero to this.
Bilateral linearly distributed random number from -this to +this.
This was suggested by Larry Polansky as a poor man's gaussian.
A random number from -this to +this that is the result of summing three uniform random generators to yield a bell-like distribution.
an exponentially distributed random number in the interval ]a, b[. This is always a Float. (Note that the distribution of numbers is not exactly an exponential distribution, since that would be unbounded: we might call it a logarithmic uniform distribution.)
aNumber |
+ the upper limit |
adverb |
a gaussian distributed random number.
standardDeviation |
+ the upper limit |
Always returns a Float.
(0..1000).collect { |num| gauss(0.0, num) }.plot;+
randomly partition a number into parts of at least min size.
parts |
+ number of parts |
min |
+ the minimum size |
75.partition(8, 3); +75.partition(75, 1);+
Some methods to ease the development of generic ugen code.
this
false if receiver cannot be used in UGen.
Generates a sine wave. Uses a wavetable lookup oscillator with linear interpolation. Frequency and phase modulation are provided for audio-rate modulation. Technically, SinOsc
uses the same implementation as Osc except that its table is fixed to be a sine wave made of 8192
samples.
freq |
+ Frequency in Hertz. Sampled at audio-rate. |
phase |
+ Phase in radians. Sampled at audio-rate. NOTE: phase values should be within the range +-8pi. If your phase values are larger then simply use .mod(2pi) to wrap them. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
SinOscFB is a sine oscillator that has phase modulation feedback; its output plugs back into the phase input. Basically this allows a modulation between a sine wave and a sawtooth like wave. Overmodulation causes chaotic oscillation. It may be useful if you want to simulate feedback FM synths.
freq |
+ The base frequency of the sine oscillator in Hertz. |
feedback |
+ The second argument is the amplitude of phase feedback in radians. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output after any multiplication. |
Take control over those lovely CPU hickups and add some excitement to your live set. But careful with the amount! Need to be trimmed for your particular CPU!
For audio dropouts.
trig |
+ Trigger input. |
spike |
+ Amount (7.0 pushes a spike with avg.cpu ~4600% on a TiBook 1Ghz). |
To mess up sclang's timing.
The SoundFile class is used to check the size, format, channels etc. when the sclang client needs this information about a SoundFile. Soundfile data can be read and modified. Soundfile data can also be read and written incrementally, so with properly designed code, there is no restriction on the file size. +
In most cases you will wish to send commands to the server to get it to load SoundFiles directly into Buffers. You will not need to use this class for this. See the Buffer helpfile.
( +f = SoundFile.new; +f.openRead(Platform.resourceDir +/+ "sounds/a11wlk01.wav"); +f.inspect; +f.close; +)+ +
When reading a sound file, the headerFormat, sampleFormat, numChannels and numFrames variables will be set according to the file on disk. +
When creating a new SoundFile, the format will be monophonic, 44.1 kHz, AIFF, floating-point by default. Users may override the defaults by passing the desired format strings to SoundFile: *openWrite, or by using SoundFile: -headerFormat, SoundFile: -sampleFormat, SoundFile: -numChannels and SoundFile: -sampleRate before calling SoundFile: -openWrite.
Creates a new SoundFile instance.
Try to open the audio file at the given path.
pathName |
+ Full path to the sound file. Use String: -standardizePath to resolve home-folder shortcuts such as |
A new SoundFile instance if successful, or nil
if file open failed. User code should check for nil
before doing anything with the SoundFile object.
Try to create an audio file at the given path. Note that there is no numFrames
argument: the number of frames is counted after writing data into the file.
pathName |
+ Full path to the sound file. Use String: -standardizePath to resolve home-folder shortcuts such as |
headerFormat |
+ A string for the sound file format. Valid strings are listed at SoundFile: -headerFormat. If not given, the default |
sampleFormat |
+ A string for the sample format. Valid strings are listed at SoundFile: -sampleFormat. If not given, the default |
numChannels |
+ An integer number of channels (1 by default). |
sampleRate |
+ An integer sample rate (44100 by default). |
A new SoundFile instance if successful, or nil
if file open failed. User code should check for nil
before doing anything with the SoundFile object.
Returns an Array of SoundFile objects whose paths match the pattern. (The associated files are closed. These objects can be used to cue playback buffers)
SoundFile.collect("sounds/*").do { |f| f.path.postln };+
Reads the data of a SoundFile, evaluates the function (passing the file as argument) and closes it again.
SoundFile.use(Platform.resourceDir +/+ "sounds/a11wlk01.wav", { |f| f.inspect });+
Normalizes a soundfile to a level set by the user. The normalized audio will be written into a second file. +
Using this class method (SoundFile.normalize) will automatically open the source file for you. You may also -openRead the SoundFile yourself and call -normalize on it. In that case, the source path is omitted because the file is already open. +
See instance method -normalize for more information.
Allocates a buffer and cues the SoundFile for playback. Returns an event parameterized to play that buffer. (See NodeEvent for a description of how events can be used to control running synths.) The event responds to play, stop, pause, resume, keeping both the file and buffer open. The file is closed and the buffer is freed when the event is sent a close message.
ev |
+ An Event can passed as an argument allowing playback to be customized using the following keys:
Where bufferSize, firstFrame, lastFrame are for buffer and playback position, and out, server, group, addAction, amp are synth parameters. Here is the default SynthDef used for stereo files: SynthDef(\diskIn2, { | bufnum, out, gate = 1, sustain, amp = 1, ar = 0, dr = 0.01 | + Out.ar(out, DiskIn.ar(2, bufnum) + * Linen.kr(gate, ar, 1, dr, 2) + * EnvGen.kr(Env.linen(0, sustain - ar - dr max: 0 ,dr),1, doneAction: Done.freeSelf) * amp) +});+ + The control sustain determines playback duration based on the firstFrame and lastFrame. The control gate allows early termination of the playback | ||||||||||||||||||||||||||||||
playNow |
+ This is a Boolean that determines whether the file is to be played immediately after cueing. f = SoundFile.collect("sounds/*"); +e = f[1].cue; + +e = f[1].cue( (addAction: 2, group: 1) ); // synth will play ahead of the default group+ | ||||||||||||||||||||||||||||||
closeWhenDone |
+ A flag to indicate wether the |
Read the header of a file. Answers a Boolean whether the read was successful. Sets the -numFrames, -numChannels and -sampleRate. Does not set the -headerFormat and -sampleFormat.
pathName |
+ a String specifying the path name of the file to read. |
Reads the sample data of the file into the raw array you supply. You must have already called -openRead. +
When you reach EOF, the array's size will be 0. Checking the array size is an effective termination condition when looping through a sound file. See the method -channelPeaks for example.
rawArray |
+ The raw array must be a FloatArray. Regardless of the sample format of the file, the array will be populated with floating point values. For integer formats, the floats will all be in the range -1..1. + The size of the FloatArray determines the maximum number of single samples (not sample frames) that will be read. If there are not enough samples left in the file, the size of the array after the readData call will be less than the original size. |
Write the header of a file. Answers a Boolean whether the write was successful.
pathName |
+ a String specifying the path name of the file to write. |
Writes the rawArray to the sample data of the file. You must have already called -openWrite.
rawArray |
+ The raw array must be a FloatArray or Signal, with all values between -1 and 1 to avoid clipping during playback. ( +f = SoundFile.new.headerFormat_("AIFF").sampleFormat_("int16").numChannels_(1); +f.openWrite("sounds/sfwrite.aiff"); + // sawtooth +b = Signal.sineFill(100, (1..20).reciprocal); + // write multiple cycles (441 * 100 = 1 sec worth) +441.do({ f.writeData(b) }); +f.close; +)+ |
answers if the file is open.
closes the file.
the duration in seconds of the file.
Normalizes a soundfile to a level set by the user. The normalized audio will be written into a second file. +
The normalizer may be used to convert a soundfile from one sample format to another (e.g., to take a floating point soundfile produced by SuperCollider and produce an int16 or int24 soundfile suitable for use in other applications).
threaded:true
to get feedback but it will take slightly longer to complete.outPath |
+ a path to the destination file. |
newHeaderFormat |
+ the desired header format of the new file; if not specified, the header format of the source file will be used. |
newSampleFormat |
+ the desired sample format of the new file; if not specified, the sample format of the source file will be used. |
startFrame |
+ an index to the sample frame to start normalizing. |
numFrames |
+ the number of sample frames to copy into the destination file (default nil, or entire soundfile). |
maxAmp |
+ the desired maximum amplitude. Provide a floating point number or, if desired, an array to specify a different level for each channel. |
linkChannels |
+ a Boolean specifying whether all channels should be scaled by the same amount. The default is true, meaning that the peak calculation will be based on the largest sample in any channel. If false, each channel's peak will be calculated independently and all channels will be scaled to maxAmp (this would alter the relative loudness of each channel). |
chunkSize |
+ how many samples to read at once (default is 4194304, or 16 MB). |
threaded |
+ if true, the normalization runs in a routine so that SC can respond (intermittently) while processing. Prevents macOS beachballing. |
Get the pathname of the file. This variable is set via the -openRead or -openWrite calls.
This is a String indicating the header format which was read by openRead and will be written by openWrite. In order to write a file with a certain header format you set this variable.
"AIFF" | Apple/SGI AIFF format |
"WAV","WAVE", "RIFF" | Microsoft WAV format |
"Sun", "NeXT" | Sun/NeXT AU format |
"SD2" | Sound Designer 2 |
"IRCAM" | Berkeley/IRCAM/CARL |
"raw" | no header = raw data |
"MAT4" | Matlab (tm) V4.2 / GNU Octave 2.0 |
"MAT5" | Matlab (tm) V5.0 / GNU Octave 2.1 |
"PAF" | Ensoniq PARIS file format |
"SVX" | Amiga IFF / SVX8 / SV16 format |
"NIST" | Sphere NIST format |
"VOC" | VOC files |
"W64" | Sonic Foundry's 64 bit RIFF/WAV |
"PVF" | Portable Voice Format |
"XI" | Fasttracker 2 Extended Instrument |
"HTK" | HMM Tool Kit format |
"SDS" | Midi Sample Dump Standard |
"AVR" | Audio Visual Research |
"FLAC" | FLAC lossless file format |
"CAF" | Core Audio File format |
Additionally, a huge number of other formats are supported read only. Please note that WAV file support is limited to 4GB. For output of multiple channels or very long recordings we suggest to use W64 (or CAF on macOS).
A String indicating the format of the sample data which was read by -openRead and will be written by -openWrite. libsndfile determines which header formats support which sample formats. This information is detailed at http://www.mega-nerd.com/libsndfile . The possible header formats are:
"int8", "int16", "int24", "int32" |
"mulaw", "alaw", |
"float" |
Not all header formats support all sample formats.
The number of sample frames in the file.
The number of channels in the file.
The sample rate of the file.
// Writing a sound file, long form: +// Set the format variables before calling 'openWrite' +// The Boolean answer from 'openWrite' tells you if it's safe to proceed +( +f = SoundFile(PathName.tmp +/+ "sf-help.wav"); +f.headerFormat = "WAV"; +f.sampleFormat = "int16"; +if(f.openWrite) { + f.writeData(Signal.sineFill(1024, [1])); + f.close; +} { + "Failed to open %".format(f.path).warn; +}; +) + +// Or, short form: Class method 'openWrite' +// f is nil if the file couldn't be opened +( +var p = PathName.tmp +/+ "sf-help.wav"; +f = SoundFile.openWrite(p, "WAV", "int16"); +if(f.notNil) { + f.writeData(Signal.sineFill(1024, [1])); + f.close; +} { + "Failed to open %".format(p).warn; +}; +) + +// Reading the file +f = SoundFile.openRead(PathName.tmp +/+ "sf-help.wav"); +f.sampleFormat; + +// To get data, create a FloatArray or Signal first +d = FloatArray.newClear(f.numFrames); +f.readData(d); +d.plot; +f.close; + +s.boot; + +// It's a proper audio file -- server can load it +b = Buffer.read(s, PathName.tmp +/+ "sf-help.wav"); + +// It's a sinewave... +a = { (PlayBuf.ar(1, b, rate: 440 * 1024/44100, loop: 1) * 0.1).dup }.play; +a.free; + +b.free; +File.delete(PathName.tmp +/+ "sf-help.wav");+ +
Spectral Entropy, with a choice of number of sub-bands. If one band, a measure of general peakiness of the spectral distribution. +
See: SPECTRAL ENTROPY AS SPEECH FEATURES FOR SPEECH RECOGNITION Aik Ming Toh, Roberto Togneri and Sven Nordholm http://www.ee.uwa.edu.au/~roberto/research/theses/tr05-01.pdf +
Pass an FFT in.
fft |
+ input fft chain, that is, from an FFT UGen |
fftsize |
+ Size of FFT buffer must be known in advance for pre-calculation |
numbands |
+ Number of sub-bands for entropy calculation; spectral bins are collected in sub-bands, and the number of outputs of the UGen is numbands |
( +{ + +var in, fft, entropy; + +//in = SinOsc.ar(MouseX.kr(100,1000),0,0.1); +//in = Mix(SinOsc.ar([440,MouseX.kr(440,880)],0,0.1)); +in= SoundIn.ar; + +fft = FFT(LocalBuf(2048), in); + +entropy=SpectralEntropy.kr(fft,2048,1); //one output band (so full spectrum's entropy) + +entropy.poll; + +Out.ar(0,Pan2.ar(0.1*Blip.ar(100,10*(entropy.sqrt)))); +}.play +) + + + + +( +{ + +var in, fft, entropy; +var amplitude; + +in= SoundIn.ar; + +amplitude = Amplitude.kr(in); + +fft = FFT(LocalBuf(1024), in); + +entropy=SpectralEntropy.kr(fft,1024,10);//10 bands + +entropy = entropy * (amplitude.min(0.2)*5.0); //scale by amplitude to avoid low amplitude noise issues + +entropy.poll; + +//Out.ar(0,Pan2.ar(0.1*Blip.ar((entropy[0])*200,entropy[1].sqrt))); + +Out.ar(0,Splay.ar(0.1*Blip.ar((entropy)*200,entropy.sqrt))); +}.play +)+ +
String represents an array of Chars. +
Strings can be written literally using double quotes: + +
A sequence of string literals will be concatenated together: + +
Backslash is the escape character. See Literals: Characters.
Note that, while Char does not support encodings aside from ASCII—such as multi-byte encodings like UTF-8 and UTF-16, or the full Latin-1 (ISO 8859-1) character set—Chars with negative values are perfectly legal, and may be strung together in strings that use these encodings. +
The SuperCollider IDE uses UTF-8 (a superset of ASCII) to decode and display strings, which means that the string "🎹🙄🎻😂🎚️🎛️🎤😍"
can be written in the editor, posted in the post window, and treated for the most part like any other string. However, because non-ASCII UTF-8 characters consist of two or more bytes, and a SuperCollider String's members are one-bit Chars, concepts of size and indexing may not behave intuitively. For instance, the "size
" of the string above is 38, not 8, and the value of its first index is -16
, which is not a valid ASCII value at all but rather the signed 8-bit representation of the first byte of the UTF-8 value of the piano keyboard emoji (🎹), 0xF09F8EB9
.
Read the entire contents of a File and return them as a new String.
Provided for backwards compatibility.
the value of Platform.resourceDir
, which is the base resource directory of SuperCollider.
Please use Platform: *resourceDir instead.
Creates a String representing the name of a note.
name |
+ Name or MIDI pitch of the note to construct. |
alt |
+ Number of half-steps (semitones) to adjust the note by. Equivalent to 100 cents. If this exceeds the |
octave |
+ The octave of the note. |
cents |
+ Number of cents, i.e. one-hundredths of a half-step (semitone) to adjust the note by. |
maxAlt |
+ Maximum number of accidentals to include in the string. Any excess will be translated into cents which can be obtained from the resulting string via the unique Defaults to 2. |
An IdentityDictionary mapping characters representing note names to their offsets within the MIDI octave. For example, $C
maps to 0
, and $D
maps to 2
. Accidentals are not supported.
Strings respond to .at in a manner similar to other indexed collections. Each element is a Char. +
Returns an Array of ASCII values of the Strings's characters. + +
Note that if a string contains multi-byte UTF-8 characters, this array will not be of the same length as the number of visible characters, nor will it necessarily be an array of valid 7-bit ASCII values. +
Returns an integer less than, equal to or greater than zero, depending on whether the receiver should be sorted before the argument, is equal to the argument or should be sorted after the argument. This is a case sensitive compare.
Returns a Boolean whether the receiver should be sorted before the argument. +
Returns a Boolean whether the receiver should be sorted after the argument. + +
Returns a Boolean whether the receiver should be sorted before the argument, including the same string. +
Returns a Boolean whether the receiver should be sorted after the argument, including the same string. +
Returns a Boolean whether the two Strings are not equal. +
Prints the string to the current post window. +
Prints the string and a carriage return to the current post window. +
Prints a formatted string with arguments to the current post window. The % character in the format string is replaced by a string representation of an argument. To print a % character use \\% . +
As -postln, but posts the compileString of the receiver. +
Prepends an error banner and posts the string. +
Prepends a warning banner and posts the string. +
Legacy method (although due to widespread use, it will not be removed). This is identical to postln
.
Compiles a String containing legal SuperCollider code and returns a Function. +
Compile and execute a String containing legal SuperCollider code, returning the result. +
Compile, execute and print the result of a String containing legal SuperCollider code. +
Returns a String formatted for compiling. +
Return a Symbol derived from the String. +
Return an Integer derived from the String. Strings beginning with non-numeric characters return 0. +
Return a Float derived from the String. Strings beginning with non-numeric characters return 0. +
Return a Float based on converting a time string in format (ddd):hh:mm:ss.sss. This is the inverse method to SimpleNumber: -asTimeString. +
Return a concatenation of the two strings. +
Return a concatenation of the two strings with a space between them. +
Concatenates this
and path
, as components of a filesystem path on the host operating system. The strings are joined to avoid duplicate path separators.
+
If this
ends with a path separator and path
begins with one, then the separator in path
is dropped. If there is a path separator on either side, this has the same effect as using ++
. If neither side has a path separator, the platform's preferred separator ('\' on Windows, '/' otherwise) is added.
+
Returns this
and path
concatenated. If path
was a PathName, the result is a PathName; otherwise, it is a String.
path |
+ Any object that can be converted to a string. Typically, either a String, Symbol, or PathName. + |
Concatenate this string with the following args. +
Same as -catArgs, but with spaces in between. +
Same as -catArgs, but with commas in between. +
As -catArgs, -scatArgs and -ccatArgs above, but takes a Collection (usually a List or an Array) as an argument. +
Note the inversion of the arguments:
regexp.matchRegexp(stringToSearch)
stringToSearch.findRegexp(regexp)
(and similar for findAllRegexp
and findRegexpAt
).findRegexp
follows the pattern established by String: -find, where the receiver is the string to be searched. matchRegexp
follows the pattern of matchItem, where the receiver is the pattern to match and the first argument is the object to be tested. This is a common source of confusion, but it is based on this precedent.
POSIX regular expression matching. Returns true if the receiver (a regular expression pattern) matches the string passed to it. The start is an offset where to start searching in the string (default: 0), end where to stop.
regexp.matchRegexp(stringToSearch)
and not the other way around! See above: String: Regular expressions.POSIX regular expression search. +
Like -findAll, but use regular expressions. So unlike findRegexp, it will just return the indices of the +
Match a regular expression at the given offset, returning the match and the length of the match in an Array, or nil if it doesn't match. The match must begin right at the offset. +
Returns the index of the string in the receiver, or nil if not found. If ignoreCase is true, find makes no difference between uppercase and lowercase letters. The offset is the point in the string where the search begins. string may be a String or a Char. +
Same like -find, but starts at the end of the string. +
Returns the indices of the string in the receiver, or nil if not found. +
Returns a Boolean indicating if the String contains string. +
Same as -contains, but case insensitive. +
Returns a Boolean indicating if the String contains string beginning at the specified index. +
Same as -containsStringAt, but case insensitive.
Returns true if this string begins/ends with the specified other string.
string |
+ The other string |
A Boolean
Rotate the string by n steps. +
Randomize the order of characters in the string. +
Like -tr, but with Strings as well as Chars as arguments. +
Returns a formatted string with arguments. The % character in the format string is replaced by a string representation of an argument. To print a % character use \\% . +
Add the escape character (\) before any character of your choice. +
Return this string enclosed in double-quote ( "
) characters.
+
Return this string enclosed in space characters. +
Return this string followed by dashes in the next line ( -
).
+
Transliteration. Replace all instances of from with to. +
Pad this string with string so it fills size character.
size |
+ Number of characters to fill |
string |
+ Padding string + |
Return this string with uppercase letters. +
Return this string with lowercase letters. +
Returns a new String with all RTF formatting removed. +
Returns an Array of Strings split at the separator. The separator is a Char, and is not included in the output array. +
Print the String on stream. +
Same as -printOn, but formatted -asCompileString. +
Where relevant, the current working directory is the same as the location of the SuperCollider app and the shell is the Bourne shell (sh). Note that the cwd, and indeed the shell itself, does not persist: + +
It is however possible to execute complex commands: + +
Also on os x applescript can be called via osascript: + +
Should you need an environment variable to persist you can use -setenv.
Execute a UNIX command asynchronously using the standard shell (sh).
action |
+ A Function that is called when the process has exited. It is passed two arguments: the exit code and pid of the exited process. |
postOutput |
+ A Boolean that controls whether or not the output of the process is displayed in the post window. |
An Integer - the pid of the newly created process. Use Integer: -pidRunning to test if a process is alive.
Example: +
Similar to -unixCmd except that the stdout of the process is returned (synchronously) rather than sent to the post window. +
Executes a UNIX command synchronously using the standard shell (sh).
Error code of the UNIX command
Execute the String in a new terminal window (asynchronously).
shell |
+ The shell used to execute the string. |
Example: +
Set the environment variable indicated in the string to equal the String value. This value will persist until it is changed or SC is quit. Note that if value is a path you may need to call -standardizePath on it. +
Returns the value contained in the environment variable indicated by the String. +
Set the environment variable to nil.
Make a directory from the given path location.
Returns an Array containing all paths matching this String. Wildcards apply, non-recursive. +
Load and execute the file at the path represented by the receiver.
Perform -pathMatch on this String, then load and execute all paths in the resultant Array. +
warn |
+ Post a warning if path doesn't point to any file. |
action |
+ If a function is passed, it is called with each path as argument. |
Load and execute the file at the path represented by the receiver, interpreting the path as relative to the current document or text file. Requires that the file has been saved. This can be used e.g. to load initialization code from files in the same folder.
warn |
+ Warn if a file is not found. |
action |
+ A function that is called for each file path that is found. |
Convert the receiver from a relative path to an absolute path, relative to the current document or text file. Requires that the current text file has been saved. Absolute paths are left untransformed.
Expand ~ to your home directory, and resolve aliases on macOS. See PathName for more complex needs. See File: *realpath if you want to resolve symlinks. +
Open file, directory or URL via the operating system. On macOS this is implemented via open
, on Linux via xdg-open
and on Windows via start
.
+
Also see -+/+ for path concatenation. +
The term "path separator" is a platform-independent term for the character(s) that can be used to separate components of a path. On Windows, both forward slash "/" and backward slash "\\" are path separators. On POSIX-based systems like macOS and Linux, only forward slash is allowed.
Return a new string suitable for use as a filename in a shell command, by enclosing it in single quotes ( '
). If the string contains any single quotes they will be escaped.
You should use this method on a path before embedding it in a string executed by -unixCmd or -systemCmd. + +
Return this path as an absolute path by prefixing it with File: *getcwd if necessary.
Return this path as relative to the specified path.
relativeTo |
+ The path to make this path relative to. |
Appends a path separator if one is not already present.
Removes a trailing path separator if one is present.
Return the filename from a Unix path. +
Return the directory name from a Unix path. +
Split off the extension from a filename or path and return both in an Array as [path or filename, extension]. +
Parse this string as YAML/JSON.
A nested structure of Arrays (for sequences), Dictionaries (for maps) and Strings (for scalars).
Same as parseYAML
but parse a file directly instead of a string. This is faster than reading a file into a string and then parse it.
Create a new Document with this. +
Create a new Document from the path corresponding to this. The selection arguments will preselect the indicated range in the new window. Returns this. +
Returns the path for the helpfile named this, if it exists, else returns nil. +
Performs -findHelpFile on this, and opens the file it if it exists, otherwise opens the main helpfile. +
Returns class StringInspector.
The following methods must be called within an Window-drawFunc or a SCUserView-drawFunc function, and will only be visible once the window or the view is refreshed. Each call to Window-refresh SCUserView-refresh will 'overwrite' all previous drawing by executing the currently defined function. +
See also: Window, UserView, Color, and Pen.
Pen.stringAtPoint, Pen.stringInRect, Pen.stringCenteredIn, Pen.stringLeftJustIn, Pen.stringRightJustIn
instead.Draws the String at the current 0@0 Point. If not transformations of the graphics state have taken place this will be the upper left corner of the window. See also Pen. +
Draws the String into the given Rect using the Font and Color specified. +
Draws the String into the given Rect using the Font and Color specified. +
Tries to return a Rect with the size needed to fit this string if drawn with given font.
font |
+ A Font |
Strips whitespace at the beginning and end of the string.
The stripped string
Like -unixCmdGetStdOut but returns the lines in an Array instead.
an Array of each line of output
Same as String: *notesDict.
Converts a note name to a MIDI note pitch.
cents |
+ Number of cents to adjust |
Float corresponding to a MIDI note pitch
-1 / 1 / 2 if the String contains b
/ #
/ x
respectively, or 0 otherwise.
Memoizes the result. +
Integer corresponding to the numerical component of the note.
Does not take into account accidentals shifting the pitch into a different octave. This is probably a bug! +
Memoizes the result. +
This is a SuperCollider variant of Dirt, the sound engine for the Tidal http://tidalcycles.org/ programming language by Alex McLean. Most of SuperDirt can be rewritten at runtime: you can add and modify effects, synth defs, load samples incrementally, and spatialise to any number of channels. You can use SuperCollider as usual.
Many examples can be found in the folders: hacks/
and scripts/
+
The examples suppose that environment variables are accessible. If you want to use ProxySpace or similar redirect environments, see below.
Return a new instance, ready to be started
numChannels |
+ The number of channels of the internal bus (this is also the maximal number of output channels). |
server |
+ Server to play on. |
Specifies the maximum number of channels your sound files have (default is 2: stereo samples)
Convenience method that gives defaults for many situations. It configures the server and starts SuperDirt with a number of orbits. It allows you to pass a few parameters:
numChannels |
+ The number of channels of the SuperDirt instance (see above). |
server |
+ Server to play on. |
numOrbits |
+ number of orbits to create on startup (see: DirtOrbit). You can add or remove them later. |
port |
+ The port to listen on (default is 57120) |
senderAddr |
+ The address to listen to (a String). |
path |
+ A path that is used to load some sound files. This is a string, a path pointing to a number of files, which usually contains a wildcard ( |
Post a string with the tidal language parameter definitions for a number of SynthDefs. Duplicates are removed automatically. Note that these all use (Nothing)
, because defaults are defined in the SynthDef default arguments.
+
Example: let (freq, _) = pF "freq" (Nothing)
+
synthNames |
+ A symbol or array of symbols of the SynthDef names for which to generate the tidal code. |
excluding |
+ The parameter names which should be excluded. Names already defined and used internally by SuperDirt will be excluded in additinon tho these. |
A list of DirtOrbits. Orbits are the basic elements of dirt. Each has its own global effects and global settings. You can start any number of orbits any time. From tidal, you can direct sounds into an orbit by the parameter: # orbit "7"
(this sends to orbit 7, if it exists, otherwise it'll wrap to whatever is available.)
+
Read a number of sound file from disk and load them into the server memory. The buffer information is also kept in buffers, a dictionary. You can do this any time while running, and incrementally add more files. +
Given a path like: "path/to/my/basedrum/*"
.
+
The dictionary will contain under the key 'basedrum'
an Array of all the Buffers from the samples found in the folder.
+
From tidal, they can be accessed e.g. by d1 $ sound $ "basedrum:1 basedrum:4"
, or alternatively by the n
parameter. Sample numbers below zero or above the number of samples in the folder will wrap, i.e. if you have a folder of 4 samples, 5 will play 0.
paths |
+ Specifies paths for sound files. This is a string, a path pointing to a number of files, which usually contains a wildcard ( Alternatively, you can pass in an Array of full paths, e.g. |
appendToExisting |
+ If set to true, this will keep existing dictionary keys (see above) and add the new samples to any existing ones. This allows you to load different folders with the same name. Otherwise it will only keep those existing names which are not found in the new set of samples. |
namingFunction |
+ Provide a function to generate your own instrument names, which you call from tidal. The function is passed the folder path (a String). By default, this function is |
Read a single folder of sound files and add all of them under 'name'
to the sample instruments. You can do this any time while running, and incrementally add more files.
folderPath |
+ The path for the folder. |
name |
+ The name of the sample instrument as it will be used in tidal. Subsequent sound files can be addressed by passing the "n" parameter, e.g. |
appendToExisting |
+ See: -loadSoundFiles + |
Read a single sound file and add it under 'name'
to the sample instruments. You can do this any time while running, and incrementally add more files.
path |
+ The path for the file. |
name |
+ The name of the sample instrument as it will be used in tidal |
appendToExisting |
+ See: -loadSoundFiles + |
Read a number of named folders. You can do this any time while running, and incrementally add more files.
names |
+ An array of symbols or strings. These are folder names and at the same time instrument names for tidal. |
path |
+ The path in which the folders can be found. |
appendToExisting |
+ See: -loadSoundFiles + |
Remove sound files and free their memory.
names |
+ An array of sample instrument names which to remove. + |
Remove all sound files and free their memory.
A list of valid sound file extensions, which can be extended (default: ["wav", "aif", "aiff", "aifc"]
). In theory, all libsndfile formats should be supported http://www.mega-nerd.com/libsndfile/#Features.
Post a list of all existing sample names, the number of variants, the range of durations, and memory requirement. +
e.g. +
circus (3) 0.17 - 0.52 sec (171 kB)
.
+
Load a number of files, usually containing SynthDefs.
path |
+ This is a string, a path pointing as´file or a to a number of files, which then contains a wildcard ( |
This method starts SuperDirt. It creates a number of DirtOrbits (each of which has global effects, output busses, and settings). It also opens a network connection.
port |
+ The port to listen on (default is 57120) |
outBusses |
+ An (a Array) of audio output channels: it determines how many DirtOrbits there will be. |
senderAddr |
+ The address to listen to (a String). |
End all audio processes and close network responder.
End all audio processes, close network responder, and free sound file resources.
All startup files and examples use the environment variables, like ~dirt
. If you want to use ProxySpace or similar redirect environments, you should bind these to their respective file (which needs to be saved to disk):
+
Starts a linear raise by rate/sec from zero when trig input crosses from non-positive to positive. +
When rate == 1, Sweep may be used to get a continually-updating measurement of the time (in seconds) since the last trigger.
trig |
+ triggers when trig input crosses from non-positive to positive. |
rate |
+ rate/sec raise rate |
// using sweep to modulate sine frequency +( +{ var trig; + trig = Impulse.kr(MouseX.kr(0.5, 20, 1)); + SinOsc.ar(Sweep.kr(trig, 700) + 500, 0, 0.2) +}.play; +) + + +// using sweep to index into a buffer +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); + +( +{ var trig; + trig = Impulse.kr(MouseX.kr(0.5, 10, 1)); + BufRd.ar(1, b, Sweep.ar(trig, BufSampleRate.ir(0))) +}.play; +) + +// backwards, variable offset +( +{ var trig, pos, rate; + trig = Impulse.kr(MouseX.kr(0.5, 10, 1)); + rate = BufSampleRate.ir(0); + pos = Sweep.ar(trig, rate.neg) + (BufFrames.ir(0) * LFNoise0.kr(0.2)); + BufRd.ar(1, b, pos) +}.play; +) + +// raising rate +( +{ var trig, rate; + trig = Impulse.kr(MouseX.kr(0.5, 10, 1)); + rate = Sweep.kr(trig, 2) + 0.5; + BufRd.ar(1, b, Sweep.ar(trig, BufSampleRate.ir(0) * rate)) +}.play; +) + +b.free+ +
Sweep can be used as a resettable Phasor or Line - one that can start, pause, resume and stop. To get a resettable XLine behavior change the linlin
to linexp
in the SynthDef below.
( +SynthDef(\lineReset, { |out, start= 0, end= 1, dur= 1, t_trig= 1, run= 1| + var phasor = Sweep.ar(t_trig, run / dur).linlin(0, 1, start, end, \minmax); + phasor.poll; + Out.ar(out, SinOsc.ar(phasor, 0, 0.2)); +}).add; +) +a = Synth(\lineReset, [\start, 400, \end, 800, \dur, 2]) +a.set(\t_trig, 1) +a.set(\run, 0) +a.set(\run, 1) +a.set(\t_trig, 1) +a.free + +//shorter duration and downwards... +a= Synth(\lineReset, [\start, 1000, \end, 500, \dur, 0.5]) +a.set(\t_trig, 1) +a.set(\run, 0) +a.set(\run, 1) +a.set(\t_trig, 1) +a.free+ +
The server application uses synth definitions as templates for creating Synth nodes. (Methods such as Function-play, etc. are simply conveniences which automatically create such a def.) The SynthDef class encapsulates the client-side representation of a given def, and provides methods for creating new defs, writing them to disk, and streaming them to a server. +
SynthDef is one of the more complicated classes in SC and an exhaustive explanation of it is beyond the scope of this document. As such, the examples at the bottom of this document and those found in the various tutorials accessible from Help may be necessary to make some aspects of its use clear.
The core of a def is its unit generator graph function. This is an instance of Function which details how the def's unit generators are interconnected, its inputs and outputs, and what parameters are available for external control. In a synth based on the def, arguments to the function will become instances of Control. These can have default values, or can be set at the time the synth is created. After creation they will be controllable through Node's set
and setn
methods, or the n_set and n_setn OSC messages.
+
There are four special types of arguments, which are treated differently:
a_input
), or that are specified as \ar
in the def's rates argument (see below), will be able to read an audio rate bus when mapped to it with /n_mapa
.i_freq
), or that are specified as \ir
in the def's rates argument (see below), will be static and non-modulatable. They will not respond to /n_set
or /n_map
. This is slightly more efficient in terms of CPU than a regular arg.t_trig
), or that are specified as \tr
in the def's rates argument (see below), will be made as a TrigControl. Setting the argument will create a control-rate impulse at the set value. This is useful for triggers./n_setn
. When setting such controls no bounds checking is done, so you are responsible for making sure that you set the correct number of arguments.See the examples below for more detail on how this works. +
Certain argument names (such as 'out' to specify an out bus) are in such common use that adopting them might be said to constitute 'good style'. One of these, 'gate' when used to control the gate input of an EnvGen, deserves special mention, as it allows one to use Node's release method. See Node for an example and more detail.
It is important to understand that although a single def can provide a great deal of flexibility through its arguments, etc., it is nevertheless a static entity. A def's UGen graph function (and the SC code within it) is evaluated only when the def is created. Thus statements like while, do, collect etc. will have no further effect at the time the def is used to create a Synth, and it is important to understand that a UGen graph function should not be designed in the same way as functions in the language, where multiple evaluations can yield different results. It will be evaluated once and only once.
if
is implemented as a linear signal crossfade when the receiver is an UGenThere are other ways of achieving similar results, however, often using UGens such as Rand. For example, the following def will have a single randomly generated frequency, which will be the same for every Synth based on it:
( +SynthDef(\help_notRand, { |out| + Out.ar(out, + SinOsc.ar(rrand(400, 800), 0, 0.2) * Line.kr(1, 0, 1, doneAction: Done.freeSelf) + ) +}).add; +) +a = Synth(\help_notRand); +b = Synth(\help_notRand); // the same freq as a+ +
This one on the other hand will have a different random freq for each Synth created:
( +SynthDef(\help_isRand, { |out| + Out.ar(out, + SinOsc.ar(Rand(400, 800), 0, 0.2) * Line.kr(1, 0, 1, doneAction: Done.freeSelf) + ) +}).add; +) +a = Synth(\help_isRand); +b = Synth(\help_isRand); // a different randomly selected freq+
Create a SynthDef instance, evaluate the ugenGraphFunc and build the ugenGraph.
name |
+ A String or Symbol (i.e. "name" or \name). This name will be used to refer to the SynthDef when creating a Synth based upon it, and should be unique. |
ugenGraphFunc |
+ An instance of Function specifying how the def's UGens are interconnected. See the discussion above for information on how the Function's arguments are specified. |
rates |
+ An optional Array of specifications for the ugenGraphFunc's arguments. The order corresponds to the order of arguments. See the examples below to see how these are used. + A specification can be:
|
prependArgs |
+ An optional Array of objects which will be passed as the first arguments to the ugenGraphFunc when it is evaluated. Arguments which receive values in this way will not be converted to instances of Control. See the |
variants |
+ An optional Event containing default argument settings. These can override the defaults specified in the ugenGraphFunc. When creating a Synth a variant can be requested by appending the defName argument in the form 'name.variant' or "name.variant". See example below. |
metadata |
+ An optional Event containing additional, user-defined information that is relevant to the use of the SynthDef in the client. The SynthDef itself is sent to the server for audio rendering; metadata are strictly client-side descriptive information. Currently the 'specs' key in the event is reserved for ControlSpecs to be associated with SynthDef arguments (this is useful for automatic GUI construction). Metadata can be persisted to disk and loaded automatically as part of a SynthDesc. See the SynthDesc help file for more details. |
Wraps a function within an enclosing synthdef.
Arguments to the wrapped function are automatically promoted to be SynthDef controls, using the same rules applied to arguments of the main UGen function. For a very simple example:
d = SynthDef(\demoWrapping, { |out| + Out.ar(out, SynthDef.wrap({ |freq| SinOsc.ar(freq) })) +}); + +d.allControlNames;+ +
Prints: [ ControlName P 0 out control 0, ControlName P 1 freq control 0 ]
.
+
The outer function declares the argument 'out', and the wrapped function has 'freq' as its argument. The resulting SynthDef has both arguments as controls, exactly as if the outer function included both as arguments. +
The rates array behaves as described earlier. PrependArgs
allows values or unit generators to be passed into the inner function from the enclosing SynthDef context. Any inner function argument that receives a prependArg value (including nil) will use that value, suppressing creation of a control for that argument. The longer example below demonstrates this technique.
+
This is very useful for mass-producing SynthDefs that have a common "shell" defining features such as enveloping or triggering mechanisms that apply to different subgraphs of unit generators. The common features need be written only once; the UGens that differ between the SynthDefs are plugged into the supporting architecture.
Get or set the default directory to which defs are written.
Remove the synthdef name
from the SynthDescLib named libname
and from its servers.
Create a new SynthDef. It is written to disk only if a def file with this name does not already exist. Note that this will not check for differences, so you will need to delete the defFile to get it to rebuild. Default for dir is to use the path specified by SynthDef.synthDefDir
.
SynthDef.writeOnce
is a legacy method. Its main use was to improve the efficiency of SynthDefs in quarks, but this is superseded by SynthDescLib. Being completely impervious to changes, it can cause difficult-to-diagnose bugs (such as having version 1.1 of a quark but with a SynthDef stuck in version 1.0). Quark developers should now use -add instead.
+The exception is very large SynthDefs, where you have a choice between -writeDefFile and this method. Even then, the efficiency savings of writeOnce
are only in disk I/O -- both methods build the SynthDef every time they run.
Adds the synthdef to the SynthDescLib specified by libname, and sends it to the library's servers. No defFile is written; all operations take place in memory.
After using this method, the synthdef can be used with event streams as in store()
, but without the permanent artifact of a file on disk. Calling this method triggers an update message with the key \synthDescAdded
for any dependants the library may have. This can be used to trigger additional behaviour every time a def/desc is added. See Object: Dependancy.
+
A server can be added by SynthDescLib.global.addServer(server)
.
+
Note that the "dir" and "mdPlugin" arguments do not exist for this method. Because no file is written, there is no need to specify a directory or write a metadata file.
( +SynthDef(\help_synth, { |out, freq = 800, sustain = 1, amp = 0.1| + Out.ar(out, + SinOsc.ar(freq, 0, 0.2) * Line.kr(amp, 0, sustain, doneAction: Done.freeSelf) + ) +}).add; +)+
Return this def's name.
Return this def's ugenGraphFunc.
Return an Event containing this def's variants.
An array of ControlName's for the controls.
(for most purposes, the method add is recommended)
Writes the def as a file called name.scsyndef in a form readable by a server. Default for dir is synthdefs/. Defs stored in the default directory will be automatically loaded by the local and internal Servers when they are booted.
Write the defFile and send a message to server to load this file. When this asynchronous command is completed, the completionMessage (a valid OSC message) is immediately executed by the server. Default for dir is synthdefs/.
Compile the def and send it to server without writing to disk (thus avoiding that annoying SynthDef buildup). When this asynchronous command is completed, the completionMessage (a valid OSC message) is immediately executed by the server.
Write the defFile and store it in the SynthDescLib specified by libname, and send a message to the library's server to load this file. When this asynchronous command is completed, the completionMessage (a valid OSC message) is immediately executed by the server. Default for libname is \global, for dir is synthdefs/. This is needed to use defs with the event stream system. See Streams and Pattern.
libname |
+ name of the SynthDescLib |
dir | |
completionMsg | |
mdPlugin |
+ (optional) The metadata plug-in class that will be used to persist metadata. If not supplied, the default plug-in is used. See the SynthDesc help file for details. |
A convenience method which compiles the def and sends it to target's server. When this asynchronous command is completed, it create one synth from this definition, using the argument values specified in the Array args. For a list of valid addActions see Synth. The default is \addToHead.
a corresponding Synth object.
// Note that constructions like SynthDef(...) and Synth(...) are short for SynthDef.new(...), etc. +// With SynthDef it is common to chain this with calls on the resulting instance, +// e.g. SynthDef(...).add or SynthDef(...).play + +// make a simple def and send it to the server + +s.boot; +SynthDef(\SimpleSine, {|freq = 440, out| Out.ar(out, SinOsc.ar(freq, 0, 0.2)) }).add; + +// the above is essentially the same as the following: +d = SynthDef(\SimpleSine, {|freq = 440, out| Out.ar(out, SinOsc.ar(freq, 0, 0.2)) }); +d.add; + +// now make a synth from it, using the default value for freq, then another with a different value +x = Synth(\SimpleSine); +y = Synth(\SimpleSine, [\freq, 660]); + +// now change the freq value for x +x.set(\freq, 880); + +x.free; y.free; + +// using the play convenience method +x = SynthDef(\SimpleSine, {|freq = 440, out| Out.ar(out, SinOsc.ar(freq, 0, 0.2)) }).play +x.free;+
// the following two defs are equivalent. The first uses a 't_' arg: +( +SynthDef(\trigTest, { |out, t_trig=0, freq=440| // t_trig creates a TrigControl + Out.ar(out, SinOsc.ar(freq+[0,1], 0, Decay2.kr(t_trig, 0.005, 1.0))); +}, [0, 4] // lag the freq by 4 seconds (the second arg), but not t_trig (won't work anyway) +); +) + +// This second version makes trig a \tr arg by specifying it in the rates array. +( +SynthDef(\trigTest2, { |out, trig=0, freq=440| + Out.ar(out, SinOsc.ar(freq+[0,1], 0, Decay2.kr(trig, 0.005, 1.0))); + }, [\tr, 4] // lag the freq (lagtime: 4s), \tr creates a TrigControl for trig +).add; +) + +// Different way of writing the same thing +( +SynthDef(\trigTest2, { |out| + Out.ar(out, SinOsc.ar(\freq.kr(440, 4) + [0,1], 0, Decay2.kr(\trig.tr, 0.005, 1.0))); +}).add; +) + + +// Using the second version create a synth +z = Synth.head(s, \trigTest2); + +// now trigger the decay envelope +z.set(\trig, 1); // you can do this multiple times +z.set(\trig, 1, \freq, 220); // hear how the freq lags +z.set(\trig, 1, \freq, 880); + +z.free; //free the synth+
// create a def with some variants +( +SynthDef(\vartest, { |out, freq = 440, amp = 0.2, a = 0.01, r = 1| + // the EnvGen with doneAction: Done.freeSelf frees the synth automatically when done + Out.ar(out, SinOsc.ar(freq, 0, EnvGen.kr(Env.perc(a, r, amp), doneAction: Done.freeSelf))); +}, variants: (alpha: [a: 0.5, r: 0.5], beta: [a: 3, r: 0.01], gamma: [a: 0.01, r: 4]) +).add; +) + +// now make some synths. First using the arg defaults +Synth(\vartest); + +// now the variant defaults +Synth('vartest.alpha'); +Synth('vartest.beta'); +Synth('vartest.gamma'); + +// override a variant +Synth('vartest.alpha', [\release, 3, \freq, 660]);+
// freqs has a literal array of defaults. This makes a multichannel Control of the same size. +( +SynthDef(\arrayarg, { |out, amp = 0.1, freqs = #[300, 400, 500, 600], gate = 1| + var env, sines; + env = Linen.kr(gate, 0.1, 1, 1, 2) * amp; + sines = SinOsc.ar(freqs +.t [0,0.5]).cubed.sum; // A mix of 4 oscillators + Out.ar(out, sines * env); +}, [0, 0.1, 0]).add; +) + +x = Synth(\arrayarg); +x.setn(\freqs, [440, 441, 442, 443]); + +// Don't accidentally set too many values, or you may have unexpected side effects +// The code below inadvertently sets the gate arg, and frees the synth +x.setn(\freqs, [300, 400, 500, 600, 0]); + +// Mr. McCartney's more complex example +( +fork { + z = Synth(\arrayarg); + + 2.wait; + 10.do { + z.setn(\freqs, {exprand(200,800.0)} ! 4); + (2 ** (0..3).choose * 0.2).wait; + }; + + z.set(\amp, -40.dbamp); + + 10.do { + z.setn(\freqs, {exprand(200,800.0)} ! 4); + (2 ** (0..3).choose * 0.2).wait; + }; + 2.wait; + + z.release; +}; +)+
// The makeEffect function below wraps a simpler function within itself and provides +// a crossfade into the effect (so you can add it without clicks), control over wet +// and dry mix, etc. +// Such functionality is useful for a variety of effects, and SynthDef-wrap +// lets you reuse the common code. +( +// the basic wrapper +~makeEffect = { |name, func, lags, numChannels = 2| + + SynthDef(name, { | i_bus = 0, gate = 1, wet = 1| + var in, sound, env, lfo; + in = In.ar(i_bus, numChannels); + env = Linen.kr(gate, 2, 1, 2, 2); // fade in the effect + + // call the wrapped function. The in and env arguments are passed to the function + // as the first two arguments (prependArgs). + // Any other arguments of the wrapped function will be Controls. + sound = SynthDef.wrap(func, lags, [in, env]); + + XOut.ar(i_bus, wet * env, sound); + }, [0, 0, 0.1] ).add; + +}; +) + +// now make a wah +( +~makeEffect.value(\wah, { |in, env, rate = 0.7, ffreq = 1200, depth = 0.8, rq = 0.1| + // in and env come from the wrapper. The rest are controls + var lfo; + lfo = LFNoise1.kr(rate, depth * ffreq, ffreq); + RLPF.ar(in, lfo, rq, 10).distort * 0.15; +}, +[0.1, 0.1, 0.1, 0.1], // lags for rate ffreq, depth and rq +2 // numChannels +); +) + +// now make a simple reverb +( +~makeEffect.value(\reverb, {|in, env| + // in and env come from the wrapper. + var input; + input = in; + 16.do({ input = AllpassC.ar(input, 0.04, Rand(0.001,0.04), 3)}); + input +}, +nil, // no lags +2 // numChannels +); +) + +// something to process +x = { { Decay2.ar(Dust2.ar(3), mul: PinkNoise.ar(0.2)) } ! 2}.play; + +y = Synth.tail(s, \wah); +z = Synth.tail(s, \reverb, [\wet, 0.5]); + +// we used an arg named gate, so Node-release can crossfade out the effects +y.release; + +// setting gate to zero has the same result +z.set(\gate, 0); + +x.free;+
// arguments named 'out' and 'gate' are commonly used to specify an output bus and +// EnvGen gate respectively. Although not required, using them can help with consistency +// and interchangeability. 'gate' is particularly useful, as it allows for Node's release +// method. +( +SynthDef(\synthDefTest, {|out, gate=1, freq=440| + // doneAction: Done.freeSelf frees the synth when EnvGen is done + Out.ar(out, SinOsc.ar(freq) * EnvGen.kr(Env.asr(0.1, 0.3, 1.3), gate, doneAction: Done.freeSelf)); +}).store; // use store for compatibility with pattern example below +) + +x = Synth(\synthDefTest, [\out, 0]); // play out through hardware output bus 0 (see Out.help) +x.release; // releases and frees the synth (if doneAction is > 2; see EnvGen) + +//equivalent: + +x = Synth(\synthDefTest); // out defaults to zero, if no default arg is given. +x.set(\gate, 0); + +// if value is negative, it overrides the release time, to -1 - gate +x = Synth(\synthDefTest); +x.set(\gate, -5); // 4 second release + +//equivalent: +x = Synth(\synthDefTest); +x.release(4); + +// if the out arg is used in a standard way, it can always be changed without knowing the synth def +x = Synth(\synthDefTest, [\out, 0]); +x.set(\out, 1); //play through channel 1 +x.release; + +// Another good example of this is with patterns, which can use gate to release notes +( +Pbind( + \instrument, \synthDefTest, + \freq, Pseq([500, 600, Prand([200, 456, 345],1)], inf), + \legato, Pseq([1.5, 0.2], inf), + \dur, 0.4, + \out, Pseq([0, 1], inf) +).play; +)+
SystemClock is more accurate than AppClock, but cannot call GUI primitives. +
See Clock for general explanation of how clocks operate.
The float you return specifies the delta to resched the function for. Returning nil will stop the task from being rescheduled.
( +SystemClock.sched(0.0,{ arg time; + time.postln; + rrand(0.1,0.9); +}); +)+ +
( +SystemClock.sched(2.0,{ + "2.0 seconds later".postln; + nil; +}); +)+
Clear the SystemClock's scheduler to stop it.
SystemClock.clear;+
( +SystemClock.schedAbs( (thisThread.seconds + 4.0).round(1.0),{ arg time; + ("the time is exactly " ++ time.asString + ++ " seconds since starting SuperCollider").postln; +}); +)+
Calls to the GUI may not be made directly from actions triggered by SystemClock or incoming socket messages (OSCFunc). +
To get around this, use { }.defer
. This will execute the function using the AppClock and is equivalent to AppClock.sched(0, function)
( +var w, r; +w = Window.new("trem", Rect(512, 256, 360, 130)); +w.front; +r = Routine({ arg time; + 60.do({ arg i; + 0.05.yield; + { + w.bounds = w.bounds.moveBy(10.rand2, 10.rand2); + w.alpha = cos(i*0.1pi)*0.5+0.5; + }.defer; + }); + 1.yield; + w.close; +}); +SystemClock.play(r); +)+ +
This example is only to show how to make calls to Cocoa/GUI when scheduling with the SystemClock. If you only wish to control the GUI, use AppClock.
Implementation of the McAulay and Quatieri 1986 sinusoidal model, as described in: +
McAulay, R. and Quatieri, T. (1986) "Speech analysis/Synthesis based on a sinusoidal representation". IEEE Transactions on Acoustics, Speech, and Signal Processing 34(4): 744--754
chain |
+ [fft] - Audio input to track, which has been pre-analysed by the FFT UGen; see examples below for the expected FFT size |
windowsize |
+ Window size used for FFT |
hopsize |
+ hop size for FFT, typically half window size |
maxpeaks |
+ Absolute maximum number of allowed peaks to be detected in the spectrum |
currentpeaks |
+ Current number of allowed peaks to be detected in the spectrum |
freqmult |
+ Resynthesis parameter to change frequency; currently causes a gross multiplication of frequency of all sinusoidal components, no envelope compensation |
tolerance |
+ Search area for matching peaks; within tolerance spectral bins |
noisefloor |
+ Minimum magnitude for a candidate peak |
b.free + +//assumes hop of half fftsize, fine +b = Buffer.alloc(s,1024,1); //for sampling rates 44100 and 48000 +//b = Buffer.alloc(s,2048,1); //for sampling rates 88200 and 96000 + +//d=Buffer.read(s,"sounds/a11wlk01.wav"); +d=Buffer.read(s,"sounds/break"); + + +( +{ + +var in, fft, output; + +in= SoundIn.ar(0); //PlayBuf.ar(1,d,BufRateScale.kr(d),1,0,1); + +fft = FFT(b, in, wintype:1); + +output=TPV.ar(fft, 1024, 512, 70,MouseX.kr(1,70), MouseY.kr(0.25,3),4,0.2); + +//Out.ar(0,Pan2.ar(output)); +output +}.play +) + +( +{ + +var in, fft, output; + +in= PlayBuf.ar(1,d,BufRateScale.kr(d),1,0,1); + +fft = FFT(b, in, wintype:1); + +output=TPV.ar(fft, 1024, 512, 50,50,1.0,MouseX.kr(0.1,100),MouseY.kr(-20,40).dbamp); + +//Out.ar(0,Pan2.ar(output)); +output +}.play +) + + +( +{ + +var in, fft, output; + +in= PlayBuf.ar(1,d,BufRateScale.kr(d),1,0,1); + +fft = FFT(b, in); + +output=TPV.ar(fft, 1024, 512, 1,1); + +output +//Out.ar(0,output); +}.plot(0.05, s, nil, -1.5, 1.5); +) + + + + + + +//residual + +b = Buffer.alloc(s,1024,1); //for sampling rates 44100 and 48000 + +d=Buffer.read(s,"sounds/break"); + + +( +{ + +var in, fft, output; +var input,inputAmp,threshhold,gate; + +in= SoundIn.ar(0); //PlayBuf.ar(1,d,BufRateScale.kr(d),1,0,1); +//inputAmp = Amplitude.kr(input); +//threshhold = 0.001; // noise gating threshold +//gate = Lag.kr(inputAmp > threshhold, 0.01); +//in= (input * gate); + +fft = FFT(b, in, wintype:1); + +output=TPV.ar(fft, 1024, 512, 50,50,1.0,4,0.2); + +//Out.ar(0,Pan2.ar(output)); +//delay by 512 samples for output, then phase measurement is from centre of frame, so +output - DelayN.ar(in,0.1, (512+512)/44100) +}.play +)+ +
Tdef registers tasks by key. All accesses to the registered tasks go through the Tdef class via that key. Registered tasks can be replaced with other tasks while playing. The old task and its replacement can automatically crossfade and the replacement time can be quantized. +
Tdef provides an interface to its superclass TaskProxy. Tdef keeps a reference to a task ( time pattern ) that can be replaced while playing. It continues playing when the old stream ended and a new stream is set and schedules the changes to the beat. One Tdef may be used in many tasks in different places. A change in the task definition Tdef propagates through all tasks. + +
Graphical overview over all current Tdefs: TdefAllGui. Overview: JITLib
Store the task in a global dictionary under key, replacing its routine function with the new one. +
Using *new(key) you can access the pattern at that key (if none is given, a default task is created)
Default source, if none is given. The default task has a function that waits in 1.0 beat steps and does nothing.
Remove all proxies from the global dictionary ( *all )
Clear all proxies, setting their source to silence.
Set or return the environment ( IdentityDictionary ) that stores all instances.
Set the default quantisation for new instances (default: 1.0). This can be an array [quant, phase, timingOffset, outset]
One Tdef may have many tasks in different places. A change in the task definition Tdef propagates through all tasks. The change does not have to be immediate - there is a scheme to schedule when the change becomes effective: a quant and clock (like elsewhere) and a condition.
Set the quantisation time for beat accurate scheduling.
val |
+ can be an array [quant, phase, timingOffset, outset], or just [quant, phase] etc. |
Provide a condition under which the pattern is switched when a new one is inserted. The stream value and a count value is passed into the function.
Create and update condition that simply counts up to n and switches the pattern then
Switch the task immediately (stuck conditions can be subverted by this).
Set the environment (an Event) for the Tdef. It is passed as first argument into the Task function.
Set arguments in the default event. If there is none, it is created and the task routine is rebuilt.
Set the source to nil
Returns a Prout that plays the task endlessly, replacing nil with a default value 1. This allows to create streams that idle on until a new pattern is inserted.
A single Tdef may serve as a definition for multiple tasks. These methods show how to fork off separate routines from one instance. Even if they run in different contexts, their definition may still be changed.
Play an independent task in parallel.
clock |
+ the clock on which to play the forked task |
quant |
+ can be an array of [quant, phase, offset], or a Quant value. |
event |
+ an event to pass into the forked task |
Pass a value (typically an Event) into the task function, and embed the Tdef in the stream.
just like any pattern, embeds itself in stream
For live coding, each Tdef also may control one instance that plays one task. This is a PauseStream, accessible in the instance variable -player.
Starts the Tdef and creates a player.
argClock |
+ a clock on which to play the Tdef |
doReset |
+ a flag whether to reset the task if already playing |
quant |
+ can be an array of [quant, phase, offset] or a Quant value. |
Stops the player
Return the current player (if the Tdef is simply used in other streams this is nil)
Perform this method on the player.
Returns true if player is running. If a Tdef is playing and its stream ends, it will schedule a stream for playing as soon as a new one is assigned to it. If it is stopped by stop, it won't.
Instead of using a Pdefn for time values, it can be useful to use a Tdef. When changing its source, it keeps the stream of values synchronized to its clock. + +
TempoClock is a scheduler like SystemClock, but it schedules relative to a tempo in beats per second. +
See Clock for general explanation of how clocks operate.
Creates a new instance of TempoClock.
tempo |
+ The initial tempo. Defaults to |
beats |
+ The time in beats, corresponding to the reference time given with the |
seconds |
+ The reference time in seconds, to which the |
queueSize |
+ The storage size of the scheduling queue. Each scheduled item takes 2 counts of space, so this size divided by 2 gives the amount of items that can be scheduled at a time. See also -queue. |
The TempoClock will be created as if it started counting beats at the time given in the seconds
argument with the starting amount given in the beats
argument. The current count of beats will thus be equal to that starting amount plus the amount of beats that would be counted since the given reference time in seconds, according to the given tempo.
+
The default arguments create a TempoClock that starts counting beats with 0
at the current logical time.
// Create a TempoClock that starts counting beats with 5 now. +( +t = TempoClock.new(2, 5); +"current beats:" + t.beats; +) + +// Create a TempoClock, as if it started counting beats 5 seconds ago with 0. +( +t = TempoClock.new(2, 0, thisThread.seconds - 5); +"current beats:" + t.beats; +)+
Sets or gets the permanent default TempoClock instantiated at startup.
TempoClock.default.beats // beats since default TempoClock was started+
The following methods only forward to the default instance, allowing you to use the TempoClock class itself in place of TempoClock.default
.
Destroys the scheduler and releases the OS thread running the scheduler.
Removes all tasks from the scheduling queue.
Sets or gets the current tempo in beats per second.
t= TempoClock.new; +t.tempo_(2.0); // equivalent to t.tempo = 2.0; +t.tempo; +t.tempo_(72/60) // 72 beats per minute +t.tempo;+
Sets or gets a Boolean value indicating whether the clock will survive cmd-period. If false the clock is stopped (and thus removed) on cmd-period. If true the clock survives cmd-period. It is false by default.
Gets or sets the current logical time in beats according to this clock. +
When getting beats
, if this clock is the current Thread's associated clock, the Thread's own time in beats is returned, otherwise the Thread's time in seconds converted to beats according to this clock is returned.
+
After changing beats
towards the future, the clock will immediately perform all tasks scheduled until the new time. Likewise, when changing beats
towards the past, already scheduled tasks will be postponed, so they will still be performed at the scheduled time in beats.
beats
, you are only changing the clocks's notion of time, and not the current Thread's logical time, which will stay the same until the Thread is called again. Hence, if this clock is the current Thread's associated clock, and you ask the clock for time in beats just after changing it, you will see no effect. Nevertheless, the effect will be visible immediately on a different Thread.( +t = TempoClock.new; + +t.sched(3, { + t.beats = 100; + t.beats.postln; // still 3 + nil +}); +) + +( +c = TempoClock.new; +fork { + loop { + c.beats.postln; // updates, because ".wait" calls the thread + 1.wait; + } +}; +) + +c.beats = 100;+
Schedules a task to be performed at a particular time in beats. +
When the scheduling time is up, the task's awake
method is called. If the method returns a number, the task will be rescheduled for the time equal to the last scheduling time plus the returned value.
+
See also: Clock: Scheduling, Object: -awake.
Schedules a task to be performed delta
amount of beats after the current Thread's logical time. If this clock is the current Thread's associated clock, the Thread's time in beats is used, otherwise the Thread's time in seconds is converted to beats according to this clock's tempo and time of origin.
+
When the scheduling time is up, the task's awake
method is called. If the method returns a number, the task will be rescheduled for the time equal to the last scheduling time plus the returned value.
+
See also: Clock: Scheduling, Object: -awake.
Plays task (a function) at the next beat, where quant is 1 by default. Shortcut for -schedAbs; see -seconds and -nextTimeOnGrid for further details on time and quant.
t= TempoClock.default; +t.play({arg beats, time, clock; [beats, time, clock].postln});+
Plays task (a function) at the next bar using -schedAbs.
Returns the scheduling queue Array in the form [beat, function]. The maximum number of items is determined by the clock's queueSize argument upon instantiation. The default queueSize of 256 allows 128 functions to be in the queue at any time.
Returns the duration in seconds of a current whole beat.
Gets or sets the number of beats per bar. The default is 4. Setting must be done from within the scheduling thread, e.g.
t= TempoClock.new; +t.schedAbs(t.nextBar, {t.beatsPerBar_(3)}); +t.beatsPerBar;+
Returns the current bar. See -bars2beats for returning beat of current bar.
Returns the number of beats at the next bar line relative to the beat argument. If beat is not supplied, returns the beat at which the next bar begins.
Returns the current bar beat (as a Float) in relation to -beatsPerBar. Values range from 0 to < beatsPerBar.
Returns bar at which -beatsPerBar was last changed. If beatsPerBar has not been changed since the clock was created, returns 0.
Returns beat at which the -beatsPerBar was last changed. If beatsPerBar has not been changed since the clock was created, returns 0.
Returns a bar as a float relative to -baseBarBeat.
Returns a beat relative to -baseBar.
t= TempoClock.default; +t.bars2beats(t.bar) // downbeat of the current bar+
Returns the logical time to next beat. quant is 1 by default, relative to baseBarBeat, see -nextTimeOnGrid.
With default values, returns the next whole beat. quant is 1 by default, phase is 0. quant is relative to -baseBarBeat, such that
t= TempoClock.default; +t.nextTimeOnGrid(t.beatsPerBar) == t.nextBar // => true+ +
Together quant and phase are useful for finding the next n beat in a bar, e.g. nextTimeOnGrid(4, 2)
will return the next 3rd beat of a bar (of 4 beats), whereas nextBar-2
may return an elapsed beat.
Returns the current elapsed time in beats. This is equivalent to tempoClock.secs2beats(Main.elapsedTime)
. It is often preferable to use -beats instead of elapsedBeats because beats uses a thread's logical time.
Returns the current elapsed time. (This method is inherited from Clock.)
Converts absolute beats to absolute seconds, returning the elapsed time of the clock at the given beats. Only works for times in the current tempo. If the tempo changes any computed time in future will be wrong.
t= TempoClock.default; +t.beats2secs(t.beats) // equivalent to t.seconds +t.beats2secs(0) // how many seconds after startup did beat 0 occur?+
Converts absolute seconds to absolute beats. Only works for times in the current tempo. If the tempo changes any computed time in future will be wrong.
t = TempoClock(1); // create a TempoClock + +// schedule an event at next whole beat +t.schedAbs(t.beats.ceil, { arg beat, sec; [beat, sec].postln; 1 }); + +t.tempo = 2; +t.tempo = 4; +t.tempo = 0.5; +t.tempo = 1; + +t.clear; + +t.schedAbs(t.beats.ceil, { arg beat, sec; [beat, sec].postln; 1 }); + +t.stop;+ +
( +// get elapsed time, round up to next second +v = Main.elapsedTime.ceil; + +// create two clocks in a 5:2 relation, starting at time v. +t = TempoClock(1, 0, v); +u = TempoClock(0.4, 0, v); + +// start two functions at beat zero in each clock. +t.schedAbs(0, { arg beat, sec; [\t, beat, sec].postln; 1 }); +u.schedAbs(0, { arg beat, sec; [\u, beat, sec].postln; 1 }); +) + +( +u.tempo = u.tempo * 3; +t.tempo = t.tempo * 3; +) + +( +u.tempo = u.tempo * 1/4; +t.tempo = t.tempo * 1/4; +) + +( +t.stop; +u.stop; +)+ +
( +// get elapsed time, round up to next second +v = Main.elapsedTime.ceil; + +// create two clocks, starting at time v. +t = TempoClock(1, 0, v); +u = TempoClock(1, 0, v); + +// start two functions at beat zero in each clock. +// t controls u's tempo. They should stay in sync. +t.schedAbs(0, { arg beat, sec; u.tempo = t.tempo * [1,2,3,4,5].choose; [\t, beat, sec].postln; 1 }); +u.schedAbs(0, { arg beat, sec; [\u, beat, sec].postln; 1 }); +) + +( +u.tempo = u.tempo * 3; +t.tempo = t.tempo * 3; +) + +( +u.tempo = u.tempo * 1/4; +t.tempo = t.tempo * 1/4; +) + +( +t.stop; +u.stop; +)+ +
A view displaying editable text.
The text displayed in the view.
text |
+ A String. |
If -setBoth is true, setting this variable also sets -string to the value interpreted as String.
obj |
+ Any object, typically one which makes sense to display as a string, such as a Float. |
A variable stating whether setting -object will also set -string.
(arg1) |
+ A Boolean. |
Gets the same as -string, but when setting also sets -string to the value interpreted as String regardless of the -setBoth flag.
val |
+ Any object, typically one which makes sense to display as a string, such as a Float. |
The alignment of the displayed text. See Alignment for possible values.
The font used to display the text.
font |
+ A Font. |
The color used to display the text.
color |
+ A Color. |
Setting this variable colors the inside of the field under the text with the given color.
color |
+ A Color. |
The action object evaluated whenever the user changes the text.
The displayed -string.
Always true.
Sets -valueAction to the current drag data.
( +w = Window.new.front; +a = TextField(w, Rect(10, 10, 150, 20)); +a.string = "hi there"; +a.action = {arg field; field.value.postln; }; +) + +// does not do the action +a.value = "yo"; +a.string = "oy"; + +a.valueAction_("this is not a pipe"); //does the action, if the value has changed +a.doAction; //evaluates the action with the content of the text field as an argument + +a.background_(Color.grey); +a.stringColor_(Color.white); +a.align_(\center);+
When triggered, Timer measures the time (in seconds) elapsed since the previous trigger, and outputs this time value as a constant. Its output will not change until the next trigger. The initial value is 0. +
If you need the time since the last trigger, where the time is continually updated, see Sweep.
trig |
+ A trigger occurs when trig signal crosses from non-positive to positive. |
// using timer to modulate sine frequency: the slower the trigger is the higher the frequency +( +{ var trig; + trig = Impulse.kr(MouseX.kr(0.5, 20, 1)); + SinOsc.ar(Timer.kr(trig) * 500 + 500, 0, 0.2) +}.play; +)+
trig |
+ [ar kr] starts a vosim pulse when a transition from non-positive to positive occurs and no other vosim is still going. audio rate input will produce sample accurate triggering. |
freq |
+ [ar kr] the frequency of the squared sinewave. |
nCycles |
+ the number of squared sinewaves to use in one vosim pulse. nCycles gets checked when VOSIM receives a trigger. |
decay |
+ the decay factor. |
A wavetable lookup oscillator which can be swept smoothly across wavetables. All the wavetables must be allocated to the same size. Fractional values of table will interpolate between two adjacent tables. +
This oscillator requires at least two buffers to be filled with a wavetable format signal. This preprocesses the Signal into a form which can be used efficiently by the Oscillator. The buffer size must be a power of 2. +
This can be achieved by creating a Buffer object and sending it one of the "b_gen" messages ( sine1, sine2, sine3 ) with the wavetable flag set to true. +
This can also be achieved by creating a Signal object and sending it the asWavetable message, saving it to disk, and having the server load it from there. +
If you use Buffer objects to manage buffer numbers, you can use the [*allocConsecutive] method to allocate a continuous block of buffers. See the Buffer helpfile for details.
bufpos |
+ Buffer index. Can be swept continuously among adjacent wavetable buffers of the same size. |
freq |
+ Frequency in Hertz. |
phase |
+ Phase offset or modulator in radians. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
A Vowel contains and handles data to describe formants that allow to distinguish the vowel sounds \a, \e, \i, \o, \u for the registers \bass, \tenor, \counterTenor, \alto and \soprano. The current implementation uses formant data that originally appeared in the C-sound Manual: http://www.csounds.com/manual/html/MiscFormants.html +
2011 -- Florian Grond, Till Bovermann +
Supported by: the Ambient Intelligence Group, CITEC ( http://www.techfak.uni-bielefeld.de/ags/ami ) Bielefeld University, and the TAI Studio ( http://TAI-Studio.org ), Department of Media, Aalto University, Helsinki. Many thanks go to Alberto de Campo and and Julian Rohrhuber.
holds formant information
Vowel.formLib.at(\a) +Vowel.formLib.at(\a, \bass) +Vowel.formLib.at(\a, \bass, \freq) +Vowel.formLib.postTree;+
a Dictionary
creates a new instance of Vowel
vowel |
+ select a vowel by the symbols \a, \e, \i, \o, \u. default value is 'a'. |
register |
+ select a register by the symbols \bass, \tenor, \counterTenor, \alto, \soprano. Default value is 'bass'. |
a Vowel
Vowel(); // defaults to A bass +Vowel(\a, \bass); +Vowel(\e, \tenor); +Vowel(\i, \counterTenor); +Vowel(\o, \alto); +Vowel(\u, \soprano); + +{Formants.ar(100, Vowel(\e, \bass)) * 0.1 }.play +{Formants.ar(200, Vowel(\o, \alto)) * 0.1 }.play +{Formants.ar(300, Vowel(\i, \soprano)) * 0.1 }.play+ +
The class exhibits multichannel expansion behaviour
{Formants.ar(100, Vowel([\e, \o], [\bass])) * 0.1 }.play +{Formants.ar(100, Vowel(\e, [\bass,\alto])) * 0.1 }.play +{Formants.ar(100, Vowel([\e, \o], [\bass,\alto])) * 0.1 }.play+
You can also explicitly set the formants
Vowel.basicNew([ 300, 400, 2700, 3800, 4950 ], [ 0, -16, -35, -40, -6 ], [ 50, 10, 170, 180, 200 ])+
freqs |
+ an array of freqs |
dBs |
+ an array of dBs |
widths |
+ an array of widths in Hz |
a Vowel
compose your own vowel based on the ones defined in formTable. Wraps when argument lengths differ (as in channel expansion).
vowelNames |
+ an array describing the vowels' character: \a, \e, \i, \o, \u. |
registers |
+ an array describing the vowels' voice: \bass, \tenor, \counter, \alt, \sopran. |
weights |
+ an array describing the vowels' weight relative to the others. Should be a normalized sum. |
a Vowel
Vowel.compose([\a,\e,\i], [\bass,\soprano,\alto], [0.2, 0.3, 0.5]); +Vowel.compose([\a,\e], [\soprano], [9, 4].normalizeSum); +Vowel.compose([\a], [\tenor, \counterTenor, \soprano], [1, 4, 2].normalizeSum); + +( +{ var v = Vowel.compose( + [\a, \e, \i], + [\soprano, \bass, \tenor, \counterTenor, \alto], + ({10.rand}!5).normalizeSum +); +Formants.ar(50 + 300.rand, v) * 0.1; +}.play; +)+
Array of freqs, one for each formant.
v = Vowel(\e, \tenor) +v.freqs +v.freqs_({|i| 100 * (i+1) }!5) +v.freqs+
Array of dBs,
v = Vowel(\u, \counterTenor) +v.dBs +v.dBs_({|i| -10 * (i) }!5) +v.dBs+
array of widths in Hz, one for each formant.
v = Vowel(\o, \soprano) +v.widths +v.widths_({ 500.rand }!5) +v.widths+
array of amps in dB, one for each formant.
array of rQ values, one for each formant.
array of midinotes, one for each formant.
v = Vowel() +v.addFormants([3000,4000], -6, [200, 200]) + +{Formants.ar(100, v) * 0.1 }.play +{Formants.ar(100, Vowel()) * 0.1}.play + +{[Formants.ar(100, Vowel()) * 0.1, Formants.ar(100, v) * 0.1 ]}.play + +v.removeFormants([5,6])+
freq | |
db | |
width |
index |
+ index of the formant to remove |
get amp value for freq according to order of filter
Vowel(\u).ampAt(100); + +~range = {|i|i*2}!2000; +Vowel(\u).ampAt(~range ).plot; +Vowel(\u).ampAt(~range ).ampdb.plot(minval: -100, maxval: 0); + +Vowel(\u).ampAt(~range, 0.5 ).plot; +Vowel(\u).ampAt(~range, 0.5 ).ampdb.plot(minval: -100, maxval: 0); + +Vowel(\u).ampAt(~range, 3 ).plot; +Vowel(\u).ampAt(~range, 3 ).ampdb.plot(minval: -100, maxval: 0); + +Vowel(\u).ampAt(~range, [3,1,0.5,2] ).plot +Vowel(\u).ampAt(~range, [3,1,0.5,2] ).ampdb.plot(minval: -100, maxval: 0)+
freq | |
filterOrder |
plot the frequency spectrum of the vowel.
fmin | |
fmax | |
fstep | |
order |
serialise the vowel into an array
an arrays of [ freqs, dBs, widths ]
convert into an Event
convert into an array of key value pairs
id |
+ optional argument added to each key |
creates controls in the wrapping synth such that the vowel's parameters can be controlled seamlessly from the language.
id |
+ Optional. If more than one vowel need to be controlled inside one Synth, this id can be used to differentiate them from each other. |
rate |
+ either \kr or \ar |
lag |
+ array of lag times |
( +x = SynthDef(\vowel, {|freq = 420| + Out.ar(0, + Formants.ar(freq.lag(0.1), Vowel(\a).addControls(lag: 1)) * 0.01 + ) +}).play +) + +x.setn(*([\freq, exprand(100, 1000)] ++ Vowel(\u, \soprano).asKeyValuePairs)); +x.setn(*([\freq, exprand(100, 500)] ++ Vowel(\e, \bass).asKeyValuePairs)); +x.setn(*([\freq, exprand(400, 1000)] ++ Vowel(\a, \soprano).asKeyValuePairs));+
plus
that | |
adverb |
minus
that | |
adverb |
multiply
that | |
adverb |
divide
that | |
adverb |
blends two vowels by linear interpolation between of midinotes, widths and dBs.
that |
+ a Vowel |
blendFrac |
+ coefficient. Range from 0.0 (this) to 1.0 (that). |
a new instance of Vowel
lift the upper formants by multiplying their dBs with a factor. The sum of all dBs remains fixed.
bright |
+ coefficient to brighten the vowel. Typical ranges are from 1 (no change) to 3. |
refFormant |
+ reference formant, whose amplitude remains unchanged. Default value is 0 (first formant) |
NOTE: If the coefficient bright is > 1 and the refFormant is > 0, the resulting signal may raise to a very big amplitude.
Vowel().brightenRel(1 ).dBs.postln; +Vowel().brightenRel(0.1).dBs.postln; +Vowel().brightenRel(1.5).dBs.postln; + +// refFormant = 0 loud on the left +{Formants.ar(100, Vowel().brightenRel(MouseX.kr(0, 2), 0 )) * 0.1}.play +// refFormant = 1 +{Formants.ar(100, Vowel().brightenRel(MouseX.kr(0, 2), 1 )) * 0.1}.play +// refFormant = 2 +{Formants.ar(100, Vowel().brightenRel(MouseX.kr(0, 2), 2 )) * 0.1}.play +// refFormant = 3 +{Formants.ar(100, Vowel().brightenRel(MouseX.kr(0, 2), 3 )) * 0.1}.play +// refFormant = 4 loud on the right +{Formants.ar(100, Vowel().brightenRel(MouseX.kr(0, 2), 4 )) * 0.1}.play+
allows to lift the upper formants by multiplying their dBs. the sum of all dBs remains constant.
bright |
+ coefficient to brighten the vowel. Typical ranges are from 1 (no change) to 3. |
refFormant |
+ reference formant, whose amplitude remains unchanged. Default value is 0 (first formant) |
Vowel().brightenLin(-1).dBs.postln; +Vowel().brightenLin( 0).dBs.postln; +Vowel().brightenLin( 1).dBs.postln; + +// refFormant = 0 loud on the left +{Formants.ar(100, Vowel().brightenLin(MouseX.kr(0, 20), 0 )) * 0.1}.play +// refFormant = 1 +{Formants.ar(100, Vowel().brightenLin(MouseX.kr(0, 20), 1 )) * 0.1}.play +// refFormant = 2 +{Formants.ar(100, Vowel().brightenLin(MouseX.kr(0, 20), 2 )) * 0.1}.play +// refFormant = 3 +{Formants.ar(100, Vowel().brightenLin(MouseX.kr(0, 20), 3 )) * 0.1}.play +// refFormant = 4 loud on the right +{Formants.ar(100, Vowel().brightenLin(MouseX.kr(0, 20), 4 )) * 0.1}.play+
allows to relatively lift the upper formants by keeping the sum of gains of all formants constant.
bright |
+ coefficient to brighten the vowel. Typical ranges are from 1 (no change) to 3. |
Vowel().brightenExp(1).dBs.postln; +Vowel().brightenExp(0.1).dBs.postln; +Vowel().brightenExp(1.5).dBs.postln; + +{Formants.ar(100, Vowel(\a, \bass).brightenExp(MouseX.kr(0,5))) * 0.1 }.play;+
(describe method here)
baseFreq |
+ undocumented |
numPartials | |
ring |
prettyprint support
stream |
archival support
In SynthDefs:
( +SynthDef(\vowblend,{|freq = 100, b1 = 0.5, b2 = 0.5, b3 = 0.5, b4 = 0.5 bright = 0, pan = 0| + var va = Vowel(\a, \bass), + ve = Vowel(\e, \tenor), + vi = Vowel(\i, \counterTenor), + vo = Vowel(\o, \alto), + vu = Vowel(\u, \soprano), + sig; + + sig = Formants.ar( + freq, + va + .blend(ve, b1) + .blend(vi, b2) + .blend(vo, b3) + .blend(vu, b4) + .brightenExp(bright, 1) + ) + * EnvGen.kr(Env.perc, 3.0, doneAction: 2); + + Out.ar(0, Pan2.ar(sig, pan, 0.1)); +}).add +) + + +( +Task({ 32.do({ arg i; + Synth(\vowblend, [ + \pan, i.linlin(0,32, -1, 1 ), + \freq, i.linlin(0,32, 30, 66 ).midicps, + \b1, 2.rand, + \b2, 2.rand, + \b3, 2.rand, + \b4, 2.rand, + \bright, 1.5.rand + ]); + 0.25.wait + +});}).play +)+ +
using addControls to create buses
( +x = SynthDef(\test, { + Out.ar(0, + Formants.ar(420, Vowel(\a).addControls(3)) * 0.01 + ) +}).play +) + +x.inspect + +x.setn(*Vowel(\i).asKeyValuePairs(3));+ +
JITLib style:
NdefMixer(s); + +Ndef(\vowel, {Formants.ar(200, Vowel(\a, \soprano)) * 0.01 }).play + +( +Ndef(\vowel, { + Formants.ar(200, + Vowel(\o, \soprano) + .blend(Vowel(\i, \tenor), SinOsc.kr(10).range(0,1))) * 0.01 +}).play +) + + +( +Ndef(\vowel, { + Formants.ar(LFNoise0.kr(10).exprange(100, 400), + Vowel(\o, \soprano) + .brightenExp(SinOsc.kr(2).range(0,1), 1), + unfold: true + ).mean * 0.01 +}).play +) + + +Ndef(\vowel, { + Formants.ar(200, Vowel(\a, \soprano).addControls(4)) * 0.01 +}).play + +Ndef(\vowel).setn(*Vowel(\e, \bass).asKeyValuePairs(4).flatten) +Ndef(\vowel).setn(*Vowel(\u).asKeyValuePairs(4).flatten) + + +Ndef(\vowel, { + Formants.ar(200, Vowel(\a, \soprano), unfold: true).scramble.keep(2) * 0.1 +}).play + +Ndef(\vowel).free(2) + +Ndef(\vowel).fadeTime = 4; + +( +Ndef(\vowel, { + Formants.ar([1, 2, 4] * 240 * {LFNoise1.kr(5, 0.003, 1)}!3, Vowel(\a, [\bass, \tenor, \soprano]), + freqMods: LFNoise1.ar(4*[0.1, 0.2, 0.3, 0.4, 0.5].scramble, 0.1, ampMods: [1, 1, 1, 0] +)).sum * 0.1 +}).play +)+ +
Fun.
( // CPU demanding +~freqs = {|i| 50 * 1.5.pow(i) }!9; +~numChan = 2; +r = Routine{ + var sustain = 8, transition = 3, overlap = 4; + var period = 1.5 * 2.sqrt; + var harms, amps; + 0.5.wait; + inf.do{ + harms = {|i| (i+1) * ~freqs.choose }!60; + amps = Vowel([\a,\e,\i,\o,\u].choose,[\bass,\tenor,\counterTenor,\alto,\soprano].choose).ampAt(harms); + { PanAz.ar(~numChan, DynKlank.ar( `[~freqs,amps,amps], + Decay.ar(Impulse.ar( exp(1)/5.0 ), SinOsc.kr( pi/9.0, 1.0.rand ).range(0.05,0.7) ) ) * + EnvGen.kr(Env.new([-40,-20, -30,-40].dbamp, [2/5.0, 1/5.0,2/5.0],'exponential'), 1.0, timeScale: 35, levelScale: 0.1, doneAction: 2) ,SinOsc.kr(0.5, 1.0.rand) )}.play; + period.wait; + } +}; +r.play; +) +r.stop; //stop spawning new synths + + +( +Ndef(\vowel).fadeTime = 5; +Ndef(\vowel, { + var freqs, dBs, widths, out; + var baseFreq = LFNoise0.kr([5, 10] * 0.1).round(0.1).exprange(50, 200) * [2, 1.01]; + #freqs, dBs, widths = (Vowel(\i, \soprano).blend(Vowel(\o, \bass), LFNoise1.kr(0.1265))).blend(Vowel(\e, \bass), LFNoise1.kr(10)).asArray; + + //freqs = freqs * SinOsc.ar([0.1, 0.2, 0.3, 0.4].scramble, Rand(), 0.1, 1); + freqs = freqs * LFNoise1.ar([0.1, 0.2, 0.3, 0.4].scramble, 0.1, 1); + + out = [freqs, widths, dBs.dbamp].flop.collect{ |args| + Formant.ar(baseFreq, *args); + }.flop; + + out = out.collect{|vocal| + Splay.ar(vocal) + }.sum.postln; + + out + * LFPulse.ar([9, 9.01], 0, 0.4).range(0, 1).lag(0.01, 0.5) + * LFPulse.ar(0.1, [0, 0.35], [0.9, 0.8]).lag(0.01) + + * 0.1 + +}).play +)+ +
WalshHadamard transform to a basis of square wave like functions rather than the more conventional sine waves of Fourier analysis. The advantage is that the transform is really efficient (no need for trigonometric functions) but the interpretation in a space of 'sequencies' and amplitudes is less intuitively comprehensible. +
The plug-in demonstrates the transform in the context of a filter; choose how many of the 64 basis elements to use in the reconstruction. 64 corresponds to the standard block size.
input |
+ Original signal to effect |
which |
+ Basis elements whose Indices are below this number will be zeroed. The default 0 means the identity transform, all basis elements will be used in the reconstruction. |
{WalshHadamard.ar(SoundIn.ar, MouseX.kr(0,64))}.play+ +
Inspired by Chad Kirby's SuperCollider2 Warp1 class, which was inspired by Richard Karpen's sndwarp for CSound. A granular time stretcher and pitchshifter.
numChannels |
+ the number of channels in the soundfile used in bufnum. |
bufnum |
+ the buffer number of a mono soundfile. |
pointer |
+ the position in the buffer. The value should be between 0 and 1, with 0 being the beginning of the buffer, and 1 the end. |
freqScale |
+ the amount of frequency shift. 1.0 is normal, 0.5 is one octave down, 2.0 is one octave up. Negative values play the soundfile backwards. |
windowSize |
+ the size of each grain window. |
envbufnum |
+ the buffer number containing a signal to use for the grain envelope. -1 uses a built-in Hanning envelope. |
overlaps |
+ the number of overlapping windows. |
windowRandRatio |
+ the amount of randomness to the windowing function. Must be between 0 (no randomness) to 1.0 (probably too random actually) |
interp |
+ the interpolation method used for pitchshifting grains. 1 = no interpolation. 2 = linear. 4 = cubic interpolation (more computationally intensive). |
mul | |
add |
Cutups in the style of Aphex Twin/ Bogdan Raczynski/ u-ziq etc, that is, sometimes so fast that a pitch is heard. With this procedure you must specifiy the sizes of allowed blocks. Each block then has some probability to be a simple statement, or some type of roll (fast set of repetitions), either even or geometric series timed. +
For additional data on the algorithm see +
Nick Collins, "further automatic BreakBeat cutting methods", +
Proceedings of Generative Art 2001 +
reproduced at http://www.cus.cam.ac.uk/~nc272
Create a WarpCutProc1 object with the given parameters.
blocksizefunc |
+ A function (or something else responding to value) returning the next block size. The function will be passed the number of beatsleft in the current phrase, and the current phrase length in case judgement is to depend on position within a phrase. |
rollfunc |
+ A function returning the number of cuts to subdivide a block in a roll. This function is passed the current blocklength as argument in case you want a decision to take account of how much space you divide. |
probs |
+ An array of three probabilities (0.0 to 1.0). They are [prob of simple statement, prob of even roll, prob of accelerando rather than decelerando geometric series]. The algorithm cascades through one prob at a time. If a block is not simple, then it is a roll. If it isn't an even roll it is a geometric roll. |
phraselength |
+ Next length of phrase in beats. |
accel |
+ a parameter to control the acceleration rate of the geometric series. Good values are 0.5 to 0.99. Do not pass 0.0 or 1.0 without expecting a crash. |
bpsd |
+ beats per sub division. Sets a primitive cut size resolution for choose offset messages. |
Called internally after a new. +
Other methods are overrides of BBCutProc base class behaviour. WarpCutProc1 will flag rolls for synthesis.
You'll have to substitute your own break sample to hear the rhythmic aspects of this procedure properly perhaps, but the Warp cutter creates such bizarre effects that it's fun on any source.
( //defaults +var sf; + +Routine.run({ + sf= BBCutBuffer("sounds/a11wlk01.wav",4); + + s.sync; + + //144 bpm + BBCut2(CutBuf1(sf),WarpCutProc1.new).play(2.4); +}); + +) + +( //oddness on AudioIn +BBCut2(CutStream1.new,WarpCutProc1({[1,2,4].wchoose([0.7,0.2,0.1])},{[9,19,29].choose},[0.0,0.5,0.5],100.0,{0.8+(0.15.rand)})).play(2.2); +) + + +//roll through a sound with UI control +( +var w; +var slider, num, data; +var sf, bbcutter, clock; + +data= //control spec data for ui controls +[ +["tempo",2,3,\lin,0.1,2.5], +["blocklength", 0, 2,\lin,1,1], +["roll", 0, 6,\lin,1,0], +["simpleprob", 0.0, 1.0,\lin,0.1,0.5], +["evenprob", 0.0, 1.0,\lin,0.1,0.5], +["accelprob", 0.0, 1.0,\lin,0.1,0.5], +["phraselength", 1.0, 5.0,\lin,1.0,2.0], +["accelparam", 0.5, 0.99,\lin,0.01,0.9], +["restchance", 0.0, 1.0,\lin,0.1,0.0], +["ampvariation", 0.0, 1.0,\lin,0.01,1.0], +["panvariation", 0.0, 1.0,\lin,0.01,0.0], +["randomoffset", 0.0, 1.0,\lin,0.01,0.0] +]; + +w = SCWindow("WarpCutProc1 demo N.M.Collins 23/08/03", Rect.new(100, 500, 500, (30*(data.size)))); + +slider= Array.fill(data.size, {arg i; DDSlider.performList(\new,[w, Rect(5,30*i, 300, 30)]++ data.at(i))}); + +slider.at(1).slider.action_({arg sl; +var val; val = slider.at(1).spec.map(sl.value); +val= [1,3,5].at(val.round(1.0).asInteger); +slider.at(1).string.string_(val); +slider.at(1).lastval=val; +}); + +slider.at(1).lastval= 3; +slider.at(2).string.string_(3); + +slider.at(2).slider.action_({arg sl; +var val; val = slider.at(2).spec.map(sl.value); +val= [3,5,7,11,13,17,19].at(val.round(1.0).asInteger); +slider.at(2).string.string_(val); +slider.at(2).lastval=val; +}); + +slider.at(2).lastval= 3; +slider.at(2).string.string_(3); + +w.front; + + +Routine.run({ + sf= BBCutBuffer(Platform.resourceDir +/+ "sounds/break.aiff",8); + + s.sync; + +bbcutter= BBCut2([ +CutBuf1(sf, +slider.at(11) //randomoffsetchance +), +CutMixer(0,1.0,{ +if(slider.at(8).value.coin, //restchance +{0}, +{ +rrand(slider.at(9).value,1.0)} //amp variation +)}, +{rrand(-1.0*(slider.at(10).value),slider.at(10).value)} //panvariation +) +], +WarpCutProc1( +slider.at(1), //block +slider.at(2), //rollcut +[slider.at(3),slider.at(4),slider.at(5)], //probs +slider.at(6), //phraselength +slider.at(7) //accelparam +) +); + +clock=ExternalClock(slider.at(0).lastval).play; + +bbcutter.play(clock); + +//update tempo ten times a second +SystemClock.sched(0.0,{clock.tempoclock.tempo_(slider.at(0).lastval); 0.1}); + +}); +)+ +
//SLUGens released under the GNU GPL as extensions for SuperCollider 3, by Nick Collins, http://composerprogrammer.com/index.html
Specify a surface z(x,y) via a buffer and scan it from the x and y inputs.
bufnum |
+ Your surface is a two dimensional array, but specified via a one dimensional buffer. The convention is exhibited below; note that you have to pass in the surface dimensions as well. |
x | |
y |
+ audio rate scanning instructions. Both values must be in the range 0.0 to 1.0, or they are wrapped into this range. |
//create buffer. I want the equation z = 2*(((x/100)**2) + ((abs(sin(10*y))/50)**(1/3)))-1 +//over a 100 by 50 area + +//2d to 1d conversion follows index= y*rowlength+ x +( +var width= 100; //= num cols +var height=50; //=num rows, though indexing bottom to top; i.e., standard Cartesian co-ordinates + +a=Array.fill(width*height,{arg i; +var xnow, ynow, x, y; + +xnow= i%width; +ynow= (i-xnow).div(width); + +x=xnow/width; +y=ynow/height; + +2*(((x)**2) + ((abs(sin(10*y)))**(1/3)))-1 + +}); + + +b=Buffer.sendCollection(s, a, 1); +) + + +//test scanning; you can't move fast enough... scan controls should also be audio rate! +{WaveTerrain.ar(b.bufnum,MouseX.kr(0.0,1.0), MouseY.kr(0.0,1.0),100,50)}.play + + +//LFNoise adds some drift to explore the landscape more +{WaveTerrain.ar(b.bufnum,SinOsc.ar(MouseX.kr(1,200,'exponential')).abs + LFNoise2.ar(2),SinOsc.ar(MouseY.kr(1,300,'exponential'),pi*0.5).abs,100,50)}.play + + + + + +//change surface equation +( +var width= 100; //= num cols +var height=50; //=num rows, though indexing bottom to top; i.e., standard Cartesian co-ordinates + +a=Array.fill(width*height,{arg i; +var xnow, ynow, x, y; + +xnow= i%width; +ynow= (i-xnow).div(width); + +x=xnow/width; +y=ynow/height; + +(((cos(5*x+1.7))**3) - ((abs(sin(23*y)))**(1/3))) + +}); + +b.sendCollection(a); +) + + + + + +//change surface equation +( +var width= 100; //= num cols +var height=50; //=num rows, though indexing bottom to top; i.e., standard Cartesian co-ordinates + +a=Array.fill(width*height,{arg i; +var xnow, ynow, x, y; + +xnow= i%width; +ynow= (i-xnow).div(width); + +x=xnow/width; +y=ynow/height; + +(((1.3*(cos(rrand(1,2)*x+1.7))**2) - ((abs(sin(rrand(1.2,4.9)*y)))**(1/2)))).max(-1.0).min(1.0) + +}); + +b.sendCollection(a); +)+ +
Generates an exponential curve from the start value to the end value. Both the start and end values must be non-zero and have the same sign.
start |
+ Starting value. |
end |
+ Ending value. |
dur |
+ Duration in seconds. |
mul |
+ Output will be multiplied by this value. |
add |
+ This value will be added to the output. |
doneAction |
+ A doneAction to be evaluated when the line is completed. See + Done for more detail. |
This class is missing documentation.
Copy and paste the text below and save to HelpSource/Classes/XiiSoundScratcher.schelp
TITLE:: XiiSoundScratcher +summary:: (put short description here) +categories:: Undocumented classes +related:: Classes/SomeRelatedClass, Reference/SomeRelatedStuff, etc. + +DESCRIPTION:: +(put long description here) + + +CLASSMETHODS:: + +METHOD:: new +(describe method here) + +ARGUMENT:: server +(describe argument here) + +ARGUMENT:: channels +(describe argument here) + +ARGUMENT:: setting +(describe argument here) + +returns:: (describe returnvalue here) + + +INSTANCEMETHODS:: + +METHOD:: refresh +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: updatePoolMenu +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: clear +(describe method here) + +ARGUMENT:: worm +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: initXiiSoundScratcher +(describe method here) + +ARGUMENT:: server +(describe argument here) + +ARGUMENT:: channels +(describe argument here) + +ARGUMENT:: setting +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: xiigui +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: getState +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: initXiiSoundScratcherSwing +(describe method here) + +ARGUMENT:: server +(describe argument here) + +ARGUMENT:: channels +(describe argument here) + +ARGUMENT:: setting +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: win +(describe method here) + +returns:: (describe returnvalue here) + + +EXAMPLES:: + +code:: +(some example code) +:: ++
This class is missing documentation.
Copy and paste the text below and save to HelpSource/Classes/XiiStyles.schelp
TITLE:: XiiStyles +summary:: (put short description here) +categories:: Undocumented classes +related:: Classes/SomeRelatedClass, Reference/SomeRelatedStuff, etc. + +DESCRIPTION:: +(put long description here) + + +CLASSMETHODS:: + +METHOD:: new +(describe method here) + +ARGUMENT:: numChannels +(describe argument here) + +returns:: (describe returnvalue here) + + +INSTANCEMETHODS:: + +METHOD:: init +(describe method here) + +ARGUMENT:: ch +(describe argument here) + +returns:: (describe returnvalue here) + +METHOD:: normal +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: dropDownWidth +(describe method here) + +returns:: (describe returnvalue here) + +METHOD:: grey +(describe method here) + +returns:: (describe returnvalue here) + + +EXAMPLES:: + +code:: +(some example code) +:: ++
The name "SuperCollider" is in fact used to indicate five different things (Figure 1):
The SuperCollider application is thus made up of two distinct, autonomous, components, a server and a client. For the first we have a choice between scsynth (SC-synthesizer) and supernova, and for the second we have sclang (SC-language).
The SuperCollider application makes use of client/server architecture which separates two functions, respectively one providing and the other requesting services. The client and the server communicate through a network.
In Figure 2 a generic network architecture is depicted: A number of clients communicating with a server by exchanging messages through a network. In SuperCollider the client and the server make use of a specific subset of CNMAT's Open Sound Control (OSC) protocol in order to communicate (over TCP or UDP). As a consequence, you will see many references to "OSC messages" in the help files. +
To avoid any possible confusion: The network is defined at an abstract level. This means that the client(s) and the server(s) can be in execution on the same physical machine. This is what normally happens when you use the SuperCollider application: two programs will run on your machine, scsynth (or supernova) and sclang. +
The server app, scsynth or supernova, is a lean and efficient command line program dedicated to audio synthesis and processing. It knows nothing about SC code, objects, Object Oriented Programming, or anything else to do with the SC language. +
The client of this server is sclang. Sclang performs two distinct tasks:
From inside sclang, starting a server app can be accomplished by: + +
The sclang interpreter can send OSC messages to the server in two fashions:
Working this way you have gained certain functionality. It provides a node ID for you automatically, it allows you to control the Synth in syntactically elegant and efficient ways (see the Synth and Node helpfiles), and to access all the advantages of object oriented programming while doing so. Encapsulating the complexities and bookkeeping greatly reduces the chance of bugs in your own code.
Language wrapping allows the user to access complex behaviours from very little code. Figure 3 (ignore for the moment that sclang is represented as a client among other possible ones, see later) schematically represents what happens when you evaluate an audio function like this: + +
In this case many server operations are hidden. To understand the passages involved in the evaluation of this code see 04. Functions and Other Functionality and 10. SynthDefs and Synths (part of Scott Wilson's tutorial). +
The OOP-style also has a small amount of overhead. It requires clientside CPU cycles and memory to create and manipulate an object. Normally this is not significant, but there may be times when you would prefer to use the less elegant, and less expensive first method, for instance when creating large numbers of grains which will simply play and then deallocate themselves. +
Thus it is possible to create synth nodes on the server without actually creating Synth objects, providing you are willing to do the required housekeeping yourself. The same is true of group nodes, buffers, and buses. A more detailed discussion of these concepts can be found in the Node Messaging helpfile. +
In conclusion, the crucial thing to remember is the distinction between things like nodes, busses, buffers, and servers and the objects that represent them in the language app (i.e. instances of Node, Bus, Buffer, and Server; these are referred to as 'Server Abstraction Objects'). Keeping these conceptually distinct will help avoid much confusion.
The client/server architecture provides three main advantages:
There are two notable drawbacks:
Apart from sclang, it is possible to control the server from any other client which provides for OSC messaging (e.g. from Java, Python, Max/MSP, etc.). For networking, see Server Architecture, NetAddr, OSCFunc. +
In general however, sclang is the preferable way to communicate with the server for three reasons:
SuperCollider implements a number of UGens supporting Fast Fourier Transform (FFT) based processing. The most basic of these are FFT and IFFT (inverse-FFT) which convert data between the time and frequency domains:
chain = FFT(buffer, input)+ +
output = IFFT(chain)+ +
FFT stores spectral data in a local buffer ( see Buffer ) in the following order: DC, nyquist, real 1f, imag 1f, real 2f, imag 2f, ... real (N-1)f, imag (N-1)f, where f is the frequency corresponding to the window size, and N is the window size / 2. +
The buffer's size must correspond to a power of 2, and must also be a multiple of SC's block size. The window size is equivalent to the buffer size, and the window overlap defaults to 2. Both FFT and IFFT use a Sine window by default, the combination of which (i.e. raised sine, that is, sine squared) is a Hanning window.
In between an FFT and an IFFT one can chain together a number of Phase Vocoder UGens (i.e. 'PV_...') to manipulate blocks of spectral data before reconversion. The process of buffering the appropriate amount of audio, windowing, conversion, overlap-add, etc. is handled for you automatically.
( +{ var in, chain; + in = WhiteNoise.ar(0.8); + chain = FFT(LocalBuf(2048), in); + chain = PV_RandComb(chain, 0.95, Impulse.kr(0.4)); + IFFT(chain); +}.play; +)+ +
In order to expand PV UGens for a multichannel input signal, an appropriate array of buffers must be provided (not a multichannel buffer):
( +{ var in, chain; + in = Ringz.ar(Impulse.ar([2, 3]), [700, 800], 0.1) * 5; + chain = FFT({ LocalBuf(2048) } ! 2, in); + chain = PV_RandComb(chain, 0.95, Impulse.kr(0.4)); + IFFT(chain); +}.play; +)+ +
PV Ugens write their output data in place, i.e. back into the same buffer from which they read. PV UGens which require two buffers write their data into the first buffer, usually called 'bufferA'.
( +{ var inA, chainA, inB, chainB, chain; + inA = LFSaw.ar(MouseY.kr(100, 1000, 1), 0, 0.2); + inB = Ringz.ar(Impulse.ar(MouseX.kr(1, 100, 1)), 700, 0.5); + chainA = FFT(LocalBuf(2048), inA); + chainB = FFT(LocalBuf(2048), inB); + chain = PV_MagMul(chainA, chainB); // writes into bufferA + 0.1 * IFFT(chain); +}.play; +) + +d.free;+ +
A similar example using a soundfile in an external buffer:
d = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); + +( +{ var inA, chainA, inB, chainB, chain; + inA = LFSaw.ar(100, 0, 0.2); + inB = PlayBuf.ar(1, d, BufRateScale.kr(d), loop: 1); + chainA = FFT(LocalBuf(2048), inA); + chainB = FFT(LocalBuf(2048), inB); + chain = PV_MagMul(chainA, chainB); // writes into bufferA + 0.1 * IFFT(chain); +}.play; +) + +d.free;+ +
Because each PV UGen overwrites the output of the previous one, it is necessary to copy the data to an additional buffer at the desired point in the chain in order to do parallel processing of input without using multiple FFT UGens. PV_Copy allows for this.
( +b = Buffer.alloc(s,2048,1); // use global buffers for plotting the data +c = Buffer.alloc(s,2048,1); +) + +//// proof of concept +( +x = { var inA, chainA, chainB; + inA = LFClipNoise.ar(100); + chainA = FFT(b, inA); + chainB = PV_Copy(chainA, c); + IFFT(chainA) - IFFT(chainB); // cancels to zero so silent! +}.play; +) +x.free; +// IFFTed frames contain the same windowed output data +b.plot(\b, Rect(200, 430, 700, 300), nil, nil); c.plot(\c, Rect(200, 100, 700, 300), nil, nil); +[b, c].do(_.free);+ +
Note that PV UGens convert as needed between cartesian (complex) and polar representations, therefore when using multiple PV UGens it may be impossible to know in which form the values will be at any given time. FFT produces complex output (see above), so while the following produces a reliable magnitude plot:
b = Buffer.alloc(s,1024); // use global buffers for plotting the data +a = { FFT(b, LFSaw.ar(4000)); 0.0 }.play; +( +b.getn(0, 1024, { arg buf; + var z, x; + z = buf.clump(2).flop; + z = [Signal.newFrom(z[0]), Signal.newFrom(z[1])]; + x = Complex(z[0], z[1]); + {x.magnitude.plot}.defer +}) +) +a.free; b.free;+ +
any Synth using PV UGens might not. +
It is possible to manipulate the FFT data directly within a synth graph (if there doesn't already exist a PV UGen which will do what you want), using the methods pvcalc, pvcalc2, pvcollect. Here's an example which uses the SequenceableCollection methods clump and flop to rearrange the order of the spectral bins:
c = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav"); + +( +x = { + var in, numFrames=2048, chain, v; + in = PlayBuf.ar(1, c, loop: 1); + chain = FFT(LocalBuf(numFrames), in); + + chain = chain.pvcalc(numFrames, {|mags, phases| + /* Play with the mags and phases, then return them */ + [mags, phases].flop.clump(2).flop.flatten + }, tobin: 250); + + Out.ar(0, 0.5 * IFFT(chain).dup); +}.play; +) +x.free; c.free;+
Care must be taken when using multichannel expansion with FFT UGens, as they require separate buffers. Code such as this can be deceptive:
chain = FFT(bufnum, {WhiteNoise.ar(0.2)}.dup);+ +
The above may seem to work, but does not. It does result in two FFT UGens, but as they both write to the same buffer, the second simply overwrites the data from the first, thus wasting CPU and accomplishing nothing. +
When using multichannel expansion with FFT UGens it is necessary to ensure that each one writes to a different buffer. Here's an example of one way to do this:
( +SynthDef("help-multichannel FFT", { arg out=0; // bufnum is an array + var in, chain; + in = [SinOsc.ar(200, 0, 0.2), WhiteNoise.ar(0.2)]; + chain = FFT(LocalBuf([2048, 2048]), in); // each FFT has a different buffer + // now we can multichannel expand as normal + chain = PV_BrickWall(chain, SinOsc.kr(-0.1)); + Out.ar(out, IFFT(chain)); +}).play; +) + +// or using global buffers + +b = {Buffer.alloc(s,2048,1)}.dup; + +( +SynthDef("help-multichannel FFT", { arg out=0, bufnum= #[0, 1]; // bufnum is an array + var in, chain; + in = [SinOsc.ar(200, 0, 0.2), WhiteNoise.ar(0.2)]; + chain = FFT(bufnum, in); // each FFT has a different buffer + // now we can multichannel expand as normal + chain = PV_BrickWall(chain, SinOsc.kr(-0.1)); + Out.ar(out, IFFT(chain)); +}).play(s,[\bufnum, b]); +)+ +
Note that dup on a UGen just makes a reference to that UGen, because UGen defines -copy to simply return the receiver. (See UGen for more detail.)
a = SinOsc.ar; +a.dup[1] === a + +true+ +
Code like IFFT(chain).dup
is found throughout the PV help files , and is just a convenient way to copy a mono signal to stereo, without further computation.
+
See also Multichannel Expansion.
The following PV UGens are included in the standard SC distribution:
For a full list of FFT UGens, see UGens>FFT in the Browse: UGens>FFT page.
You will still see remnants of the old GUI classes in some SuperCollider help files and quarks. We're still working on updating all the help files. If you encounter such old classes in a quark, you can do a public service by informing the quark author.
The most fundamental element of the GUI is the Window. It occupies a rectangular space on screen within which other GUI elements are displayed. It usually has a bar that displays the window's title and allows for moving it, resizing it and closing it with the controls it displays or through mouse and keyboard interaction. Some of these aspects may be controlled within SuperCollider GUI code, though it is largely platform-dependent how precisely interaction with a window happens and is visually indicated. +
The GUI elements contained within a Window are called views. They all inherit from the basic View class. The view occupies a rectangular space of the window within which it draws itself to display some data or to indicate a mode of interaction between the user and the program. Views receive keyboard and mouse events generated by the user and respond to them by controlling the behavior of the program. They also display information about the state of the program and the data on which it operates. +
There are also special types of views that can contain other views and are thus called containers, for example the CompositeView. They allow for structuring GUI in a hierarchical way. A container view is called a parent of the views it contains, and they are called its children. Hierarchical organization allows to easily change aspects of all the views within a container: if the parent view is hidden, so are all the children; if the parent view is moved, so are they. Children are positioned with coordinates relative to their parent.
The following example shows a window containing a Button, a Slider and a group of StaticText views contained in a CompositeView. When the button is clicked the visibility of the CompositeView is toggled, while interacting with the Slider will move the CompositeView (and consequently all its contents) in horizontal direction.
w = Window.new("GUI Introduction", Rect(200,200,255,100)); +b = Button.new(w,Rect(10,0,80,30)).states_([["Hide"],["Show"]]); +s = Slider.new(w,Rect(95,0,150,30)); +c = CompositeView.new(w,Rect(20,35,100,60)); +StaticText.new(c,Rect(0,0,80,30)).string_("Hello"); +StaticText.new(c,Rect(20,30,80,30)).string_("World!"); +b.action = { c.visible = b.value.asBoolean.not }; +s.action = { c.bounds = Rect( s.value * 150 + 20, 35, 100, 100 ) }; +w.front;+
As a handy alternative to specifying all the dimensions and positions of views explicitly in code, SuperCollider allows for automatic positioning and resizing of views in relation to each other and in relation to window size - at the view creation and dynamically, when window is resized. There is several mechanisms for this purpose.
Views can automatically resize or move when their parent is resized, in one of the nine different ways that define how each of the view's edges will move along with the parent's edges. For documentation see the view's resize method and Resize behaviour document.
w = Window.new("GUI Introduction", Rect(200,200,200,200)); +TextField.new(w,Rect(0,0,200,30)).resize_(2); +Slider.new(w,Rect(0,30,30,170)).resize_(4); +TextView.new(w,Rect(30,30,170,170)).resize_(5); +w.front;+
Decorators are objects that can be assigned to container views to carry the task of positioning the container's child views (currently there exists only one: FlowLayout). After a decorator is assigned to a container, the views created as its children will automatically be positioned in a specific pattern. See documentation of FlowLayout for details.
w = Window.new("GUI Introduction", Rect(200,200,320,320)).front; +// notice that FlowLayout refers to w.view, which is the container view +// automatically created with the window and occupying its entire space +w.view.decorator = FlowLayout(w.view.bounds); +14.do{ Slider(w, 150@20) };+
Layout classes make part of a complex system to manage both position and size of views. Using layouts, only relations of views within a pattern of organization need to be specified and their exact positions as well as sizes will automatically be deduced based on their type (the content they display and the type of interaction they offer) and in accord with principles of good GUI usability. Layouts also position and resize views dynamically, whenever their parent is resized or their contents change. +
See the Layout Management guide for detailed explanation.
w = Window.new("GUI Introduction").layout_( + VLayout( + HLayout( Button(), TextField(), Button() ), + TextView() + ) +).front;+
Views offer various ways to customize their appearance. This ranges from decorative changing of colors they use to draw themselves to controlling how they display various kinds of data.
Colors are represented in GUI code by the Color class. +
A typical color that can be customized is background color - a color of choice can be applied to whatever is considered to be the background of a particular view. Views that display some text will typically also allow customizing its color as well. +
Custom colors may be associated with different changing states of views or data they display, for example: Button allows to associate background and text colors with each one of its states, and will thus switch colors together with state when clicked; ListView allows to set a different background color for each of its items, as well as special background and text colors applied only to the item currently selected. +
Whenever you execute the following example, random colors will be applied to different aspects of the views:
( +w = Window("GUI Introduction").background_(Color.rand).front; +b = Button(w, Rect(10,10,100,30)).states_([ + ["One",Color.rand,Color.rand], + ["Two",Color.rand,Color.rand], + ["Three",Color.rand,Color.rand] +]); +l = ListView.new(w, Rect(10,50,200,100)) + .items_(["One","Two","Three"]) + .colors_([Color.rand,Color.rand,Color.rand]) + .hiliteColor_(Color.blue) + .selectedStringColor_(Color.white); +s = Slider(w, Rect(10, 160, 200, 20)) + .knobColor_(Color.rand) + .background_(Color.rand); +)+
In Qt GUI, the complete set of colors used to draw the views is represented by a palette (see the QPalette class). Using a palette, you can define (most of) the appearance of the whole GUI in one go. +
In the following example, clicking on the button will switch between two palettes. Note however, that the color assigned to the first Button state will beat the red color defined in the palette, and that colors of individual ListView items are not controlled by the palette.
( +x = QPalette.auto(Color.red(0.8), Color.red(0.5)); +y = QPalette.auto(Color.cyan(1.4), Color.cyan(1.8)); +p = QtGUI.palette; +QtGUI.palette = x; +w = Window.new("GUI Introduction").front; +w.onClose = {QtGUI.palette = p}; +Button.new(w, Rect(10,10,100,30)).states_([ + ["Red", Color.black, Color.grey(0.7)], + ["Cyan"] +]).action_({ |b| QtGUI.palette = if(b.value == 0){x}{y} }); +ListView.new(w, Rect(10,50,200,100)) + .items_(["One","Two","Three"]) + .colors_([Color.grey(0.4),Color.grey(0.5),Color.grey(0.6)]); +Slider(w, Rect(10, 160, 200, 20)); +RangeSlider(w, Rect(10, 190, 200, 20)); +)+
Views that display some text will typically allow you to specify a custom font for it. Fonts are represented by the Font class, which can also be queried for the default font used in general, as well as the default font specifically for the "serif", "sans-serif" and "monospace" font types. It can also be queried for all available fonts on the system.
( +w = Window.new("GUI Introduction",Rect(200,200,200,70)).front; +a = [Font.defaultMonoFace, Font.defaultSansFace, Font.defaultSerifFace]; +b = Button.new(w,Rect(10,10,180,50)) + .states_([["Monospace"],["Sans serif"],["Serif"]]) + .font_(a[0]) + .action_({|b| b.font = a[b.value]}); +)+
Complex views may have many other ways to customize how they display the same data. MultiSliderView and EnvelopeView are good examples.
Views and windows can be assigned actions that they will perform whenever a specific event occurs as a result of user's interaction. Technically, an action can be any Object, and when the relevant event occurs, it's value method will be called. For example, it is useful to assign a Function as an action, which allows one to define an arbitrary chunk of code to be performed in response to a GUI event. +
Objects can also be given to views and windows to evaluate on events that are not a direct result of user's interaction, but convey useful information about the view's operation and the state it moved in. In this case they are often differentiated from actions and called hooks. +
Here, we will give an overview of different kinds of actions and hooks. See View: Actions%20in%20general and following sections for precise explanation of how to assign and make use of them.
Views can typically be assigned a default action with their action setter method, which will be performed when the view's primary mode of interaction is invoked. The default action for a Button for example occurs when it is clicked, for a Slider when its handle is moved. +
In the following example, pressing the button will open an exact same window but at different position.
~makeWindow = { var w; + w = Window.new("Evader",Rect(500.rand + 100, 500.rand + 100, 200,50)).front; + Button.new(w,Rect(10,10,180,30)).states_([["Evade"]]).action_(~makeWindow); +}; +~makeWindow.value;+
All the views can be assigned actions to specific mouse and keyboard events, no matter what other effects those events might have on the view or what other specialized actions or hooks the view might trigger on these events. +
You can assign actions to mouse events generated when the mouse pointer enters the space of a view, when it moves over them, and when a mouse button is pressed or released. +
See View: Mouse%20actions for details. +
In the following example the StaticText will report whether the Button is pressed or released.
w = Window.new(bounds:Rect(200,200,200,50)).front; +b = Button.new(w,Rect(10,10,80,30)).states_([["Off"],["On"]]); +t = StaticText(w,Rect(100,10,90,30)).string_("Button released"); +b.mouseDownAction = { t.string = "Button pressed" }; +b.mouseUpAction = { t.string = "Button released" };+ +
You can assign actions to keyboard events generated whenever a key is pressed or released while the view has keyboard focus. Keyboard focus is a state of a view in which it has exclusive priority to respond to keyboard events. A view that has keyboard focus typically in a way visually indicates so. On most platforms, pressing the Tab key will switch the keyboard focus between views in the active window and clicking on a view will give it focus. +
See View: Key%20actions for details. +
Typing text into any of the TextFields in the following example will change the color of the rectangle bellow, for each TextField a different color.
w = Window.new(bounds:Rect(200,200,200,100)).front; +x = TextField(w,Rect(10,10,80,30)); +y = TextField(w,Rect(110,10,80,30)); +t = StaticText(w,Rect(10,40,180,50)); +~reset = {t.background = Color.red}; +x.keyDownAction = {t.background = Color.green}; +x.keyUpAction = ~reset; +y.keyDownAction = {t.background = Color.blue}; +y.keyUpAction = ~reset; +~reset.value;+ +
If a key or mouse event is not handled by the view on which it occurs, it may propagate to the parent view, and trigger the parent's action. See View: Key%20and%20mouse%20event%20processing for detailed explanation.
When a mouse button is pressed on a view together with Cmd(Mac OS) or Ctrl(Other OS) key and the mouse pointer is moved while holding the button, a drag-and-drop operation is initiated - in case the view supports it. Most views have a default object that they export when a drag is attempted. For a Slider it is its value, for a List it is the numeric index of the currently selected item, etc. It is said that the exported object is being dragged. When the dragging gesture ends on another view by releasing the mouse button on top of it, it is said that the dragged object was dropped on another view. A view may respond to various objects dropped on it in different ways. +
It is possible to customize what object a view exports when dragged from and how a view reacts to objects dropped by assigning custom drag and drop actions. +
See View: Drag%20and%20drop for details.
( + w = Window.new.front; + a = Button(w, Rect(10, 10, 200, 20)).states_([["Hi There!"]]); + a.beginDragAction = { a.dragLabel ="I'm dragging: \""++ a.states[0][0]++"\""; a.states[0][0] }; + DragSink(w,Rect(10,40,200,20)).align_(\center).string="Cmd-drag from Button to here"; +)+
Some views can be assigned actions on other events specific to their mode of interaction with the user which you are invited to discover by consulting their documentation.
Hooks are various events that signify important changes of state of GUI elements. Technically they are used the same way as actions, but are distinguished from them to denote events that are not a direct result of the user's interaction. Methods of GUI classes used to assign hooks are usually prefixed with "on". (You will also find this naming pattern in methods of other SuperCollider classes, that have hooks in the same sense). +
For example, one hook that every view as well as Window has is onClose, which is triggered when the window is closed or the view is removed. Other hooks for example exist for the case when a Window becomes or ceases to be the active one.
The UserView is a view that displays and does nothing on itself, but allows you to define how it will be drawn, and for which you can define the entire behavior using mouse, key, and drag and drop actions. For documentation on all of these aspects, see UserView, View, and Pen. The explanation below, however, will demonstrate the basic techniques for designing a custom view. +
You will be using the Pen class to draw the view. Pen is a powerful class that allows you to algorithmically draw using simple visual primitives like lines, arcs, curves, rectangles, ellipses, etc. and fill the shapes with colors and gradients.
Creating a custom view involves the following steps:
You can omit steps which you don't need.
( +var value = 0.5; +w = Window.new.front; + +// (1) create a UserView +v = UserView(w,Rect(50,50,200,20)); + +// (2) define a drawing function using Pen +v.drawFunc = { + // Draw the fill + Pen.fillColor = Color.grey; + Pen.addRect(Rect(0,0, v.bounds.width*value,v.bounds.height)); + Pen.fill; + // Draw the triangle + Pen.fillColor = Color.red; + Pen.moveTo(((v.bounds.width*value)-5) @ v.bounds.height); + Pen.lineTo(((v.bounds.width*value)+5) @ v.bounds.height); + Pen.lineTo(((v.bounds.width*value)) @ (v.bounds.height/2)); + Pen.lineTo(((v.bounds.width*value)-5) @ v.bounds.height); + Pen.fill; + // Draw the frame + Pen.strokeColor = Color.black; + Pen.addRect(Rect(0,0, v.bounds.width,v.bounds.height)); + Pen.stroke; +}; + +// (3) set the default action +v.action = {value.postln; v.refresh}; + +// (4) define mouse actions +v.mouseDownAction = { arg view, x = 0.5,y, m; + //m.postln; + ([256, 0].includes(m)).if{ // restrict to no modifier + value = (x).linlin(0,v.bounds.width,0,1); v.doAction}; +}; + +v.mouseMoveAction = v.mouseDownAction; + +// (5) (optional) define key actions +v.keyDownAction = { arg view, char, modifiers, unicode,keycode; + if (unicode == 16rF700, { value = (value+0.1).clip(0,1) }); + if (unicode == 16rF703, { value = (value+0.1).clip(0,1) }); + if (unicode == 16rF701, { value = (value-0.1).clip(0,1) }); + if (unicode == 16rF702, { value = (value-0.1).clip(0,1) }); + v.doAction; +}; + +// (6) (optional) define drag and drop behavior +v.beginDragAction = {value}; // what to drag +v.canReceiveDragHandler = {View.currentDrag.isNumber}; // what to receive +v.receiveDragHandler = {value = View.currentDrag; v.doAction }; // what to do on receiving + + +// just for testing drag and drop +Slider(w,Rect(50,100,200,20)); + +StaticText(w,Rect(50,150,350,50)).string_("To Test Drag and Drop,\nHold down Cmd (Ctl) Key"); + +)+
If you attempt to interact with a GUI object in the contexts listed above, an error will be thrown. +
Therefore, if you want to use Functions, Routines, Tasks and other similar objects to schedule code that interacts with GUI elements, you must do so using the AppClock, since code scheduled on the AppClock is performed in the main application context. You can of course also reschedule GUI code to the AppClock from within code performed in other contexts, and the 'defer' mechanism is a convenient shorthand for this. +
An example of scheduling GUI code on the AppClock:
w=Window.new.front; +Routine{ + 20.do{ + w.bounds=Rect(200.rand, 200+200.rand, 300,300); + 0.1.wait; + }; + w.close; +}.play(AppClock)+ +
The same thing using the SystemClock in combination with the defer mechanism:
w=Window.new.front; +Routine{ + 20.do{ + {w.bounds=Rect(200.rand, 200+200.rand, 300,300) }.defer; // you must defer this + 0.1.wait; + }; + {w.close}.defer; // you must defer this +}.play(SystemClock)+ +
As mentioned above, using the GUI system is also not allowed in code performed directly in response to OSC messages (this includes functions given to all kinds of OSC responder classes). The same solutions as above apply:
2D chaos ugens similar to sc chaos ugens, but x is mapped to freq, y to amplitude. arguments are minfreq, maxfreq, parameters. where applicable, the parameters arguments are in the same order as sc chaos ugens
{ Henon2DC.ar(440, 1880, LFNoise2.kr(1, 0.1, 1.3), 0.3).dup }.play +{ Gbman2DL.ar(200, 800).dup }.play + +// each also has a corresponding trigger +{ Ringz.ar(StandardTrig.ar(10, 20), 2205, 0.02).fold2(0.5).dup }.play + +// can be k-rate +{ SinOsc.ar(Latoocarfian2DN.kr(4, 16, mul:220, add:440)).cubed.fold2(0.6).dup }.play + +// trig & oscillator +( +{ +var trig, sig; +trig=LatoocarfianTrig.kr(4, 12, [1.1, 1.5]); +sig=Latoocarfian2DC.ar(420, 8200, Lag.kr(TRand.kr(0.4, 0.8, trig), 0.1), Lag.kr(TRand.kr(2.0, 3.0, trig), 0.1), d:[1.0, 1.05], mul:Latch.kr(trig, trig)); +(sig.cubed * 2).tanh +}.play +)+ +
and there are a few chaos patterns as well. Phenon, etc. (see class file). these output arrays where applicable and are normalized from 0-1 by default (you can remove normalization by setting the n argument to false).
p=Pstandard.new; +x=p.asStream; + +20.do({ x.next.postln });+ +
Multiple channels of audio are represented as Arrays.
s.boot; +// one channel +{ Blip.ar(800,4,0.1) }.play; + +// two channels +{ [ Blip.ar(800,4,0.1), WhiteNoise.ar(0.1) ] }.play;+ +
Each channel of output will go out a different speaker, so your limit here is two for a stereo output. If you have a supported multi channel audio interface or card then you can output as many channels as the card supports. +
All UGens have only a single output. This uniformity facilitates the use of array operations to perform manipulation of multi channel structures. +
In order to implement multichannel output, UGens create a separate UGen known as an OutputProxy for each output. An OutputProxy is just a place holder for the output of a multichannel UGen. OutputProxies are created internally, you never need to create them yourself, but it is good to be aware that they exist so you'll know what they are when you run across them.
// look at the outputs of Pan2: +Pan2.ar(PinkNoise.ar(0.1), FSinOsc.kr(3)).dump; + +play({ Pan2.ar(PinkNoise.ar(0.1), FSinOsc.kr(1)); });+
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. All but a few special unit generators perform multichannel expansion. Only Arrays are expanded, no other type of Collection, not even subclasses of Array.
{ Blip.ar(500,8,0.1) }.play // one channel + +// the array in the freq input causes an Array of 2 Blips to be created : +{ Blip.ar([499,600],8,0.1) }.play // two channels + +Blip.ar(500,8,0.1).postln // one unit generator created. + +Blip.ar([500,601],8,0.1).postln // two unit generators created.+ +
Multichannel expansion will propagate through the expression graph. When a unit generator constructor is called with an array of inputs, it returns an array of instances. If that array is the input to another constructor, then another array is created, and so on.
{ RLPF.ar(Saw.ar([100,250],0.05), XLine.kr(8000,400,5), 0.05) }.play; + +// the [100,250] array of frequency inputs to Saw causes Saw.ar to return +// an array of two Saws, that array causes RLPF.ar to create two RLPFs. +// Both RLPFs share a single instance of XLine.+ +
When a constructor is parameterized by two or more arrays, then the number of channels created is equal to the longest array, with parameters being pulled from each array in parallel. The shorter arrays will wrap. +
for example, the following:
Pulse.ar([400, 500, 600],[0.5, 0.1], 0.2)+ +
is equivalent to:
[ Pulse.ar(400,0.5,0.2), Pulse.ar(500,0.1,0.2), Pulse.ar(600,0.5,0.2) ]+ +
A more complex example based on the Saw example above is given below. In this example, the XLine is expanded to two instances, one going from 8000 Hz to 400 Hz and the other going in the opposite direction from 500 Hz to 7000 Hz. These two XLines are 'married' to the two Saw oscillators and used to parameterize two copies of RLPF. So on the left channel a 100 Hz Saw is filtered from 8000 Hz to 400 Hz and on the right channel a 250 Hz Saw is filtered from 500 Hz to 7000 Hz.
{ RLPF.ar(Saw.ar([100,250],0.05), XLine.kr([8000,500],[400,7000],5), 0.05) }.play;+
Many operators and methods also multichannel expand. For example all common math operators:
{ Saw.ar([100,250]) * [0.5,0.8] }.play; +{ Saw.ar(LFNoise1.kr(1).range(0,100) + [100,250]) }.play;+ +
Also the various UGen convenience functions like .clip2
, .lag
and .range
:
{ Saw.ar(LFNoise1.kr(1).range(100,[200,300])) }.play; +{ Saw.ar(LFPulse.kr(1).range(100,[200,300]).lag([0.1,0.2])) }.play;+ +
The expansion is handled by wrapper-methods defined in SequenceableCollection. +
You can use Object: -multiChannelPerform to do multichannel expansion with any method on any kind of object:
["foo","bar"].multiChannelPerform(\toUpper);+ +
The shorter arrays wrap:
["foo","bar","zoo"].multiChannelPerform('++', ["l","ba"])+
The method flop swaps columns and rows, allowing to derive series of argument sets:
( +SynthDef("help_multichannel", { |out=0, freq=440, mod=0.1, modrange=20| + Out.ar(out, + SinOsc.ar( + LFPar.kr(mod, 0, modrange) + freq + ) * EnvGate(0.1) + ) +}).send(s); +)+ +
( +var freq, mod, modrange; + +freq = Array.exprand(8, 400, 5000); +mod = Array.exprand(8, 0.1, 2); +modrange = Array.rand(8, 0.1, 40); + +fork { + [\freq, freq, \mod, mod, \modrange, modrange].flop.do { |args| + args.postln; + Synth("help_multichannel", args); + 0.3.wait; + } +}; +)+ +
Similarly, Function:flop returns an unevaluated function that will expand to its arguments when evaluated:
( +SynthDef("blip", { |out, freq| + Out.ar(out, + Line.ar(0.1, 0, 0.05, 1, 0, 2) * Pulse.ar(freq * [1, 1.02]) + ) +}).add; + +a = { |dur=1, x=1, n=10, freq=400| + fork { n.do { + if(x.coin) { Synth("blip", [\freq, freq]) }; + (dur / n).wait; + } } +}.flop; +) + +a.value(5, [0.3, 0.3, 0.2], [12, 32, 64], [1000, 710, 700]);+
Some UGens create stereo output from mono input, and might not behave as expected regarding multichannel expansion. +
For example, Pan2 :
{ Pan2.ar(SinOsc.ar([500,600]),[-0.5,0.5]) }.play;+ +
The expectation here might be that the two sines would get individual pan positions. And they do, but Pan2 expands into two stereo ugens nested in an outer array, resulting in a total of four output channels. play
will add an Out UGen for each of them, resulting in both Pan2's writing to the same output bus:
Pan2.ar(SinOsc.ar([500,600]),[-0.5,0.5]) + +// prints: +// [ [ an OutputProxy, an OutputProxy ], [ an OutputProxy, an OutputProxy ] ]+ +
In this case, the solution is simply to sum the nested four channels into a single stereo-channel:
{ Pan2.ar(SinOsc.ar([500,600]),[-0.5,0.5]).sum }.play;+ +
If we take a look at the resulting UGen graph of the code above, we can see that it is correct. The two Pan2 is mixed together to create a single stereo output:
{ Pan2.ar(SinOsc.ar([500,600]),[-0.5,0.5]).sum }.asSynthDef.dumpUGens + +// prints: +// [ 0_Control, scalar, nil ] +// [ 1_SinOsc, audio, [ 500, 0 ] ] +// [ 2_Pan2, audio, [ 1_SinOsc, -0.5, 1 ] ] +// [ 3_SinOsc, audio, [ 600, 0 ] ] +// [ 4_Pan2, audio, [ 3_SinOsc, 0.5, 1 ] ] +// [ 5_+, audio, [ 2_Pan2[0], 4_Pan2[0] ] ] +// [ 6_+, audio, [ 2_Pan2[1], 4_Pan2[1] ] ] +// [ 7_Out, audio, [ 0_Control[0], 5_+, 6_+ ] ]+
Some unit generators such as Klank require arrays of values as inputs. Since all arrays are expanded, you need to protect some arrays by a Ref object. A Ref instance is an object with a single slot named 'value' that serves as a holder of an object. Ref.new(object)
is one way to create a Ref, but there is a syntactic shortcut. The backquote `
is a unary operator that is equivalent to calling Ref.new(something)
. So to protect arrays that are inputs to a Klank or similar UGens you write:
Klank.ar(`[[400,500,600],[1,2,1]], z)+ +
You can still create multiple Klanks by giving it an array of Ref'ed arrays.
Klank.ar([ `[[400,500,600],[1,2,1]], `[[700,800,900],[1,2,1]] ], z)+ +
is equivalent to:
[ Klank.ar(`[[400,500,600],[1,2,1]], z), Klank.ar(`[[700,800,900],[1,2,1]], z)]+ +
Also the Refs multichannelExpand when passed to a Klank:
Klank.ar(`[[[400,500,600], [700,800,900]],[1,2,1]], z)+ +
, which is is equivalent to:
[ Klank.ar(`[[400,500,600],[1,2,1]], z), Klank.ar(`[[700,800,900],[1,2,1]], z)]+
The Mix object provides the means for reducing multichannel arrays to a single channel.
Mix.new([a, b, c]) // array of channels+ +
or
[a, b, c].sum+ +
is equivalent to:
a + b + c // mixed to one+ +
Mix is more efficient than using + since it can perform multiple additions at a time. But the main advantage is that it can deal with situations where the number of channels is arbitrary or determined at runtime.
// three channels of Pulse are mixed to one channel +{ Mix.new( Pulse.ar([400, 501, 600], [0.5, 0.1], 0.1) ) }.play+ +
Multi channel expansion works differently for Mix. Mix takes one input which is an array (one not protected by a Ref). That array does not cause copies of Mix to be made. All elements of the array are mixed together in a single Mix object. On the other hand if the array contains one or more arrays then multi channel expansion is performed one level down. This allows you to mix an array of stereo (two element) arrays resulting in one two channel array. For example:
Mix.new( [ [a, b], [c, d], [e, f] ] ) // input is an array of stereo pairs+ +
is equivalent to:
// mixed to a single stereo pair +[ Mix.new( [a, c, e] ), Mix.new( [b, d, f] ) ]+ +
Currently it is not recursive. You cannot use Mix on arrays of arrays of arrays. +
Here's a final example illustrating multi channel expansion and Mix. By changing the variable 'n' you can change the number of voices in the patch. How many voices can your machine handle?
( +{ + var n; + n = 8; // number of 'voices' + Mix.new( // mix all stereo pairs down. + Pan2.ar( // pan the voice to a stereo position + CombL.ar( // a comb filter used as a string resonator + Dust.ar( // random impulses as an excitation function + // an array to cause expansion of Dust to n channels + // 1 means one impulse per second on average + Array.fill(n, 1), + 0.3 // amplitude + ), + 0.01, // max delay time in seconds + // array of different random lengths for each 'string' + Array.fill(n, {0.004.rand+0.0003}), + 4 // decay time in seconds + ), + Array.fill(n,{1.0.rand2}) // give each voice a different pan position + ) + ) +}.play; +)+
FileDialog can hang — currently we are only able to reproduce on KDE, but other OS's could be affected. +
On Windows and Linux, running code with Ctrl+Enter in the help browser (not the editor) only evaluates the current line. +
QtWebEngine, a hard dependency of SCLang and SCIDE, is difficult or impossible to install in some environments. Work is underway to make it an optional component, but this will not happen in time for 3.10.
A NO_X11
option has been added to the build system so that server plugins requiring an X server such as MouseX can be omitted.
sclang and scide have long been stuck with Qt 5.5 due to Qt dropping QtWebKit for QtWebEngine. They have been upgraded for compatibility with Qt 5.7+. We recommend using the most recent version of Qt. The impacts of this change include:
The minimum required version is now CMake 3.5 instead of CMake 2.8. +
scel (the emacs package) is now a submodule.
Many issues with Unicode paths on Windows were fixed in 3.9. A few remaining cases involving sound files remained, and are now fixed:
/b_read
family of commands in scsynthSoundFileView
in the sclang GUIFixed a build failure with the CMake option SYSTEM_YAMLCPP=on
.
+
Fixed a misleading deprecation warning when CMAKE_INSTALL_PREFIX
is set to the home directory in Linux.
+
Fixed CMAKE_PREFIX_PATH
incorrectly defaulting to /usr/local/
on macOS under some conditions.
supernova now has latency compensation.
scsynth's latency compensation had a math error that ended up doubling the latency. It is fixed now. +
For consistency with scsynth, supernova no longer requires the final argument to /b_allocReadChannel
.
+
One second folks, takin' a quick break here. Gotta get my morning cuppa! Alright, we're back. +
Fixed a missing newline in some of supernova's error messages. +
Fixed errors in supernova's /s_getn
.
+
Fix supernova's response to /g_queryTree
so it matches scsynth.
Fixed clicks in Convolution2L.
Menus are now supported in the Qt GUI. See help files for Menu
, MenuAction
, ToolBar
, and MainMenu
.
+
Added wrappers for over 100 special mathematical functions (gamma function, Bessel functions, elliptic integrals, etc.) from the Boost library. +
SerialPort now works on Windows. +
FileDialog
and Dialog
now support a "path" argument that specifies a default directory when the dialog appears.
+
QTreeView
has a new method: setColumnWidth
.
Breaking change: Float:asString
now always produces a decimal point, so 3.0.asString
is now "3.0"
instead of "3"
.
+
Breaking change: The server
argument has changed to target
in Function:asBuffer
, Function:loadToFloatArray
, and Function:plot
, and now allows spawning the plotting synth relative to a group or node rather than just a server.
+
Breaking change: File:mkdir
now returns a Boolean indicating whether the operation was successful. Previously, it returned the File object.
+
Scrollbars now always appear for ScrollView on Linux an Windows, as a temporary workaround for a very odd dependency on the use of the scroll wheel.
Removed some unused Qt dependencies from the build system.
Breaking change: Fixed a long-standing math error in SimpleNumber:expexp
.
+
Fixed extreme CPU usage of sclang when built without Qt. +
On Windows, the directory where extensions were installed was accidentally changed in 3.9. It has been reverted. +
Fixed a crash when calling File.copy
when the destination exists.
+
Fixed two Array:lace
issues: a crash when any element is an empty array, and an error when no length argument is provided and any element is not an array.
+
Fixed conditions where Integer:forBy
can cause sclang to freeze when the step size is 0 or a floating point value with an absolute value less than 1.
+
Fixed some incorrect output in FunctionDef:dumpByteCodes
.
+
Fixed Node:release
getting stuck on negative release times, which are now equivalent to 0.
+
Fixed ==
on Signal
objects randomly returning the wrong result.
UnitTest.passVerbosity
allows changing the verbosity of test failure reports. See the UnitTest
help file for more information.
+
Added new UGen methods .snap
and .softRound
.
+
Node:query
has a new action
argument, allowing specification of a callback function.
+
.degrad
and .raddeg
are now implemented for UGens.
The default behavior of SerialPort.devices
pattern matching has been improved to match a wider variety of devices on macOS and Linux.
+
Internal calls to .interpret
have been removed from Color.fromHexString
and History.unformatTime
, improving both performance and security.
SerialPort.cleanupAll
is deprecated.
+
Providing an integer index for SerialPort.new
is deprecated.
BufWr.ar
no longer allows its input signals to be control rate, which caused the server to read from garbage memory.
+
Buffer:query
returned incorrect results if multiple query messages are sent at once. This has been fixed.
+
Fixed fragilities in path joining methods such as +/+
, withTrailingSlash
, and withoutTrailingSlash
.
+
Fixed bugs when certain pattern classes are passed in 0 as the number of repeats. +
Fixed Event.addEventType
ignoring the parentEvent
argument.
+
Fixed Pkey
being skipped because the default number of repeats is nil
instead of inf
.
+
Fixed some harmless but annoying errors about extensions of nonexistent classes when sclang is built without Qt. +
ProxySpace:linkDoc
was broken — switching documents did not actually change ProxySpaces. This is fixed now.
+
Recorder:prepareForRecord
produced an error if the recordings path does not exist. It now makes the directory if it doesn't exist.
+
Fixed bugs when providing multiple paths in ServerOptions:ugensPluginPath
.
+
Fixed HelpBrowser
(the class, not the IDE help browser) being unusable since it didn't trigger rendering of help files when links are clicked.
+
Fixed some bugs in EnvGate
: throwing an error when fadeTime
is a constant rather than a UGen input, and i_level
not behaving as documented.
+
Fixed occasional hangs when rebooting supernova. +
Fixed confusing user feedback with the "Check for updates" button in the quarks GUI. +
Buffer
methods ensure that the buffer number in outbound OSC messages is an integer. This fixes errors in supernova, which is stricter than scsynth about the buffer number type.
+
Fixed confusing user feedback with the "Check for updates" button in the quarks GUI. +
Fixed missing default arguments in fold2
, wrap2
, and excess
methods of Collection
for consistency with SimpleNumber
.
+
Fixed incorrect template matching behavior in OSCFunc
and related functionality.
+
Fixed "Message 'extension' not understood" preventing Image
from working.
The IDE has a prettier default theme. The old theme still exists as "classic." +
The IDE now properly highlights scale degree literals like 4s
.
The IDE has a prettier default theme. The old theme still exists as "classic." +
The IDE now has a unified look across all platforms, and its color scheme adapts to match the editor theme. +
The SCDoc TOC and menubar have been redesigned again. +
Various tweaks to the appearance of the IDE: nicer tabs, better border colors.
When starting the IDE, detached docklet sometimes spawn as unresponsive. This has been fixed. +
Syntax colors in the help browser now match the IDE. +
Only one preference window can be open at a time now. +
Fixed tabs reversing in order when restoring a session.
The 3.12 release brings new features, countless bugfixes, as well as project and documentation updates. See the repository for all the changes. A big thank you to all developers for your contributions! +
Change log highlights:
Supernova on Windows (#4763) +
Bela support (#5295) +
macOS Big Sur support (#5298, #5356)
Bigger build matrix add CI jobs to implement platform support RFC (#4906) +
Updated portaudio submodule (#4925) +
Implement RFC 10: Replace oppressive terminology with more accurate alternatives (#5251, #5424, #5470) +
Increase required C++ standard to C++17 (#5396) +
Move CI from Travis/AppVeyor to GitHub Actions (#5261, #5273 #5371, #5377) +
Run TestSuite in CI (#5332)
Builds for older macOS systems (#5537)
Exclude default paths: change from command line parameter to language file flag (#3733)
MIDI realtime messages: Push correct number of values onto the stack (#5200)
Fuzzy equals add fuzzy array comparisons (#4468) +
String -runInTerminal
on Windows (#4882)
+
Provide suggestions on method not found
: (#4866)
+
Add "lazy equality" |==| operator (#5183) +
New class: CondVar (#5436, #5448, #5456)
Various UnitTest fixes (#5461, #5457)
Clip values on hardware out busses (macOS) (5110, #5454)
Supernova bind to the specified address (#5474) +
Supernova on macOS: avoid resampling when talking to audio hardware (#4477)
Servers not booting on Windows if some system logs are missing (#5393) +
macOS builds include a custom build of libsndfile to support older macOS versions (#5518)
PanAz: initialize amps in Ctor (#4973) +
EnvGen fixes (#5217, #4921, #4793)
Classname highlighting before introspection is available (#5438)
OSC communication between programs is often done to send messages from one application to another, possibly with the applications running on different computers. In SuperCollider this communication is done by creating a NetAddr of the target application and creating an OSCFunc to listen to another application. The underlying protocol of OSC is either UDP or TCP.
To establish communication to another application, you need to know on which port that application is listening. For example if an application is listening on port 7771, we can create a NetAddr and send it a message: +
To listen to another application, that application needs to send a message to the port SuperCollider is listening on. Normally the default port is 57120, but it could be something different if that port was already bound when SC started. The current default port can be retrieved with + +
Or you can retrieve both the IP and the port with: + +
You can open additional ports using Main: -openUDPPort. This will return a Boolean indicating whether SC succeeded in opening the new port. Or you can just pass a custom port as the recvPort argument to OSCFunc and it will open it automatically if not already open. + +
To listen to incoming messages, an OSCFunc needs to be created in SuperCollider. If the sending application has a fixed port it sends message from, you can set the OSCFunc to listen only to messages coming from that IP and port: + +
Some applications (notably Pd and Max) do not send messages from a fixed port, but instead use a different port each time they send out a message, or each time a patch starts up it picks a random port. In that case the OSCFunc needs to be set up, so that it listens to messages coming from anywhere. You do this by passing nil as the srcID argument. +
OSCFunc has a convenience method, OSCFunc: *trace which posts all incoming OSC messages: +
All incoming OSC messages call the message recvOSCmessage in Main. If needed, one can add a custom Function or other object to Main's recvOSCFunc variable. Although one can do this directly using the corresponding setter, it is better to use the Main: -addOSCRecvFunc and Main: -removeOSCRecvFunc to avoid overwriting any other functions that may have been added by class code. +
Inbound OSC messages must have type tags, or an error will be thrown. +
The following conversions are supported outbound:
$N
(nil), $T
(true), $F
(false), $I
(infinity/impulse). Be careful with this -- if the destination entity doesn't agree that the tag consumes zero bytes, the remaining part of the OSC message may be corrupted!If NetAddr.useDoubles
is set to true, then in outbound messages, sclang will use the "d" (double) type tag instead of "f". OSC doubles are 64-bit. It is up to you to ensure that the receiver understands "d".
+
The following type tags are supported inbound:
+inf
nil
"r" (RGBA color), "S" (symbol), and "[" and "]" (array start and end) are not supported. +
If an unrecognized tag is encountered, sclang will make that unrecognized tag into a Char object and add that to the OSC message. It will then skip ahead in the OSC message as if it were reading a string -- it looks for the next null byte and then skips 0-3 bytes for 4-byte alignment.
To ensure correct timing of events on the server, OSC messages may be sent with a time stamp, indicating the precise time the sound is expected to hit the hardware output.
In the SuperCollider language, the time stamp is generated behind the scenes based on a parameter called "latency." +
To understand how latency works, we need to understand the concepts of logical time and physical time. +
Every clock in SuperCollider has both a logical time and physical time.
While a scheduled function or event is executing, logical time holds steady at the "expected" value. That is, if the event is scheduled for 60 seconds exactly, throughout the event's execution, the logical time will be 60 seconds. If the event takes 2 seconds to execute (very rare), at the end of the event, the logical time will still be 60 seconds but the physical time will be 62 seconds. If the next event is to happen 3 seconds after the first began, its logical time will be 63 seconds. Logical time is not affected by fluctuations in system performance. +
This sequencing example illustrates the difference. It's written deliberately inefficiently to expose the problem more clearly. Two copies of the same routine get started at the same time. On a theoretically perfect machine, in which operations take no time, we would hear both channels in perfect sync. No such machine exists, and this is obviously not the case when you listen. The routines also print out the logical time (clock.beats) and physical time (clock.elapsedBeats) just before playing a grain. + +
This is the output: + +
Notice that even though the left and right channel patterns were scheduled for exactly the same time, the events don't complete executing at the same time. Further, the Synth(...) call instructs the server to play the synth immediately on receipt, so the right channel will be lagging behind the left by about 2 ms each event--and not by the same amount each time. Timing, then, is always slightly imprecise. +
This version is the same, but it generates each synth with a 1/4 second latency parameter: + +
By using makeBundle with a time argument of 0.25, the \s_new messages for the left and right channel are sent with the same timestamp: the clock's current logical time plus the time argument. Note in the table that both channels have the same logical time throughout, so the two channels are in perfect sync. +
These routines are written deliberately badly. If they're made maximally efficient, the synchronization will be tighter even without the latency factor, but it can never be perfect. You'll also see this issue, however, if you have several routines executing and several of them are supposed to execute at the same time. Some will execute sooner than others, but their logical time will all be the same. If they're all using the same amount of latency, you will still hear them at the same time. +
In general, all synths that are triggered by live input (MIDI, GUI, HID) should specify no latency so that they execute as soon as possible. All sequencing routines should use latency to ensure perfect timing. +
The latency value should allow enough time for the event to execute and generate the OSC bundle, and for the server to interpret the message and render the audio in time to reach the hardware output on time. If the client and server are on the same machine, this value can be quite low. Running over a network, you must allow more time. (Latency compensates for network timing jitter also.) +
Pbind automatically imports a latency parameter from the server's latency variable. You can set the default latency for event patterns like this: + +
Here are three ways to play a synth with the latency parameter: +
SuperColliderAU is an AudioUnit wrapper that allows using SuperCollider servers inside AudioUnits hosts on macOS. The embedded server may be controlled over OSC as usual. In addition, it may be packed with a synth definition and a configuration file that defines its parameters.
Copy the bundle "SuperColliderAU.component" into /Library/Audio/Plug-ins/Components or in ~/Library/Audio/Plug-ins/Components
Start up an Audio Units host application. Common hosts include Apple Logic and Ableton Live. A list of supported hosts can be found in this page of the SuperCollider swiki (feel free to update it): +
http://swiki.hfbk-hamburg.de:8888/MusicTechnology/823 +
Find SuperColliderAU among the rest of AudioUnit plugins and add an instance to a track (check the manual of your host if you don't know how to do this). A panel will appear telling you which port the server is listening to for OSC messages. +
Now you can run this code from within the SuperCollider language to talk directly with the embedded server: +
As an AudioUnit plugin, SuperColliderAU is packaged in a component bundle. All the files needed by SuperColliderAU can be found in the Resources folder inside the bundle:
Stores configuration parameters for the server (see ServerOptions)
Stores parameter configuration for standalone plugins. SuperColliderAU will use this to display and set the parameters of your synth definition. Note that the default values must be the same for the synth definition, they will not be set by the wrapper. The value in this file is only for display.
For standalone plugins you should have your synth definition here. When controlling the server remotely you can just send the synth definitions.
Here you need all the SC plugins you intend to use with that server.
Besides controlling SuperColliderAU from within the language, you can create AudioUnits plugins that are controlled from the host using the default GUI. This can be done manually by duplicating SupercolliderAU.component, adding a synthdef and associated SC plugins and editing pluginSpec.plist. However, if you want an AudioUnit with a unique identifier (the identifier is what you see from within the host GUI) you have to recompile its resource file (with the new identifier) using Rez. This program is included in Apple's Developer Tools. A helper class that automates all this process is maintained in the AudioUnitBuilder quark. Using the class AudioUnitBuilder you can create standalone AudioUnit plugins without leaving SuperCollider.
Using a program such as SuperCollider introduces a number of issues regarding timing and order of execution. Realtime audio synthesis requires that samples are calculated and played back at a certain rate and on a certain schedule, in order to avoid dropouts, glitches, etc. Other tasks, such as loading a sample into memory, might take arbitrary amounts of time, and may not be needed within a definite timeframe. This is the difference between synchronous and asynchronous tasks. +
Problems can arise when synchronous tasks are dependent upon the completion of asynchronous ones. For instance trying to play a sample that may or may not have been completely loaded yet. +
In SC2 this was relatively simple to handle. One scheduled synchronous tasks during synthesis, i.e. within the scope of a Synth.play
. Asynchronous tasks were executed in order, outside of synthesis. Thus one would first create buffers, load samples into them, and then start synthesis and play them back. The interpreter made sure that each step was only done when the necessary previous step had been completed.
+
In SC3 the separation of language and synth apps creates a problem: How does one side know that the other has completed necessary tasks, or in other words, how does the left hand know if the right is finished? The flexibility gained by the new architecture introduces another layer of complexity, and an additional demand on the user. +
A simple way to deal with this is to execute code in blocks. In the following code, for instance, each block or line of code is dependent upon the previous one being completed.
// Execute these one at a time + +// Boot the server +s.boot; + +// Compile a SynthDef and write it to disk +( +SynthDef("Help-SynthDef", { arg out = 0; + Out.ar(out, PinkNoise.ar(0.1)) +}).writeDefFile; +) + +// Load it into the server +s.loadSynthDef("Help-SynthDef"); + +// Create a Synth with it +x = Synth.new("Help-SynthDef", s); + +// Free the node on the server +x.free; + +// Allow the client-side Synth object to be garbage collected +x = nil;+ +
In the previous example it was necessary to use interpreter variables (the variables a-z, which are declared at compile time) in order to refer to previously created objects in later blocks or lines of code. If one had declared a variable within a block of code (i.e. var mySynth;
) than it would have only persisted within that scope. (See the helpfile Scoping and Closure for more detail.)
+
This style of working, executing lines or blocks of code one at a time, can be very dynamic and flexible, and can be quite useful in a performance situation, especially when improvising. But it does raise the issues of scope and persistence. Another way around this that allows for more descriptive variable names is to use environment variables (i.e. names that begin with ~, so ~mysynth;
see the Environment helpfile for details). However, in both methods you become responsible for making sure that objects and nodes do not persist when you no longer need them.
( +SynthDef("Help-SynthDef", { arg out = 0; + Out.ar(out, PinkNoise.ar(0.1)) +}).send(s); +) + +// make a Synth and assign it to an environment variable +~mysynth = Synth.new("Help-SynthDef", s); + +// free the synth +~mysynth.free; + +// but you've still got a Synth object +~mysynth.postln; + +// so remove it from the Environment so that the Synth will be garbage collected +currentEnvironment.removeAt(\mysynth);+ +
But what if you want to have one block of code which contains a number of synchronous and asynchronous tasks. The following will cause an error, as the SynthDef that the server needs has not yet been received.
// Doing this all at once produces the error "FAILURE /s_new SynthDef not found" +( +var name; +name = "Rand-SynthDef" ++ 400.0.rand; // use a random name to ensure it's not already loaded + +SynthDef(name, { arg out=0; + Out.ar(out, PinkNoise.ar(0.1)) +}).send(s); + +Synth.new(name, s); +)+ +
A crude solution would be to schedule the dependant code for execution after a seemingly sufficient delay using a clock.
// This one works since the def gets to the server app first +( +var name; +name = "Rand-SynthDef" ++ 400.0.rand; + +SynthDef(name, { arg out = 0; + Out.ar(out, PinkNoise.ar(0.1)) +}).send(s); + +SystemClock.sched(0.05, {Synth.new(name, s);}); // create a Synth after 0.05 seconds +)+ +
Although this works, it's not very elegant or efficient. What would be better would be to have the next thing execute immediately upon the previous thing's completion. To explore this, we'll look at an example which is already implemented. +
You may have realized that first example above was needlessly complex. SynthDef-play will do all of this compilation, sending, and Synth creation in one stroke of the enter key.
// All at once +( +SynthDef("Help-SynthDef", { arg out = 0; + Out.ar(out, PinkNoise.ar(0.1)) +}).play(s); +)+ +
Let's take a look at the method definition for SynthDef-play and see what it does.
play { arg target,args,addAction=\addToTail; + var synth, msg; + target = target.asTarget; + + synth = Synth.basicNew(name,target.server); // create a Synth, but not a synth node + msg = synth.newMsg(target, addAction, args);// make a message that will add a synth node + this.send(target.server, msg); // ** send the def, and the message as a completion message + ^synth // return the Synth object + }+ +
This might seem a little complicated if you're not used to mucking about in class definitions, but the important part is the second argument to this.send(target.server, msg);
. This argument is a completion message, it is a message that the server will execute when the send action is complete. In this case it says create a synth node on the server which corresponds to the Synth object I've already created, when and only when the def has been sent to the server app. (See the helpfile Server Command Reference for details on messaging.)
+
Many methods in SC have the option to include completion messages. Here we can use SynthDef-send to accomplish the same thing as SynthDef-play:
// Compile, send, and start playing +( +SynthDef("Help-SynthDef", { arg out=0; + Out.ar(out, PinkNoise.ar(0.1)) +}).send(s, ["s_new", "Help-SynthDef", x = s.nextNodeID]); +// this is 'messaging' style, see below +) +s.sendMsg("n_free", x);+ +
The completion message needs to be an OSC message, but it can also be some code which when evaluated returns one:
// Interpret some code to return a completion message. The .value is needed. +// This and the preceding example are essentially the same as SynthDef.play +( +SynthDef("Help-SynthDef", { arg out=0; + Out.ar(out, PinkNoise.ar(0.1)) +}).send(s, {x = Synth.basicNew("Help-SynthDef"); x.newMsg; }.value); // 'object' style +) +x.free;+ +
If you prefer to work in 'messaging' style, this is pretty simple. If you prefer to work in 'object' style, you can use the commands like newMsg
, setMsg
, etc. with objects to create appropriate server messages. The two proceeding examples show the difference. See the Node Messaging helpfile for more detail.
+
In the case of Buffer objects a function can be used as a completion message. It will be evaluated and passed the Buffer object as an argument. This will happen after the Buffer object is created, but before the message is sent to the server. It can also return a valid OSC message for the server to execute upon completion.
( +SynthDef("help-Buffer",{ arg out=0, bufnum; + Out.ar( + out, + PlayBuf.ar(1,bufnum,BufRateScale.kr(bufnum)) + ) +}).load(s); + +y = Synth.basicNew("help-Buffer"); // not sent yet + +b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav", action: { arg buffer; + // synth add its s_new msg to follow + // after the buffer read completes + y.newMsg(s,\addToTail,[\bufnum,buffer]) + } // .value NOT needed, unlike in the previous example +); +) + +// when done... +y.free; +b.free;+ +
The main purpose of completion messages is to provide OSC messages for the server to execute immediately upon completion. In the case of Buffer there is essentially no difference between the following:
( +b = Buffer.alloc(s, 44100, + completionMessage: { arg buffer; ("bufnum:" + buffer).postln; } +); +) + +// this is equivalent to the above +( +b = Buffer.alloc; +("bufnum:" + b).postln; +)+ +
One can also evaluate a function in response to a 'done' message, or indeed any other one, using an OSCFunc. See its help file for details.
( +SynthDef("help-SendTrig",{ + SendTrig.kr(Dust.kr(1.0), 0, 0.9); +}).send(s); + +// register to receive this message +a = OSCFunc({ arg msg, time; + ("This is the done message for the SynthDef.send:" + [time, msg]).postln; +}, '/done', a.addr).oneShot; // remove me automatically when done +b = OSCFunc({ arg msg, time; + [time, msg].postln; +}, '/tr', s.addr); +c = OSCFunc({ arg msg; + "this is another call".postln; +}, '/tr', s.addr); +) + +x = Synth.new("help-SendTrig"); +b.free; +c.free; +x.free;+ +
SuperCollider is an audio server, programming language, and IDE for sound synthesis and algorithmic composition.
These are useful starting points for getting help on SuperCollider:
SuperCollider is free software published under the GPL: Licensing. +
These help files are published under the Creative Commons CC-BY-SA-3 license: HelpDocsLicensing.
License for SuperCollider Help
+ ++ +
SuperCollider help documentation is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
+ +Executable SuperCollider code remains under the GPL as stated in SuperCollider's main license conditions.
+ +To supply attribution for a help document (which may have multiple contributors), unless otherwise indicated you may credit "SuperCollider 3 documentation contributors".
+ ++ +
(Note for editors: Please edit the source directly. This is because the HTML source has embedded metadata.)
+ + + diff --git a/doc-schelp/Help-3.12.2/Other/Licensing.html b/doc-schelp/Help-3.12.2/Other/Licensing.html new file mode 100644 index 0000000..55c7dff --- /dev/null +++ b/doc-schelp/Help-3.12.2/Other/Licensing.html @@ -0,0 +1,32 @@ + + + + + + +SuperCollider Licensing
+SuperCollider is copyright © James McCartney and many other contributors.
+SuperCollider is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
+--
+Some further notes:
+SuperCollider was originally published under the terms of version 2 or later of the GPL, but the application now includes some GPL3-licensed code. If you have a need specifically for GPL2 compatibility then it is possible to recompile SuperCollider from source without the GPL3 elements.
+SuperCollider's help documentation is published under a Creative Commons license.
+ + diff --git a/doc-schelp/Help-3.12.2/Other/ccbysa3_88x31.png b/doc-schelp/Help-3.12.2/Other/ccbysa3_88x31.png new file mode 100644 index 0000000..f0a944e Binary files /dev/null and b/doc-schelp/Help-3.12.2/Other/ccbysa3_88x31.png differ diff --git a/doc-schelp/Help-3.12.2/Overviews/BBCut.html b/doc-schelp/Help-3.12.2/Overviews/BBCut.html new file mode 100644 index 0000000..d8e7311 --- /dev/null +++ b/doc-schelp/Help-3.12.2/Overviews/BBCut.html @@ -0,0 +1,64 @@ +BBCut is a collection of SuperCollider classes for automated event analysis, beat induction and algorithmic audio splicing. It is released as public open source under the GNU General Public License. It was written by Nick M. Collins, and is maintained by Nathan Ho since 2016.
Set s.latency = 0.05;
when using BBCut. The default of 0.2 seconds is dangerous and may interact adversely with scheduling especially for faster tempi and beat tracking. Only tempi in a standard range of around 1-4bps are supported.
+
Copy break.aiff and break2.aiff from the BBCut source into Platform.userExtensionDir +/+ "sounds/"
. This is an optional step, but you will need this to run the examples.
+
BBCut used to come with three server plugins -- AnalyzeEvents2, AutoTrack, and DrumTrack. AutoTrack is now known as BeatTrack and made it to the core library, and the other two ugens are in sc3-plugins. As a result, sc3-plugins is required. (If you want to use DrumTrack, a fairly recent version is needed.)
A quick note on naming: the second version of BBCut uses a weird naming scheme so that versions 1 and 2 can coexist without any conflicts. (For example, the main class is called BBCut2.) This is no longer needed because the modern quarks system can take care of multiple versions. The next major version of BBCut will fix the naming scheme. +
Tutorials
+ +Core/Misc
Machine Listening (can also be used independently of the cutters...)
Cut Procedures (CutProcs)
Cut Synthesisers (CutSynths)
Cut Effects
Scheduling
Please ask questions or file bugs at https://github.com/snappizz/BBCut/issues.
Version 2: Thanks to the many academic researchers whose work has been an inspiration, and to my PhD supervisors Ian Cross and Alan Blackwell for giving me the time to work on this project. Funding from AHRC. +
Version 1: Thanks to Charles Ames, MDX Sonic Arts, the SuperCollider List and James McCartney. +
Academic papers about bbcut are available from http://composerprogrammer.com/research.html.
The following table contains classes that are independent of GUI implementation, but may use other GUI classes or be used with any of them.
Class | Description |
EZSlider | |
EZNumber | |
EZRanger | |
EZListView | |
EZPopUpMenu | |
EZScroller | |
EZKnob | |
EZText | |
Gradient | Linear gradient between two colors. |
HiliteGradient | Radial gradient between two colors |
FlowLayout | A decorator that positions views in horizontal series, wrapping to a new line when meeting a border |
Color | Representation of colors |
FreqScopeView | A view based on ScopeView that displays frequency spectrum |
FreqScope | A window containing a FreqScopeView and tools to control its display |
The following table contains generic classes and their corresponding classes in each GUI kit, for window and most views.
Generic | CocoaGUI | SwingGUI | Qt GUI | Description |
Window | SCWindow | JSCWindow | QWindow | a frame that can contain gadgets |
N/A | SCModalWindow | N/A | a modal window | |
N/A | SCModalSheet | N/A | a modal sheet to attach to windows | |
View | SCView | JSCView | QView | The base class for most view classes. Important help file. |
CompositeView | SCCompositeView | JSCCompositeView | QView | container view for nesting layouts |
HLayoutView | SCHLayoutView | JSCHLayoutView | QHLayoutView | container view with horizontal distribution of children |
VLayoutView | SCVLayoutView | JSCVLayoutView | QVLayoutView | container view with vertical distribution of children |
Button | SCButton | JSCButton | QButton | a multiple state push button |
PopUpMenu | SCPopUpMenu | JSCPopUpMenu | QPopUpMenu | a collapsed multiple choice button |
Slider | SCSlider | JSCSlider | QSlider | a horizontal or vertical slider |
RangeSlider | SCRangeSlider | JSCRangeSlider | QRangeSlider | horizontal or vertical interval slider |
Slider2D | SC2DSlider | JSC2DSlider | QSlider2D | a horizontally and vertically moveable slider |
TextField | SCTextField | JSCTextField | QTextField | an editable one line text field |
ListView | SCListView | JSCListView | QListView | a list of text items |
StaticText | SCStaticText | JSCStaticText | QStaticText | a text label |
NumberBox | SCNumberBox | JSCNumberBox | QNumberBox | editable number field |
DragSource | SCDragSource | JSCDragSource | QDragSource | object container acting as a source for drag-n-drop |
DragSink | SCDragSink | JSCDragSink | QDragSink | object container acting as a target for drag-n-drop |
DragBoth | SCDragBoth | JSCDragBoth | QDragBoth | combination of DragSource and DragSink |
Stethoscope | SCStethoscope | JStethoscope | QStethoscope | oscilloscope tool |
ScopeView | SCScope | JSCScope | QScope | oscilloscope view |
TabletView | SCTabletView | JSCTabletView | N/A | view for receiving graphic tablet data |
TabletSlider2D | SC2DTabletSlider | N/A | N/A | 2D slider with support for graphic tablet data |
FreqScope | FreqScope | FreqScope | FreqScope | spectrum tool |
FreqScopeView | FreqScopeView | FreqScopeView | FreqScopeView | spectrum view |
MultiSliderView | SCMultiSliderView | JSCMultiSliderView | QMultiSliderView | array of sliders |
EnvelopeView | SCEnvelopeView | JSCEnvelopeView | QEnvelopeView | breakpoint envelope editor |
UserView | SCUserView | JSCUserView | QUserView | view for user-defined drawing operations |
SoundFileView | SCSoundFileView | JSCSoundFileView | QSoundFileView | waveform view / editor for sound files |
MovieView | SCMovieView | JSCMovieView | N/A | canvas for movie (QuickTime) and image display |
TextView | SCTextView | JSCTextView | QTextView | multiline text editor |
N/A | SCQuartzComposerView | N/A | N/A | view for displaying QuartzComposer documents |
N/A | SCImage | N/A | N/A | an image component for the Mac OS X |
N/A | SCImageFilter | N/A | N/A | a filter class to use with SCImage |
N/A | SCImageKernel | N/A | N/A | a kernel class to use with SCImage. |
Knob | SCKnob | JKnob | QKnob | a knob view |
The following table contains classes used in layout management.
Class | Description |
QLayout | The abstract base class of all layouts. |
QLineLayout | The abstract base class of layouts that arrange items in a line. |
HLayout | A layout that arranges items in horizontal line. |
VLayout | A layout that arranges items in vertical line. |
GridLayout | A layout that arranges items in a 2 dimensional grid. |
StackLayout | A layout that stacks items on top of each other. |
Generic | CocoaGUI | SwingGUI | Qt GUI | Description |
Dialog | CocoaDialog | SwingDialog | QDialog | file selection dialog management |
Font | SCFont | JFont | QFont | a font typeface description. |
Pen | SCPen | JPen | QPen | custom drawing operations class |
MouseX | MouseX | JMouseX | MouseX | UGen. JMouseX for backward compatiblility only. |
MouseY | MouseY | JMouseY | MouseY | UGen. JMouseY for backward compatiblility only. |
MouseButton | MouseButton | JMouseButton | MouseButton | UGen. JMouseButton for backward compatiblility only. |
KeyState | KeyState | JKeyState | KeyState | UGen. JKeyState for backward compatiblility only. |
use GUI.speech | Speech | JSpeech | N/A | text-to-speech synthesis management |
SuperCollider supports operator overloading. Operators can thus be applied to a variety of different objects; Numbers, Ugens, Collections, and so on. When operators are applied to ugens they result in BinaryOpUGens or UnaryOpUGens, through the methods of AbstractFunction. +
This is a list of some of the common unary and binary operators that are implemented by several classes. See the specific classes for details and other operators. +
You can see which classes implements a specific operator by clicking on the method name.
Unary operators may be written in two ways: +
Inversion.
Reciprocal (1/x).
Absolute value.
Next lower integer.
Next higher integer.
Fractional part.
Sign function.
-1 when a < 0, +1 when a > 0, 0 when a is 0
Squared value.
a*a
Cubed value.
a*a*a
Square root.
The definition of square root is extended for signals so that sqrt(a) when a<0 returns -sqrt(-a).
Exponential.
Convert MIDI note number to cycles per second.
Convert cycles per second to MIDI note.
Convert an interval in MIDI notes into a frequency ratio.
Convert a frequency ratio to an interval in MIDI notes.
Convert decibels to linear amplitude.
Convert linear amplitude to decibels.
Convert decimal octaves to cycles per second.
Convert cycles per second to decimal octaves.
See also Randomness
Returns an evenly distributed random value between this and zero. +
Returns an evenly distributed random value between [+this ... - this]. +
Returns a linearly distributed random value between this and zero. +
Returns a linearly distributed random value between [+this ... - this]. +
Returns a value from a gaussian-like random distribution between this and zero. This was suggested by Larry Polansky as a loose approximation of gaussian. Follows the formula: + +
Returns one or zero with the probability given by the argument. +
Natural logarithm.
Base 2 logarithm.
Base 10 logarithm.
Sine.
Cosine.
Tangent.
Arcsine.
Arccosine.
Arctangent.
Hyperbolic sine.
Hyperbolic cosine.
Hyperbolic tangent.
Nonlinear distortion.
The formula used is : + +
Here is an example : +
Nonlinear distortion.
Distortion with a perfectly linear region from -0.5 to +0.5 +
Test if signal is >= 0.
Test if signal is < 0.
Test if signal is > 0.
Three different syntaxes can be used for binary operators consisting of letters: + +
Operators consisting of symbols are written like this: +
Addition.
Subtraction.
Multiplication.
Division.
Floating point modulo.
Exponentiation. Same as pow.
Exponentiation.
neg(neg(a) ** b)
. This allows exponentiation of negative signal values by noninteger exponents.Least common multiple. This definition extends the usual definition and returns a negative number if any of the operands is negative. This makes it consistent with the lattice-theoretical interpretation and its idempotency, commutative, associative, absorption laws. +
Following the example of the programming language J (see: J concepts in SC), lcm is analogous to logical and (see also: http://www.jsoftware.com/papers/eem/gcd.htm). + +
+
Greatest common divisor. This definition extends the usual definition and returns a negative number if both operands are negative. This makes it consistent with the lattice-theoretical interpretation and its idempotency, commutative, associative, absorption laws. +
"greater" means "divisible by" in this interpretation, so gcd(-1, -1)
returns a negative number. This is necessary to make the whole system consistent (fundamental law of arithmetics, idempotency and absorption laws would fail). See examples below.
+
Following the example of the programming language J (see: J concepts in SC), gcd is analogous to logical or (see also: http://www.jsoftware.com/papers/eem/gcd.htm). + + +
Here is an overview of how negative numbers are treated: +
Less than.
Less than or equal.
Greater than.
With UGens, this can be useful for triggering purposes, among other things: +
Greater than or equal.
Equal.
Not equal.
"Lazy equality." See Object: -|==|.
Return first argument. +
Minimum.
Maximum.
Quantization by rounding. Rounds a to the nearest multiple of b.
Quantization by truncation. Truncate a to a multiple of b.
Hypotenuse. Returns the square root of the sum of the squares of a and b. Or equivalently, the distance from the origin to the point (x, y).
In this example, hypot is used to calculate a doppler shift pitch and amplitude based on distance. + +
The next example uses the distance to modulate a delay line. +
Hypotenuse approximation. Returns an approximation of the square root of the sum of the squares of x and y.
The formula used is : + +
hypotApx is used to implement Complex method magnitudeApx. This should not be used for simulating a doppler shift because it is discontinuous. Use hypot. +
Returns the arctangent of y/x.
OK, now we can add a pan to the hypot doppler examples by using atan2 to find the azimuth, or direction angle, of the sound source. Assume speakers at +/- 45 degrees and clip the direction to between those. +
Ring modulation plus first source.
Return the value of ((a*b) + a). This is more efficient than using separate unit generators for the multiply and add. +
See also *, ring1, ring2, ring3, ring4. + +
same as : + +
normal ring modulation: +
Ring modulation plus both sources.
Return the value of ((a*b) + a + b). This is more efficient than using separate unit generators for the multiply and adds. + +
same as : +
Ring modulation variant.
Return the value of (a*a *b). This is more efficient than using separate unit generators for each multiply. + +
same as : +
Ring modulation variant.
Return the value of ((a*a *b) - (a*b*b)). This is more efficient than using separate unit generators for each operation. + +
same as : +
Sum of squares.
Return the value of (a*a) + (b*b). This is more efficient than using separate unit generators for each operation. + +
same as : +
Difference of squares.
Return the value of (a*a) - (b*b). This is more efficient than using separate unit generators for each operation. + +
same as : +
Square of the sum.
Return the value of (a + b)**2. This is more efficient than using separate unit generators for each operation. + +
same as : +
Square of the difference.
Return the value of (a - b)**2. This is more efficient than using separate unit generators for each operation. + +
same as : +
Absolute value of the difference. abs(a - b)
On a circle, there are two distances between two points. This operator returns the smaller value of the two.
Thresholding.
0 when a < b, otherwise a. +
Two quadrant multiply.
0 when b <= 0, a*b when b > 0 +
Scale negative part of input.
a*b when a < 0, otherwise a. +
Bilateral clipping.
clips input wave a to +/- b +
Bilateral wrapping.
wraps input wave to +/- b +
Bilateral folding.
folds input wave a to +/- b +
Residual of clipping.
Returns the difference of the original signal and its clipped form: (a - clip2(a,b)). + +
This reference provides information on audio device selection, including platform-specific details. +
Specific devices can be selected through an instance of ServerOptions. To access ServerOptions
instance of the default server, you can use Server.default.options
. ServerOptions also allows you to specify other important parameters of the device - the sample rate and hardware buffer size.
By default the server will boot to your system's default audio devices. If you want to explicitly tell the server to use the internal soundcard, you need to specify both input and output device. The following example comes from a MacBook Pro: + +
In case of a dedicated audio interface, you might need to specify a single device, e.g. + +
On macOS you can programmatically obtain a list of available audio devices without booting the server: +
One possible reason why a server may fail to boot is a mismatch between input and output devices' sample rates, which may occur when using a different device for input and output (which is the case when using a Mac's internal soundcard). If the server fails to boot due to sample rate mismatch, an error will be posted accordingly. +
You should set both input and output devices' sample rate to the same value. You can do this in Audio MIDI Setup: +
Sometimes you might want to use multiple devices for input or output. macOS provides a way to combine multiple physical devices into a virtual Aggregate Device. To create one, you have to open the Audio MIDI Setup application (in /Applications/Utilities
). You should do this from an user account with administrator privileges.
+
+
Audio Devices
window click on the Plus button on the bottom left and choose Create Aggregate Device
.Use
column on the right for the devices you want to combine.Now you need to tell SuperCollider to use your new aggregate device. + +
After rebooting the server (Server.default.reboot
) you should see in the post window that it now uses the Aggregate Device instead of system defaults:
[...] +"Aggregate Device" Input Device +[...] +"Aggregate Device" Output Device+
Note that when you specify a sound device through SuperCollider's ServerOptions
, there is no need to use the aggregate device as the system's default device.
By default, SuperCollider on Linux uses JACK, and the audio device selection is managed by the JACK server. ServerOptions
cannot override JACK's selection of audio hardware.
When the server is compiled to use JACK as the audio backend, the ServerOption
's device
can be used in two ways:
A nil
device is equivalent to Server.default.options.device = "default:SuperCollider";
+
The JACK connections can be configured via the environment variables SC_JACK_DEFAULT_INPUTS
and SC_JACK_DEFAULT_OUTPUTS
. The format is either a string that specifies another jack client or a comma-separated list of jack ports.
+
By default the server will boot to your system's default audio devices using MME
driver (which usually means higher latency).
+
On Windows there are multiple audio driver APIs (e.g. MME
, WASAPI
, ASIO
etc.) that can be used to communicate with audio devices. The API (listed before the device name) needs to match between the input and the output, for example:
+
+
On Windows you cannot programmatically obtain the list of available audio devices in sclang. However, all the devices are listed in the post window when the server is booting: Server.default.boot
. Partial device name matching is supported in Windows (though not in macOS).
.inDevice
, as well as .outDevice
), unless you use ASIOMME
, WDM-KS
etc.).The following list provides basic reference for different APIs. The most recommended APIs are listed first.
If ASIO driver is available, it is probably the best choice to ensure low input/output latency. ASIO drivers usually provide both inputs and outputs through a single device.
If you are using an internal soundcard or a device which does not come with an ASIO driver, an alternative is to use ASIO4ALL. It is a virtual ASIO driver, communicating with the soundcard using Windows' native APIs. It might provide better performance with built-in soundcards and it should allow for multichannel operation with such devices (if supported by the hardware). Use a web search engine to find a download link.
After installing ASIO4ALL, it can be selected as follows (confirm in the post window when the server boots): + +
A Function is an expression which defines operations to be performed when it is sent the value
message. In functional languages, a function would be known as a lambda expression. Function definitions are enclosed in curly brackets {}
. Argument declarations, if any, follow the open bracket. Variable declarations follow argument declarations. An expression follows the declarations.
{ arg a, b, c; var d; d = a * b; c + d }+ +
Functions are not evaluated immediately when they occur in code, but are passed as values just like integers or strings. +
A function may be evaluated by passing it the value
message and a list of arguments.
+
When evaluated, the function returns the value of its expression.
f = { arg a, b; a + b }; +f.value(4, 5).postln; +f.value(10, 200).postln;+ +
An empty function returns the value nil when evaluated.
{}.value.postln;+ +
A function can be thought as a machine able to perform a task on demand, e.g. a calculator. The calculator can receive input (args) and can output a value, the result of the performed operations. The function definition can then be thought as the building of the calculator: once built, the calculator does nothing until it is requested to work (by passing the value method to a function). The following figure depicts an empty function, input without output, output without input, and the general case with input and output.
An argument list immediately follows the open curly bracket of a function definition. An argument list either begins with the reserved word arg
, or is contained between two vertical bars. If a function takes no arguments, then the argument list may be omitted.
+
Names of arguments in the list may be initialized to a default value using the following syntax forms. Arguments which are not explicitly initialized will be set to nil if no value is passed for them. +
"arg" style, default value is a literal: { arg x = 1; .... }
[1]
+
"arg" style, default value is an expression: { arg x = 10.rand; ... }
[2]
+
"arg" style, default value is a literal but you want to treat it like an expression: { arg x = (2); ... }
[2]
+
Pipe style, default value is a literal: { |x = 1| ... }
[1]
+
Pipe style, default value is an expression: { |x = (10.rand)| ... }
[2]
+
If the last argument in the list is preceded by three dots (an ellipsis), then all the remaining arguments that were passed will be assigned to that variable as an Array. Arguments must be separated by commas. +
examples:
{ arg a, b, c=3; } // is equivalent to: + +{ |a, b, c=3| } + +{ arg x='stop', y, z=0; } // these args are initialised + +{ arg a, b, c ... d; } // any arguments after the first 3 will be assigned to d as an Array+ +
If you want all the arguments put in an Array
arg ... z;+ +
In general arguments may be initialized to literals or expressions, but in the case of Function:play or SynthDef:play, they may only be initialized to literals.
// this is okay: + +{ arg a = Array.geom(4, 100, 3); a * 4 }.value; + +// this is not: + +{ arg freq = Array.geom(4, 100, 3); Mix(SinOsc.ar(freq, 0, 0.1)) }.play; // silence + +// but this is: +{ arg freq = #[ 100, 300, 900, 2700 ]; Mix(SinOsc.ar(freq, 0, 0.1)) }.play; // silence+ +
See Literals for more information. +
Argument defaults that are literals are stored as part of the FunctionDef. Arguments passed at runtime -- including nil -- always override the defaults:
f = { arg x = 1; x }; +f.value(2); // prints 2 + +f.value; // prints 1 + +f.value(nil); // prints nil+ +
Since expressions are evaluated when the function is called, they cannot be stored in the FunctionDef. They are executed only if the passed-in value is nil.
f = { arg x = 10.rand; x }; +f.value(100); // prints 100 + +f.value; // prints a number 0-9 + +f.value(nil); // prints a number 0-9!+ +
This means you can use expression-style to define a default that cannot be overridden by nil.
f = { arg x = (3); x }; +f.value(nil); // prints 3+ +
Note: Parentheses are required when initializing an argument to an expression, if the argument list is written inside ||
pipes.
( +var abc = 2; +{ arg x = abc+1; x } // OK +) + +( +var abc = 2; +{ |x = abc+1| x } +) +ERROR: Parse error + in file 'selected text' + line 1 char 10: + { |x = abc•+1| x } +----------------------------------- +ERROR: Command line parse failed + +( +var abc = 2; +{ |x = (abc+1)| x } // OK +) + +( +var abc = 2; +{ |x (abc+1)| x } // In ||, the = may be omitted if () are there +)+ +
This is because the pipe character also serves as a binary operator. Without parentheses, expressions such as the following are ambiguous:
{ |a, b, c = a | b | c }+ +
The following produce identical function definitions. Expression-style defaults are simply a shortcut syntax for the latter.
{ arg x = 10.rand; x }; + +{ arg x; + x ?? { x = 10.rand }; + x +};+
Following the argument declarations are the variable declarations. These may be declared in any order. Variable lists are preceded by the reserved word var
. There can be multiple var declaration lists if necessary. Variables may be initialized to default values in the same way as arguments. Variable declarations lists may not contain an ellipsis.
var level=0, slope=1, curve=1;+
copyright © 2002 James McCartney
The SuperCollider 3 Synth Server is a simple but powerful synthesis engine. While synthesis is running, new modules can be created, destroyed and repatched, sample buffers can be created and reallocated. Effects processes can be created and patched into a signal flow dynamically at scheduled times. All running modules are ordered in a tree of nodes that define an order of execution. Patching between modules is done through global audio and control buses. +
All commands are received via TCP or UDP using a simplified version of Open Sound Control (OSC). The synth server and its client(s) may be on the same machine or across a network. The synth server does not send or receive MIDI. It is expected that the client will send all control commands. If MIDI is desired, it is up to the client to receive it and convert it to appropriate OSC commands for the synth engine. +
Synth definitions are stored in files generated by the SuperCollider language application. Unit generator definitions are Mach-O bundles (not to be confused with CFBundles). The Unit generator API is a simple C interface.
A Node is an addressable node in a tree of nodes run by the synth engine. There are two types, Synths and Groups. The tree defines the order of execution of all Synths. All nodes have an integer ID.
A Group is a collection of Nodes represented as a linked list. A new Node may be added to the head or tail of the group. The Nodes within a Group may be controlled together. The Nodes in a Group may be both Synths and other Groups. At startup there is a top level group with an ID of zero that defines the root of the tree. If the server was booted from within SCLang (as opposed to from the command line) there will also be a 'default group' with an ID of 1 which is the default target for all new Nodes. See RootNode and default_group for more info.
A Synth is a collection of unit generators that run together. They can be addressed and controlled by commands to the synthesis engine. They read input and write output to global audio and control buses. Synths can have their own local controls that are set via commands to the server.
Synths are created from Synth Definitions. Synth Definition files are created by the SuperCollider language application and are loaded into the synth server. Synth Definitions are referred to by name.
Synths send audio signals to each other via a single global array of audio buses. Audio buses are indexed by integers beginning with zero. Using buses rather than connecting synths to each other directly allows synths to connect themselves to the community of other synths without having to know anything about them specifically. The lowest numbered buses get written to the audio hardware outputs. Immediately following the output buses are the input buses, read from the audio hardware inputs. The number of bus channels defined as inputs and outputs do not have to match that of the hardware.
Synths can send control signals to each other via a single global array of control buses. Buses are indexed by integers beginning with zero.
Buffers are arrays of 32 bit floating point values with a small descriptive header. Buffers are stored in a single global array indexed by integers beginning with zero. Buffers may be safely allocated, loaded and freed while synthesis is running, even while unit generators are using them. Buffers are used for wave tables, sample buffers, delay lines, envelopes, or for any other need which can use an array of floating point values. Sound files may be loaded into or written from buffers.
Unit Generator Definitions are plug-ins loaded automatically when the program starts. They are binary code libraries that are used as building blocks by Synths to build synthesis algorithms. Unit Generator Definitions have names that match the names of SuperCollider language classes used in building Synth Definitions.
One of -u or -t must be supplied. Both may be supplied.
The cmd-filename should be a file that contains OSC bundles sorted in ascending time order. If cmd-filename is the underscore character _, then OSC will be streamed from standard input. +
The audio input will taken from input-filename. If input-filename is the underscore character _, then no input file will be read. +
Output will be written to output-filename. +
The output file's sample rate is specified by sample-rate. The output file header-format should be one of: AIFF, WAVE, NeXT. The output file sample-format should be one of: int16, int24, int32, float, double. The number of channels in the output file is specified with the -o argument.
scscynth -u 57117 >synth_log & + + Accept commands via UDP on port 57117. + Send output to file "synth_log" + Run asynchronously: &. + +scsynth -N score.osc _ out.aiff 48000 AIFF int24 + + Run in non real time mode with command file score.osc, + no input file, and output file named out.aiff. + Sample rate is 48000. Output file header format is aiff, + sample format is 24 bit integer.+
Messages are similar in format to Open Sound Control messages, except that OSC #bundles may not be nested, and pattern matching of the command name is not performed. When streamed via TCP, Messages are each preceded by a 32 bit integer giving the length in bytes of the message. UDP datagrams contain this length information already.
All values are in network byte order.
long | a 64 bit integer. Used for time stamps only. |
int | a 32 bit integer. |
float | a 32 bit single precision floating point number. |
double | a 64 bit double precision floating point number. |
string | a string of 8 bit ASCII characters, zero padded to a multiple of 4 bytes. |
bytes | a buffer of data preceded by a 32 bit length field and padded to a multiple of 4 bytes. |
Command arguments have single character tags which occur in a tag string to identify their types.
'i' | an int. |
'f' | a float |
's' | a string |
'b' | bytes |
a Command consists of:
string | the command name. See the Command Reference below. |
string | a string with tags defined by the types of the arguments to follow. The tag string begins with a comma ',' character. |
... | any combination of arguments of types: int, float, string or bytes. |
a Bundle consists of:
time stamp | long. Time stamps are in the same format as defined by Open Sound Control : The top 32 bits are seconds since 1900 and the lower 32 bits represent the 32 bit fraction of one second. |
... | a series of Commands each preceded by a 32-bit integer byte length. |
a Message consists of:
The following is a list of all server commands and their arguments. +
Each command has a command number which can be sent to the server as a 32 bit integer instead of an OSC style string. Command numbers are listed at the end of this document. +
If a command's description contains the word Asynchronous, then that command will be passed to a background thread to complete so as not to steal CPU time from the audio synthesis thread. All asynchronous commands send a reply to the client when they are completed. Many asynchronous commands can contain an OSC message or bundle to be executed upon completion. eg.
["/d_load", "synthdefs/void.scsyndef", + ["/s_new", "void", 1001, 1, 0] // completion message + ]+
Quit program. Exits the synthesis server.
Register to receive notifications from server
int | one to receive notifications, zero to stop receiving them. |
If argument is one, server will remember your return address and send you notifications. if argument is zero, server will stop sending you notifications.
Query the status. Replies to sender with the following message:
int | 1. unused. |
int | number of unit generators. |
int | number of synths. |
int | number of groups. |
int | number of loaded synth definitions. |
float | average percent CPU usage for signal processing |
float | peak percent CPU usage for signal processing |
double | nominal sample rate |
double | actual sample rate |
/status
messages won't be posted, if the server is in /dumpOSC
modePlug-in defined command.
string | command name |
... | any arguments |
Commands are defined by plug-ins.
Display incoming OSC messages.
int | code |
Turns on and off printing of the contents of incoming Open Sound Control messages. This is useful when debugging your command stream. +
The values for the code are as follows:
0 | turn dumping OFF. |
1 | print the parsed contents of the message. |
2 | print the contents in hexadecimal. |
3 | print both the parsed and hexadecimal representations of the contents. |
Notify when async commands have completed.
int | a unique number identifying this command. |
Replies with a /synced message when all asynchronous commands received before this one have completed. The reply will contain the sent unique ID.
Clear all scheduled bundles. Removes all bundles from the scheduling queue.
Enable/disable error message posting.
int | mode |
Turn on or off error messages sent to the SuperCollider post window. Useful when sending a message, such as /n_free, whose failure does not necessarily indicate anything wrong. +
The values for mode are as follows:
0 | turn off error posting until the next ['/error', 1] message. |
1 | turn on error posting. |
For convenience of client-side methods, you can also suppress errors temporarily, for the scope of a single bundle.
-1 | turn off locally in the bundle -- error posting reverts to the "permanent" setting for the next message or bundle. |
-2 | turn on locally in the bundle. |
These "temporary" states accumulate within a single bundle -- so if you have nested calls to methods that use bundle-local error suppression, error posting remains off until all the layers have been unwrapped. If you use ['/error', -1] within a self-bundling method, you should always close it with ['/error', -2] so that subsequent bundled messages will take the correct error posting status. However, even if this is not done, the next bundle or message received will begin with the standard error posting status, as set by modes 0 or 1. +
Temporary error suppression may not affect asynchronous commands in every case.
Receive a synth definition file.
bytes | buffer of data. |
bytes | an OSC message to execute upon completion. (optional) |
Loads a file of synth definitions from a buffer in the message. Resident definitions with the same names are overwritten.
Load synth definition.
string | pathname of file. Can be a pattern like "synthdefs/perc-*" |
bytes | an OSC message to execute upon completion. (optional) |
Loads a file of synth definitions. Resident definitions with the same names are overwritten.
Load a directory of synth definitions.
string | pathname of directory. |
bytes | an OSC message to execute upon completion. (optional) |
Loads a directory of synth definitions files. Resident definitions with the same names are overwritten.
Delete synth definition.
N * string | synth def name |
Removes a synth definition. The definition is removed immediately, and does not wait for synth nodes based on that definition to end.
Delete a node.
N * int | node ID |
Stops a node abruptly, removes it from its group, and frees its memory. A list of node IDs may be specified. Using this method can cause a click if the node is not silent at the time it is freed.
Turn node on or off.
N * |
|
Using this method to start and stop nodes can cause a click if the node is not silent at the time run flag is toggled.
Set a node's control value(s).
int | node ID | ||||
N * |
|
Takes a list of pairs of control indices and values and sets the controls to those values. If the node is a group, then it sets the controls of every node in the group. +
This message now supports array type tags ($[ and $]) in the control/value component of the OSC message. Arrayed control values are applied in the manner of n_setn (i.e., sequentially starting at the indexed or named control).
Set ranges of a node's control value(s).
int | node ID | ||||||
N * |
|
Set contiguous ranges of control indices to sets of values. For each range, the starting control index is given followed by the number of controls to change, followed by the values. If the node is a group, then it sets the controls of every node in the group.
Fill ranges of a node's control value(s).
int | node ID | ||||||
N * |
|
Set contiguous ranges of control indices to single values. For each range, the starting control index is given followed by the number of controls to change, followed by the value to fill. If the node is a group, then it sets the controls of every node in the group.
Map a node's controls to read from a bus.
int | node ID | ||||
N * |
|
Takes a list of pairs of control names or indices and bus indices and causes those controls to be read continuously from a global control bus. If the node is a group, then it maps the controls of every node in the group. If the control bus index is -1 then any current mapping is undone. Any n_set, n_setn and n_fill command will also unmap the control.
Map a node's controls to read from buses.
int | node ID | ||||||
N * |
|
Takes a list of triplets of control names or indices, bus indices, and number of controls to map and causes those controls to be mapped sequentially to buses. If the node is a group, then it maps the controls of every node in the group. If the control bus index is -1 then any current mapping is undone. Any n_set, n_setn and n_fill command will also unmap the control.
Map a node's controls to read from an audio bus.
int | node ID | ||||
N * |
|
Takes a list of pairs of control names or indices and audio bus indices and causes those controls to be read continuously from a global audio bus. If the node is a group, then it maps the controls of every node in the group. If the audio bus index is -1 then any current mapping is undone. Any n_set, n_setn and n_fill command will also unmap the control. For the full audio rate signal, the argument must have its rate set to \ar.
Map a node's controls to read from audio buses.
int | node ID | ||||||
N * |
|
Takes a list of triplets of control names or indices, audio bus indices, and number of controls to map and causes those controls to be mapped sequentially to buses. If the node is a group, then it maps the controls of every node in the group. If the audio bus index is -1 then any current mapping is undone. Any n_set, n_setn and n_fill command will also unmap the control. For the full audio rate signal, the argument must have its rate set to \ar.
Place a node before another.
N * |
|
Places node A in the same group as node B, to execute immediately before node B.
Place a node after another.
N * |
|
Places node A in the same group as node B, to execute immediately after node B.
Get info about a node.
N * int | node ID |
The server sends an /n_info message for each node to registered clients. See Node Notifications below for the format of the /n_info message.
Trace a node.
N * int | node IDs |
Causes a synth to print out the values of the inputs and outputs of its unit generators for one control period. Causes a group to print the node IDs and names of each node in the group for one control period.
Move and order a list of nodes.
int | add action (0,1,2 or 3 see below) |
int | add target ID |
N * int | node IDs |
Move the listed nodes to the location specified by the target and add action, and place them in the order specified. Nodes which have already been freed will be ignored.
0 | construct the node order at the head of the group specified by the add target ID. |
1 | construct the node order at the tail of the group specified by the add target ID. |
2 | construct the node order just before the node specified by the add target ID. |
3 | construct the node order just after the node specified by the add target ID. |
Create a new synth.
string | synth definition name | ||||
int | synth ID | ||||
int | add action (0,1,2, 3 or 4 see below) | ||||
int | add target ID | ||||
N * |
|
Create a new synth from a synth definition, give it an ID, and add it to the tree of nodes. There are four ways to add the node to the tree as determined by the add action argument which is defined as follows:
0 | add the new node to the the head of the group specified by the add target ID. |
1 | add the new node to the the tail of the group specified by the add target ID. |
2 | add the new node just before the node specified by the add target ID. |
3 | add the new node just after the node specified by the add target ID. |
4 | the new node replaces the node specified by the add target ID. The target node is freed. |
Controls may be set when creating the synth. The control arguments are the same as for the n_set command. +
If you send /s_new with a synth ID of -1, then the server will generate an ID for you. The server reserves all negative IDs. Since you don't know what the ID is, you cannot talk to this node directly later. So this is useful for nodes that are of finite duration and that get the control information they need from arguments and buses or messages directed to their group. In addition no notifications are sent when there are changes of state for this node, such as /go, /end, /on, /off. +
If you use a node ID of -1 for any other command, such as /n_map, then it refers to the most recently created node by /s_new (auto generated ID or not). This is how you can map the controls of a node with an auto generated ID. In a multi-client situation, the only way you can be sure what node -1 refers to is to put the messages in a bundle. +
This message now supports array type tags ($[ and $]) in the control/value component of the OSC message. Arrayed control values are applied in the manner of n_setn (i.e., sequentially starting at the indexed or named control). See the Node Messaging helpfile.
Get control value(s).
int | synth ID |
N * int or string | a control index or name |
Replies to sender with the corresponding /n_set command.
Get ranges of control value(s).
int | synth ID | ||||
N * |
|
Get contiguous ranges of controls. Replies to sender with the corresponding /n_setn command.
Auto-reassign synth's ID to a reserved value.
N * int | synth IDs |
This command is used when the client no longer needs to communicate with the synth and wants to have the freedom to reuse the ID. The server will reassign this synth to a reserved negative number. This command is purely for bookkeeping convenience of the client. No notification is sent when this occurs.
Create a new group.
N * |
|
Create a new group and add it to the tree of nodes. There are four ways to add the group to the tree as determined by the add action argument which is defined as follows (the same as for /s_new):
0 | add the new group to the the head of the group specified by the add target ID. |
1 | add the new group to the the tail of the group specified by the add target ID. |
2 | add the new group just before the node specified by the add target ID. |
3 | add the new group just after the node specified by the add target ID. |
4 | the new node replaces the node specified by the add target ID. The target node is freed. |
Multiple groups may be created in one command by adding arguments.
Create a new parallel group.
N * |
|
Create a new parallel group and add it to the tree of nodes. Parallel groups are relaxed groups, their child nodes are evaluated in unspecified order. There are four ways to add the group to the tree as determined by the add action argument which is defined as follows (the same as for /s_new):
0 | add the new group to the the head of the group specified by the add target ID. |
1 | add the new group to the the tail of the group specified by the add target ID. |
2 | add the new group just before the node specified by the add target ID. |
3 | add the new group just after the node specified by the add target ID. |
4 | the new node replaces the node specified by the add target ID. The target node is freed. |
Multiple groups may be created in one command by adding arguments.
Add node to head of group.
N * |
|
Adds the node to the head (first to be executed) of the group.
Add node to tail of group.
N * |
|
Adds the node to the tail (last to be executed) of the group.
Delete all nodes in a group.
N * int | group ID(s) |
Frees all nodes in the group. A list of groups may be specified.
Free all synths in this group and all its sub-groups.
N * int | group ID(s) |
Traverses all groups below this group and frees all the synths. Sub-groups are not freed. A list of groups may be specified.
Post a representation of this group's node subtree.
N * |
|
Posts a representation of this group's node subtree, i.e. all the groups and synths contained within it, optionally including the current control values for synths.
Get a representation of this group's node subtree.
N * |
|
Request a representation of this group's node subtree, i.e. all the groups and synths contained within it. Replies to the sender with a /g_queryTree.reply message listing all of the nodes contained within the group in the following format:
int | flag: if synth control values are included 1, else 0 | ||||||||||||||||
int | node ID of the requested group | ||||||||||||||||
int | number of child nodes contained within the requested group | ||||||||||||||||
then for each node in the subtree: |
|
N.B. The order of nodes corresponds to their execution order on the server. Thus child nodes (those contained within a group) are listed immediately following their parent. See the method Server:queryAllNodes for an example of how to process this reply.
Send a command to a unit generator.
int | node ID |
int | unit generator index |
string | command name |
... | any arguments |
Sends all arguments following the command name to the unit generator to be performed. Commands are defined by unit generator plug ins.
Buffers are stored in a global array, indexed by integers starting at zero.
Allocate buffer space.
int | buffer number |
int | number of frames |
int | number of channels (optional. default = 1 channel) |
bytes | an OSC message to execute upon completion. (optional) |
Allocates zero filled buffer to number of channels and samples.
Allocate buffer space and read a sound file.
int | buffer number |
string | path name of a sound file. |
int | starting frame in file (optional. default = 0) |
int | number of frames to read (optional. default = 0, see below) |
bytes | an OSC message to execute upon completion. (optional) |
Allocates buffer to number of channels of file and number of samples requested, or fewer if sound file is smaller than requested. Reads sound file data from the given starting frame in the file. If the number of frames argument is less than or equal to zero, the entire file is read.
Allocate buffer space and read channels from a sound file.
int | buffer number | ||
string | path name of a sound file | ||
int | starting frame in file | ||
int | number of frames to read | ||
N * | N >= 0
| ||
bytes | an OSC message to execute upon completion. (optional) |
As b_allocRead, but reads individual channels into the allocated buffer in the order specified.
Read sound file data into an existing buffer.
int | buffer number |
string | path name of a sound file. |
int | starting frame in file (optional. default = 0) |
int | number of frames to read (optional. default = -1, see below) |
int | starting frame in buffer (optional. default = 0) |
int | leave file open (optional. default = 0) |
bytes | an OSC message to execute upon completion. (optional) |
Reads sound file data from the given starting frame in the file and writes it to the given starting frame in the buffer. If number of frames is less than zero, the entire file is read. If reading a file to be used by DiskIn ugen then you will want to set "leave file open" to one, otherwise set it to zero.
Read sound file channel data into an existing buffer.
int | buffer number | ||
string | path name of a sound file | ||
int | starting frame in file | ||
int | number of frames to read | ||
int | starting frame in buffer | ||
int | leave file open | ||
N * | N >= 0
| ||
bytes | completion message |
As b_read, but reads individual channels in the order specified. The number of channels requested must match the number of channels in the buffer.
Write sound file data.
int | buffer number |
string | path name of a sound file. |
string | header format. |
string | sample format. |
int | number of frames to write (optional. default = -1, see below) |
int | starting frame in buffer (optional. default = 0) |
int | leave file open (optional. default = 0) |
bytes | an OSC message to execute upon completion. (optional) |
Write a buffer as a sound file.
Not all combinations of header format and sample format are possible. If number of frames is less than zero, all samples from the starting frame to the end of the buffer are written. If opening a file to be used by DiskOut ugen then you will want to set "leave file open" to one, otherwise set it to zero. If "leave file open" is set to one then the file is created, but no frames are written until the DiskOut ugen does so.
Free buffer data.
int | buffer number |
bytes | an OSC message to execute upon completion. (optional) |
Frees buffer space allocated for this buffer.
Zero sample data.
int | buffer number |
bytes | an OSC message to execute upon completion. (optional) |
Sets all samples in the buffer to zero.
Set sample value(s).
int | buffer number | ||||
N * |
|
Takes a list of pairs of sample indices and values and sets the samples to those values.
Set ranges of sample value(s).
int | buffer number | ||||||
N * |
|
Set contiguous ranges of sample indices to sets of values. For each range, the starting sample index is given followed by the number of samples to change, followed by the values.
Fill ranges of sample value(s).
int | buffer number | ||||||
N * |
|
Set contiguous ranges of sample indices to single values. For each range, the starting sample index is given followed by the number of samples to change, followed by the value to fill. This is only meant for setting a few samples, not whole buffers or large sections.
Call a command to fill a buffer.
int | buffer number |
string | command name |
... | command arguments |
Plug-ins can define commands that operate on buffers. The arguments after the command name are defined by the command. The currently defined buffer fill commands are listed below in a separate section.
Close soundfile.
int | buffer number |
After using a buffer with DiskOut, close the soundfile and write header information.
Get buffer info.
N * int | buffer number(s) |
Responds to the sender with a /b_info message. The arguments to /b_info are as follows:
N * |
|
Get sample value(s).
int | buffer number |
N * int | a sample index |
Replies to sender with the corresponding /b_set command.
Get ranges of sample value(s).
int | buffer number | ||||
N * |
|
Get contiguous ranges of samples. Replies to sender with the corresponding /b_setn command. This is only meant for getting a few samples, not whole buffers or large sections.
Set bus value(s).
N * |
|
Takes a list of pairs of bus indices and values and sets the buses to those values.
Set ranges of bus value(s).
N * |
|
Set contiguous ranges of buses to sets of values. For each range, the starting bus index is given followed by the number of channels to change, followed by the values.
Fill ranges of bus value(s).
N * |
|
Set contiguous ranges of buses to single values. For each range, the starting sample index is given followed by the number of buses to change, followed by the value to fill.
Get bus value(s).
N * int | a bus index |
Takes a list of buses and replies to sender with the corresponding /c_set command.
Get ranges of bus value(s).
N * |
|
Get contiguous ranges of buses. Replies to sender with the corresponding /c_setn command.
End real time mode, close file. Not yet implemented. +
This message should be sent in a bundle in non real time mode. The bundle timestamp will establish the ending time of the file. This command will end non real time mode and close the sound file. Replies to sender with /done when complete.
These messages are sent by the server in response to some commands.
An asynchronous message has completed.
string | the name of the command |
other | (optional) some commands provide other information, for example a buffer index. |
Sent in response to all asynchronous commands. Sent only to the sender of the original message.
An error occurred.
string | the name of the command |
string | the error message. |
other | (optional) some commands provide other information, for example a buffer index. |
There was a problem. Sent only to the sender of the original message.
A command was received too late. not yet implemented
int | the high 32 bits of the original time stamp. |
int | the low 32 bits of the original time stamp. |
int | the high 32 bits of the time it was executed. |
int | the low 32 bits of the time it was executed. |
The command was received too late to be executed on time. Sent only to the sender of the original message.
These messages are sent as notification of some event to all clients who have registered via the /notify command. +
All of these have the same arguments:
int | node ID |
int | the node's parent group ID |
int | previous node ID, -1 if no previous node. |
int | next node ID, -1 if no next node. |
int | 1 if the node is a group, 0 if it is a synth |
The following two arguments are only sent if the node is a group: | |
int | the ID of the head node, -1 if there is no head node. |
int | the ID of the tail node, -1 if there is no tail node. |
A node was started. This command is sent to all registered clients when a node is created.
A node ended. This command is sent to all registered clients when a node ends and is deallocated.
A node was turned off. This command is sent to all registered clients when a node is turned off.
A node was turned on. This command is sent to all registered clients when a node is turned on.
A node was moved. This command is sent to all registered clients when a node is moved.
Reply to /n_query. This command is sent to all registered clients in response to an /n_query command.
These messages are sent as notification of some event to all clients who have registered via the /notify command.
A trigger message.
int | node ID |
int | trigger ID |
float | trigger value |
This command is the mechanism that synths can use to trigger events in clients. The node ID is the node that is sending the trigger. The trigger ID and value are determined by inputs to the SendTrig unit generator which is the originator of this message.
These are the currently defined fill routines for use with the /b_gen command.
There are three defined fill routines for sine waves. +
The flags are defined as follows:
1 | normalize - Normalize peak amplitude of wave to 1.0. |
2 | wavetable - If set, then the buffer is written in wavetable format so that it can be read by interpolating oscillators. |
4 | clear - if set then the buffer is cleared before new partials are written into it. Otherwise the new partials are summed with the existing contents of the buffer. |
These flags can be added together to create a unique single integer flag that describes the true/false combinations for these three options:
3 | 1 + 2 | normalize + wavetable |
5 | 1 + 4 | normalize + clear |
6 | 2 + 4 | wavetable + clear |
7 | 1 + 2 + 4 | normalize + wavetable + clear |
int | flags, see above | ||
N * |
|
Fills a buffer with a series of sine wave partials. The first float value specifies the amplitude of the first partial, the second float value specifies the amplitude of the second partial, and so on.
int | flags, see above | ||||
N * |
|
Similar to sine1 except that each partial frequency is specified explicitly instead of being an integer series of partials. Non-integer partial frequencies are possible.
int | flags, see above | ||||||
N * |
|
Similar to sine2 except that each partial may have a nonzero starting phase.
int | flags, see above | ||
N * |
|
Fills a buffer with a series of chebyshev polynomials, which can be defined as:
cheby(n) = amplitude * cos(n * acos(x))+ +
The first float value specifies the amplitude for n = 1, the second float value specifies the amplitude for n = 2, and so on. To eliminate a DC offset when used as a waveshaper, the wavetable is offset so that the center value is zero.
int | sample position in destination |
int | source buffer number |
int | sample position in source |
int | number of samples to copy |
Copy samples from the source buffer to the destination buffer specified in the b_gen command. If the number of samples to copy is negative, the maximum number of samples possible is copied.
These are the currently defined command numbers. More may be added to the end of the list in the future.
enum { + cmd_none = 0, + + cmd_notify = 1, + cmd_status = 2, + cmd_quit = 3, + cmd_cmd = 4, + + cmd_d_recv = 5, + cmd_d_load = 6, + cmd_d_loadDir = 7, + cmd_d_freeAll = 8, + + cmd_s_new = 9, + + cmd_n_trace = 10, + cmd_n_free = 11, + cmd_n_run = 12, + cmd_n_cmd = 13, + cmd_n_map = 14, + cmd_n_set = 15, + cmd_n_setn = 16, + cmd_n_fill = 17, + cmd_n_before = 18, + cmd_n_after = 19, + + cmd_u_cmd = 20, + + cmd_g_new = 21, + cmd_g_head = 22, + cmd_g_tail = 23, + cmd_g_freeAll = 24, + + cmd_c_set = 25, + cmd_c_setn = 26, + cmd_c_fill = 27, + + cmd_b_alloc = 28, + cmd_b_allocRead = 29, + cmd_b_read = 30, + cmd_b_write = 31, + cmd_b_free = 32, + cmd_b_close = 33, + cmd_b_zero = 34, + cmd_b_set = 35, + cmd_b_setn = 36, + cmd_b_fill = 37, + cmd_b_gen = 38, + + cmd_dumpOSC = 39, + + cmd_c_get = 40, + cmd_c_getn = 41, + cmd_b_get = 42, + cmd_b_getn = 43, + cmd_s_get = 44, + cmd_s_getn = 45, + + cmd_n_query = 46, + cmd_b_query = 47, + + cmd_n_mapn = 48, + cmd_s_noid = 49, + + cmd_g_deepFree = 50, + cmd_clearSched = 51, + + cmd_sync = 52, + + cmd_d_free = 53, + + cmd_b_allocReadChannel = 54, + cmd_b_readChannel = 55, + + cmd_g_dumpTree = 56, + cmd_g_queryTree = 57, + + + cmd_error = 58, + + cmd_s_newargs = 59, + + cmd_n_mapa = 60, + cmd_n_mapan = 61, + cmd_n_order = 62, + + NUMBER_OF_COMMANDS = 63 +};+ +
copyright © 2002 James McCartney - converted to ScDoc format 2011 by Jonatan Liljedahl
UGens can also use if +
the condition ugen is 0 / 1 +
the functions will be inlined, which plucks the code from the functions and uses a more efficient jump statement. + +
failure to inline due to variable declarations + + +
Create an object that behaves like a stream that returns values for a limited (or infinite) number of times. +
For a full list of implementing classes, see Methods: loop and Methods: repeat
f = { 3.yield }; + x = Routine({ f.loop }); + 10.do({ x.next.postln })+
x = 5; + y = x.repeat(6); + y.nextN(8);+
x = Prand([1, 2]).repeat(6).asStream; + x.nextN(8);+
x = Prand([1, 2]).loop.asStream; + x.nextN(8);+
x = Routine({ 3.do({ arg i; i.yield }) }).repeat(6); + x.nextN(8);+
x = Routine({ 3.do({ arg i; i.yield }) }).loop; + x.nextN(8);+
The play
message is of common use in sc. Different objects respond to it in various ways, but the simple meaning is: start a process. It is usually implemented by objects in contributed libraries as well.
+
play usually returns the playing object which might not be the same as the one the message was sent to. +
opposite: stop
For a full list of which classes that implements play
, see Methods: play
returns: the clock + +
See Clock: *play
returns: the routine + +
See Routine: -play
returns: the stream +
the stream will loop until it returns nil + +
returns: the stream + +
See Stream: -play and Task: -play
returns: an EventStreamPlayer + +
See Pattern: -play
The following play messages both cause a SynthDef to be written, send it to the server and start a synth with it there.
gate
control for releasing and crossfading. If the function provides its own releasable envelope, this is omitted.Also note that they should not be used in quickly running automated processes, as there are more efficient alternatives ( see SynthDefs versus Synths )
returns: a Synth
outbus | on what bus to play (default: 0) |
fadeTime | in what time to fade out when released (default: 0.02) |
addAction | where to add the node (\addToHead by default) |
args | controls to set when starting the synth |
See Function: -play +
returns: a Synth +
Note that you need an out ugen to hear the result. Examples of how to write to the busses in the helpfiles: Out / ReplaceOut / XOut / OffsetOut +
Nevertheless, synths can also run without any writing activity: (see e.g. SendTrig) +
Some operations provide an out ugen internally: see for example function.play
, which plays out to a bus number provided in the argument passed to .play
+
+
See SynthDef: -play
Synth.play(function)
is synonymous, for backwards compatibility with sc2Options: + |
+
+ Ignore case
+ Whole word
+ Starts with
+ Ends with
+ Regexp
+
+ |
Match: + |
+
+ Title/Filename
+ Summary
+ Categories
+ Methods
+
+ |
by Scott Wilson and James Harkins
It is traditional when learning a new programming language to start with a simple program called 'Hello World'. This just makes the program print the text 'Hello World!' to well, wherever it prints text. In SC that's a place called the post window. The post window is the one that opened up when you first started SC, and a bunch of stuff was printed there which looks something like this: + +
Don't worry too much about what all that means just now, just keep in mind that this is where SC will send you information. It's also where we'll get the result of our Hello World program, which you can see below: + +
To execute it, simply click to place the cursor somewhere on the same line as the code and then press Shift-Enter (Shift-Return on macOS). Try this now. +
If all went well, you should see this in the post window. + +
Alternatively you can also first select the code you wish to execute (by clicking and dragging over the text until it is highlighted) and then press Ctrl-Enter (Cmd-Return on macOS). Try this now. +
Now let's take a closer look at the code. The first bit, "Hello World!"
, is a kind of Object, called a String. An object is basically just a way of representing something in the computer, for instance a bit of text, or an oscillator, that allows you to control it and send messages to it. More about that later, but for now just understand that a String is a way of representing a bit of text.
+
The second bit, .postln;
, says 'print me (or a meaningful description of me) to the post window.' Remember postln, it's your friend. You can apply it to almost anything in SC and get something meaningful back. This can be very handy when tracking down bugs in your code.
+
Why did it print twice? Well, when you execute code in SC, it always posts the result of the last bit of code (the last statement). So it first prints because we explicitly told it to print, and then it prints the result of this operation, which happens to be the same in this case. +
So in this case we didn't really need the postln
bit. But in the following example we would. Select both lines of text by clicking and dragging over them, and then execute, i.e. press Ctrl-Enter (Cmd-Return on macOS).
+
+
The first line, 'Hello there, I'm SuperCollider!' would not have printed if we didn't have the explicit postln. +
In general, when you are meant to execute several lines of code at the same time they will be surrounded by parentheses, as in the example below. You can have your cursor anywhere in this region (or on the line of the parentheses on macOS), then double-click and press Ctrl-Enter or Shift-Enter (Cmd-Return or Shift-Return on macOS) - this selects the whole region and executes it. Try it out on the example below. + +
Double clicking inside a pair of enclosing parentheses may not select the entire region on all systems, notably not on modern Macs. On some it may only select the double clicked word. Thus it may be good, to make it a habit to stick to a particular technique. E.g. to first always check you have selected all of the intended code, regardless of the selection technique, before pressing either Ctrl-Enter or Shift-Enter (Cmd-Return or Shift-Enter on macOS). Then stick to the technique that works best for you. Experiment with this to learn how your system behaves. +
When code is not surrounded by parentheses it is generally intended to be executed one line at a time. You can have your cursor anywhere in a line of code and press Ctrl-Enter or Shift-Enter (Cmd-Return or Shift-Return on macOS) - this selects the whole line and executes it. +
Note that each of the lines within the block of code ends with a semi-colon. This is very important when executing multiple lines of code. Try what happens when you execute following variant of the almost identical code. + +
Executing the code above results in a 'Parse Error'. With an error of this kind, the dot in the error message shows you where SC ran into trouble. Here it happens just after "Ishmael."
.
+
+
Usually the problem actually occurs a little before that, so that's where you should look. In this case, it's the lack of a semi-colon at the end of the previous line. Note that each line of code ends normally with a semi-colon. This is how you separate lines of code in SC. Since we didn't have a semi-colon between the two lines we have gotten an error. +
Note also, having an extra semi-colon at the very end of the last piece of code does not hurt and is tolerated by SC for reasons of convenience. +
A couple of more notes about the post window. It's very useful to be able to see it, but sometimes it can get hidden behind other windows. You can bring it to the front at any time by pressing Cmd-\. +
Other times, the post window becomes full of text and hard to read. You can clear it at any time by pressing Ctrl-Shift-P (Cmd-Shift-P on macOS).
SuperCollider is actually three programs:
The sclang part is a sophisticated programming language with nice features for building GUIs (Graphical User Interfaces); and the server part is a lean, mean, efficient UNIX command line application (meaning it runs without any GUI representation). +
They communicate by a protocol called OSC (Open Sound Control), over either UDP (User Datagram Protocol) or TCP (Transmission Control Protocol), which are network protocols also used on the internet. Because the client and server communicate this way, more advanced projects might run them on separate computers for performance reasons. In fact, it's even possible that they could be running in different parts of the world! However, just because these two applications communicate using common internet protocols does not mean they must be connected to the internet or on different computers. Most of the time they will be running on the same computer, and the "networking" aspect of things will be relatively transparent for you. Especially while you're still getting started. +
You can only communicate with the server using OSC messages over the network, but luckily the language app has lots of powerful objects which represent things on the server and allow you to control them easily and elegantly. Understanding how exactly that works is crucial to effectively working in SC, so we'll be talking about that in some depth. +
But first let's have a little fun, and make some sound! +
For more information see: +
How to Use the Interpreter, Literals, String, Client vs Server, Server Architecture
Open a new tab or window by pressing Ctrl-N (Cmd-N on macOS) or choose 'New' from the File menu. Save the document by giving it a name like 'My first SC code.scd'. Note, you should always use extension '.scd' for files containing SC code. Copy some of above code examples and paste them into the new document using Ctrl-C and Ctrl-V (Cmd-C and Cmd-V on macOS) or use the copy and paste Edit menu items. +
SC will let you edit the help files and documentation, so it's always a good idea to copy text over before changing it to avoid accidentally saving altered help files! +
Experiment with altering the text between the quotes to print different things to the post window. Do this with both blocks of text wrapped in parentheses, and single lines. +
____________________ +
This document is part of the tutorial Getting Started With SuperCollider. +
Click here to go on to the next section: 03. Start Your Engines +
Click here to return to the table of Contents: 00. Getting Started With SC
Before we can make any sound, we need to start or 'boot' a server application. The easiest way to do this is to use the shortcut Ctrl-B (Cmd-B on macOS). There is also a Server menu entry for "Boot Server". +
Notice that the white font on the black view on the bottom of the window has changed to green. This indicates that the server is running. The view also provides you with some information about CPU usage, and some other things which probably aren't too clear yet. More about them soon. +
Also take a look at the post window, where SC has given you some info, and let you know that it booted okay. For example: + +
If for some reason it failed to boot, there should be information printed about the error that occurred. If this happens, please reach out to the community for help: https://supercollider.github.io#community +
By default you can refer to the localhost server in your code by using the letter s
. You can send messages to start and stop it like so:
+
+
Try this out and then leave the server running. Many examples in the documentation have s.boot
at the beginning, but in general you should make sure the server is running before using any examples that generate audio, or otherwise access the server. In general the examples in this tutorial assume that the server is running.
+
You can also refer to the default server with the text Server.default
, for example:
+
+
For more information see: +
Server +
____________________ +
This document is part of the tutorial Getting Started With SuperCollider. +
Click here to go on to the next section: 04. Functions and Other Functionality +
Click here to return to the table of Contents: 00. Getting Started With SC
The easiest way to get sound from SC is to use a Function. Below is a simple example of this. Execute this (after making sure the server is booted), and when you're sick of it, press Cmd - . (that's hold down the command key and press the period or fullstop key) to stop the sound. This will always stop all sound in SC. You'll be using it a lot, so commit it to memory. + +
Not too inspiring? Don't worry, we're just getting started, and this is just a simple example to demonstrate Functions and sound. We'll take it apart a bit below. +
Before we get to doing that though, let's learn a little about Functions in general. +
A Function is just a reusable bit of code. You define a Function by enclosing code in 'curly brackets': { }. Here's an example: + +
The stuff within the curly brackets is what will get executed each time you reuse, or evaluate the Function. Note that this is written like an equation, i.e. f = {...}
. This is not an equation in the mathematical sense, it's what's called an assignment. Basically it allows me to name the Function I've created, by storing it in a variable called f
. A variable is just a name representing a slot in which we can store things, such as a Function, a number, a list, etc. Execute the following lines one at a time and watch the post window:
+
+
Both times it should say 'a Function'
. Now whenever we want to refer to our Function we can just use the letter f. That's in fact what makes it reusable! Otherwise we'd need to type the Function in every time.
+
So how do we reuse it? Execute the following lines one at a time and watch the post window: + +
Our Function is an object, (i.e a thing that does something or represents something), which we have defined and stored in the variable f
. The bit of code that says '.value'
says evaluate this function now. This is an example of sending a message to an object. This follows the syntax someObject.someMessage. The dot must go in between.
+
Now this next bit is a little bit tricky. In a given object, each message calls (calls means executes) a particular method. Different types of objects may have methods with the same name, and thus respond to the same message in different ways. Whoah, get that? Read it again slowly, as this is pretty important: +
Different types of objects may have methods with the same name, and thus respond to the same message in different ways. +
What's interesting about this is that the actual methods may differ in what they do, but as long as they implement a method with that name, they become interchangeable in your code. +
A good example is 'value'. All objects in SC respond to the message 'value'. When you 'call' a method, it always 'returns' something, such as a value or a result. When you call the method 'value' on a Function it will evaluate and return the result of its last line of code. The example below will return the number 5. + +
Often methods simply return the object itself. This is the case with most objects and the message 'value'. The example below demonstrates this. (Everything to the right of the //
is a 'comment', which means that SC just ignores it. Comments are a good idea to make your code clearer.)
+
+
This means that by using the 'value' method Functions and other objects can be interchangeable in your code. This is an example of polymorphism, which is one of the powerful features of what's called Object Oriented Programming. Polymorphism just means that different objects are interchangeable (at least providing they return something sensible for what you're doing) if they respond to the same message. Object Oriented Programming (or OOP, as it's called for short) just means programming with objects. Simple, yes? Here's another short example showing this in action: + +
Start to see how this could be useful? +
Functions can also have what are called arguments. These are values which are passed into the Function when it is evaluated. The example below demonstrates how this works. See if you can guess what the result will be before executing it. + +
Arguments are declared at the beginning of the Function, using the keyword 'arg'
. You can then refer to them just like variables. When you call value on a Function, you can pass in arguments, in order, by putting them in parentheses: someFunc.value(arg1, arg2)
. This is the same with any method that takes arguments, not just value.
+
You can specify different orders by using what are called keyword arguments: + +
You can mix regular and keyword style if you like, but the regular args must come first: + +
(Note that SC has no operator precedence, i.e. math operations are done in order, and division and multiplication are not done first. To force an order use parentheses. e.g. 4 + (2* 8) ) +
Sometimes it's useful to set default values for arguments. You can do this like so: + +
Default values must be what are called literals. Literals are basically numbers, strings, symbols (more on these later), or collections of them. Don't worry if that doesn't totally make sense, it will become clearer as we go on. +
There is an alternate way to specify args, which is to enclose them within two vertical lines. (On most keyboards the vertical line symbol is Shift-\ ) The following two Functions are equivalent: + +
Why have two different ways? Well some people like the second one better and consider it a shortcut. SC has a number of syntax shortcuts like this, which can make writing code a little faster. In any case you will encounter both forms, so you need to be aware of them. +
You can also have variables in a Function. These you need to declare at the beginning of the Function, just after the args, using the keyword 'var'
.
+
+
Variable and argument names can consist of letters and numbers, but must begin with a lower-case letter and cannot contain spaces. +
Variables are only valid for what is called their scope. The scope of a variable declared in a Function is that Function, i.e. the area between the two curly brackets. Execute these one at a time: + +
You can also declare variables at the top of any block of code which you execute altogether (i.e. by selecting it all). In such a case that block of code is the variable's scope. Execute the block (in parentheses) and then the last line. + +
You may be wondering why we haven't needed to declare variables like f
, and why they don't seem to have any particular scope (i.e. they keep their values even when executing code one line at a time). The letters a to z are what are called interpreter variables. These are pre-declared when you start up SC, and have an unlimited, or 'global', scope. This makes them useful for quick tests or examples. You've already encountered one of these, the variable 's', which you'll recall by default refers to the localhost server.
+
For more information see: +
Functions, Function, Assignment Statements, Introduction to Objects, Literals, Scoping and Closure +
____________________ +
This document is part of the tutorial Getting Started With SuperCollider. +
Click here to go on to the next section: 05. Functions and Sound +
Click here to return to the table of Contents: 00. Getting Started With SC
I've probably bored you enough with technical details, so let's get back to making noise, which I assume is why you're reading this after all. Trust me though, all this work will pay off later, and believe it or not, we've already covered a fair amount of the basics of the language, at least in passing. +
Let's go back to our sound example, or rather a slightly simplified version of it. Check that the localhost server is running, execute the code below and then press Cmd-. when you've had enough. + +
In this case we've created a Function by enclosing some code in curly brackets, and then called the method 'play' on that Function. To Functions 'play' means evaluate yourself and play the result on a server. If you don't specify a server, you'll get the default one, which you'll recall is stored in the variable 's' and is set at startup to be the localhost server. +
We didn't store the Function in a variable, so it can't be reused. (Well, actually you could just execute the same line of code again, but you know what I mean...) This is often the case when using Function-play, as it is useful as a quick way of getting something to make noise, and is often used for testing purposes. There are other ways of reusing Functions for sounds, which are often better and more efficient as we will see. +
Lets look at what's between the curly brackets. We're taking something called a 'SinOsc' and we're sending it the message ar, with a few arguments. It turns out that SinOsc is an example of something called a class. To understand what a class is, we need to know a little more about OOP and objects. +
In a nutshell, an object is some data, i.e. some information, and a set of operations that you can perform on that data. You might have many different objects of the same type. These are called instances. The type itself is the object's class. For instance we might have a class called Student, and several instances of it, Bob, Dave and Sue. All three will have the same types of data, for instance they might have a bit of data named gpa. The value of each bit of data could be different however. They would also have the same methods to operate on the data. For instance they could have a method called calculateGPA, or something similar. +
An object's class defines its set of data (or instance variables as they are called) and methods. In addition it may define some other methods which only you send only to the class itself, and some data to be used by all of its instances. These are called class methods and class variables. +
All classes begin with upper-case letters, so it's pretty easy to identify them in code. +
Classes are what you use to make objects. They're like a template. You do this through class methods such as 'new', or, in the case of our SinOsc class above, 'ar'. Such methods return an object, an instance, and the arguments affect what its data will be, and how it will behave. Now take another look at the example in question: + +
This tells the class SinOsc to make an instance of itself. All SinOscs are an example of what are called unit generators, or UGens. These are objects which produce audio or control signals. SinOsc is a sine wave oscillator. This means that it will produce a signal consisting of a single frequency. A graph of its waveform would look like this:
This waveform loops, creating the output signal. 'ar' means make the instance audio rate. SuperCollider calculates audio in groups of samples, called blocks. Audio rate means that the UGen will calculate a value for each sample in the block. There's another method, 'kr', which means control rate. This means calculate a single value for each block of samples. This can save a lot of computing power, and is fine for (you guessed it) signals which control other UGens, but it's not fine enough detail for synthesizing audio signals. +
The three arguments to SinOsc-ar given in the example determine a few things about the resulting instance. I happen to know that the arguments are frequency, phase, and mul. (We'll get to how I know that in a second.) Frequency is just the frequency of the oscillator in Hertz (Hz), or cycles per second (cps). Phase refers to where it will start in the cycle of its waveform. For SinOsc (but not for all UGens) phase is given in radians. If you don't know what radians are, don't worry, just understand that it's a value between 0 and 2 * pi. (You can look at a trigonometry text if you really want more detail.) So if we made a SinOsc with a phase of (pi * 0.5), or one quarter of the way through its cycle, the waveform would look like this:
+ +Make sense? Here are several cycles of the two side by side to make the idea clearer:
+ +So what about 'mul'? Mul is a special argument that almost all UGens have. It's so ubiquitous that it's usually not even explained in the documentation. It just means a value or signal by which the output of the UGen will be multiplied. It turns out that in the case of audio signals, this affects the amplitude of the signal, or how loud it is. The default mul of most UGens is 1, which means that the signal will oscillate between 1 and -1. This is a good default as anything bigger would cause clipping and distortion. A mul of 0 would be effectively silent, as if the volume knob was turned all the way down. +
To make clearer how mul works, here is a graph of two SinOscs, one with the default mul of 1, and one with a mul of 0.25:
+ +Get the idea? There's also another similar arg called 'add' (also generally unexplained in the doc), which (you guessed it) is something which is added to the output signal. This can be quite useful for things like control signals. 'add' has a default value of 0, which is why we don't need to specify something for it. +
Okay, with all this in mind, let's review our example, with comments: +
Here's another example of polymorphism, and how powerful it is. When creating Functions of UGens, for many arguments you don't have to use fixed values, you can in fact use other UGens! Below is an example which demonstrates this: + +
Try this. (Again, use Cmd-. to stop the sound.) +
What we've done here is plugged the first SinOsc (a control rate one!) into the mul arg of the second one. So its output is being multiplied by the output of the second one. Now lets look at the first SinOsc's arguments. +
Frequency is set to 0.5 cps, which if you think about it a bit means that it will complete one cycle every 2 seconds. (1 / 0.5 = 2) +
Mul and add are both set to 0.5. Think for a second about what that will do. If by default SinOsc goes between 1 and -1, then a mul of 0.5 will scale that down to between 0.5 and -0.5. Adding 0.5 to that brings it to between 0 and 1, a rather good range for mul! +
The phase of 1.5pi (this just means 1.5 * pi) means 3/4 of the way through its cycle, which if you look at the first graph above you'll see is the lowest point, or in this case, 0. So the ampOsc SinOsc's waveform will look like this:
+ +And what we have in the end is a SinOsc that fades gently in and out. Shifting the phase just means that we start quiet and fade in. We're effectively using ampOsc as what is called an amplitude envelope. There are other ways of doing the same thing, some of them simpler, but this demonstrates the principle. +
Patching together UGens in this way is the basic way that you make sound in SC. For an overview of the various types of UGens available in SC, see Browse: UGens or Tour of UGens. +
For more information see: +
Functions, Function, Browse: UGens Tour of UGens
Experiment with altering the Functions in the text above. For instance try changing the frequencies of the SinOsc, or making multi-channel versions of things. +
____________________ +
This document is part of the tutorial Getting Started With SuperCollider. +
Click here to go on to the next section: 06. Presented in Living Stereo +
Click here to return to the table of Contents: 00. Getting Started With SC
When you schedule a function (as in the Scheduling Events tutorial), the function always begins at the beginning and runs through to the end. For sequencing, it's more useful to have a control structure that can run part of the way through, return a value, and then pick up where it left off the next time it's needed. In SuperCollider, this is a Routine. +
Routines can be used for data processing, e.g.
r = Routine({ + "abcde".yield; + "fghij".yield; + "klmno".yield; + "pqrst".yield; + "uvwxy".yield; + "z{|}~".yield; +}); + +r.next; // get the next value from the Routine +6.do({ r.next.postln });+ +
The first time you call next, the routine yields "abcde". This yield value becomes the result of r.next, and is printed in the post window. On the second next call, execution picks up just after the first yield and continues with the second string, and so forth. When there is nothing more to yield, r.next returns nil. +
We will come back to the use of routines for data generation. More important for sequencing is what happens when you schedule a routine on a clock, and the routine returns time values.
Recall that, when you schedule a function on a clock, numbers returned by the function are treated as time values -- specifically, the amount of time until the function should execute again. The same thing happens with numbers yielded by a routine.
r = Routine({ + var delta; + loop { + delta = rrand(1, 3) * 0.5; + "Will wait ".post; delta.postln; + delta.yield; + } +}); + +r.next; + +TempoClock.default.sched(0, r); + +r.stop;+ +
Now let's replace the posting statements with instructions to play a synth. Preparation:
( +SynthDef(\singrain, { |freq = 440, amp = 0.2, sustain = 1| + var sig; + sig = SinOsc.ar(freq, 0, amp) * EnvGen.kr(Env.perc(0.01, sustain), doneAction: 2); + Out.ar(0, sig ! 2); // sig ! 2 is the same as [sig, sig] +}).add; + +r = Routine({ + var delta; + loop { + delta = rrand(1, 3) * 0.5; + Synth(\singrain, [freq: exprand(200, 800), amp: rrand(0.1, 0.5), sustain: delta * 0.8]); + delta.yield; + } +}); +)+ +
Scheduling a routine makes a certain sense, but playing a routine seems more intuitive.
r.play; + +r.stop;+ +
There you go -- our first sequence.
Routines have one sticky little characteristic that can limit their usefulness as musical objects. Once you stop a routine, you can only start it over again from the beginning. There is no way to replay the routine from the point where it was stopped. +
Task is a variation that can be paused and resumed at will. For example, let's iterate over a C major scale. Note that all of SuperCollider's control structures are valid inside a Routine or Task. Note also that we can use 'wait' as a synonym for 'yield'.
( +t = Task({ + loop { + [60, 62, 64, 65, 67, 69, 71, 72].do({ |midi| + Synth(\singrain, [freq: midi.midicps, amp: 0.2, sustain: 0.1]); + 0.125.wait; + }); + } +}).play; +) + +// probably stops in the middle of the scale +t.stop; + +t.play; // should pick up with the next note + +t.stop;+ +
Task will be used for the remainder of this tutorial.
By default, play applied to a Task starts the Task immediately. Most of the time, many tasks will be running simultaneously, and they should be synchronized. While there might be a virtuoso out there somewhere who can hit the enter key at just right time for precise sync, most of us would prefer a more reliable mechanism. +
Play takes several arguments to control its behavior.
aRoutine.play(clock, quant) +aTask.play(argClock, doReset, quant)+
The quant argument uses a basic model of two numbers, which can be related to the western concept of meter: +
quant: Corresponds roughly to bar length; the current time is rounded up to the next multiple of this number phase: Position within the bar (0 = beginning of the bar) +
For convenience, if you just want to start at the beginning of the bar, you can give the bar length as a number. An array of two numbers tells SuperCollider the bar length and the phase. +
To see how this works in practice, let's take the C major scale above and play two copies of it slightly offset. We'll slow the rhythm down to 16th-notes (0.25) and start the second one 8th-note into the bar. We will need two tasks to do this, which will be manufactured in a function.
( +f = { + Task({ + loop { + [60, 62, 64, 65, 67, 69, 71, 72].do({ |midi| + Synth(\singrain, [freq: midi.midicps, amp: 0.2, sustain: 0.1]); + 0.25.wait; + }); + } + }); +}; +) + +t = f.value.play(quant: 4); // start on next 4-beat boundary + +u = f.value.play(quant: [4, 0.5]); // next 4-beat boundary + a half-beat + +t.stop; u.stop;+
The previous example controls the generation of one parameter (pitch) by looping over an array inside the Task. What if you want to control several parameters? +
Remember that routines can also generate data, in addition to their scheduling capabilities. You can refer to as many data routines as you want in your sequence.
( +var midi, dur; +midi = Routine({ + [60, 72, 71, 67, 69, 71, 72, 60, 69, 67].do({ |midi| midi.yield }); +}); +dur = Routine({ + [2, 2, 1, 0.5, 0.5, 1, 1, 2, 2, 3].do({ |dur| dur.yield }); +}); + +SynthDef(\smooth, { |freq = 440, sustain = 1, amp = 0.5| + var sig; + sig = SinOsc.ar(freq, 0, amp) * EnvGen.kr(Env.linen(0.05, sustain, 0.1), doneAction: 2); + Out.ar(0, sig ! 2) +}).add; + +r = Task({ + var delta; + while { + delta = dur.next; + delta.notNil + } { + Synth(\smooth, [freq: midi.next.midicps, sustain: delta]); + delta.yield; + } +}).play(quant: TempoClock.default.beats + 1.0); +)+ +
Note that routines are used for the data, but task is used for play. Also, unlike the previous infinite sequences, this one stops when it runs out of data. That's the purpose of the while loop -- it continues only as long as the 'dur' data stream keeps pumping out values. (See the Control Structures helpfile for more on while.) +
There must be an easier way to write the data streams -- repeatedly writing the same do loop is certainly inconvenient. In fact, there is such a way, covered in the next tutorial: sequencing with patterns. +
(Here we use quant simply to delay Task onset by one beat. This is because it takes some time for the synthdef to be ready for use on the server. Without it, the first note would not be heard.)
Using Synth as in the preceding examples can result in small but sometimes noticeable timing inaccuracies. This is because it takes a short time to transmit OSC messages from your code to the server, and this time is not always constant. SuperCollider deals with this by giving you the option to send the message with a timestamp telling the server exactly when the message should take effect. A latency value is used to calculate the timestamp. +
Latency works by adding itself to the current time on the clock. If all the messages go out with the same latency value, their timing will be precise relative to each other and to the clock. The Server OSC timing help file explains in more detail how this works, but you don't really need to know all of that in order to use it. The main point is to use a consistent, small latency value for perfect timing. (A Server object has a latency variable that you can use for consistency.) +
Here's an example illustrating the kinds of inaccuracy you might hear. The inaccuracy may be more or less noticeable on different systems. It uses the \singrain SynthDef above and plays 10 notes per second.
( +t = Task({ + loop { + Synth(\singrain, [freq: exprand(400, 1200), sustain: 0.08]); + 0.1.wait; + } +}).play; +) + +t.stop;+ +
The easiest way to add latency to your outgoing Synths is with the Server makeBundle method. Don't worry about how it works for now -- the important thing is that it uses the first value for latency, and runs the messages produced by the function according to that latency.
( +t = Task({ + loop { + s.makeBundle(s.latency, { + Synth(\singrain, [freq: exprand(400, 1200), sustain: 0.08]); + }); + 0.1.wait; + } +}).play; +) + +t.stop;+ +
See also: +
Routine, Task, Quant, Server OSC timing, Bundled Server Messages
Make a more interesting SynthDef to replace the \smooth SynthDef. Use more arguments for greater variability. Then change the data streams in the 'Over the Rainbow' example and add new data streams to play a different tune, more expressively. +
____________________ +
This document is part of the tutorial Getting Started With SuperCollider. +
Click here to go on to the next section: 16. Sequencing with Patterns +
Click here to return to the table of Contents: 00. Getting Started With SC
The SuperCollider Pattern library provides a means of specifying dynamic structural transformations of musical processes. It provides similar capabilities as one finds in Nyquist, Elody, Siren, Kyma, HMSL, DMix, and Patchwork. +
By using coroutines and streams rather than eager functional methods it is able to work in a lazy event by event method instead of the all-at-once method of Elody and Siren. It provides the kind of dynamic live control found in HMSL but with the more general event models of the others. In Nyquist and Siren certain transformation like Stretch and Transpose are specially coded into the framework. In SuperCollider Patterns, any parameter may have transformations applied to it. The only one treated specially is time, so that parallel streams can be merged. +
In order to understand the framework, a number of concepts must be covered. These concepts are embodied in the classes for Streams, Patterns, and Events. You should learn these concepts in the order presented. The framework is built up in layers. If you skip ahead to get to the cool stuff first, you will have missed some important points.
A stream represents a lazy sequence of values. The next value in the sequence is obtained by sending the message next to the stream object. The sequence can be restarted from the beginning by sending the message reset to the stream object. A stream can be of finite or infinite length. When a finite length stream has reached the end, it returns nil. +
A stream can be any object that responds to the next and reset messages. Any object that responds to these messages can act as a stream. It happens that the class Object defines next and reset for all objects. In Object, both next and reset are defined to return this
. Thus any object is by default a stream that represents an infinite sequence of itself.
7.next.postln; // 7 responds to next by returning itself+
In addition to the default streams implemented by Object, there is a class Stream that provides more functionality such as math operations on streams and filtering of streams. +
A generally useful subclass of Stream is the class FuncStream which allows the user to provide functions to execute in response to next and reset. Here is a FuncStream that represents an infinite random sequence:
( +var a; +a = FuncStream.new({ #[1, 2, 3, 4].choose }); +5.do({ a.next.postln; }); // print 5 values from the stream +)+ +
Another useful subclass of Stream is Routine which is a special kind of function that can act like a Stream. Routines are functions that can return a value from the middle and then be resumed from that point when called again. The yield message returns a value from the Routine. The next time the Routine is called it begins by returning from the yield and continues from that point. See the Routine help file. +
Here is a Routine that represents a finite sequence of values:
( +var a; +a = Routine.new({ + 3.do({ arg i; i.yield; }) + }); +4.do({ a.next.postln; }); // print 4 values from stream +)+ +
and another:
( +var a; +a = Routine.new({ + 3.do({ arg i; + (i+1).do({ arg j; j.yield; }) + }) + }); +8.do({ a.next.postln; }); // print 8 values from stream +)+
Stream is a subclass of AbstractFunction which means that one can do math operations on streams to produce other streams. +
Applying a unary operator to a stream:
( +var a, b; +// a is a stream that counts from 0 to 9 +a = Routine.new({ + 10.do({ arg i; i.yield; }) + }); +b = a.squared; // stream b is a square of the stream a +12.do({ b.next.postln; }); +)+ +
Using a binary operator on a stream:
( +var a, b; +// a is a stream that counts from 0 to 9 +a = Routine.new({ + 10.do({ arg i; i.yield; }) + }); +b = a + 100; // add a constant value to stream a +12.do({ b.next.postln; }); +)+ +
Using a binary operator on two streams:
( +var a, b, c; +// a is a stream that counts from 0 to 9 +a = Routine.new({ + 10.do({ arg i; i.yield; }) + }); +// b is a stream that counts from 100 to 280 by 20 +b = Routine.new({ + forBy (100,280,20, { arg i; i.yield }) + }); +c = a + b; // add streams a and b +12.do({ c.next.postln; }); +)+
Streams respond to the messages collect
, select
, and reject
by returning a new Stream.
+
The collect
message returns a stream that is modified by a function in the same way as the collect message sent to a Collection returns a modified Collection.
( +var a, b; +// a is a stream that counts from 0 to 9 +a = Routine.new({ + 10.do({ arg i; i.yield; }) + }); +// b is a stream that adds 100 to even values +b = a.collect({ arg item; if (item.even, { item + 100 },{ item }); }); +6.do({ b.next.postln; }); +)+ +
The select
message creates a stream that passes only items that return true from a user supplied function.
( +var a, b; +// a is a stream that counts from 0 to 9 +a = Routine.new({ + 10.do({ arg i; i.yield; }) + }); +// b is a stream that only returns the odd values from stream a +b = a.select({ arg item; item.odd; }); +6.do({ b.next.postln; }); +)+ +
The reject
message creates a stream that passes only items that return false from a user supplied function.
( +var a, b; +// a is a stream that counts from 0 to 9 +a = Routine.new({ + 10.do({ arg i; i.yield; }) + }); +// b is a stream that only returns the non-odd values from stream a +b = a.reject({ arg item; item.odd; }); +6.do({ b.next.postln; }); +)+
Here is a sound example to show how you might use Streams to generate musical material.
( + s = Server.local; + SynthDef(\help_SPE1, { arg i_out=0, freq; + var out; + out = RLPF.ar( + LFSaw.ar( freq, mul: EnvGen.kr( Env.perc, levelScale: 0.3, doneAction: 2 )), + LFNoise1.kr(1, 36, 110).midicps, + 0.1 + ); + // out = [out, DelayN.ar(out, 0.04, 0.04) ]; + 4.do({ out = AllpassN.ar(out, 0.05, [0.05.rand, 0.05.rand], 4) }); + Out.ar( i_out, out ); + }).send(s); +) +( +// streams as a sequence of pitches + var stream, dur; + dur = 1/8; + stream = Routine.new({ + loop({ + if (0.5.coin, { + // run of fifths: + 24.yield; + 31.yield; + 36.yield; + 43.yield; + 48.yield; + 55.yield; + }); + rrand(2,5).do({ + // varying arpeggio + 60.yield; + #[63,65].choose.yield; + 67.yield; + #[70,72,74].choose.yield; + }); + // random high melody + rrand(3,9).do({ #[74,75,77,79,81].choose.yield }); + }); + }); + Routine({ + loop({ + Synth(\help_SPE1, [ \freq, stream.next.midicps ] ); + dur.wait; // synonym for yield, used by .play to schedule next occurence + }) + }).play +)+
More about Streams can be learned from the book A Little Smalltalk by Timothy Budd. He calls them Generators and shows how they can be used to solve problems like the "eight queens" problem etc. +
To go to the next file: Understanding Streams, Patterns and Events - Part 2
The preceding sections showed how to use Streams and Patterns to generate complex sequences of values for a single parameter at a time. +
This section covers Environments and Events, which are used to build a symbolic event framework for patterns, allowing you to control all aspects of a composition using patterns.
An Environment is an IdentityDictionary mapping Symbols to values. There is always one current Environment which is stored in the currentEnvironment
class variable of class Object.
+
Symbol and value pairs may be put into the current Environment as follows:
currentEnvironment.put(\myvariable, 999);+ +
and retrieved from the current Environment as follows:
currentEnvironment.at(\myvariable).postln;+ +
The compiler provides a shorthand for the two constructs above.
~myvariable = 888;+ +
is equivalent to:
currentEnvironment.put(\myvariable, 888);+ +
and:
~myvariable.postln;+ +
is equivalent to:
currentEnvironment.at(\myvariable).postln;+
Environment has a class method make which can be used to create an Environment and fill it with values. What make does is temporarily replace the current Environment with a new one, call your function where you fill the Environment with values, then it replaces the previous current Environment and returns you the new one.
( +var a; +a = Environment.make({ + ~a = 100; + ~b = 200; + ~c = 300; +}); +a.postln; +)+
The instance method use lets you temporarily replace the current Environment with one you have made. The use method returns the result of your function instead of the Environment like make does.
( +var a; +a = Environment.make({ + ~a = 10; + ~b = 200; + ~c = 3000; +}); +a.use({ + ~a + ~b + ~c +}).postln; +)+ +
There is also a use class method for when you want to make and use the result from an Environment directly.
( +var a; +a = Environment.use({ + ~a = 10; + ~b = 200; + ~c = 3000; + ~a + ~b + ~c +}).postln; +)+
It is possible to call a Function and have it look up any unspecified argument values from the current Environment. This is done with the valueEnvir and valueArrayEnvir methods. These methods will, for any unspecified argument value, look in the current Environment for a symbol with the same name as the argument. If the argument is not found then whatever the function defines as the default value for that argument is used.
( +var f; + +// define a function +f = { arg x, y, z; [x, y, z].postln; }; + +Environment.use({ + ~x = 7; + ~y = 8; + ~z = 9; + + f.valueEnvir(1, 2, 3); // all values supplied + f.valueEnvir(1, 2); // z is looked up in the current Environment + f.valueEnvir(1); // y and z are looked up in the current Environment + f.valueEnvir; // all arguments are looked up in the current Environment + f.valueEnvir(z: 1); // x and y are looked up in the current Environment +}); +)+ +
Here is a somewhat contrived example of how the Environment might be used to manufacture SynthDefs. Even though the three functions below have the freq, amp and pan args declared in different orders it does not matter, because valueEnvir looks them up in the environment.
( +var a, b, c, n; + +n = 40; +a = { arg freq, amp, pan; + Pan2.ar(SinOsc.ar(freq), pan, amp); +}; +b = { arg amp, pan, freq; + Pan2.ar(RLPF.ar(Saw.ar(freq), freq * 6, 0.1), pan, amp); +}; +c = { arg pan, freq, amp; + Pan2.ar(Resonz.ar(GrayNoise.ar, freq * 2, 0.1), pan, amp * 2); +}; + +Task({ + n.do({ arg i; + SynthDef("Help-SPE4-EnvirDef-" ++ i.asString, { + var out; + Environment.use({ + // set values in the environment + ~freq = exprand(80, 600); + ~amp = 0.1 + 0.3.rand; + ~pan = 1.0.rand2; + + // call a randomly chosen instrument function + // with values from the environment + out = [a,b,c].choose.valueEnvir; + }); + out = CombC.ar(out, 0.2, 0.2, 3, 1, out); + out = out * EnvGen.kr( + Env.sine, doneAction: 2, timeScale: 1.0 + 6.0.rand, levelScale: 0.3 + ); + Out.ar( 0, out ); + }).send(s); + 0.02.wait; + }); + loop({ + Synth( "Help-SPE4-EnvirDef-" ++ n.rand.asString ); + (0.5 + 2.0.rand).wait; + }); +}).play; +)+
The class Event is a subclass of Environment. Events are mappings of Symbols representing names of parameters for a musical event to their value. This lets you put any information you want into an event. +
The class getter method default retrieves the default prototype event which has been initialized with values for many useful parameters. It represents only one possible event model. You are free to create your own, however it would be good to understand the one provided first so that you can see what can be done. +
A prototype event is a default event which will be transformed by the streams returned by patterns. Compositions produced by event patterns are created entirely from transformations of copies of a single protoEvent. +
The patterns discussed in parts 2 and 3 are known as "value patterns" because their streams return a single value for each call to next. Here we introduce "event patterns" which once turned into streams, return an Event for each call to next. +
The class Pbind provides a bridge between value patterns and event patterns. It binds symbols in each event to values obtained from a pattern. Pbind takes arguments in pairs, the first of a pair being a Symbol and the second being a value Pattern. Any object can act as a Pattern, so you can use constants as the pattern ( see \amp
in the example below ).
+
The Pbind stream returns nil whenever the first one of its streams ends.
Pbind( \freq, Pseq([440,880]) ).play+ +
An event stream is created for a Pattern by sending it the asStream
message. What Pbind does is to produce a stream which puts the values for its symbols into the event, possibly overwriting previous bindings to those symbols:
t = Pbind( \freq, Pseq([440,880]) ).asStream; +t.next(Event.default); +t.next(Event.default); +t.next(Event.default);+ +
When calling Pattern: -play an EventStreamPlayer is automatically generated which handles scheduling as well as passing the protoEvent into the event stream.
The class EventStreamPlayer is a subclass of PauseStream. A PauseStream is just a wrapper for a stream allowing to play, stop, start it, etc... +
EventStreamPlayers are initialized using the event stream returned by Pbind-asStream, as well as with a protoEvent. The EventStreamPlayer passes in a protoEvent, at each call to next on the Pbind stream. The Pbind stream copies the event to pass down and back up the tree of pattern streams so that each stream can modify it. +
An EventStreamPlayer is itself a stream which returns scalars (numbers) which are used by the clock to schedule its next invocation. At every call to EventStreamPlayer-next by the clock, the player gets its delta values by querying the Event after it has been returned by the Pbind stream traversal.
In SC2, you called asEventStream on an Pattern and you'd get a stream which actually returned events. +
In SC3, if you want an event stream proper you call asStream on the Event Pattern. This will give you a stream of events which you can then use to initialize an EventStreamPlayer object. You don't however need to worry about that because it is usually done for you in Pattern's play method. Also changed is that you do not pass in your protoEvent through the asStream method. It is passed in for you by the EventStreamPlayer at each call to next on the stream. +
Here you can see what the stream returned from a Pbind looks like.
( +var pattern, stream; + + // bind Symbol xyz to values obtained from a pattern +pattern = Pbind( + \xyz, Pseq([1, 2, 3]) +); + + // create a stream of events for the Pbind pattern. +stream = pattern.asStream; + + // event Streams require a prototype event as input. + // this example uses an empty Event as a prototype +4.do({ stream.next(Event.new).postln; }); +)+ +
Here is an example with more bindings.
( +var pattern, stream; + +pattern = Pbind( + \abc, Prand([6, 7, 8, 9], inf ), + \xyz, Pseq([1, 2, 3], 2 ), + \uuu, 999 // a constant represents an infinite sequence of itself +); + +stream = pattern.asStream; + +7.do({ stream.next(Event.new).postln; }); +)+ +
The ListPatterns discussed in part 3 can be put around Event Streams to create sequences of Event Streams.
( +var pattern, stream; +pattern = + Pseq([ + Pbind( \abc, Pseq([1, 2, 3])), + Pbind( \def, Pseq([4, 5, 6])), + Pbind( \xyz, Pseq([7, 8, 9])) + ]); +stream = pattern.asStream; +10.do({ stream.next(Event.new).postln; }); +) + +( +var pattern, stream; +pattern = + Prand([ + Pbind( \abc, Pseq([1, 2, 3])), + Pbind( \def, Pseq([4, 5, 6])), + Pbind( \xyz, Pseq([7, 8, 9])) + ], 3); +stream = pattern.asStream; +10.do({ stream.next(Event.new).postln; }); +)+ +
To go to the next file: Understanding Streams, Patterns and Events - Part 5
+
+/* Some example nonsense code to test syntax coloring */ + +~foo = SomeClass.new(\abc, 123, "xyz") { |one, two, three=42.7| + var myVariable, test = pi*5, bool = true; + + if ((two & one) < 3) { // A comment... + "hello".postln; + (abc: $x, def: \y, ghi: nil); + } { + [~bar, zoo, thisProcess.platform]; + }; +}; + +Routine { + inf.do {|i| + ("count"+i).postln; + 5.0.rand.wait; + }; +}.play; ++ + +