fmalcher / soundcraft-ui

Connection library for the Soundcraft Ui Series (Ui12, Ui16, Ui24R)
MIT License
60 stars 13 forks source link

Get VU meter values #95

Closed dvolonnino closed 1 month ago

dvolonnino commented 1 year ago

Any idea how I can get the VU meter value for a channel using conn.conn.allMessages? Use case is I want to monitor the meter values and detect when audio is playing on a channel.

fmalcher commented 1 year ago

At the moment, there is no way to get the VU messages. There are plenty of them, and they are being filtered out at the root of the message stream so that the pipeline doesn't have to process them. However, I think we could make them available in a lazy manner, maybe behind a config flag. Can you (or someone else) find out how the VU messages can be decoded?

dvolonnino commented 1 year ago

@fmalcher are these not the VU messages?

VU2^GAIGBAoCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAAD3AAAAAAD3 VUA^GAICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

I'm trying to find out how to decode, just need to see an example of one.

fmalcher commented 1 year ago

That's right!

marcowartmann commented 1 year ago

@dvolonnino I just started writing a ui24r connector in Python and would also be interested in how to decode the VU messages. I try to send them further to an Arduino board via I2C protocol to visualize the levels with NeoPixel LED Stripes. Would be nice to get the result of your decoding investigation

marcowartmann commented 1 year ago

@dvolonnino I found a code where it looks like the decoding should be done: https://github.com/stevaedrum/ui2mcp/blob/master/main.c Search for VuMeter in the code maybe that helps, unfortunally I'm quite new to coding, looks not easy to understand

fmalcher commented 1 year ago

That looks good! Seems like this is the part: https://github.com/stevaedrum/ui2mcp/blob/master/main.c#L1594-L1720

The string is base64-encoded and each message describes the VU state of the whole mixer across all channels. Looks like each byte is a different portion of information. The first 7 bytes describe the number of channels: input, media, sub group, FX, AUX, master, line-in. The next 6 bytes is the VU info for the first channel, and so on. The gate activation info is also part of this message. Looks rather complex!

Maybe a look into the original implementation is helpful …

marcowartmann commented 1 year ago

Thank you for the fast response. I captured some websocket traffic yesterday and it looks like the VU messages from our UI24r could be decoded that way. I got messages approx. every 30 - 40 ms and i can see some values on the channel my bandmate plugged in his guitar.

marcowartmann commented 1 year ago

Here an short overview with the first 4 Channels if somebody is interested in. After base64 decoding i took the single bytes in HEX for each value.

Screenshot 2023-01-03 at 10 13 19

paulkilroy commented 1 year ago

@marcowartmann Did you decipher which field you'll use for your NeoPixel? I'm doing a similar project but on ESP32 with C not Python. Seems like I want to use vuCompOut for the NeoPixels? Its on a scale of 0 to 255?

marcowartmann commented 1 year ago

@marcowartmann Did you decipher which field you'll use for your NeoPixel? I'm doing a similar project but on ESP32 with C not Python. Seems like I want to use vuCompOut for the NeoPixels? Its on a scale of 0 to 255?

@paulkilroy Yes its 0 - 255. I was interested in the vuPre and vuPost values. It depends on what you want to visualize. I guess vuCompOut represents the level on compressor output. I suggest to check the UI24r block diagram on the manual below (chapter 2.3) https://www.soundcraft.com/zh/product_documents/ui24r_manual_v1-0_web-pdf

paulkilroy commented 1 year ago

@marcowartmann That helps -- thanks!

yleguern64 commented 1 month ago

Hello,

I found something and I tried by get LineIN sound level (R+L) in Websocket connection.

This is a method that converted by ChatGPT from the ui2mcp main.c file to Javascript.


