class library: DUGen - the server now handles audio-rate inputs correctly
[supercollider.git] / SCClassLibrary / Common / GUI / ServerMeter.sc
blobe8760ccd7dfea6b8aa2a1e93b2f9197c7a390ec1
1 ServerMeterView{
3         classvar serverMeterViews,      updateFreq = 10, dBLow = -80, meterWidth = 15, gapWidth = 4, <height = 230;
5         var <view;
6         var inresp, outresp, insynth, outsynth, synthFunc, responderFunc, server, numIns, numOuts, inmeters, outmeters;
8         *new{ |aserver,parent,leftUp,numIns,numOuts|
9                 ^super.new.init(aserver,parent,leftUp,numIns,numOuts)
10         }
12         *getWidth{ arg numIns,numOuts, server;
13                 ^20+((numIns + numOuts + 2) * (meterWidth + gapWidth))
14         }
16         init { arg aserver, parent, leftUp, anumIns,anumOuts;
17                 var innerView, viewWidth, levelIndic, palette;
19                 numIns = anumIns ?? { server.options.numInputBusChannels };
20                 numOuts = anumOuts ?? { server.options.numOutputBusChannels };
22                 viewWidth= this.class.getWidth(anumIns,anumOuts);
24                 leftUp = leftUp ? (0@0);
26                 server = aserver;
28                 view = CompositeView(parent, Rect(leftUp.x,leftUp.y, viewWidth, height) );
29                 try { view.palette = \QPalette.asClass.new.windowText_(Color.grey(0.6)) };
30                 view.onClose_({ this.stop });
31                 innerView = CompositeView(view, Rect(10,25, viewWidth, height) );
32                 innerView.addFlowLayout(0@0, gapWidth@gapWidth);
34                 // dB scale
35                 UserView(innerView, Rect(0,0,meterWidth,195)).drawFunc_({
36                         Pen.color = Color.white;
37                         Pen.font = Font.sansSerif(10).boldVariant;
38                         Pen.stringCenteredIn("0", Rect(0, 0, meterWidth, 12));
39                         Pen.stringCenteredIn("-80", Rect(0, 170, meterWidth, 12));
40                 });
42                 (numIns > 0).if({
43                         // ins
44                         StaticText(view, Rect(10, 5, 100, 15))
45                                 .font_(Font.sansSerif(10).boldVariant)
46                                 .stringColor_(Color.white)
47                                 .string_("Inputs");
48                         inmeters = Array.fill( numIns, { arg i;
49                                 var comp;
50                                 comp = CompositeView(innerView, Rect(0,0,meterWidth,195)).resize_(5);
51                                 StaticText(comp, Rect(0, 180, meterWidth, 15))
52                                         .font_(Font.sansSerif(9).boldVariant)
53                                         .stringColor_(Color.white)
54                                         .string_(i.asString);
55                                 levelIndic = LevelIndicator( comp, Rect(0,0,meterWidth,180) ).warning_(0.9).critical_(1.0)
56                                         .drawsPeak_(true)
57                                         .numTicks_(9)
58                                         .numMajorTicks_(3);
59                                 try{ levelIndic.background = Color.grey(0.3) };
60                         });
61                 });
63                 if((numIns > 0) && (numOuts > 0)){
64                         // divider
65                         UserView(innerView, Rect(0,0,meterWidth,180)).drawFunc_({
66                                 Pen.color = Color.white;
67                                 Pen.line(((meterWidth + gapWidth) * 0.5)@0, ((meterWidth + gapWidth) * 0.5)@180);
68                                 Pen.stroke;
69                         });
70                 };
72                 // outs
73                 (numOuts > 0).if({
74                         StaticText(view, Rect(10 + if(numIns > 0 , ((numIns + 2) * (meterWidth + gapWidth)), 0), 5, 100, 15))
75                                 .font_(Font.sansSerif(10).boldVariant)
76                                 .stringColor_(Color.white)
77                                 .string_("Outputs");
78                         outmeters = Array.fill( numOuts, { arg i;
79                                 var comp;
80                                 comp = CompositeView(innerView, Rect(0,0,meterWidth,195));
81                                 StaticText(comp, Rect(0, 180, meterWidth, 15))
82                                         .font_(Font.sansSerif(9).boldVariant)
83                                         .stringColor_(Color.white)
84                                         .string_(i.asString);
85                                 levelIndic = LevelIndicator( comp, Rect(0,0,meterWidth,180) ).warning_(0.9).critical_(1.0)
86                                         .drawsPeak_(true)
87                                         .numTicks_(9)
88                                         .numMajorTicks_(3);
89                                 try{ levelIndic.background = Color.grey(0.3) }
90                         });
91                 });
93                 this.setSynthFunc(inmeters, outmeters);
94                 this.start;
96         }
98         setSynthFunc{
99                 var numRMSSamps, numRMSSampsRecip;
101                 synthFunc = {
102                         //responders and synths are started only once per server
103                         var numIns = server.options.numInputBusChannels;
104                         var numOuts = server.options.numOutputBusChannels;
105                         numRMSSamps = server.sampleRate / updateFreq;
106                         numRMSSampsRecip = 1 / numRMSSamps;
108                         server.bind({
109                                 (numIns > 0).if({
110                                         insynth = SynthDef(server.name ++ "InputLevels", {
111                                                 var in = In.ar(NumOutputBuses.ir, numIns);
112                                                 SendPeakRMS.kr(in, updateFreq, 3, "/" ++ server.name ++ "InLevels")
113                                         }).play(RootNode(server), nil, \addToHead);
114                                 });
115                                 (numOuts > 0).if({
116                                         outsynth = SynthDef(server.name ++ "OutputLevels", {
117                                                 var in = In.ar(0, numOuts);
118                                                 SendPeakRMS.kr(in, updateFreq, 3, "/" ++ server.name ++ "OutLevels")
119                                         }).play(RootNode(server), nil, \addToTail);
120                                 });
121                         });
122                 };
123         }
125         startResponders{
126                 var numRMSSamps, numRMSSampsRecip;
128                 //responders and synths are started only once per server
129                 numRMSSamps = server.sampleRate / updateFreq;
130                 numRMSSampsRecip = 1 / numRMSSamps;
131                 (numIns > 0).if({
132                         inresp = OSCFunc({|msg|
133                                 {
134                                         try {
135                                                 var channelCount = msg.size - 3 / 2;
137                                                 channelCount.do {|channel|
138                                                         var baseIndex = 3 + (2*channel);
139                                                         var peakLevel = msg.at(baseIndex);
140                                                         var rmsValue  = msg.at(baseIndex + 1);
141                                                         var meter = inmeters.at(channel);
142                                                         if (meter.isClosed.not) {
143                                                                 meter.peakLevel = peakLevel.ampdb.linlin(dBLow, 0, 0, 1);
144                                                                 meter.value = rmsValue.ampdb.linlin(dBLow, 0, 0, 1);
145                                                         }
146                                                 }
147                                         } { |error|
148                                                 if(error.isKindOf(PrimitiveFailedError).not) { error.throw }
149                                         };
150                                 }.defer;
151                         }, ("/" ++ server.name ++ "InLevels").asSymbol, server.addr).fix;
152                 });
153                 (numOuts > 0).if({
154                         outresp = OSCFunc({|msg|
155                                 {
156                                         try {
157                                                 var channelCount = msg.size - 3 / 2;
159                                                 channelCount.do {|channel|
160                                                         var baseIndex = 3 + (2*channel);
161                                                         var peakLevel = msg.at(baseIndex);
162                                                         var rmsValue  = msg.at(baseIndex + 1);
163                                                         var meter = outmeters.at(channel);
164                                                         if (meter.isClosed.not) {
165                                                                 meter.peakLevel = peakLevel.ampdb.linlin(dBLow, 0, 0, 1);
166                                                                 meter.value = rmsValue.ampdb.linlin(dBLow, 0, 0, 1);
167                                                         }
168                                                 }
169                                         } { |error|
170                                                 if(error.isKindOf(PrimitiveFailedError).not) { error.throw }
171                                         };
172                                 }.defer;
173                         }, ("/" ++ server.name ++ "OutLevels").asSymbol, server.addr).fix;
174                 });
175         }
177         start{
178                 if(serverMeterViews.isNil){
179                         serverMeterViews = IdentityDictionary.new;
180                 };
181                 if(serverMeterViews[server].isNil){
182                         serverMeterViews.put(server,List[this]);
183                         ServerTree.add(synthFunc);
184                         if(server.serverRunning, synthFunc); // otherwise starts when booted
185                         server.doWhenBooted({this.startResponders});
186                 }{
187                         if(serverMeterViews[server].size == 0){
188                                 ServerTree.add(synthFunc);
189                                 if(server.serverRunning, synthFunc); // otherwise starts when booted
192                         };
193                         serverMeterViews[server].add(this);
194                         server.doWhenBooted({this.startResponders});
195                 }
197         }
199         stop{
200                 serverMeterViews[server].remove(this);
202                 if(serverMeterViews[server].size == 0){
203                         (numIns > 0).if({ insynth.free; });
204                         (numOuts > 0).if({outsynth.free; });
205                         ServerTree.remove(synthFunc);
206                 };
208                 (numIns > 0).if({ inresp.free; });
209                 (numOuts > 0).if({ outresp.free; });
210         }
212         remove{
213                 view.remove
214         }
217 ServerMeter{
219         var <window, <meterView;
221         *new{ |server, numIns, numOuts|
223                 var window, meterView;
225                 numIns = numIns ?? { server.options.numInputBusChannels };
226                 numOuts = numOuts ?? { server.options.numOutputBusChannels };
228                 window = Window.new(server.name ++ " levels (dBFS)",
229                                                         Rect(5, 305, ServerMeterView.getWidth(numIns,numOuts), ServerMeterView.height),
230                                                         false);
231                 window.view.background = Color.grey(0.4);
233                 meterView = ServerMeterView(server, window, 0@0, numIns, numOuts);
234                 meterView.view.keyDownAction_({ arg view, char, modifiers;
235                         if(modifiers & 16515072 == 0) {
236                                 case
237                                 {char === 27.asAscii } { window.close };
238                         };
239                 });
241                 window.front;
243                 ^super.newCopyArgs(window,meterView)
245         }