*/
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/";
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();
}
if( playChord == null ) {
// もう鳴らさないので、歌詞表示に通知して終了
- if( lyricDisplay != null )
- lyricDisplay.appendChord(null);
+ if( lyricDisplay != null ) lyricDisplay.appendChord(null);
return;
}
// あの楽器っぽい表示
}
// コードボタンからのコードを、カポつき演奏キーからオリジナルキーへ変換
Key originalKey = chordMatrix.getKeySignatureCapo();
- Chord originalChord = playChord.clone().transpose(
+ Chord originalChord = playChord.transposedChord(
chordMatrix.capoSelecter.getCapo(),
chordMatrix.getKeySignature()
);
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);
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
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); }
}
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;
}
/**
* コードダイアグラムを構築します。
- * @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() {
{
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;
* コードボタン
*/
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;
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);
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;
}
}
/**
* コードボタンマトリクスの構築
- * @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());}
});
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);
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();
}
});
setLayout(new GridLayout( 4, N_COLUMNS, 2, 2 ));
- setKeySignature( new Key() );
+ setKeySignature(new Key());
//
// Make chord label selections index
//
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 ) {
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);
}
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
}
}
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);
}
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);
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);
}
}
}
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;
}
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();
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' ) {
}
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);
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);
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();
}
* ドラッグされたかどうか調べます。
* @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 );
}
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();
}
/**
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;
}
}
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;
* 和音(コード - musical chord)のクラス
*/
public class Chord implements Cloneable {
- /**
- * コード構成音の順序に対応する色
- */
+ /** コード構成音の順序に対応する色 */
public static final Color NOTE_INDEX_COLORS[] = {
Color.red,
new Color(0x40,0x40,0xFF),
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) */
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); }
+ };
/**
* このコードのルート音
*/
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;
}
/**
* ルート音を返します。
*/
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() )
return -1;
}
/**
- * コード構成音を格納したノート番号の配列を返します。
- * (ベース音は含まれません)
- * 音域が指定された場合、その音域に合わせたノート番号を返します。
+ * コード構成音を格納したノート番号の配列を返します(ベース音は含まれません)。
+ * 音域が指定された場合、その音域の範囲内に収まるように転回されます。
* @param range 音域(null可)
* @param key キー(null可)
* @return ノート番号の配列
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 構成音のインデックス値
*/
* @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 この和音のコード名
@Override
public String toString() {
String chordSymbol = rootNoteSymbol + symbolSuffix();
- if( ! rootNoteSymbol.equals(bassNoteSymbol) ) {
- chordSymbol += "/" + bassNoteSymbol;
- }
+ if( ! rootNoteSymbol.equals(bassNoteSymbol) ) chordSymbol += "/" + bassNoteSymbol;
return chordSymbol;
}
/**
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
}
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;
}
}
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 = "";
* @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); // 最初または最後の小節かを覚えておく
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 ) {
//
// 最初または最後の小節は常にトニックにする。
// サブドミナントを超えるとスケールを外れるので、超えそうになったらランダムに決め直す。
//
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 ) {
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);
break;
}
measure.add( lastChordStroke = new ChordStroke(chord) );
- prev_chord = chord;
+ prevChord = chord;
}
line.add(measure);
if( (mp+1) % 8 == 0 ) { // 8小節おきに改行
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();
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));
}
}
}
}
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();
}
}
* 指定されたコードと同名、または最も近い調を構築します。
* <ul>
* <li>コード構成音に短3度があればマイナー、なければメジャーとなります(区別なしになることはありません)。</li>
- * <li>調として存在しないコード(例:A#)が来た場合、存在する異名同音の調(例:B♭)に置き換えられます。</li>
+ * <li>調として存在しないコード(例:A♯)が来た場合、存在する異名同音の調(例:B♭)に置き換えられます。</li>
* </ul>
* @param chord コード(和音)
*/
return mmo.equals(majorMinor) ? this : new Key(co5 - 3 * majorMinor.index(), mmo);
}
/**
- * この調に異名同音の調があればそれを返します。
- * <p>ä¾\8bã\81\88ã\81°ã\80\81â\99ï¼\95å\80\8bï¼\88Dâ\99ã\83¡ã\82¸ã\83£ã\83¼ï¼\89ã\81®å ´å\90\88ã\81¯â\99¯ï¼\97å\80\8bï¼\88Câ\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ï¼\88Dâ\99ã\83¡ã\82¸ã\83£ã\83¼ï¼\89ã\81®å ´å\90\88ã\80\81ç\95°å\90\8då\90\8cé\9f³ã\81®èª¿ã\81¯â\99¯ï¼\97å\80\8bï¼\88Câ\99¯ã\83¡ã\82¸ã\83£ã\83¼ï¼\89ã\81¨ã\81ªã\82\8aます。
* 異名同音の調が存在しない調(4♯~4♭)に対してこのメソッドを呼び出した場合、
* この調自身を返します。
* </p>