const rawTrames = [
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9yIAIiD3GAAYFvcAAAAA9wAAAAD3AAAAAPcAAAAA90pKAEpJ90tLAEtJ9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9yAAIB/3FgAWFfcAAAAA9wAAAAD3AAAAAPcAAAAA90pKAEpJ90lJAElJ9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9yEAIR/3FwAXFfcAAAAA9wAAAAD3AAAAAPcAAAAA90pKAEpJ90pKAEpI9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9yEAISH3FwAXF/cAAAAA9wAAAAD3AAAAAPcAAAAA90pKAEpK90pKAEpK9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9yAAIB/3FgAWFfcAAAAA9wAAAAD3AAAAAPcAAAAA90lJAElI90lJAElJ9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9yAAISD3FwAXFvcAAAAA9wAAAAD3AAAAAPcAAAAA90pKAEpI90pKAEpK9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9xoAGhj3EAAQDvcAAAAA9wAAAAD3AAAAAPcAAAAA90REAERD90JCAEJC9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9xsAGxn3EQARD/cAAAAA9wAAAAD3AAAAAPcAAAAA90ZGAEZE90NDAENC9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9yAAISD3FwAXF/cAAAAA9wAAAAD3AAAAAPcAAAAA90pKAEpK90pKAEpJ9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9x4AHh33FAAUE/cAAAAA9wAAAAD3AAAAAPcAAAAA90ZGAEZF90dHAEdG9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9ycnACcm9ygoACgn9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9ygoACgm9ykpACkp9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9yUlACUl9yoqACoq9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xYWABYV9xcXABcX9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xISABIR9xAQABAP9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9zEAMTH3JwAnJ/cAAAAA9wAAAAD3AAAAAPcAAAAA91xcAFxc915eAF5e9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9yMAIyP3GQAZGfcAAAAA9wAAAAD3AAAAAPcAAAAA91VVAFVV9zs7ADs69w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9xgAGBj3DgAODvcAAAAA9wAAAAD3AAAAAPcAAAAA90tLAEtL9y0tAC0t9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wwADAv3AgACAvcAAAAA9wAAAAD3AAAAAPcAAAAA90BAAEBA9x4eAB4e9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9zExADEx9wICAAIC9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9yAgACAg9wAAAAAA9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAAA9wAAAAAA9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9zc3ADc291NTAFNS9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9ywALCz3IgAiIvcAAAAA9wAAAAD3AAAAAPcAAAAA911dAF1d91JSAFJS9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9ysAKyv3IQAhIfcAAAAA9wAAAAD3AAAAAPcAAAAA91paAFpa90hIAEhI9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9yQAJCT3GgAaGvcAAAAA9wAAAAD3AAAAAPcAAAAA91RUAFRU90JCAEJC9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9xoAGhr3EAAQEPcAAAAA9wAAAAD3AAAAAPcAAAAA90tLAEtL9zc3ADc39w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9xIAEhL3CAAICPcAAAAA9wAAAAD3AAAAAPcAAAAA90JCAEJC9zIzADIz9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wwADAv3AgACAfcAAAAA9wAAAAD3AAAAAPcAAAAA9zs7ADs79y4uAC4r9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9yIAIiD3GAAYFvcAAAAA9wAAAAD3AAAAAPcAAAAA90tLAEtK90pKAEpJ9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9x8AHx/3FQAVFfcAAAAA9wAAAAD3AAAAAPcAAAAA90dHAEdH90lJAElJ9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9xIAEhD3CAAIBvcAAAAA9wAAAAD3AAAAAPcAAAAA9zw8ADw69zs7ADs49w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9xAAEA33BgAGA/cAAAAA9wAAAAD3AAAAAPcAAAAA9zg4ADg39zk6ADk49w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9zQANDD3KwAqJ/cAAAAA9wAAAAD3AAAAAPcAAAAA915eAF5b91xcAFxZ9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9zcAODf3LgAuLvcAAAAA9wAAAAD3AAAAAPcAAAAA92BgAGBg92BgAGBg9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA90MAQ0H3OQA5N/cAAAAA9wAAAAD3AAAAAPcAAAAA929vAG9v93BwAHBs9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA90gASEj3PgA+PvcAAAAA9wAAAAD3AAAAAPcAAAAA93FxAHFx93FxAHFx9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA90oASkr3QABAQPcAAAAA9wAAAAD3AAAAAPcAAAAA93NzAHNz93NzAHNz9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA90cAR0f3PQA9PfcAAAAA9wAAAAD3AAAAAPcAAAAA93FxAHFx93BwAHBv9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA90UARUL3OwA7OPcAAAAA9wAAAAD3AAAAAPcAAAAA921tAG1r925uAG5t9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9zwAPDX3MgAyK/cAAAAA9wAAAAD3AAAAAPcAAAAA92JiAGJc92ZmAGZg9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9zYANjX3LAAsLPcAAAAA9wAAAAD3AAAAAPcAAAAA911dAF1d92BgAGBg9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9zYANjb3LAAsLPcAAAAA9wAAAAD3AAAAAPcAAAAA92BgAGBg92BgAGBg9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9zgAODj3LgAuLvcAAAAA9wAAAAD3AAAAAPcAAAAA92FhAGFh92FhAGFh9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9yYAJiT3GwAaGvcAAAAA9wAAAAD3AAAAAPcAAAAA90tLAEtL90pKAEpK9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9x0AHRv3EwATEfcAAAAA9wAAAAD3AAAAAPcAAAAA90VFAEVE90ZGAEZE9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9x4AHh33FAAUE/cAAAAA9wAAAAD3AAAAAPcAAAAA90dHAEdH90dHAEdF9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wMAAwP3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9y0tAC0t9ywsACws9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wQABAL3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9y4uAC4t9ywsACwq9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wMAAwL3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9y0tAC0t9ysrACsr9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9yAgACAd9yAgACAe9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xcXABcV9xgYABgW9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xQUABQU9xUVABUU9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9zEAMTH3JwAnJ/cAAAAA9wAAAAD3AAAAAPcAAAAA91xcAFxc915eAF5e9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9ygAKCj3HgAeHvcAAAAA9wAAAAD3AAAAAPcAAAAA91paAFpa90BAAEBA9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9x0AHR33FAAUFPcAAAAA9wAAAAD3AAAAAPcAAAAA91BQAFBQ9zU1ADU09w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9xIAEhH3CAAICPcAAAAA9wAAAAD3AAAAAPcAAAAA90VFAEVF9yYmACYm9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wQABAT3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9zk5ADk59xMTABMT9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9ywsACwr9wAAAAAA9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xcXABcX9wAAAAAA9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAAA9wAAAAAA9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9y8ALy33JQAlI/cAAAAA9wAAAAD3AAAAAPcAAAAA92FhAGFe90pKAEpJ9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9yYAJyb3HQAcHfcAAAAA9wAAAAD3AAAAAPcAAAAA91dXAFdX90JCAEJB9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9x0AHR33EwATE/cAAAAA9wAAAAD3AAAAAPcAAAAA909PAE9P9zo6ADo69w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9xMAExL3CQAJCPcAAAAA9wAAAAD3AAAAAPcAAAAA90REAERE9y8vAC8u9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wsACwr3AQABAPcAAAAA9wAAAAD3AAAAAPcAAAAA9zw8ADw69yMjACMi9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9y4uAC4s9x0dAB0b9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9ycnACcm9xwcABwb9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xYWABYW9xgYABgY9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xcXABcX9xMTABMT9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xwcABwa9xsbABsY9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xwcABwb9xsbABsa9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9yAgACAe9x0dAB0d9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9x0dAB0b9x4eAB4c9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9yAgACAg9x4eAB4c9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9x4eAB4e9x0dAB0b9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xsbABsa9xoaABoa9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9x8fAB8e9x0dAB0b9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9x4eAB4e9x0dAB0d9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xcXABcW9xkZABkZ9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xwcABwc9xkaABkY9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9x4eAB4b9xsbABsZ9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9yEhACEg9x0dAB0d9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9x4eAB4d9x4eAB4c9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9x8fAB8f9x0dAB0c9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9yEhACEg9x8fAB8d9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9x8fAB8f9x0dAB0d9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9x8fAB8f9x4eAB4e9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xsbABsZ9xwcABwc9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9xkZABkZ9x4eAB4a9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9x0dAB0d9xscABsa9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9zEAMTH3JwAnJ/cAAAAA9wAAAAD3AAAAAPcAAAAA91xcAFxc915eAF5e9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9ycAJyb3HQAcHfcAAAAA9wAAAAD3AAAAAPcAAAAA91lZAFlZ9z8/AD8/9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9x0AHR33EwATE/cAAAAA9wAAAAD3AAAAAPcAAAAA909PAE9P9zQ0ADQz9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wMAAwP3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9zg4ADg49xAQABAQ9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9zQ0ADQ09wcHAAcG9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9yMjACMj9wAAAAAA9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9woKAAoK9wAAAAAA9w==',
    'VU2^CAIEBAQCAgAAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAAAAPcAAAAAAAD3AAAAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAD3AAAAAPcAAAAA9wAAAAAA9wAAAAAA9w=='
];

