OSDN Git Service

リファクタリング(Chordクラス周りを中心に)
authorAkiyoshi Kamide <kamide@yk.rim.or.jp>
Sat, 24 Dec 2016 17:03:02 +0000 (02:03 +0900)
committerAkiyoshi Kamide <kamide@yk.rim.or.jp>
Sat, 24 Dec 2016 17:03:02 +0000 (02:03 +0900)
src/camidion/chordhelper/ChordHelperApplet.java
src/camidion/chordhelper/ChordTextField.java
src/camidion/chordhelper/chorddiagram/CapoComboBoxModel.java
src/camidion/chordhelper/chorddiagram/ChordDiagram.java
src/camidion/chordhelper/chordmatrix/ChordMatrix.java
src/camidion/chordhelper/music/Chord.java
src/camidion/chordhelper/music/ChordProgression.java
src/camidion/chordhelper/music/Key.java

index 5ad308f..168c935 100644 (file)
@@ -283,7 +283,7 @@ public class ChordHelperApplet extends JApplet {
         */
        public static class VersionInfo {
                public static final String      NAME = "MIDI Chord Helper";
-               public static final String      VERSION = "Ver.20161223.1";
+               public static final String      VERSION = "Ver.2016124.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/";
@@ -415,9 +415,9 @@ public class ChordHelperApplet extends JApplet {
                rootPaneDefaultBgcolor = getContentPane().getBackground();
                //
                // コードダイアグラム、コードボタン、ピアノ鍵盤のセットアップ
-               CapoComboBoxModel capoValueModel = new CapoComboBoxModel();
-               chordDiagram = new ChordDiagram(capoValueModel);
-               chordMatrix = new ChordMatrix(capoValueModel) {{
+               CapoComboBoxModel capoComboBoxModel = new CapoComboBoxModel();
+               chordDiagram = new ChordDiagram(capoComboBoxModel);
+               chordMatrix = new ChordMatrix(capoComboBoxModel) {{
                        addChordMatrixListener(new ChordMatrixListener(){
                                public void keySignatureChanged() {
                                        Key capoKey = getKeySignatureCapo();
@@ -786,8 +786,7 @@ public class ChordHelperApplet extends JApplet {
                }
                if( playChord == null ) {
                        // もう鳴らさないので、歌詞表示に通知して終了
-                       if( lyricDisplay != null )
-                               lyricDisplay.appendChord(null);
+                       if( lyricDisplay != null ) lyricDisplay.appendChord(null);
                        return;
                }
                // あの楽器っぽい表示
@@ -797,7 +796,7 @@ public class ChordHelperApplet extends JApplet {
                }
                // コードボタンからのコードを、カポつき演奏キーからオリジナルキーへ変換
                Key originalKey = chordMatrix.getKeySignatureCapo();
-               Chord originalChord = playChord.clone().transpose(
+               Chord originalChord = playChord.transposedChord(
                        chordMatrix.capoSelecter.getCapo(),
                        chordMatrix.getKeySignature()
                );
@@ -861,11 +860,9 @@ public class ChordHelperApplet extends JApplet {
                Chord diagramChord;
                int chordDiagramCapo = chordDiagram.capoSelecterView.getCapo();
                if( chordDiagramCapo == chordMatrix.capoSelecter.getCapo() )
-                       diagramChord = playChord.clone();
+                       diagramChord = playChord;
                else
-                       diagramChord = originalChord.clone().transpose(
-                               - chordDiagramCapo, originalKey
-                       );
+                       diagramChord = originalChord.transposedChord(-chordDiagramCapo, originalKey);
                chordDiagram.setChord(diagramChord);
                if( chordDiagram.recordTextButton.isSelected() )
                        lyricDisplay.appendChord(diagramChord);
index fe74f96..1d0c65d 100644 (file)
@@ -131,6 +131,6 @@ public class ChordTextField extends JTextField {
                        return;
                String delimiter = ""; // was "\n"
                setText( getText() + (chord == null ? delimiter : chord + " ") );
-               currentChord = ( chord == null ? null : chord.clone() );
+               currentChord = chord;
        }
 }
\ No newline at end of file
index baf596c..7c771d7 100644 (file)
@@ -1,23 +1,10 @@
 package camidion.chordhelper.chorddiagram;
 
-import javax.swing.ComboBoxModel;
-import javax.swing.event.ListDataListener;
+import javax.swing.DefaultComboBoxModel;
 
 /**
  * カポ選択コンボボックスモデル(選択範囲:1~11)
  */
-public class CapoComboBoxModel implements ComboBoxModel<Integer> {
-       private Integer selectedValue = 1;
-       @Override
-       public int getSize() { return 11; }
-       @Override
-       public Integer getElementAt(int index) { return index + 1; }
-       @Override
-       public void addListDataListener(ListDataListener l) { }
-       @Override
-       public void removeListDataListener(ListDataListener l) { }
-       @Override
-       public void setSelectedItem(Object item) { selectedValue = (Integer)item; }
-       @Override
-       public Object getSelectedItem() { return selectedValue; }
+public class CapoComboBoxModel extends DefaultComboBoxModel<Integer> {
+       { for( int i=1; i<=11; i++ ) addElement(i); }
 }
index 42f77c8..7a31bc0 100644 (file)
@@ -18,7 +18,6 @@ import java.util.Objects;
 import javax.swing.Box;
 import javax.swing.BoxLayout;
 import javax.swing.ButtonGroup;
-import javax.swing.ComboBoxModel;
 import javax.swing.JPanel;
 import javax.swing.JRadioButton;
 import javax.swing.JScrollBar;
@@ -71,10 +70,10 @@ public class ChordDiagram extends JPanel {
        }
        /**
         * コードダイアグラムを構築します。
-        * @param capoValueModel カポ値選択コンボボックスのデータモデル
+        * @param capoComboBoxModel カポ値選択コンボボックスのデータモデル
         */
-       public ChordDiagram(ComboBoxModel<Integer> capoValueModel) {
-               capoSelecterView.valueSelecter.setModel(capoValueModel);
+       public ChordDiagram(CapoComboBoxModel capoComboBoxModel) {
+               capoSelecterView.valueSelecter.setModel(capoComboBoxModel);
                setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
                add(new JPanel() {
                        {
index 80e70ac..459b7f4 100644 (file)
@@ -48,31 +48,21 @@ import camidion.chordhelper.music.NoteSymbol;
 public class ChordMatrix extends JPanel
        implements MouseListener, KeyListener, MouseMotionListener, MouseWheelListener
 {
-       /**
-        * 列数
-        */
+       /** 列数 */
        public static final int N_COLUMNS = Music.SEMITONES_PER_OCTAVE * 2 + 1;
-       /**
-        * 行数
-        */
+       /** 行数 */
        public static final int CHORD_BUTTON_ROWS = 3;
-       /**
-        * 調号ボタン
-        */
-       public Co5Label keysigLabels[] = new Co5Label[ N_COLUMNS ];
-       /**
-        * コードボタン
-        */
+       /** 調号ボタン */
+       public Co5Label keysigLabels[] = new Co5Label[N_COLUMNS];
+       /** コードボタン */
        public ChordLabel chordLabels[] = new ChordLabel[N_COLUMNS * CHORD_BUTTON_ROWS];
-       /**
-        * コードボタンの下のコード表示部
-        */
+       /** コードボタンの下のコード表示部 */
        public ChordDisplayLabel chordDisplay = new ChordDisplayLabel("Chord Pad", this, null);
 
        private static class ChordLabelSelection {
-               ChordLabel chordLabel;
-               int bitIndex;
-               boolean isSus4;
+               private ChordLabel chordLabel;
+               private int bitIndex;
+               private boolean isSus4;
                public ChordLabelSelection(ChordLabel chordLabel, int bitIndex) {
                        this.chordLabel = chordLabel;
                        this.bitIndex = bitIndex;
@@ -252,12 +242,12 @@ public class ChordMatrix extends JPanel
         * コードボタン
         */
        private class ChordLabel extends JLabel {
-               public byte checkBits = 0;
-               public int co5Value;
-               public boolean isMinor;
-               public boolean isSus4;
-               public boolean isSelected = false;
-               public Chord chord;
+               private byte checkBits = 0;
+               private int co5Value;
+               private boolean isMinor;
+               private boolean isSus4;
+               private boolean isSelected = false;
+               private Chord chord;
 
                private boolean inActiveZone = true;
                private Font boldFont;
@@ -273,9 +263,9 @@ public class ChordMatrix extends JPanel
                        if( isMinor ) co5Value -= 3;
                        String labelText = ( isSus4 ? chord.symbolSuffix() : chord.toString() );
                        if( isMinor && labelText.length() > 3 ) {
-                               float small_point_size = getFont().getSize2D() - 2;
-                               boldFont = getFont().deriveFont(Font.BOLD, small_point_size);
-                               plainFont = getFont().deriveFont(Font.PLAIN, small_point_size);
+                               float smallPointSize = getFont().getSize2D() - 2;
+                               boldFont = getFont().deriveFont(Font.BOLD, smallPointSize);
+                               plainFont = getFont().deriveFont(Font.PLAIN, smallPointSize);
                        }
                        else {
                                boldFont = getFont().deriveFont(Font.BOLD);
@@ -460,37 +450,27 @@ public class ChordMatrix extends JPanel
                        setFont( is_bold ? boldFont : plainFont );
                }
                public void keyChanged() {
-                       int co5_key = capoKey.toCo5();
-                       int co5_offset = co5Value - co5_key;
-                       inActiveZone = (co5_offset <= 6 && co5_offset >= -6) ;
-                       int root_note = chord.rootNoteSymbol().toNoteNumber();
+                       int co5Key = capoKey.toCo5();
+                       int co5Offset = co5Value - co5Key;
+                       inActiveZone = (co5Offset <= 6 && co5Offset >= -6) ;
+                       int rootNote = chord.rootNoteSymbol().toNoteNumber();
                        //
                        // Reconstruct color index
                        //
                        // Root
-                       indicatorColorIndices[0] = Music.isOnScale(
-                               root_note, co5_key
-                       ) ? 0 : co5_offset > 0 ? 1 : 2;
+                       indicatorColorIndices[0] = Music.isOnScale(rootNote, co5Key) ? 0 : co5Offset > 0 ? 1 : 2;
                        //
                        // 3rd / sus4
-                       indicatorColorIndices[1] = Music.isOnScale(
-                               root_note+(isMinor?3:isSus4?5:4), co5_key
-                       ) ? 0 : co5_offset > 0 ? 1 : 2;
+                       indicatorColorIndices[1] = Music.isOnScale(rootNote+(isMinor?3:isSus4?5:4), co5Key) ? 0 : co5Offset > 0 ? 1 : 2;
                        //
                        // P5th
-                       indicatorColorIndices[2] = Music.isOnScale(
-                               root_note+7, co5_key
-                       ) ? 0 : co5_offset > 0 ? 1 : 2;
+                       indicatorColorIndices[2] = Music.isOnScale(rootNote+7, co5Key) ? 0 : co5Offset > 0 ? 1 : 2;
                        //
                        // dim5th
-                       indicatorColorIndices[3] = Music.isOnScale(
-                               root_note+6, co5_key
-                       ) ? 0 : co5_offset > 4 ? 1 : 2;
+                       indicatorColorIndices[3] = Music.isOnScale(rootNote+6, co5Key) ? 0 : co5Offset > 4 ? 1 : 2;
                        //
                        // aug5th
-                       indicatorColorIndices[4] = Music.isOnScale(
-                               root_note+8, co5_key
-                       ) ? 0 : co5_offset > -3 ? 1 : 2;
+                       indicatorColorIndices[4] = Music.isOnScale(rootNote+8, co5Key) ? 0 : co5Offset > -3 ? 1 : 2;
                }
        }
 
@@ -542,10 +522,10 @@ public class ChordMatrix extends JPanel
 
        /**
         * コードボタンマトリクスの構築
-        * @param capoValueModel カポ選択値モデル
+        * @param capoComboBoxModel カポ値選択コンボボックスのデータモデル
         */
-       public ChordMatrix(CapoComboBoxModel capoValueModel) {
-               capoSelecter = new CapoSelecterView(capoValueModel) {{
+       public ChordMatrix(CapoComboBoxModel capoComboBoxModel) {
+               capoSelecter = new CapoSelecterView(capoComboBoxModel) {{
                        checkbox.addItemListener(new ItemListener() {
                                public void itemStateChanged(ItemEvent e) {capoChanged(getCapo());}
                        });
@@ -569,11 +549,12 @@ public class ChordMatrix extends JPanel
                for (i=0; i < N_COLUMNS * CHORD_BUTTON_ROWS; i++) {
                        row = i / N_COLUMNS;
                        v = i - (N_COLUMNS * row) - 12;
-                       Chord chord = new Chord(
-                               new NoteSymbol(row==2 ? v+3 : v)
-                       );
-                       if( row==0 ) chord.set(Chord.Interval.SUS4);
-                       else if( row==2 ) chord.set(Chord.Interval.MINOR);
+                       Chord chord;
+                       switch(row) {
+                       case 0: chord = new Chord(new NoteSymbol(v), Chord.Interval.SUS4); break;
+                       case 2: chord = new Chord(new NoteSymbol(v+3), Chord.Interval.MINOR); break;
+                       default: chord = new Chord(new NoteSymbol(v)); break;
+                       }
                        ChordLabel cl = new ChordLabel(chord);
                        cl.addMouseListener(this);
                        cl.addMouseMotionListener(this);
@@ -585,9 +566,7 @@ public class ChordMatrix extends JPanel
                setOpaque(true);
                addKeyListener(this);
                addFocusListener(new FocusListener() {
-                       public void focusGained(FocusEvent e) {
-                               repaint();
-                       }
+                       public void focusGained(FocusEvent e) { repaint(); }
                        public void focusLost(FocusEvent e) {
                                selectedChord = selectedChordCapo = null;
                                fireChordChanged();
@@ -595,7 +574,7 @@ public class ChordMatrix extends JPanel
                        }
                });
                setLayout(new GridLayout( 4, N_COLUMNS, 2, 2 ));
-               setKeySignature( new Key() );
+               setKeySignature(new Key());
                //
                // Make chord label selections index
                //
@@ -661,7 +640,7 @@ public class ChordMatrix extends JPanel
                        if( e.isControlDown() )
                                chord.set(Chord.Interval.NINTH);
                        else
-                               chord.clear(Chord.OffsetIndex.NINTH);
+                               chord.clear(Chord.Interval.NINTH);
 
                        if( e.isAltDown() ) {
                                if( cl.isSus4 ) {
@@ -702,39 +681,32 @@ public class ChordMatrix extends JPanel
        public void mouseDragged(MouseEvent e) {
                Component obj = e.getComponent();
                if( obj instanceof ChordLabel ) {
-                       ChordLabel l_src = (ChordLabel)obj;
+                       ChordLabel labelDraggedFrom = (ChordLabel)obj;
                        Component obj2 = this.getComponentAt(
-                               l_src.getX() + e.getX(),
-                               l_src.getY() + e.getY()
+                               labelDraggedFrom.getX() + e.getX(),
+                               labelDraggedFrom.getY() + e.getY()
                        );
                        if( obj2 == this ) {
-                               //
                                // Entered gap between chord buttons - do nothing
-                               //
                                return;
                        }
-                       ChordLabel l_dst =
-                               ( (obj2 instanceof ChordLabel ) ? (ChordLabel)obj2 : null );
-                       if( l_dst == l_src ) {
-                               //
+                       ChordLabel labelDraggedTo = ((obj2 instanceof ChordLabel) ? (ChordLabel)obj2 : null);
+                       if( labelDraggedTo == labelDraggedFrom ) {
                                // Returned to original chord button
-                               //
                                destinationChordLabel = null;
                                return;
                        }
                        if( destinationChordLabel != null ) {
-                               //
                                // Already touched another chord button
-                               //
                                return;
                        }
-                       Chord chord = l_src.chord.clone();
-                       if( l_src.isMinor ) {
-                               if( l_dst == null ) { // Out of chord buttons
+                       Chord chord = labelDraggedFrom.chord.clone();
+                       if( labelDraggedFrom.isMinor ) {
+                               if( labelDraggedTo == null ) { // Out of chord buttons
                                        // mM7
                                        chord.set(Chord.Interval.MAJOR_SEVENTH);
                                }
-                               else if( l_src.co5Value < l_dst.co5Value ) { // Right
+                               else if( labelDraggedFrom.co5Value < labelDraggedTo.co5Value ) { // Right
                                        // m6
                                        chord.set(Chord.Interval.SIXTH);
                                }
@@ -743,14 +715,14 @@ public class ChordMatrix extends JPanel
                                        chord.set(Chord.Interval.SEVENTH);
                                }
                        }
-                       else if( l_src.isSus4 ) {
-                               if( l_dst == null ) { // Out of chord buttons
+                       else if( labelDraggedFrom.isSus4 ) {
+                               if( labelDraggedTo == null ) { // Out of chord buttons
                                        return;
                                }
-                               else if( ! l_dst.isSus4 ) { // Down from sus4 to major
+                               else if( ! labelDraggedTo.isSus4 ) { // Down from sus4 to major
                                        chord.set(Chord.Interval.MAJOR);
                                }
-                               else if( l_src.co5Value < l_dst.co5Value ) { // Right
+                               else if( labelDraggedFrom.co5Value < labelDraggedTo.co5Value ) { // Right
                                        chord.set(Chord.Interval.NINTH);
                                }
                                else { // Left
@@ -759,17 +731,17 @@ public class ChordMatrix extends JPanel
                                }
                        }
                        else {
-                               if( l_dst == null ) { // Out of chord buttons
+                               if( labelDraggedTo == null ) { // Out of chord buttons
                                        return;
                                }
-                               else if( l_dst.isSus4 ) { // Up from major to sus4
+                               else if( labelDraggedTo.isSus4 ) { // Up from major to sus4
                                        chord.set(Chord.Interval.NINTH);
                                }
-                               else if( l_src.co5Value < l_dst.co5Value ) { // Right
+                               else if( labelDraggedFrom.co5Value < labelDraggedTo.co5Value ) { // Right
                                        // M7
                                        chord.set(Chord.Interval.MAJOR_SEVENTH);
                                }
-                               else if( l_dst.isMinor ) { // Down from major to minor
+                               else if( labelDraggedTo.isMinor ) { // Down from major to minor
                                        // 6
                                        chord.set(Chord.Interval.SIXTH);
                                }
@@ -778,7 +750,7 @@ public class ChordMatrix extends JPanel
                                        chord.set(Chord.Interval.SEVENTH);
                                }
                        }
-                       if( chord.isSet(Chord.OffsetIndex.NINTH) || (l_src.isSus4 && (l_dst == null || ! l_dst.isSus4) ) ) {
+                       if( chord.isSet(Chord.Interval.NINTH) || (labelDraggedFrom.isSus4 && (labelDraggedTo == null || ! labelDraggedTo.isSus4) ) ) {
                                if( (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {
                                        if( e.isShiftDown() ) {
                                                chord.set(Chord.Interval.MAJOR_SEVENTH);
@@ -795,10 +767,10 @@ public class ChordMatrix extends JPanel
                                if( e.isControlDown() )
                                        chord.set(Chord.Interval.NINTH);
                                else
-                                       chord.clear(Chord.OffsetIndex.NINTH);
+                                       chord.clear(Chord.Interval.NINTH);
                        }
                        if( e.isAltDown() ) {
-                               if( l_src.isSus4 ) {
+                               if( labelDraggedFrom.isSus4 ) {
                                        chord.set(Chord.Interval.MAJOR);
                                        chord.set(Chord.Interval.SHARP5);
                                }
@@ -807,7 +779,7 @@ public class ChordMatrix extends JPanel
                                }
                        }
                        setSelectedChord(chord);
-                       destinationChordLabel = (l_dst == null ? l_src : l_dst ) ;
+                       destinationChordLabel = (labelDraggedTo == null ? labelDraggedFrom : labelDraggedTo ) ;
                }
                else if( obj instanceof Co5Label ) {
                        Co5Label l_src = (Co5Label)obj;
@@ -847,7 +819,7 @@ public class ChordMatrix extends JPanel
        }
        private Chord.Interval pcKeyNextShift7;
        public void keyPressed(KeyEvent e) {
-               int i = -1, i_col = -1, i_row = 1;
+               int i = -1, iCol = -1, iRow = 1;
                boolean shiftPressed = false; // True if Shift-key pressed or CapsLocked
                char keyChar = e.getKeyChar();
                int keyCode = e.getKeyCode();
@@ -863,28 +835,28 @@ public class ChordMatrix extends JPanel
                        return;
                }
                else if( (i = "asdfghjkl;:]".indexOf(keyChar)) >= 0 ) {
-                       i_col = i + keyCo5 + 7;
+                       iCol = i + keyCo5 + 7;
                }
                else if( (i = "ASDFGHJKL+*}".indexOf(keyChar)) >= 0 ) {
-                       i_col = i + keyCo5 + 7;
+                       iCol = i + keyCo5 + 7;
                        shiftPressed = true;
                }
                else if( (i = "zxcvbnm,./\\".indexOf(keyChar)) >=0 ) {
-                       i_col = i + keyCo5 + 7;
-                       i_row = 2;
+                       iCol = i + keyCo5 + 7;
+                       iRow = 2;
                }
                else if( (i = "ZXCVBNM<>?_".indexOf(keyChar)) >=0 ) {
-                       i_col = i + keyCo5 + 7;
-                       i_row = 2;
+                       iCol = i + keyCo5 + 7;
+                       iRow = 2;
                        shiftPressed = true;
                }
                else if( (i = "qwertyuiop@[".indexOf(keyChar)) >= 0 ) {
-                       i_col = i + keyCo5 + 7;
-                       i_row = 0;
+                       iCol = i + keyCo5 + 7;
+                       iRow = 0;
                }
                else if( (i = "QWERTYUIOP`{".indexOf(keyChar)) >= 0 ) {
-                       i_col = i + keyCo5 + 7;
-                       i_row = 0;
+                       iCol = i + keyCo5 + 7;
+                       iRow = 0;
                        shiftPressed = true;
                }
                else if( keyChar == '5' ) {
@@ -916,15 +888,15 @@ public class ChordMatrix extends JPanel
                }
                if( i < 0 ) // No key char found
                        return;
-               if( i_col < 0 ) i_col += 12; else if( i_col > N_COLUMNS ) i_col -= 12;
-               cl = chordLabels[i_col + N_COLUMNS * i_row];
+               if( iCol < 0 ) iCol += 12; else if( iCol > N_COLUMNS ) iCol -= 12;
+               cl = chordLabels[iCol + N_COLUMNS * iRow];
                chord = cl.chord.clone();
                if( shiftPressed ) {
                        chord.set(Chord.Interval.SEVENTH);
                }
                // specify by previous key
                else if( pcKeyNextShift7 == null ) {
-                       chord.clear(Chord.OffsetIndex.SEVENTH);
+                       chord.clear(Chord.Interval.SEVENTH);
                }
                else {
                        chord.set(pcKeyNextShift7);
@@ -938,9 +910,7 @@ public class ChordMatrix extends JPanel
                                chord.set(Chord.Interval.FLAT5);
                        }
                }
-               if( e.isControlDown() ) { // Cannot use for ninth ?
-                       chord.set(Chord.Interval.NINTH);
-               }
+               if( e.isControlDown() ) chord.set(Chord.Interval.NINTH);
                if( selectedChordLabel != null ) clear();
                (selectedChordLabel = cl).setSelection(true);
                setSelectedChord(chord);
@@ -1028,9 +998,7 @@ public class ChordMatrix extends JPanel
        protected void capoChanged(int newCapo) {
                if(capo == newCapo) return;
                capoKey = key.transposedKey(capo = newCapo);
-               selectedChordCapo = (
-                       selectedChord == null ? null : selectedChord.clone().transpose(newCapo)
-               );
+               selectedChordCapo = (selectedChord == null ? null : selectedChord.transposedChord(newCapo));
                for( ChordLabel cl : chordLabels ) cl.keyChanged();
                fireKeySignatureChanged();
        }
@@ -1048,23 +1016,21 @@ public class ChordMatrix extends JPanel
         * ドラッグされたかどうか調べます。
         * @return ドラッグ先コードボタンがあればtrue
         */
-       public boolean isDragged() {
-               return destinationChordLabel != null ;
-       }
+       public boolean isDragged() { return destinationChordLabel != null ; }
 
        private boolean isDark = false;
-       public void setDarkMode(boolean is_dark) {
-               this.isDark = is_dark;
-               currentColorset = (is_dark ? darkModeColorset : normalModeColorset);
+       public void setDarkMode(boolean isDark) {
+               this.isDark = isDark;
+               currentColorset = (isDark ? darkModeColorset : normalModeColorset);
                setBackground( currentColorset.focus[0] );
                Key prev_key = key;
                key = null;
                setKeySignature(prev_key);
                for( int i=0; i < keysigLabels.length; i++ ) keysigLabels[i].setSelection();
                for( int i=0; i <  chordLabels.length; i++ ) chordLabels[i].setSelection();
-               chordGuide.setDarkMode( is_dark );
-               chordDisplay.setDarkMode( is_dark );
-               Color col = is_dark ? Color.black : null;
+               chordGuide.setDarkMode(isDark);
+               chordDisplay.setDarkMode(isDark);
+               Color col = isDark ? Color.black : null;
                capoSelecter.setBackground( col );
                capoSelecter.valueSelecter.setBackground( col );
        }
@@ -1081,35 +1047,34 @@ public class ChordMatrix extends JPanel
        public void setBeat(SequenceTickIndex sequenceTickIndex) {
                byte beat = (byte)(sequenceTickIndex.lastBeat);
                byte tsu = sequenceTickIndex.timesigUpper;
-               if( currentBeat == beat && timesigUpper == tsu )
-                       return;
+               if( currentBeat == beat && timesigUpper == tsu ) return;
                timesigUpper = tsu;
                currentBeat = beat;
                keysigLabels[ key.toCo5() + 12 ].repaint();
        }
 
-       private ChordLabel      selectedChordLabel = null;
+       private ChordLabel selectedChordLabel = null;
        public JComponent getSelectedButton() {
                return selectedChordLabel;
        }
-       private Chord   selectedChord = null;
+       private Chord selectedChord = null;
        public Chord getSelectedChord() {
                return selectedChord;
        }
-       private Chord   selectedChordCapo = null;
+       private Chord selectedChordCapo = null;
        public Chord getSelectedChordCapo() {
                return selectedChordCapo;
        }
        public void setSelectedChordCapo( Chord chord ) {
                setNoteIndex(-1); // Cancel arpeggio mode
-               selectedChord = (chord == null ? null : chord.clone().transpose(-capo,capoKey));
+               selectedChord = (chord == null ? null : chord.transposedChord(-capo,capoKey));
                selectedChordCapo = chord;
                fireChordChanged();
        }
        public void setSelectedChord( Chord chord ) {
                setNoteIndex(-1); // Cancel arpeggio mode
                selectedChord = chord;
-               selectedChordCapo = (chord == null ? null : chord.clone().transpose(capo,key));
+               selectedChordCapo = (chord == null ? null : chord.transposedChord(capo,key));
                fireChordChanged();
        }
        /**
@@ -1122,10 +1087,7 @@ public class ChordMatrix extends JPanel
                        try {
                                chord = new Chord(chordSymbol);
                        } catch( IllegalArgumentException e ) {
-                               JOptionPane.showMessageDialog(
-                                       null, e.getMessage(), "Input error",
-                                       JOptionPane.ERROR_MESSAGE
-                               );
+                               JOptionPane.showMessageDialog(null, e.getMessage(), "Input error", JOptionPane.ERROR_MESSAGE);
                                return;
                        }
                }
index 22eecd5..190051a 100644 (file)
@@ -1,6 +1,8 @@
 package camidion.chordhelper.music;
 
 import java.awt.Color;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Vector;
@@ -11,9 +13,7 @@ import javax.swing.JLabel;
  * 和音(コード - musical chord)のクラス
  */
 public class Chord implements Cloneable {
-       /**
-        * コード構成音の順序に対応する色
-        */
+       /** コード構成音の順序に対応する色 */
        public static final Color NOTE_INDEX_COLORS[] = {
                Color.red,
                new Color(0x40,0x40,0xFF),
@@ -23,20 +23,11 @@ public class Chord implements Cloneable {
                Color.orange,
                Color.green
        };
-       /**
-        * 音程差の半音オフセットのインデックス
-        */
-       public static enum OffsetIndex {
-               THIRD,
-               FIFTH,
-               SEVENTH,
-               NINTH,
-               ELEVENTH,
-               THIRTEENTH
+       /** 音程差の半音オフセットのインデックス */
+       private static enum OffsetIndex {
+               THIRD, FIFTH, SEVENTH, NINTH, ELEVENTH, THIRTEENTH
        }
-       /**
-        * 音程差
-        */
+       /** 音程差 */
        public static enum Interval {
 
                /** 長2度(major 2nd / sus2) */
@@ -83,36 +74,27 @@ public class Chord implements Cloneable {
                        this.chromaticOffset = chromaticOffset;
                        this.offsetIndex = offsetIndex;
                }
-               private OffsetIndex offsetIndex;
-               private int chromaticOffset;
                /**
                 * 半音差を返します。
                 * @return 半音差
                 */
                public int getChromaticOffset() { return chromaticOffset; }
+               private int chromaticOffset;
                /**
                 * 対応するインデックスを返します。
                 * @return 対応するインデックス
                 */
-               public OffsetIndex getChromaticOffsetIndex() {
-                       return offsetIndex;
-               }
+               public OffsetIndex getChromaticOffsetIndex() { return offsetIndex; }
+               private OffsetIndex offsetIndex;
        }
+
        /**
-        * デフォルトの半音値(メジャーコード固定)
-        */
-       public static Map<OffsetIndex, Interval>
-               DEFAULT_OFFSETS = new HashMap<OffsetIndex, Interval>() {
-                       {
-                               Interval itv;
-                               itv = Interval.MAJOR; put(itv.getChromaticOffsetIndex(), itv);
-                               itv = Interval.PARFECT5; put(itv.getChromaticOffsetIndex(), itv);
-                       }
-               };
-       /**
-        * 現在有効な構成音の音程(ルート音を除く)
+        * 現在有効な構成音(ルート、ベースは除く)の音程(初期値はメジャーコードの構成音)
         */
-       public Map<OffsetIndex, Interval> offsets = new HashMap<>(DEFAULT_OFFSETS);
+       private Map<OffsetIndex, Interval> offsets = new HashMap<OffsetIndex, Interval>() {
+               private void set(Interval itv) { put(itv.getChromaticOffsetIndex(), itv); }
+               { set(Interval.MAJOR); set(Interval.PARFECT5); }
+       };
        /**
         * このコードのルート音
         */
@@ -123,174 +105,105 @@ public class Chord implements Cloneable {
        private NoteSymbol bassNoteSymbol;
 
        /**
-        * コード C major を構築します。
+        * 指定されたルート音と構成音を持つメジャーコードを構築します。
+        * @param root ルート音(ベース音としても使う)
+        * @param itvs その他の構成音の音程
         */
-       public Chord() {
-               this(new NoteSymbol());
+       public Chord(NoteSymbol root, Interval... itvs) { this(root, root, itvs); }
+       /**
+        * 指定されたルート音、ベース音、構成音を持つメジャーコードを構築します。
+        * @param root ルート音
+        * @param bass ベース音
+        * @param itvs その他の構成音の音程
+        */
+       public Chord(NoteSymbol root, NoteSymbol bass, Interval... itvs) {
+               this(root, bass, Arrays.asList(itvs));
        }
        /**
-        * 指定した音名のメジャーコードを構築します。
-        * @param noteSymbol 音名
+        * 指定されたルート音、ベース音、構成音を持つメジャーコードを構築します。
+        * @param root ルート音
+        * @param bass ベース音
+        * @param itvs その他の構成音の音程
         */
-       public Chord(NoteSymbol noteSymbol) {
-               setRoot(noteSymbol);
-               setBass(noteSymbol);
+       public Chord(NoteSymbol root, NoteSymbol bass, Collection<Interval> itvs) {
+               bassNoteSymbol = root;
+               rootNoteSymbol = bass;
+               for(Interval itv : itvs) if(itv != null) set(itv);
        }
        /**
         * 指定された調と同名のコードを構築します。
-        * <p>元の調がマイナーキーの場合はマイナーコード、
-        * それ以外の場合はメジャーコードになります。
-        * </p>
         * @param key 調
         */
        public Chord(Key key) {
                int keyCo5 = key.toCo5();
                if( key.majorMinor() == Key.MajorMinor.MINOR ) {
-                       keyCo5 += 3;
-                       set(Interval.MINOR);
+                       keyCo5 += 3; set(Interval.MINOR);
                }
-               setRoot(new NoteSymbol(keyCo5));
-               setBass(new NoteSymbol(keyCo5));
+               bassNoteSymbol = rootNoteSymbol = new NoteSymbol(keyCo5);
        }
        /**
         * コード名の文字列からコードを構築します。
         * @param chordSymbol コード名の文字列
         */
        public Chord(String chordSymbol) {
-               setChordSymbol(chordSymbol);
-       }
-       /**
-        * このコードのクローンを作成します。
-        */
-       @Override
-       public Chord clone() {
-               Chord newChord = new Chord(rootNoteSymbol);
-               newChord.offsets = new HashMap<>(offsets);
-               newChord.setBass(bassNoteSymbol);
-               return newChord;
-       }
-       /**
-        * コードのルート音を指定された音階に置換します。
-        * @param rootNoteSymbol 音階
-        * @return このコード自身(置換後)
-        */
-       public Chord setRoot(NoteSymbol rootNoteSymbol) {
-               this.rootNoteSymbol = rootNoteSymbol;
-               return this;
-       }
-       /**
-        * コードのベース音を指定された音階に置換します。
-        * @param rootNoteSymbol 音階
-        * @return このコード自身(置換後)
-        */
-       public Chord setBass(NoteSymbol rootNoteSymbol) {
-               this.bassNoteSymbol = rootNoteSymbol;
-               return this;
-       }
-       /**
-        * コードの種類を設定します。
-        * @param itv 設定する音程
-        */
-       public void set(Interval itv) {
-               offsets.put(itv.getChromaticOffsetIndex(), itv);
-       }
-       /**
-        * コードに設定した音程をクリアします。
-        * @param index 半音差インデックス
-        */
-       public void clear(OffsetIndex index) {
-               offsets.remove(index);
-       }
-       //
-       // コードネームの文字列が示すコードに置き換えます。
-       public Chord setChordSymbol(String chordSymbol) {
                //
                // 分数コードの分子と分母に分ける
                String parts[] = chordSymbol.trim().split("(/|on)");
-               if( parts.length == 0 ) {
-                       return this;
-               }
+               if( parts.length == 0 ) return;
+               //
                // ルート音とベース音を設定
-               setRoot(new NoteSymbol(parts[0]));
-               setBass(new NoteSymbol(parts[ parts.length > 1 ? 1 : 0 ]));
+               rootNoteSymbol = new NoteSymbol(parts[0]);
+               if( parts.length > 1 && ! parts[0].equals(parts[1]) ) {
+                       bassNoteSymbol = new NoteSymbol(parts[1]);
+               } else {
+                       bassNoteSymbol = rootNoteSymbol;
+               }
                String suffix = parts[0].replaceFirst("^[A-G][#bx]*","");
                //
                // () があれば、その中身を取り出す
                String suffixParts[] = suffix.split("[\\(\\)]");
-               if( suffixParts.length == 0 ) {
-                       return this;
-               }
+               if( suffixParts.length == 0 ) return;
                String suffixParen = "";
                if( suffixParts.length > 1 ) {
                        suffixParen = suffixParts[1];
                        suffix = suffixParts[0];
                }
-               Interval itv;
+               // +5 -5 aug dim
+               if( suffix.matches(".*(\\+5|aug|#5).*") ) set(Interval.FLAT5);
+               else if( suffix.matches(".*(-5|dim|b5).*") ) set(Interval.SHARP5);
                //
-               // +5 -5 aug dim の判定
-               set(
-                       suffix.matches(".*(\\+5|aug|#5).*") ? Interval.SHARP5 :
-                       suffix.matches(".*(-5|dim|b5).*") ? Interval.FLAT5 :
-                       Interval.PARFECT5
-               );
+               // 6 7 M7
+               if( suffix.matches(".*(M7|maj7|M9|maj9).*") ) set(Interval.MAJOR_SEVENTH);
+               else if( suffix.matches(".*(6|dim[79]).*") ) set(Interval.SIXTH);
+               else if( suffix.matches(".*7.*") ) set(Interval.SEVENTH);
                //
-               // 6 7 M7 の判定
-               itv = suffix.matches(".*(M7|maj7|M9|maj9).*") ? Interval.MAJOR_SEVENTH :
-                       suffix.matches(".*(6|dim[79]).*") ? Interval.SIXTH :
-                       suffix.matches(".*7.*") ? Interval.SEVENTH :
-                       null;
-               if(itv==null)
-                       clear(OffsetIndex.SEVENTH);
-               else
-                       set(itv);
-               //
-               // マイナーの判定。maj7 と間違えないように比較
-               set(
-                       (suffix.matches(".*m.*") && ! suffix.matches(".*ma.*") ) ? Interval.MINOR :
-                       suffix.matches(".*sus4.*") ? Interval.SUS4 :
-                       Interval.MAJOR
-               );
+               // minor sus4  (maj7 と間違えないように比較)
+               if( suffix.matches(".*m.*") && ! suffix.matches(".*ma.*") ) set(Interval.MINOR);
+               else if( suffix.matches(".*sus4.*") ) set(Interval.SUS4);
                //
                // 9th の判定
                if( suffix.matches(".*9.*") ) {
                        set(Interval.NINTH);
-                       if( ! suffix.matches( ".*(add9|6|M9|maj9|dim9).*") ) {
-                               set(Interval.SEVENTH);
-                       }
+                       if( ! suffix.matches(".*(add9|6|M9|maj9|dim9).*") ) set(Interval.SEVENTH);
                }
                else {
-                       offsets.remove(OffsetIndex.NINTH);
-                       offsets.remove(OffsetIndex.ELEVENTH);
-                       offsets.remove(OffsetIndex.THIRTEENTH);
-                       //
                        // () の中を , で分ける
-                       String parts_in_paren[] = suffixParen.split(",");
-                       for( String p : parts_in_paren ) {
-                               if( p.matches("(\\+9|#9)") )
-                                       offsets.put(OffsetIndex.NINTH, Interval.SHARP9);
-                               else if( p.matches("(-9|b9)") )
-                                       offsets.put(OffsetIndex.NINTH, Interval.FLAT9);
-                               else if( p.matches("9") )
-                                       offsets.put(OffsetIndex.NINTH, Interval.NINTH);
+                       for( String p : suffixParen.split(",") ) {
+                               if( p.matches("(\\+9|#9)") ) set(Interval.SHARP9);
+                               else if( p.matches("(-9|b9)") ) set(Interval.FLAT9);
+                               else if( p.matches("9") ) set(Interval.NINTH);
 
-                               if( p.matches("(\\+11|#11)") )
-                                       offsets.put(OffsetIndex.ELEVENTH, Interval.SHARP11);
-                               else if( p.matches("11") )
-                                       offsets.put(OffsetIndex.ELEVENTH, Interval.ELEVENTH);
+                               if( p.matches("(\\+11|#11)") ) set(Interval.SHARP11);
+                               else if( p.matches("11") ) set(Interval.ELEVENTH);
 
-                               if( p.matches("(-13|b13)") )
-                                       offsets.put(OffsetIndex.THIRTEENTH, Interval.FLAT13);
-                               else if( p.matches("13") )
-                                       offsets.put(OffsetIndex.THIRTEENTH, Interval.THIRTEENTH);
+                               if( p.matches("(-13|b13)") ) set(Interval.FLAT13);
+                               else if( p.matches("13") ) set(Interval.THIRTEENTH);
 
                                // -5 や +5 が () の中にあっても解釈できるようにする
-                               if( p.matches("(-5|b5)") )
-                                       offsets.put(OffsetIndex.FIFTH, Interval.FLAT5);
-                               else if( p.matches("(\\+5|#5)") )
-                                       offsets.put(OffsetIndex.FIFTH, Interval.SHARP5);
+                               if( p.matches("(-5|b5)") ) set(Interval.FLAT5);
+                               else if( p.matches("(\\+5|#5)") ) set(Interval.SHARP5);
                        }
                }
-               return this;
        }
        /**
         * ルート音を返します。
@@ -303,75 +216,64 @@ public class Chord implements Cloneable {
         */
        public NoteSymbol bassNoteSymbol() { return bassNoteSymbol; }
        /**
+        * このコードの構成音を、ルート音からの音程の配列として返します。
+        * ルート音自身やベース音は含まれません。
+        * @return 音程の配列
+        */
+       public Interval[] intervals() {
+               return offsets.values().toArray(new Interval[offsets.size()]);
+       }
+       /**
         * 指定した音程が設定されているか調べます。
         * @param itv 音程
         * @return 指定した音程が設定されていたらtrue
         */
        public boolean isSet(Interval itv) {
-               return offsets.get(itv.getChromaticOffsetIndex()) == itv;
+               return itv.equals(offsets.get(itv.getChromaticOffsetIndex()));
        }
        /**
-        * 指定したインデックスに音程が設定されているか調べます。
-        * @param index インデックス
-        * @return 指定したインデックスに音程が設定されていたらtrue
-        */
-       public boolean isSet(OffsetIndex index) {
-               return offsets.containsKey(index);
-       }
-       /**
-        * コードが等しいかどうかを判定します。
+        * コードの同一性を判定します。ルート音、ベース音の異名同音は異なるものとみなされます。
+        * @param anObject 比較対象
         * @return 等しければtrue
         */
        @Override
        public boolean equals(Object anObject) {
-               if( this == anObject )
-                       return true;
+               if( anObject == this ) return true;
                if( anObject instanceof Chord ) {
                        Chord another = (Chord) anObject;
-                       if( ! rootNoteSymbol.equals(another.rootNoteSymbol) )
-                               return false;
-                       if( ! bassNoteSymbol.equals(another.bassNoteSymbol) )
-                               return false;
+                       if( ! rootNoteSymbol.equals(another.rootNoteSymbol) ) return false;
+                       if( ! bassNoteSymbol.equals(another.bassNoteSymbol) ) return false;
                        return offsets.equals(another.offsets);
                }
                return false;
        }
        @Override
-       public int hashCode() {
-               return toString().hashCode();
-       }
+       public int hashCode() { return toString().hashCode(); }
        /**
-        * ã\82³ã\83¼ã\83\89ã\81\8cç­\89ã\81\97ã\81\84ã\81\8bã\81©ã\81\86ã\81\8bã\82\92ã\80\81ç\95°å\90\8då\90\8cé\9f³ã\82\92ç\84¡è¦\96ã\81\97ã\81¦判定します。
+        * ã\83«ã\83¼ã\83\88é\9f³ã\80\81ã\83\99ã\83¼ã\82¹é\9f³ã\81®ç\95°å\90\8då\90\8cé\9f³ã\82\92å\90\8cã\81\98ã\81¨ã\81¿ã\81ªã\81\97ã\81\9fã\81\86ã\81\88ã\81§ã\80\81ã\82³ã\83¼ã\83\89ã\81®å\90\8cä¸\80æ\80§ã\82\92判定します。
         * @param another 比較対象のコード
         * @return 等しければtrue
         */
        public boolean equalsEnharmonically(Chord another) {
-               if( this == another )
-                       return true;
-               if( another == null )
-                       return false;
-               if( ! rootNoteSymbol.equalsEnharmonically(another.rootNoteSymbol) )
-                       return false;
-               if( ! bassNoteSymbol.equalsEnharmonically(another.bassNoteSymbol) )
-                       return false;
+               if( another == this ) return true;
+               if( another == null ) return false;
+               if( ! rootNoteSymbol.equalsEnharmonically(another.rootNoteSymbol) ) return false;
+               if( ! bassNoteSymbol.equalsEnharmonically(another.bassNoteSymbol) ) return false;
                return offsets.equals(another.offsets);
        }
        /**
-        * コード構成音の数を返します
-        * (ルート音は含まれますが、ベース音は含まれません)。
-        *
+        * コード構成音の数を返します。ルート音は含まれますが、ベース音は含まれません。
         * @return コード構成音の数
         */
        public int numberOfNotes() { return offsets.size() + 1; }
        /**
-        * 指定された位置にあるノート番号を返します。
+        * 指定された位置にある構成音のノート番号を返します。
         * @param index 位置(0をルート音とした構成音の順序)
         * @return ノート番号(該当する音がない場合は -1)
         */
        public int noteAt(int index) {
                int rootnote = rootNoteSymbol.toNoteNumber();
-               if( index == 0 )
-                       return rootnote;
+               if( index == 0 ) return rootnote;
                Interval itv;
                int i=0;
                for( OffsetIndex offsetIndex : OffsetIndex.values() )
@@ -380,9 +282,8 @@ public class Chord implements Cloneable {
                return -1;
        }
        /**
-        * コード構成音を格納したノート番号の配列を返します。
-        * (ベース音は含まれません)
-        * 音域が指定された場合、その音域に合わせたノート番号を返します。
+        * コード構成音を格納したノート番号の配列を返します(ベース音は含まれません)。
+        * 音域が指定された場合、その音域の範囲内に収まるように転回されます。
         * @param range 音域(null可)
         * @param key キー(null可)
         * @return ノート番号の配列
@@ -396,15 +297,12 @@ public class Chord implements Cloneable {
                for( OffsetIndex offsetIndex : OffsetIndex.values() )
                        if( (itv = offsets.get(offsetIndex)) != null )
                                ia[++i] = rootnote + itv.getChromaticOffset();
-               if( range != null )
-                       range.invertNotesOf(ia, key);
+               if( range != null ) range.invertNotesOf(ia, key);
                return ia;
        }
        /**
-        * MIDI ノート番号が、コードの構成音の何番目(0=ルート音)に
-        * あるかを表すインデックス値を返します。
-        * 構成音に該当しない場合は -1 を返します。
-        * ベース音は検索されません。
+        * MIDIノート番号が、コードの構成音の何番目(0=ルート音)にあるかを表すインデックス値を返します。
+        * 構成音に該当しない場合は -1 を返します。ベース音は検索されません。
         * @param noteNumber MIDIノート番号
         * @return 構成音のインデックス値
         */
@@ -427,52 +325,53 @@ public class Chord implements Cloneable {
         * @param key 調べるキー
         * @return スケールを外れている構成音がなければtrue
         */
-       public boolean isOnScaleInKey(Key key) {
-               return isOnScaleInKey(key.toCo5());
-       }
+       public boolean isOnScaleIn(Key key) { return isOnScaleInKey(key.toCo5()); }
        private boolean isOnScaleInKey(int keyCo5) {
                int rootnote = rootNoteSymbol.toNoteNumber();
-               if( ! Music.isOnScale(rootnote, keyCo5) )
-                       return false;
+               if( ! Music.isOnScale(rootnote, keyCo5) ) return false;
                Interval itv;
                for( OffsetIndex offsetIndex : OffsetIndex.values() ) {
-                       if( (itv = offsets.get(offsetIndex)) == null )
-                               continue;
-                       if( ! Music.isOnScale(rootnote + itv.getChromaticOffset(), keyCo5) )
-                               return false;
+                       if( (itv = offsets.get(offsetIndex)) == null ) continue;
+                       if( ! Music.isOnScale(rootnote + itv.getChromaticOffset(), keyCo5) ) return false;
                }
                return true;
        }
        /**
-        * 移調したコードを返します。
-        * @param chromatic_offset 移調幅(半音単位)
+        * C/Amの調に近いほうの♯、♭の表記で、移調したコードを返します。
+        * @param chromaticOffset 移調幅(半音単位)
         * @return 移調した新しいコード(移調幅が0の場合は自分自身)
         */
-       public Chord transpose(int chromatic_offset) {
-               return transposedChord(chromatic_offset, 0);
+       public Chord transposedChord(int chromaticOffset) {
+               return transposedChord(chromaticOffset, 0);
        }
-       public Chord transpose(int chromatic_offset, Key original_key) {
-               return transposedChord(chromatic_offset, original_key.toCo5());
+       /**
+        * 指定された調に近いほうの♯、♭の表記で、移調したコードを返します。
+        * @param chromaticOffset 移調幅(半音単位)
+        * @param originalKey 基準とする調
+        * @return 移調した新しいコード(移調幅が0の場合は自分自身)
+        */
+       public Chord transposedChord(int chromaticOffset, Key originalKey) {
+               return transposedChord(chromaticOffset, originalKey.toCo5());
        }
-       private Chord transposedChord(int chromatic_offset, int original_key_co5) {
-               if( chromatic_offset == 0 ) return this;
-               int offsetCo5 = Music.mod12(Music.reverseCo5(chromatic_offset));
+       private Chord transposedChord(int chromaticOffset, int originalKeyCo5) {
+               if( chromaticOffset == 0 ) return this;
+               int offsetCo5 = Music.mod12(Music.reverseCo5(chromaticOffset));
                if( offsetCo5 > 6 ) offsetCo5 -= 12;
-               int key_co5   = original_key_co5 + offsetCo5;
+               int keyCo5 = originalKeyCo5 + offsetCo5;
                //
                int newRootCo5 = rootNoteSymbol.toCo5() + offsetCo5;
                int newBassCo5 = bassNoteSymbol.toCo5() + offsetCo5;
-               if( key_co5 > 6 ) {
+               if( keyCo5 > 6 ) {
                        newRootCo5 -= 12;
                        newBassCo5 -= 12;
                }
-               else if( key_co5 < -5 ) {
+               else if( keyCo5 < -5 ) {
                        newRootCo5 += 12;
                        newBassCo5 += 12;
                }
-               setRoot(new NoteSymbol(newRootCo5));
-               return setBass(new NoteSymbol(newBassCo5));
+               return new Chord(new NoteSymbol(newRootCo5), new NoteSymbol(newBassCo5), intervals());
        }
+
        /**
         * この和音の文字列表現としてコード名を返します。
         * @return この和音のコード名
@@ -480,9 +379,7 @@ public class Chord implements Cloneable {
        @Override
        public String toString() {
                String chordSymbol = rootNoteSymbol + symbolSuffix();
-               if( ! rootNoteSymbol.equals(bassNoteSymbol) ) {
-                       chordSymbol += "/" + bassNoteSymbol;
-               }
+               if( ! rootNoteSymbol.equals(bassNoteSymbol) ) chordSymbol += "/" + bassNoteSymbol;
                return chordSymbol;
        }
        /**
@@ -676,4 +573,28 @@ public class Chord implements Cloneable {
                else if( suffix.isEmpty() ) return " major";
                else return suffix ;
        }
+
+       /**
+        * このコードのクローンを作成します。
+        */
+       @Override
+       public Chord clone() {
+               Chord newChord = new Chord(rootNoteSymbol, bassNoteSymbol);
+               newChord.offsets = new HashMap<>(offsets);
+               return newChord;
+       }
+       /**
+        * 指定した音程の構成音を設定します。
+        * @param itv 設定する音程
+        */
+       public void set(Interval itv) {
+               offsets.put(itv.getChromaticOffsetIndex(), itv);
+       }
+       /**
+        * 指定した音程の構成音をクリアします。
+        * @param itv クリアする音程
+        */
+       public void clear(Interval itv) {
+               offsets.remove(itv.getChromaticOffsetIndex());
+       }
 }
\ No newline at end of file
index 9ed8f9f..71ab49c 100644 (file)
@@ -39,15 +39,15 @@ public class ChordProgression {
        }
 
        class ChordStroke {
-               Chord chord; int beat_length; TickRange tickRange = 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;
                }
        }
@@ -69,7 +69,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 +80,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 +100,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 = "";
@@ -183,13 +183,13 @@ 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.MajorMinor.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;
+               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); // 最初または最後の小節かを覚えておく
@@ -202,12 +202,11 @@ 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;
+                               co5Offset = 0;
+                               prevCo5Offset = prevChord.rootNoteSymbol().toCo5() - keyCo5;
                                if( ! is_end ) {
                                        //
                                        // 最初または最後の小節は常にトニックにする。
@@ -215,21 +214,19 @@ public class ChordProgression {
                                        // サブドミナントを超えるとスケールを外れるので、超えそうになったらランダムに決め直す。
                                        //
                                        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 ) {
+                               chord = new Chord(new NoteSymbol(keyCo5 + co5Offset));
+                               switch(co5Offset) {
                                // ルート音ごとに、7th などの付加や、メジャーマイナー反転を行う確率を決める
                                case 5: // VII
                                        if( Math.random() < 0.5 ) {
@@ -241,7 +238,7 @@ public class ChordProgression {
                                                chord.set(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);
@@ -279,7 +276,7 @@ public class ChordProgression {
                                        break;
                                }
                                measure.add( lastChordStroke = new ChordStroke(chord) );
-                               prev_chord = chord;
+                               prevChord = chord;
                        }
                        line.add(measure);
                        if( (mp+1) % 8 == 0 ) { // 8小節おきに改行
@@ -359,20 +356,19 @@ public class ChordProgression {
                                        Object element = measure.get(i);
                                        if( element instanceof ChordStroke ) {
                                                ChordStroke cs = (ChordStroke)element;
-                                               Chord newChord = cs.chord.clone();
                                                //
                                                // キーが未設定のときは、最初のコードから推測して設定
-                                               if( key == null ) key = new Key(newChord);
+                                               if( key == null ) key = new Key(cs.chord);
                                                //
-                                               newChord.transpose( chromaticOffset, key );
-                                               measure.set(i, new ChordStroke(newChord, cs.beat_length));
+                                               Chord newChord = cs.chord.transposedChord(chromaticOffset, key);
+                                               measure.set(i, new ChordStroke(newChord, cs.beatLength));
                                        }
                                }
                        }
                }
                key = key.transposedKey(chromaticOffset);
        }
-       // 異名同音の♭とを切り替える
+       // 異名同音の♭とを切り替える
        public void toggleEnharmonically() {
                if( key == null ) return;
                int original_key_co5 = key.toCo5();
@@ -393,10 +389,16 @@ public class ChordProgression {
                                        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));
                                        }
                                }
                        }
@@ -421,7 +423,7 @@ public class ChordProgression {
                                        }
                                        else if( element instanceof ChordStroke ) {
                                                ChordStroke chord_stroke = (ChordStroke)element;
-                                               tick_range.moveForward( tpb * chord_stroke.beat_length );
+                                               tick_range.moveForward( tpb * chord_stroke.beatLength );
                                                chord_stroke.tickRange = tick_range.clone();
                                        }
                                }
index 22ef914..d401f2d 100644 (file)
@@ -117,7 +117,7 @@ public class Key {
         * 指定されたコードと同名、または最も近い調を構築します。
         * <ul>
         * <li>コード構成音に短3度があればマイナー、なければメジャーとなります(区別なしになることはありません)。</li>
-        * <li>調として存在しないコード(例:A)が来た場合、存在する異名同音の調(例:B♭)に置き換えられます。</li>
+        * <li>調として存在しないコード(例:A)が来た場合、存在する異名同音の調(例:B♭)に置き換えられます。</li>
         * </ul>
         * @param chord コード(和音)
         */
@@ -260,8 +260,8 @@ public class Key {
                return mmo.equals(majorMinor) ? this : new Key(co5 - 3 * majorMinor.index(), mmo);
        }
        /**
-        * この調に異名同音の調があればそれを返します。
-        * <p>ä¾\8bã\81\88ã\81°ã\80\81â\99­ï¼\95å\80\8bï¼\88\99­ã\83¡ã\82¸ã\83£ã\83¼ï¼\89ã\81®å ´å\90\88ã\81¯â\99¯ï¼\97å\80\8bï¼\88\99¯ã\83¡ã\82¸ã\83£ã\83¼ï¼\89ã\81«ç½®æ\8f\9bã\81\95ã\82\8cます。
+        * ã\81\93ã\81®èª¿ã\81«ç\95°å\90\8då\90\8cé\9f³ã\81®èª¿ã\81\8cã\81\82ã\82\8cã\81°ã\80\81ã\81\9dã\82\8cã\82\92è¿\94ã\81\97ã\81¾ã\81\99ã\80\82
+        * <p>ä¾\8bã\81\88ã\81°ã\80\81â\99­ï¼\95å\80\8bï¼\88\99­ã\83¡ã\82¸ã\83£ã\83¼ï¼\89ã\81®å ´å\90\88ã\80\81ç\95°å\90\8då\90\8cé\9f³ã\81®èª¿ã\81¯â\99¯ï¼\97å\80\8bï¼\88\99¯ã\83¡ã\82¸ã\83£ã\83¼ï¼\89ã\81¨ã\81ªã\82\8aます。
         * 異名同音の調が存在しない調(4♯~4♭)に対してこのメソッドを呼び出した場合、
         * この調自身を返します。
         * </p>