Skip to content

Commit

Permalink
EuclidX: latest version
Browse files Browse the repository at this point in the history
Added Padding parameter, bjorklund fixes
  • Loading branch information
djphazer committed Feb 9, 2023
1 parent 835c3af commit 5c598ab
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 62 deletions.
155 changes: 101 additions & 54 deletions software/o_c_REV/HEM_EuclidX.ino
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,22 @@

#include "bjorklund.h"

const int NUM_PARAMS = 4;
const int NUM_PARAMS = 5;
const int PARAM_SIZE = 5;

class EuclidX : public HemisphereApplet {
public:

enum EuclidXParam {
LENGTH1, BEATS1, OFFSET1, PADDING1,
LENGTH2, BEATS2, OFFSET2, PADDING2,
CV_DEST1,
CV_DEST2,
LAST_SETTING = CV_DEST2
};

const char* applet_name() {
return "AnnularFu";
return "EuclidX";
}

void Start() {
Expand All @@ -46,7 +54,8 @@ public:
actual_length[ch] = length[ch] = 16;
actual_beats[ch] = beats[ch] = 4 + (ch * 4);
actual_offset[ch] = offset[ch] = 0;
pattern[ch] = EuclideanPattern(length[ch], beats[ch], 0);
actual_padding[ch] = padding[ch] = 0;
pattern[ch] = EuclideanPattern(length[ch], beats[ch], offset[ch], padding[ch]);
}
step = 0;
}
Expand All @@ -63,40 +72,44 @@ public:
actual_length[ch] = length[ch];
actual_beats[ch] = beats[ch];
actual_offset[ch] = offset[ch];
actual_padding[ch] = padding[ch];

// process CV inputs
ForEachChannel(cv_ch) {
switch (cv_dest[cv_ch] - ch * (NUM_PARAMS-1)) {
case 0: // length
actual_length[ch] = constrain(actual_length[ch] + Proportion(cv_data[cv_ch], HEMISPHERE_MAX_CV, 31), 1, 32);
switch (cv_dest[cv_ch] - ch * LENGTH2) { // this is dumb, but efficient
case LENGTH1:
actual_length[ch] = constrain(actual_length[ch] + Proportion(cv_data[cv_ch], HEMISPHERE_MAX_CV, 31), 2, 32);
break;
case 1: // beats
case BEATS1:
actual_beats[ch] = constrain(actual_beats[ch] + Proportion(cv_data[cv_ch], HEMISPHERE_MAX_CV, actual_length[ch]), 1, actual_length[ch]);
break;
case 2: // offset
actual_offset[ch] = constrain(actual_offset[ch] + Proportion(cv_data[cv_ch], HEMISPHERE_MAX_CV, actual_length[ch]), 0, actual_length[ch]-1);
case OFFSET1:
actual_offset[ch] = constrain(actual_offset[ch] + Proportion(cv_data[cv_ch], HEMISPHERE_MAX_CV, actual_length[ch] + actual_padding[ch]), 0, actual_length[ch] + padding[ch] - 1);
break;
case PADDING1:
actual_padding[ch] = constrain(actual_padding[ch] + Proportion(cv_data[cv_ch], HEMISPHERE_MAX_CV, 32 - actual_length[ch]), 0, 32 - actual_length[ch]);
break;
default: break;
}
}

// Store the pattern for display
pattern[ch] = EuclideanPattern(actual_length[ch], actual_beats[ch], actual_offset[ch]);
pattern[ch] = EuclideanPattern(actual_length[ch], actual_beats[ch], actual_offset[ch], actual_padding[ch]);
}

// Process triggers and step forward on clock
if (Clock(0)) {

ForEachChannel(ch) {
// actually output the triggers
int sb = step % actual_length[ch];
int sb = step % (actual_length[ch] + actual_padding[ch]);
if ((pattern[ch] >> sb) & 0x01) {
ClockOut(ch);
}
}

// Plan for the thing to run forever and ever
if (++step >= actual_length[0] * actual_length[1]) step = 0;
if (++step >= (actual_length[0]+actual_padding[0]) * (actual_length[1]+actual_padding[1])) step = 0;
}
}

Expand All @@ -107,27 +120,40 @@ public:
}

void OnButtonPress() {
if (++cursor > 7) cursor = 0;
ResetCursor();
CursorAction(cursor, LAST_SETTING);
}