function decodeVU2(vu2String) {
    // Remove the "VU2^" prefix and decode the Base64 content
    const base64Content = vu2String.slice(4);
    const decodedData = Uint8Array.from(atob(base64Content), char => char.charCodeAt(0));

    // Extract the number of channels and other elements
    const UIChannel = decodedData[0];
    const UIMedia = decodedData[1];
    const UISubGroup = decodedData[2];
    const UIFx = decodedData[3];
    const UIAux = decodedData[4];
    const UIMaster = decodedData[5];
    const UILineIn = decodedData[6];

    const ui = [];
    let t = 8;

    // Decode Input Channels
    for (let i = 0; i < UIChannel; ++i) {
        ui.push({
            vuPre: decodedData[t],
            vuPost: decodedData[t + 1],
            vuPostFader: decodedData[t + 2],
            vuGateIn: decodedData[t + 3],
            vuCompOut: decodedData[t + 4],
            vuCompMeter: (decodedData[t + 5] & 0x80) ? (0x7F - (decodedData[t + 5] ^ 0x80)) : (0x7F - decodedData[t + 5]),
            gate: (decodedData[t + 5] & 0x80) ? 1 : 0
        });
        t += 6;
    }

    // Decode Media Channels
    for (let i = 0; i < UIMedia; ++i) {
        ui.push({
            vuPre: decodedData[t],
            vuPost: decodedData[t + 1],
            vuPostFader: decodedData[t + 2],
            vuGateIn: decodedData[t + 3],
            vuCompOut: decodedData[t + 4],
            vuCompMeter: (decodedData[t + 5] & 0x80) ? (0x7F - (decodedData[t + 5] ^ 0x80)) : (0x7F - decodedData[t + 5]),
            gate: (decodedData[t + 5] & 0x80) ? 1 : 0
        });
        t += 6;
    }

    // Decode SubGroups
    for (let i = 0; i < UISubGroup; ++i) {
        ui.push({
            vuPostL: decodedData[t],
            vuPostR: decodedData[t + 1],
            vuPostFaderL: decodedData[t + 2],
            vuPostFaderR: decodedData[t + 3],
            vuGateIn: decodedData[t + 4],
            vuCompOut: decodedData[t + 5],
            vuCompMeter: (decodedData[t + 6] & 0x80) ? (0x7F - (decodedData[t + 6] ^ 0x80)) : (0x7F - decodedData[t + 6]),
            gate: (decodedData[t + 6] & 0x80) ? 1 : 0
        });
        t += 7;
    }

    // Decode FX Channels
    for (let i = 0; i < UIFx; ++i) {
        ui.push({
            vuPostL: decodedData[t],
            vuPostR: decodedData[t + 1],
            vuPostFaderL: decodedData[t + 2],
            vuPostFaderR: decodedData[t + 3],
            vuGateIn: decodedData[t + 4],
            vuCompOut: decodedData[t + 5],
            vuCompMeter: (decodedData[t + 6] & 0x80) ? (0x7F - (decodedData[t + 6] ^ 0x80)) : (0x7F - decodedData[t + 6]),
            gate: (decodedData[t + 6] & 0x80) ? 1 : 0
        });
        t += 7;
    }

    // Decode Aux Channels
    for (let i = 0; i < UIAux; ++i) {
        ui.push({
            vuPost: decodedData[t],
            vuPostFader: decodedData[t + 1],
            vuGateIn: decodedData[t + 2],
            vuCompOut: decodedData[t + 3],
            vuCompMeter: (decodedData[t + 4] & 0x80) ? (0x7F - (decodedData[t + 4] ^ 0x80)) : (0x7F - decodedData[t + 4]),
            gate: (decodedData[t + 4] & 0x80) ? 1 : 0
        });
        t += 5;
    }

    // Decode Master Channels
    for (let i = 0; i < UIMaster; ++i) {
        ui.push({
            vuPost: decodedData[t],
            vuPostFader: decodedData[t + 1],
            vuGateIn: decodedData[t + 2],
            vuCompOut: decodedData[t + 3],
            vuCompMeter: (decodedData[t + 4] & 0x80) ? (0x7F - (decodedData[t + 4] ^ 0x80)) : (0x7F - decodedData[t + 4]),
            gate: (decodedData[t + 4] & 0x80) ? 1 : 0
        });
        t += 5;
    }

    const lines = [];
    // Decode Line Inputs
    for (let i = 0; i < UILineIn; ++i) {
        lines.push({
            vuPre: decodedData[t],
            vuPost: decodedData[t + 1],
            vuPostFader: decodedData[t + 2],
            vuGateIn: decodedData[t + 3],
            vuCompOut: decodedData[t + 4],
            vuCompMeter: (decodedData[t + 5] & 0x80) ? (0x7F - (decodedData[t + 5] ^ 0x80)) : (0x7F - decodedData[t + 5]),
            gate: (decodedData[t + 5] & 0x80) ? 1 : 0
        });
        t += 6;
    }

    console.log(lines);

    return ui;
}

