OSDN Git Service

リファクタリング(音名インデックス計算の最適化など)
authorAkiyoshi Kamide <kamide@yk.rim.or.jp>
Sun, 27 Nov 2016 17:03:58 +0000 (02:03 +0900)
committerAkiyoshi Kamide <kamide@yk.rim.or.jp>
Sun, 27 Nov 2016 17:03:58 +0000 (02:03 +0900)
src/camidion/chordhelper/ChordHelperApplet.java
src/camidion/chordhelper/chordmatrix/ChordMatrix.java
src/camidion/chordhelper/midieditor/KeySignatureLabel.java
src/camidion/chordhelper/midieditor/KeySignatureSelecter.java
src/camidion/chordhelper/music/Chord.java
src/camidion/chordhelper/music/Key.java
src/camidion/chordhelper/music/MIDISpec.java
src/camidion/chordhelper/music/NoteSymbol.java
src/camidion/chordhelper/music/NoteSymbolLanguage.java [new file with mode: 0644]
src/camidion/chordhelper/music/SymbolLanguage.java [deleted file]

index 808ca65..c7c5dc5 100644 (file)
@@ -283,7 +283,7 @@ public class ChordHelperApplet extends JApplet {
         */
        public static class VersionInfo {
                public static final String      NAME = "MIDI Chord Helper";
-               public static final String      VERSION = "Ver.20161123.1";
+               public static final String      VERSION = "Ver.20161127.1";
                public static final String      COPYRIGHT = "Copyright (C) 2004-2016";
                public static final String      AUTHER = "@きよし - Akiyoshi Kamide";
                public static final String      URL = "http://www.yk.rim.or.jp/~kamide/music/chordhelper/";
index de63731..70ba611 100644 (file)
@@ -37,7 +37,7 @@ import camidion.chordhelper.music.Chord;
 import camidion.chordhelper.music.Key;
 import camidion.chordhelper.music.Music;
 import camidion.chordhelper.music.NoteSymbol;
-import camidion.chordhelper.music.SymbolLanguage;
+import camidion.chordhelper.music.NoteSymbolLanguage;
 
 /**
  * MIDI Chord Helper 用のコードボタンマトリクス
@@ -174,7 +174,7 @@ public class ChordMatrix extends JPanel
                        }
                        else {
                                tip += key.signatureDescription() + " " +
-                                       key.toStringIn(SymbolLanguage.IN_JAPANESE);
+                                       key.toStringIn(NoteSymbolLanguage.IN_JAPANESE);
                                if( v == 0 ) {
                                        setIcon(new ButtonIcon(ButtonIcon.NATURAL_ICON));
                                }
index 2eed35c..27cb202 100644 (file)
@@ -3,7 +3,7 @@ package camidion.chordhelper.midieditor;
 import javax.swing.JLabel;
 
 import camidion.chordhelper.music.Key;
-import camidion.chordhelper.music.SymbolLanguage;
+import camidion.chordhelper.music.NoteSymbolLanguage;
 
 /**
  * 調表示ラベル
@@ -22,8 +22,8 @@ public class KeySignatureLabel extends JLabel {
                }
                setText( "key:" + key.toString() );
                setToolTipText(
-                       "Key: " + key.toStringIn(SymbolLanguage.NAME)
-                       + " "  + key.toStringIn(SymbolLanguage.IN_JAPANESE)
+                       "Key: " + key.toStringIn(NoteSymbolLanguage.NAME)
+                       + " "  + key.toStringIn(NoteSymbolLanguage.IN_JAPANESE)
                        + " (" + key.signatureDescription() + ")"
                );
                setEnabled(true);
index 48eff59..0dfc99f 100644 (file)
@@ -9,7 +9,7 @@ import javax.swing.JLabel;
 import javax.swing.JPanel;
 
 import camidion.chordhelper.music.Key;
-import camidion.chordhelper.music.SymbolLanguage;
+import camidion.chordhelper.music.NoteSymbolLanguage;
 
 /**
  * 調性選択
@@ -46,8 +46,8 @@ public class KeySignatureSelecter extends JPanel implements ActionListener {
        private void updateToolTipText() {
                Key key = getKey();
                keysigCombobox.setToolTipText(
-                       "Key: " + key.toStringIn( SymbolLanguage.NAME )
-                       + " "  + key.toStringIn( SymbolLanguage.IN_JAPANESE )
+                       "Key: " + key.toStringIn( NoteSymbolLanguage.NAME )
+                       + " "  + key.toStringIn( NoteSymbolLanguage.IN_JAPANESE )
                        + " (" + key.signatureDescription() + ")"
                );
        }
index 294afc6..e20dedc 100644 (file)
@@ -526,9 +526,9 @@ public class Chord implements Cloneable {
         * @return コードの説明(英語)
         */
        public String toName() {
-               String chord_name = rootNoteSymbol.toStringIn(SymbolLanguage.NAME) + nameSuffix() ;
+               String chord_name = rootNoteSymbol.toStringIn(NoteSymbolLanguage.NAME) + nameSuffix() ;
                if( ! rootNoteSymbol.equals(bassNoteSymbol) ) {
-                       chord_name += " on " + bassNoteSymbol.toStringIn(SymbolLanguage.NAME);
+                       chord_name += " on " + bassNoteSymbol.toStringIn(NoteSymbolLanguage.NAME);
                }
                return chord_name;
        }
index 0caac4b..1ac709b 100644 (file)
@@ -51,9 +51,7 @@ public class Key implements Cloneable {
         * @param co5 五度圏インデックス値
         * @param majorMinor {@link #MAJOR}、{@link #MINOR}、{@link #MAJOR_OR_MINOR} のいずれか
         */
-       public Key(int co5, int majorMinor) {
-               setKey(co5, majorMinor);
-       }
+       public Key(int co5, int majorMinor) { setKey(co5, majorMinor); }
        /**
         * 指定の五度圏インデックス値を持つ、
         * メジャー/マイナーの明確な調を構築します。
@@ -61,16 +59,12 @@ public class Key implements Cloneable {
         * @param co5 五度圏インデックス値
         * @param isMinor true:マイナー、false:メジャー
         */
-       public Key(int co5, boolean isMinor) {
-               setKey(co5, isMinor);
-       }
+       public Key(int co5, boolean isMinor) { setKey(co5, isMinor); }
        /**
         * MIDIの調データ(メタメッセージ2byte)から調を構築します。
         * @param midiData MIDIの調データ
         */
-       public Key(byte midiData[]) {
-               setBytes(midiData);
-       }
+       public Key(byte midiData[]) { setBytes(midiData); }
        /**
         * C、Am のような文字列から調を構築します。
         * @param keySymbol 調を表す文字列
@@ -89,13 +83,10 @@ public class Key implements Cloneable {
                setKey(chord.rootNoteSymbol().toCo5(isMinor), isMinor);
        }
        @Override
-       public Key clone() {
-               return new Key(co5, majorMinor);
-       }
+       public Key clone() { return new Key(co5, majorMinor); }
        @Override
        public boolean equals(Object anObject) {
-               if( this == anObject )
-                       return true;
+               if( this == anObject ) return true;
                if( anObject instanceof Key ) {
                        Key another = (Key) anObject;
                        return
@@ -105,9 +96,7 @@ public class Key implements Cloneable {
                return false;
        }
        @Override
-       public int hashCode() {
-               return majorMinor * 256 + co5 ;
-       }
+       public int hashCode() { return majorMinor * 256 + co5 ; }
        private void setKey(int co5, boolean isMinor) {
                setKey( co5, isMinor ? MINOR : MAJOR );
        }
@@ -149,9 +138,7 @@ public class Key implements Cloneable {
         * 相対ドの音階を返します。
         * @return 相対ドの音階(0~11)
         */
-       public int relativeDo() {
-               return NoteSymbol.toNoteNumber(co5);
-       }
+       public int relativeDo() { return NoteSymbol.toNoteNumber(co5); }
        /**
         * この調のルート音を返します。
         * メジャーキーの場合は相対ド、
@@ -171,9 +158,7 @@ public class Key implements Cloneable {
         * @param noteNumber ノート番号
         * @return 指定されたノート番号がこのキーのスケールの構成音ならtrue
         */
-       public boolean isOnScale(int noteNumber) {
-               return Music.isOnScale(noteNumber, co5);
-       }
+       public boolean isOnScale(int noteNumber) { return Music.isOnScale(noteNumber, co5); }
        /**
         * この調を、指定された半音オフセット値だけ移調します。
         *
@@ -192,10 +177,7 @@ public class Key implements Cloneable {
         * </p>
         */
        public void toggleEnharmonically() {
-               if( co5 > 4 )
-                       co5 -= 12;
-               else if( co5 < -4 )
-                       co5 += 12;
+               if( co5 > 4 ) co5 -= 12; else if( co5 < -4 ) co5 += 12;
        }
        /**
         * この調を正規化します。
@@ -217,9 +199,7 @@ public class Key implements Cloneable {
         *
         * @return 平行調
         */
-       public Key relativeKey() {
-               return new Key(co5, majorMinor * (-1));
-       }
+       public Key relativeKey() { return new Key(co5, -majorMinor); }
        /**
         * 同主調を生成して返します。
         * これは元の調とルート音が同じで、メジャーとマイナーが異なる調です。
@@ -242,23 +222,19 @@ public class Key implements Cloneable {
         * 五度圏で真裏にあたる調を生成して返します。
         * @return 五度圏で真裏にあたる調
         */
-       public Key oppositeKey() {
-               return new Key(Music.oppositeCo5(co5), majorMinor);
-       }
+       public Key oppositeKey() { return new Key(Music.oppositeCo5(co5), majorMinor); }
        /**
         * この調の文字列表現を C、Am のような形式で返します。
         * @return この調の文字列表現
         */
        @Override
-       public String toString() {
-               return toStringIn(SymbolLanguage.SYMBOL);
-       }
+       public String toString() { return toStringIn(NoteSymbolLanguage.SYMBOL); }
        /**
         * この調の文字列表現を、指定された形式で返します。
         * @return この調の文字列表現
         */
-       public String toStringIn(SymbolLanguage language) {
-               return language.keyOf(new NoteSymbol(co5), majorMinor);
+       public String toStringIn(NoteSymbolLanguage language) {
+               return language.keyStringOf(new NoteSymbol(co5), majorMinor);
        }
        /**
         * 調号を表す半角文字列を返します。
index 2cddb91..ab59912 100644 (file)
@@ -851,7 +851,7 @@ public class MIDISpec {
                                if( msgdata.length == 2 ) {
                                        Key key = new Key(msgdata);
                                        str += ": " + key.signatureDescription();
-                                       str += " (" + key.toStringIn(SymbolLanguage.NAME) + ")";
+                                       str += " (" + key.toStringIn(NoteSymbolLanguage.NAME) + ")";
                                        break;
                                }
                                str += ": Size not 2 byte : data=(";
index cca5fec..1639dd4 100644 (file)
@@ -13,6 +13,8 @@ package camidion.chordhelper.music;
  * </p>
  */
 public class NoteSymbol implements Cloneable {
+       private static final int INDEX_OF_A = NoteSymbolLanguage.SYMBOL.indexOf("A");
+       private static final int INDEX_OF_C = NoteSymbolLanguage.SYMBOL.indexOf("C");
        /**
         * メジャーキー基準の五度圏インデックス値
         */
@@ -40,12 +42,10 @@ public class NoteSymbol implements Cloneable {
         *  指定の音名が空、またはA~Gの範囲を外れている場合
         */
        public NoteSymbol(String noteSymbol) {
-               this(SymbolLanguage.SYMBOL.majorCo5Of(noteSymbol.trim()));
+               this(NoteSymbolLanguage.SYMBOL.indexOf(noteSymbol) - INDEX_OF_C);
        }
        @Override
-       protected NoteSymbol clone() {
-               return new NoteSymbol(majorCo5);
-       }
+       protected NoteSymbol clone() { return new NoteSymbol(majorCo5); }
        /**
         * この音階が指定されたオブジェクトと等しいか調べます。
         *
@@ -57,13 +57,9 @@ public class NoteSymbol implements Cloneable {
         */
        @Override
        public boolean equals(Object anObject) {
-               if( this == anObject )
-                       return true;
-               if( anObject instanceof NoteSymbol ) {
-                       NoteSymbol another = (NoteSymbol) anObject;
-                       return majorCo5 == another.majorCo5;
-               }
-               return false;
+               if( this == anObject ) return true;
+               if( ! (anObject instanceof NoteSymbol) ) return false;
+               return majorCo5 == ((NoteSymbol)anObject).majorCo5;
        }
        /**
         * この音階のハッシュコード値として、
@@ -98,9 +94,7 @@ public class NoteSymbol implements Cloneable {
         * @param isMinor マイナーのときtrue
         * @return 五度圏インデックス値
         */
-       public int toCo5(boolean isMinor) {
-               return isMinor ? majorCo5 - 3 : majorCo5;
-       }
+       public int toCo5(boolean isMinor) { return isMinor ? majorCo5 - 3 : majorCo5; }
        /**
         * ノート番号(0~11)を返します。
         * <p>これはMIDIノート番号からオクターブ情報を抜いた値と同じです。
@@ -116,15 +110,13 @@ public class NoteSymbol implements Cloneable {
         * @return この音階の文字列表現
         */
        @Override
-       public String toString() {
-               return toStringIn(SymbolLanguage.SYMBOL, false);
-       }
+       public String toString() { return toStringIn(NoteSymbolLanguage.SYMBOL); }
        /**
         * 指定した言語モードにおける文字列表現を返します。
         * @param language 言語モード
         * @return 文字列表現
         */
-       public String toStringIn(SymbolLanguage language) {
+       public String toStringIn(NoteSymbolLanguage language) {
                return toStringIn(language, false);
        }
        /**
@@ -139,61 +131,52 @@ public class NoteSymbol implements Cloneable {
         * @param isMinor マイナーのときtrue
         * @return 文字列表現
         */
-       public String toStringIn(SymbolLanguage language, boolean isMinor) {
-               int co5index771 = majorCo5 + 15; // 0(Fbb) -> 7(Fb) -> 14(F) -> 15(C)
-               if( isMinor ) co5index771 += 3; // 15(C) -> 18(Am)
-               if( co5index771 < 0 || co5index771 >= 35 ) {
+       public String toStringIn(NoteSymbolLanguage language, boolean isMinor) {
+               int index = majorCo5 + (isMinor ? INDEX_OF_A : INDEX_OF_C);
+               if( index < 0 || index >= 35 ) {
                        // インデックスOB発生の恐れがある場合
                        // 調号 5b ~ 6# の範囲に収まるルート音となるような異名同音(enharmonic)に置き換える
-                       co5index771 = Music.mod12(co5index771);  // returns 0(Fbb) ... 7(Fb) 8(Cb) 9(Gb) 10(Db) 11(Ab)
+                       index = Music.mod12(index);  // returns 0(Fbb) ... 7(Fb) 8(Cb) 9(Gb) 10(Db) 11(Ab)
                        if( isMinor ) {
                                // 18(Am)
-                               if( co5index771 == 0 )
-                                       co5index771 += Music.SEMITONES_PER_OCTAVE * 2; // 0(Fbbm) -> 24(D#m 5#)
+                               if( index == 0 )
+                                       index += Music.SEMITONES_PER_OCTAVE * 2; // 0(Fbbm) -> 24(D#m 5#)
                                else
-                                       co5index771 += Music.SEMITONES_PER_OCTAVE;  // 1(Cbbm) -> 13(Bbm 5b)
+                                       index += Music.SEMITONES_PER_OCTAVE;  // 1(Cbbm) -> 13(Bbm 5b)
                        }
                        else {
                                // 15(C)
-                               if( co5index771 < 10 )  // 0(Fbb) -> 12(Eb 3b), 9(Gb) -> 21(F# 6#)
-                                       co5index771 += Music.SEMITONES_PER_OCTAVE;
+                               if( index < 10 )  // 0(Fbb) -> 12(Eb 3b), 9(Gb) -> 21(F# 6#)
+                                       index += Music.SEMITONES_PER_OCTAVE;
                        }
                }
-               return language.noteSymbolOf(co5index771);
+               return language.stringOf(index);
        }
        /**
         * 指定の最大文字数の範囲で、MIDIノート番号が示す音名を返します。
+        * <p>白鍵の場合は A ~ G までの文字、黒鍵の場合は#と♭の両方の表現を返します。
+        * ただし、制限文字数の指定により#と♭の両方を返せないことがわかった場合、
+        * 五度圏のC/Amに近いキーでよく使われるほうの表記(C# Eb F# Ab Bb)だけを返します。
+        * </p>
         * <p>ノート番号だけでは物理的な音階情報しか得られないため、
         * 白鍵で#♭のついた音階表現(B#、Cb など)、
         * ダブルシャープ、ダブルフラットを使った表現は返しません。
         * </p>
-        * <p>白鍵の場合は A ~ G までの文字、黒鍵の場合は#と♭の両方の表現を返します。
-        * ただし、制限文字数の指定により、#と♭の両方を返せないことがわかった場合は、
-        * 五度圏で値0(キー C / Am)からの距離が、メジャー、マイナーの両方を含めて
-        * 近くにあるほうの表現(C# Eb F# Ab Bb)のみを返します。
-        * </p>
-        * @param noteNo MIDIノート番号
+        * @param noteNumber MIDIノート番号
         * @param maxChars 最大文字数
         * @return MIDIノート番号が示す音名
         */
-       public static String noteNumberToSymbol(int noteNo, int maxChars) {
-               int co5 = Music.mod12(Music.reverseCo5(noteNo));
-               if( co5 == 11 ) {
-                       return (new NoteSymbol(-1)).toString();
-               }
-               else if( co5 >= 6 ) {
-                       if( maxChars >= 7 ) {
-                               return
-                                       (new NoteSymbol(co5)).toString() + " / " +
-                                       (new NoteSymbol(co5 - 12)).toString();
-                       }
-                       else {
-                               // String capacity not enough
-                               // Select only one note (sharped or flatted)
-                               return (new NoteSymbol(co5 - ((co5 >= 8) ? 12 : 0))).toString();
-                       }
-               }
-               else return (new NoteSymbol(co5)).toString();
+       public static String noteNumberToSymbol(int noteNumber, int maxChars) {
+               int co5 = Music.mod12(Music.reverseCo5(noteNumber));
+               if( co5 == 11 ) co5 -= Music.SEMITONES_PER_OCTAVE; // E# -> F
+               if( co5 < 6 ) return (new NoteSymbol(co5)).toString(); // F C G D A E B
+
+               if( maxChars >= "F# / Gb".length() ) return
+                               (new NoteSymbol(co5)).toString() + " / " +
+                               (new NoteSymbol(co5 - Music.SEMITONES_PER_OCTAVE)).toString();
+
+               if( co5 >= 8 ) co5 -= Music.SEMITONES_PER_OCTAVE; // G# -> Ab, D# -> Eb, A# -> Bb
+               return (new NoteSymbol(co5)).toString(); // C# Eb F# Ab Bb
        }
        /**
         * 最大256文字の範囲で、MIDIノート番号が示す音名を返します。
diff --git a/src/camidion/chordhelper/music/NoteSymbolLanguage.java b/src/camidion/chordhelper/music/NoteSymbolLanguage.java
new file mode 100644 (file)
index 0000000..82ef7f6
--- /dev/null
@@ -0,0 +1,122 @@
+package camidion.chordhelper.music;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 音階シンボルの言語モードによる違いを定義します。
+ * <p>各音階にはインデックス値が割り当てられます。
+ * このインデックス値は、五度圏順で並んだ変化記号のない7音階(FCGDAEB)に対するインデックス値
+ * x (0~6) と、
+ * 変化記号(ダブルフラット、フラット、なし、シャープ、ダブルシャープ)に対するインデックス値
+ * y (0~4) に基づいて、次の式で計算されます。</p>
+ * <pre>インデックス値 = 5y + x</pre>
+ * <p>このインデックス値の範囲は下記のように 0~34 となります。</p>
+ * <pre>Fbb=0, Cbb=1, .. Bb=13, F=14, C=15, .. B=20, F#=21, C#=22, .. B#=27, Fx=28, .. Bx=34</pre>
+ */
+public enum NoteSymbolLanguage {
+       /**
+        * シンボル表記(Bb, F#)
+        */
+       SYMBOL(Arrays.asList("bb","b","","#","x"),"","m"),
+       /**
+        * 英名表記(B flat, F sharp)
+        */
+       NAME(Arrays.asList(" double flat"," flat",""," sharp"," double sharp")," major"," minor"),
+       /**
+        * 日本名表記(変ロ, 嬰ヘ)
+        */
+       IN_JAPANESE(Arrays.asList("重変","変","","嬰","重嬰"),"長調","短調");
+       /**
+        * ♭や♯の表記を、半音下がる数が多いほうから順に並べたリスト
+        */
+       private List<String> sharpFlatList;
+       /**
+        * 音名を五度圏順で並べた7文字
+        */
+       private String notes;
+       /**
+        * メジャーを表す文字列
+        */
+       private String major;
+       /**
+        * マイナーを表す文字列
+        */
+       private String minor;
+       /**
+        * メジャーとマイナーを併記する場合の区切り文字
+        */
+       private String majorMinorDelimiter;
+
+       private NoteSymbolLanguage(List<String> sharpFlatList, String major, String minor) {
+               if( (this.sharpFlatList = sharpFlatList).contains("変") ) {
+                       this.notes = "ヘハトニイホロ";
+                       this.majorMinorDelimiter = "/";
+               } else {
+                       this.notes = "FCGDAEB";
+                       this.majorMinorDelimiter = " / ";
+               }
+               this.major = major;
+               this.minor = minor;
+       }
+       /**
+        * インデックス値に該当する音名を返します。
+        * @param index インデックス値(定義は{@link NoteSymbolLanguage}参照)
+        * @return 音名(例:Bb、B flat、変ロ)
+        * @throws IndexOutOfBoundsException インデックスが範囲を外れている場合
+        */
+       public String stringOf(int index) {
+               int sharpFlatIndex = index / 7; // 0 1 2 3 4
+               int noteSymbolIndex = index - sharpFlatIndex * 7; // 0 1 2 3 4 5 6
+               String note = notes.substring(noteSymbolIndex, noteSymbolIndex+1);
+               String sharpFlat = sharpFlatList.get(sharpFlatIndex);
+               return this == IN_JAPANESE ? sharpFlat + note : note + sharpFlat;
+       }
+       /**
+        * 調の文字列表現を返します。メジャー/マイナーの区別が不明な場合、両方の表現を返します。
+        * @param note 音名
+        * @param majorMinor -1:マイナー 0:不明 1:メジャー
+        * @return 調の文字列表現
+        */
+       public String keyStringOf(NoteSymbol note, int majorMinor) {
+               String majorString = note.toStringIn(this, false) + major;
+               if( majorMinor > 0 ) {
+                       return majorString;
+               }
+               else {
+                       String minorString = note.toStringIn(this, true) + minor;
+                       return majorMinor < 0 ? minorString : majorString + majorMinorDelimiter + minorString ;
+               }
+       }
+       /**
+        * 音名の文字列(英字のみ)に対するインデックス値を返します。
+        * 音名は通常大文字ですが、小文字も認識します。
+        *
+        * @param noteSymbol 音名の文字列
+        * @return インデックス値(定義は{@link NoteSymbolLanguage}参照)
+        * @throws
+        * UnsupportedOperationException このオブジェクトが {@link #IN_JAPANESE} の場合
+        * @throws
+        * NullPointerException 指定された音名がnullの場合
+        * @throws
+        * IllegalArgumentException 指定された音名が空、または[A~G、a~g]の範囲を外れている場合
+        */
+       public int indexOf(String noteSymbol) {
+               if( this == IN_JAPANESE ) throw new UnsupportedOperationException();
+               Objects.requireNonNull(noteSymbol,"Musical note symbol must not be null");
+               String trimmed = noteSymbol.trim();
+               if( trimmed.isEmpty() ) throw new IllegalArgumentException("Empty musical note symbol");
+               char prefix = trimmed.charAt(0);
+               int index = notes.indexOf(Character.toUpperCase(prefix));
+               if( index < 0 ) {
+                       throw new IllegalArgumentException("Unknown musical note symbol ["+noteSymbol+"] not in ["+notes+"]");
+               }
+               String suffix = trimmed.substring(1);
+               for( String sf : sharpFlatList ) {
+                       if( suffix.startsWith(sf) ) break; // bb が先にヒットするので b と間違える心配はない
+                       index += 7;
+               }
+               return index;
+       }
+}
diff --git a/src/camidion/chordhelper/music/SymbolLanguage.java b/src/camidion/chordhelper/music/SymbolLanguage.java
deleted file mode 100644 (file)
index 91e733e..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-package camidion.chordhelper.music;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * シンボルの言語モード(音階、調など)
- */
-public enum SymbolLanguage {
-       /**
-        * シンボル表記(Bb, F#)
-        */
-       SYMBOL(Arrays.asList("bb","b","","#","x"),"FCGDAEB",false,"","m"," / "),
-       /**
-        * 英名表記(B flat, F sharp)
-        */
-       NAME(Arrays.asList(" double flat"," flat",""," sharp"," double sharp"),
-                       "FCGDAEB",false," major"," minor"," / "),
-       /**
-        * 日本名表記(変ロ, 嬰ヘ)
-        */
-       IN_JAPANESE(Arrays.asList("重変","変","","嬰","重嬰"),
-                       "ヘハトニイホロ",true,"長調","短調","/");
-       /**
-        * ♭や♯の表記を、半音下がる数が多いほうから順に並べたリスト
-        */
-       private List<String> sharpFlatList;
-       /**
-        * 音名を五度圏順で並べた文字列(必ず7文字でなければならない)
-        */
-       private String notes;
-       /**
-        * 変化記号が音名の前につく(true)か後ろにつく(false)か
-        * <p>英語の場合は B♭ のように♭が後ろ、
-        * 日本語の場合は「変ロ」のように「変」が前につくことを表します。
-        * </p>
-        */
-       private boolean preSharpFlat;
-       /**
-        * メジャーを表す文字列
-        */
-       private String major;
-       /**
-        * マイナーを表す文字列
-        */
-       private String minor;
-       /**
-        * メジャーとマイナーを併記する場合の区切り文字
-        */
-       private String majorMinorDelimiter;
-
-       private SymbolLanguage(List<String> sharpFlatList, String notes, boolean preSharpFlat,
-               String major, String minor, String majorMinorDelimiter
-       ) {
-               this.sharpFlatList = sharpFlatList;
-               this.notes = notes;
-               this.preSharpFlat = preSharpFlat;
-               this.major = major;
-               this.minor = minor;
-               this.majorMinorDelimiter = majorMinorDelimiter;
-       }
-       /**
-        * 補正した五度圏インデックスに該当する音名を返します。
-        * 負数を避けるため、C=0ではなく、+15した値、
-        * すなわちFbb=0になるよう補正したインデックスを使います。
-        * @param co5index771 補正した五度圏インデックス(範囲:0~34)
-        * (Fbb=0, Cbb=1, .. F=14, C=15, .. F#=21, C#=22, .. B#=27, Fx=28, .. Bx=34)
-        * @return 音名(例:Bb、B flat、変ロ)
-        * @throws IndexOutOfBoundsException 補正した五度圏インデックスが範囲を外れている場合
-        */
-       public String noteSymbolOf(int co5index771) {
-               int sharpFlatIndex = co5index771 / 7; // 0 1 2 3 4
-               int noteSymbolIndex = co5index771 - sharpFlatIndex * 7; // 0 1 2 3 4 5 6
-               String note = notes.substring(noteSymbolIndex, noteSymbolIndex+1);
-               String sharpFlat = sharpFlatList.get(sharpFlatIndex);
-               return preSharpFlat ? sharpFlat + note : note + sharpFlat;
-       }
-       /**
-        * 調の文字列表現を返します。メジャー/マイナーの区別が不明な場合、両方の表現を返します。
-        * @param note 音名
-        * @param majorMinor -1:マイナー 0:不明 1:メジャー
-        * @return 調の文字列表現
-        */
-       public String keyOf(NoteSymbol note, int majorMinor) {
-               String majorString = note.toStringIn(this, false) + major;
-               if( majorMinor > 0 ) {
-                       return majorString;
-               }
-               else {
-                       String minorString = note.toStringIn(this, true) + minor;
-                       return majorMinor < 0 ? minorString : majorString + majorMinorDelimiter + minorString ;
-               }
-       }
-       /**
-        * 音名の文字列を、メジャーキー基準の五度圏インデックス値に変換します。
-        * 音名は通常大文字ですが、小文字も認識します。
-        *
-        * @param noteSymbol 音名の文字列
-        * @return メジャーキー基準の五度圏インデックス値
-        * @throws
-        * IllegalArgumentException 指定された音名が空、または[A~G、a~g]の範囲を外れている場合
-        * @throws
-        * NullPointerException 指定された音名がnullの場合
-        */
-       public int majorCo5Of(String noteSymbol) {
-               if( Objects.requireNonNull(noteSymbol,"Musical note symbol must not be null").isEmpty() ) {
-                       throw new IllegalArgumentException("Empty musical note symbol");
-               }
-               int co5 = notes.indexOf(Character.toUpperCase(noteSymbol.charAt(0)));
-               if( co5 < 0 ) {
-                       throw new IllegalArgumentException("Unknown musical note symbol ["+noteSymbol+"] not in ["+notes+"]");
-               }
-               co5--;
-               int offset = -14;
-               for( String sharpFlat : sharpFlatList ) {
-                       if( ! sharpFlat.isEmpty() && noteSymbol.startsWith(sharpFlat,1) ) {
-                               // 変化記号を発見
-                               // bb のほうが b よりも先にマッチするので誤判定の心配なし
-                               co5 += offset;
-                               break;
-                       }
-                       offset += 7;
-               }
-               return co5;
-       }
-}