Highlighted
Mud Contributor - Level 2
Contributor - Level 2

interpreting VU meter values in OSC message /meters/1

So I batch subscribe to /meters/1, which returns a blob of 16 bit ints. I've found via experimentation that offset values[4-21] are the channel VU meters, but I don't really know how to interpret the values.

Specifically, how do I turn that number into a percentage of height on the VU meter?

I currently have a kludge I'm doing, developed entirely by trial and error, that gives me meters heights that... kinda look like what I see in the iPad app:

public static float XR18VUMeterToPercentage(Int16 value)
{
// this is some weird massaging of the data, but it produces VU percentages
// roughly like what I see in Behringer's apps
float halfIntRange = Int16.MaxValue / 2.0f;
float signal = Math.Max(0, value + halfIntRange);
float percentage = signal / halfIntRange;
return percentage;
}

What should I actually be doing here?
Mud Contributor - Level 2 09-10-2017

09-10-2017

interpreting VU meter values in OSC message /meters/1

So I batch subscribe to /meters/1, which returns a blob of 16 bit ints. I've found via experimentation that offset values[4-21] are the channel VU meters, but I don't really know how to interpret the values.

Specifically, how do I turn that number into a percentage of height on the VU meter?

I currently have a kludge I'm doing, developed entirely by trial and error, that gives me meters heights that... kinda look like what I see in the iPad app:

public static float XR18VUMeterToPercentage(Int16 value)
{
// this is some weird massaging of the data, but it produces VU percentages
// roughly like what I see in Behringer's apps
float halfIntRange = Int16.MaxValue / 2.0f;
float signal = Math.Max(0, value + halfIntRange);
float percentage = signal / halfIntRange;
return percentage;
}

What should I actually be doing here?

  • 0 Kudos
  • 5 Replies
  • Reply
Volunteer Moderator

Re: interpreting VU meter values in OSC message /meters/1

All of this is explained in Patricks osc-documentation and there are programming examples as well.
Robert Lofgren | Did you find my post helpful? Give kudos and/or mark it as a solution!
Contributor - Level 2

Re: interpreting VU meter values in OSC message /meters/1

Eric Tetz;131239 wrote:

What should I actually be doing here?

Divide the signed 16 bit integer by 256 (as a float or double) to get the value in decibels if you need it to get values from -128 to 127.996
The values are mostly negative (with 0 representing clip) but there are some meter values for FX slots that can return positive values.

To scale to your 'VU' meter you can do it simply in a linear fashion by determining the range you want to display (say -60 to 0 which contains 15361 discrete values) and divide your UI dimensions by the number of discrete values in your range less one, and offset accordingly.

(If you are putting a meter alongside a fader then the fader curves are somewhat different so you will need to write an algorithm to transpose the meter data into a suitable value)
Mud Contributor - Level 2
Contributor - Level 2

Re: interpreting VU meter values in OSC message /meters/1

Dave Meadowcroft;131248 wrote:
Divide the signed 16 bit integer by 256 (as a float or double) to get the value in decibels if you need it to get values from -128 to 127.996 The values are mostly negative (with 0 representing clip)


Ahh... OK. So for purposes of displaying a typical VU meter, I can ignore everything above 0, right? None of the Behringer mixer apps show overflow, they just peak out at 0 with the line faded to red. So it seems like my guesswork kludge was basically correct: ignore top half of value range, divide by half the value range to get percentage.

To use your numbers, if I have a bar which I'm drawing for the VU meter, and I want to know what percentage to fill this bar (from the bottom), I can do:

[FONT=Courier New] [SIZE=2]double db = Math.min(rawInt16MeterValue/256, 0); // convert to decibel [-128.0, 128.0], truncate everything above 0
double vuBarPercentage = 1 + db / 128; // convert to percentage value [0.0, 1.0][/SIZE]
[/FONT]
Sound about right?

Dave Meadowcroft wrote:
If you are putting a meter alongside a fader then the fader curves are somewhat different so you will need to write an algorithm to transpose the meter data into a suitable value)


In fact I am doing that. Any tip on that algorithm?

Robert Lofgren;131241 wrote:
All of this is explained in Patricks osc-documentation and there are programming examples as well.


