Monitoring Audio Bus Volume

Hello everyone,

I wanted to ask if anyone knows a way of monitoring the level of audio busses separately. I dedicated each synth with a send to control the volumes but I can’t see how loud they are. As far as I know ServerMeterView only monitors the input and output busses. A possible solution that I read in the forums was PeakMonitor, but I wanted more of a mixer GUI that would show the levels of all the busses that I want to see on the same window side by side.

My last resort is somehow finding a UGen that would follow the input volume and send this to a GUI with LevelIndicator via a control Bus.

I was just wondering if anyone knows a more direct approach or any efficient way to use PeakMonitor quark in such manner?

I’m also interested in this. Here’s a quick prototype I made:

(
var monitorBus = Bus.control(s, 1);
var ampValue = 0;
var view = UserView()
.drawFunc_({ |view|
	var rectSize = ampValue.linlin(
		0, 1,
		0, view.bounds.height
	);
	Pen.fillColor_(Color.green);
	Pen.fillRect(
		Rect(
			0,
			view.bounds.height - rectSize,
			view.bounds.width,
			rectSize
		)
	);
});

SynthDef(\monitorTest, { |out = 0, monitorBus|
	var snd = SinOsc.ar(220, mul: 0.4) * SinOsc.ar(0.1);
	var ampTracker = Amplitude.ar(snd);
	snd = snd!2;
	Out.ar(out, snd);
	Out.kr(monitorBus, ampTracker);
}).add;

fork {
	s.sync;
	Synth(\monitorTest, [\monitorBus, monitorBus]);

	Routine({
		loop {
			monitorBus.get({ |value|
				ampValue = value;
				{ view.refresh; }.defer;
			});
			0.05.wait;
		}
	}).play;
};

CmdPeriod.doOnce({
	monitorBus.free;
	view.close;
});

view.front;
)

I’m using Amplitude to track the amplitude of the signal within the Synthdef, then write it to a Bus, which is regularly read using Bus.get, which in turns calls for a View refresh according to the current value. Now that I think about it, the View refresh should be independent from Bus.get, which should only update the variable the View is reading from.

1 Like

Take a look at the help file for LevelIndicator and the examples.

2 Likes

Thanks for the code! I was originally hoping for a quark with all the GUI stuff readily available, but I guess I have to code everything :sweat_smile: I am going to use this approach with LevelIndicator, but I also find it a nice touch that you did there with Pen, it enables more customization.

Hi,

For GUI monitoring of audio buses I use an old class by Yvan Volochine, modified a little here to work with current SC.

Here is the class, YVBusMeter:

// original code by Yvan Volochine
// fixes for Swing and multichannel by Marije Baalman
// fixes for SC 3.13 by blindmanonacid

// TODO:
// - add a decay to the vu-s (Amplitude.kr's release param?) -> thx Blackrain
// - separate GUI from this code

YVBusMeter {
	var <meterGui, s, w;
	var <name, <coord;
	var <bus;
	var <responder, <meterSynth, <defName;

	*new { |win, bus, coordinates|
		if (win.notNil and: {bus.notNil}, {
			^super.new.init(win, bus, coordinates)
		}, {
			Error(" args").throw
		})
	}

	free {
		try{
			responder.free;
			meterSynth.free;
		}
	}

	init { |win, b, coordinates|
		s      = Server.default;
		coord  = coordinates ?? [10, 10, 10, 160];
		w      = win;
		//name   = ("vu_" ++ Date.localtime.stamp).asSymbol;
		name   = ("/vu_" ++ UniqueID.next).asSymbol;
		bus   = b;
		this.drawMeter;
		CmdPeriod.doOnce{ this.free };
	}


	drawMeter {
		this.addSynth;
		meterGui = LevelIndicator(w, Rect(*coord));
		meterGui.drawsPeak = true;
		this.addOscResponder;
	}

	addOscResponder {

		responder = OSCFunc({ arg msg;
			{
				if(meterGui.notNil, {
					meterGui.value_(msg[3].ampdb.linlin(-40, 0, 0, 1))
					    .peakLevel_(msg[4].ampdb.linlin(-40, 0, 0, 1))
					    .warning_(0.75)
					    .critical_(0.95)
				})
			}.defer; // FIXME
		}, name, s.addr).add;

		w.onClose_({ this.free })
	}

	addSynth {
		s.waitForBoot {
			defName = "forMeter_"++ UniqueID.next;
			SynthDef( defName, {
				|busid=0|
				var imp, delimp, snd, id;
				snd    = In.ar(busid, bus.numChannels);
				imp    = Impulse.kr(10); // change polling interval
				delimp = Delay1.kr(imp);
				// measure rms and Peak
				SendReply.kr(
					imp,
					name,
					[Amplitude.kr(snd, 0.01, 0.5), K2A.ar(Peak.ar(snd, delimp).lag(0, 3))]
				)
			}).send(s);
			s.sync;
			meterSynth = Synth.new( defName, [\busid, bus.index], s, \addToTail);
		}
	}

}

And here is some example code:


(
w= Window("busmeter", Rect(0, 0, 200, 400));
g= Bus.audio(s, 1);
YVBusMeter.new(w, g, [20, 20, 160, 360]); // (window, Bus, [left, top, hsize, vsize])
w.front;
)
b= Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");
(
x = SynthDef(\help_Buffer, { arg out = 0, bufnum;
	Out.ar( [0, 1, g],
        PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum), loop: 1)
    )
}).play(s,[\bufnum, b]);
)
x.free; b.free;
1 Like

Thanks a lot, this was the kind of approach that I was looking for