1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
CLASS:: ScopeView
summary:: A buffer plotting view.
categories:: GUI>Views
related:: Classes/Stethoscope, Classes/FreqScopeView, Classes/FreqScope
DESCRIPTION::
ScopeView is mainly intended to support the implementation of link::Classes/Stethoscope:: (an oscilloscope), link::Classes/FreqScopeView:: (a basic frequency spectrum plotting view) and link::Classes/FreqScope:: (a frequency spectrum analyzer tool).
It is optimized to efficiently perform frequent plotting of the contents of a link::Classes/Buffer:: into which a link::Classes/ScopeOut2:: UGen is writing. It will periodically poll the buffer for data and update the plot, as long as the ScopeOut2 UGen is writing into it; the buffer will not be plotted otherwise.
CLASSMETHODS::
PRIVATE:: key
INSTANCEMETHODS::
METHOD:: bufnum
The number of the Buffer to plot.
To set up the ScopeView object for plotting, one needs to set a valid buffer number and tell a link::Classes/ScopeOut2:: UGen to write to it. Before the view starts periodically plotting the buffer, however, one needs to assign it a link::Classes/Server:: object and call the link::#-start:: method on it.
If the ScopeOut2 UGen stops writing or an invalid buffer number is set, the plotting will pause.
argument::
An integer.
METHOD:: server
An instance of ScopeView must be assigned to a particular server for it to work.
argument::
A link::Classes/Server:: object
METHOD:: start
Tells the ScopeView object to start plotting.
METHOD:: stop
Tells the ScopeView object to stop plotting. Plotting can be resumed anytime with the link::#start:: method.
METHOD:: style
The plotting style:
list::
## 0 = the channels are vertically spaced
## 1 = the channels are overlaid
## 2 = lissajou; the first two channels are used for 2D plotting (as streams of x and y coordinates).
::
argument::
One of the above Integers.
METHOD:: xZoom
The scaling factor on the horizontal axis.
argument::
A Float.
METHOD:: yZoom
The scaling factor on the vertical axis.
argument::
A Float.
METHOD:: x
The horizontal offset.
argument::
A Float.
METHOD:: y
The vertical offset.
argument::
A Float.
METHOD:: fill
Fill area under scope.
argument::
A Boolean.
METHOD:: waveColors
The colors used to plot each of the channels.
argument::
An Array of Colors, one per channel.
EXAMPLES::
SUBSECTION:: A step-by-step example
code::
// boot the server (if not already booted)
s.boot;
// execute the following two blocks in succession:
(
f = Buffer.alloc(s,1024,2);
b = Bus.audio(s,1);
w = Window.new.front;
w.onClose = { // free everything when finished
c.stop; a.free; d.free; f.free; b.free;
"SynthDefs, busses and buffers have all been freed.".postln;
};
c = ScopeView(w.view,w.view.bounds.insetAll(10,10,10,10));
c.bufnum = f.bufnum;
c.server = s; // Important: one must assign the ScopeView to a server
)
(
// listening to the bus, using ScopeOut2 to write it to the buffer
a = SynthDef("monoscope", { arg bus, bufnum;
var z;
z = In.ar(bus, 2);
ScopeOut2.ar(z, bufnum);
Out.ar(0, z);
}).play(
target: RootNode(s),
args: [\bus, b.index, \bufnum, f.bufnum],
addAction: \addToTail // make sure it goes after what you are scoping
);
// making noise onto the buffer
d = SynthDef("noise", { arg bus;
var z;
z = LFSaw.ar(SinOsc.kr(0.1).range(300,1000),[0,1]*pi) * 0.1;
Out.ar(bus, z);
}).play(
s,
[\bus,b.index]
);
c.start; // Tell the ScopeView to start
CmdPeriod.doOnce({w.close});
)
c.style = 0; // vertically spaced
c.style = 1; // overlapped
c.style = 2; // x/y
::
SUBSECTION:: An interactive example with sound
This explains all the options:
code::
(
s.waitForBoot({
var func, sdef1, sdef2, syn1, syn2,startButton ;
f = Buffer.alloc(s,1024,2);
b = Bus.audio(s,1);
w = Window("Scope", Rect(150, Window.screenBounds.height-500,790,400)).front;
c = ScopeView(w,Rect(10,10,380,380)); // this is SCScope
c.bufnum = f.bufnum;
// IMPORTANT
c.server = s;
v = CompositeView(w,Rect(400,10,380,380)).background_(Color.rand(0.7));
v.decorator = n = FlowLayout(v.bounds, margin: 0@0, gap: 5@5);
a = StaticText(v, Rect(20, 70, 90, 20)).string_(" xZoom = 1").background_(Color.rand);
m = Slider(v, Rect(20, 60, 285, 20)).background_(a.background).action_({func.value}).value_(0.5);
d = StaticText(v, Rect(20, 70, 90, 20)).string_(" yZoom = 1").background_(Color.rand);
g = Slider(v, Rect(20, 60, 285, 20)).background_(d.background).action_({func.value}).value_(0.5);
h = StaticText(v, Rect(20, 70, 90, 20)).string_(" x = 0").background_(Color.rand);
i = Slider(v, Rect(20, 60, 285, 20)).background_(h.background).action_({func.value}).value_(0.5);
Button(v, Rect(0,0,380, 20))
.states_([["waveColors = [ Color.rand, ... ]",Color.black,Color.rand]])
.action_({c.waveColors = [Color.rand,Color.rand]});
Button(v, Rect(0,0,380, 20))
.states_([[" background = Color.rand(0.1,0.3) ",Color.black,Color.rand]])
.action_({c.background = Color.rand(0.1,0.3) });
t = Button(v, Rect(0,0,380, 20))
.states_([["Current style is 0",Color.black,Color.rand],
["Current style is 1",Color.black,Color.rand],
["Current style is 2",Color.black,Color.rand]])
.action_({func.value});
func = {
c.xZoom = ([0.25, 10, \exp, 1/8, 1].asSpec.map(m.value)); a.string = " xZoom = %".format(c.xZoom);
c.yZoom = ([0.25, 10, \exp, 1/8, 1].asSpec.map(g.value)); d.string = " yZoom = %".format(c.yZoom);
c.x = ([ -1024,1024, \linear, 1/8, 1].asSpec.map(i.value)); h.string = " x = %".format(c.x);
c.style=t.value
};
startButton = Button.new(v, Rect(0,0,380, 50))
.states_([["Start Sound",Color.black,Color.green],["Stop Sound",Color.black,Color.red]]).action_({});
startButton.action_{
(startButton.value==1).if{
syn1 = SynthDef("test1", { arg bus, bufnum;
var z;
z = In.ar(bus,2);
// ScopeOut2 writes the audio to the buffer
// IMPORTANT - ScopeOut2, not ScopeOut
ScopeOut2.ar(z, bufnum);
Out.ar(0,z);
}).play(
RootNode(s),
[\bus,b.index, \bufnum, f.bufnum] ,
\addToTail // make sure it goes after what you are scoping
);
// making noise onto the buffer
syn2 = SynthDef("test2", { arg bus;
var z;
z = PMOsc.ar([300,250],*SinOsc.ar([0.027,0.017])*pi) * 0.1;
Out.ar(bus, z);
}).play(s,[\bus,b.index]);
}{syn1.free; syn2.free};
};
// IMPORTANT
c.start;
w.onClose={syn1.free; syn2.free; b.free; f.free};
CmdPeriod.doOnce({w.close});
})
)
::
|