3 classvar serverMeterViews, updateFreq = 10, dBLow = -80, meterWidth = 15, gapWidth = 4, <height = 230;
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)
12 *getWidth{ arg numIns,numOuts, server;
13 ^20+((numIns + numOuts + 2) * (meterWidth + gapWidth))
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);
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);
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));
44 StaticText(view, Rect(10, 5, 100, 15))
45 .font_(Font.sansSerif(10).boldVariant)
46 .stringColor_(Color.white)
48 inmeters = Array.fill( numIns, { arg i;
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)
55 levelIndic = LevelIndicator( comp, Rect(0,0,meterWidth,180) ).warning_(0.9).critical_(1.0)
59 try{ levelIndic.background = Color.grey(0.3) };
63 if((numIns > 0) && (numOuts > 0)){
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);
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)
78 outmeters = Array.fill( numOuts, { arg i;
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)
85 levelIndic = LevelIndicator( comp, Rect(0,0,meterWidth,180) ).warning_(0.9).critical_(1.0)
89 try{ levelIndic.background = Color.grey(0.3) }
93 this.setSynthFunc(inmeters, outmeters);
99 var numRMSSamps, numRMSSampsRecip;
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;
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);
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);
126 var numRMSSamps, numRMSSampsRecip;
128 //responders and synths are started only once per server
129 numRMSSamps = server.sampleRate / updateFreq;
130 numRMSSampsRecip = 1 / numRMSSamps;
132 inresp = OSCFunc({|msg|
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);
148 if(error.isKindOf(PrimitiveFailedError).not) { error.throw }
151 }, ("/" ++ server.name ++ "InLevels").asSymbol, server.addr).fix;
154 outresp = OSCFunc({|msg|
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);
170 if(error.isKindOf(PrimitiveFailedError).not) { error.throw }
173 }, ("/" ++ server.name ++ "OutLevels").asSymbol, server.addr).fix;
178 if(serverMeterViews.isNil){
179 serverMeterViews = IdentityDictionary.new;
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});
187 if(serverMeterViews[server].size == 0){
188 ServerTree.add(synthFunc);
189 if(server.serverRunning, synthFunc); // otherwise starts when booted
193 serverMeterViews[server].add(this);
194 server.doWhenBooted({this.startResponders});
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);
208 (numIns > 0).if({ inresp.free; });
209 (numOuts > 0).if({ outresp.free; });
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),
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) {
237 {char === 27.asAscii } { window.close };
243 ^super.newCopyArgs(window,meterView)