Before discussing how we can scope audio signals in real time, it is worth reviewing the various ways in which we can create static graphs and charts out of arbitrary numerical datasets or signals.
Using plot and plot graph
SuperCollider provides us with a very handy plot
method. We can use this method in different situations to create graphs on the fly from instances of Function
, ArrayedCollection
, Env
, Buffer
, SoundFile
, WaveTable
, and from a series of other objects (also depending on what extensions we have installed). An example of this is shown in the following code:
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
In all cases, the resulting graphs will be automatically normalized with respect to the kind of data plotted so that each dimensions' display range is determined by the minimum and maximum quantities it has to represent; that is, to say that the plot's graph is content-dependent. Additionally, their meaning depends upon the receiver (that is, the kind of object plotted) so that for instances of Array
, Wavetable
, or Signal
, the graph would represent the value per index; for UGen graphs, amplitude per unit time; for instances of Env
, value per unit time; and for instances of Buffer
, amplitude per frame. Since its behavior is different for different kinds of objects, the plot is said to be
polymorphic. We should always consider the implicit consequences of these two properties. For example, the following two waveforms could be easily mistaken as identical, even if they are not:
To compensate for such a phenomenon, we need to explicitly set the minima (minval) and maxima (maxval) arguments. Interestingly enough, we can also plot abstract functions as long as they are one-argument ones and return some arithmetic value. We can do this with the plotGraph
method, as follows:
Here, the interpreter calculates the output of the given function for 100 different values in the range of ± π and populates the graph with the results; the horizontal axis representing node indexes and the vertical axis representing the function's output.
Note
Buffer objects have a finite capacitance measured in frames; each frame may hold exactly one sample, therefore, a frame is the container of a sample.
Polymorphism in Computer Science refers to the ability in programming to present the same interface for different underlying forms.
Both plot
and plotGraph
are convenient methods, which ostensibly are just abstractions of a series of tasks. Whenever they are invoked, a parent Window
is created containing an instance of Plotter
whose specifications are configured accordingly. Explicitly creating and using Plotter
allows sophisticated control over the way our data is plotted. The following code exemplifies a number of features of the Plotter
object:
( // data visualization using custom plotters
// the parent window
var window = Window.new("Plotter Example", Rect(0,0,640,480)).front;
// the datasets to visualize
var datasetA = Array.fill(1000,{rrand(-1.0,1.0)});// random floats
var datasetB = [ // a 2-dimensional array of random floats
Array.fill(10,{rrand(-1.0,1.0)}),
Array.fill(10,{rrand(-1.0,1.0)})
];
// the plotters
var plotterA = Plotter("PlotterA",Rect(5,5,630,235),window);
var plotterB = Plotter("PlotterB",Rect(5,240,630,235),window);
// setup and customize plotterA
plotterA.value_(datasetA); // load dataset
plotterA.setProperties( // customize appearance
\plotColor, Color.red, // plot color
\backgroundColor, Color.black, // background color
\gridColorX, Color.white, // gridX color
\gridColorY, Color.yellow) // gridY color
.editMode_(true) // allow editing with the cursor
.editFunc_({ // this function is evaluated whenever data is edited
arg plotter,plotIndex,index,val,x,y;
("Value: " ++ val ++ " inserted at index: " ++ index ++
".").postln;
});
// setup and customize plotterB
plotterB.value_(datasetB); // load datasetB
plotterB.superpose_(true); // allow channels overlay
plotterB.setProperties(
\plotColor, [Color.blue,Color.green], // plot colors
\backgroundColor, Color.grey, // background color
\gridOnX, false, // no horizontal grid
\gridOnY, false) // no vertical grid
.plotMode_(\steps); // use step interpolation
)
The result is illustrated in the following screenshot:
The comments pretty much explain everything. The first Plotter
object is editable, which means that we can alter the graph when dragging and clicking on it with the mouse. Whenever we do so, editFunc
will be evaluated with the following that are passed as arguments:
The Plotter
object.
The plot index (which is only meaningful if there is more than one graph, such as for multichannel signals, of course).
The index position (horizontal axis value).
The value of the vertical dimension.
The x and the y positioning of the cursor.
In this case, while clicking or dragging with the mouse, a simple message is printed in the console.
The second Plotter
object that operates on a multichannel dataset will create ramps out of every individual channel and superimpose them on the same graph using different colors. Using plotMode
, we can select between the following alternative data representation modes, namely, \linear
(linear interpolation), \points
(data points only), \plines
(both lines and points), \levels
(horizontal lines), and \steps
(ramps).
In a visualization context, we may encounter situations wherein we need to plot the contents of some audio file. We could do so with Buffer
and Plotter
, yet there does exist a dedicated class for such cases, namely, SoundFileView
as shown in the following code:
Again the code is pretty straightforward; the only implication being that we need to open and read the actual file with a SoundFile
object before we can read its contents into the SoundFileView
object. When large sound files are involved, we will have to use readWithTask
instead to avoid overloading our computer's memory. Then, if needed, we can use the zoom
(or zoomToFrac
) and
scrollTo
methods to only display portions of the file or to animate its contents. For example, the previous code could continue as shown in the following code:
Note that SuperCollider will refuse to schedule any GUI-related operation in the SystemClock
class, hence we will have to use defer
whenever such operations are involved. This is so that we can implicitly schedule them in the AppClock
instead.