Playing Musical Scale with a potentiometer and send data to Midi

Hi Folks,

as the subject already tells I'd like to know whether it is possible to get the following with an Arduino Nano:

I'd like to play a musical scale with a fader potentiometer. This would imply sending the corresponding notes to a midi device and displaying the note on an LCD-display.

Here is what I already know (as a newbie):
My fader potentiometer creates values from 0 - 127.

So can I programm my arduino to tell my midi device the following:

If the potentiometer value is between 0-15 then play Note C ; send "Note C" to LCD display.
If the potentiometer value is between 16-30 then play Note D; send "Note D" to LCD display.
....and so on!?
If the potentiometer value doesn't change then keep on playing that note.

The potentiometer should therefore simply replace a midi keyboard.
I'm really looking forward to your answers so that I can start programming:)

Thanks for your help in advance!
Marl

I'm really looking forward to your answers so that I can start programming:)

When you ask some questions, we can provide answers.

Think about your requirements. You have a number of intervals, and some actions that are to be performed if the value is in the interval. The interval could be determined by dividing the potentiometer value by 16. Then the action could be chosen by a switch statement. Or, use an array of structs to choose what to do when the interval is n.

marl808:
Here is what I already know (as a newbie):
My fader potentiometer creates values from 0 - 127.

Why does it not produce values from 0 to 1023 like any other potentiometer?!?
See: https://www.arduino.cc/en/Tutorial/AnalogInput

Thanks for your answers so far!

Why does it not produce values from 0 to 1023 like any other potentiometer?!?

You're right, John. I've seen in a tutorial that they transfered the poti-scale (0-1023) to a midi-scale (0-127) -> see map code below.

void loop() {

potiValue = analogRead(A0);
controllerValue = map(potiValue,0,1023,0,127);

Isn't this neccessary?

The interval could be determined by dividing the potentiometer value by 16. Then the action could be chosen by a switch statement. Or, use an array of structs to choose what to do when the interval is n.

Thanks, Paul. This seems to be exactly what I'm looking for:)
So, here are my questions:

  1. What is the best or easiest way to write this into the programme - as two options (switch statement or arrays) were mentioned!?

2)What are the Midi-codes for special notes (for example: If the potentiometer value is between 0 and 64 then play note c (as if hitting the c key on a midi keyboard)?

  1. I guess that the same methods (switch statement or arrays, as mentioned in my first question) can be used to send and write the played note on the LC-display, right?

Thanks!

So you have 8 notes to play. You want to control which note with the analog input.

const int MIDI_NOTES = {60,62,64,65,67,69,71,72)
char *NoteNames[] = {"C4", "D4", "E4", "F4", "G4", "A4", "B4", "C5"};

void loop() {
    int index = (analogInput(A0) * 8) / 1024;
    // Play this note: MIDI_NOTES[index]
    // Display this name: NoteNames[index]
}

Thanks John!

I had to make slight changes due to warnings while checking the code in the Arduino software.

I still have some questions:
1) Does this code now really imply that my potentiometer fader will be separated into 8 equal parts and therefore send those 8 notes to my midi device according to the position of the fader?
2) Will the code work as follows?

int controlChange = 176; // MIDI Channel 1
int controllerNumber = 21;
int indexOld = 0;
int index = 0;
int potiValue = 0;

const int MIDI_NOTES[] = {60,62,64,65,67,69,71,72};
const char *NoteNames[] = {"C4", "D4", "E4", "F4", "G4", "A4", "B4", "C5"};

void setup() {
Serial.begin(31250);
}

void loop() {

potiValue = analogRead(A0);
int index = (potiValue * 8 ) / 1024;
// Play this note: MIDI_NOTES[index]
// Display this name: NoteNames[index]
if (index != indexOld) {
Serial.write(controlChange);
Serial.write(controllerNumber);
Serial.write(potiValue);
}

indexOld = index;
}

  if (index != indexOld) {
  Serial.write(controlChange);
  Serial.write(controllerNumber);
  Serial.write(potiValue);
   }

I don't think that will play notes.
'controlChange' (0xB0) is not the same as NoteOn, Channel 1 (0x91).
'controllerNumber' is not the MIDI note number
and
'potiValue' is not the velocity.