void OnEncoderMove(int direction) {
int ch = cursor < NUM_PARAMS ? 0 : 1;
int f = cursor - (ch * NUM_PARAMS); // Cursor function
switch (f) {
case 0:
actual_length[ch] = length[ch] = constrain(length[ch] + direction, 3, 32);
if (!EditMode()) {
MoveCursor(cursor, direction, LAST_SETTING);
return;
}

int ch = cursor < LENGTH2 ? 0 : 1;
switch (cursor) {
case LENGTH1:
case LENGTH2:
actual_length[ch] = length[ch] = constrain(length[ch] + direction, 2, 32);
if (beats[ch] > length[ch]) beats[ch] = length[ch];
if (offset[ch] >= length[ch]) offset[ch] = length[ch]-1;
if (padding[ch] > 32 - length[ch]) padding[ch] = 32 - length[ch];
if (offset[ch] >= length[ch] + padding[ch]) offset[ch] = length[ch] + padding[ch] - 1;
break;
case 1:
case BEATS1:
case BEATS2:
actual_beats[ch] = beats[ch] = constrain(beats[ch] + direction, 1, length[ch]);
break;
case 2:
actual_offset[ch] = offset[ch] = constrain(offset[ch] + direction, 0, length[ch] - 1);
case OFFSET1:
case OFFSET2:
actual_offset[ch] = offset[ch] = constrain(offset[ch] + direction, 0, length[ch] + padding[ch] - 1);
break;
case PADDING1:
case PADDING2:
padding[ch] = constrain(padding[ch] + direction, 0, 32 - length[ch]);
break;
case CV_DEST1:
case CV_DEST2:
cv_dest[cursor - CV_DEST1] = (EuclidXParam) constrain(cv_dest[cursor - CV_DEST1] + direction, LENGTH1, PADDING2);
break;
case 3: // CV destination
cv_dest[ch] = constrain(cv_dest[ch] + direction, 0, 5);
}
}

Expand All @@ -141,6 +167,8 @@ public:
Pack(data, PackLocation {5 * PARAM_SIZE, PARAM_SIZE}, offset[1]);
Pack(data, PackLocation {6 * PARAM_SIZE, PARAM_SIZE}, cv_dest[0]);
Pack(data, PackLocation {7 * PARAM_SIZE, PARAM_SIZE}, cv_dest[1]);
Pack(data, PackLocation {8 * PARAM_SIZE, PARAM_SIZE}, padding[0]);
Pack(data, PackLocation {9 * PARAM_SIZE, PARAM_SIZE}, padding[1]);
return data;
}

Expand All @@ -151,8 +179,10 @@ public:
actual_beats[1] = beats[1] = Unpack(data, PackLocation {3 * PARAM_SIZE, PARAM_SIZE}) + 1;
actual_offset[0] = offset[0] = Unpack(data, PackLocation {4 * PARAM_SIZE, PARAM_SIZE});
actual_offset[1] = offset[1] = Unpack(data, PackLocation {5 * PARAM_SIZE, PARAM_SIZE});
cv_dest[0] = Unpack(data, PackLocation {6 * PARAM_SIZE, PARAM_SIZE});
cv_dest[1] = Unpack(data, PackLocation {7 * PARAM_SIZE, PARAM_SIZE});
cv_dest[0] = (EuclidXParam) Unpack(data, PackLocation {6 * PARAM_SIZE, PARAM_SIZE});
cv_dest[1] = (EuclidXParam) Unpack(data, PackLocation {7 * PARAM_SIZE, PARAM_SIZE});
actual_padding[0] = padding[0] = Unpack(data, PackLocation {8 * PARAM_SIZE, PARAM_SIZE});
actual_padding[1] = padding[1] = Unpack(data, PackLocation {9 * PARAM_SIZE, PARAM_SIZE});
}

protected:
Expand All @@ -167,35 +197,36 @@ protected:

private:
int step;
int cursor = 0; // Ch1: 0=Length, 1=Hits; Ch2: 2=Length 3=Hits
int cursor = LENGTH1; // EuclidXParam
uint32_t pattern[2];

// Settings
uint8_t length[2];
uint8_t beats[2];
uint8_t offset[2];
uint8_t padding[2];
uint8_t actual_length[2];
uint8_t actual_beats[2];
uint8_t actual_offset[2];
uint8_t actual_padding[2];

