HelpBrowser: path box becomes a more conventional search box
[supercollider.git] / SCClassLibrary / JITLib / ProxySpace / InBus.sc
blob5103cab1b4eb7d8871bf1164bad378ab29773afc
1 InBus {
3         *ar { | bus, numChannels = 2, offset = 0 |
4                 ^this.getOutput(bus.asBus, 'audio', numChannels, offset);
5         }
7         *kr { | bus, numChannels=1, offset = 0 |
8                 ^this.getOutput(bus.asBus, 'control', numChannels, offset);
9         }
11         *getOutput { | bus, argRate, numChannels, offset = 0 |
13                 var out, index;
14                 var rate = bus.rate;
15                 var startIndex = bus.index + offset;
16                 var n = bus.numChannels;
17                 if(n >= numChannels) {
18                         index = startIndex.min(n + bus.index);
19                 } {
20                         index = Array.fill(numChannels, { arg i; startIndex + (i % n) });
21                         numChannels = 1;
22                 };
24                 out = if(offset.isInteger) {
25                                         if(rate === 'audio')
26                                                 { InFeedback.ar(index, numChannels) }
27                                                 { In.kr(index, numChannels) }
28                         } {
29                                         if(rate === 'audio')
30                                                 { XInFeedback.ar(index, numChannels) }
31                                                 { XIn.kr(index, numChannels) }
32                         };
34                 ^if(argRate === rate) { out } { // if not the same rate, convert rates
35                         if(argRate === 'audio') { K2A.ar(out) } { A2K.kr(out) }
36                 };
38         }
42 XIn {
44         *ar { | which, n |
46                 ^XFade2.ar( // use equal power crossfading for audio rate
47                         In.ar(which.round(2), n),
48                         In.ar(which.trunc(2) + 1, n),
49                         (which * 2 - 1).fold2(1)
50                 )
52         }
54         *kr { | which, n |
55                 ^LinXFade2.kr( // use linear crossfading for control rate
56                         In.kr(which.round(2), n),
57                         In.kr(which.trunc(2) + 1, n),
58                         (which * 2 - 1).fold2(1)
59                 )
60         }
64 XInFeedback {
66         *ar { | which, n |
67                 ^XFade2.ar(
68                         InFeedback.ar(which.round(2), n),
69                         InFeedback.ar(which.trunc(2) + 1, n),
70                         (which * 2 - 1).fold2(1)
71                 );
72         }
77 // listens on a fixed index (or several)
78 // plays out to various other indices.
80 Monitor {
81         classvar <>warnPlayN = true;
83         var <ins, <outs, <amps = #[1.0], <vol = 1.0;
84         var <group, synthIDs, synthAmps, <>fadeTime = 0.02;
86         var <usedPlayN;  // default case
88         usedPlayN_ { |flag|
89                 var old, new, states;
91         //      [\noWarn, warnPlayN.not, \noInit, usedPlayN.isNil, \stays, usedPlayN == flag, \noOuts, outs.isNil].postln;
93                         // normal case: init or stay the same
94                 if (warnPlayN.not or: { usedPlayN.isNil or: { usedPlayN == flag } } /*or: { outs.isNil }*/) {
95                         usedPlayN = flag;
96                         ^nil
97                 };
99                 states = [\playN, \play];
100                 #old, new = if (usedPlayN, states, { states.reverse });
101                 warn("monitor switched from % to % - channels may be wrong! \n"
102                         "\t Settings were: outs: % amps: % ins: % vol: %!".format(old, new, outs, amps, ins, vol)
103                 );
104                 usedPlayN = flag;
105         }
107         play { | fromIndex, fromNumChannels=2, toIndex, toNumChannels,
108                         target, multi=false, volume, fadeTime=0.02, addAction |
110                 var server, inGroup, numChannels, bundle, divider;
112                 inGroup = target.asGroup;
113                 server = inGroup.server;
115                 bundle = List.new;
116                 this.playToBundle(
117                         bundle, fromIndex, fromNumChannels, toIndex,
118                         toNumChannels, inGroup, multi, volume, fadeTime, addAction
119                 );
120                 server.listSendBundle(server.latency, bundle);
121                 this.usedPlayN_(false);
122         }
124         stop { | argFadeTime |
125                 var oldGroup = group;
126                 fadeTime = argFadeTime ? fadeTime;
127                 synthIDs = [];
128                 synthAmps = [];
129                 if(oldGroup.isPlaying) {
130                         oldGroup.release(fadeTime);
131                         SystemClock.sched(fadeTime, { oldGroup.free })
132                 };
133                 if (group.notNil) {
134                         group.isPlaying = false;
135                         group = nil;
136                 };
137         }
139         isPlaying { ^group.isPlaying }
140         numChannels { ^outs.size }
142         // multichannel support
144         playN { | out, amp, in, vol, fadeTime, target, addAction |
145                 var bundle = List.new;
146                 var server, inGroup;
147                 inGroup = target.asGroup;
148                 server = inGroup.server;
150                 this.playNToBundle(bundle, out, amp, in, vol, fadeTime, inGroup, addAction);
151                 server.listSendBundle(server.latency, bundle);
152                 this.usedPlayN_(true);
153         }
155         // setting volume and output offset.
156         // lists are only flat lists for now.
158         vol_ { | val |
159                 if(val == vol) { ^this };
160                 vol = val;
161                 this.amps = amps;
162         }
164         // first channel interface
166         out_ { | index |
167                 var offset = index - outs[0];
168                 this.outs = outs + offset
169         }
171         out {
172                 ^outs !? { outs[0] }
173         }
175         // multi channel interface
177         outs_ { | indices |
178                 if (outs.isNil) {
179                         "Monitor - initialising  outs: %\n".postf(indices);
180                         outs = indices;
181                         ^this
182                 };
183                 if (outs.collect(_.size) != indices.collect(_.size)) {
184                         "new outs do not match old outs shape:".warn;
185                         ("old:" + outs).postln;
186                         ("new:" + indices).postln;
187                         " use playN to change topology!".postln;
188                         ^this;
189                 };
191                 outs = indices;
192                 if(this.isPlaying) {
193                         group.server.listSendBundle(group.server.latency,
194                                 [15, synthIDs, "out", outs.flat].flop
195                         )
196                 }
197         }
199         amps_ { | values |
200                 if (amps.isNil) {
201                         "Monitor - initialising  amps: %\n".postf(values);
202                         amps = values;
203                         ^this
204                 };
205                 if (values.collect(_.size) != amps.collect(_.size)) {
206                         "new amps do not match old amps shape:".warn;
207                         ("old:" + amps).postln;
208                         ("new:" + values).postln;
209                         " use playN to change topology!".postln;
210                         ^this;
211                 };
213                 synthAmps = values.flat * vol;
214                 amps = values;
215                 if (this.isPlaying) {
216                         group.server.listSendBundle(group.server.latency,
217                                 [15, synthIDs, "vol", synthAmps].flop
218                         );
219                 };
220         }
222         // bundling
224         playNToBundle { |       bundle,
225                                         argOuts = (outs ?? {(0..ins.size-1)}),
226                                         argAmps = (amps),
227                                         argIns = (ins),
228                                         argVol = (vol),
229                                         argFadeTime = (fadeTime),
230                                         inGroup,
231                                         addAction,
232                                         defName = "system_link_audio_1" |
234                 var triplets, server;
236                 outs = argOuts; ins = argIns; amps = argAmps; vol = argVol; fadeTime = argFadeTime;
239                 if (ins.size != outs.size)
240                         { Error("wrong size of outs and ins" ++ [outs, amps, ins]).throw };
242                 triplets = [ins, outs, amps].flop;
244                 if(this.isPlaying) {
245                         this.stopToBundle(bundle)
246                 } {
247                         this.newGroupToBundle(bundle, inGroup, addAction)
248                 };
249                 synthIDs = [];
250                 synthAmps = [];
252                 server = group.server;
254                 triplets.do { | trip, i |
255                         var in, out, amp;
256                         #in, out, amp = trip;
257                         out = out.asArray;
258                         amp = amp.asArray;
259                         out.do { | item, j |
260                                 var id = server.nextNodeID;
261                                 synthIDs = synthIDs.add(id);
262                                 synthAmps = synthAmps.add(amp[j]);
263                                 bundle.add([9, defName,
264                                         id, 1, group.nodeID,
265                                         "out", item,
266                                         "in", in,
267                                         "vol", amp.clipAt(j) * vol
268                                 ])
269                         }
270                 };
271                 bundle.add([15, group.nodeID, "fadeTime", fadeTime])
272         }
274         // optimizes ranges of channels
276         playToBundle { | bundle, fromIndex, fromNumChannels=2, toIndex, toNumChannels,
277                         inGroup, multi = false, volume, inFadeTime, addAction |
279                 var server, numChannels, defname, chanRange, n;
281                 toIndex = toIndex ?? { if(outs.notNil, { outs[0] }, 0) };
283                 vol = volume ? vol;
284                 fadeTime = inFadeTime ? fadeTime ? 0.02;        // remembers monitor fadeTime.
286                 toNumChannels = toNumChannels ? fromNumChannels;
287                 server = inGroup.server;
289                 if(this.isPlaying) {
290                         if(multi.not) {
291                                 this.stopToBundle(bundle);
292                                 outs = [];
293                         }
294                 } {
295                         this.newGroupToBundle(bundle, inGroup, addAction);
296                         if (multi.not) { outs = [] }
297                 };
299                 amps = [];
301                 numChannels = max(fromNumChannels, toNumChannels);
302                 chanRange = if(toNumChannels.even and: { fromNumChannels.even }, 2, 1);
303                 defname = "system_link_audio_" ++ chanRange;
304                 (numChannels div: chanRange).do { arg i;
305                         var id = server.nextNodeID;
306                         var out = toIndex + (i * chanRange % toNumChannels);
307                         var in = fromIndex + (i * chanRange % fromNumChannels);
308                         synthIDs = synthIDs.add(id);
309                         outs = outs.add(out);
310                         ins = ins.add(in);
311                         amps = amps.add(1.0);
312                         bundle.add([9, defname, id, 1, group.nodeID, "out", out, "in", in]);
313                 };
314                 bundle.add([15, group.nodeID, "fadeTime", fadeTime, "vol", vol]);
315         }
318         playNBusToBundle { | bundle, outs, amps, ins, bus, vol, fadeTime, group, addAction |
319                 var size;
320                 outs = outs ?? {this.outs.unbubble} ? 0;        // remember old ones if none given
321                 if (outs.isNumber) { outs = (0 .. bus.numChannels - 1) + outs };
322                 size = outs.size;
323                 ins = if(ins.notNil)
324                                 { ins.wrap(0, bus.numChannels - 1).asArray }
325                                 {(0..(bus.numChannels - 1)) }
326                                 + bus.index;
328                 ins = ins.wrapExtend(outs.size); // should maybe be done in playNToBundle, in flop?
329                 this.playNToBundle(bundle, outs, amps, ins, vol, fadeTime, group, addAction: addAction)
330         }
333         newGroupToBundle { | bundle, target, addAction=(\addToTail) |
334                                 target = target.asGroup;
335                                 group = Group.basicNew(target.server);
336                                 group.isPlaying = true;
337                                 bundle.add(group.newMsg(target, addAction));
338                                 NodeWatcher.register(group);
339         }
342         stopToBundle { | bundle | // maybe with fade later.
343                 bundle.add([15, group.nodeID, "gate", 0]);
344                 synthIDs = [];
345                 synthAmps = [];
346         }
348         hasSeriesOuts {
349                 if (outs.isNil) { ^true };
350                 ^(outs.size < 1) or: { ^outs.differentiate.drop(1).every(_ == 1) };
351         }