将音符字符串转换为 MIDI 音高数
Converting musical note Strings to MIDI pitch number
我写了下面的方法来将音符(八度附加到末尾)转换为相应的 MIDI 音高:
// 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;
}
而且效果很好。但是,我还希望能够将 convertToPitch()
与备用音符值(Db 变为 C# 等)一起用于每个具有备用名称的音符。有没有办法在不破坏我的方法的情况下做到这一点?
您可以先 "normalizing" 输入对预期输入的注释。 IE。使用所有可能的注释和规范化映射初始化字符串 -> 字符串映射。应该只会导致在构造函数中进行一些地图初始化并在 convertToPitch 的开头调用 map 方法。
你可以这样做
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;
}
您可以单独处理临时记号。因此,对您的代码进行最少的修改:
[...]
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 !!
[...]
但是,在我看来,使用数组索引作为映射并不是最好的方法。根据您的输入格式,这不起作用,因为 Cb 是有效音符,但没有数组索引 -1。您可以创建一个 Map<String,Integer>
(例如 "C" 映射到 0,"D" 映射到 2,等等)或使用 enumeration. You should also use Exceptions 而不是返回 -1;这样您就可以区分格式错误的字符串 ("abbc123") 和超出范围的值 ("B#9")。
最后,如果我没记错的话,MIDI 音高有一个偏移量(比如 C0 不是 0 而是 12)。
我写了下面的方法来将音符(八度附加到末尾)转换为相应的 MIDI 音高:
// 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;
}
而且效果很好。但是,我还希望能够将 convertToPitch()
与备用音符值(Db 变为 C# 等)一起用于每个具有备用名称的音符。有没有办法在不破坏我的方法的情况下做到这一点?
您可以先 "normalizing" 输入对预期输入的注释。 IE。使用所有可能的注释和规范化映射初始化字符串 -> 字符串映射。应该只会导致在构造函数中进行一些地图初始化并在 convertToPitch 的开头调用 map 方法。
你可以这样做
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;
}
您可以单独处理临时记号。因此,对您的代码进行最少的修改:
[...]
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 !!
[...]
但是,在我看来,使用数组索引作为映射并不是最好的方法。根据您的输入格式,这不起作用,因为 Cb 是有效音符,但没有数组索引 -1。您可以创建一个 Map<String,Integer>
(例如 "C" 映射到 0,"D" 映射到 2,等等)或使用 enumeration. You should also use Exceptions 而不是返回 -1;这样您就可以区分格式错误的字符串 ("abbc123") 和超出范围的值 ("B#9")。
最后,如果我没记错的话,MIDI 音高有一个偏移量(比如 C0 不是 0 而是 12)。