uint8_t cv_dest[2];
EuclidXParam cv_dest[2] = {BEATS1, BEATS2}; // input modulation

void DrawSteps() {
//int spacing = 1;
gfxLine(0, 45, 63, 45);
gfxLine(0, 62, 63, 62);
gfxLine(0, 53, 63, 53);
gfxLine(0, 54, 63, 54);
ForEachChannel(ch) {
for (int i = 0; i < 16; i++) {
if ((pattern[ch] >> ((i + step) % actual_length[ch])) & 0x1) {
if ((pattern[ch] >> ((i + step) % (actual_length[ch]+actual_padding[ch]) )) & 0x1) {
gfxRect(4 * i + 1, 48 + 9 * ch, 3, 3);
//gfxLine(4 * i + 2, 47 + 9 * ch, 4 * i + 2, 47 + 9 * ch + 4);
} else {
gfxPixel(4 * i + 2, 47 + 9 * ch + 2);
}

if ((i + step) % actual_length[ch] == 0) {
if ((i + step) % (actual_length[ch]+actual_padding[ch]) == 0) {
//gfxLine(4 * i, 46 + 9 * ch, 4 * i, 52 + 9 * ch);
gfxLine(4 * i, 46 + 9 * ch, 4 * i, 46 + 9 * ch + 1);
gfxLine(4 * i, 52 + 9 * ch - 1, 4 * i, 52 + 9 * ch);
Expand All @@ -205,38 +236,54 @@ private:
}

void DrawEditor() {
int spacing = 18;
const int spacing = 16;

gfxBitmap(4 + 0 * spacing, 15, 8, LOOP_ICON);
gfxBitmap(4 + 1 * spacing, 15, 8, X_NOTE_ICON);
gfxBitmap(4 + 2 * spacing, 15, 8, LEFT_RIGHT_ICON);
if (cursor < CV_DEST1) {
gfxBitmap(8 + 0 * spacing, 15, 8, LOOP_ICON);
}
gfxBitmap(8 + 1 * spacing, 15, 8, X_NOTE_ICON);
gfxBitmap(8 + 2 * spacing, 15, 8, LEFT_RIGHT_ICON);
gfxPrint(8 + 3 * spacing, 15, "+");

int y = 15;
ForEachChannel (ch) {
int y = 15 + 10 * (ch + 1);
gfxPrint(4 + 0 * spacing, y, actual_length[ch]);
gfxPrint(4 + 1 * spacing, y, actual_beats[ch]);
gfxPrint(4 + 2 * spacing, y, actual_offset[ch]);

int f = cursor - ch * NUM_PARAMS;
switch (f) {
case 0:
case 1:
case 2:
gfxCursor(4 + f * spacing, y + 7, 12);
break;
case 3: // CV dest selection
gfxBitmap(1 + 3 * spacing, y, 8, CV_ICON);
break;
}
y += 10;
gfxPrint(3 + 0 * spacing + pad(10, actual_length[ch]), y, actual_length[ch]);
gfxPrint(3 + 1 * spacing + pad(10, actual_beats[ch]), y, actual_beats[ch]);
gfxPrint(3 + 2 * spacing + pad(10, actual_offset[ch]), y, actual_offset[ch]);
gfxPrint(3 + 3 * spacing + pad(10, actual_padding[ch]), y, actual_padding[ch]);

// CV assignment indicators
ForEachChannel(ch_dest) {
int ff = cv_dest[ch_dest] - (NUM_PARAMS-1)*ch;
if (ff >= 0 && ff < (NUM_PARAMS-1))
int ff = cv_dest[ch_dest] - LENGTH2*ch;
if (ff >= 0 && ff < LENGTH2)
gfxBitmap(ff * spacing, y, 3, ch_dest?SUB_TWO:SUP_ONE);
}
}

int ch = cursor < LENGTH2 ? 0 : 1;
int f = cursor - ch * LENGTH2;
y = 33;
switch (cursor) {
case LENGTH2:
case BEATS2:
case OFFSET2:
case PADDING2:
y += 10;
case LENGTH1:
case BEATS1:
case OFFSET1:
case PADDING1:
gfxCursor(3 + f * spacing, y, 13);
break;

case CV_DEST1:
case CV_DEST2:
gfxBitmap(0, 13 + (cursor - CV_DEST1)*5, 8, CV_ICON);
gfxBitmap(8, 15, 3, (cursor - CV_DEST1)? SUB_TWO : SUP_ONE);
gfxCursor(0, 19 + (cursor - CV_DEST1)*5, 11, 7);
break;
}
}
};

Expand Down
36 changes: 34 additions & 2 deletions software/o_c_REV/HemisphereApplet.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ typedef struct PackLocation {

class HemisphereApplet {
public:
static uint8_t modal_edit_mode;
static void CycleEditMode() {
++modal_edit_mode %= 3;
}

virtual const char* applet_name(); // Maximum of 9 characters
virtual void Start();
Expand Down Expand Up @@ -181,10 +185,35 @@ class HemisphereApplet {
}
}

// handle modal edit mode toggle or cursor advance
void CursorAction(int &cursor, int max) {
if (modal_edit_mode) {
isEditing = !isEditing;
} else {
cursor++;
cursor %= max + 1;
ResetCursor();
}
}
void MoveCursor(int &cursor, int direction, int max) {
cursor += direction;
if (modal_edit_mode == 2) { // wrap cursor
if (cursor < 0) cursor = max;
else cursor %= max + 1;
} else {
cursor = constrain(cursor, 0, max);
}
ResetCursor();
}
bool EditMode() {
return (isEditing || !modal_edit_mode);
}

//////////////// Offset graphics methods
////////////////////////////////////////////////////////////////////////////////
void gfxCursor(int x, int y, int w) {
if (CursorBlink()) gfxLine(x, y, x + w - 1, y);
void gfxCursor(int x, int y, int w, int h = 9) { // assumes standard text height for highlighting
if (isEditing) gfxInvert(x, y - h, w, h);
else if (CursorBlink()) gfxLine(x, y, x + w - 1, y);
}

void gfxPos(int x, int y) {
Expand Down Expand Up @@ -376,6 +405,7 @@ class HemisphereApplet {

protected:
bool hemisphere; // Which hemisphere (0, 1) this applet uses
bool isEditing = false; // modal editing toggle
const char* help[4];
virtual void SetHelp();

Expand Down Expand Up @@ -470,3 +500,5 @@ class HemisphereApplet {
bool changed_cv[2]; // Has the input changed by more than 1/8 semitone since the last read?
int last_cv[2]; // For change detection
};

uint8_t HemisphereApplet::modal_edit_mode = 0; // 0=old behavior, 1=modal editing, 2=modal with wraparound
12 changes: 7 additions & 5 deletions software/o_c_REV/bjorklund.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1334,12 +1334,14 @@ bool EuclideanFilter(uint8_t num_steps, uint8_t num_beats, uint8_t rotation, uin
return static_cast<bool>(pattern & (0x01 << clock)) ;
}

uint32_t EuclideanPattern(uint8_t num_steps, uint8_t num_beats, uint8_t rotation) {
num_beats %= (num_steps + 1);
uint32_t pattern = bjorklund_patterns[((num_steps - 2) * 33) + num_beats];
uint32_t EuclideanPattern(uint8_t num_steps, uint8_t num_beats, uint8_t rotation, uint8_t padding) {
if (num_steps < 2) num_steps = 2;
if (num_beats > num_steps) num_beats = num_steps;

uint32_t pattern = bjorklund_patterns[((num_steps - 2) * 33) + num_beats];
if (rotation) {
rotation %= num_steps;
pattern = rotl32(pattern, num_steps, rotation) ;
rotation = rotation % (num_steps + padding);
pattern = rotl32(pattern, num_steps + padding, rotation) ;
}
return pattern;
}
2 changes: 1 addition & 1 deletion software/o_c_REV/bjorklund.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@ inline uint32_t rotl32(uint32_t input, unsigned int length, unsigned int count)
}

bool EuclideanFilter(uint8_t num_steps, uint8_t num_beats, uint8_t rotation, uint32_t clock);
uint32_t EuclideanPattern(uint8_t num_steps, uint8_t num_beats, uint8_t rotation);
uint32_t EuclideanPattern(uint8_t num_steps, uint8_t num_beats, uint8_t rotation, uint8_t padding = 0);

#endif // BJORKLUND_H_

0 comments on commit 5c598ab

Please sign in to comment.