diff --git a/src/Stages.cpp b/src/Stages.cpp
index 26960a6..154eb07 100644
--- a/src/Stages.cpp
+++ b/src/Stages.cpp
@@ -42,59 +42,78 @@ struct LongPressButton {
}
};
+struct GroupInfo {
+ int first_segment = 0;
+ int segment_count = 0;
+ bool gated = false;
+};
struct GroupBuilder {
- GroupBuilder() {
- for (size_t i = 0; i < NUM_CHANNELS; i++) {
- groupSize[i] = 0;
- }
- }
- size_t groupSize[NUM_CHANNELS];
- bool isPatched = false;
+ GroupInfo groups[NUM_CHANNELS];
+ int groupCount = 0;
- bool buildGroups(std::vector *inputs, size_t first, size_t count) {
- bool changed = false;
- isPatched = false;
- size_t activeGroup = 0;
+ bool buildGroups(std::vector *gateInputs, size_t first, size_t count) {
+ bool any_gates = false;
- for (int i = count - 1; i >= 0; i -= 1) {
- bool patched = (*inputs)[first + i].active;
+ GroupInfo nextGroups[NUM_CHANNELS];
- activeGroup++;
- if (!patched) {
- changed = changed || (groupSize[i] != 0);
- groupSize[i] = 0;
+ int currentGroup = 0;
+ for (int i = 0; i < NUM_CHANNELS; i++) {
+ bool gated = (*gateInputs)[first + i].active;
+
+ if (!any_gates) {
+ if (!gated) {
+ // No gates at all yet, segments are all single segment groups
+ nextGroups[currentGroup].first_segment = i;
+ nextGroups[currentGroup].segment_count = 1;
+ nextGroups[currentGroup].gated = false;
+ currentGroup++;
+ }
+ else {
+ // first gate, current group is start of a segment group
+ any_gates = true;
+ nextGroups[currentGroup].first_segment = i;
+ nextGroups[currentGroup].segment_count = 1;
+ nextGroups[currentGroup].gated = true;
+ currentGroup++;
+ }
}
- else if (patched) {
- isPatched = true;
- changed = changed || (groupSize[i] != activeGroup);
- groupSize[i] = activeGroup;
- activeGroup = 0;
+ else {
+ if (!gated) {
+ // We've had a gate, this ungated segment is part of the previous group
+ nextGroups[currentGroup-1].segment_count++;
+ }
+ else {
+ // This gated input indicates the start of the next group
+ nextGroups[currentGroup].first_segment = i;
+ nextGroups[currentGroup].segment_count = 1;
+ nextGroups[currentGroup].gated = true;
+ currentGroup++;
+ }
}
}
- return changed;
- }
-
- int groupForSegment(int segment) {
- int group = 0;
- int currentGroupSize = 0;
+ bool changed = false;
- for (int i = 0; i < NUM_CHANNELS; i++) {
- if (currentGroupSize <= 0) {
- currentGroupSize = max(1, groupSize[i]);
- group = i;
- }
+ if (currentGroup != groupCount) {
+ changed = true;
+ groupCount = currentGroup;
+ }
- if (segment == i) {
- return group;
+ for (int i = 0; i < groupCount; i++) {
+ if (nextGroups[i].segment_count != groups[i].segment_count ||
+ nextGroups[i].gated != groups[i].gated ||
+ nextGroups[i].first_segment != groups[i].first_segment) {
+ changed = true;
}
- currentGroupSize -= 1;
+ groups[i].first_segment = nextGroups[i].first_segment;
+ groups[i].segment_count = nextGroups[i].segment_count;
+ groups[i].gated = nextGroups[i].gated;
}
- return segment;
+ return changed;
}
};
@@ -203,42 +222,53 @@ struct Stages : Module {
// Process block
stages::SegmentGenerator::Output out[BLOCK_SIZE] = {};
- for (int i = 0; i < NUM_CHANNELS;) {
+ for (int i = 0; i < groupBuilder.groupCount; i++) {
+ GroupInfo &group = groupBuilder.groups[i];
+
// Check if the config needs applying to the segment generator for this group
- bool segment_changed = groups_changed;
- int numberOfLoops = 0;
- for (int j = 0; j < max(1, groupBuilder.groupSize[i]); j++) {
- numberOfLoops += configurations[i + j].loop ? 1 : 0;
- segment_changed |= configuration_changed[i + j];
- configuration_changed[i + j] = false;
+ bool apply_config = groups_changed;
+ int numberOfLoopsInGroup = 0;
+ for (int j = 0; j < group.segment_count; j++) {
+ int segment = group.first_segment + j;
+ numberOfLoopsInGroup += configurations[segment].loop ? 1 : 0;
+ apply_config |= configuration_changed[segment];
+ configuration_changed[segment] = false;
}
- if (segment_changed) {
- if (numberOfLoops > 2) {
- for (int j = 0; j < max(1, groupBuilder.groupSize[i]); j++) {
- configurations[i + j].loop = false;
- }
+ if (numberOfLoopsInGroup > 2) {
+ // Too many segments are looping, turn them all off
+ apply_config = true;
+ for (int j = 0; j < group.segment_count; j++) {
+ configurations[group.first_segment + j].loop = false;
}
- segment_generator[i].Configure(groupBuilder.groupSize[i] > 0, &configurations[i], max(1, groupBuilder.groupSize[i]));
+ }
+
+ if (apply_config) {
+ segment_generator[i].Configure(group.gated, &configurations[group.first_segment], group.segment_count);
}
// Set the segment parameters on the generator we're about to process
- for (int j = 0; j < segment_generator[i].num_segments(); j++) {
- segment_generator[i].set_segment_parameters(j, primaries[i + j], secondaries[i + j]);
+ for (int j = 0; j < group.segment_count; j++) {
+ segment_generator[i].set_segment_parameters(j, primaries[group.first_segment + j], secondaries[group.first_segment + j]);
}
- segment_generator[i].Process(gate_flags[i], out, BLOCK_SIZE);
+ segment_generator[i].Process(gate_flags[group.first_segment], out, BLOCK_SIZE);
- // Set the outputs for the active segment in each output sample
- // All outputs also go to the first segment
for (int j = 0; j < BLOCK_SIZE; j++) {
- for (int k = 1; k < segment_generator[i].num_segments(); k++) {
- envelopeBuffer[i + k][j] = k == out[j].segment ? 1 - out[j].phase : 0.f;
+ for (int k = 1; k < group.segment_count; k++) {
+ int segment = group.first_segment + k;
+ if (k == out[j].segment) {
+ // Set the phase output for the active segment
+ envelopeBuffer[segment][j] = 1.f - out[j].phase;
+ }
+ else {
+ // Non active segments have 0.f output
+ envelopeBuffer[segment][j] = 0.f;
+ }
}
- envelopeBuffer[i][j] = out[j].value;
+ // First group segment gets the actual output
+ envelopeBuffer[group.first_segment][j] = out[j].value;
}
-
- i += segment_generator[i].num_segments();
}
}
@@ -247,27 +277,35 @@ struct Stages : Module {
configuration_changed[i] = true;
}
- void toggleLoop(int i) {
- configuration_changed[i] = true;
- configurations[i].loop = !configurations[i].loop;
+ void toggleLoop(int segment) {
+ configuration_changed[segment] = true;
+ configurations[segment].loop = !configurations[segment].loop;
- // ensure that we're the only loop item in the group
- if (configurations[i].loop) {
- int group = groupBuilder.groupForSegment(i);
+ // ensure that we don't have too many looping segments in the group
+ if (configurations[segment].loop) {
+ int segment_count = 0;
+ for (int i = 0; i < groupBuilder.groupCount; i++) {
+ segment_count += groupBuilder.groups[i].segment_count;
- // See how many loop items we have
- int loopitems = 0;
+ if (segment_count > segment) {
+ GroupInfo &group = groupBuilder.groups[i];
- for (size_t j = 0; j < groupBuilder.groupSize[group]; j++) {
- loopitems += configurations[group + j].loop ? 1 : 0;
- }
+ // See how many loop items we have
+ int numberOfLoopsInGroup = 0;
- // If we've got too many loop items, clear down to the one looping segment
- if (loopitems > 2) {
- for (size_t j = 0; j < groupBuilder.groupSize[group]; j++) {
- configurations[group + j].loop = (group + (int)j) == i;
+ for (int j = 0; j < group.segment_count; j++) {
+ numberOfLoopsInGroup += configurations[group.first_segment + j].loop ? 1 : 0;
+ }
+
+ // If we've got too many loop items, clear down to the one looping segment
+ if (numberOfLoopsInGroup > 2) {
+ for (int j = 0; j < group.segment_count; j++) {
+ configurations[group.first_segment + j].loop = (group.first_segment + j) == segment;
+ }
+ }
+
+ break;
}
- loopitems = 1;
}
}
}
@@ -302,35 +340,34 @@ struct Stages : Module {
}
// Output
- int currentGroupSize = 0;
- int loopcount = 0;
- for (int i = 0; i < NUM_CHANNELS; i++) {
- float envelope = envelopeBuffer[i][blockIndex];
- outputs[ENVELOPE_OUTPUTS + i].value = envelope * 8.f;
- lights[ENVELOPE_LIGHTS + i].setBrightnessSmooth(envelope);
+ for (int i = 0; i < groupBuilder.groupCount; i++) {
+ GroupInfo &group = groupBuilder.groups[i];
- if (currentGroupSize <= 0) {
- currentGroupSize = max(1, groupBuilder.groupSize[i]);
- loopcount = 0;
- }
- currentGroupSize -= 1;
+ int numberOfLoopsInGroup = 0;
+ for (int j = 0; j < group.segment_count; j++) {
+ int segment = group.first_segment + j;
- loopcount += configurations[i].loop ? 1 : 0;
- float flashlevel = 1.f;
+ float envelope = envelopeBuffer[segment][blockIndex];
+ outputs[ENVELOPE_OUTPUTS + segment].value = envelope * 8.f;
+ lights[ENVELOPE_LIGHTS + segment].setBrightnessSmooth(envelope);
- if (configurations[i].loop && loopcount == 1) {
- flashlevel = abs(sinf(2.0f * M_PI * lightOscillatorPhase));
- }
- else if (configurations[i].loop && loopcount > 1) {
- float advancedPhase = lightOscillatorPhase + 0.25f;
- if (advancedPhase > 1.0f)
- advancedPhase -= 1.0f;
+ numberOfLoopsInGroup += configurations[segment].loop ? 1 : 0;
+ float flashlevel = 1.f;
- flashlevel = abs(sinf(2.0f * M_PI * advancedPhase));
- }
+ if (configurations[segment].loop && numberOfLoopsInGroup == 1) {
+ flashlevel = abs(sinf(2.0f * M_PI * lightOscillatorPhase));
+ }
+ else if (configurations[segment].loop && numberOfLoopsInGroup > 1) {
+ float advancedPhase = lightOscillatorPhase + 0.25f;
+ if (advancedPhase > 1.0f)
+ advancedPhase -= 1.0f;
+
+ flashlevel = abs(sinf(2.0f * M_PI * advancedPhase));
+ }
- lights[TYPE_LIGHTS + i * 2 + 0].setBrightness((configurations[i].type == 0 || configurations[i].type == 1) * flashlevel);
- lights[TYPE_LIGHTS + i * 2 + 1].setBrightness((configurations[i].type == 1 || configurations[i].type == 2) * flashlevel);
+ lights[TYPE_LIGHTS + segment * 2 + 0].setBrightness((configurations[segment].type == 0 || configurations[segment].type == 1) * flashlevel);
+ lights[TYPE_LIGHTS + segment * 2 + 1].setBrightness((configurations[segment].type == 1 || configurations[segment].type == 2) * flashlevel);
+ }
}
}
};