Browse Source

Muxlicer: All In can actually also act as All Out on hardware, so implement that here

* fix sequence reset logic
* COM/IO works even if sequence is stopped (uses active step)
* tweak knob colours
tags/v2.1.0
hemmer 9 months ago
parent
commit
81e2dbba6e
4 changed files with 83 additions and 53 deletions
  1. +7
    -7
      res/Davies1900hBlack.svg
  2. +4
    -4
      res/Davies1900hDarkGrey.svg
  3. +7
    -7
      res/Davies1900hLightGrey.svg
  4. +65
    -35
      src/Muxlicer.hpp

+ 7
- 7
res/Davies1900hBlack.svg View File

@@ -32,9 +32,9 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.919596"
inkscape:cx="-28.930746"
inkscape:cy="1.3566533"
inkscape:zoom="15.839192"
inkscape:cx="-2.0958648"
inkscape:cy="1.6720715"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
@@ -43,9 +43,9 @@
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="2560"
inkscape:window-height="1393"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-height="1346"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
units="px"
inkscape:document-rotation="0" />
@@ -77,7 +77,7 @@
<path
inkscape:connector-curvature="0"
id="path6445"
style="fill:#606060;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33335185"
style="fill:#3d3d3d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33335185"
d="m 0,0 c 0,-9.941 -8.06,-18 -18,-18 -9.941,0 -18,8.059 -18,18 0,9.941 8.059,18 18,18 C -8.06,18 0,9.941 0,0" />
</g>
<g


+ 4
- 4
res/Davies1900hDarkGrey.svg View File

@@ -43,9 +43,9 @@
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="2560"
inkscape:window-height="1361"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-height="1346"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
units="px"
inkscape:document-rotation="0" />
@@ -77,7 +77,7 @@
<path
inkscape:connector-curvature="0"
id="path6457"
style="fill:#a8a8a8;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33334816"
style="fill:#838383;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33334816"
d="m 0,0 c 0,-9.941 -8.059,-18 -18,-18 -9.941,0 -18,8.059 -18,18 0,9.941 8.059,18 18,18 C -8.059,18 0,9.941 0,0" />
</g>
<g


+ 7
- 7
res/Davies1900hLightGrey.svg View File

@@ -32,9 +32,9 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.959798"
inkscape:cx="-91.947964"
inkscape:cy="8.8255993"
inkscape:zoom="7.919596"
inkscape:cx="-55.623016"
inkscape:cy="34.945885"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
@@ -43,9 +43,9 @@
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="2560"
inkscape:window-height="1393"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-height="1346"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
units="px"
inkscape:document-rotation="0" />
@@ -57,7 +57,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>


+ 65
- 35
src/Muxlicer.hpp View File

