1 package camidion.chordhelper.music;
7 * <p>内部的には次の値を持っています。</p>
9 * <li>五度圏インデックス値。これは調号の♯の数(♭の数は負数)と同じです。</li>
10 * <li>メジャー/マイナーの区別(無指定ありの3値)</li>
12 * <p>これらの値はMIDIのメタメッセージにある調号のパラメータに対応します。
15 public class Key implements Cloneable {
17 * メジャーかマイナーかが特定できていないことを示す値
19 public static final int MAJOR_OR_MINOR = 0;
23 public static final int MAJOR = 1;
27 public static final int MINOR = -1;
35 private int majorMinor;
37 * 調号が空のキー(ハ長調またはイ短調)を構築します。
39 public Key() { setKey(0, MAJOR_OR_MINOR); }
42 * メジャーとマイナーを指定せずに構築します。
44 * @param co5 五度圏インデックス値
46 public Key(int co5) { setKey(co5, MAJOR_OR_MINOR); }
49 * メジャー/マイナーを指定した調を構築します。
51 * @param co5 五度圏インデックス値
52 * @param majorMinor {@link #MAJOR}、{@link #MINOR}、{@link #MAJOR_OR_MINOR} のいずれか
54 public Key(int co5, int majorMinor) {
55 setKey(co5, majorMinor);
59 * メジャー/マイナーの明確な調を構築します。
61 * @param co5 五度圏インデックス値
62 * @param isMinor true:マイナー、false:メジャー
64 public Key(int co5, boolean isMinor) {
68 * MIDIの調データ(メタメッセージ2byte)から調を構築します。
69 * @param midiData MIDIの調データ
71 public Key(byte midiData[]) {
75 * C、Am のような文字列から調を構築します。
76 * @param keySymbol 調を表す文字列
77 * @throw IllegalArgumentException 調を表す文字列が不正の場合
79 public Key(String keySymbol) throws IllegalArgumentException {
80 boolean isMinor = keySymbol.matches(".*m");
81 setKey((new NoteSymbol(keySymbol)).toCo5(isMinor), isMinor);
84 * 指定されたコードと同名の調を構築します。
85 * @param chord コード(和音)
87 public Key(Chord chord) {
88 boolean isMinor = chord.isSet(Chord.Interval.MINOR);
89 setKey(chord.rootNoteSymbol().toCo5(isMinor), isMinor);
93 return new Key(co5, majorMinor);
96 public boolean equals(Object anObject) {
97 if( this == anObject )
99 if( anObject instanceof Key ) {
100 Key another = (Key) anObject;
102 co5 == another.toCo5() &&
103 majorMinor == another.majorMinor() ;
108 public int hashCode() {
109 return majorMinor * 256 + co5 ;
111 private void setKey(int co5, boolean isMinor) {
112 setKey( co5, isMinor ? MINOR : MAJOR );
114 private void setKey(int co5, int majorMinor) {
116 this.majorMinor = majorMinor;
120 * MIDIの調データ(メタメッセージ2byte)を設定します。
121 * @param data MIDIの調データ
123 public void setBytes( byte[] data ) {
124 byte sharpFlat = data.length > 0 ? data[0] : 0;
125 byte isMinor = data.length > 1 ? data[1] : 0;
126 setKey( (int)sharpFlat, isMinor==1 );
129 * MIDIの調データ(メタメッセージ2byte)を生成して返します。
132 public byte[] getBytes() {
133 byte data[] = new byte[2];
134 data[0] = (byte)(co5 & 0xFF);
135 data[1] = (byte)(majorMinor == MINOR ? 1 : 0);
142 public int toCo5() { return co5; }
145 * @return {@link #MAJOR}、{@link #MINOR}、{@link #MAJOR_OR_MINOR} のいずれか
147 public int majorMinor() { return majorMinor; }
150 * @return 相対ドの音階(0~11)
152 public int relativeDo() {
153 return NoteSymbol.toNoteNumber(co5);
158 * マイナーキーの場合は相対ラの音階です。
160 * @return キーのルート音(0~11)
162 public int rootNoteNumber() {
163 int n = relativeDo();
164 return majorMinor==MINOR ? Music.mod12(n-3) : n;
167 * 指定されたノート番号の音が、この調のスケールの構成音か調べます。
168 * メジャーキーの場合はメジャースケール、
169 * マイナーキーの場合はナチュラルマイナースケールとして判断されます。
171 * @param noteNumber ノート番号
172 * @return 指定されたノート番号がこのキーのスケールの構成音ならtrue
174 public boolean isOnScale(int noteNumber) {
175 return Music.isOnScale(noteNumber, co5);
178 * この調を、指定された半音オフセット値だけ移調します。
180 * @param chromaticOffset 半音オフセット値
181 * @return このオブジェクト自身(移調後)
183 public Key transpose(int chromaticOffset) {
184 co5 = Music.transposeCo5(co5, chromaticOffset);
188 * この調に異名同音の調がある場合、その調に置換します。
189 * <p>例えば、♭5個(D♭メジャー)の場合は♯7個(C♯メジャー)に置換されます。
190 * 異名同音の調が存在しないキー(4♯~4♭)に対してこのメソッドを呼び出しても、
194 public void toggleEnharmonically() {
202 * 調が7♭~7♯の範囲に入っていない場合、
205 public void normalize() {
206 if( co5 < -7 || co5 > 7 ) {
207 co5 = Music.mod12( co5 );
208 if( co5 > 6 ) co5 -= Music.SEMITONES_PER_OCTAVE;
213 * これは元の調と同じ調号を持つ、メジャーとマイナーが異なる調です。
215 * <p>メジャーとマイナーの区別が不明な場合、クローンを生成したのと同じことになります。
220 public Key relativeKey() {
221 return new Key(co5, majorMinor * (-1));
225 * これは元の調とルート音が同じで、メジャーとマイナーが異なる調です。
227 * <p>メジャーとマイナーの区別が不明な場合、クローンを生成したのと同じことになります。
229 * 7♭~7♯の範囲をはみ出すことがあります(正規化は行われません)。
234 public Key parallelKey() {
235 switch( majorMinor ) {
236 case MAJOR: return new Key( co5-3, MINOR );
237 case MINOR: return new Key( co5+3, MAJOR );
238 default: return new Key(co5);
242 * 五度圏で真裏にあたる調を生成して返します。
243 * @return 五度圏で真裏にあたる調
245 public Key oppositeKey() {
246 return new Key(Music.oppositeCo5(co5), majorMinor);
249 * この調の文字列表現を C、Am のような形式で返します。
253 public String toString() {
254 return toStringIn(SymbolLanguage.SYMBOL);
257 * この調の文字列表現を、指定された形式で返します。
260 public String toStringIn(SymbolLanguage language) {
261 return language.toStringIn(new NoteSymbol(co5), majorMinor);
265 * 正規化された状態において最大2文字になるよう調整されます。
269 public String signature() {
275 case -2: return "bb";
277 if( co5 >= 3 && co5 <= 7 ) return co5 + "#" ;
278 else if( co5 <= -3 && co5 >= -7 ) return (-co5) + "b" ;
286 public String signatureDescription() {
288 case 0: return "no sharps or flats";
289 case 1: return "1 sharp";
290 case -1: return "1 flat";
291 default: return co5 < 0 ? (-co5) + " flats" : co5 + " sharps" ;