MultiPot 3

Uses a single potentiometer input to control three target values (layers). After a layer change, the outputs will be updated A) to the current poti value, immediately when the poti is touched (overwrite mode) B) to the current poti value, after the poti has passed the current value of the target (pickup mode) C) so that the remaining travel of the potentiometer spans all the remaining travel of the value (scale mode). After some time without movement of the potentiometer, the potentiometer has to be turned by a certain amount before new changes will be registered. This helps to prevent sudden changes in values caused by measurement noise, even if the pot is not moved. The deadband size can be configured.
Author:
License: GPL
Github: TSG/ctrl/PotDemux 3.axo

Inlets

frac32 poti

frac32 set0

frac32 set1

frac32 set2

int32.positive select the layer to be edited by the poti control

bool32.rising rising edge loads the values from the set input into the layer outputs (patch loading!)

Outlets

frac32 out0

frac32 out1

frac32 out2

Attributes

combo size of the potentiometer deadband (see description)

combo mode of operation (see description)

Declaration
static const int kNumLayers = 3;
static const s32 deadbandSize = (attr_deadbandSize << 19);

s32 latch[kNumLayers];
u32 timeoutCounter;
s32 lastLoad;
s8 isMoving; // 0 == stopped, 1 == up, -1 == down
s32 motionTrackPos;
#if attr_mode == 2
bool catched;
u32 lastLayer;
s32 lastPotValue;
#endif

#if attr_mode == 3
u32 lastLayer;
s32 lastPotValue;
float scale;
s32 potiStart, valueStart;
s8 lastDirection; // 0 == invalid, 1 == up, 2 == down
#endif
Init
for (int i = 0; i < kNumLayers; i++)
  latch[i] = 0;
timeoutCounter = 10000;
lastLoad = 0;
isMoving = 0;
motionTrackPos = 0;
#if attr_mode == 2
catched = false;
lastLayer = 0;
lastPotValue = 0;
#endif
#if attr_mode == 3
lastLayer = 0;
lastPotValue = 0;
scale = 0;
potiStart = 0;
valueStart = 0;
lastDirection = 0; // 0 == invalid, 1 == up, 2 == down
#endif
Control Rate
u32 layer = inlet_layer;

// detect layer switching and update "catch" status
#if attr_mode == 2 || attr_mode == 3
if (lastLayer != layer) {
#if attr_mode == 2
  catched = false;
  if ((layer >= 0) && (layer < 3)) {
    s32 deviation = latch[layer] - inlet_poti;
    if ((deviation < deadbandSize) && (deviation > -deadbandSize)) {
      catched = true;
    }
  }
#elif attr_mode == 3
  lastDirection = 0; // reset to invalid
#endif
}
#endif

// pot was untouched for some time
if (!isMoving) {
  // check for a change in the value
  s32 change = inlet_poti - motionTrackPos;
  if (change > deadbandSize) {
    timeoutCounter = 0;
    motionTrackPos = inlet_poti;
    isMoving = 1;
  } else if (change < -deadbandSize) {
    timeoutCounter = 0;
    motionTrackPos = inlet_poti;
    isMoving = -1;
  }
}
// the poti is moving. But did it stop just now?
if (isMoving) // no else!
{
  timeoutCounter++;
  if (timeoutCounter >= 50) {
    s32 change = inlet_poti - motionTrackPos;
    if (change > deadbandSize >> 1) {
      timeoutCounter = 0;
      motionTrackPos = inlet_poti;
      isMoving = 1;
    } else if (change<-deadbandSize> > 1) {
      timeoutCounter = 0;
      motionTrackPos = inlet_poti;
      isMoving = -1;
    } else if (timeoutCounter > 2000) {
#if attr_mode == 3
      lastDirection = 0;
#endif
      isMoving = 0;
    }
  }
}

// apply new value if there was a change recently
if (isMoving) {
  if ((layer >= 0) && (layer < 3)) {
#if attr_mode == 1
    latch[layer] = inlet_poti;
#elif attr_mode == 2
    if (!catched) {
      if (((lastPotValue <= latch[layer]) && (inlet_poti >= latch[layer])) ||
          ((lastPotValue > latch[layer]) && (inlet_poti <= latch[layer]))) {
        catched = true;
      }
    }
    if (catched) // no else!
    {
      latch[layer] = inlet_poti;
    }
#elif attr_mode == 3
    if (inlet_poti >= 64 << 21)
      latch[layer] = 64 << 21;
    else if (inlet_poti <= 0)
      latch[layer] = 0;
    else {
      if ((lastDirection < 1) && (isMoving >= 1) && (inlet_poti < 64 << 21)) {
        potiStart = inlet_poti;
        valueStart = latch[layer];
        s32 remainingPotiTravel = (64 << 21) - inlet_poti;
        s32 remainingValueTravel = (64 << 21) - latch[layer];
        scale = ((float)remainingValueTravel) / ((float)remainingPotiTravel);
      } else if ((lastDirection > -1) && (isMoving <= -1) && (inlet_poti > 0)) {
        potiStart = inlet_poti;
        valueStart = latch[layer];
        scale = (float)(latch[layer]) / (float)(inlet_poti);
      }

      if (isMoving == 1) {
        latch[layer] = valueStart + scale * (inlet_poti - potiStart);
      } else if (isMoving == -1) {
        latch[layer] = valueStart + scale * (inlet_poti - potiStart);
      }
    }
    lastDirection = isMoving;
#endif
  }

#if attr_mode == 2 || attr_mode == 3
  lastPotValue = inlet_poti;
#endif
}

// load values if trigger received
if (inlet_load && !lastLoad) {
  latch[0] = inlet_set0;
  latch[1] = inlet_set1;
  latch[2] = inlet_set2;
}
lastLoad = inlet_load;

// write outputs
outlet_out0 = latch[0];
outlet_out1 = latch[1];
outlet_out2 = latch[2];

#if attr_mode == 2 || attr_mode == 3
lastLayer = inlet_layer;
#endif

Privacy

© 2024 Zrna Research