@@ -214,6 +214,7 @@ struct Muxlicer : Module {
ENUMS(GATE_OUTPUTS, 8),
ENUMS(MUX_OUTPUTS, 8),
COM_OUTPUT,
ALL_OUTPUT,
NUM_OUTPUTS
};
enum LightIds {
@@ -257,7 +258,7 @@ struct Muxlicer : Module {

uint32_t runIndex; // which step are we on (0 to 7)
uint32_t addressIndex = 0;
bool reset = false;
bool playHeadHasReset = false;
bool tapped = false;

// used to track the clock (e.g. if external clock is not connected). NOTE: this clock
@@ -376,6 +377,10 @@ struct Muxlicer : Module {
internalClockLength = 0.250f;
internalClockProgress = 0;
runIndex = 0;
mainClockMultDiv.multDiv = 1;
outputClockMultDiv.multDiv = 1;
quadraticGatesOnly = false;
playState = STATE_STOPPED;
}

void process(const ProcessArgs& args) override {
@@ -396,14 +401,7 @@ struct Muxlicer : Module {

mainClockMultDiv.multDiv = getClockOptionFromParam();

if (resetTrigger.process(rescale(inputs[RESET_INPUT].getVoltage(), 0.1f, 2.f, 0.f, 1.f))) {
reset = true;
if (playState == STATE_STOPPED) {
playState = STATE_PLAY_ONCE;
}
}

processPlayResetSwitch();
processPlayResetLogic();

const float address = params[ADDRESS_PARAM].getValue() + inputs[ADDRESS_INPUT].getVoltage();
const bool isAddressInRunMode = address < 0.f;
@@ -438,26 +436,32 @@ struct Muxlicer : Module {
// so we must use a BooleanTrigger on the divided/mult'd signal in order to detect rising edge / when to advance the sequence
const bool dividedMultipliedClockPulseReceived = mainClockTrigger.process(mainClockMultDiv.process(args.sampleTime, clockPulseReceived));

// reset _doesn't_ reset/sync the clock, it just moves the sequence index marker back to the start
if (reset) {
runIndex = 0;
reset = false;
resetTimer.trigger();
}
// see https://vcvrack.com/manual/VoltageStandards#Timing
const bool resetGracePeriodActive = resetTimer.process(args.sampleTime);

if (dividedMultipliedClockPulseReceived) {
if (isAddressInRunMode && !resetGracePeriodActive) {
if (isAddressInRunMode && !resetGracePeriodActive && playState != STATE_STOPPED) {
runIndex++;
if (runIndex >= SEQUENCE_LENGTH) {
// both play modes will reset to step 0 and fire an EOC trigger
runIndex = 0;
endOfCyclePulse.trigger(1e-3);

// additionally stop if in one shot mode
if (playState == STATE_PLAY_ONCE) {
playState = STATE_STOPPED;
// the sequence resets by placing the play head at the final step (so that the next clock pulse
// ticks over onto the first step) - so if we are on the final step _because_ we've reset,
// then don't fire EOC
if (playHeadHasReset) {
playHeadHasReset = false;
runIndex = 0;
}
// otherwise we've naturally arrived at the last step so do fire EOC etc
else {
endOfCyclePulse.trigger(1e-3);

// stop on this step if in one shot mode
if (playState == STATE_PLAY_ONCE) {
playState = STATE_STOPPED;
}
else {
runIndex = 0;
}
}
}
}
@@ -486,7 +490,7 @@ struct Muxlicer : Module {
const int gateMode = getGateMode();

// current gate output _and_ "All Gates" output both get the gate pattern from multiClock
// NOTE: isAllGatesOutHigh can also be read by expanders
// NOTE: isAllGatesOutHigh can also be read by expanders
isAllGatesOutHigh = multiClock.getGate(gateMode) && (playState != STATE_STOPPED);
outputs[GATE_OUTPUTS + addressIndex].setVoltage(isAllGatesOutHigh * 10.f);
lights[GATE_LIGHTS + addressIndex].setBrightness(isAllGatesOutHigh * 1.f);
@@ -502,17 +506,22 @@ struct Muxlicer : Module {
outputs[MUX_OUTPUTS + i].setVoltageSimd<float_4>(0.f, c);
}

if (playState != STATE_STOPPED) {
const float_4 com_input = inputs[COM_INPUT].getPolyVoltageSimd<float_4>(c);
const float_4 com_input = inputs[COM_INPUT].getPolyVoltageSimd<float_4>(c);
if (outputs[MUX_OUTPUTS + addressIndex].isConnected()) {
outputs[MUX_OUTPUTS + addressIndex].setVoltageSimd(stepVolume * com_input, c);
outputs[ALL_OUTPUT].setVoltageSimd<float_4>(0.f, c);
}
else if (outputs[ALL_OUTPUT].isConnected()) {
outputs[ALL_OUTPUT].setVoltageSimd(stepVolume * com_input, c);
}
}

for (int i = 0; i < 8; i++) {
outputs[MUX_OUTPUTS + i].setChannels(numActiveEngines);
}
outputs[ALL_OUTPUT].setChannels(numActiveEngines);
}
else if (modeCOMIO == COM_8_IN_1_OUT && playState != STATE_STOPPED) {
else if (modeCOMIO == COM_8_IN_1_OUT) {
// we need at least one active engine, even if nothing is connected
// as we want the voltage that is normalled to All In to be processed
int numActiveEngines = std::max(1, inputs[ALL_INPUT].getChannels());
@@ -532,7 +541,7 @@ struct Muxlicer : Module {
// 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
isOutputClockHigh = outputClockMultDiv.process(args.sampleTime, clockPulseReceived) && playStateMask;
isOutputClockHigh = outputClockMultDiv.process(args.sampleTime, clockPulseReceived) && playStateMask;
outputs[CLOCK_OUTPUT].setVoltage(isOutputClockHigh * 10.f);
lights[CLOCK_LIGHT].setBrightness(isOutputClockHigh * 1.f);

@@ -541,7 +550,18 @@ struct Muxlicer : Module {

}

void processPlayResetSwitch() {
void processPlayResetLogic() {

const bool resetPulseInReceived = resetTrigger.process(rescale(inputs[RESET_INPUT].getVoltage(), 0.1f, 2.f, 0.f, 1.f));
if (resetPulseInReceived) {
playHeadHasReset = true;
runIndex = 8;

if (playState == STATE_STOPPED) {
playState = STATE_PLAY_ONCE;
}
resetTimer.trigger();
}

// if the play switch has effectively been activated for the first time,
// i.e. it's not just still being held
@@ -555,8 +575,8 @@ struct Muxlicer : Module {
}
else if (params[PLAY_PARAM].getValue() == STATE_PLAY_ONCE) {
playState = STATE_PLAY_ONCE;
runIndex = 0;
reset = true;
runIndex = 8;
playHeadHasReset = true;
}
}
// otherwise we are in play mode (and we've not just held onto the play switch),
@@ -569,8 +589,8 @@ struct Muxlicer : Module {
}
// bottom will reset
else if (params[PLAY_PARAM].getValue() == STATE_PLAY_ONCE) {
reset = true;
runIndex = 0;
playHeadHasReset = true;
runIndex = 8;
}
}
}
@@ -710,6 +730,7 @@ struct MuxlicerWidget : ModuleWidget {
addOutput(createOutput<BefacoOutputPort>(mm2px(Vec(61.69671, 109.29256)), module, Muxlicer::MUX_OUTPUTS + 6));
addOutput(createOutput<BefacoOutputPort>(mm2px(Vec(71.82537, 109.29256)), module, Muxlicer::MUX_OUTPUTS + 7));
addOutput(createOutput<BefacoOutputPort>(mm2px(Vec(36.3142, 98.07911)), module, Muxlicer::COM_OUTPUT));
addOutput(createOutput<BefacoOutputPort>(mm2px(Vec(16.11766, 98.09121)), module, Muxlicer::ALL_OUTPUT));

updatePortVisibilityForIOMode(Muxlicer::COM_8_IN_1_OUT);

@@ -902,9 +923,14 @@ struct MuxlicerWidget : ModuleWidget {

menu->addChild(new MenuSeparator());

OutputRangeItem* outputRangeItem = createMenuItem<OutputRangeItem>("All In Normalled Value", "▸");
outputRangeItem->module = module;
menu->addChild(outputRangeItem);
if (module->modeCOMIO == Muxlicer::COM_8_IN_1_OUT) {
OutputRangeItem* outputRangeItem = createMenuItem<OutputRangeItem>("All In Normalled Value", "▸");
outputRangeItem->module = module;
menu->addChild(outputRangeItem);
}
else {
menu->addChild(createMenuLabel<MenuLabel>("All In Normalled Value (disabled)"));
}

OutputClockStopStartItem* outputClockStopStartItem =
createMenuItem<OutputClockStopStartItem>("Output clock follows play/stop", CHECKMARK(module->quadraticGatesOnly));
@@ -932,11 +958,13 @@ struct MuxlicerWidget : ModuleWidget {
APP->scene->rack->clearCablesOnPort(outputs[i]);
}
APP->scene->rack->clearCablesOnPort(inputs[Muxlicer::COM_INPUT]);
APP->scene->rack->clearCablesOnPort(inputs[Muxlicer::ALL_INPUT]);

for (int i = Muxlicer::MUX_INPUTS; i <= Muxlicer::MUX_INPUTS_LAST; ++i) {
APP->scene->rack->clearCablesOnPort(inputs[i]);
}
APP->scene->rack->clearCablesOnPort(outputs[Muxlicer::COM_OUTPUT]);
APP->scene->rack->clearCablesOnPort(outputs[Muxlicer::ALL_OUTPUT]);
}

// set ports visibility, either for 1 input -> 8 outputs or 8 inputs -> 1 output
@@ -948,11 +976,13 @@ struct MuxlicerWidget : ModuleWidget {
outputs[i]->visible = visibleToggle;
}
inputs[Muxlicer::COM_INPUT]->visible = visibleToggle;
outputs[Muxlicer::ALL_OUTPUT]->visible = visibleToggle;

for (int i = Muxlicer::MUX_INPUTS; i <= Muxlicer::MUX_INPUTS_LAST; ++i) {
inputs[i]->visible = !visibleToggle;
}
outputs[Muxlicer::COM_OUTPUT]->visible = !visibleToggle;
inputs[Muxlicer::ALL_INPUT]->visible = !visibleToggle;
}




Loading…
Cancel
Save