Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / SCClassLibrary / Common / Control / Volume.sc
blob31299f1f2251b3323669550bc070636d3487d25b
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 {
25         var startBus, numChans, <min, <max, server, persist, <ampSynth, <>window, <volume, spec;
26         var <lag, sdname, gui = false, <muteamp, cpFun, <isMuted=false;
27         var <synthNumChans;     // the actual number of channels, which might be set automatically
28         var sdInitialized;
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                 gui = false;
40                 sdInitialized = Condition();
41                 if (server.serverRunning) {
42                         this.sendSynthDef
43                 } {
44                         ServerBoot.add ({
45                                 ampSynth = nil;
46                                 this.sendSynthDef;
47                         }, server)
48                 };
49         }
51         sendSynthDef {
52                 if(numChans.isNil) {
53                         synthNumChans = server.options.numOutputBusChannels;
54                 } {
55                         synthNumChans = numChans;
56                 };
58                 sdname = (\volumeAmpControl ++ synthNumChans).asSymbol;
60                 sdInitialized = Condition();
62                 SynthDef(sdname,
63                         { arg volumeAmp = 1, volumeLag = 0.1, gate=1, bus;
64                                 XOut.ar(bus,
65                                         Linen.kr(gate, releaseTime: 0.05, doneAction:2),
66                                         In.ar(bus, synthNumChans) * Lag.kr(volumeAmp, volumeLag) );
67                 }).send(server);
69                 fork {
70                         server.sync(sdInitialized);
71                         if (cpFun.isNil) {
72                                 ServerTree.add(cpFun = {
73                                         ampSynth = nil;
74                                         if (persist) {
75                                                 this.volume_(this.volume)
76                                         } {
77                                                 this.free;
78                                         };
79                                 });
80                         };
81                         if (server.serverRunning.not) {
82                                 this.volume_(this.volume)
83                         }
84                 }
85         }
87         play {arg mute = false;
88                 if (ampSynth.isNil) {
89                         if(server.serverRunning) {
90                                 ampSynth = Synth.after(server.defaultGroup, sdname,
91                                         [\volumeAmp, volume.dbamp, \volumeLag, lag, \bus, startBus]);
92                                 mute.if({this.mute});
93                         } {
94                                 "Volume only works on a running Server. Please boot".warn;
95                         }
96                 }
97         }
99         free {
100                 ampSynth.release;
101                 ampSynth = nil;
102         }
104         numChans { ^numChans ? synthNumChans ? server.options.numOutputBusChannels }
105         numChans_ { |num|
106                 if(ampSynth.notNil and: { num != synthNumChans }) {
107                         "Change in number of channels will not take effect until volume is reset to 0dB.".warn;
108                 };
109                 numChans = num;
110         }
112         mute {
113                 isMuted = true;
114                 muteamp = volume;
115                 this.changed(\mute, true);
116                 if (ampSynth.notNil) {
117                         ampSynth.set(\volumeAmp, 0);
118                 } {
119                         this.playVolume(true)
120                 }
121         }
123         unmute {
124                 isMuted = false;
125                 ampSynth.set(\volumeAmp, muteamp.dbamp);
126                 this.changed(\mute, false);
127                 if (this.muteamp == 0.0) {
128                         this.free;
129                 }
130         }
132         // sets volume back to 1 - removes the synth
133         reset {
134                 this.free;
135         }
137         // cleaner with MVC - in db
138         volume_ { arg aVolume;
139                 volume = aVolume;
140                 if (volume == 0.0) {
141                         if (ampSynth.notNil and: {this.isMuted.not}) {
142                                 this.free;
143                         }
144                 } {
145                         if (server.serverRunning) {
146                                 this.playVolume(isMuted);
147                         }
148                 };
149                 volume = volume.clip(min, max);
150                 if(isMuted) { muteamp = volume };
151                 if(ampSynth.notNil && isMuted.not) { ampSynth.set(\volumeAmp, volume.dbamp) };
152                 this.changed(\amp, volume);
153         }
155         playVolume { arg muted = false;
156                 if (ampSynth.isNil and: {
157                         (volume != 0.0) or: {muted} }) {
158                         this.play(muted);
159                 }
160         }
162         lag_ { arg aLagTime;
163                 lag = aLagTime;
164                 ampSynth.set(\volumeLag, lag);
165         }
167         setVolumeRange { arg argMin, argMax;
168                 argMin !? { min = argMin };
169                 argMax !? { max = argMax };
170                 this.changed(\ampRange, min, max);
171         }
174         gui { arg window, bounds;
175                 //              this.debug(\gui);
176                 ^VolumeGui(this, window, bounds)
177         }
179         close {
180                 window.close;
181         }
184 VolumeGui{
185         var <>model;
186         var window, spec, slider, box, simpleController;
188         *new{|model, win, bounds|
189                 ^super.new.model_(model).init(win, bounds)
190         }
192         init{|win, bounds|
193                 spec = [model.min, model.max, \db].asSpec;
194                 bounds = bounds ?? {Rect(100, 100, 80, 330)};
195                 window = win ?? {GUI.window.new("Volume", bounds).front};
196                 box = GUI.numberBox.new(window, Rect(10, 10, 60, 30))
197                 .value_(model.volume) ;
198                 slider = GUI.slider.new(window, Rect(10, 40, 60, 280))
199                 .value_(spec.unmap(model.volume)) ;
200                 slider.action_({ arg item ;
201                         model.volume_(spec.map(item.value));
202                 }) ;
203                 box.action_({ arg item ;
204                         model.volume_(item.value) ;
205                 }) ;
206                 window.onClose_({
207                         simpleController.remove;
208                 });
209                 simpleController = SimpleController(model)
210                 .put(\amp, {|changer, what, volume|
211                         this.debug(volume);
212                         box.value_(volume.round(0.01)) ;
213                         slider.value_(spec.unmap(volume)) ;
214                 })
215                 .put(\ampRange, {|changer, what, min, max|
216                         spec = [min, max, \db].asSpec.debug;
217                         slider.value_(spec.unmap(model.volume)) ;
218                 })
219         }