fmalcher/soundcraft-ui

Add support for automix

fmalcher opened this issue · 8 comments

see bitfocus/companion-module-soundcraft-ui#72

channel

  • enable/disable automix, automix state (i.0.amixgroup^0), values 0, 1 (groups) and -1 (disabled)
  • automix weight (i.0.amix^0.5193637621023514), linear and dB

global

  • automix groups enable/disable, group state (automix.a.on^0), a/b, values 0/1
  • global response time (automix.time^0.1821943259976487)

I started with this and encountered a blocking issue: Conversion from the internal linear values to the "global response time" in milliseconds is not linear. Probably it's logarithmic, but I did not investigate this further.

We need to understand this conversion and need to implement two functions to convert these value forth and back.
Here are a few value samples (first: milliseconds value, second: linear value):

[4000, 1],
[3719, 0.976303317535545],
[3400, 0.9478672985781991],
[2568, 0.8641390205371249],
[2090, 0.8072669826224329],
[1741, 0.7598736176935229],
[1183, 0.6682464454976303],
[831, 0.5939968404423381],
[470, 0.4897314375987363],
[265, 0.40126382306477104],
[143, 0.3206951026856241],
[67, 0.23380726698262255],
[51, 0.2037914691943129],
[38, 0.17061611374407593],
[27, 0.12796208530805697],
[21, 0.08056872037914703,],
[20, 0.06477093206951037],
[20, 0]

@koalaraptor Can you try to find out how this conversion works?

What did NOT work is powers of 10:

const upper = 4000;
const lower = 20;

const x = Math.pow(10, value * 3);
return Math.round(((x / 1000) * (upper - lower) + lower) * 10) / 10;

How about chatGPT?

Here is it's answer to your examples:

The conversion from linear values to the "global response time" in milliseconds seems to be a non-linear transformation. The provided samples suggest that it may be logarithmic or some other mathematical function. In JavaScript, you can try to find out how this conversion works by using different mathematical functions and fitting them to the given data to see which one provides the best approximation.

One common non-linear function to consider for this kind of transformation is the logarithmic function. Here's a JavaScript code snippet that attempts to approximate the conversion using a logarithmic function and provides functions for both conversion directions (forward and backward):

// Forward conversion: Linear to Milliseconds
function linearToMilliseconds(linearValue) {
  if (linearValue === 0) return 0;
  // Assuming logarithmic conversion
  return Math.exp(linearValue) * 1000;
}

// Backward conversion: Milliseconds to Linear
function millisecondsToLinear(millisecondsValue) {
  if (millisecondsValue === 0) return 0;
  // Assuming logarithmic conversion
  return Math.log(millisecondsValue / 1000);
}

// Test with sample values
const samples = [
  [4000, 1],
  [3719, 0.976303317535545],
  [3400, 0.9478672985781991],
  // Add more samples as needed
];

samples.forEach((sample) => {
  const milliseconds = sample[0];
  const linear = sample[1];
  const calculatedLinear = millisecondsToLinear(milliseconds);
  const calculatedMilliseconds = linearToMilliseconds(linear);
  console.log(`Milliseconds: ${milliseconds}, Linear: ${linear}`);
  console.log(`Calculated Linear: ${calculatedLinear}, Calculated Milliseconds: ${calculatedMilliseconds}`);
  console.log('---');
});

This code uses a logarithmic transformation for the conversion and should provide approximate results. You can add more sample data to improve the accuracy of the approximation if needed.

Keep in mind that this is just one possible approach, and the actual conversion function might be more complex. If this approximation does not match the actual behavior closely enough, you may need to explore other mathematical functions or methods for fitting the data.

What do you think? might this work?

I know that it has something to do with exponential/logarithmic functions, but I did not find out the details.
This ChatGPT answer is practically useless since it only tells us that Math.exp() and Math.log() exist ;-)
So if ChatGPT has some more ideas, let me know! 🙂

Hello @fmalcher

I am afraid i don't have much knowledge to support this kind of issue, but i do know someone who used to talk directly with the soundcraft team, i will try and reach out to him if he can help out.

Out of curiosity, is it important for the implementation to work for now?
While it would be nice to have the control, it's not critical in most use cases i have encountered ✌️

I know that it has something to do with exponential/logarithmic functions, but I did not find out the details. This ChatGPT answer is practically useless since it only tells us that Math.exp() and Math.log() exist ;-) So if ChatGPT has some more ideas, let me know! 🙂

Sorry, just tried to see if it was of any help...

Out of curiosity, is it important for the implementation to work for now?

You're right – it's not important for the rest of the features. However, if we add support for automix, this should be included to be feature-complete.

I figured it out. It was a weird constant that I found by approximating the value step-by-step.
Then I rearranged the equation to get the back conversion.

/**
 * Convert time fader value from linear float value (between 0 and 1) to milliseconds between 20..4000 ms
 * @param value linear fader value
 */
export function faderValueToTimeMs(value: number) {
  return Math.round(
    (timeMsUpperBound - timeMsLowerBound) * Math.pow(value, 3.0517) + timeMsLowerBound
  );
}

/**
 * Convert milliseconds to linear time fader value (between 0 and 1)
 * @param timeMs time in milliseconds between 20..4000
 */
export function timeMsToFaderValue(timeMs: number) {
  const sanitizedTime = clamp(timeMs, timeMsLowerBound, timeMsUpperBound);
  const result = Math.pow(
    (sanitizedTime - timeMsLowerBound) / (timeMsUpperBound - timeMsLowerBound),
    0.32768620768751844
  );

  return Math.floor(result * 10000000) / 10000000;
  // 0.32768620768751844 = 1 / 3.0517
}