Befaco Muxlicer: Self-patching Com out to Gate mode cv in

I have a question about GATE MODE behavior.

What I did

  1. Connect “Com I/O output” to “Gate mode CV input”
  2. Turn “Gate mode knob” clockwise fully
  3. Set the fader values as [0.1, 0, 0.2, 0, 0.3, 0, 0.4, 0] from left to right

I chose 0 at even-numbered steps, so I thought no gates would occur at it, but a short trigger occurred. I don’t have a hardware, so I can’t tell it is intended or not. I put an image which I hope to explain this situation.

Nice bug report thanks! I’ll take a look. My worry is that it’s due to the one sample delay introduced by cables, and that produces one sample trigger (from the previous step’s COM IO).

EDIT: added here: Self-patched Muxlicer (COMIO to Gate Mode) creates triggers when not expected. · Issue #30 · VCVRack/Befaco · GitHub

1 Like

The below (truncated) patch technically works as a possible fix but is unfortunately too slow. At least confirms the 1-sample delay theory. Does anyone know a more efficient way to detect if the current module is self patched (i.e. without iterating over all cables)?

I also initially tried a hook in draw doing something like APP->scene->rack->getCompleteCablesOnPort(this->getInput(Muxlicer::GATE_MODE_INPUT)); etc but that seems wrong (e.g. it wouldn’t work in headless).

diff --git a/src/Muxlicer.cpp b/src/Muxlicer.cpp
index 8f24a0e..2ae079e 100644
--- a/src/Muxlicer.cpp
+++ b/src/Muxlicer.cpp

 		// there is an option to stop output clock when play stops
 		const bool playStateMask = !outputClockFollowsPlayMode || (playState != STATE_STOPPED);
 		// NOTE: outputClockOut can also be read by expanders
@@ -611,6 +611,31 @@ struct Muxlicer : Module {
 		}
 	}
 
+	bool isCOMIOSelfPatchedToGateInput() {
+		// if either is not connected then we're not self patched
+		if (!inputs[GATE_MODE_INPUT].isConnected() || !outputs[COM_OUTPUT].isConnected()) {
+			return false;
+		}
+
+		// loop over all cables
+		for (int64_t cableId : APP->engine->getCableIds()) {
+
+			// if _this_ muxlicer has a self patching cable
+			const bool selfPatchedCurrentModule = (APP->engine->getCable(cableId)->inputModule->id == this->id &&
+			                                       APP->engine->getCable(cableId)->outputModule->id == this->id);
+			if (selfPatchedCurrentModule) {
+				// and that self patch is COM IO -> Gate Input
+				const bool comIOToGateInput = (APP->engine->getCable(cableId)->inputId == GATE_MODE_INPUT &&
+				                               APP->engine->getCable(cableId)->outputId == COM_OUTPUT);
+
+				if (comIOToGateInput) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
 
 	// determines how many gates to yield per step
 	int getGateMode() {
@@ -618,9 +643,12 @@ struct Muxlicer : Module {
 		int gate;
 
 		if (inputs[GATE_MODE_INPUT].isConnected()) {
+
+			float cvIn = isCOMIOSelfPatchedToGateInput() ? outputs[COM_OUTPUT].getVoltage() : inputs[GATE_MODE_INPUT].getVoltage();
+
 			// with gate acting as attenuator, hardware reacts in 1V increments,
 			// where x V -> (x + 1) V yields (x - 1) gates in that time
-			float gateCV = clamp(inputs[GATE_MODE_INPUT].getVoltage(), 0.f, 10.f);
+			float gateCV = clamp(cvIn, 0.f, 10.f);
 			float knobAttenuation = rescale(params[GATE_MODE_PARAM].getValue(), -1.f, 8.f, 0.f, 1.f);
 
 			gate = int (floor(gateCV * knobAttenuation)) - 1;