Do you have a link? I found X32.OSC.pdf (unfortunately after I'd done my own reverse engineering effort to build my app) and found that a lot of it didn't apply to the XR18. For instance, the list of fader values totally doesn't work (my testing shows 160 values rather than 1024), /meters/1 returns different values on the XR18 than what's in that documentation, and there's nothing in there describing what Dave Meadowcroft just explained. The closest is a mention under /meters/16 of dividing a 32 bit int by 256 to get a value in the range [-128,0].

Is there document somewhere that covers the XAir?
Contributor - Level 2

Re: interpreting VU meter values in OSC message /meters/1

Eric Tetz;131290 wrote:
Ahh... OK. So for purposes of displaying a typical VU meter, I can ignore everything above 0, right?
Yes

Eric Tetz;131290 wrote:
To use your numbers, if I have a bar which I'm drawing for the VU meter, and I want to know what percentage to fill this bar (from the bottom), I can do:

[FONT=Courier New] [SIZE=2]double db = Math.min(rawInt16MeterValue/256, 0); // convert to decibel [-128.0, 128.0], truncate everything above 0
double vuBarPercentage = 1 + db / 128; // convert to percentage value [0.0, 1.0][/SIZE]
[/FONT]
Sound about right?
Yes depending on how the platform you are developing for does the Y axis. Most have zero at the top and positive numbers move down the screen.

Eric Tetz;131290 wrote:
In fact I am doing that. Any tip on that algorithm?
This is in Java (Android) but is easily translatable to other languages. Faders have a float range of 0 to 1 where -90 (labelled -oo) is 0 and +10 is 1. This code replicates that so the meter is capable of the same range. The top 10dB is potentially an issue here - the easiest way is not to draw anything at all in the top 25% of the meter!

public class MeterUtils {

public static double meterValueToDecibels(short meterValue) {
return meterValue / 256.0;
}

public static float decibelsToFaderOrLevelFloat(double decibels) {
float result = 0;
if (decibels >= 10) {
result = 1;
} else if (decibels >= -10) {
result = (float) ((decibels + 30) / 40);
} else if (decibels >=- 30) {
result = (float) ((decibels + 50) / 80);
} else if (decibels >= -60) {
result = (float) ((decibels + 70) / 160);
} else if (decibels > -90) {
result = (float) ((decibels + 90) / 480);
}
return result;
}

public static float meterValueToFaderOrLevelFloat(short meterValue) {
return decibelsToFaderOrLevelFloat(meterValueToDecibels(meterValue));
}
}


Eric Tetz;131290 wrote:
Is there document somewhere that covers the XAir?
There are some differences between the X AIR and X32. This is especially different when it comes to meters as you have discovered. There isn't a document that I know of specifically for X AIR.
Contributor - Level 2

Re: interpreting VU meter values in OSC message /meters/1

Hi Eric,

If you need to just consider the meter height and not the fader travel with it's extra 10dB then instead of the above, try the below. Using the shortToFloat method, passing the meter value received from the console, you will get a value of 0 (-90 db or less) to 1 (0 dB or more) following the scale of Music Group's fader/level value type.


public class MeterUtils {

private static final int METER_POINTS = 12;
private static final double METER_SEGMENT = 1.0 / METER_POINTS;
private static final double METER_DECIBELS_RATIO = 256;
public static final float MINUS_18dB_POINT;
public static final float MINUS_3dB_POINT;

static {
MINUS_18dB_POINT = meterDecibelsToFloat(-18);
MINUS_3dB_POINT = meterDecibelsToFloat(-3);
}

public static double shortToMeterDecibels(short s) {
return s / METER_DECIBELS_RATIO;
}
public static short meterDecibelsToShort(double meterDecibels) {
return (short) (meterDecibels * METER_DECIBELS_RATIO);
}

public static float meterDecibelsToFloat(double meterDecibels) {
if (meterDecibels >= 0)
return 1;
if (meterDecibels >= -10)
return (float) ((meterDecibels + 30) / 30);
if (meterDecibels >= -30)
return (float) ((meterDecibels + 50) / 60);
if (meterDecibels >= -60)
return (float) ((meterDecibels + 70) / 120);
if (meterDecibels > -90)
return (float) ((meterDecibels + 90) / 360);
return 0;
}
public static double floatToMeterDecibels(float f) {
if (f > 1)
return 0;
if (f >= (METER_SEGMENT * 8))
return (30.0 * f) - 30;
if (f >= (METER_SEGMENT * 4))
return (60.0 * f) - 50;
if (f >= METER_SEGMENT)
return (120.0 * f) - 70;
if (f > 0)
return (360.0 * f) - 90;
return -90;
}
}