Reputation: 567
I have written the following method to convert notes (with the octave appended to the end) to the corresponding MIDI pitch:
// Converts a note string (MUST HAVE OCTAVE) to an integer pitch.
public static int convertToPitch(String note) {
String sym = "";
int oct = 0;
String[] notes = { "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B" };
char[] splitNote = note.toCharArray();
// If the length is two, then grab the symbol and number.
// Otherwise, it must be a two-char note.
if (splitNote.length == 2) {
sym += splitNote[0];
oct = splitNote[1];
} else if (splitNote.length == 3) {
sym += Character.toString(splitNote[0]);
sym += Character.toString(splitNote[1]);
oct = splitNote[2];
}
// Find the corresponding note in the array.
for (int i = 0; i < notes.length; i++) {
if (notes[i].equals(sym)) {
return Character.getNumericValue(oct) * 12 + i;
}
}
// If nothing was found, we return -1.
return -1;
}
And it works just great. However, I would also like to be able to use convertToPitch()
with the alternate note value (Db becomes C#, etc.) for each note with an alternate name. Is there a way to do this without tearing my method apart?
Upvotes: 0
Views: 2967
Reputation: 413
You can handle the accidentals separately. So with the least modification of your code:
[...]
String sym = "";
int oct = 0;
int accidental = 0; // initialize with default value 0 (i.e. no accidental)
[...]
} else if (splitNote.length == 3) {
sym += Character.toString(splitNote[0]);
switch (splitNote[1]){
case '#':
accidental = 1;
break;
case 'b':
accidental = -1;
break;
default:
return -1; // you should really use Exceptions instead
}
// don't concat accidental to sym
oct = splitNote[2];
[...]
return Character.getNumericValue(oct) * 12 + i + accidental; // !! read the documentation of Character#getNumericValue !!
[...]
However, using the array index as a mapping isn't the best approach in my opinion. Depending on your input format this doesn't work since Cb is a valid note but there is no array index -1. You could create a Map<String,Integer>
(like "C" is mapped to 0, "D" to 2, etc.) or use an enumeration. You should also use Exceptions instead of returning -1; this way you can differentiate between a malformatted string ("abbc123") and a value out of range ("B#9").
Finally, If I remember correctly, there is an offset in MIDI pitch (like C0 isn't 0 but 12).
Upvotes: 0
Reputation: 2672
You could do it like this
public static int convertToPitch(String note) {
String sym = "";
int oct = 0;
String[][] notes = { {"C"}, {"Db", "C#"}, {"D"}, {"Eb", "D#"}, {"E"},
{"F"}, {"Gb", "F#"}, {"G"}, {"Ab", "G#"}, {"A"}, {"Bb", "A#"}, {"B"} };
char[] splitNote = note.toCharArray();
// If the length is two, then grab the symbol and number.
// Otherwise, it must be a two-char note.
if (splitNote.length == 2) {
sym += splitNote[0];
oct = splitNote[1];
} else if (splitNote.length == 3) {
sym += Character.toString(splitNote[0]);
sym += Character.toString(splitNote[1]);
oct = splitNote[2];
}
// Find the corresponding note in the array.
for (int i = 0; i < notes.length; i++)
for (int j = 0; j < notes[i].length; j++) {
if (notes[i][j].equals(sym)) {
return Character.getNumericValue(oct) * 12 + i;
}
}
// If nothing was found, we return -1.
return -1;
}
Upvotes: 1
Reputation: 785
You could maybe begin by "normalizing" input note to expected input. I.e. initialize a string -> string map with all possible notes and normalized mappings. Should only result in some map initialization in constructor and call to map method in the beginning of convertToPitch.
Upvotes: 2