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;
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 view.onClose_({ this.stop });
30 innerView = CompositeView(view, Rect(10,25, viewWidth, height) );
31 innerView.addFlowLayout(0@0, gapWidth@gapWidth);
34 UserView(innerView, Rect(0,0,meterWidth,195)).drawFunc_({
35 Pen.color = Color.white;
36 Pen.font = Font.sansSerif(10).boldVariant;
37 Pen.stringCenteredIn("0", Rect(0, 0, meterWidth, 12));
38 Pen.stringCenteredIn("-80", Rect(0, 170, meterWidth, 12));
43 StaticText(view, Rect(10, 5, 100, 15))
44 .font_(Font.sansSerif(10).boldVariant)
45 .stringColor_(Color.white)
47 inmeters = Array.fill( numIns, { arg i;
49 comp = CompositeView(innerView, Rect(0,0,meterWidth,195)).resize_(5);
50 StaticText(comp, Rect(0, 180, meterWidth, 15))
51 .font_(Font.sansSerif(9).boldVariant)
52 .stringColor_(Color.white)
54 LevelIndicator( comp, Rect(0,0,meterWidth,180) ).warning_(0.9).critical_(1.0)
61 if((numIns > 0) && (numOuts > 0)){
63 UserView(innerView, Rect(0,0,meterWidth,180)).drawFunc_({
64 Pen.color = Color.white;
65 Pen.line(((meterWidth + gapWidth) * 0.5)@0, ((meterWidth + gapWidth) * 0.5)@180);
72 StaticText(view, Rect(10 + if(numIns > 0 , ((numIns + 2) * (meterWidth + gapWidth)), 0), 5, 100, 15))
73 .font_(Font.sansSerif(10).boldVariant)
74 .stringColor_(Color.white)
76 outmeters = Array.fill( numOuts, { arg i;
78 comp = CompositeView(innerView, Rect(0,0,meterWidth,195));
79 StaticText(comp, Rect(0, 180, meterWidth, 15))
80 .font_(Font.sansSerif(9).boldVariant)
81 .stringColor_(Color.white)
83 LevelIndicator( comp, Rect(0,0,meterWidth,180) ).warning_(0.9).critical_(1.0)
90 this.setSynthFunc(inmeters, outmeters);
96 var numRMSSamps, numRMSSampsRecip;
99 //responders and synths are started only once per server
100 var numIns = server.options.numInputBusChannels;
101 var numOuts = server.options.numOutputBusChannels;
102 numRMSSamps = server.sampleRate / updateFreq;
103 numRMSSampsRecip = 1 / numRMSSamps;
107 insynth = SynthDef(server.name ++ "InputLevels", {
108 var in = In.ar(NumOutputBuses.ir, numIns);
109 SendPeakRMS.kr(in, updateFreq, 3, "/" ++ server.name ++ "InLevels")
110 }).play(RootNode(server), nil, \addToHead);
113 outsynth = SynthDef(server.name ++ "OutputLevels", {
114 var in = In.ar(0, numOuts);
115 SendPeakRMS.kr(in, updateFreq, 3, "/" ++ server.name ++ "OutLevels")
116 }).play(RootNode(server), nil, \addToTail);
123 var numRMSSamps, numRMSSampsRecip;
125 //responders and synths are started only once per server
126 numRMSSamps = server.sampleRate / updateFreq;
127 numRMSSampsRecip = 1 / numRMSSamps;
129 inresp = OSCFunc({|msg|
132 var channelCount = msg.size - 3 / 2;
134 channelCount.do {|channel|
135 var baseIndex = 3 + (2*channel);
136 var peakLevel = msg.at(baseIndex);
137 var rmsValue = msg.at(baseIndex + 1);
138 var meter = inmeters.at(channel);
139 if (meter.isClosed.not) {
140 meter.peakLevel = peakLevel.ampdb.linlin(dBLow, 0, 0, 1);
141 meter.value = rmsValue.ampdb.linlin(dBLow, 0, 0, 1);
145 if(error.isKindOf(PrimitiveFailedError).not) { error.throw }
148 }, ("/" ++ server.name ++ "InLevels").asSymbol, server.addr).fix;
151 outresp = OSCFunc({|msg|
154 var channelCount = msg.size - 3 / 2;
156 channelCount.do {|channel|
157 var baseIndex = 3 + (2*channel);
158 var peakLevel = msg.at(baseIndex);
159 var rmsValue = msg.at(baseIndex + 1);
160 var meter = outmeters.at(channel);
161 if (meter.isClosed.not) {
162 meter.peakLevel = peakLevel.ampdb.linlin(dBLow, 0, 0, 1);
163 meter.value = rmsValue.ampdb.linlin(dBLow, 0, 0, 1);
167 if(error.isKindOf(PrimitiveFailedError).not) { error.throw }
170 }, ("/" ++ server.name ++ "OutLevels").asSymbol, server.addr).fix;
175 if(serverMeterViews.isNil){
176 serverMeterViews = IdentityDictionary.new;
178 if(serverMeterViews[server].isNil){
179 serverMeterViews.put(server,List[this]);
180 ServerTree.add(synthFunc);
181 if(server.serverRunning, synthFunc); // otherwise starts when booted
182 server.doWhenBooted({this.startResponders});
184 if(serverMeterViews[server].size == 0){
185 ServerTree.add(synthFunc);
186 if(server.serverRunning, synthFunc); // otherwise starts when booted
190 serverMeterViews[server].add(this);
191 server.doWhenBooted({this.startResponders});
197 serverMeterViews[server].remove(this);
199 if(serverMeterViews[server].size == 0){
200 (numIns > 0).if({ insynth.free; });
201 (numOuts > 0).if({outsynth.free; });
202 ServerTree.remove(synthFunc);
205 (numIns > 0).if({ inresp.free; });
206 (numOuts > 0).if({ outresp.free; });
216 var <window, <meterView;
218 *new{ |server, numIns, numOuts|
220 var window, meterView;
222 numIns = numIns ?? { server.options.numInputBusChannels };
223 numOuts = numOuts ?? { server.options.numOutputBusChannels };
225 window = Window.new(server.name ++ " levels (dBFS)",
226 Rect(5, 305, ServerMeterView.getWidth(numIns,numOuts), ServerMeterView.height),
228 window.view.background = Color.grey(0.4);
230 meterView = ServerMeterView(server, window, 0@0, numIns, numOuts);
231 meterView.view.keyDownAction_({ arg view, char, modifiers;
232 if(modifiers & 16515072 == 0) {
234 {char === 27.asAscii } { window.close };
240 ^super.newCopyArgs(window,meterView)