cmake build system: visiblity support for clang
[supercollider.git] / SCClassLibrary / Common / Control / Volume.sc
blob98a19e7400104aab7c97c03806d4db28f1fa5bd3
1 /*
3 s.boot;
5 a = Volume(s, 0, s.options.numOutputBusChannels);
6 a.play;
8 z = {SinOsc.ar(440, 0, 0.1)}.play
10 s.volume.volume_(-9);
11 s.volume.volume;
12 a.mute;
13 a.unmute;
14 a.volume_(0);
16 a.gui;
18 a.free;
20 z.free;
24 Volume {
26         var startBus, numChans, <min, <max, server, persist, <ampSynth, <>window, <volume, spec;
27         var <lag, sdname, gui, <isPlaying, <muteamp, cpFun, <isMuted=false, <isPrepping;
28         var <synthNumChans;     // the actual number of channels, which might be set automatically
30         *new { arg server, startBus = 0, numChans, min = -90, max = 6, persist = false;
31                 ^super.newCopyArgs(startBus, numChans, min, max, server, persist).initVolume;
32         }
34         initVolume {
35                 var cond;
36                 server = server ?? {Server.default};
37                 volume = 0;
38                 lag = 0.1;
39                 isPlaying = false;
40                 isPrepping = false;
41                 gui = false;
42         }
44         sendDef {
45                 SynthDef(sdname = (\volumeAmpControl ++ synthNumChans).asSymbol,
46                         { arg volumeAmp = 1, volumeLag = 0.1, volumeGate=1;
47                         XOut.ar(startBus,
48                                 Linen.kr(volumeGate, releaseTime: 0.05, doneAction:2),
49                                 In.ar(startBus, synthNumChans) * Lag.kr(volumeAmp, volumeLag) );
50                 }).send(server);
51         }
53         play {arg mute = false;
54                 var cond, nodeID;
55                 cond = Condition.new;
56                 (isPlaying || isPrepping).not.if({
57                         server.serverRunning.if({
58                                 Routine.run({
59                                         if(numChans.isNil) {
60                                                 synthNumChans = server.options.numOutputBusChannels;
61                                         } {
62                                                 synthNumChans = numChans;
63                                         };
64                                         this.sendDef;
65                                         isPrepping = true;
66                                         server.sync(cond);
67                                         isPlaying = true;
68                                         isPrepping = false;
69                                         cpFun.isNil.if({
70                                                 CmdPeriod.add(cpFun = {
71                                                         var     nodeIDToFree;
72                                                         ampSynth.notNil.if({
73                                                                 nodeIDToFree = ampSynth.nodeID;
74                                                                 {       server.nodeAllocator
75                                                                         .freePerm(nodeIDToFree);
76                                                                 }.defer(1.0);
77                                                         });
78                                                         persist.if({
79                                                                 isPlaying = false;
80                                                                 this.play(isMuted);
81                                                         }, {
82                                                                 this.free;
83                                                         });
84                                                 });
85                                         });
86                                                 // we have permanent node IDs so we should use them
87                                         nodeID = server.nodeAllocator.allocPerm(1);
88                                         ampSynth = Synth.basicNew(sdname, server, nodeID);
89                                         server.sendBundle(nil, ampSynth.newMsg(1,
90                                                 [\volumeAmp, volume.dbamp, \volumeLag, lag],
91                                                 addAction: \addAfter));
92                                         mute.if({this.mute});
93                                 })
94                         }, {
95                                 "Volume only works on a running Server. Please boot".warn;
96                         })
97                 })
98         }
100         free {
101                 var     nodeIDToFree = ampSynth.nodeID;
102                 ampSynth.set(\volumeGate, 0.0);
103                 { server.nodeAllocator.freePerm(nodeIDToFree) }.defer(1.0);
104                 isPlaying = false;
105                 CmdPeriod.remove(cpFun);
106                 cpFun = nil;
107         }
109         numChans { ^numChans ? synthNumChans ? server.options.numOutputBusChannels }
110         numChans_ { |num|
111                 if(isPlaying and: { num != synthNumChans }) {
112                         "Change in number of channels will not take effect until volume is reset to 0dB.".warn;
113                 };
114                 numChans = num;
115         }
117         mute {
118                 this.isPlaying.if({
119                         this.prmute;
120                 }, {
121                         this.playVolume(true)
122                 });
123         }
125         unmute {
126                 this.prunmute;
127                 (this.muteamp == 0.0).if({
128                         this.free;
129                 });
130         }
133         prmute {
134                 isMuted = true;
135                 muteamp = volume;
136                 ampSynth.set(\volumeAmp, 0);
137                 this.changed(\mute, true);
138         }
140         prunmute {
141                 isMuted = false;
142                 ampSynth.set(\volumeAmp, muteamp.dbamp);
143                 this.changed(\mute, false);
144         }
146         // sets volume back to 1 - removes the synth
147         reset {
148                 this.free;
149         }
151         // cleaner with MVC - in db
152         volume_ { arg aVolume;
153                 volume = aVolume;
154                 (volume == 0.0).if({
155                         (this.isPlaying and: {this.isMuted.not}).if({
156                                 this.free;
157                         })
158                 }, {
159                         server.serverRunning.if({
160                                 this.playVolume(isMuted);
161                         })
162                 });
163                 volume = volume.clip(min, max);
164                 if(isMuted) { muteamp = volume };
165                 if(isPlaying && isMuted.not) { ampSynth.set(\volumeAmp, volume.dbamp) };
166                 this.changed(\amp, volume);
167         }
169         playVolume { arg muted = false;
170                 (this.isPlaying.not and: {
171                         (volume != 0.0) or: {muted}
172                 }).if({
173                         this.play(muted);
174                 })
175         }
177         lag_ { arg aLagTime;
178                 lag = aLagTime;
179                 ampSynth.set(\volumeLag, lag);
180         }
182         setVolumeRange { arg argMin, argMax;
183                 argMin !? { min = argMin };
184                 argMax !? { max = argMax };
185                 this.changed(\ampRange, min, max);
186         }
189         gui { arg window, bounds;
190 //              this.debug(\gui);
191                 ^VolumeGui(this, window, bounds)
192         }
194         close {
195                 window.close;
196         }
199 VolumeGui{
200         var <>model;
201         var window, spec, slider, box, simpleController;
203         *new{|model, win, bounds|
204                 ^super.new.model_(model).init(win, bounds)
205         }
207         init{|win, bounds|
208                 spec = [model.min, model.max, \db].asSpec;
209                 bounds = bounds ?? {Rect(100, 100, 80, 330)};
210                 window = win ?? {GUI.window.new("Volume", bounds).front};
211                 box = GUI.numberBox.new(window, Rect(10, 10, 60, 30))
212                         .value_(model.volume) ;
213                 slider = GUI.slider.new(window, Rect(10, 40, 60, 280))
214                         .value_(spec.unmap(model.volume)) ;
215                 slider.action_({ arg item ;
216                         model.volume_(spec.map(item.value));
217                 }) ;
218                 box.action_({ arg item ;
219                         model.volume_(item.value) ;
220                 }) ;
221                 window.onClose_({
222                         simpleController.remove;
223                 });
224                 simpleController = SimpleController(model)
225                                 .put(\amp, {|changer, what, volume|
226                                         this.debug(volume);
227                                         box.value_(volume.round(0.01)) ;
228                                         slider.value_(spec.unmap(volume)) ;
229                                 })
230                                 .put(\ampRange, {|changer, what, min, max|
231                                         spec = [min, max, \db].asSpec.debug;
232                                         slider.value_(spec.unmap(model.volume)) ;
233                                 })
234         }