How would I modify a short data type array into a long data type array?

Hi,

I am new to Arduino and programming and I am trying to modify a sketch that initially required a short data type array. The modification I want to make to the sketch I believe, requires a long data type array.

The modification I need is to add an additional two channels to the sketch. Currently the sketch has 15 (1 for an accent track, 14 for drum keys).

I would need to make it 17 (1 for an accent track, 16 for drum keys). Making it 17 requires a larger data type, i.e. going from 16bit(short) to 32bit(long).

How would I modify this sketch to accommodate the additional two channels?

So far I have updated the following:

#define EUCL_TRACKS from 15 to 17

added two additional drum keys as needed

byte dkeys[]={0,36,37,38,39,42,45,46,48,49,50,51,54,56,69};

added two additional CCs as needed

byte dcontrol[]={96,97,102,103,104,105,106,107,108,109,110,111,112,113,114};

I believe the array that needs to be a long data type:

short drumloop[]={0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };

Since 0-15 = 16bits, I have hit the max for this short datatype array.

What do I need to do to accommodate the 18bits (0-17) in the above array?

Currently when I run the sketch all the drum tracks respond EXCEPT the last one, meaning it is out of range. I really appreciate any support on figuring this out.

The sketch is below:

#include <RK002.h>

#define APP_NAME "16CH Euclidean Drums"
#define APP_AUTHOR "mmryspace"
#define APP_VERSION "0.1"
#define APP_GUID "78183b0f-f95a-45c8-9fc1-2d547664d541"

#define EUCL_TRACKS 17

/* 
 * 
 * This sketch plays a set of keys (preset to GM keys) via the Euclidean calculations.
 * 
 * Euclidean rhythms are controllable in external play with CCs:
 * 96,97,102,103,104,105,106,107,108,109,110,111,112,113,114
 * CC 96 will control the euclidean velocity accent instead of triggering a key
 * 
 * keynumbers triggered are: 36,37,38,39,42,45,46,48,49,50,51,54,56,69
 * 
 * By default, if the sequencer is running, pressing a drum key will mute/unmute the pattern for that sound.
 * 
 * ONLINE PARAMETERS:
 * CHANNEL   = MIDI channel on which the Euclid is operating
 * LOOP      = loop length of the euclid pattern.
 * TEMPOTHRU = forward tempo/transport messages from RK002 in to out (def = on)
 * 
 */
 
RK002_DECLARE_INFO(APP_NAME,APP_AUTHOR,APP_VERSION,APP_GUID)
RK002_DECLARE_PARAM(CHANNEL,1,1,16,10) 
RK002_DECLARE_PARAM(LOOP,1,1,32,16) // default value of 16 can be adjusted, e.g. to 32 
RK002_DECLARE_PARAM(TEMPOTHRU,1,0,1,1)

// Keys for each channel, matched to the drum machine
//  0 -> not a drum key but is an accent track
byte dkeys[]={0,36,37,38,39,42,45,46,48,49,50,51,54,56,69};

// Euclid CCs for each channel, first one (96) is for the accent track
byte dcontrol[]={96,97,102,103,104,105,106,107,108,109,110,111,112,113,114};