To play notes on Channel 1, try:

  if (index != indexOld) {
  Serial.write((byte)0x91);  // NoteOn, Channel 1
  Serial.write((byte)MIDI_NOTES[index]);  // MIDI note number
  Serial.write((byte)127);  // Maximum Velocity (loud)
   }

Thanks, once again - I changed the "if"-lines.

Now, back to my first question to make things clear:

const int MIDI_NOTES[] = {60,62,64,65,67,69,71,72};
const char *NoteNames[] = {"C4", "D4", "E4", "F4", "G4", "A4", "B4", "C5"};

Those are the midi codes for the corresponding notes, e.g. 60 represents C4, right!?

void loop() {

potiValue = analogRead(A0);
int index = (potiValue * 8 ) / 1024;
// Play this note: MIDI_NOTES[index]
// Display this name: NoteNames[index]

  1. Does this code now really imply that my potentiometer fader will be separated into 8 equal parts or better 8 note info parts and therefore be able to send those 8 notes to my midi device according to the position of the fader?

Could you (or anyone else) please explain in detail to me what this code means?

Thanks in advance!

marl808:
Those are the midi codes for the corresponding notes, e.g. 60 represents C4, right!?

Yes.

marl808:

  1. Does this code now really imply that my potentiometer fader will be separated into 8 equal parts or better 8 note info parts and therefore be able to send those 8 notes to my midi device according to the position of the fader?

Yes.

marl808:
Could you (or anyone else) please explain in detail to me what this code means?

I'll add a bunch of comments:

void loop() {
  // Get a value 'potiValue' from 0 to 1023 that represents
  // the position of the potentiometer.
  potiValue = analogRead(A0);

  // Multiply that value by 8 to get a value from 0 to 8184
  // then integer divide by 1024 to get a value from 0 to 7
  // to act as an index into the 8 note array
  int index = (potiValue * 8 ) / 1024;

  // Play this note: MIDI_NOTES[index]
  // Display this name: NoteNames[index]

  byte noteNumber = MIDI_NOTES[index];

  // Don't bother sending the note if it is the same as the
  // last note sent.
  if (index != indexOld) {
    // Send the three bytes of a NoteOn MIDI message
    Serial.write((byte)0x91);  // NoteOn, Channel 1
    Serial.write(noteNumber);  // MIDI note number
    Serial.write((byte)127);  // Maximum Velocity (loud)
  }
  // Remember the last note sent so we don't send the 
  // same one again if the potentiometer is not moved
  indexOld = index;  
}

Thanks John for the fast reply and thanks for your explanations!

As we are still in theory, here's another thought:

const int MIDI_NOTES[] = {60,62,64,65,67,69,71,72};
const char *NoteNames[] = {"C4", "D4", "E4", "F4", "G4", "A4", "B4", "C5"};

This is the set midi scale.

Am I right that I could theoretically use a second potentiometer to choose another predefined scale.

For example:

const int MIDI_NOTES1[] = {0,1,2,3,4,5,6,7,8};
const char *NoteNames1[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"};

int index = (potiValue * 9 ) / 1024;

Would you use another array for that poti or kind of an if-loop "if poti value is between 0-100 use "const int MIDI_NOTES1" ? How?

Thanks in advance!

marl808:
Am I right that I could theoretically use a second potentiometer to choose another predefined scale.

Yes. You have 6 analog inputs and can connect a potentiometer to each.

marl808:
Would you use another array for that poti or kind of an if-loop "if poti value is between 0-100 use "const int MIDI_NOTES1" ? How?

If it was a different arrangement of notes I would use a different array. I would also name them to make it clear which was which. Using the name MIDI_NOTES works if you only have one but when you have two the name MIDI_CHROMATIC_SCALE might make more sense. Using the names MIDI_NOTES and MIDI_NOTES1 shows a lack of planning.

Thanks again!

Here's another attempt to have the option of several scales to choose from:

int controlChange = 176; // MIDI Channel 1
int controllerNumber = 21;
int indexOld = 0;
int index = 0;
int potiValue = 0;
int scalePoti=0;
int scaleValue=0;

// *********************************
// SCALE INFORMATION:
// *********************************

// *** A Major *** COUNT: 0
const int Amaj0[] = {57,59,61,62,64,66,68,69};
const char *Amaj0Names[] = {"A6", "B6", "C#6", "C6", "E6", "F#6", "G#6", "A7"};

// *** A Major 1*** COUNT: 1
int Amaj1[] = {69,71,73,74,76,78,80,81};

// *** B Major *** COUNT: 2

int Bmaj0[] = {59,61,63,64,66,68,70,71};

// *** B Major 1*** COUNT: 3
int Bmaj1[] = {71,73,75,76,78,80,82,83};

// *** C Major *** COUNT: 4
int Cmaj0[] = {60,62,64,65,67,69,71,72};

// *** C Major 1 *** COUNT: 5
int Cmaj1[] = {72,74,76,77,79,81,83,84};

// *** D Major *** COUNT: 6
int Dmaj0[] = {62,64,66,67,69,71,73,74};

// *** D Major 1*** COUNT: 7
int Dmaj1[] = {74,76,78,79,81,83,85,86};

// *** E Major *** COUNT: 8
int Emaj0[] = {64,66,68,69,71,73,75,76};

// *** E Major 1*** COUNT: 9
int Emaj1[] = {76,78,80,81,83,85,87,88};

// *** F Major *** COUNT: 10
int Fmaj0[] = {65,67,69,70,72,74,76,77};

// *** F Major 1*** COUNT: 11
int Fmaj1[] = {77,79,81,82,84,86,88,89};

// *** G Major *** COUNT: 12
int Gmaj0[] = {55,57,59,60,62,64,66,67};

// *** G Major 1*** COUNT: 13
int Gmaj1[] = {67,69,71,72,74,76,78,79};

// *** G Mixolydian*** COUNT: 14
int Gmix[] = {81,83,84,86,88,89,79,81};

// *** Pentatonic*** COUNT: 15
int Pent[] = {0,2,4,7,9};

// *** Pentatonic 8*** COUNT: 16
int Pent8[] = {84,86,88,91,93};

// *** Blues*** COUNT: 17
int Blue[] = {0,2,3,4,5,7,9,10,11};

// *** Bluz 10*** COUNT: 18
int Bluz[] = {108,110,111,112,113,115,117,118,119};

// *** Phrygian 5*** COUNT: 19
int Phry5[] = {48,49,51,53,55,56,58};

// **********************************************
// ******* END OF SCALE INFORMATION ********
// **********************************************

void setup() {
Serial.begin(31250);
}

void loop() {

scalePoti = analogRead(A1);
scaleValue = (scalePoti * 19) / 1024;
// Display scaleValue

if (scaleValue = 0) {
potiValue = analogRead(A0);
int index = (potiValue * 8) / 1024;
// Play this note: Amaj0[index]
// Display this name: Amaj0Names[index]
}
if (index != indexOld) {
Serial.write((byte)0x91); // NoteOn, Channel 1
Serial.write((byte)Amaj0[index]);
Serial.write((byte)127); // Maximum Velocity (loud)
}

indexOld = index;
}

Would this work if I wrote if-orders for all the other scales (like I did it wit A Major -> Amaj0)?

Or is there a better or easier way to do that (Choose musical scale from 0 - 19 and play the corresponding notes via a potentiometer)?

marl808:
Or is there a better or easier way to do that (Choose musical scale from 0 - 19 and play the corresponding notes via a potentiometer)?

A far better way is to have an array of 19 pointers and an array of 19 scale lengths. Use the '1-of-19' pot to select the scale and use the other pot to select the note from the scale. Because not all of your scales are 8 notes you will need the array of scale lengths.
You can extend the controls by having separate sliders for:
Type of scale (chromatic, Major, Minor, pentatonic, mixilodean...)
Starting note (C through B)
Octave
Then you could select an A Major, B Minor, E-flat Major, or whatever other kind of scale you wanted in whatever octave you like.

Maybe use a Rotary Encoder instead, each 'click' of the encoder is the next note up or down. Much less fiddly than a pot and you don't spend a bunch of time doing comparisons to find where in the musical scale you are.
Maybe have 2 of them - one to select a note within a scale, and the other to select the scale.

Thanks John, thanks Crossroads,

A far better way is to have an array of 19 pointers and an array of 19 scale lengths. Use the '1-of-19' pot to select the scale and use the other pot to select the note from the scale.

This sounds good, but there's one problem - me and my beginner programming skills;)

Is this what you mean:

const int Scale[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}
const int ScaleLength[] = {8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,5,5,9,9,7}

  1. Can I use my "Scale Information", like

// *** A Major *** COUNT: 0
const int Amaj0[] = {57,59,61,62,64,66,68,69};
const char *Amaj0Names[] = {"A6", "B6", "C#6", "C6", "E6", "F#6", "G#6", "A7"};

for this and refer with the array const int Scale back to this info?
If yes, how do you refer back to this info? Code?

Thanks
Marl

marl808:
Is this what you mean:

const int Scale[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}
const int ScaleLength[] = {8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,5,5,9,9,7}

Not quite. You need an array of scales:

const byte * Scales[] = {Amaj0, Amaj1, Bmaj0... };

Similarly, if you want to display note names, you will want an array of lists of note names.

Did you get the single scale working correctly? You should get that perfected first before trying to elaborate.

Thanks again!

const byte * Scales[] = {Amaj0, Amaj1, Bmaj0... };

Where does the programm get the following info from?

Amaj0[] = {57,59,61,62,64,66,68,69};

I mean, are the scales known to the programm?

So I was envisioning you would have 2 readings, one that is a number from 1 to 13 representing the note in a scale,
with C-C#-D-D#-E-F-F#-G-G#-A-A#-B-C represented as 1-2-3-4-5-6-7-8-9-10-11-12-13,
these would also be the 13 low notes on a Piano (assuming the lowest note is a C, I don't know what it is really)
thus a scale could be 1-3-5-6-8-10-12-13 for C1 Major,
C2 Major could be 13-15-17-18-20-22-24-25.
C3 Major 25-27-29-30-32-34-36-37
etc thru C7 Major
byte CMajor [] = {
1,3,5,6,8,10,12,13, // C1
13,15,17,18,20,22,24,25, // C2
25,27,29,30,32,34,36,37, // C3
37,39,41,43,44,46,48,49, // C4
49,51,53,55,56,58,60,61, // C5
61,63,65,66,68,70,72,73 // C6
73,75,77,78,80,82,84,84, // C7
};

Then if encoder/potentiometer reading1 = 2, for 2nd scale, (altho in case 2nd Octave)
encoder/pot reading2 = 4 for 4th note of scale,

sendToMidi = CMajor[(encoder1*8) + encoder2]; // plays 4th note of 2nd scale

Then have multiple arrays, each representing a different scale - AMaj, AMin, A#Maj, A#Min, A7Maj, etc. each with its own set of notes for the octaves across the keyboard, and a third pot to select the scale.

For the major scales listed in post #11 there is no need to store all the notes in arrays. As long as you know the first note you can get the others by adding 0, 2, 4, 5, 7, 9, 11 or 12 to it. Define an array with those values and index into it to get what should be added to the first note.

Here is some demo code to show what I mean:

// MIDI scales

// first notes for major scales
const int AMAJ = 69;
const int BMAJ = 59;
const int BMAJ1 = 71;
const int CMAJ = 60;
const int CMAJ1 = 72;

// add to first note to get each note in scale
const int majorNotes[] = {
  0, 2, 4, 5, 7, 9, 11, 12}; 

const int majorNoteCount = sizeof(majorNotes) / sizeof(majorNotes[0]);

// print all notes in a scale
//
void showMajorScale(int firstNote)
{
  int note;

  // while more notes
  for(int i=0; i < majorNoteCount; i++)
  {

    // index into array to get value for this note
    note = firstNote + majorNotes[i];
    Serial.println(note);

  } // for

  Serial.println();

}

void setup()
{
  Serial.begin(9600);

  // print notes for major scales
  Serial.println("A Major: ");
  showMajorScale(AMAJ);

  Serial.println("B Major: ");
  showMajorScale(BMAJ);

  Serial.println("B Major 1: ");
  showMajorScale(BMAJ1);

  Serial.println("C Major: ");
  showMajorScale(CMAJ);

  Serial.println("C Major 1: ");
  showMajorScale(CMAJ1);


}


void loop()
{

}

As long as you know the first note you can get the others by adding 0, 2, 4, 5, 7, 9, 11 or 12 to it.

That is only true for the Major scale. For the Minor and other augmented(?) scales, the relative notes are different.
Perhaps an array for each kind of scale?
And another for the starting note of the scale by octave?