OSDN Git Service

コードボタン下のテキスト欄に入力するコードの音名が小文字でも受け付けるようにした
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / music / Key.java
1 package camidion.chordhelper.music;
2
3
4 /**
5  * 調(キー)を表すクラスです。
6  *
7  * <p>内部的には次の値を持っています。</p>
8  * <ul>
9  * <li>五度圏インデックス値。これは調号の♯の数(♭の数は負数)と同じです。</li>
10  * <li>メジャー/マイナーの区別(無指定ありの3値)</li>
11  * </ul>
12  * <p>これらの値はMIDIのメタメッセージにある調号のパラメータに対応します。
13  * </p>
14  */
15 public class Key implements Cloneable {
16         /**
17          * メジャーかマイナーかが特定できていないことを示す値
18          */
19         public static final int MAJOR_OR_MINOR = 0;
20         /**
21          * メジャーキー(長調)
22          */
23         public static final int MAJOR = 1;
24         /**
25          * マイナーキー(短調)
26          */
27         public static final int MINOR = -1;
28         /**
29          * この調の五度圏インデックス値
30          */
31         private int co5;
32         /**
33          * メジャー・マイナーの種別
34          */
35         private int majorMinor;
36         /**
37          * 調号が空のキー(ハ長調またはイ短調)を構築します。
38          */
39         public Key() { setKey(0, MAJOR_OR_MINOR); }
40         /**
41          * 指定の五度圏インデックス値を持つ調を、
42          * メジャーとマイナーを指定せずに構築します。
43          *
44          * @param co5 五度圏インデックス値
45          */
46         public Key(int co5) { setKey(co5, MAJOR_OR_MINOR); }
47         /**
48          * 指定の五度圏インデックス値を持つ、
49          * メジャー/マイナーを指定した調を構築します。
50          *
51          * @param co5 五度圏インデックス値
52          * @param majorMinor {@link #MAJOR}、{@link #MINOR}、{@link #MAJOR_OR_MINOR} のいずれか
53          */
54         public Key(int co5, int majorMinor) {
55                 setKey(co5, majorMinor);
56         }
57         /**
58          * 指定の五度圏インデックス値を持つ、
59          * メジャー/マイナーの明確な調を構築します。
60          *
61          * @param co5 五度圏インデックス値
62          * @param isMinor true:マイナー、false:メジャー
63          */
64         public Key(int co5, boolean isMinor) {
65                 setKey(co5, isMinor);
66         }
67         /**
68          * MIDIの調データ(メタメッセージ2byte)から調を構築します。
69          * @param midiData MIDIの調データ
70          */
71         public Key(byte midiData[]) {
72                 setBytes(midiData);
73         }
74         /**
75          * C、Am のような文字列から調を構築します。
76          * @param keySymbol 調を表す文字列
77          * @throw IllegalArgumentException 調を表す文字列が不正の場合
78          */
79         public Key(String keySymbol) throws IllegalArgumentException {
80                 boolean isMinor = keySymbol.matches(".*m");
81                 setKey((new NoteSymbol(keySymbol)).toCo5(isMinor), isMinor);
82         }
83         /**
84          * 指定されたコードと同名の調を構築します。
85          * @param chord コード(和音)
86          */
87         public Key(Chord chord) {
88                 boolean isMinor = chord.isSet(Chord.Interval.MINOR);
89                 setKey(chord.rootNoteSymbol().toCo5(isMinor), isMinor);
90         }
91         @Override
92         public Key clone() {
93                 return new Key(co5, majorMinor);
94         }
95         @Override
96         public boolean equals(Object anObject) {
97                 if( this == anObject )
98                         return true;
99                 if( anObject instanceof Key ) {
100                         Key another = (Key) anObject;
101                         return
102                                 co5 == another.toCo5() &&
103                                 majorMinor == another.majorMinor() ;
104                 }
105                 return false;
106         }
107         @Override
108         public int hashCode() {
109                 return majorMinor * 256 + co5 ;
110         }
111         private void setKey(int co5, boolean isMinor) {
112                 setKey( co5, isMinor ? MINOR : MAJOR );
113         }
114         private void setKey(int co5, int majorMinor) {
115                 this.co5 = co5;
116                 this.majorMinor = majorMinor;
117                 normalize();
118         }
119         /**
120          * MIDIの調データ(メタメッセージ2byte)を設定します。
121          * @param data MIDIの調データ
122          */
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 );
127         }
128         /**
129          * MIDIの調データ(メタメッセージ2byte)を生成して返します。
130          * @return  MIDIの調データ
131          */
132         public byte[] getBytes() {
133                 byte data[] = new byte[2];
134                 data[0] = (byte)(co5 & 0xFF);
135                 data[1] = (byte)(majorMinor == MINOR ? 1 : 0);
136                 return data;
137         }
138         /**
139          * 五度圏インデックス値を返します。
140          * @return 五度圏インデックス値
141          */
142         public int toCo5() { return co5; }
143         /**
144          * メジャー/マイナーの区別を返します。
145          * @return {@link #MAJOR}、{@link #MINOR}、{@link #MAJOR_OR_MINOR} のいずれか
146          */
147         public int majorMinor() { return majorMinor; }
148         /**
149          * 相対ドの音階を返します。
150          * @return 相対ドの音階(0~11)
151          */
152         public int relativeDo() {
153                 return NoteSymbol.toNoteNumber(co5);
154         }
155         /**
156          * この調のルート音を返します。
157          * メジャーキーの場合は相対ド、
158          * マイナーキーの場合は相対ラの音階です。
159          *
160          * @return キーのルート音(0~11)
161          */
162         public int rootNoteNumber() {
163                 int n = relativeDo();
164                 return majorMinor==MINOR ? Music.mod12(n-3) : n;
165         }
166         /**
167          * 指定されたノート番号の音が、この調のスケールの構成音か調べます。
168          * メジャーキーの場合はメジャースケール、
169          * マイナーキーの場合はナチュラルマイナースケールとして判断されます。
170          *
171          * @param noteNumber ノート番号
172          * @return 指定されたノート番号がこのキーのスケールの構成音ならtrue
173          */
174         public boolean isOnScale(int noteNumber) {
175                 return Music.isOnScale(noteNumber, co5);
176         }
177         /**
178          * この調を、指定された半音オフセット値だけ移調します。
179          *
180          * @param chromaticOffset 半音オフセット値
181          * @return このオブジェクト自身(移調後)
182          */
183         public Key transpose(int chromaticOffset) {
184                 co5 = Music.transposeCo5(co5, chromaticOffset);
185                 return this;
186         }
187         /**
188          * この調に異名同音の調がある場合、その調に置換します。
189          * <p>例えば、♭5個(D♭メジャー)の場合は♯7個(C♯メジャー)に置換されます。
190          * 異名同音の調が存在しないキー(4♯~4♭)に対してこのメソッドを呼び出しても、
191          * 何も変化しません。
192          * </p>
193          */
194         public void toggleEnharmonically() {
195                 if( co5 > 4 )
196                         co5 -= 12;
197                 else if( co5 < -4 )
198                         co5 += 12;
199         }
200         /**
201          * この調を正規化します。
202          * 調が7♭~7♯の範囲に入っていない場合、
203          * その範囲に入るよう調整されます。
204          */
205         public void normalize() {
206                 if( co5 < -7 || co5 > 7 ) {
207                         co5 = Music.mod12( co5 );
208                         if( co5 > 6 ) co5 -= Music.SEMITONES_PER_OCTAVE;
209                 }
210         }
211         /**
212          * 平行調を生成して返します。
213          * これは元の調と同じ調号を持つ、メジャーとマイナーが異なる調です。
214          *
215          * <p>メジャーとマイナーの区別が不明な場合、クローンを生成したのと同じことになります。
216          * </p>
217          *
218          * @return 平行調
219          */
220         public Key relativeKey() {
221                 return new Key(co5, majorMinor * (-1));
222         }
223         /**
224          * 同主調を生成して返します。
225          * これは元の調とルート音が同じで、メジャーとマイナーが異なる調です。
226          *
227          * <p>メジャーとマイナーの区別が不明な場合、クローンを生成したのと同じことになります。
228          * 元の調の♭、♯の数が5個以上の場合、
229          * 7♭~7♯の範囲をはみ出すことがあります(正規化は行われません)。
230          * </p>
231          *
232          * @return 同主調
233          */
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);
239                 }
240         }
241         /**
242          * 五度圏で真裏にあたる調を生成して返します。
243          * @return 五度圏で真裏にあたる調
244          */
245         public Key oppositeKey() {
246                 return new Key(Music.oppositeCo5(co5), majorMinor);
247         }
248         /**
249          * この調の文字列表現を C、Am のような形式で返します。
250          * @return この調の文字列表現
251          */
252         @Override
253         public String toString() {
254                 return toStringIn(SymbolLanguage.SYMBOL);
255         }
256         /**
257          * この調の文字列表現を、指定された形式で返します。
258          * @return この調の文字列表現
259          */
260         public String toStringIn(SymbolLanguage language) {
261                 return language.toStringIn(new NoteSymbol(co5), majorMinor);
262         }
263         /**
264          * 調号を表す半角文字列を返します。
265          * 正規化された状態において最大2文字になるよう調整されます。
266          *
267          * @return 調号を表す半角文字列
268          */
269         public String signature() {
270                 switch(co5) {
271                 case  0: return "==";
272                 case  1: return "#";
273                 case -1: return "b";
274                 case  2: return "##";
275                 case -2: return "bb";
276                 default:
277                         if( co5 >= 3 && co5 <= 7 ) return co5 + "#" ;
278                         else if( co5 <= -3 && co5 >= -7 ) return (-co5) + "b" ;
279                         return "";
280                 }
281         }
282         /**
283          * 調号の説明(英語)を返します。
284          * @return 調号の説明
285          */
286         public String signatureDescription() {
287                 switch(co5) {
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" ;
292                 }
293         }
294 }