1 QLevelIndicator : QView {
3 *qtClass {^'QcLevelIndicator'}
6 ^this.getProperty(\value)
9 this.setProperty(\value, val);
11 valueAction_ { arg val;
12 this.setProperty(\value, val);
17 this.setProperty(\warning, val);
21 this.setProperty(\critical, val);
26 this.nonimpl("style");
29 background { ^this.getProperty(\grooveColor) }
30 background_ { arg color; this.setProperty(\grooveColor, color) }
33 this.nonimpl("numSteps");
37 this.nonimpl("image");
40 numTicks_ {arg number;
41 this.setProperty(\ticks, number);
44 numMajorTicks_ {arg number;
45 this.setProperty(\majorTicks, number);
49 this.setProperty(\drawPeak, bool);
53 this.setProperty(\peak, val);
56 *meterServer { arg server;
57 var window, inmeters, outmeters, inresp, outresp, insynth, outsynth, func;
59 var view, viewWidth, meterWidth = 15, gapWidth = 4;
60 var updateFreq = 10, dBLow = -80;
61 var numRMSSamps, numRMSSampsRecip;
64 numIns = server.options.numInputBusChannels;
65 numOuts = server.options.numOutputBusChannels;
66 viewWidth = (numIns + numOuts + 2) * (meterWidth + gapWidth);
67 window = Window.new(server.name ++ " levels (dBFS)", Rect(5, 305, viewWidth + 20, 230));
68 window.view.background = Color.grey(0.4);
70 view = CompositeView(window, Rect(10,25, viewWidth, 180) );
71 view.addFlowLayout(0@0, gapWidth@gapWidth);
74 UserView(view, Rect(0,0,meterWidth,195)).drawFunc_({
75 Pen.color = Color.white;
76 Pen.font = Font("Helvetica", 10, true);
77 Pen.stringCenteredIn("0", Rect(0, 0, meterWidth, 12));
78 Pen.stringCenteredIn("-80", Rect(0, 170, meterWidth, 12));
83 StaticText(window, Rect(10, 5, 100, 15))
84 .font_(Font("Helvetica", 10, true))
85 .stringColor_(Color.white)
87 inmeters = Array.fill( numIns, { arg i;
89 comp = CompositeView(view, Rect(0,0,meterWidth,195)).resize_(5);
90 StaticText(comp, Rect(0, 180, meterWidth, 15))
91 .font_(Font("Helvetica", 9, true))
92 .stringColor_(Color.white)
94 LevelIndicator( comp, Rect(0,0,meterWidth,180) ).warning_(0.9).critical_(1.0)
101 UserView(view, Rect(0,0,meterWidth,180)).drawFunc_({
102 Pen.color = Color.white;
103 Pen.line(((meterWidth + gapWidth) * 0.5)@0, ((meterWidth + gapWidth) * 0.5)@180);
109 StaticText(window, Rect(10 + if(numIns > 0 , ((numIns + 2) * (meterWidth + gapWidth)), 0), 5, 100, 15))
110 .font_(Font("Helvetica", 10, true))
111 .stringColor_(Color.white)
113 outmeters = Array.fill( numOuts, { arg i;
115 comp = CompositeView(view, Rect(0,0,meterWidth,195));
116 StaticText(comp, Rect(0, 180, meterWidth, 15))
117 .font_(Font("Helvetica", 9, true))
118 .stringColor_(Color.white)
119 .string_(i.asString);
120 LevelIndicator( comp, Rect(0,0,meterWidth,180) ).warning_(0.9).critical_(1.0)
129 numRMSSamps = server.sampleRate / updateFreq;
130 numRMSSampsRecip = 1 / numRMSSamps;
132 inresp = OSCProxy({ |msg, t| {try {
133 msg.copyToEnd(3).pairsDo({|val, peak, i|
137 meter.value = (val.max(0.0) * numRMSSampsRecip).sqrt.ampdb.linlin(dBLow, 0, 0, 1);
138 meter.peakLevel = peak.ampdb.linlin(dBLow, 0, 0, 1);
140 }, ("/" ++ server.name ++ "InLevels").asSymbol, server.addr).fix;
142 outresp = OSCProxy({ |msg, t| {try {
143 msg.copyToEnd(3).pairsDo({|val, peak, i|
146 meter = outmeters[i];
147 meter.value = (val.max(0.0) * numRMSSampsRecip).sqrt.ampdb.linlin(dBLow, 0, 0, 1);
148 meter.peakLevel = peak.ampdb.linlin(dBLow, 0, 0, 1);
150 }, ("/" ++ server.name ++ "OutLevels").asSymbol, server.addr).fix;
153 insynth = SynthDef(server.name ++ "InputLevels", {
155 in = In.ar(NumOutputBuses.ir, numIns);
156 imp = Impulse.ar(updateFreq);
157 SendReply.ar(imp, "/" ++ server.name ++ "InLevels",
158 // do the mean and sqrt clientside to save CPU
160 RunningSum.ar(in.squared, numRMSSamps),
161 Peak.ar(in, Delay1.ar(imp)).lag(0, 3)]
164 }).play(RootNode(server), nil, \addToHead);
167 outsynth = SynthDef(server.name ++ "OutputLevels", {
169 in = In.ar(0, numOuts);
170 imp = Impulse.ar(updateFreq);
171 SendReply.ar(imp, "/" ++ server.name ++ "OutLevels",
172 // do the mean and sqrt clientside to save CPU
174 RunningSum.ar(in.squared, numRMSSamps),
175 Peak.ar(in, Delay1.ar(imp)).lag(0, 3)
178 }).play(RootNode(server), nil, \addToTail);
183 (server.options.numInputBusChannels > 0).if({ inresp.clear;});
187 ServerTree.remove(func);
190 ServerTree.add(func);
191 if(server.serverRunning, func); // otherwise starts when booted