*/
public static class VersionInfo {
public static final String NAME = "MIDI Chord Helper";
- public static final String VERSION = "Ver.20161229.1";
+ public static final String VERSION = "Ver.20161230.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/";
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import javax.swing.JComponent;
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) { }
public void mouseDragged(MouseEvent e) {
- Component obj = e.getComponent();
- if( obj instanceof ChordLabel ) {
- ChordLabel labelDraggedFrom = (ChordLabel)obj;
- Component obj2 = this.getComponentAt(
+ Component draggedFrom = e.getComponent();
+ if( draggedFrom instanceof ChordLabel ) {
+ ChordLabel labelDraggedFrom = (ChordLabel)draggedFrom;
+ Component draggedTo = getComponentAt(
labelDraggedFrom.getX() + e.getX(),
labelDraggedFrom.getY() + e.getY()
);
- if( obj2 == this ) {
- // Entered gap between chord buttons - do nothing
- return;
- }
- ChordLabel labelDraggedTo = ((obj2 instanceof ChordLabel) ? (ChordLabel)obj2 : null);
+ if( draggedTo == this ) return; // Entered to gap between chord buttons
+ ChordLabel labelDraggedTo = ((draggedTo instanceof ChordLabel) ? (ChordLabel)draggedTo : null);
if( labelDraggedTo == labelDraggedFrom ) {
- // Returned to original chord button
- destinationChordLabel = null;
- return;
- }
- if( destinationChordLabel != null ) {
- // Already touched another chord button
- return;
+ destinationChordLabel = null; return;
}
+ if( destinationChordLabel != null ) return;
Chord chord = labelDraggedFrom.chord.clone();
if( labelDraggedFrom.isMinor ) {
if( labelDraggedTo == null ) { // Out of chord buttons
setSelectedChord(chord);
destinationChordLabel = (labelDraggedTo == null ? labelDraggedFrom : labelDraggedTo ) ;
}
- else if( obj instanceof KeySignatureLabel ) {
- KeySignatureLabel l_src = (KeySignatureLabel)obj;
- Component obj2 = this.getComponentAt(
- l_src.getX() + e.getX(),
- l_src.getY() + e.getY()
+ else if( draggedFrom instanceof KeySignatureLabel ) {
+ KeySignatureLabel keyDraggedFrom = (KeySignatureLabel)draggedFrom;
+ Component draggedTo = getComponentAt(
+ keyDraggedFrom.getX() + e.getX(),
+ keyDraggedFrom.getY() + e.getY()
);
- if( !(obj2 instanceof KeySignatureLabel) ) {
- return;
- }
- KeySignatureLabel l_dst = (KeySignatureLabel)obj2;
- int v = l_dst.co5Value;
+ if( !(draggedTo instanceof KeySignatureLabel) ) return;
+ KeySignatureLabel keyDraggedTo = (KeySignatureLabel)draggedTo;
+ int v = keyDraggedTo.co5Value;
if( (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {
- setKeySignature( new Key(Music.oppositeCo5(v)) );
- }
- else {
- setKeySignature( new Key(v) );
+ v = Music.oppositeCo5(v);
}
+ setKeySignature(new Key(v));
repaint();
}
}
setKeySignature(new Key(Music.transposeCo5(keyCo5, 1)));
return;
}
- if( i < 0 ) // No key char found
- return;
+ if( i < 0 ) return; // No key char found
if( iCol < 0 ) iCol += 12; else if( iCol > N_COLUMNS ) iCol -= 12;
cl = chordLabels[iCol + N_COLUMNS * iRow];
- chord = cl.chord.clone();
+ List<Chord.Interval> intervals = new ArrayList<Chord.Interval>(Arrays.asList(cl.chord.intervals()));
if( shiftPressed ) {
- chord.set(Chord.Interval.SEVENTH);
+ if( ! intervals.contains(Chord.Interval.SEVENTH) ) {
+ intervals.add(Chord.Interval.SEVENTH);
+ }
}
- // specify by previous key
else if( pcKeyNextShift7 == null ) {
- chord.clear(Chord.Interval.SEVENTH);
- }
- else {
- chord.set(pcKeyNextShift7);
+ intervals.remove(Chord.Interval.SEVENTH);
}
+ else intervals.add(pcKeyNextShift7);
if( e.isAltDown() ) {
if( cl.isSus4 ) {
- chord.set(Chord.Interval.MAJOR); // To cancel sus4
- chord.set(Chord.Interval.SHARP5);
- }
- else {
- chord.set(Chord.Interval.FLAT5);
+ intervals.remove(Chord.Interval.SUS4);
+ intervals.add(Chord.Interval.SHARP5);
}
+ else intervals.add(Chord.Interval.FLAT5);
+ }
+ if( e.isControlDown() ) {
+ // TODO: ^Hなど、ここに到達する前に特殊な keyChar がやってきてしまうことがある
+ intervals.add(Chord.Interval.NINTH);
}
- if( e.isControlDown() ) chord.set(Chord.Interval.NINTH);
+ chord = new Chord(cl.chord, intervals);
if( selectedChordLabel != null ) clear();
(selectedChordLabel = cl).setSelection(true);
setSelectedChord(chord);
};
/**
- * 現在有効な構成音(ルート、ベースは除く)の音程(初期値はメジャーコードの構成音)
+ * 現在有効な構成音(ルート、ベースは除く)の音程
*/
- private Map<OffsetIndex, Interval> offsets = new HashMap<OffsetIndex, Interval>() {
- private void set(Interval itv) { put(itv.getChromaticOffsetIndex(), itv); }
- { set(Interval.MAJOR); set(Interval.PARFECT5); }
- };
+ private Map<OffsetIndex, Interval> offsets;
/**
* このコードのルート音
*/
/**
* 指定されたルート音と構成音を持つコードを構築します。
* @param root ルート音(ベース音としても使う)
- * @param itvs その他の構成音の音程
+ * @param intervals その他の構成音の音程(長三度、完全五度は指定しなくてもデフォルトで設定されます)
*/
- public Chord(NoteSymbol root, Interval... itvs) { this(root, root, itvs); }
+ public Chord(NoteSymbol root, Interval... intervals) {
+ this(root, root, intervals);
+ }
/**
- * 指定されたルート音、ベース音、構成音(可変個数)を持つコードを構築します。
+ * 指定されたルート音、ベース音、構成音を持つコードを構築します。
* @param root ルート音
* @param bass ベース音
- * @param itvs その他の構成音の音程
+ * @param intervals その他の構成音の音程(長三度、完全五度は指定しなくてもデフォルトで設定されます)
*/
- public Chord(NoteSymbol root, NoteSymbol bass, Interval... itvs) {
- this(root, bass, Arrays.asList(itvs));
+ public Chord(NoteSymbol root, NoteSymbol bass, Interval... intervals) {
+ this(root, bass, Arrays.asList(intervals));
}
/**
- * 指定されたルート音、ベース音、構成音(コレクション)を持つコードを構築します。
+ * 指定されたルート音、ベース音、構成音を持つコードを構築します。
* @param root ルート音
* @param bass ベース音
- * @param itvs その他の構成音の音程
+ * @param intervals その他の構成音の音程(長三度、完全五度は指定しなくてもデフォルトで設定されます)
*/
- public Chord(NoteSymbol root, NoteSymbol bass, Collection<Interval> itvs) {
+ public Chord(NoteSymbol root, NoteSymbol bass, Collection<Interval> intervals) {
rootNoteSymbol = root;
bassNoteSymbol = bass;
- for(Interval itv : itvs) if(itv != null) set(itv);
+ offsets = new HashMap<>();
+ set(Interval.MAJOR);
+ set(Interval.PARFECT5);
+ set(intervals);
}
/**
- * 元のコードの構成音の一部を変更した新しいコードを構築します。
+ * 元のコードのルート音、ベース音以外の構成音の一部を変更した新しいコードを構築します。
* @param chord 元のコード
- * @param itvs ルート音、ベース音を除いた、変更したい構成音の音程
+ * @param intervals 変更したい構成音の音程(ルート音、ベース音を除く)
+ * @throws NullPointerException 元のコードにnullが指定された場合
*/
- public Chord(Chord original, Interval... itvs) {
- this(original.rootNoteSymbol, original.bassNoteSymbol);
+ public Chord(Chord original, Interval... intervals) {
+ this(original, Arrays.asList(intervals));
+ }
+ /**
+ * 元のコードのルート音、ベース音以外の構成音の一部を変更した新しいコードを構築します。
+ * @param chord 元のコード
+ * @param intervals 変更したい構成音の音程(ルート音、ベース音を除く)
+ * @throws NullPointerException 元のコードにnullが指定された場合
+ */
+ public Chord(Chord original, Collection<Interval> intervals) {
+ rootNoteSymbol = original.rootNoteSymbol;
+ bassNoteSymbol = original.bassNoteSymbol;
offsets = new HashMap<>(original.offsets);
- for(Interval itv : itvs) if(itv != null) set(itv);
+ set(intervals);
}
/**
* 指定された調と同名のコードを構築します。
* @param key 調
+ * @throws NullPointerException 調にnullが指定された場合
*/
public Chord(Key key) {
- int keyCo5 = key.toCo5();
- if( key.majorMinor() == Key.MajorMinor.MINOR ) {
+ offsets = new HashMap<>(); set(Interval.PARFECT5);
+ int keyCo5 = key.toCo5(); if( key.majorMinor() == Key.MajorMinor.MINOR ) {
keyCo5 += 3; set(Interval.MINOR);
- }
+ } else set(Interval.MAJOR);
bassNoteSymbol = rootNoteSymbol = new NoteSymbol(keyCo5);
}
/**
- * コード名の文字列からコードを構築します。
- * @param chordSymbol コード名の文字列
+ * コード名からコードを構築します。
+ * @param chordSymbol コード名
+ * @throws NullPointerException コード名にnullが指定された場合
*/
public Chord(String chordSymbol) {
+ offsets = new HashMap<>();
+ set(Interval.MAJOR);
+ set(Interval.PARFECT5);
//
- // 分数コードの分子と分母に分ける
- String parts[] = chordSymbol.trim().split("(/|on)");
- if( parts.length == 0 ) return;
- //
- // ルート音とベース音を設定
- rootNoteSymbol = new NoteSymbol(parts[0]);
- if( parts.length > 1 && ! parts[0].equals(parts[1]) ) {
- bassNoteSymbol = new NoteSymbol(parts[1]);
+ // 分数コードの分子と分母に分け、先頭の音名を取り込む
+ String rootOnBass[] = chordSymbol.trim().split("(/|on)");
+ if( rootOnBass.length == 0 ) {
+ bassNoteSymbol = rootNoteSymbol = new NoteSymbol();
+ return;
+ }
+ rootNoteSymbol = new NoteSymbol(rootOnBass[0]);
+ if( rootOnBass.length > 1 && ! rootOnBass[0].equals(rootOnBass[1]) ) {
+ // 分子(ルート音)と異なる分母(ベース音)が指定されていた場合
+ bassNoteSymbol = new NoteSymbol(rootOnBass[1]);
} else {
bassNoteSymbol = rootNoteSymbol;
}
- // 先頭の音名はもういらないので削除
- String suffix = parts[0].replaceFirst("^[A-G][#bx]*","");
+ // 先頭の音名はもういらないので削除し、サフィックスだけ残す
+ String suffix = rootOnBass[0].replaceFirst("^[A-G][#bx]*","");
//
- // () があれば、その中身を取り出す
- String suffixParts[] = suffix.split("[\\(\\)]");
- if( suffixParts.length == 0 ) return;
+ // ()の中と外に分ける
+ String suffixWithParen[] = suffix.split("[\\(\\)]");
+ if( suffixWithParen.length == 0 ) return;
String suffixParen = "";
- if( suffixParts.length > 1 ) {
- suffixParen = suffixParts[1];
- suffix = suffixParts[0];
+ if( suffixWithParen.length > 1 ) {
+ suffixParen = suffixWithParen[1];
+ suffix = suffixWithParen[0];
}
// +5 -5 aug dim
if( suffix.matches(".*(\\+5|aug|#5).*") ) set(Interval.FLAT5);
else if( suffix.matches(".*(6|dim[79]).*") ) set(Interval.SIXTH);
else if( suffix.matches(".*7.*") ) set(Interval.SEVENTH);
//
- // minor sus4 (maj7 と間違えないように比較しつつ、mmaj7も解釈させる)
- if( suffix.matches(".*m.*") && ! suffix.matches(".*ma.*") || suffix.matches(".*mma.*") ) set(Interval.MINOR);
+ // minor sus4 (m と maj7 を間違えないよう比較しつつ、mmaj7 も認識させる)
+ if( suffix.matches(".*m.*") && ! suffix.matches(".*ma.*") || suffix.matches(".*mma.*") ) {
+ set(Interval.MINOR);
+ }
else if( suffix.matches(".*sus4.*") ) set(Interval.SUS4);
//
// 9th の判定
* コードの同一性を判定します。ルート音、ベース音の異名同音は異なるものとみなされます。
* @param anObject 比較対象
* @return 等しければtrue
+ * @see #equalsEnharmonically(Chord)
*/
@Override
public boolean equals(Object anObject) {
* ルート音、ベース音の異名同音を同じとみなしたうえで、コードの同一性を判定します。
* @param another 比較対象のコード
* @return 等しければtrue
+ * @see #equals(Object)
*/
public boolean equalsEnharmonically(Chord another) {
if( another == this ) return true;
}
/**
* 指定した音程の構成音を設定します。
- * @param itv 設定する音程
+ * @param interval 設定する音程
+ */
+ public void set(Interval interval) {
+ offsets.put(interval.getChromaticOffsetIndex(), interval);
+ }
+ /**
+ * 指定した複数の音程の構成音を設定します。
+ * @param intervals 設定する音程
*/
- public void set(Interval itv) {
- offsets.put(itv.getChromaticOffsetIndex(), itv);
+ protected void set(Collection<Interval> intervals) {
+ for(Interval itv : intervals) if(itv != null) set(itv);
}
/**
* 指定した音程の構成音をクリアします。
package camidion.chordhelper.music;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Vector;
import java.util.regex.Pattern;
key = new Key(keyCo5, Key.MajorMinor.MAJOR);
lines = new Vector<Line>();
Line line = new Line();
- boolean is_end;
+ boolean isEnd;
Chord chord, prevChord = new Chord(new NoteSymbol(keyCo5));
int co5Offset, prevCo5Offset;
double r;
for( int mp=0; mp<measureLength; mp++ ) {
- is_end = (mp == 0 || mp == measureLength - 1); // 最初または最後の小節かを覚えておく
+ isEnd = (mp == 0 || mp == measureLength - 1); // 最初または最後の小節かを覚えておく
Measure measure = new Measure();
ChordStroke lastChordStroke = null;
for( int i=0; i<timeSignatureUpper; i++ ) {
}
co5Offset = 0;
prevCo5Offset = prevChord.rootNoteSymbol().toCo5() - keyCo5;
- if( ! is_end ) {
+ if( ! isEnd ) {
//
// 最初または最後の小節は常にトニックにする。
// 完全五度ずつ下がる進行を基本としつつ、時々そうでない進行も出現するようにする。
} while( co5Offset == prevCo5Offset );
}
}
- chord = new Chord(new NoteSymbol(keyCo5 + co5Offset));
+ NoteSymbol rootNote = new NoteSymbol(keyCo5 + co5Offset);
+ List<Chord.Interval> intervals = new ArrayList<>();
switch(co5Offset) {
// ルート音ごとに、7th などの付加や、メジャーマイナー反転を行う確率を決める
case 5: // VII
if( Math.random() < 0.5 ) {
// m7-5
- chord.set(Chord.Interval.MINOR);
- chord.set(Chord.Interval.FLAT5);
+ intervals.add(Chord.Interval.MINOR);
+ intervals.add(Chord.Interval.FLAT5);
}
if( Math.random() < 0.8 )
- chord.set(Chord.Interval.SEVENTH);
+ intervals.add(Chord.Interval.SEVENTH);
break;
case 4: // Secondary dominant (III)
if( prevCo5Offset == 5 ) {
// ルートが長7度→長3度の進行のとき、反転確率を上げる。
// (ハ長調でいう Bm7-5 の次に E7 を出現しやすくする)
- if( Math.random() < 0.2 ) chord.set(Chord.Interval.MINOR);
+ if( Math.random() < 0.2 ) intervals.add(Chord.Interval.MINOR);
}
else {
- if( Math.random() < 0.8 ) chord.set(Chord.Interval.MINOR);
+ if( Math.random() < 0.8 ) intervals.add(Chord.Interval.MINOR);
}
- if( Math.random() < 0.7 ) chord.set(Chord.Interval.SEVENTH);
+ if( Math.random() < 0.7 ) intervals.add(Chord.Interval.SEVENTH);
break;
case 3: // VI
- if( Math.random() < 0.8 ) chord.set(Chord.Interval.MINOR);
- if( Math.random() < 0.7 ) chord.set(Chord.Interval.SEVENTH);
+ if( Math.random() < 0.8 ) intervals.add(Chord.Interval.MINOR);
+ if( Math.random() < 0.7 ) intervals.add(Chord.Interval.SEVENTH);
break;
case 2: // II
- if( Math.random() < 0.8 ) chord.set(Chord.Interval.MINOR);
- if( Math.random() < 0.7 ) chord.set(Chord.Interval.SEVENTH);
+ if( Math.random() < 0.8 ) intervals.add(Chord.Interval.MINOR);
+ if( Math.random() < 0.7 ) intervals.add(Chord.Interval.SEVENTH);
break;
case 1: // Dominant (V)
- if( Math.random() < 0.1 ) chord.set(Chord.Interval.MINOR);
- if( Math.random() < 0.3 ) chord.set(Chord.Interval.SEVENTH);
- if( Math.random() < 0.2 ) chord.set(Chord.Interval.NINTH);
+ if( Math.random() < 0.1 ) intervals.add(Chord.Interval.MINOR);
+ if( Math.random() < 0.3 ) intervals.add(Chord.Interval.SEVENTH);
+ if( Math.random() < 0.2 ) intervals.add(Chord.Interval.NINTH);
break;
case 0: // Tonic(ここでマイナーで終わるとさみしいので setMinorThird() はしない)
- if( Math.random() < 0.2 ) chord.set(Chord.Interval.MAJOR_SEVENTH);
- if( Math.random() < 0.2 ) chord.set(Chord.Interval.NINTH);
+ if( Math.random() < 0.2 ) intervals.add(Chord.Interval.MAJOR_SEVENTH);
+ if( Math.random() < 0.2 ) intervals.add(Chord.Interval.NINTH);
break;
case -1: // Sub-dominant (IV)
if( Math.random() < 0.1 ) {
- chord.set(Chord.Interval.MINOR);
- if( Math.random() < 0.3 ) chord.set(Chord.Interval.SEVENTH);
+ intervals.add(Chord.Interval.MINOR);
+ if( Math.random() < 0.3 ) intervals.add(Chord.Interval.SEVENTH);
}
else
- if( Math.random() < 0.2 ) chord.set(Chord.Interval.MAJOR_SEVENTH);
- if( Math.random() < 0.2 ) chord.set(Chord.Interval.NINTH);
+ if( Math.random() < 0.2 ) intervals.add(Chord.Interval.MAJOR_SEVENTH);
+ if( Math.random() < 0.2 ) intervals.add(Chord.Interval.NINTH);
break;
}
+ chord = new Chord(rootNote, rootNote, intervals);
measure.add( lastChordStroke = new ChordStroke(chord) );
prevChord = chord;
}