3 classvar serverMeterViews, updateFreq = 10, dBLow = -80, meterWidth = 15, gapWidth = 4, <height = 230;
4 classvar serverCleanupFuncs;
7 var inresp, outresp, synthFunc, responderFunc, server, numIns, numOuts, inmeters, outmeters, startResponderFunc;
9 *new{ |aserver,parent,leftUp,numIns,numOuts|
10 ^super.new.init(aserver,parent,leftUp,numIns,numOuts)
13 *getWidth{ arg numIns,numOuts, server;
14 ^20+((numIns + numOuts + 2) * (meterWidth + gapWidth))
17 init { arg aserver, parent, leftUp, anumIns,anumOuts;
18 var innerView, viewWidth, levelIndic, palette;
22 numIns = anumIns ?? { server.options.numInputBusChannels };
23 numOuts = anumOuts ?? { server.options.numOutputBusChannels };
25 viewWidth= this.class.getWidth(anumIns,anumOuts);
27 leftUp = leftUp ? (0@0);
29 view = CompositeView(parent, Rect(leftUp.x,leftUp.y, viewWidth, height) );
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_({
37 Pen.color = \QPalette.asClass.new.windowText;
39 Pen.color = Color.white;
41 Pen.font = Font.sansSerif(10).boldVariant;
42 Pen.stringCenteredIn("0", Rect(0, 0, meterWidth, 12));
43 Pen.stringCenteredIn("-80", Rect(0, 170, meterWidth, 12));
48 StaticText(view, Rect(10, 5, 100, 15))
49 .font_(Font.sansSerif(10).boldVariant)
51 inmeters = Array.fill( numIns, { arg i;
53 comp = CompositeView(innerView, Rect(0,0,meterWidth,195)).resize_(5);
54 StaticText(comp, Rect(0, 180, meterWidth, 15))
55 .font_(Font.sansSerif(9).boldVariant)
57 levelIndic = LevelIndicator( comp, Rect(0,0,meterWidth,180) ).warning_(0.9).critical_(1.0)
64 if((numIns > 0) && (numOuts > 0)){
66 UserView(innerView, Rect(0,0,meterWidth,180)).drawFunc_({
68 Pen.color = \QPalette.asClass.new.windowText;
70 Pen.color = Color.white;
72 Pen.line(((meterWidth + gapWidth) * 0.5)@0, ((meterWidth + gapWidth) * 0.5)@180);
79 StaticText(view, Rect(10 + if(numIns > 0 , ((numIns + 2) * (meterWidth + gapWidth)), 0), 5, 100, 15))
80 .font_(Font.sansSerif(10).boldVariant)
82 outmeters = Array.fill( numOuts, { arg i;
84 comp = CompositeView(innerView, Rect(0,0,meterWidth,195));
85 StaticText(comp, Rect(0, 180, meterWidth, 15))
86 .font_(Font.sansSerif(9).boldVariant)
88 levelIndic = LevelIndicator( comp, Rect(0,0,meterWidth,180) ).warning_(0.9).critical_(1.0)
95 this.setSynthFunc(inmeters, outmeters);
96 startResponderFunc = {this.startResponders};
101 var numRMSSamps, numRMSSampsRecip;
104 //responders and synths are started only once per server
105 var numIns = server.options.numInputBusChannels;
106 var numOuts = server.options.numOutputBusChannels;
107 numRMSSamps = server.sampleRate / updateFreq;
108 numRMSSampsRecip = 1 / numRMSSamps;
111 var insynth, outsynth;
113 insynth = SynthDef(server.name ++ "InputLevels", {
114 var in = In.ar(NumOutputBuses.ir, numIns);
115 SendPeakRMS.kr(in, updateFreq, 3, "/" ++ server.name ++ "InLevels")
116 }).play(RootNode(server), nil, \addToHead);
119 outsynth = SynthDef(server.name ++ "OutputLevels", {
120 var in = In.ar(0, numOuts);
121 SendPeakRMS.kr(in, updateFreq, 3, "/" ++ server.name ++ "OutLevels")
122 }).play(RootNode(server), nil, \addToTail);
125 if (serverCleanupFuncs.isNil) {
126 serverCleanupFuncs = IdentityDictionary.new;
128 serverCleanupFuncs.put(server, {
131 ServerTree.remove(synthFunc, server);
138 var numRMSSamps, numRMSSampsRecip;
140 //responders and synths are started only once per server
141 numRMSSamps = server.sampleRate / updateFreq;
142 numRMSSampsRecip = 1 / numRMSSamps;
144 inresp = OSCFunc({|msg|
147 var channelCount = msg.size - 3 / 2;
149 channelCount.do {|channel|
150 var baseIndex = 3 + (2*channel);
151 var peakLevel = msg.at(baseIndex);
152 var rmsValue = msg.at(baseIndex + 1);
153 var meter = inmeters.at(channel);
154 if (meter.isClosed.not) {
155 meter.peakLevel = peakLevel.ampdb.linlin(dBLow, 0, 0, 1, \min);
156 meter.value = rmsValue.ampdb.linlin(dBLow, 0, 0, 1);
160 if(error.isKindOf(PrimitiveFailedError).not) { error.throw }
163 }, ("/" ++ server.name ++ "InLevels").asSymbol, server.addr).fix;
166 outresp = OSCFunc({|msg|
169 var channelCount = msg.size - 3 / 2;
171 channelCount.do {|channel|
172 var baseIndex = 3 + (2*channel);
173 var peakLevel = msg.at(baseIndex);
174 var rmsValue = msg.at(baseIndex + 1);
175 var meter = outmeters.at(channel);
176 if (meter.isClosed.not) {
177 meter.peakLevel = peakLevel.ampdb.linlin(dBLow, 0, 0, 1, \min);
178 meter.value = rmsValue.ampdb.linlin(dBLow, 0, 0, 1);
182 if(error.isKindOf(PrimitiveFailedError).not) { error.throw }
185 }, ("/" ++ server.name ++ "OutLevels").asSymbol, server.addr).fix;
190 if(serverMeterViews.isNil){
191 serverMeterViews = IdentityDictionary.new;
193 if(serverMeterViews[server].isNil){
194 serverMeterViews.put(server, List());
196 if(serverMeterViews[server].size == 0){
197 ServerTree.add(synthFunc, server);
198 if(server.serverRunning, synthFunc); // otherwise starts when booted
200 serverMeterViews[server].add(this);
201 if (server.serverRunning) {
204 ServerBoot.add (startResponderFunc, server)
209 serverMeterViews[server].remove(this);
211 if(serverMeterViews[server].size == 0){
212 serverCleanupFuncs[server].value;
213 serverCleanupFuncs.removeAt(server);
216 (numIns > 0).if({ inresp.free; });
217 (numOuts > 0).if({ outresp.free; });
219 ServerBoot.remove(startResponderFunc, server)
229 var <window, <meterView;
231 *new{ |server, numIns, numOuts|
233 var window, meterView;
235 numIns = numIns ?? { server.options.numInputBusChannels };
236 numOuts = numOuts ?? { server.options.numOutputBusChannels };
238 window = Window.new(server.name ++ " levels (dBFS)",
239 Rect(5, 305, ServerMeterView.getWidth(numIns,numOuts), ServerMeterView.height),
242 meterView = ServerMeterView(server, window, 0@0, numIns, numOuts);
243 meterView.view.keyDownAction_({ arg view, char, modifiers;
244 if(modifiers & 16515072 == 0) {
246 {char === 27.asAscii } { window.close };
252 ^super.newCopyArgs(window,meterView)