foo_vis_vumeter is modern reimplementation of the analog VU meter component by DRON. It renders using DirectX 12.
.bin files. Refer to the
".bin File Specification" and "foobar2000 .rar
Specification" sections below..zip skins. Refer to the "AIMP
Analog .zip Specification" below..zip skins. Refer to the "AIMP LED
.zip Specification" below.foo_vis_vumeter.fb2k-component) from the component
page.foo_vis_vumeter.fbk2-component into foobar2000
using the File > Preferences > Components >
Install... menu item.<foobar2000 profile folder>\vumeter.The panels or skins files define the visualizations.
The foobar2000 component is not packaged with any panels. It supports
various formats, namely foobar2000 .bin files, AIMP analog
.zip skins and AIMP LED .zip skins.
Use these references to find community-made skins or create new ones using VUEditor:
VUEditor.zip (in Russian).foo_vis_vumeter
thread in the HydrogenAudio Forum.The component is controlled via the context menu, accessible by right-clicking anywhere on the component window. In addition, fine tuning can be performed by using the mouse wheel.
What the mouse wheel controls is selected through the "Tuning" menu. If the changes result in bad state, use the "Options > Reset" option. The selected tuning mode value can be reset to its default using middle click.
In the context menu there are a few symbols placed next to some options. These are their meaning:
What follows are the different menu categories with a short explanation of each option.
At the very top of the context menu, the current resolution of the loaded panel is displayed as a grayed informational item. If no panel is loaded, it shows a corresponding message.
Layout:
Mode:
(left + right) / 2. Side is show in the right panel and is
calculated as (left - right) / 2. Same downmixing into left
and right channels for multi-channel audio as the Stereo mode applies
before mid and side components are calculated.Levels:
libebur128 library--except for the 400ms window as that is
controlled/fine-tuned by the sample window option.Movement:
Decay:
Tuning (mouse wheel-controlled):
.bin panels. Default is 0
and the range is [-128.0, 512.0]. If:
Ctrl key is held down during the mouse wheel scroll,
the step size is 100.Shift key is held down during the mouse wheel scroll,
the step size is 10.Left Alt key is held down during the mouse wheel
scroll, the step size is 0.1..bin panels when a level other
than BS.1770 or R 128 is selected. Default is 0 and the range is [0.0,
2.0] in steps of 0.01.Ctrl key is held down during the mouse wheel scroll,
the step size is 10.Shift key is held down during the mouse wheel scroll,
the step size is 0.1.Left Alt key is held down during the mouse wheel
scroll, the step size is 0.01Options:
.ini files that may accompany the .bin files,
both loose and archived in .rar, and disables automatic
layout selection from filename layout suffixes. Defaults are not ignored
by default.Configure: opens a dialog box with the tuning options.
Mix: opens a dialog box with the channel level mixer board.
Explore: opens the profile directory in Windows Explorer.
Fullscreen: toggles the component between fullscreen mode and embedded or windowed mode. Keeps the screen on and stops the screensaver from starting during fullscreen. In fullscreen mode, the mouse cursor is automatically hidden after 1 second of inactivity and reappears on mouse movement or context menu interaction.
The Fullscreen option is bolded because it is the double-click default.
The different panels/skins found during the scan of the
<foobar2000 profile folder>\vumeter directory appear
in lexicographical order (directories first) below the "Fullscreen"
toggle.
There are some options under: Preferences > Advanced > Visualisations > VU Meter:
<unchecked>. Enables debug
console logging.<unchecked>. Unloads
resources when the component window is hidden.<empty> (implies
<foobar2000 profile folder>\vumeter\ when empty).
Supports %fb2k_profile_path% and environment variables
(also enclosed between %; DOS-style).<empty>; untested). If
the MD5 hash of the entered string matches a specific digest, the teapot
easter egg mode is activated on launch.The currently selected tuning parameter is denoted by a square symbol
under the context menu's Tuning popup. The selected tuning
parameter can be changed by three main methods. The first is directly
through selecting the desired one in the context menu. The second is
using a modifier key and the vertical mouse wheel together to cycle
through the parameters in menu order. The modifier key that must be held
down while the vertical mouse wheel turns is one of Esc,
Right Alt or AltGr. The third is to use the
horizontal mouse scroll wheel. As the tuning parameter changes, the
mouse tooltip should display the active one and its current value.
Once again, the selected tuning mode value can be reset to its default using middle click and all tuning parameters will fall back to their defaults if the Options > Reset option is used.
Note: selecting a parameter simply means this is the one that will be affected by the mouse wheel and middle mouse click. The combination of all settings and tuning parameter values determines the visualization's behavior appearance.
Tuning can also be done through the context menu's Configure dialog box. Notes:
Home/PgUp/PgDn/End.Channel level mixing can be customized through the context menu's Mix dialog box. Notes:
.bin panels. It
contains the edge-sampled RGB hexadecimal background color. It can be
changed to any other valid color, overriding the automatically-sampled
color.Skin filenames can include a layout suffix using the pattern
{N} where N is a number from 0 to 4
corresponding to the layout modes (0 = Left+Right H, 1 = Left+Right V, 2
= Left, 3 = Right, 4 = Mono). When a skin with a layout suffix is
loaded, the layout is automatically set to the specified mode. The
suffix is removed from the display name in the context menu. This
behavior is disabled when "Ignore Defaults" is enabled.
The component adds a dynamic menu item under foobar2000's main menu
(hidden by default; hold Shift when opening to reveal). It
displays the panel groups and skins, along with "Previous" and "Next"
navigation commands per group.
The press-and-tap touch gesture switches skins/panels within the same group.
The horizontal mouse wheel and buttons 4 and 5 can be used to cycle through the selected tuning option. Be aware that buttons 4 and 5 might have other side-effects in the player such as moving to the next or previous track.
A screenshot can be taken by holding Ctrl and
left-clicking on the component window. The screenshot is saved as a PNG
file in the screenshots subfolder of the panels directory.
Files are named sequentially (shot_00.png,
shot_01.png, etc.).
As with the mouse buttons, be aware that any custom keyboard shortcuts set in the player can alter or prevent the component from reacting to these.
Alt + Enter: toggles fullscreen.Esc: exits an easter egg mode (teapot or text
entry).Backspace: removes the last character in text entry
mode.Delete: clears all text in text entry mode.Page Up / Page Down: changes the texture
in teapot mode.Up Up Down Down Left Right Left Right B A): activates the
teapot easter egg.I D D Q D): activates the text entry
easter egg.The needle movement can be approximated using the following formula:
display = old +/- (e ^ (new - old) - 1) * rise/decay. The
rise or decay factor limits how far the needle can move per frame or
iteration of the calculation.
The needle position, frame number in foobar2000 .bin
panels or angle in AIMP analog .zip skins, is calculated
using the point-slope equation referenced to the zero dB frame or angle.
For the AIMP LED .zip skins, the "light" fill is calculated
by interpolating between the points in the dbs parameter's
array using Catmull-Rom splines.
Used a circuit of a stereo VU meter, tracing the PCB and translating all the component values of the driving circuit into a SPICE netlist. The netlist was simulated to get the step/transient and impulse responses of the "system." Since the VU meter face plate is logarithmic, the circuit uses analog differentiator, integrator and summer circuits. To validate the netlist simulation correctness, various points of the working circuit were probed with an oscilloscope.
With the characteristics of the driving circuit in hand, turned attention to the needle. The needle can be modeled as a mass-spring-damper system. So, setting up the ODE for the system (with some minor guesses) provides the behavior characteristics of the needle. This is a common second-order system with known roots.
The needle was set up underdamped and simulated/"animated" as a PID controller (see this series for additional insights). The PID coefficients were derived from the transient and impulse response targets of the SPICE simulation as those come from the voltage levels (closest to the visualization audio levels provided by foobar2000). The actual PID coefficients are simplified into higher level abstractions that are called "rise" and "decay"--where the larger numbers means faster. And also "jitter" which is a control on the minimum increment to the error accumulator.
This methodology only provides an approximate model of the analog behavior of the needle in a discrete simulation. Although the derivation was detailed, the needle movement of this component is not and will never be an accurate duplicate to its real-world analog. However, using the tuning knobs provided along time and experimentation it should be possible to create a pleasing ballistic needle movement.
.bin File
SpecificationPanel Specification
===================
Reverse Engineer: Jimmy Cassis
Date: 2024-10-01
This specification defines the format of the BIN file.
Panel File Format
-----------------
The files in this format use ".bin" extension.
The panel file begins with a header.
The panel file format layout:
Offset Size Description
0 2 Bitmap width (16-bit unsigned integer, little-endian), between 2 and 4096
2 2 Bitmap height (16-bit unsigned integer, little-endian), between 2 and 4096
4 2 Frame count (16-bit unsigned integer, little-endian), between 2 and 1024
6 2 Zero dB frame (16-bit unsigned integer, little-endian), between 0 and 1024
8 n Needle delta offset list (array of 32-bit unsigned integer offsets, little-endian), `n` is "frames * 4" and the first entry must be greater than or equal to 32; note that some offsets might be repeated due to the "knots" interpolation.
8+n p Background image bitmap pixel array (BI_RGB format [B8G8R8A8 UNORM], 32 bits per pixel, little-endian), `p` is "width * height * 4", format below
8+n+p l Optional, lamp delta offset list (array of 32-bit unsigned integer offsets, little-endian), `l` is "frames * 4"
8+n+p+l d Needle delta arrays (variable size, offsets are big-endian, deltas are BI_RGB and apply per channel), `d` is variable, format below
8+n+p+l+d a Optional, lamp delta arrays (variable size, offsets are big-endian, deltas are BI_RGB and apply per channel), `a` is variable, format below
The total size of the file is: 8+n+p+l+d+a
The total size of the file must be greater than: 8+n+p
The bitmap pixel array layout is as follows (address _decreases_ from left-to-right) and arranged in top-down order:
+-------------------------+-------------------------+-------------------------+-------------------------+
Sample Length: | 8 | 8 | 8 | 8 |
Channel Membership: | Alpha | Red | Green | Blue |
Pixel Bits: | A A A A a a a a | R R R R r r r r | G G G G g g g g | B B B B b b b b |
Bit Number: | 31 30 29 28 27 26 25 24 | 23 22 21 20 19 18 17 16 | 15 14 13 12 11 10 9 8 | 7 6 5 4 3 2 1 0 |
+-------------------------+-------------------------+-------------------------+-------------------------+
^ ^
| |
| + less significant byte
+ more significant byte
The needle and lamp delta arrays use the following format:
- For every frame there is a 2-byte big-endian offset while the most significant bit (MSB) of the even byte is unset.
- If the MSB for the even byte is set, it indicates a count (excluding the MSB) of the number of pixel channels to override starting at the offset in the pixel array indicated by the sum of all the previous 2-byte offsets.
* Note that the offset might not necessarily start or end at a pixel boundary.
- Repeat, calculating a new offset from the end of the final overriden pixel until the even byte MSB is set and the count is zero (0x80).
Worked example for the needle delta array (address _increases_ from left-to-right):
+-----------------------------------+-----------------------+-----+-----------------------------------------------------------+-----+---------...---+
Offset Sum: | 174272 | 7 | 1489| 19 | 1481| 23 ... |
Offset: |32768+32768+32768+32768+32768+10438| 7| 0| 1| 2| 3| 4| 5| 6| 1488|19| 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13|14|15|16|17|18| 1480|23| 0| 1|...| 0|
Bytes: |7F FF 7F FF 7F FF 7F FF 7F FF 28 BF 87 8A 8B 87 00 E0 E2 DE 05 D0 93 B1 B2 AE 00 69 6A 67 00 6B 6D 68 00 B8 B9 B6 00 E0 E2 DF 05 C8 97 E6 EE ... 80|
Pixel Bits: |^^ ** | Bb Gg Rr Aa Bb Gg Rr| | Bb Gg Rr Aa Bb Gg Rr Aa Bb Gg Rr Aa Bb Gg Rr Aa Bb Gg Rr| | Bb Gg ...End|
+-|--|------------------------------+-----------------------+-----+-----------------------------------------------------------+-----+---------...---+
| |
| + more significant byte
+ less significant byte
Notes
-----
When drawing, ignore the alpha channel.
Extensions
----------
1. Separate Left and Right Panels
By default, the panel file represents both the left and the right panels.
If the file format is repeated at the end of the delta arrays, it is assumed that the first format corresponds to the left panel and the second format corresponds to the right panel.
The file formats can be concatenated uncompressed into one file. Alternatively, individually compressed files can be concatenated (keep the same compression scheme for both).
Another method to provide separate left and right panels is to provide a pair of files where the left panel file ends in `1.bin` and the right panel file in `2.bin`.
2. Transparency (Alpha Channel)
By default, the alpha channel is ignored.
If the alpha channel is not to be ignored (BI_BITFIELDS format [B8G8R8A8 UNORM], 32 bits per pixel, little-endian), set bit 7 (the most significant bit) of the upper byte of the zero dB frame in the header.
3. Bottom-up Background Bitmap Pixel Array.
By default, the bitmap pixel array is assumed to be in top-down order.
If the bitmap pixel array is arranged in bottom-up order instead of top-down order, set bit 6 of the upper byte of the zero dB frame in the header.
.bin NotesWarning: Identifying LZMA-compressed heuristic is limited; especially if the skin was compressed outside of VUEditor. Therefore it is possible to confuse an uncompressed
.binfile of any width less than 225 with a LZMA-uncompressed one. Using a bzip2-compressed file mitigates this issue since this scheme starts with an easily identifiable magic number.
.zip
Specificationskin.ini alongside
corresponding PNG images at the root of the archive.0.png
(background), 1.png (needle), 2.png (glass)
with optional 3.png (LED) and bg.png
(wallpaper).VU section in the skin.ini file; the supported
parameters are MinAngle, MinLevel,
ZeroAngle, ZeroLevel, MaxAngle,
MaxLevel, PivotPointX,
PivotPointY, MobilityNegative,
MobilityPositive, orientation, and
dbs. The section and parameter names are case
insensitive.l_0.png (background), l_1.png (needle),
l_2.png (glass) with optional l_3.png (LED)
and the right panel images must be named r_0.png
(background), r_1.png (needle), r_2.png
(glass) with optional r_3.png (LED). There can be an
optional bg.png (wallpaper). The image names are case
insensitive.skin.ini file under the
VU_L section and the right panel parameters must be
specified under the VU_R section. The supported parameters
are the same as the single panel ones.Note 1:
MobilityNegative,MobilityPositive, andorientationparameters are currently unused.
Note 2: The
dbsparameter array is optional and only used in skins that include an LED component in addition to the needle. As noted previously, the LED image is{l_,r_,}3.png.
Note 3: Missing parameters are assumed to be 0 or false.
.zip
Specification (LVU)settings.ini
alongside corresponding PNG images at the root of the archive.settings.ini file under the left section and
the right panel parameters must be specified under the
right section. The background must be specified in the
bg section. The supported parameter in all 3 sections is
png. The supported parameters in the left and
right sections are orientation,
start_point, finish_point, pos_x,
pos_y, and dbs.dbs image "dimension" can increase or decrease with
the decibel level depending on whether the LED image is covered or
uncovered.orientation value of 0 specifies that the
dbs dimension refers to width (i.e., horizontal).
Conversely a value of 1 refers to height as the value that changes with
the level (i.e., vertical).png
parameter for each section..rar
Specification<filename>.bin file alongside an optional
<filename>.ini file. Where
<filename> must exactly match the name used as the
base name of <filename>.rar. Further, these files
must be placed in the root of the archive.<filename>1.bin/<filename>2.bin
naming pattern (with or without space between file name root and number)
is also supported for individual L/R panels..bin file(s) contained in the archive must follow
the .bin file specification outlined above and compressed,
if desired, using a supported format.DEFAULT section in the <filename>.ini
file; the supported parameters are fall, rise,
singleMeter, peakLED, isVertical,
curveAdj, and bgColour. The section and
parameter names are case insensitive.Note 1:
fallandriseparameters are currently unused.
Note 2: The
peakLEDparameter being true sets Levels to _Mixed as soon as the panel is loaded. ThesingleMeterandisVerticalparameters combine to set the Layout mode. ThebgColourparameter sets the background color, overriding the edge color detection. ThecurveAdjparameter flattens the logarithmic curve by affecting the quadratic parameter.
Note 3: Missing parameters are assumed to be 0 or false.
.ini File.rar specification, except that
<filename>.bin and <filename>.ini
are not archived together.<filename>.bin and
<filename>.ini must live in the same folder.The component exposes COM interfaces for automation. The
IVUMeter and IVUMeterWindow COM interfaces for
managing and interacting with the foobar2000 VU Meter component. In
foobar2000, these interfaces are mainly accessed through JavaScript.
com_get_interfaceRetrieves a IVUMeter interface instance.
extern "C" __declspec(dllimport) HRESULT __cdecl com_get_interface(IVUMeter** pp);LayoutEnumDefines the layout options for the VU Meter window.
| Value | Name | Description |
|---|---|---|
| 0 | BothH | Both channels, horizontal |
| 1 | BothV | Both channels, vertical |
| 2 | Left | Left channel only |
| 3 | Right | Right channel only |
| 4 | Mono | Mono layout |
IVUMeterProvides control and status for the VU Meter component.
IID:
{9EBE6FDE-502B-4571-922F-5D5942A53638}
| Property | Returns (get) / Accepts (put) | Description |
|---|---|---|
ComponentVersion |
UINT; get |
Gets component's version number. |
ComponentVersionText |
BSTR; get |
Gets component's version as text. |
LeftLevel |
FLOAT; get |
Gets left channel RMS level. |
RightLevel |
FLOAT; get |
Gets right channel RMS level. |
LeftPeak |
FLOAT; get |
Gets left channel peak level. |
RightPeak |
FLOAT; get |
Gets right channel peak level. |
Offset |
DOUBLE; get, put |
Gets or sets gain offset value. |
UpdatePeriod |
DOUBLE; get |
Gets update window period. |
DownmixChannels |
BOOL; get, put |
Gets or sets downmix state. |
RoundCorners |
FLOAT; get, put |
Gets or sets corner rounding. |
CubicInterpolation |
BOOL; get, put |
Gets or sets cubic interpolation state. |
DisableTuning |
BOOL; get, put |
Gets or sets tuning disable state. |
DisallowFullscreen |
BOOL; get, put |
Gets or sets fullscreen restriction state. |
MultiMonFullscreen |
BOOL; get, put |
Gets or sets multi-monitor fullscreen state. |
RemoveBackground |
BOOL; get, put |
Gets or sets background removal state. |
EnableTransparency |
BOOL; get, put |
Gets or sets transparency state. |
IgnoreDefaults |
BOOL; get, put |
Gets or sets "ignore defaults" state. |
Freeze |
BOOL; get, put |
Gets or sets freeze state. |
ShowCounter |
BOOL; get, put |
Gets or sets FPS counter visibility. |
ScreensaverMode |
BOOL; get, put |
Gets or sets screensaver mode state. |
VSync |
BOOL; get, put |
Gets or sets (no effect) VSync state. |
WindowID |
UINT; get |
Gets current active window ID. Returns -1 if there is no active window. |
WindowCount |
UINT; get |
Gets total window count. |
| Function | Returns | Description |
|---|---|---|
Print(BSTR* Text) |
None | Prints text to the foobar2000 console. |
Rescan() |
None | Rescans vumeter folder. |
Defaults() |
None | Restores defaults. |
CreateWindow(UINT ID) |
IVUMeterWindow** |
Creates VUMeterWindow interface for the window using ID. |
RegisterWindow(UINT ID) |
None | Sets window using ID as active. |
RegisterRect(UINT ID, INT Left, INT Top, INT Width, INT Height) |
None | Registers a rectangle region -- no effect. |
IVUMeterWindowRepresents a VU Meter window instance with properties for visibility, layout, and skin/panel configuration.
IID:
{482C5F92-2D21-4F6F-9857-87C1D2C04344}
| Method | Returns (get) / Accepts (put) | Description |
|---|---|---|
Visible |
BOOL; get, put |
Gets or sets (no effect) window visibility state. |
Enabled |
BOOL; get, put |
Gets or sets (no effect) window enabled state. |
Layout |
LayoutEnum; get, put |
Gets or sets current layout. |
LockAspectRatio |
BOOL; get, put |
Gets or sets aspect ratio lock state. |
SkinName |
BSTR; get, put |
Gets current skin name or changes skin. |
GroupName |
BSTR; get, put |
Gets current group name or changes skin to first one in group. |
GroupCount |
UINT; get |
Gets number of groups (directories). |
SkinCount |
UINT; get |
Gets number of skins in group of currently-selected skin. |
| Function | Returns | Description |
|---|---|---|
SetBounds(int Left, int Top, int Width, int Height) |
None | Sets window position and size -- no effect. |
SetConfig(BSTR GroupName, BSTR SkinName, LayoutEnum Layout) |
None | Changes skin and layout. |
LoadSkin(BSTR GroupName, BSTR SkinName) |
None | Changes skin. |
LoadPrevSkin(BSTR GroupName) |
None | Loads previous skin in group (does not wrap around). |
LoadNextSkin(BSTR GroupName) |
None | Loads next skin in group (does not wrap around). |
GroupSize(UINT Index) |
UINT |
Gets size of a group by index. |
Groups(UINT Index) |
BSTR |
Gets group name by index. |
Skins(UINT Index) |
BSTR |
Gets skin name by index in group. |
Skin(UINT Group, UINT Index) |
BSTR |
Gets skin name by group index and skin index. |