package camidion.chordhelper.music;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Vector;
import java.util.regex.Pattern;
public class ChordProgression {
public class TickRange implements Cloneable {
- long start_tick_pos = 0, end_tick_pos = 0;
+ long startTickPos = 0, end_tick_pos = 0;
public TickRange( long tick_pos ) {
- end_tick_pos = start_tick_pos = tick_pos;
+ end_tick_pos = startTickPos = tick_pos;
}
public TickRange( long start_tick_pos, long end_tick_pos ) {
- this.start_tick_pos = start_tick_pos;
+ this.startTickPos = start_tick_pos;
this.end_tick_pos = end_tick_pos;
}
protected TickRange clone() {
- return new TickRange( start_tick_pos, end_tick_pos );
+ return new TickRange( startTickPos, end_tick_pos );
}
public void moveForward() {
- start_tick_pos = end_tick_pos;
+ startTickPos = end_tick_pos;
}
public void moveForward( long duration ) {
- start_tick_pos = end_tick_pos;
+ startTickPos = end_tick_pos;
end_tick_pos += duration;
}
public long duration() {
- return end_tick_pos - start_tick_pos;
+ return end_tick_pos - startTickPos;
}
public boolean contains( long tick ) {
- return ( tick >= start_tick_pos && tick < end_tick_pos );
+ return ( tick >= startTickPos && tick < end_tick_pos );
}
}
class ChordStroke {
- Chord chord; int beat_length; TickRange tick_range = null;
+ Chord chord; int beatLength; TickRange tickRange = null;
public ChordStroke(Chord chord) { this( chord, 1 ); }
public ChordStroke(Chord chord, int beat_length) {
this.chord = chord;
- this.beat_length = beat_length;
+ this.beatLength = beat_length;
}
public String toString() {
String str = chord.toString();
- for( int i=2; i <= beat_length; i++ ) str += " %";
+ for( int i=2; i <= beatLength; i++ ) str += " %";
return str;
}
}
// 時間位置付き歌詞
public class Lyrics {
String text = null;
- Long start_tick_pos = null;
+ Long startTickPos = null;
public Lyrics(String text) { this.text = text; }
public Lyrics(String text, long tick_pos) {
- this.text = text; start_tick_pos = tick_pos;
+ this.text = text; startTickPos = tick_pos;
}
public String toString() { return text; }
}
int n = 0;
for( Object obj : this ) {
if( obj instanceof ChordStroke ) {
- n += ((ChordStroke)obj).beat_length;
+ n += ((ChordStroke)obj).beatLength;
}
}
return n;
int l, l_prev = 0;
for( Object obj : this ) {
if( obj instanceof ChordStroke ) {
- l = ((ChordStroke)obj).beat_length;
+ l = ((ChordStroke)obj).beatLength;
if( l_prev > 0 && l_prev != l ) {
return false;
}
if( last_chord_stroke == null ) {
return 0;
}
- return last_chord_stroke.beat_length += num_beats;
+ return last_chord_stroke.beatLength += num_beats;
}
public String toString() {
String str = "";
= (ChordProgression.ChordStroke)element;
// 小節の先頭と末尾の tick を求める
if( start_tick_pos < 0 ) {
- start_tick_pos = chord_stroke.tick_range.start_tick_pos;
+ start_tick_pos = chord_stroke.tickRange.startTickPos;
}
- end_tick_pos = chord_stroke.tick_range.end_tick_pos;
+ end_tick_pos = chord_stroke.tickRange.end_tick_pos;
}
if( start_tick_pos < 0 || end_tick_pos < 0 ) {
return null;
continue;
ChordProgression.ChordStroke chord_stroke
= (ChordProgression.ChordStroke)element;
- if( chord_stroke.tick_range.contains(tick) ) {
+ if( chord_stroke.tickRange.contains(tick) ) {
return chord_stroke;
}
}
* @param timeSignatureUpper 拍子の分子
*/
public ChordProgression( int measureLength, int timeSignatureUpper ) {
- int key_co5 = (int)(Math.random() * 12) - 5;
- key = new Key( key_co5, Key.MAJOR );
+ int keyCo5 = (int)(Math.random() * 12) - 5;
+ key = new Key(keyCo5, Key.MajorMinor.MAJOR);
lines = new Vector<Line>();
Line line = new Line();
- boolean is_end;
- Chord chord, prev_chord = new Chord(new NoteSymbol(key_co5));
- int co5_offset, prev_co5_offset;
+ 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++ ) {
i % 2 != 0 && Math.random() < 0.9
){
// もう一拍延長
- lastChordStroke.beat_length++;
+ lastChordStroke.beatLength++;
continue;
}
- chord = new Chord(new NoteSymbol(key_co5));
- co5_offset = 0;
- prev_co5_offset = prev_chord.rootNoteSymbol().toCo5() - key_co5;
- if( ! is_end ) {
+ co5Offset = 0;
+ prevCo5Offset = prevChord.rootNoteSymbol().toCo5() - keyCo5;
+ if( ! isEnd ) {
//
// 最初または最後の小節は常にトニックにする。
// 完全五度ずつ下がる進行を基本としつつ、時々そうでない進行も出現するようにする。
// サブドミナントを超えるとスケールを外れるので、超えそうになったらランダムに決め直す。
//
r = Math.random();
- co5_offset = prev_co5_offset - 1;
- if( co5_offset < -1 || (prev_co5_offset < 5 && r < 0.5) ) {
+ co5Offset = prevCo5Offset - 1;
+ if( co5Offset < -1 || (prevCo5Offset < 5 && r < 0.5) ) {
//
// 長7度がルートとなるコードの出現確率を半減させながらコードを決める
// (余りが6のときだけが長7度)
// なお、前回と同じコードは使わないようにする。
do {
- co5_offset = (int)(Math.random() * 13) % 7 - 1;
- } while( co5_offset == prev_co5_offset );
+ co5Offset = (int)(Math.random() * 13) % 7 - 1;
+ } while( co5Offset == prevCo5Offset );
}
- int co5RootNote = key_co5 + co5_offset;
- chord.setRoot(new NoteSymbol(co5RootNote));
- chord.setBass(new NoteSymbol(co5RootNote));
}
- switch( co5_offset ) {
+ 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( prev_co5_offset == 5 ) {
+ 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) );
- prev_chord = chord;
+ prevChord = chord;
}
line.add(measure);
if( (mp+1) % 8 == 0 ) { // 8小節おきに改行
}
// Major/minor 切り替え
- public void toggleKeyMajorMinor() {
- key = key.relativeKey();
- }
+ public void toggleKeyMajorMinor() { key = key.relativeKey(); }
// コード進行の移調
public void transpose(int chromaticOffset) {
Object element = measure.get(i);
if( element instanceof ChordStroke ) {
ChordStroke cs = (ChordStroke)element;
- Chord new_chord = cs.chord.clone();
//
// キーが未設定のときは、最初のコードから推測して設定
- if( key == null ) key = new Key( new_chord );
+ if( key == null ) key = new Key(cs.chord);
//
- new_chord.transpose( chromaticOffset, key );
- measure.set( i, new ChordStroke( new_chord, cs.beat_length ) );
+ Chord newChord = cs.chord.transposedNewChord(chromaticOffset, key);
+ measure.set(i, new ChordStroke(newChord, cs.beatLength));
}
}
}
}
- key.transpose(chromaticOffset);
+ key = key.transposedKey(chromaticOffset);
}
- // 異名同音の♭と#を切り替える
+ // 異名同音の♭と♯を切り替える
public void toggleEnharmonically() {
if( key == null ) return;
int original_key_co5 = key.toCo5();
else {
return;
}
- key.toggleEnharmonically();
+ key = key.enharmonicKey();
for( Line line : lines ) {
for( Measure measure : line ) {
for( int i=0; i<measure.size(); i++ ) {
Object element = measure.get(i);
if( element instanceof ChordStroke ) {
ChordStroke cs = (ChordStroke)element;
- Chord newChord = cs.chord.clone();
- newChord.setRoot(new NoteSymbol(newChord.rootNoteSymbol().toCo5() + co5Offset));
- newChord.setBass(new NoteSymbol(newChord.bassNoteSymbol().toCo5() + co5Offset));
- measure.set( i, new ChordStroke( newChord, cs.beat_length ) );
+ NoteSymbol root = cs.chord.rootNoteSymbol();
+ NoteSymbol bass = cs.chord.bassNoteSymbol();
+ if( root.equals(bass) ) {
+ bass = root = new NoteSymbol(root.toCo5() + co5Offset);
+ } else {
+ root = new NoteSymbol(root.toCo5() + co5Offset);
+ bass = new NoteSymbol(bass.toCo5() + co5Offset);
+ }
+ Chord newChord = new Chord(root, bass, cs.chord.intervals());
+ measure.set(i, new ChordStroke(newChord, cs.beatLength));
}
}
}
// コード進行の中に時間軸(MIDI tick)を書き込む
//
public void setTickPositions( FirstTrackSpec first_track ) {
- ticks_per_measure = first_track.ticks_per_measure;
+ ticks_per_measure = first_track.ticksPerMeasure;
TickRange tick_range = new TickRange(
- first_track.pre_measures * ticks_per_measure
+ first_track.preMeasures * ticks_per_measure
);
for( Line line : lines ) { // 行単位の処理
for( Measure measure : line ) { // 小節単位の処理
long tpb = measure.ticks_per_beat = ticks_per_measure / n_beats ;
for( Object element : measure ) {
if( element instanceof Lyrics ) {
- ((Lyrics)element).start_tick_pos = tick_range.start_tick_pos;
+ ((Lyrics)element).startTickPos = tick_range.startTickPos;
continue;
}
else if( element instanceof ChordStroke ) {
ChordStroke chord_stroke = (ChordStroke)element;
- tick_range.moveForward( tpb * chord_stroke.beat_length );
- chord_stroke.tick_range = tick_range.clone();
+ tick_range.moveForward( tpb * chord_stroke.beatLength );
+ chord_stroke.tickRange = tick_range.clone();
}
}
}