Beginner trouble with coding MIDI transpose function (actually more Arduino code

Hello! Im a beginner and im having trouble with writing the first sketch for my instrument project.
Its a midi controller with 9 switches and a potentiometer.

I want pin 14 and 15 to be the note transpose function (+ / -) which will be read by the buttons later to modify pitch of the note that the button plays. Keyroot = pitch/note for the button and also the root for the other buttons (to make them play in a certain scale depending on how you transpose)

First I had trouble with the transpose function, but now I have seem to got the transpose function working except the code is having trouble reading different parts of itself:

From serial monitor:

" number of minus pushes: 13
current key is
91off
on
number of PLUS pushes: 14
current key is:
92off
on
number of minus pushes: 13
current key is:
91off "

Which is correct, but the midi note doesn’t get transposed?
I tried changing the default key and it didn’t work so there is some miscommunication I guess?
When I started on this code I made this “root key” pattern that was working before I started on transpose coding.

Any tips welcome to make this faster or anything else.
Thank you.

Code:

#include <MIDI.h>
#include <SoftwareSerial.h>
#include <MIDIUSB.h>
#include <PitchToNote.h>
#define NUM_BUTTONS  8


//Transpose Ints

// Constants
int  transpose1Pin = 14;    // the pin that the pushtranspose is attached to
int  transpose2Pin = 15;    // the pin that the pushtranspose is attached to

// Variables
int transposePushCounter = 0;   // counter for the number of transpose presses
int transpose1State = 0;         // current state of the transpose
int transpose2State = 0;         // current state of the transpose
int lasttranspose1State = 0;     // previous state of the transpose
int lasttranspose2State = 0;     // previous state of the transpose
//int transpose = transposePushCounter;
uint8_t  defaultkey = 48;   //48
uint8_t  currentkey = 0;
uint8_t  keyroot1 = currentkey;
uint8_t  keyroot2 = keyroot1 + 2;
uint8_t  keyroot3 = keyroot2 + 2;
uint8_t  keyroot4 = keyroot3 + 1;
uint8_t  keyroot5 = keyroot4 + 2;
uint8_t  keyroot6 = keyroot5 + 2;
uint8_t  keyroot7 = keyroot6 + 1;
uint8_t  keyroot8 = keyroot7 + 1;

const uint8_t  button1 = 2;
const uint8_t  button2 = 3;
const uint8_t  button3 = 4;
const uint8_t  button4 = 5;
const uint8_t  button5 = 6;
const uint8_t  button6 = 7;
const uint8_t  button7 = 8;
const uint8_t  intensityPot = 0;  //A0 input
const uint8_t buttons[NUM_BUTTONS] = {button1, button2, button3, button4, button5, button6, button7};
const byte noteFunction[NUM_BUTTONS] = {keyroot1, keyroot2, keyroot3, keyroot4, keyroot5, keyroot6, keyroot7};
const int LEDpin = 13;     //  Indicator LED


//software serial
SoftwareSerial midiSerial(1, 0); // digital pins that we'll use for soft serial RX & TX

void setup() {
  //  set the states of the I/O pins:
  pinMode(LEDpin, OUTPUT);
  // initialize the transpose pin as a input:
  pinMode(transpose1Pin, INPUT_PULLUP);
  pinMode(transpose2Pin, INPUT_PULLUP);


  //  Set MIDI baud rate:
  Serial.begin(9600);


  midiSerial.begin(31250);
  for (int i = 0; i < NUM_BUTTONS; i++)
    pinMode(buttons[i], INPUT_PULLUP);
}
uint8_t  notesTime[NUM_BUTTONS];
uint8_t  pressedButtons = 0x00;
uint8_t  previousButtons = 0x00;
uint8_t  intensity;


// First parameter is the event type (0x0B = control change).
// Second parameter is the event type, combined with the channel.
// Third parameter is the control number number (0-119).
// Fourth parameter is the control value (0-127).

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
}
void loop() {

  //TRANSPOSE 1

  // read the pushtranspose input pin:
  transpose1State = digitalRead(transpose1Pin);

  // compare the transposeState to its previous state
  if (transpose1State != lasttranspose1State) {
    // if the state has changed, increment the counter
    if (transpose1State == LOW) {
      // if the current state is HIGH then the transpose went from off to on:
      transposePushCounter--;

      Serial.println("on");
      Serial.print("number of minus pushes: ");
      Serial.println(transposePushCounter);
      int currentkey = defaultkey + transposePushCounter;
      Serial.println("current key is:");
      Serial.print(currentkey);

    } else {
      // if the current state is LOWa then the transpose went from on to off:
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    // delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lasttranspose1State = transpose1State;


  //TRANSPOSE 2

  transpose2State = digitalRead(transpose2Pin);

  if (transpose2State != lasttranspose2State) {
    if (transpose2State == LOW) {
      transposePushCounter++;
      Serial.println("on");
      Serial.print("number of PLUS pushes: ");
      Serial.println(transposePushCounter);
      uint8_t  currentkey = defaultkey + transposePushCounter;
      Serial.println("current key is:");
      Serial.print(currentkey);

    } else {

      Serial.println("off");
    }

    lasttranspose2State = transpose2State;
  }

  readButtons();
  readIntensity();
  playNotes();

}


//code for note buttons/potentiometer (Working)
void readButtons()
{


  for (int i = 0; i < NUM_BUTTONS; i++)
  {
    if (digitalRead(buttons[i]) == LOW)
    {
      bitWrite(pressedButtons, i, 1);
      delay(50);
    }
    else
      bitWrite(pressedButtons, i, 0);
  }
}


void readIntensity()
{
  int val = analogRead(intensityPot);
  intensity = (int) (map(val, 0, 1023, 0, 127));
}



void playNotes()
{

  for (int i = 0; i < NUM_BUTTONS; i++)
  {
    if (bitRead(pressedButtons, i) != bitRead(previousButtons, i))
    {
      if (bitRead(pressedButtons, i))
      {
        bitWrite(previousButtons, i , 1);
        noteOn(0, noteFunction[i], intensity);
        MidiUSB.flush();
        Serial.print ("Current key:");
        Serial.println (currentkey);

      }   else
      {
        bitWrite(previousButtons, i , 0);
        noteOff(0, noteFunction[i], 0);
        MidiUSB.flush();
      }
    }
  }
}

// First parameter is the event type (0x09 = note on, 0x08 = note off).
// Second parameter is note-on/note-off, combined with the channel.
// Channel can be anything between 0-15. Typically reported to the user as 1-16.
// Third parameter is the note number (48 = middle C).
// Fourth parameter is the velocity (64 = normal, 127 = fastest).

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}

Can we start with what Arduino you are using? From a quick look at the code you seem to be trying to use SoftwareSerial on the same pins 0 and 1 that the hardware Serial normally uses. And you've included both MIDI.h and MIDIUSB.h which is confusing. And then you don't actually use SoftwareSerial anywhere in the code.

Anyway from just a quick look it seems that your transposing function is based on a misunderstanding about how arrays work. You seem to have the idea that the noteFunction array entries will dynamically change value without your code having to do anything about it but it doesn't work like that. You defined e.g keyroot1 to have the value of currentroot AT COMPILE TIME i.e. 0 and then put it into the noteFunction array. But that value is now fixed. It will not change just because the value of currentroot is changed at some later time. If instead of just printing currentroot you print noteFunction[ i ] a few times you'll probably see what's happening.

You might find that passing something like (currentroot + noteFunction[ i ]) into NoteOn or NoteOff instead of just noteFunction[ i ] is nearer to what you want.

Steve

Thank you for the reply. I am using Arduino micro. Im aware about the included midi.h and software serial but they are there for later use once I have the project closer to the end, I thought I would leave it there and if I would run into any trouble it would show up earlier than when I actually try to implement “analog” midi to it. Maybe this is a bad idea and I should remove it? I included for the reason that the project will when complete be a full instrument with both USB midi and analog midi and built in samples all controlled from code. Your answer makes sense, now my code doesn’t read the current root and note function when it sends the note? I think I need to read or learn some about the structure of a sketch. The array I use I stole from another midi library or example, maybe I would gain more from making the code from scratch for a better understanding? Thank you

Let me clarify: Right now my project sends midi thru MIDIUSB library and im monitoring in MIDI monitor just for easier monitoring than thru a midi din jack output

Im having trouble understanding how to solve this and I think its too complicated, have spent soon 3 days of trying to code this and im getting nowhere. It seems the code needs a whole rearrangement from start and I don't think its worth it

The problem isn't particularly complicated but unfortunately I think your current starting point is a bit too complex for your current level of understanding. It's always difficult modifying code that you don't really understand.

It may be better initially to write a simple program which just sends a given note for each key/button.

When that's working you can add on transposition which is just getting a number via your up/down keys then adding that number to the note number you would have sent. You already have the code that does most of that.

When that's working you can look at adding other functions, perhaps a display to show what the current transpose and perhaps key name is. But the trick is to do it all a bit at at a time and completely understand the code in each stage.

But designing and writing an electronic musical instrument from scratch is something that is going to take a lot longer than 3 days. If you don't think it's worth it then that's your decision. Shame really, it sounds like an interesting project. What was the potentiometer going to be doing?

Steve

Take a look at this example:
Control-Surface/Doc/Doxygen/Ex.17.Transposer.ino-example.html

Code:







```
#include <Control_Surface.h>

Digital buttons[] = {
  {2, 0x3C, 1}, // pin 2, note C4, channel 1
  {3, 0x3E, 1}, // pin 3, note D4, channel 1
  {4, 0x40, 1}, // pin 4, note E4, channel 1
  {5, 0x41, 1}, // pin 5, note F4, channel 1
  {6, 0x43, 1}, // pin 6, note G4, channel 1
  {7, 0x45, 1}, // pin 7, note A4, channel 1
  {8, 0x47, 1}, // pin 8, note B4, channel 1
  {9, 0x48, 1}, // pin 9, note C5, channel 1
};

Bank transposedButtons; // Create a bank (i.e. collection of buttons and other inputs)
Transposer transposer(transposedButtons, {10, 11}, -12, +12); // Create a transposer that works on the bank,
                                                              // with an increment button on pin 10 and a
                                                              // decrement button on pin 11,
                                                              // with a minimum of -12 semitones (i.e.
                                                              // one octave lower) and a maximum of +12
                                                              // semitones (i.e. one octave higher).

void setupcolor=#000000[/color] {
  transposedButtons.addcolor=#000000[/color]; // Add the buttons to the bank
}

void loopcolor=#000000[/color] {
  Control_Surface.refreshcolor=#000000[/color]; // refresh everything
}
```

|

Also, you can’t start the hardware UART on pins 0 and 1, and then start SoftwareSerial on the same pins. Just don’t use SoftwareSerial. Ever.

The Control-Surface library used above handles the setup of the Serial ports for you if you just use the default settings.

Pieter

I'll look into and try the posted code, I started making a simpler version taking the things I myself wrote in the code and throwing the other "collected example" code away. I think my music equipment knowledge is higher than my coding, I know what math to do but I can't get the numbers to talk with each other I guess.

The project is based/inspired by the earliest of synths mellotron (it played a tape in a loop for each note), I want to combine that idea with a MIDI device/station idea (the arduino) which would be controlled by my hardware (the buttons etc) that triggers "built in" sound samples (on SD card I guess will be the first prototype). To get the old time sound the samples (triggered by midi) will be sent to a cassette recorder head and the signal will then be ran back into a speaker (or whatever audio input/output) to get the mellotron vibe. cassette motor will be controlled by arduino to achieve different effects (I have this working). I want to implement different midi patterns and chords because I want it to be kind of like a pocket orchestra that is used to write songs in a portable way but it should also be compatible with other midi devices or instruments, I also want to take advantage of the BLE technology and all the new tech things I've learnt from buying these things thru years and want to make them more available for others (code is cheaper than hardware) for example you could also be able to send sustain commands or arpeggios to other devices (or midi clock / time code).

I know this won't be achieved in three days but I was hoping at least I would be able to get somewhere in this simple code, I think It just calls for a few days break than start on it again. It will probably take forever since it feels like a never ending project. But all in all it is just math right, so once you have made the main code and understand it than it should be easy to build other modifications like arpeggios. Also I have not done any coding since I was 13 years old when I was learning javascript so I think its gonna tak e a while. I started writing a simpler code but its frustrating knowing I will have to replace it later to make it faster or less "latency" and better.

Thanks for replies