rawTrames.forEach(trame => {
    const result = decodeVU2(trame);
    // console.log(result);
});

Maybe it could be useful....

I think it is not a good idea to implement a the reception of the VU2 message, but maybe we could have an helper to decode only for subscribe from an attribute something like vu2Messages$ (similar to allMessages$)

fmalcher commented 1 month ago

Thanks @yleguern64 ! I also started a few experiment yesterday and they looked promising. 🙂 We can already receive the VU2 messages, we "just" need a different stream for them. At the moment, they are just filtered to nowhere.

What I'm not quite sure about is how the library should publish the VU data. On big stream with the full decoded message as an object? Single streams for each individual channel? Both? Should this be integrated into the MasterChannel class or should it stay a whole separate thing? I will think about this in the next couple of weeks. I think we're on a good way to solve this. 🙂

fmalcher commented 1 month ago

😏

Screenshot 2024-09-03 at 15 56 53

fmalcher commented 1 month ago

It works basically as a proof of concept, but it's not well integrated, yet. One problem is that different channel types behave differently, e.g. FX and sub-group channels have two VU meters, VCA has none, AUX channels have no vuPre.

It will make things a lot easier if we do not try to integrate VU information into the channel API (MasterChannel etc.) but put them into a whole different class VuProcessor. This also stays closer to the mixer API: VU messages are separate from the rest of the mixer state.

// with separate VuProcessor
conn.vuProcessor.input(1).post$

// as part of the channel
conn.master.input(1).vu.post$

Do you have any thoughts about this?

yleguern64 commented 1 month ago

It works basically as a proof of concept, but it's not well integrated, yet. One problem is that different channel types behave differently, e.g. FX and sub-group channels have two VU meters, VCA has none, AUX channels have no vuPre.

It will make things a lot easier if we do not try to integrate VU information into the channel API (MasterChannel etc.) but put them into a whole different class VuProcessor. This also stays closer to the mixer API: VU messages are separate from the rest of the mixer state.

// with separate VuProcessor
conn.vuProcessor.input(1).post$

// as part of the channel
conn.master.input(1).vu.post$

Do you have any thoughts about this?

Do we know what's difference between the 2 VU METERS ?

For the Classes Architecture, 3 solutions are available... You can:

  1. implements an attribute "messagesVu2$" for messages directly in the MixerStore with the object values (as a raw keys/values object like :[]any)...
  2. implements an attribut "messagesVu2$" for messages in the SoundcraftUI.master.fx(n), SoundcraftUI.master.line(n) ect... Master or DelayableMasterChannel
  3. implements a vumeter Class as an Object instance attribute in the SoundcraftUI class and add attributes in this class. Override with new values when received by the software from the hardware mixer... We will subscribe to states depend of the line, aux, attributes of the vumeter...

1 - is the fastest solution to develop 2 - is the middle ground between complexity and maintainability 3 - is the best one, but it requires more effort and time to develop

It depends on your timeline... but I want to congratulate you on the quality of the project, and I'm sure this functionality will be greatly appreciated!

fmalcher commented 1 month ago

@yleguern64 Thanks for your thoughts! I also thought about all of these approaches and my suggestion is the following:

In my previous message, the code example shows approaches (2) and (3):

// (3) with separate VuProcessor
conn.vuProcessor.input(1).post$

// (2) as part of the channel
conn.master.input(1).vu.post$

While I like the API surface of (2), I clearly advocate approach (3): We put all VU information in a separate class VuProcessor and don't mix them with the existing channel information.

If this is fine for all of you, I will implement this in the upcoming days.

fmalcher commented 1 month ago

PR #139 now has a fully working version that is ready for review. 🙂 :tada: