common: win32utils - compile fix
[supercollider.git] / SCClassLibrary / Common / Control / Volume.sc
bloba53b71f7171d8365ba2ad1a1b66db2da9ff59104
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                 ServerBoot.add {|aServer|
43                         if (aServer == server) {
44                                 isPlaying = false;
45                                 ampSynth = nil;
46                         }
47                 }
48         }
50         sendDef {
51                 SynthDef(sdname = (\volumeAmpControl ++ synthNumChans).asSymbol,
52                         { arg volumeAmp = 1, volumeLag = 0.1, volumeGate=1;
53                         XOut.ar(startBus,
54                                 Linen.kr(volumeGate, releaseTime: 0.05, doneAction:2),
55                                 In.ar(startBus, synthNumChans) * Lag.kr(volumeAmp, volumeLag) );
56                 }).send(server);
57         }
59         play {arg mute = false;
60                 var cond, nodeID;
61                 cond = Condition.new;
62                 (isPlaying || isPrepping).not.if({
63                         server.serverRunning.if({
64                                 Routine.run({
65                                         if(numChans.isNil) {
66                                                 synthNumChans = server.options.numOutputBusChannels;
67                                         } {
68                                                 synthNumChans = numChans;
69                                         };
70                                         this.sendDef;
71                                         isPrepping = true;
72                                         server.sync(cond);
73                                         isPlaying = true;
74                                         isPrepping = false;
75                                         cpFun.isNil.if({
76                                                 CmdPeriod.add(cpFun = {
77                                                         var     nodeIDToFree;
78                                                         ampSynth.notNil.if({
79                                                                 nodeIDToFree = ampSynth.nodeID;
80                                                                 {       server.nodeAllocator
81                                                                         .freePerm(nodeIDToFree);
82                                                                 }.defer(1.0);
83                                                         });
84                                                         persist.if({
85                                                                 isPlaying = false;
86                                                                 this.play(isMuted);
87                                                         }, {
88                                                                 this.free;
89                                                         });
90                                                 });
91                                         });
92                                                 // we have permanent node IDs so we should use them
93                                         nodeID = server.nodeAllocator.allocPerm(1);
94                                         ampSynth = Synth.basicNew(sdname, server, nodeID);
95                                         server.sendBundle(nil, ampSynth.newMsg(1,
96                                                 [\volumeAmp, volume.dbamp, \volumeLag, lag],
97                                                 addAction: \addAfter));
98                                         mute.if({this.mute});
99                                 })
100                         }, {
101                                 "Volume only works on a running Server. Please boot".warn;
102                         })
103                 })
104         }
106         free {
107                 var     nodeIDToFree = ampSynth.nodeID;
108                 ampSynth.set(\volumeGate, 0.0);
109                 { server.nodeAllocator.freePerm(nodeIDToFree) }.defer(1.0);
110                 isPlaying = false;
111                 CmdPeriod.remove(cpFun);
112                 cpFun = nil;
113         }
115         numChans { ^numChans ? synthNumChans ? server.options.numOutputBusChannels }
116         numChans_ { |num|
117                 if(isPlaying and: { num != synthNumChans }) {
118                         "Change in number of channels will not take effect until volume is reset to 0dB.".warn;
119                 };
120                 numChans = num;
121         }
123         mute {
124                 this.isPlaying.if({
125                         this.prmute;
126                 }, {
127                         this.playVolume(true)
128                 });
129         }
131         unmute {
132                 this.prunmute;
133                 (this.muteamp == 0.0).if({
134                         this.free;
135                 });
136         }
139         prmute {
140                 isMuted = true;
141                 muteamp = volume;
142                 ampSynth.set(\volumeAmp, 0);
143                 this.changed(\mute, true);
144         }
146         prunmute {
147                 isMuted = false;
148                 ampSynth.set(\volumeAmp, muteamp.dbamp);
149                 this.changed(\mute, false);
150         }
152         // sets volume back to 1 - removes the synth
153         reset {
154                 this.free;
155         }
157         // cleaner with MVC - in db
158         volume_ { arg aVolume;
159                 volume = aVolume;
160                 (volume == 0.0).if({
161                         (this.isPlaying and: {this.isMuted.not}).if({
162                                 this.free;
163                         })
164                 }, {
165                         server.serverRunning.if({
166                                 this.playVolume(isMuted);
167                         })
168                 });
169                 volume = volume.clip(min, max);
170                 if(isMuted) { muteamp = volume };
171                 if(isPlaying && isMuted.not) { ampSynth.set(\volumeAmp, volume.dbamp) };
172                 this.changed(\amp, volume);
173         }
175         playVolume { arg muted = false;
176                 (this.isPlaying.not and: {
177                         (volume != 0.0) or: {muted}
178                 }).if({
179                         this.play(muted);
180                 })
181         }
183         lag_ { arg aLagTime;
184                 lag = aLagTime;
185                 ampSynth.set(\volumeLag, lag);
186         }
188         setVolumeRange { arg argMin, argMax;
189                 argMin !? { min = argMin };
190                 argMax !? { max = argMax };
191                 this.changed(\ampRange, min, max);
192         }
195         gui { arg window, bounds;
196 //              this.debug(\gui);
197                 ^VolumeGui(this, window, bounds)
198         }
200         close {
201                 window.close;
202         }
205 VolumeGui{
206         var <>model;
207         var window, spec, slider, box, simpleController;
209         *new{|model, win, bounds|
210                 ^super.new.model_(model).init(win, bounds)
211         }
213         init{|win, bounds|
214                 spec = [model.min, model.max, \db].asSpec;
215                 bounds = bounds ?? {Rect(100, 100, 80, 330)};
216                 window = win ?? {GUI.window.new("Volume", bounds).front};
217                 box = GUI.numberBox.new(window, Rect(10, 10, 60, 30))
218                         .value_(model.volume) ;
219                 slider = GUI.slider.new(window, Rect(10, 40, 60, 280))
220                         .value_(spec.unmap(model.volume)) ;
221                 slider.action_({ arg item ;
222                         model.volume_(spec.map(item.value));
223                 }) ;
224                 box.action_({ arg item ;
225                         model.volume_(item.value) ;
226                 }) ;
227                 window.onClose_({
228                         simpleController.remove;
229                 });
230                 simpleController = SimpleController(model)
231                                 .put(\amp, {|changer, what, volume|
232                                         this.debug(volume);
233                                         box.value_(volume.round(0.01)) ;
234                                         slider.value_(spec.unmap(volume)) ;
235                                 })
236                                 .put(\ampRange, {|changer, what, min, max|
237                                         spec = [min, max, \db].asSpec.debug;
238                                         slider.value_(spec.unmap(model.volume)) ;
239                                 })
240         }