// a series of 16bit numbers to hold the triggers
short drumloop[]={0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
short drum_active=0;
short muteChannel=0xffff;
short isPatterned=0;

byte dChannel=9;
byte seq_position=0;
bool drum_run=false;
byte ppqn=0;

//handler triggered on note on message:

bool RK002_onStop(){
  drum_run=false;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}
bool RK002_onClock(){
  advanceDrum();
  ppqn++;
  ppqn=ppqn%6;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}
bool RK002_onStart(){
  ppqn=0;
  seq_position=0;
  drum_run=true;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}
bool RK002_onContinue(){
  drum_run=!drum_run;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}

bool RK002_onControlChange(byte channel,byte number,byte val){
  bool retval=true;
  if(channel==dChannel){ // are we in euclidean set channel?
    for (byte i=0;i<EUCL_TRACKS;i++){ // traverse the CC's
      // euclid control
      if(number==dcontrol[i]){ // match the cc
        if(val>0){ // is there activity
          bitSet(isPatterned,i);          // euclidean pattern controls this
        }else{
          bitSet(muteChannel,i); // pattern is cleared: note key can just control the sound
          bitClear(isPatterned,i);        // reset trig flag for this one
        }
        Euclid(i,int((val+1)/8)); // make the pattern based on CC val
        retval=false;
      }
    }
  }
  return retval;
}

// key to mute (NOTE: comment out this function to disable muting)

bool RK002_onNoteOn(byte channel,byte number,byte value){
  bool retval=true;
  // euclid
  if(channel==dChannel){ // in designated MIDI channel?
    for (byte i=0 ; i<EUCL_TRACKS ; i++){
      if(dkeys[i] == number){
        if(drum_run){
          // key is in programmed range and sequencer is running
          if(bitRead(isPatterned,i)){ // is a defined key and patterned?
            if(bitRead(muteChannel,i)){        // is it not muted?
              bitClear(muteChannel,i);         // mute (0 = muted)
            }else{
              //RK002_sendNoteOff(channel,number,value);
              bitSet(muteChannel,i);           // unmute
            }
            retval=false;
          }
        }
      }
    }
  }
  return retval;
}
bool RK002_onNoteOff(byte channel,byte number,byte value){
  bool retval=true;

  if(channel==dChannel){ // in designated MIDI channel?
    for (byte i=0 ; i<EUCL_TRACKS ; i++){
      if(dkeys[i]==number){
        if(drum_run){
          // key is in programmed range and sequencer is running
          if(bitRead(isPatterned,i)){ // is a defined key and patterned?
            if(!bitRead(muteChannel,i)) retval=false; //  muted: do not play off
          }
        }
      }
    }
  }
  return retval;
}
void advanceDrum(){
  byte n;
  bool drum_trigger;
  if(ppqn==0){
    // skip n = 0 - this is an accent track
    for(n=1;n<EUCL_TRACKS;n++){
      drum_trigger=bitRead(drumloop[seq_position],n);
      if(drum_trigger && drum_run && bitRead(muteChannel,n)){
        // bit 15 = velocity seq
        RK002_sendNoteOn(dChannel, dkeys[n], 99+bitRead(drumloop[seq_position],0)*28);//veloLoop[seq_position]);
        bitSet(drum_active,n);
      }
    }
    seq_position++;
    seq_position=seq_position % RK002_paramGet(LOOP);//sizeof(drumloop);
  }else if (ppqn==1){
    for(n=1;n<EUCL_TRACKS;n++){
      drum_trigger=bitRead(drum_active,n);
      if(drum_trigger){
        RK002_sendNoteOff(dChannel, dkeys[n], 100);
        bitClear(drum_active,n);
      }
    }
  }
}


void Euclid(byte drumBit, byte trigs) {
  byte cur = RK002_paramGet(LOOP);
  for (byte i=0; i<RK002_paramGet(LOOP); i++) {
    if (cur >= RK002_paramGet(LOOP)) {
      cur -= RK002_paramGet(LOOP);
      bitSet(drumloop[i],drumBit);
    } 
    else {
      bitClear(drumloop[i],drumBit);
    }
    if ((trigs==0) && (drumBit<RK002_paramGet(LOOP))) bitClear(drumloop[i],drumBit);
    cur += trigs;
  }
}

void updateParams()
{
  // convert readable channels 1-16 to internal 0-15
  dChannel = RK002_paramGet(CHANNEL) - 1;
}

void RK002_onParamChange(unsigned param_nr, int val){
  updateParams();
}
// initialize
void setup()
{
    //RK002_clockSetTempo(1200);
    RK002_clockSetMode(0); // auto clock mode (will be clock slaved)
    updateParams();
}

// the main loop
void loop()
{
}

Have you tried "long" type?

MIDI only has 16 tracks, you cannot make it 17.

Why do you need so many tracks? Can't you just use different notes on the same track?

Pieter

arduino_new:
Have you tried "long" type?

I have tried "long" type. With the long type, only some of the tracks are responsive but they are now out of order. the dkeys and dcontrol arrays are supposed to correspond to one another in the order they are in their respective arrays. Now the last four tracks don't respond at all.

PieterP:
MIDI only has 16 tracks, you cannot make it 17.

Why do you need so many tracks? Can't you just use different notes on the same track?

Pieter

You are correct, but these tracks don't correlate to MIDI channels. The Euclidean Generator runs on one MIDI channel: MIDI channel 10. RK002_DECLARE_PARAM(CHANNEL,1,1,16,10)

The "tracks" in this case are the various drum voices from a 16 pad drum machine and they are CC mapped to a MIDI controller where the sketch can generate various euclidean rhythms on MIDI channel 10

Here is the Error information I have received when changing the data type to "long" for

drumloop[]={0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };

in case it is helpful along with the above info:

In function 'bool RK002_onNoteOn(byte, byte, byte)':
106:17: warning: iteration 15u invokes undefined behavior [-Waggressive-loop-optimizations]
       if(dkeys[i] == number){
                 ^
105:5: note: containing loop
     for (byte i=0 ; i<EUCL_TRACKS ; i++){
     ^
In function 'bool RK002_onNoteOff(byte, byte, byte)':
129:17: warning: iteration 15u invokes undefined behavior [-Waggressive-loop-optimizations]
       if(dkeys[i]==number){
                 ^
128:5: note: containing loop
     for (byte i=0 ; i<EUCL_TRACKS ; i++){
     ^
In function 'bool RK002_onControlChange(byte, byte, byte)':
84:28: warning: iteration 15u invokes undefined behavior [-Waggressive-loop-optimizations]
       if(number==dcontrol[i]){ // match the cc
                            ^
82:5: note: containing loop
     for (byte i=0;i<EUCL_TRACKS;i++){ // traverse the CC's
     ^
Sketch uses 5216 bytes (36%) of program storage space. Maximum is 14336 bytes.
Global variables use 736 bytes (17%) of dynamic memory, leaving 3360 bytes for local variables. Maximum is 4096 bytes.

It has nothing to do with the data type.

How many elements does "dkeys" have? What elements are you trying to read in that loop?

#define EUCL_TRACKS 17

dkeys[] and dcontrol[] do not have enough elements to accept an index of 0-16.

cattledog:

#define EUCL_TRACKS 17

dkeys[] and dcontrol[] do not have enough elements to accept an index of 0-16.

PieterP:
It has nothing to do with the data type.

How many elements does "dkeys" have? What elements are you trying to read in that loop?

PieterP:
It has nothing to do with the data type.

How many elements does "dkeys" have? What elements are you trying to read in that loop?

Ooops - My apologies. I accidentally provided the original 15 channel sketch, hence the error messages. Below is the code I meant to share. However, when I try using the "long" data type with the drumloop array, the dkeys no longer correspond with the dcontrol as they are ordered in the arrays. Of the total 16 CCs in dcontrol array, the last two (115,116) do not respond and 114 triggers a sound but once it is triggered, continues to play even when knob is turned back to zero.

#include <RK002.h>

#define APP_NAME "16CH Euclidean Drums"
#define APP_AUTHOR "mmryspace"
#define APP_VERSION "0.1"
#define APP_GUID "78183b0f-f95a-45c8-9fc1-2d547664d541"

#define EUCL_TRACKS 17

/* 
 * 
 * This sketch plays a set of keys (preset to GM keys) via the Euclidean calculations.
 * 
 * Euclidean rhythms are controllable in external play with CCs
 * 
 * By default, if the sequencer is running, pressing a drum key will mute/unmute the pattern for that sound.
 * 
 * ONLINE PARAMETERS:
 * CHANNEL   = MIDI channel on which the Euclidean drums are operating
 * LOOP      = loop length of the Euclidean pattern
 * TEMPOTHRU = forward tempo/transport messages from RK002 in to out (def = on)
 * 
 */
 
RK002_DECLARE_INFO(APP_NAME,APP_AUTHOR,APP_VERSION,APP_GUID)
RK002_DECLARE_PARAM(CHANNEL,1,1,16,10) 
RK002_DECLARE_PARAM(LOOP,1,1,32,16) // default value of 16 can be adjusted, e.g. to 32 
RK002_DECLARE_PARAM(TEMPOTHRU,1,0,1,1)

// Keys for each Euclidean channel that the CCs (dcontrol) will control
// 0 -> this is not a drum key but is an accent track
byte dkeys[]={0,36,38,42,46,50,48,45,41,37,39,56,44,49,57,53,51};

// Euclidean CCs for each channel 
// 96 -> this CC is for the accent track (0)
byte dcontrol[]={96,97,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116};
 
long  drumloop[]={0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
short drum_active=0;
short muteChannel=0xffff;
short isPatterned=0;

byte dChannel=9;
byte seq_position=0;
bool drum_run=false;
byte ppqn=0;

//handler triggered on note on message:

bool RK002_onStop(){
  drum_run=false;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}
bool RK002_onClock(){
  advanceDrum();
  ppqn++;
  ppqn=ppqn%6;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}
bool RK002_onStart(){
  ppqn=0;
  seq_position=0;
  drum_run=true;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}
bool RK002_onContinue(){
  drum_run=!drum_run;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}

bool RK002_onControlChange(byte channel,byte number,byte val){
  bool retval=true;
  // euclidean
  if(channel==dChannel){ // are we in euclidean set channel?
    for (byte i=0;i<EUCL_TRACKS;i++){ // traverse the CC's
      // euclid control
      if(number==dcontrol[i]){ // match the cc
        if(val>0){ // is there activity
          bitSet(isPatterned,i);          // euclidean pattern controls this
        }else{
          bitSet(muteChannel,i); // pattern is cleared: note key can just control the sound
          bitClear(isPatterned,i);        // reset trig flag for this one
        }
        Euclid(i,int((val+1)/8)); // make the pattern based on CC val
        retval=false;
      }
    }
  }
  return retval;
}

// key to mute (NOTE: comment out this function to disable muting)

bool RK002_onNoteOn(byte channel,byte number,byte value){
  bool retval=true;
  // euclidean
  if(channel==dChannel){ // are we in designated MIDI channel?
    for (byte i=0 ; i<EUCL_TRACKS ; i++){
      if(dkeys[i] == number){
        if(drum_run){
          // key is in programmed range and sequencer is running
          if(bitRead(isPatterned,i)){ // is a defined key and patterned?
            if(bitRead(muteChannel,i)){        // is it not muted?
              bitClear(muteChannel,i);         // mute (0 = muted)
            }else{
              //RK002_sendNoteOff(channel,number,value);
              bitSet(muteChannel,i);           // unmute
            }
            retval=false;
          }
        }
      }
    }
  }
  return retval;
}
bool RK002_onNoteOff(byte channel,byte number,byte value){
  bool retval=true;

  if(channel==dChannel){ // in designated MIDI channel?
    for (byte i=0 ; i<EUCL_TRACKS ; i++){
      if(dkeys[i]==number){
        if(drum_run){
          // key is in programmed range and sequencer is running
          if(bitRead(isPatterned,i)){ // is a defined key and patterned?
            if(!bitRead(muteChannel,i)) retval=false; //  muted: do not play off
          }
        }
      }
    }
  }
  return retval;
}
void advanceDrum(){
  byte n;
  bool drum_trigger;
  if(ppqn==0){
    // skip n = 0 - this is an accent track
    for(n=1;n<EUCL_TRACKS;n++){
      drum_trigger=bitRead(drumloop[seq_position],n);
      if(drum_trigger && drum_run && bitRead(muteChannel,n)){
        // bit 15 = velocity seq
        RK002_sendNoteOn(dChannel, dkeys[n], 99+bitRead(drumloop[seq_position],0)*28);//veloLoop[seq_position]);
        bitSet(drum_active,n);
      }
    }
    seq_position++;
    seq_position=seq_position % RK002_paramGet(LOOP);//sizeof(drumloop);
  }else if (ppqn==1){
    for(n=1;n<EUCL_TRACKS;n++){
      drum_trigger=bitRead(drum_active,n);
      if(drum_trigger){
        RK002_sendNoteOff(dChannel, dkeys[n], 100);
        bitClear(drum_active,n);
      }
    }
  }
}


void Euclid(byte drumBit, byte trigs) {
  byte cur = RK002_paramGet(LOOP);
  for (byte i=0; i<RK002_paramGet(LOOP); i++) {
    if (cur >= RK002_paramGet(LOOP)) {
      cur -= RK002_paramGet(LOOP);
      bitSet(drumloop[i],drumBit);
    } 
    else {
      bitClear(drumloop[i],drumBit);
    }
    if ((trigs==0) && (drumBit<RK002_paramGet(LOOP))) bitClear(drumloop[i],drumBit);
    cur += trigs;
  }
}

void updateParams()
{
  // convert readable channels 1-16 to internal 0-15
  dChannel = RK002_paramGet(CHANNEL) - 1;
}

void RK002_onParamChange(unsigned param_nr, int val){
  updateParams();
}
// initialize
void setup()
{
    //RK002_clockSetTempo(1200);
    RK002_clockSetMode(0); // auto clock mode (will be clock slaved)
    updateParams();
}

// the main loop
void loop()
{
}

Shouldn't "drum_active" be 32 bits as well?

I think that uint16_t and uint32_t are clearer than short and long here.

Shouldn't "drum_active" be 32 bits as well?

short isPatterned=0;
short muteChannel=0xffff;

The bits in these 16 bit variables are index numbered 0 thru 15

There are several places in the code like this where i is used with a value greater than 15 in relation to the bits of these variables. Check all the places in the code where bitSet(), bitClear(), and bitRead() are used.

bool RK002_onNoteOn(byte channel,byte number,byte value){
  bool retval=true;
  // euclidean
  if(channel==dChannel){ // are we in designated MIDI channel?
    for (byte i=0 ; i<EUCL_TRACKS ; i++){
      if(dkeys[i] == number){
        if(drum_run){
          // key is in programmed range and sequencer is running
          if(bitRead(isPatterned,i)){ // is a defined key and patterned?
            if(bitRead(muteChannel,i)){        // is it not muted?
              bitClear(muteChannel,i);         // mute (0 = muted)
            }else{
              //RK002_sendNoteOff(channel,number,value);
              bitSet(muteChannel,i);           // unmute
            }
            retval=false;
          }
        }
      }
    }
  }
  return retval;
}

I would think that all these uint16_t variables which get indexed with i should also be uint32_t.

cattledog:

short isPatterned=0;

short muteChannel=0xffff;




The bits in these 16 bit variables are index numbered 0 thru 15

There are several places in the code like this where i is used with a value greater than 15 in relation to the bits of these variables. Check all the places in the code where bitSet(), bitClear(), and bitRead() are used.



bool RK002_onNoteOn(byte channel,byte number,byte value){
  bool retval=true;
  // euclidean
  if(channel==dChannel){ // are we in designated MIDI channel?
    for (byte i=0 ; i<EUCL_TRACKS ; i++){
      if(dkeys[i] == number){
        if(drum_run){
          // key is in programmed range and sequencer is running
          if(bitRead(isPatterned,i)){ // is a defined key and patterned?
            if(bitRead(muteChannel,i)){        // is it not muted?
              bitClear(muteChannel,i);        // mute (0 = muted)
            }else{
              //RK002_sendNoteOff(channel,number,value);
              bitSet(muteChannel,i);          // unmute
            }
            retval=false;
          }
        }
      }
    }
  }
  return retval;
}




I would think that all these uint16_t variables which get indexed with i should also be uint32_t.

Ok, this is starting to make more sense - thank you to everyone who has taken the time to look into this issue. I am going to modify the sketch and report back with results. Very much appreciated!

Here is my updated code below and I am still having issues with the sketch even though I have modified the "short" variables to "long".

Here is the current behavior: the dkeys array is being read at index 3 so the accent track (0) and the first two drum sounds (36, 38) are out of range in relation to

byte dcontrol[]={96,97,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116};

In other words, everything is getting shifted / pushed forward out of range.

Another symptom is that in the dkeys array, index 16 activates a sound but will not stop making a sound. You can increase it's velocity to its fullest extent but when you return it to 0 it continues to play and cannot be muted which is the expected behavior.

I tried both unsigned long and long in these variables yet something is still incorrect. I could certainly use more experienced eyes on what I might be missing, i.e. why is it not indexing the array at the beginning as it should and how would I go about resolving this in the sketch?

#include <RK002.h>

#define APP_NAME "16CH Euclidean Drums"
#define APP_AUTHOR "mmryspace"
#define APP_VERSION "0.1"
#define APP_GUID "78183b0f-f95a-45c8-9fc1-2d547664d541"

#define EUCL_TRACKS 17

/* 
 * 
 * This sketch plays a set of keys (preset to GM keys) via the Euclidean calculations.
 * 
 * Euclidean rhythms are controllable in external play with CCs
 * 
 * By default, if the sequencer is running, pressing a drum key will mute/unmute the pattern for that sound.
 * 
 * ONLINE PARAMETERS:
 * CHANNEL   = MIDI channel on which the Euclidean drums are operating
 * LOOP      = loop length of the Euclidean pattern
 * TEMPOTHRU = forward tempo/transport messages from RK002 in to out (def = on)
 * 
 */
 
RK002_DECLARE_INFO(APP_NAME,APP_AUTHOR,APP_VERSION,APP_GUID)
RK002_DECLARE_PARAM(CHANNEL,1,1,16,10) 
RK002_DECLARE_PARAM(LOOP,1,1,32,16) // default value of 16 can be adjusted, e.g. to 32 
RK002_DECLARE_PARAM(TEMPOTHRU,1,0,1,1)

// Keys for each Euclidean channel that the CCs (dcontrol) will control
// 0 -> this is not a drum key but is an accent track
byte dkeys[]={0,36,38,42,46,50,48,45,41,37,39,56,44,49,57,53,51};

// Euclidean CCs for each channel 
// 96 -> this CC is for the accent track (0)
byte dcontrol[]={96,97,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116};

// a series of 32bit numbers to hold the triggers -- do these variables need to be unsigned long or just long?
long  drumloop[]={0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
long  drum_active=0;
long  muteChannel=0xffffffff;
long  isPatterned=0;

byte dChannel=9;
byte seq_position=0;
bool drum_run=false;
byte ppqn=0;

//handler triggered on note on message:

bool RK002_onStop(){
  drum_run=false;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}
bool RK002_onClock(){
  advanceDrum();
  ppqn++;
  ppqn=ppqn%6;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}
bool RK002_onStart(){
  ppqn=0;
  seq_position=0;
  drum_run=true;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}
bool RK002_onContinue(){
  drum_run=!drum_run;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}

bool RK002_onControlChange(byte channel,byte number,byte val){
  bool retval=true;
  // euclidean
  if(channel==dChannel){ // are we in euclidean set channel?
    for (byte i=0;i<EUCL_TRACKS;i++){ // traverse the CC's
      // euclid control
      if(number==dcontrol[i]){ // match the cc
        if(val>0){ // is there activity
          bitSet(isPatterned,i);          // euclidean pattern controls this
        }else{
          bitSet(muteChannel,i); // pattern is cleared: note key can just control the sound
          bitClear(isPatterned,i);        // reset trig flag for this one
        }
        Euclid(i,int((val+1)/8)); // make the pattern based on CC val
        retval=false;
      }
    }
  }
  return retval;
}

// key to mute (NOTE: comment out this function to disable muting)

bool RK002_onNoteOn(byte channel,byte number,byte value){
  bool retval=true;
  // euclidean
  if(channel==dChannel){ // are we in designated MIDI channel?
    for (byte i=0 ; i<EUCL_TRACKS ; i++){
      if(dkeys[i] == number){
        if(drum_run){
          // key is in programmed range and sequencer is running
          if(bitRead(isPatterned,i)){ // is a defined key and patterned?
            if(bitRead(muteChannel,i)){        // is it not muted?
              bitClear(muteChannel,i);         // mute (0 = muted)
            }else{
              //RK002_sendNoteOff(channel,number,value);
              bitSet(muteChannel,i);           // unmute
            }
            retval=false;
          }
        }
      }
    }
  }
  return retval;
}
bool RK002_onNoteOff(byte channel,byte number,byte value){
  bool retval=true;

  if(channel==dChannel){ // in designated MIDI channel?
    for (byte i=0 ; i<EUCL_TRACKS ; i++){
      if(dkeys[i]==number){
        if(drum_run){
          // key is in programmed range and sequencer is running
          if(bitRead(isPatterned,i)){ // is a defined key and patterned?
            if(!bitRead(muteChannel,i)) retval=false; //  muted: do not play off
          }
        }
      }
    }
  }
  return retval;
}
void advanceDrum(){
  byte n;
  bool drum_trigger;
  if(ppqn==0){
    // skip n = 0 - this is an accent track
    for(n=1;n<EUCL_TRACKS;n++){
      drum_trigger=bitRead(drumloop[seq_position],n);
      if(drum_trigger && drum_run && bitRead(muteChannel,n)){
        // bit 17 = velocity seq?
        RK002_sendNoteOn(dChannel, dkeys[n], 99+bitRead(drumloop[seq_position],0)*28); //veloLoop[seq_position]);if bit=0 ; resulting value = 0. if bit = 1 then resulting value = 28. these values are then added to 99 so resulting velocity is either 99 or 127.            
        bitSet(drum_active,n);
      }
    }
    seq_position++;
    seq_position=seq_position % RK002_paramGet(LOOP);//sizeof(drumloop);
  }else if (ppqn==1){
    for(n=1;n<EUCL_TRACKS;n++){
      drum_trigger=bitRead(drum_active,n);
      if(drum_trigger){
        RK002_sendNoteOff(dChannel, dkeys[n], 100);
        bitClear(drum_active,n);
      }
    }
  }
}


void Euclid(byte drumBit, byte trigs) {
  byte cur = RK002_paramGet(LOOP);
  for (byte i=0; i<RK002_paramGet(LOOP); i++) {
    if (cur >= RK002_paramGet(LOOP)) {
      cur -= RK002_paramGet(LOOP);
      bitSet(drumloop[i],drumBit);
    } 
    else {
      bitClear(drumloop[i],drumBit);
    }
    if ((trigs==0) && (drumBit<RK002_paramGet(LOOP))) bitClear(drumloop[i],drumBit);
    cur += trigs;
  }
}

void updateParams()
{
  // convert readable channels 1-16 to internal 0-15
  dChannel = RK002_paramGet(CHANNEL) - 1;
}

void RK002_onParamChange(unsigned param_nr, int val){
  updateParams();
}
// initialize
void setup()
{
    //RK002_clockSetTempo(1200);
    RK002_clockSetMode(0); // auto clock mode (will be clock slaved)
    updateParams();
}

// the main loop
void loop()
{
}

I must admit that I don't really understand your code or the problem statement very well.

On thing I do see in the program is the use of array index and variable bit index together.

For example here:

for(n=1;n<EUCL_TRACKS;n++){
      drum_trigger=bitRead(drum_active,n);
      if(drum_trigger){
        RK002_sendNoteOff(dChannel, dkeys[n], 100);
        bitClear(drum_active,n);
      }
    }

The array elements are indexed left to right, and the bits of a variable are indexed from right to left. That is the rightmost bit (lsb) is bit 0. Perhaps this inversion of indexes between the arrays and the variables is an issue in your code.

cattledog:
I must admit that I don't really understand your code or the problem statement very well.

On thing I do see in the program is the use of array index and variable bit index together.

For example here:

for(n=1;n<EUCL_TRACKS;n++){

drum_trigger=bitRead(drum_active,n);
      if(drum_trigger){
        RK002_sendNoteOff(dChannel, dkeys[n], 100);
        bitClear(drum_active,n);
      }
    }




The array elements are indexed left to right, and the bits of a variable are indexed from right to left. That is the rightmost bit (lsb) is bit 0. Perhaps this inversion of indexes between the arrays and the variables is an issue in your code.

My apologies - I am a novice so I have probably stated the problem incorrectly. The original sketch allowed for a MIDI controller to generate euclidean rhythms on a connected drum machine across 14 tracks with each track also having access to an accent track which is an additional track. 15 total tracks. So when you turned a knob on the controller, it generates a euclidean rhythm for each drum sound. Turn a knob, generate a rhythm for that particular drum sound. The numbers in the dkeys array corresponds with MIDI key #s, so for example 36 is the bass drum. So in the dcontrol array, 97 would be the corresponding CC or "continuous controller" message that increases or decreases the euclidean rhythm for that particular drum sound. The numbers in the dkeys array is ordered to match the layout of the drum machine and MIDI controller, which is why it does not go sequentially from 0-57.

If this is helpful, the original sketch appeared to work fine with only 15 total tracks, but then when I wanted to try and modify the sketch for a drum machine that has two more drum sounds (tracks), that is, two this is where I started experiencing trouble. The original sketch was written for 16bit datatype to account for the 15 total tracks but now I have a total of 17 tracks. 0-15 = 16bits, the max for the short datatype. So it was suggested here that I modify these variables from short to long:

// a series of 32bit numbers to hold the triggers -- do these variables need to be unsigned long or just long?
unsigned long  drumloop[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
unsigned long  drum_active=0;
unsigned long  muteChannel=0xffffffff; // 1111 1111 1111 1111 1111 1111 1111 1111
unsigned long  isPatterned=0;

If the code worked in the short datatype with just 15 tracks, I wonder if what you are saying about the inversion of indexes still factor into being an issue with a different datatype and two additional tracks?

I am a complete novice, but I do understand what you are saying about the inversion.

To clarify: are you saying that the dkeys array elements may need to be reordered to reflect the rightmost bit (lsb) rule? Thanks again for all the help so far.

Thanks for the additional information. I think you have explained it well all along, but I'm just not familiar with the MIDI controller and how it all comes together. I did get to learn about Euclidean Rhythms which were new to me, and I spent some time this week down the Google/You Tube rabbit hole.

I am a complete novice, but I do understand what you are saying about the inversion.
To clarify: are you saying that the dkeys array elements may need to be reordered to reflect the rightmost bit (lsb) rule? Thanks again for all the help so far.

You can either reorder the elements, or you can invert the indexes when you access a number.

For example with an n bit number and you want to access bit y from the right ( i.e y...2,1,0)and depending on its value use the corresponding element which is y from the right in an n element array it would be element[n-1-y]

Whichever method makes more sense to you and leaves your head spinning less :wink:

cattledog:
Thanks for the additional information. I think you have explained it well all along, but I'm just not familiar with the MIDI controller and how it all comes together. I did get to learn about Euclidean Rhythms which were new to me, and I spent some time this week down the Google/You Tube rabbit hole.

You can either reorder the elements, or you can invert the indexes when you access a number.

For example with an n bit number and you want to access bit y from the right ( i.e y...2,1,0)and depending on its value use the corresponding element which is y from the right in an n element array it would be element[n-1-y]

Whichever method makes more sense to you and leaves your head spinning less :wink:

So glad this problem introduced you to Euclidean rhythms! They can be a lot of fun for music creation. Here is a quick video demonstrating exactly how this sketch works with the device I am writing the sketch for (the orange MIDI cable) a MIDI controller and a drum machine(with fewer tracks: 8 instead of 16): https://www.youtube.com/watch?v=kZlfjPekw8Y

Here is the original sketch I have been trying to modify: RK002 DDD-1 Euclid - Pastebin.com
You can see in the short arrays in question where the array elements are indexed left to right, and the bits of a variable are indexed from right to left. This works in the short datatype realm. I guess I am curious why it wouldn't work when I move to a long datatype, i.e. 16bit -> 32bit?

I think I am going to start by reordering the array elements and see what happens and will certainly report back asap. Thanks again for all the help so far.

UPDATE: I tried reordering the dkeys array as suggested.. The sketch exhibits almost the same behavior as before: the last 3 drum sounds (dkeys) still do not play. In other words, the last 3 dkeys elements do not work in relation to their associated dcontrol element, i.e. no activity when you turn the knobs.

It seems like everything in the dkeys array is still getting shifted forward.

byte dkeys[]={0,36,38,42,46,50,48,45,41,37,39,56,44,49,57,53,51};

So in the above array: the dkeys array is being read starting at array index 3, so I believe the accent track (0) and the first two drum sounds (36, 38) are out of range. So on the MIDI controller, knob #1 that is supposed to control the dkey 36, now controls dkey 42. As a result, every knob (dcontrol) is shifted three places so the last three knobs do nothing.

UPDATE: I tried reordering the dkeys array as suggested.. The sketch exhibits almost the same behavior as before: the last 3 drum sounds (dkeys) still do not play. In other words, the last 3 dkeys elements do not work in relation to their associated dcontrol element, i.e. no activity when you turn the knobs.

I am running out of ideas. There is a lot of the sketch going on behind the scenes inside the library code and with the hardware. I'm afraid there may be something there which limits you to 16 elements.

Is there any way to get some Serial printing patched into your sketch? You may be able to figure out what is going on with the dkeys and dcontrol arrays and the indexing with some debug prints.

cattledog:
I am running out of ideas. There is a lot of the sketch going on behind the scenes inside the library code and with the hardware. I'm afraid there may be something there which limits you to 16 elements.

Is there any way to get some Serial printing patched into your sketch? You may be able to figure out what is going on with the dkeys and dcontrol arrays and the indexing with some debug prints.

Hi, I have been in touch with the developer and I confirmed that there is not a hardware limitation. I have been able to ALMOST get this sketch to work with one exception. The developer suggested I use the INT datatype for the variables in question. The updated code is below. The one thing that is not working is that the last dkey element in the array (51) which corresponds to the last dcontrol array element (116). The behavior is that once you start turning the knob (dcontrol 116) for dkey 51, the euclidean generator engages and works with no issue but when you try to return the knob back to 0, i.e. turn off, it continues to play at its lowest triggered setting. It is supposed to stop playing altogether and the all of the other drum tracks (dkeys) do this. There is something about that last array element still causing an issue. The developer suggested that there might be another variable that might not be big enough, i.e. needs a datatype to accommodate this sketch but I have not been able to track this down yet and resolve the issue. Perhaps it is something in the indexing that is missing a flag to turn off the last dcontrol element in the array?

To your question about serial printing, there is a debugging function: https://www.retrokits.com/rk002-duy/5/

I am not sure how I would implement it in this sketch to get the desired info?

Here is the ALMOST working sketch:

#include <RK002.h>

#define APP_NAME "16CH Euclidean Drums"
#define APP_AUTHOR "mmryspace"
#define APP_VERSION "0.1"
#define APP_GUID "78183b0f-f95a-45c8-9fc1-2d547664d541"

#define EUCL_TRACKS 17

/* 
 * 
 * This sketch plays a set of keys (preset to GM keys) via the Euclidean calculations.
 * 
 * Euclidean rhythms are controllable in external play with CCs
 * 
 * By default, if the sequencer is running, pressing a drum key will mute/unmute the pattern for that sound.
 * 
 * ONLINE PARAMETERS:
 * CHANNEL   = MIDI channel on which the Euclidean drums are operating
 * LOOP      = loop length of the Euclidean pattern
 * TEMPOTHRU = forward tempo/transport messages from RK002 in to out (def = on)
 * 
 */
 
RK002_DECLARE_INFO(APP_NAME,APP_AUTHOR,APP_VERSION,APP_GUID)
RK002_DECLARE_PARAM(CHANNEL,1,1,16,10) 
RK002_DECLARE_PARAM(LOOP,1,1,32,16) // default value of 16 can be adjusted, e.g. to 32 
RK002_DECLARE_PARAM(TEMPOTHRU,1,0,1,1)

// Keys for each Euclidean channel that the CCs (dcontrol) will control
// 0 -> this is not a drum key but is an accent track
byte dkeys[]={0,36,38,42,46,50,48,45,41,37,39,56,44,49,57,53,51};

// Euclidean CCs for each channel 
// 96 -> this CC is for the accent track (0)
byte dcontrol[]={96,97,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116};

// a series of 32bit numbers to hold the triggers
int  drumloop[]={0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
int  drum_active=0;
int  muteChannel=0xffffffff;
int  isPatterned=0;

byte dChannel=9;
byte seq_position=0;
bool drum_run=false;
byte ppqn=0;

//handler triggered on note on message:

bool RK002_onStop(){
  drum_run=false;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}
bool RK002_onClock(){
  advanceDrum();
  ppqn++;
  ppqn=ppqn%6;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}
bool RK002_onStart(){
  ppqn=0;
  seq_position=0;
  drum_run=true;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}
bool RK002_onContinue(){
  drum_run=!drum_run;
  if(RK002_paramGet(TEMPOTHRU)) return true;
  return false;
}

bool RK002_onControlChange(byte channel,byte number,byte val){
  bool retval=true;
  // euclidean
  if(channel==dChannel){ // are we in euclidean set channel?
    for (byte i=0;i<EUCL_TRACKS;i++){ // traverse the CC's
      // euclid control
      if(number==dcontrol[i]){ // match the cc
        if(val>0){ // is there activity
          bitSet(isPatterned,i);          // euclidean pattern controls this
        }else{
          bitSet(muteChannel,i); // pattern is cleared: note key can just control the sound
          bitClear(isPatterned,i);        // reset trig flag for this one
        }
        Euclid(i,int((val+1)/8)); // make the pattern based on CC val
        retval=false;
      }
    }
  }
  return retval;
}

// key to mute (NOTE: comment out this function to disable muting)

bool RK002_onNoteOn(byte channel,byte number,byte value){
  bool retval=true;
  // euclidean
  if(channel==dChannel){ // are we in designated MIDI channel?
    for (byte i=0 ; i<EUCL_TRACKS ; i++){
      if(dkeys[i] == number){
        if(drum_run){
          // key is in programmed range and sequencer is running
          if(bitRead(isPatterned,i)){ // is a defined key and patterned?
            if(bitRead(muteChannel,i)){        // is it not muted?
              bitClear(muteChannel,i);         // mute (0 = muted)
            }else{
              //RK002_sendNoteOff(channel,number,value);
              bitSet(muteChannel,i);           // unmute
            }
            retval=false;
          }
        }
      }
    }
  }
  return retval;
}
bool RK002_onNoteOff(byte channel,byte number,byte value){
  bool retval=true;

  if(channel==dChannel){ // in designated MIDI channel?
    for (byte i=0 ; i<EUCL_TRACKS ; i++){
      if(dkeys[i]==number){
        if(drum_run){
          // key is in programmed range and sequencer is running
          if(bitRead(isPatterned,i)){ // is a defined key and patterned?
            if(!bitRead(muteChannel,i)) retval=false; //  muted: do not play off
          }
        }
      }
    }
  }
  return retval;
}
void advanceDrum(){
  byte n;
  bool drum_trigger;
  if(ppqn==0){
    // skip n = 0 - this is an accent track
    for(n=1;n<EUCL_TRACKS;n++){
      drum_trigger=bitRead(drumloop[seq_position],n);
      if(drum_trigger && drum_run && bitRead(muteChannel,n)){
        // bit 17 = velocity seq
        RK002_sendNoteOn(dChannel, dkeys[n], 99+bitRead(drumloop[seq_position],0)*28); //veloLoop[seq_position]);if bit=0 ; resulting value = 0. if bit = 1 then resulting value = 28. these values are then added to 99 so resulting velocity is either 99 or 127.            
        bitSet(drum_active,n);
      }
    }
    seq_position++;
    seq_position=seq_position % RK002_paramGet(LOOP);//sizeof(drumloop);
  }else if (ppqn==1){
    for(n=1;n<EUCL_TRACKS;n++){
      drum_trigger=bitRead(drum_active,n);
      if(drum_trigger){
        RK002_sendNoteOff(dChannel, dkeys[n], 100);
        bitClear(drum_active,n);
      }
    }
  }
}


void Euclid(byte drumBit, byte trigs) {
  byte cur = RK002_paramGet(LOOP);
  for (byte i=0; i<RK002_paramGet(LOOP); i++) {
    if (cur >= RK002_paramGet(LOOP)) {
      cur -= RK002_paramGet(LOOP);
      bitSet(drumloop[i],drumBit);
    } 
    else {
      bitClear(drumloop[i],drumBit);
    }
    if ((trigs==0) && (drumBit<RK002_paramGet(LOOP))) bitClear(drumloop[i],drumBit);
    cur += trigs;
  }
}

void updateParams()
{
  // convert readable channels 1-16 to internal 0-15
  dChannel = RK002_paramGet(CHANNEL) - 1;
}

void RK002_onParamChange(unsigned param_nr, int val){
  updateParams();
}
// initialize
void setup()
{
    //RK002_clockSetTempo(1200);
    RK002_clockSetMode(0); // auto clock mode (will be clock slaved)
    updateParams();
}

// the main loop
void loop()
{
}
int  drum_active=0;
int  muteChannel=0xffffffff;
int  isPatterned=0;

The developer suggested I use the INT datatype for the variables in question.

From Arduino.h

#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))

What Arduino are you using. Is it possible that an int is 32 bits?
Otherwise, there is no bit 16 in an int. They are bit 0-15 and the bitSet() and bitClear() macros are bitshifting into memory you don't "own" when i == 16. Depending on how the memory in the processor is organized in the compiled sketch, this can be an issue causing unpredicatible results.

for (byte i=0;i<EUCL_TRACKS;i++){ // traverse the CC's
      // euclid control
      if(number==dcontrol[i]){ // match the cc
        if(val>0){ // is there activity
          bitSet(isPatterned,i);          // euclidean pattern controls this
        }else{
          bitSet(muteChannel,i); // pattern is cleared: note key can just control the sound
          bitClear(isPatterned,i);        // reset trig flag for this one
        }

The behavior is that once you start turning the knob (dcontrol 116) for dkey 51, the euclidean generator engages and works with no issue but when you try to return the knob back to 0, i.e. turn off, it continues to play at its lowest triggered setting. It is supposed to stop playing altogether and the all of the other drum tracks (dkeys) do this.

Is this the section of code which should be turning off the playing when the knob is switched back to 0?

bool RK002_onNoteOff(byte channel,byte number,byte value){
  bool retval=true;

  if(channel==dChannel){ // in designated MIDI channel?
    for (byte i=0 ; i<EUCL_TRACKS ; i++){
      if(dkeys[i]==number){
        if(drum_run){
          // key is in programmed range and sequencer is running
          if(bitRead(isPatterned,i)){ // is a defined key and patterned?
            if(!bitRead(muteChannel,i)) retval=false; //  muted: do not play off
          }
        }
      }
    }
  }
  return retval;
}

cattledog:

int  drum_active=0;

int  muteChannel=0xffffffff;
int  isPatterned=0;




From Arduino.h


#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))




What Arduino are you using. Is it possible that an int is 32 bits?
Otherwise, there is no bit 16 in an int. They are bit 0-15 and the bitSet() and bitClear() macros are bitshifting into memory you don't "own" when i == 16. Depending on how the memory in the processor is organized in the compiled sketch, this can be an issue causing unpredicatible results.



for (byte i=0;i<EUCL_TRACKS;i++){ // traverse the CC's
      // euclid control
      if(number==dcontrol[i]){ // match the cc
        if(val>0){ // is there activity
          bitSet(isPatterned,i);          // euclidean pattern controls this
        }else{
          bitSet(muteChannel,i); // pattern is cleared: note key can just control the sound
          bitClear(isPatterned,i);        // reset trig flag for this one
        }




Is this the section of code which should be turning off the playing when the knob is switched back to 0?



bool RK002_onNoteOff(byte channel,byte number,byte value){
  bool retval=true;

if(channel==dChannel){ // in designated MIDI channel?
    for (byte i=0 ; i<EUCL_TRACKS ; i++){
      if(dkeys[i]==number){
        if(drum_run){
          // key is in programmed range and sequencer is running
          if(bitRead(isPatterned,i)){ // is a defined key and patterned?
            if(!bitRead(muteChannel,i)) retval=false; //  muted: do not play off
          }
        }
      }
    }
  }
  return retval;
}

I am not sure this matters or not, but as a test, I also tried using long and unsigned long for those three int variables. Those data types also produced the same behavior.

I am unsure which Arduino is being used in the MIDI cable these sketches get uploaded to, but it has to be small enough to fit inside of the orange plug pictured here.

This is the board manager URL: https://www.retrokits.com/rk002/arduino/package_retrokits_index.json
so perhaps we can confirm what an int is in this scenario and the type of Arduino being used.

This is the code that should be turning off the euclidean generator for that specific knob:

bool RK002_onControlChange(byte channel,byte number,byte val){
  bool retval=true;
  // euclidean
  if(channel==dChannel){ // are we in euclidean set channel?
    for (byte i=0;i<EUCL_TRACKS;i++){ // traverse the CC's
      // euclid control
      if(number==dcontrol[i]){ // match the cc
        if(val>0){ // is there activity
          bitSet(isPatterned,i);          // euclidean pattern controls this
        }else{
          bitSet(muteChannel,i); // pattern is cleared: note key can just control the sound
          bitClear(isPatterned,i);        // reset trig flag for this one
        }
        Euclid(i,int((val+1)/8)); // make the pattern based on CC val
        retval=false;
      }
    }
  }
  return retval;
}