Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 1 of 1

Thread: Help Needed with DIN MIDI "Drone" mode sketch

  1. #1

    Help Needed with DIN MIDI "Drone" mode sketch

    Hi Everyone,

    I am trying to code a modified version of the drone keys sketch by Retrokits. The sketch does the following:

    * You play one or multiple keys on a keyboard, these played keys will stay active, even after release of all the keys.
    * Only when you press one or more keys again after you've released all, the held keys will be released and you can play a new set.
    * Modwheel turns all notes notes off when value is zero.

    I want to code a version for a hardware monophonic sequencer that also has a parallel sequence mode (2 sequences run in parrallel, 2 channels of MIDI, e.g. SEQ A = ch 1, SEQ B = ch 2).

    I am using the Teensy LC.

    The sketch I have in mind is button based where with a button press you can toggle in and out of this "drone" mode:
    Default mode: no drones
    Drone mode: like described above. However, since the sequencer is monophonic, instead of how the retrokits version works, I want to "stack" drone note ons because one drone note at a time per channel is well, underwhelming.

    As the drone note ons stack, the array elements should fill in the order the notes are received, then if those same notes are pressed again, they are turned off (note off).
    OR they ALL get turned off by a CC value that sends an "all notes off " note off function (like the modwheel above)
    OR when the button is pressed to go back to default mode (no drones) all active drone notes are turned off.

    Here's my current working not-really-working version below. I am a beginner coder so and I would also like to maintain an "arduino style" approach using note array buffers to achieve the desired goal. After multiple attempts of trying to determine the issues I am having, I am unable to get the arrays working as I have described above and I could really use some guidance and feedback. Thanks so much in advance.

    Code:
    #include <Arduino.h>
    
    #include "EncoderTool.h"
    using namespace EncoderTool;
    PolledEncoder encoder; // Polling encoder, please call corresponding tick() functions as often as possible, e.g. in loop
    
    // Bourns PEL12D Rotary Encoder (2 LEDs, 1 button)
    const int ENC_PIN_A   = 12; // encoder pin A
    const int ENC_PIN_B   = 11; // encoder pin B
    const int ENC_BUTTON  = 2;  // encoder button pin
    const int LED_2_GREEN = 22; // encoder led green
    const int LED_1_RED   = 23; // encoder led red
    
    byte button_mode = 0; // initilize button mode state
    
    #include <MIDI.h>
    using namespace midi; // midi::Start, midi::Stop, midi::Continue, etc
    MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); // initialize 1 instance of DIN / hardware MIDI
    
    byte type, note, velocity, channel_received, data1, data2, cc_number, cc_value; // MIDI data
    
    byte note_state; // is the note off (0) or on (1)
    
    byte velocity_adjust = 36; // the SQ-1 default velocity is fixed at 64 (0-127) so we're gonna boost it a little...
    
    byte toggle_drone_notes; // encoder CC toggle for drone mode
    byte active_drone_notes = 0; // initilize the active drone notes counter
    byte drone_cc = 1; // modwheel cc = 1
    byte drone_enable; // flag to enable drone notes
    
    byte notes_received_poly_bank[32] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
    byte notes_sent_poly_bank[32]     = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
    
    long current_time, previous_time; // loop time keepers
    
    // void allDronesOff() { // we need a way to turn drones off while drone mode is enabled in addition to when the button toggle disables drone mode
    //
    //   byte note_off_select = 0;
    //
    //   for (byte j = 0; j < 32; j++) {
    //
    //     if (note == notes_received_poly_bank[j]) { // if the note is already in the bank and we're turing it off)
    //
    //       note_off_select = j;
    //       break;
    //
    //     }
    //
    //   }
    //
    //   MIDI.sendNoteOff(notes_sent_poly_bank[note_off_select], velocity, channel_received);
    //   notes_received_poly_bank[note_off_select] = 255;
    //   notes_sent_poly_bank[note_off_select]     = 255;
    //
    //   active_drone_notes = 0; // resets the active drone notes counter
    //
    // }
    
    void onEncoderChanged(int value, int delta) { // use the encoder to pass an "all drones off" function via modulation wheel cc (see allDronesOff function)
    
      if (button_mode == 0) {
    
        // no filter processing
        Serial.print("Mode 0, value: ");
        Serial.println(value);
    
      }
    
      if (button_mode == 1) { // toggles the drone mode on/off when drone mode is enabled. by default it is enabled when activating drone mode in the setup
    
        toggle_drone_notes = value; // the encoder value range 0-1 (drone off or on)
    
        if (drone_cc == 1) {
    
          cc_number = drone_cc;
          cc_value = toggle_drone_notes;
    
          MIDI.sendControlChange(cc_number, cc_value, channel_received); // MIDI.sendControlChange(number, value, channel);
          MIDI.sendControlChange(cc_number, cc_value, channel_received + 1); // for parallel modes global midi channel + 1, e.g. if Seq A is 3, Seq B is 4
    
          if (cc_value == 0) { // When CC value is 0, we need to note off all active notes
    
            byte note_off_select = 0;
    
            for (byte j = 0; j < 32; j++) {
    
              if (note == notes_received_poly_bank[j]) { // if the note is already in the bank and we're turing it off)
    
                note_off_select = j;
                break;
    
              }
    
            }
    
            MIDI.sendNoteOff(notes_sent_poly_bank[note_off_select], velocity, channel_received);
            notes_received_poly_bank[note_off_select] = 255;
            notes_sent_poly_bank[note_off_select]     = 255;
    
            active_drone_notes = 0; // resets the active drone notes counter
    
          }
    
        }
    
        Serial.print("Mode 1, value: ");
        Serial.println(value);
    
      }
    
    } // encoder value change over
    
    void onButtonChanged(int32_t state) { // button state
    
      if (state == LOW) { // if the button fell and button state is LOW (LOW when pressed), change the mode
    
        drone_enable = !drone_enable; // drone flag toggle
    
        button_mode++; // here we want to account for more than one on / off mode
    
        if (button_mode > 1) {
    
          button_mode = 0;
    
        }
    
        if (button_mode == 0) { // default mode (no drones)
    
          if (drone_enable == 0) {
    
            digitalWrite(LED_2_GREEN, LOW);
    
            // Serial.print("Send NoteOff");
            // Serial.println();
    
          }
    
        }
    
        if (button_mode == 1) { // drone mode
    
          if (drone_enable == 1) {
    
            digitalWrite(LED_2_GREEN, HIGH);
    
            // Serial.print("Send NoteOn");
            // Serial.println();
    
          }
    
        }
    
        Serial.print("button_mode = ");
        Serial.println(button_mode);
    
        Serial.print("drone_enable = ");
        Serial.println(drone_enable);
    
      } // button if over
    
    } // button state over
    
    void processMidi() {
    
      if (MIDI.read()) { // is there a DIN MIDI message incoming?
    
        note_state = 2; // initialize the note state as receiving incoming MIDI note data
        type       = MIDI.getType(); // what kind of MIDI message is being received?
    
        if (type == NoteOn) {
    
          note             = MIDI.getData1(); // MIDI note data to be sent with note on
          velocity         = MIDI.getData2() + velocity_adjust; // MIDI note velocity data to be sent with note on
          channel_received = MIDI.getChannel(); // // MIDI channel data to be sent with note
    
          if (velocity > 0) {
    
            note_state = 1;  // note on
            // Serial.print("Note On:  ");
            // Serial.print(note);
            // Serial.print("  velocity: ");
            // Serial.println(velocity);
            // Serial.println();
          }
    
          if (velocity == 0) { // some systems send velocity 0 as note off, but the SQ-1 does not: it sends a note off right at the same time it sends the next note on
    
            note_state = 0; // note off
            // Serial.print("Note Off: ");
            // Serial.println(note);
            // Serial.println();
          }
    
          if (drone_enable == 0) { //note on messages will be sent on without any filtering, i.e. no droning
    
            byte note_on_select = 0;
    
            for (byte j = 0; j < 32; j++) { // scan the bank to find a blank spot. Using 255 as midi is 0-127 so we can keep it a byte
    
              if (notes_received_poly_bank[j] == 255) {
    
              note_on_select = j;
              break;
    
              }
    
            }
    
            notes_received_poly_bank[note_on_select] = note; // the incoming note in the array
            notes_sent_poly_bank[note_on_select]     = note;
            MIDI.sendNoteOn(note, velocity, channel_received);
    
          }
    
          if (drone_enable == 1) { // note on messages will be sent with filtering, i.e. drone note ons without immediate note offs
    
            if (active_drone_notes == 0) {
    
                byte note_off_select = 0;
    
                for (byte j = 0; j < 32; j++) {
    
                  if (note == notes_received_poly_bank[j]) {  // if the note is already in the bank and we're turing it off)
    
                  note_off_select = j;
                  break;
    
                  }
    
                }
    
                MIDI.sendNoteOff(notes_sent_poly_bank[note_off_select], velocity, channel_received);
                notes_received_poly_bank[note_off_select] = 255;
                notes_sent_poly_bank[note_off_select]     = 255;
    
                active_drone_notes = 0; // resets the active drone notes counter
    
            }
    
            byte note_on_select = 0;
    
            for (byte j = 0; j < 32; j++) { // scan the bank to find a blank spot. Using 255 as midi is 0-127 so we can keep it a byte
    
              if (notes_received_poly_bank[j] == 255) {
    
              note_on_select = j;
              break;
    
              }
    
            }
    
            notes_received_poly_bank[note_on_select] = note; // the incoming note in the notes_received_poly_bank array
            notes_sent_poly_bank[note_on_select]     = note; // the outgoing note from the notes_sent_poly_bank sent array
            MIDI.sendNoteOn(note, velocity, channel_received);
    
            active_drone_notes++; // increment the number of active drone notes
    
          }
    
        }
    
        else if (type == NoteOff) { // else if can be used so we only have one outcome
    
          note             = MIDI.getData1(); // MIDI note data to be sent with note on
          velocity         = MIDI.getData2(); // MIDI note velocity data to be sent with note on
          channel_received = MIDI.getChannel(); // MIDI channel data to be sent with note off
    
          note_state       = 0; // note off
    
          if (drone_enable == 0) {
    
            byte note_off_select = 0;
    
            for (byte j = 0; j < 32; j++) {
    
              if (note == notes_received_poly_bank[j]) {  // if the note is already in the bank and we're turing it off)
    
              note_off_select = j;
              break;
    
              }
    
            }
    
            MIDI.sendNoteOff(notes_sent_poly_bank[note_off_select], velocity, channel_received);
            notes_received_poly_bank[note_off_select] = 255;
            notes_sent_poly_bank[note_off_select]     = 255;
    
          }
    
          if (drone_enable == 1) {
    
            active_drone_notes--; // decrement the number of active drone notes
    
          }
    
        }
    
        // if (note_state == 2) {
        //
        //   if (drone_enable == 1) {
        //
        //   }
        //
        //   if (drone_enable == 0) {
        //
        //   }
        //
        // }
        //
        // if (note_state == 1) {
        //
        //   MIDI.sendNoteOn(note, velocity, channel_received);
        //
        // }
        //
        //
        // if (note_state == 0) {
        //
        //   MIDI.sendNoteOff(note, velocity, channel_received);
        //
        // }
    
        // if (note_state == 1) {
        //
        //   byte note_on_select = 0;
        //
        //   for (byte j = 0; j < 32; j++) { // scan the bank to find a blank spot. Using 255 as midi is 0-127 so we can keep it a byte
        //
        //     if (notes_received_poly_bank[j] == 255) {
        //
        //     note_on_select = j;
        //     break;
        //
        //     }
        //
        //   }
        //
        //   notes_received_poly_bank[note_on_select] = note; // the incoming note in the array
        //   notes_sent_poly_bank[note_on_select]     = note;
        //   MIDI.sendNoteOn(note, velocity, channel_received);
        //
        // }
        //
        // if (note_state == 0) {
        //
        //   byte note_off_select = 0;
        //
        //   for (byte j = 0; j < 4; j++) {
        //
        //     if (note == notes_received_poly_bank[j]) {  // if the note is already in the bank and we're turing it off)
        //
        //     note_off_select = j;
        //     break;
        //
        //     }
        //
        //   }
        //
        //   MIDI.sendNoteOff(notes_sent_poly_bank[note_off_select], velocity, channel_received);
        //   notes_received_poly_bank[note_off_select] = 255;
        //   notes_sent_poly_bank[note_off_select]     = 255;
        //
        // }
    
        else if (type == Clock) {
    
        }
    
        else if (type == Start) {
    
          // toggle drone mode off for default MIDI channel
          // in PARALLEL Sequence modeswhere there are to MIDI channels cooresponding to seq a and b:
          // Could we have SEQ A running a sequence and SEQ B doing droning?!?!?!?!
    
        }
    
        else if (type == Continue) {
    
        }
    
        else if (type == Stop) {
    
          // do we need a condition to disable drone mode if a Stop message is received?
    
        }
    
        else if (type == ControlChange) {
    
        }
    
        else {
    
            data1 = MIDI.getData1();
            data2 = MIDI.getData2();
    
          }
    
          // wer're still inside "if (MIDI.read()) {"
    
        if (1) { // if we're receiveing MIDI input(1), debug
    
          Serial.println();
          Serial.print("notes received array: ");
          for (byte j = 0; j < 4; j++) {
    
    
              Serial.print(notes_received_poly_bank[j]); Serial.print(" ");
            }
    
            Serial.println(); // Serial.println();
    
            Serial.print("notes sent array:     ");
            for (byte j = 0; j < 4; j++) {
    
    
              Serial.print(notes_sent_poly_bank[j]); Serial.print(" ");
            }
    
            Serial.println(); // Serial.println();
        }
    
      } // if MIDI read is over
    
    } // processMidi is over
    
    void setup() {
    
      pinMode(LED_2_GREEN, OUTPUT);
      pinMode(LED_1_RED, OUTPUT);
      pinMode(ENC_BUTTON, INPUT_PULLUP);
      digitalWrite(LED_1_RED, HIGH); // init red LED on
    
      encoder.begin(ENC_PIN_A, ENC_PIN_B, ENC_BUTTON);
      encoder.setValue(1); // set the counter to a position
      encoder.setLimits(0,1); // limits the count range
      encoder.attachCallback(onEncoderChanged); // connect the encoder callback function
      encoder.attachButtonCallback(onButtonChanged); // connect the button callback function
    
      MIDI.begin(MIDI_CHANNEL_OMNI); // turn on DIN MIDI
      MIDI.turnThruOff(); // if you want the incoming MIDI notes to be copied and sent out comment out this line
    
    }
    
    void loop() {
    
      current_time = millis();
      encoder.tick();
      processMidi();
    
    }
    Last edited by mmryspace; 03-12-2022 at 06:19 PM. Reason: more details

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •