OSDN Git Service

Chordクラスのイミュータブル化
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / music / ChordProgression.java
index f157fe4..b2652e6 100644 (file)
@@ -1,5 +1,7 @@
 package camidion.chordhelper.music;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Vector;
 import java.util.regex.Pattern;
 
@@ -12,42 +14,42 @@ import javax.sound.midi.Sequence;
 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;
                }
        }
@@ -55,10 +57,10 @@ public class ChordProgression {
        // 時間位置付き歌詞
        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; }
        }
@@ -69,7 +71,7 @@ public class ChordProgression {
                        int n = 0;
                        for( Object obj : this ) {
                                if( obj instanceof ChordStroke ) {
-                                       n += ((ChordStroke)obj).beat_length;
+                                       n += ((ChordStroke)obj).beatLength;
                                }
                        }
                        return n;
@@ -80,7 +82,7 @@ public class ChordProgression {
                        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;
                                        }
@@ -100,7 +102,7 @@ public class ChordProgression {
                        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 = "";
@@ -127,9 +129,9 @@ public class ChordProgression {
                                = (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;
@@ -142,7 +144,7 @@ public class ChordProgression {
                                        continue;
                                ChordProgression.ChordStroke chord_stroke
                                = (ChordProgression.ChordStroke)element;
-                               if( chord_stroke.tick_range.contains(tick) ) {
+                               if( chord_stroke.tickRange.contains(tick) ) {
                                        return chord_stroke;
                                }
                        }
@@ -183,16 +185,16 @@ public class ChordProgression {
         * @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++ ) {
@@ -202,84 +204,83 @@ public class ChordProgression {
                                        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小節おきに改行
@@ -349,9 +350,7 @@ public class ChordProgression {
        }
 
        // Major/minor 切り替え
-       public void toggleKeyMajorMinor() {
-               key = key.relativeKey();
-       }
+       public void toggleKeyMajorMinor() { key = key.relativeKey(); }
 
        // コード進行の移調
        public void transpose(int chromaticOffset) {
@@ -361,20 +360,19 @@ public class ChordProgression {
                                        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();
@@ -388,17 +386,23 @@ public class ChordProgression {
                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));
                                        }
                                }
                        }
@@ -407,9 +411,9 @@ public class ChordProgression {
        // コード進行の中に時間軸(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 ) { // 小節単位の処理
@@ -418,13 +422,13 @@ public class ChordProgression {
                                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();
                                        }
                                }
                        }