2016-02-29 269 views
0

我寫了下面的方法來轉換票據(標到最後八度)到相應的MIDI間距:轉換音符絃樂到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#等)爲每個音符指定一個備用名稱。有沒有辦法做到這一點,而不撕裂我的方法呢?

+0

儘管它起作用,但這個原始實現似乎還遠沒有優化,所以請不要擔心將它們分開。設置一個'Map'來執行sym-integer字符串是一種改進(可以很容易地採用'#'註釋)。不必要的字符串連接也是你可能想要查看的東西。 Imho,甚至更好,是通過邏輯確定sym-integer:例如通過第一個音符切換來獲得「基數」,並在'b'/''#'上加/減一個基數以獲得最終數字,這樣您就可以擺脫所有字符串concat和線性搜索等。 –

回答

1

你可以做這樣的

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; 
} 
2

你可能會開始「標準化」輸入注意到預期的輸入。即用所有可能的音符和規範化的映射初始化一個字符串 - >字符串映射。應僅在構造函數中導致一些映射初始化,並在convertToPitch的開頭調用映射方法。

0

可以單獨處理的記號。所以,你的代碼的修改最少:

[...] 
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。您還應該使用Exceptions而不是返回-1;這樣你可以區分格式不正確的字符串(「abbc123」)和超出範圍的值(「B#9」)。
最後,如果我沒有記錯,MIDI節距有偏移(如C0不是0,但是12)。

+0

C0是確實是0. C1是12. http://www.midimountain.com/midi/midi_note_numbers.html – Ellis

+0

這真的取決於你的輸入格式。 [MIDI規範](https://www.midi.org/specifications)(不幸在網上不可用,但請參閱[維基百科文章](https://en.wikipedia.org/wiki/MIDI_Tuning_Standard)) _middle C_是音符編號60.如果您的輸入是[科學音高符號](https://en.wikipedia.org/wiki/Scientific_pitch_notation),那麼_middle C_是C4。所以如果C4是60,那麼C0是12,C-1是0.這是你的代碼遇到麻煩的地方,因爲八度可以是負數。 – danzel

+0

此外,似乎並非所有制造商都遵守科學基準表示法;根據[本博客](http://cote.cc/blog/logic-studio-9-midi-note-numbers),_middle C_是Logic Studio 9中的C3。 – danzel