import camidion.chordhelper.chordmatrix.ChordMatrix;
import camidion.chordhelper.music.Chord;
import camidion.chordhelper.music.MIDISpec;
-import camidion.chordhelper.music.Music;
-import camidion.chordhelper.music.NoteSymbol;
+import camidion.chordhelper.music.Note;
import camidion.chordhelper.pianokeyboard.PianoKeyboard;
/**
setText("MIDI note No." + noteNumber + " : " + pn);
}
else {
- String ns = NoteSymbol.noteNumberToSymbol(noteNumber);
- double f = Music.noteNumberToFrequency(noteNumber);
+ String ns = Note.noteNumberToSymbol(noteNumber);
+ double f = MIDISpec.noteNumberToFrequency(noteNumber);
setText("Note: "+ns+" - MIDI note No."+noteNumber+" : "+Math.round(f)+"Hz");
}
}
*/
public static class VersionInfo {
public static final String NAME = "MIDI Chord Helper";
- public static final String VERSION = "Ver.20170104.1";
+ public static final String VERSION = "Ver.20170109.1";
public static final String COPYRIGHT = "Copyright (C) 2004-2017";
public static final String AUTHER = "@きよし - Akiyoshi Kamide";
public static final String URL = "http://www.yk.rim.or.jp/~kamide/music/chordhelper/";
import camidion.chordhelper.chorddiagram.ChordDiagram.Instrument;
import camidion.chordhelper.music.Chord;
-import camidion.chordhelper.music.Music;
-import camidion.chordhelper.music.NoteSymbol;
+import camidion.chordhelper.music.Note;
/**
* コードダイアグラム表示部
}
g2.setColor(getForeground());
g2.drawString(
- NoteSymbol.noteNumberToSymbol(notesWhenOpen[i], 2),
+ Note.noteNumberToSymbol(notesWhenOpen[i], 2),
LEFT_MARGIN_WIDTH,
string_y + (fm.getHeight() - fm.getDescent())/2
);
continue;
int note = notesWhenOpen[button.stringIndex];
note += (e.getButton()==MouseEvent.BUTTON3 ? 11 : 1);
- notesWhenOpen[button.stringIndex] = Music.mod12(note);
+ notesWhenOpen[button.stringIndex] = Note.mod12(note);
setChord();
return;
}
import camidion.chordhelper.midieditor.SequenceTickIndex;
import camidion.chordhelper.music.Chord;
import camidion.chordhelper.music.Key;
-import camidion.chordhelper.music.Music;
-import camidion.chordhelper.music.NoteSymbol;
+import camidion.chordhelper.music.Note;
/**
* MIDI Chord Helper 用のコードボタンマトリクス
implements MouseListener, KeyListener, MouseMotionListener, MouseWheelListener
{
/** 列数 */
- public static final int N_COLUMNS = Music.SEMITONES_PER_OCTAVE * 2 + 1;
+ public static final int N_COLUMNS = Note.SEMITONES_PER_OCTAVE * 2 + 1;
/** 行数 */
public static final int CHORD_BUTTON_ROWS = 3;
/** 調号ボタン */
/** コードボタンの下のコード表示部 */
public ChordDisplayLabel chordDisplay = new ChordDisplayLabel("Chord Pad", this, null);
- private NoteWeight noteWeightArray[] = new NoteWeight[Music.SEMITONES_PER_OCTAVE];
+ private NoteWeight noteWeightArray[] = new NoteWeight[Note.SEMITONES_PER_OCTAVE];
/**
* 発音中のノート表示をクリアします。
*/
*/
public void note(boolean isNoteOn, int noteNumber) {
int diff = (isNoteOn ? 1 : -1);
- NoteWeight w = noteWeightArray[Music.mod12(noteNumber)];
+ NoteWeight w = noteWeightArray[Note.mod12(noteNumber)];
if( noteNumber < 49 ) w.addBass(diff); else w.add(diff);
}
String tip = "Key signature: ";
if(v != key.toCo5()) tip += "out of range" ; else {
tip += key.signatureDescription() + " " +
- key.toStringIn(NoteSymbol.Language.IN_JAPANESE);
+ key.toStringIn(Note.Language.IN_JAPANESE);
if( v == 0 ) {
setIcon(new ButtonIcon(ButtonIcon.NATURAL_ICON));
}
setFont(isBold ? boldFont : plainFont);
}
public void keyChanged() {
- int co5Key = capoKey.toCo5();
- int co5Offset = co5Value - co5Key;
+ int co5Offset = co5Value - capoKey.toCo5();
inActiveZone = (co5Offset <= 6 && co5Offset >= -6) ;
- int rootNote = chord.rootNoteSymbol().toNoteNumber();
+ int root = chord.rootNoteSymbol().toNoteNumber();
//
// Reconstruct color index
//
// Root
- indicatorColorIndices[0] = Music.isOnScale(rootNote, co5Key) ? 0 : co5Offset > 0 ? 1 : 2;
+ indicatorColorIndices[0] = capoKey.isOnScale(root) ? 0 : co5Offset > 0 ? 1 : 2;
//
// 3rd / sus4
- indicatorColorIndices[1] = Music.isOnScale(rootNote+(isMinor?3:isSus4?5:4), co5Key) ? 0 : co5Offset > 0 ? 1 : 2;
+ indicatorColorIndices[1] = capoKey.isOnScale(root+(isMinor?3:isSus4?5:4)) ? 0 : co5Offset > 0 ? 1 : 2;
//
// P5th
- indicatorColorIndices[2] = Music.isOnScale(rootNote+7, co5Key) ? 0 : co5Offset > 0 ? 1 : 2;
+ indicatorColorIndices[2] = capoKey.isOnScale(root+7) ? 0 : co5Offset > 0 ? 1 : 2;
//
// dim5th
- indicatorColorIndices[3] = Music.isOnScale(rootNote+6, co5Key) ? 0 : co5Offset > 4 ? 1 : 2;
+ indicatorColorIndices[3] = capoKey.isOnScale(root+6) ? 0 : co5Offset > 4 ? 1 : 2;
//
// aug5th
- indicatorColorIndices[4] = Music.isOnScale(rootNote+8, co5Key) ? 0 : co5Offset > -3 ? 1 : 2;
+ indicatorColorIndices[4] = capoKey.isOnScale(root+8) ? 0 : co5Offset > -3 ? 1 : 2;
}
}
// Make key-signature labels and chord labels
KeySignatureLabel l;
int i, v;
- for (i=0, v= -Music.SEMITONES_PER_OCTAVE; i<N_COLUMNS; i++, v++) {
+ for (i=0, v= -Note.SEMITONES_PER_OCTAVE; i<N_COLUMNS; i++, v++) {
l = new KeySignatureLabel(v);
l.addMouseListener(this);
l.addMouseMotionListener(this);
v = i - (N_COLUMNS * row) - 12;
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;
+ case 0: chord = new Chord(new Note(v), Chord.Interval.SUS4); break;
+ case 2: chord = new Chord(new Note(v+3), Chord.Interval.MINOR); break;
+ default: chord = new Chord(new Note(v)); break;
}
ChordLabel cl = new ChordLabel(chord);
cl.addMouseListener(this);
}
});
setLayout(new GridLayout( 4, N_COLUMNS, 2, 2 ));
- setKeySignature(new Key());
+ setKeySignature(Key.C_MAJOR_OR_A_MINOR);
//
// Setup note weight array
//
else if( obj instanceof KeySignatureLabel ) {
int v = ((KeySignatureLabel)obj).co5Value;
if( (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {
- setKeySignature( new Key(Music.oppositeCo5(v)) );
+ setKeySignature( new Key(Note.oppositeCo5(v)) );
}
else if ( v == key.toCo5() ) {
//
KeySignatureLabel keyDraggedTo = (KeySignatureLabel)draggedTo;
int v = keyDraggedTo.co5Value;
if( (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {
- v = Music.oppositeCo5(v);
+ v = Note.oppositeCo5(v);
}
setKeySignature(new Key(v));
repaint();
}
else if( keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_KP_DOWN ) {
// Semitone down
- setKeySignature(new Key(Music.transposeCo5(keyCo5, -1)));
+ setKeySignature(new Key(Note.transposeCo5(keyCo5, -1)));
return;
}
else if( keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_KP_UP ) {
// Semitone up
- setKeySignature(new Key(Music.transposeCo5(keyCo5, 1)));
+ setKeySignature(new Key(Note.transposeCo5(keyCo5, 1)));
return;
}
if( i < 0 ) return; // No key char found
}
else {
keysigLabels[this.key.toCo5() + 12].setSelection(false);
- for( i = Music.mod12(this.key.toCo5()); i < N_COLUMNS; i+=12 ) {
+ for( i = Note.mod12(this.key.toCo5()); i < N_COLUMNS; i+=12 ) {
keysigLabels[i].setBackground(false);
}
}
// Set new value
keysigLabels[i = key.toCo5() + 12].setSelection(true);
- for( i = Music.mod12(key.toCo5()); i < N_COLUMNS; i+=12 ) {
+ for( i = Note.mod12(key.toCo5()); i < N_COLUMNS; i+=12 ) {
keysigLabels[i].setBackground(true);
}
// Change chord-label's color & font
}
else cl.setBackground(i_color);
if( !(cl.isSus4) ) {
- if( this.key != null && Music.mod12(cl.co5Value - this.key.toCo5()) == 0)
+ if( this.key != null && Note.mod12(cl.co5Value - this.key.toCo5()) == 0)
cl.setBold(false);
- if( Music.mod12( cl.co5Value - key.toCo5() ) == 0 )
+ if( Note.mod12( cl.co5Value - key.toCo5() ) == 0 )
cl.setBold(true);
}
}
import javax.swing.JLabel;
import camidion.chordhelper.music.Key;
-import camidion.chordhelper.music.NoteSymbol;
+import camidion.chordhelper.music.Note;
/**
* 調表示ラベル
}
setText( "key:" + key.toString() );
setToolTipText(
- "Key: " + key.toStringIn(NoteSymbol.Language.NAME)
- + " " + key.toStringIn(NoteSymbol.Language.IN_JAPANESE)
+ "Key: " + key.toStringIn(Note.Language.NAME)
+ + " " + key.toStringIn(Note.Language.IN_JAPANESE)
+ " (" + key.signatureDescription() + ")"
);
setEnabled(true);
import javax.swing.JPanel;
import camidion.chordhelper.music.Key;
-import camidion.chordhelper.music.NoteSymbol;
+import camidion.chordhelper.music.Note;
/**
* 調性選択フォーム
public void actionPerformed(ActionEvent e) {
Key key = (Key)keysigCombobox.getSelectedItem();
keysigCombobox.setToolTipText(
- "Key: " + key.toStringIn( NoteSymbol.Language.NAME )
- + " " + key.toStringIn( NoteSymbol.Language.IN_JAPANESE )
+ "Key: " + key.toStringIn( Note.Language.NAME )
+ + " " + key.toStringIn( Note.Language.IN_JAPANESE )
+ " (" + key.signatureDescription() + ")"
);
}
import camidion.chordhelper.music.Key;
import camidion.chordhelper.music.MIDISpec;
-import camidion.chordhelper.music.NoteSymbol;
+import camidion.chordhelper.music.Note;
import camidion.chordhelper.pianokeyboard.PianoKeyboardAdapter;
import camidion.chordhelper.pianokeyboard.PianoKeyboardPanel;
new DefaultComboBoxModel<String>() {
{
for( int i = 0; i<=0x7F; i++ ) addElement(
- String.format("0x%02X : %d : %s", i, i, NoteSymbol.noteNumberToSymbol(i))
+ String.format("0x%02X : %d : %s", i, i, Note.noteNumberToSymbol(i))
);
// Center note C
setSelectedItem(getElementAt(60));
* ルート音を返します。
* @return ルート音
*/
- public NoteSymbol rootNoteSymbol() { return rootNoteSymbol; }
- private NoteSymbol rootNoteSymbol;
+ public Note rootNoteSymbol() { return rootNoteSymbol; }
+ private Note rootNoteSymbol;
/**
* ベース音を返します。オンコードの場合はルート音と異なります。
* @return ベース音
*/
- public NoteSymbol bassNoteSymbol() { return bassNoteSymbol; }
- private NoteSymbol bassNoteSymbol;
+ public Note bassNoteSymbol() { return bassNoteSymbol; }
+ private Note bassNoteSymbol;
/**
* 指定した音程が設定されているか調べます。
* @param interval 音程
* (メジャーコードの構成音である長三度・完全五度は、指定しなくてもデフォルトで設定されます)
* @throws NullPointerException ルート音にnullが指定された場合
*/
- public Chord(NoteSymbol root, Interval... intervals) {
+ public Chord(Note root, Interval... intervals) {
this(root, root, intervals);
}
/**
* (メジャーコードの構成音である長三度・完全五度は、指定しなくてもデフォルトで設定されます)
* @throws NullPointerException ルート音にnullが指定された場合
*/
- public Chord(NoteSymbol root, NoteSymbol bass, Interval... intervals) {
+ public Chord(Note root, Note bass, Interval... intervals) {
this(root, bass, Arrays.asList(intervals));
}
/**
* (メジャーコードの構成音である長三度・完全五度は、指定しなくてもデフォルトで設定されます)
* @throws NullPointerException ルート音、または構成音コレクションにnullが指定された場合
*/
- public Chord(NoteSymbol root, NoteSymbol bass, Collection<Interval> intervals) {
+ public Chord(Note root, Note bass, Collection<Interval> intervals) {
rootNoteSymbol = Objects.requireNonNull(root);
bassNoteSymbol = (bass==null ? root : bass);
intervalMap = new HashMap<>();
int keyCo5 = key.toCo5(); if( key.majorMinor() == Key.MajorMinor.MINOR ) {
keyCo5 += 3; set(Interval.MINOR);
} else set(Interval.MAJOR);
- bassNoteSymbol = rootNoteSymbol = new NoteSymbol(keyCo5);
+ bassNoteSymbol = rootNoteSymbol = new Note(keyCo5);
fixIntervals();
}
/**
public Chord(String chordSymbol) {
String rootOnBass[] = Objects.requireNonNull(chordSymbol, "Chord symbol must not be null").trim().split("(/|on)");
String root = (rootOnBass.length > 0 ? rootOnBass[0].trim() : "");
- bassNoteSymbol = rootNoteSymbol = new NoteSymbol(root);
+ bassNoteSymbol = rootNoteSymbol = new Note(root);
if( rootOnBass.length > 1 ) {
String bass = rootOnBass[1].trim();
- if( ! root.equals(bass) ) bassNoteSymbol = new NoteSymbol(bass);
+ if( ! root.equals(bass) ) bassNoteSymbol = new Note(bass);
}
intervalMap = new HashMap<>();
set(Interval.MAJOR);
*/
public int indexOf(int noteNumber) {
int relativeNote = noteNumber - rootNoteSymbol.toNoteNumber();
- if( Music.mod12(relativeNote) == 0 ) return 0;
+ if( Note.mod12(relativeNote) == 0 ) return 0;
Interval itv;
int i=0;
for( IntervalGroup offsetIndex : IntervalGroup.values() ) {
if( (itv = intervalMap.get(offsetIndex)) != null ) {
i++;
- if( Music.mod12(relativeNote - itv.getChromaticOffset()) == 0 )
+ if( Note.mod12(relativeNote - itv.getChromaticOffset()) == 0 )
return i;
}
}
* @throws NullPointerException キーにnullが指定された場合
*/
public boolean isOnScaleIn(Key key) {
- int keyCo5 = key.toCo5();
- int rootnote = rootNoteSymbol.toNoteNumber();
- if( ! Music.isOnScale(rootnote, keyCo5) ) return false;
+ int root = rootNoteSymbol.toNoteNumber();
+ if( ! key.isOnScale(root) ) return false;
Interval itv;
for( IntervalGroup offsetIndex : IntervalGroup.values() ) {
if( (itv = intervalMap.get(offsetIndex)) == null ) continue;
- if( ! Music.isOnScale(rootnote + itv.getChromaticOffset(), keyCo5) ) return false;
+ if( ! key.isOnScale(root + itv.getChromaticOffset()) ) return false;
}
return true;
}
}
private Chord transposedNewChord(int chromaticOffset, int originalKeyCo5) {
if( chromaticOffset == 0 ) return this;
- int offsetCo5 = Music.mod12(Music.toggleCo5(chromaticOffset));
+ int offsetCo5 = Note.mod12(Note.toggleCo5(chromaticOffset));
if( offsetCo5 > 6 ) offsetCo5 -= 12;
int keyCo5 = originalKeyCo5 + offsetCo5;
int newRootCo5 = rootNoteSymbol.toCo5() + offsetCo5;
newRootCo5 += 12;
newBassCo5 += 12;
}
- NoteSymbol root = new NoteSymbol(newRootCo5);
- NoteSymbol bass = (newBassCo5 == newRootCo5 ? root : new NoteSymbol(newBassCo5));
+ Note root = new Note(newRootCo5);
+ Note bass = (newBassCo5 == newRootCo5 ? root : new Note(newBassCo5));
return new Chord(root, bass, intervals);
}
* @return コードの説明(英語)
*/
public String toName() {
- String name = rootNoteSymbol.toStringIn(NoteSymbol.Language.NAME) + nameSuffix() ;
+ String name = rootNoteSymbol.toStringIn(Note.Language.NAME) + nameSuffix() ;
if( ! rootNoteSymbol.equals(bassNoteSymbol) ) {
- name += " on " + bassNoteSymbol.toStringIn(NoteSymbol.Language.NAME);
+ name += " on " + bassNoteSymbol.toStringIn(Note.Language.NAME);
}
return name;
}
} while( co5Offset == prevCo5Offset );
}
}
- NoteSymbol rootNote = new NoteSymbol(keyCo5 + co5Offset);
+ Note rootNote = new Note(keyCo5 + co5Offset);
List<Chord.Interval> intervals = new ArrayList<>();
switch(co5Offset) {
// ルート音ごとに、7th などの付加や、メジャーマイナー反転を行う確率を決める
int original_key_co5 = key.toCo5();
int co5Offset = 0;
if( original_key_co5 > 4 ) {
- co5Offset = -Music.SEMITONES_PER_OCTAVE;
+ co5Offset = -Note.SEMITONES_PER_OCTAVE;
}
else if( original_key_co5 < -4 ) {
- co5Offset = Music.SEMITONES_PER_OCTAVE;
+ co5Offset = Note.SEMITONES_PER_OCTAVE;
}
else {
return;
Object element = measure.get(i);
if( element instanceof ChordStroke ) {
ChordStroke cs = (ChordStroke)element;
- NoteSymbol root = cs.chord.rootNoteSymbol();
- NoteSymbol bass = cs.chord.bassNoteSymbol();
+ Note root = cs.chord.rootNoteSymbol();
+ Note bass = cs.chord.bassNoteSymbol();
if( root.equals(bass) ) {
- bass = root = new NoteSymbol(root.toCo5() + co5Offset);
+ bass = root = new Note(root.toCo5() + co5Offset);
} else {
- root = new NoteSymbol(root.toCo5() + co5Offset);
- bass = new NoteSymbol(bass.toCo5() + co5Offset);
+ root = new Note(root.toCo5() + co5Offset);
+ bass = new Note(bass.toCo5() + co5Offset);
}
Chord newChord = new Chord(root, bass, cs.chord.intervals());
measure.set(i, new ChordStroke(newChord, cs.beatLength));
*/
public static final int MAX_SHARPS_OR_FLATS = 7;
/**
+ * 調号が空(C/Am ハ長調またはイ短調)で、メジャー・マイナーの区別のない調
+ */
+ public static final Key C_MAJOR_OR_A_MINOR = new Key();
+ private Key() { }
+ /**
* キー指定(メジャー/マイナー/両方)
*/
public enum MajorMinor {
*/
private MajorMinor majorMinor = MajorMinor.MAJOR_OR_MINOR;
/**
- * 調号が空(C/Am ハ長調またはイ短調)で、メジャー・マイナーの区別のない調を構築します。
- */
- public Key() { }
- /**
* 指定の五度圏インデックス値を持つ、メジャー・マイナーの区別のない調を構築します。
*
* @param co5 五度圏インデックス値
* @throw IllegalArgumentException 引数が空文字列の場合、または音名で始まっていない場合
*/
public Key(String keySymbol) throws IllegalArgumentException {
- co5 = NoteSymbol.co5OfSymbol(keySymbol);
+ co5 = Note.toCo5Of(keySymbol);
if( keySymbol.matches(".*m") ) { majorMinor = MajorMinor.MINOR; co5 -= 3; }
else majorMinor = MajorMinor.MAJOR;
normalize();
*/
private void normalize() {
if( co5 >= -MAX_SHARPS_OR_FLATS && co5 <= MAX_SHARPS_OR_FLATS ) return;
- if( (co5 = Music.mod12(co5)) > 6 ) co5 -= Music.SEMITONES_PER_OCTAVE;
+ if( (co5 = Note.mod12(co5)) > 6 ) co5 -= Note.SEMITONES_PER_OCTAVE;
}
/**
* 五度圏インデックス値を返します。
*/
@Override
public String toString() {
- String s = toStringIn(NoteSymbol.Language.SYMBOL);
- if(majorMinor == MajorMinor.MAJOR_OR_MINOR) s = signature() + " : " + s;
+ String s = toStringIn(Note.Language.SYMBOL);
+ if( majorMinor == MajorMinor.MAJOR_OR_MINOR ) s = signature() + " : " + s;
return s;
}
/**
- * この調の文字列表現を、指定された形式で返します。
+ * この調の文字列表現を、指定された言語モードで返します。
+ * @param language 言語モード
* @return この調の文字列表現
*/
- public String toStringIn(NoteSymbol.Language language) {
+ public String toStringIn(Note.Language language) {
return language.stringOf(this);
}
/**
return new byte[] {(byte) co5, (byte) ((majorMinor == MajorMinor.MINOR) ? 1 : 0)};
}
/**
- * 相対ドの音階を返します。
+ * この調の相対ドの音階を返します。
* @return 相対ドの音階(0~11)
*/
- public int relativeDo() { return NoteSymbol.majorCo5ToNoteNumber(co5); }
+ public int relativeDo() { return Note.mod12(Note.toggleCo5(co5)); }
/**
* この調のルート音を表すノート番号(オクターブ抜き)を返します。
- * メジャーキーの場合は相対ド、
- * マイナーキーの場合は相対ラの音階です。
+ * メジャーキーの場合は相対ド、マイナーキーの場合は相対ラの音階です。
*
* @return キーのルート音(0~11)
*/
public int rootNoteNumber() {
- int n = relativeDo();
- return majorMinor==MajorMinor.MINOR ? Music.mod12(n-3) : n;
+ return majorMinor==MajorMinor.MINOR ? Note.mod12(Note.toggleCo5(co5) - 3) : relativeDo();
}
/**
- * 指定されたノート番号の音が、この調のスケールの構成音か調べます。
- * メジャーキーの場合はメジャースケール、
- * マイナーキーの場合はナチュラルマイナースケールとして判断されます。
+ * 指定されたMIDIノート番号の示す音階が、この調のメジャースケールまたは
+ * ナチュラルマイナースケールの構成音に該当するか調べます。
+ *
+ * キーがハ長調またはイ短調の場合、白鍵のときにtrue、黒鍵のときにfalseを返します。
*
* @param noteNumber ノート番号
* @return 指定されたノート番号がこのキーのスケールの構成音ならtrue
*/
- public boolean isOnScale(int noteNumber) { return Music.isOnScale(noteNumber, co5); }
+ public boolean isOnScale(int noteNumber) {
+ return Note.mod12(Note.toggleCo5(noteNumber) - co5 + 1) < 7 ;
+ }
/**
* この調に対する平行調を返します。
* これは元の調と同じ調号を持つ、メジャーとマイナーが異なる調です。
* @return 移調した調
*/
public Key transposedKey(int chromaticOffset) {
- return chromaticOffset == 0 ? this : new Key(Music.transposeCo5(co5, chromaticOffset), majorMinor);
+ return chromaticOffset == 0 ? this : new Key(Note.transposeCo5(co5, chromaticOffset), majorMinor);
}
/**
* 五度圏で真裏にあたる調を返します。
* @return 五度圏で真裏にあたる調
*/
public Key createOppositeKey() {
- return new Key(Music.oppositeCo5(co5), majorMinor);
+ return new Key(Note.oppositeCo5(co5), majorMinor);
}
}
public static final int MAX_CHANNELS = 16;
public static final int PITCH_BEND_NONE = 8192;
/**
+ * 指定されたMIDIノート番号の音の周波数(A=440Hzチューニング時)を返します。
+ * @param noteNumber MIDIノート番号
+ * @return 音の周波数[Hz]
+ */
+ public static double noteNumberToFrequency(int noteNumber) {
+ return 55 * Math.pow( 2, (double)(noteNumber - 33)/12 );
+ }
+ /**
* メタメッセージタイプ名マップ
*/
private static final Map<Integer,String>
str += getPercussionName(data1);
}
else {
- str += NoteSymbol.noteNumberToSymbol(data1);
+ str += Note.noteNumberToSymbol(data1);
}
str +="] Velocity=" + data2;
break;
if( msgdata.length == 2 ) {
Key key = new Key(msgdata);
str += ": " + key.signatureDescription();
- str += " (" + key.toStringIn(NoteSymbol.Language.NAME) + ")";
+ str += " (" + key.toStringIn(Note.Language.NAME) + ")";
break;
}
str += ": Size not 2 byte : data=(";
*/
public MelodyTrackSpec(int ch, String name) {
super(ch,name);
- range = new Range(Music.SEMITONES_PER_OCTAVE * 5, Music.SEMITONES_PER_OCTAVE * 6 );
+ range = new Range(Note.SEMITONES_PER_OCTAVE * 5, Note.SEMITONES_PER_OCTAVE * 6 );
}
/**
* 音域を指定してメロディトラック仕様を構築
int totalWeight = 0;
for( i=0; i<noteWeights.length; i++ ) {
noteNumber = range.minNote + i;
- int m12 = Music.mod12(noteNumber - chord.rootNoteSymbol().toNoteNumber());
+ int m12 = Note.mod12(noteNumber - chord.rootNoteSymbol().toNoteNumber());
int w;
if( chord.indexOf(noteNumber) >= 0 ) {
// コード構成音は確率を上げる
+++ /dev/null
-package camidion.chordhelper.music;
-
-/**
- * 音楽理論ユーティリティ
- *
- * Circle-Of-Fifth based music theory functions
- *
- * @author Copyright (C) 2004-2016 @きよし - Akiyoshi Kamide
- */
-public class Music {
- /**
- * 1オクターブの半音数
- */
- public static final int SEMITONES_PER_OCTAVE = 12;
- /**
- * MIDIノート番号と、メジャーキー基準の五度圏インデックス値との間の変換を行います。
- *
- * <p>双方向の変換に対応しています。
- * 内部的には、元の値が奇数のときに6(半オクターブ)を足し、偶数のときにそのまま返しているだけです。
- * 値は0~11であるとは限りません。その範囲に補正したい場合は {@link #mod12(int)} を併用します。
- * </p>
- *
- * @param n 元の値
- * @return 変換結果
- */
- public static int toggleCo5(int n) { return (n & 1) == 0 ? n : n+6 ; }
- /**
- * ノート番号からオクターブ成分を抜きます。
- * <p>n % 12 と似ていますが、Java の % 演算子では、左辺に負数を与えると答えも負数になってしまうため、
- * n % 12 で計算しても 0~11 の範囲を外れてしまうことがあります。
- * そこで、負数の場合に12を足すことにより 0~11 の範囲に入るよう補正します。
- * </p>
- * @param n 元のノート番号
- * @return オクターブ成分を抜いたノート番号(0~11)
- */
- public static int mod12(int n) {
- int qn = n % SEMITONES_PER_OCTAVE;
- return qn < 0 ? qn + 12 : qn ;
- }
- /**
- * 指定されたMIDIノート番号の音の周波数(A=440Hzチューニング時)を返します。
- * @param noteNumber MIDIノート番号
- * @return 音の周波数[Hz]
- */
- public static double noteNumberToFrequency(int noteNumber) {
- return 55 * Math.pow( 2, (double)(noteNumber - 33)/12 );
- }
- /**
- * MIDIノート番号の示す音階が、指定された調(五度圏インデックス値)におけるメジャースケールまたは
- * ナチュラルマイナースケールの構成音に該当するか調べます。
- *
- * <p>調の五度圏インデックス値に0(ハ長調またはイ短調)を指定すると、
- * 白鍵のときにtrue、黒鍵のときにfalseを返します。
- * </p>
- *
- * @param noteNumber 調べたい音階のノート番号
- * @param keyCo5 調の五度圏インデックス値
- * @return スケール構成音のときtrue、スケールを外れている場合false
- */
- public static boolean isOnScale(int noteNumber, int keyCo5) {
- return mod12(toggleCo5(noteNumber) - keyCo5 + 1) < 7 ;
- }
- /**
- * 五度圏インデックス値で表された音階を、
- * 指定された半音数だけ移調した結果を返します。
- *
- * <p>移調する半音数が0の場合、指定の五度圏インデックス値をそのまま返します。
- * それ以外の場合、移調結果を -5 ~ 6 の範囲で返します。
- * </p>
- *
- * @param co5 五度圏インデックス値
- * @param chromaticOffset 移調する半音数
- * @return 移調結果
- */
- public static int transposeCo5(int co5, int chromaticOffset) {
- if( chromaticOffset == 0 ) return co5;
- int transposedCo5 = mod12( co5 + toggleCo5(chromaticOffset) );
- if( transposedCo5 > 6 ) transposedCo5 -= Music.SEMITONES_PER_OCTAVE;
- return transposedCo5;
- }
- /**
- * 指定の五度圏インデックス値の真裏にあたる値を返します。
- * @param co5 五度圏インデックス値
- * @return 真裏の五度圏インデックス値
- */
- public static int oppositeCo5(int co5) { return co5 > 0 ? co5 - 6 : co5 + 6; }
-}
-
* 非常に高い親和性を持ちます。
* </p>
*/
-public class NoteSymbol {
- private static final int INDEX_OF_A = Language.SYMBOL.indexOf("A");
- private static final int INDEX_OF_C = Language.SYMBOL.indexOf("C");
+public class Note {
/**
* 音階や調を表すシンボルの言語モードによる違いを定義します。
* <p>音名には、下記のような五度圏順のインデックス値(0~34)が割り当てられます。
*/
private String majorMinorDelimiter;
/**
- * 調の文字列表現を返します。メジャー/マイナーの区別がない場合、両方の表現を返します。
- * @param majorCo5 調の五度圏の値(0 == C/Am)
- * @param majorMinor メジャー/マイナーの区別
+ * 調の文字列表現を返します。メジャー/マイナーの区別がない場合、両方の表現を返します(例: "A / F#m")。
+ * @param key 対象の調
* @return 調の文字列表現
*/
public String stringOf(Key key) {
return s + stringOf(co5 + INDEX_OF_A) + minor;
}
}
+ private static final int INDEX_OF_A = Language.SYMBOL.indexOf("A");
+ private static final int INDEX_OF_C = Language.SYMBOL.indexOf("C");
/** メジャーキー基準の五度圏インデックス値 */
private int majorCo5;
/** ノート番号(0~11) */
private int noteNumber;
/**
+ * 1オクターブの半音数
+ */
+ public static final int SEMITONES_PER_OCTAVE = 12;
+ /**
* 五度圏インデックス値(メジャーキー基準)から音名を構築します。
* @param majorCo5 五度圏インデックス値
*/
- public NoteSymbol(int majorCo5) {
- noteNumber = majorCo5ToNoteNumber(this.majorCo5 = majorCo5);
+ public Note(int majorCo5) {
+ noteNumber = mod12(toggleCo5(this.majorCo5 = majorCo5));
}
/**
* 音名を文字列から構築します。
* @throws NullPointerException 引数がnullの場合
* @throws IllegalArgumentException 引数が空文字列の場合、または音名で始まっていない場合
*/
- public NoteSymbol(String noteSymbol) { this(co5OfSymbol(noteSymbol)); }
+ public Note(String noteSymbol) { this(toCo5Of(noteSymbol)); }
/**
* この音階が指定されたオブジェクトと等しいか調べます。
*
@Override
public boolean equals(Object anObject) {
if( this == anObject ) return true;
- if( ! (anObject instanceof NoteSymbol) ) return false;
- return majorCo5 == ((NoteSymbol)anObject).majorCo5;
+ if( ! (anObject instanceof Note) ) return false;
+ return majorCo5 == ((Note)anObject).majorCo5;
}
/**
* この音階のハッシュコード値として、
* @param another 比較対象の音階
* @return 等しければtrue
*/
- public boolean equalsEnharmonically(NoteSymbol another) {
+ public boolean equalsEnharmonically(Note another) {
return this == another || this.noteNumber == another.noteNumber;
}
/**
*/
public int toCo5() { return majorCo5; }
/**
- * ノート番号(0~11)を返します。
- * <p>これはMIDIノート番号からオクターブ情報を抜いた値と同じです。
- * 五度圏インデックス値をノート番号に変換した場合、
- * 異名同音、すなわち同じ音階が♭表記、♯表記のどちらだったか
- * という情報は失われます。
- * </p>
- * @return ノート番号(0~11)
+ * 引数で指定された音名のメジャーキー基準の五度圏インデックスを返します。
+ * @param noteSymbol 音名の文字列
+ * @return メジャーキー基準の五度圏インデックス
+ * @throws NullPointerException 引数がnullの場合
+ * @throws IllegalArgumentException 引数が空文字列の場合、または音名で始まっていない場合
+ */
+ public static int toCo5Of(String noteSymbol) {
+ return Language.SYMBOL.indexOf(noteSymbol) - INDEX_OF_C;
+ }
+ /**
+ * この音階に対応するMIDIノート番号(0オリジン表記)の最小値(オクターブの最も低い値)を返します。
+ * @return MIDIノート番号の最小値(0~11)
*/
public int toNoteNumber() { return noteNumber; }
/**
* @return 文字列表現
* @throws NullPointerException 言語モードがnullの場合
*/
- public String toStringIn(Language language) { return stringOf(majorCo5, language); }
-
+ public String toStringIn(Language language) {
+ return language.stringOf(majorCo5 + INDEX_OF_C);
+ }
/**
- * 指定された五度圏インデックス値(メジャーキー基準)に対応する音階の文字列表現を、
- * 指定された言語モードで返します。
- * @param majorCo5 五度圏インデックス値(メジャーキー基準、すなわち0のときC)
- * @param language 言語モード
- * @return 文字列表現
- * @throws NullPointerException 言語モードがnullの場合
- * @throws IndexOutOfBoundsException
- * 五度圏インデックス値がダブルフラット(F♭♭,C♭♭,…)からダブルシャープ(…,Ex,Bx)までの範囲(-15 ~ 19)を外れている場合
+ * MIDIノート番号と、メジャーキー基準の五度圏インデックス値との間の変換を行います。
+ *
+ * <p>双方向の変換に対応しています。
+ * 内部的には、元の値が奇数のときに6(半オクターブ)を足し、偶数のときにそのまま返しているだけです。
+ * 値は0~11であるとは限りません。その範囲に補正したい場合は {@link #mod12(int)} を併用します。
+ * </p>
+ *
+ * @param n 元の値
+ * @return 変換結果
*/
- public static String stringOf(int majorCo5, Language language) {
- return language.stringOf(majorCo5 + INDEX_OF_C);
+ public static int toggleCo5(int n) { return (n & 1) == 0 ? n : n+6 ; }
+ /**
+ * ノート番号からオクターブ成分を抜きます。
+ * <p>n % 12 と似ていますが、Java の % 演算子では、左辺に負数を与えると答えも負数になってしまうため、
+ * n % 12 で計算しても 0~11 の範囲を外れてしまうことがあります。
+ * そこで、負数の場合に12を足すことにより 0~11 の範囲に入るよう補正します。
+ * </p>
+ * @param n 元のノート番号
+ * @return オクターブ成分を抜いたノート番号(0~11)
+ */
+ public static int mod12(int n) {
+ int qn = n % SEMITONES_PER_OCTAVE;
+ return qn < 0 ? qn + 12 : qn ;
}
/**
- * 引数で指定された音名のメジャーキー基準の五度圏インデックスを返します。
- * @param noteSymbol 音名の文字列
- * @return メジャーキー基準の五度圏インデックス
- * @throws NullPointerException 引数がnullの場合
- * @throws IllegalArgumentException 引数が空文字列の場合、または音名で始まっていない場合
+ * 五度圏インデックス値で表された音階を、
+ * 指定された半音数だけ移調した結果を返します。
+ *
+ * <p>移調する半音数が0の場合、指定の五度圏インデックス値をそのまま返します。
+ * それ以外の場合、移調結果を -5 ~ 6 の範囲で返します。
+ * </p>
+ *
+ * @param co5 五度圏インデックス値
+ * @param chromaticOffset 移調する半音数
+ * @return 移調結果
*/
- public static int co5OfSymbol(String noteSymbol) {
- return Language.SYMBOL.indexOf(noteSymbol) - INDEX_OF_C;
+ public static int transposeCo5(int co5, int chromaticOffset) {
+ if( chromaticOffset == 0 ) return co5;
+ int transposedCo5 = mod12( co5 + toggleCo5(chromaticOffset) );
+ if( transposedCo5 > 6 ) transposedCo5 -= Note.SEMITONES_PER_OCTAVE;
+ return transposedCo5;
}
/**
+ * 指定の五度圏インデックス値の真裏にあたる値を返します。
+ * @param co5 五度圏インデックス値
+ * @return 真裏の五度圏インデックス値
+ */
+ public static int oppositeCo5(int co5) { return co5 > 0 ? co5 - 6 : co5 + 6; }
+ /**
* 指定の最大文字数の範囲で、MIDIノート番号が示す音名を返します。
* <p>白鍵の場合は A ~ G までの文字、黒鍵の場合は#と♭の両方の表現を返します。
* ただし、制限文字数の指定により#と♭の両方を返せないことがわかった場合、
* @return MIDIノート番号が示す音名
*/
public static String noteNumberToSymbol(int noteNumber, int maxChars) {
- int co5 = Music.mod12(Music.toggleCo5(noteNumber));
- if( co5 == 11 ) co5 -= Music.SEMITONES_PER_OCTAVE; // E# -> F
- if( co5 < 6 ) return stringOf(co5, Language.SYMBOL); // F C G D A E B
+ int co5 = mod12(toggleCo5(noteNumber));
+ if( co5 == 11 ) co5 -= Note.SEMITONES_PER_OCTAVE; // E# -> F
+ if( co5 < 6 ) return Language.SYMBOL.stringOf(co5 + INDEX_OF_C); // F C G D A E B
if( maxChars < 0 || maxChars >= "F# / Gb".length() ) return
- stringOf(co5, Language.SYMBOL) + " / " +
- stringOf(co5 - Music.SEMITONES_PER_OCTAVE, Language.SYMBOL);
+ Language.SYMBOL.stringOf(co5 + INDEX_OF_C) + " / " +
+ Language.SYMBOL.stringOf(co5 + INDEX_OF_C - Note.SEMITONES_PER_OCTAVE);
- if( co5 >= 8 ) co5 -= Music.SEMITONES_PER_OCTAVE; // G# -> Ab, D# -> Eb, A# -> Bb
- return stringOf(co5, Language.SYMBOL); // C# Eb F# Ab Bb
+ if( co5 >= 8 ) co5 -= Note.SEMITONES_PER_OCTAVE; // G# -> Ab, D# -> Eb, A# -> Bb
+ return Language.SYMBOL.stringOf(co5 + INDEX_OF_C); // C# Eb F# Ab Bb
}
/**
* MIDIノート番号が示す音名を返します。
public static String noteNumberToSymbol(int noteNumber) {
return noteNumberToSymbol(noteNumber, -1);
}
- /**
- * 指定された五度圏インデックス値(メジャーキー基準、すなわち0のときC)をノート番号(0~11 : C~B)に変換します。
- *
- * <p>これはMIDIノート番号からオクターブ情報を抜いた値と同じです。
- * 五度圏インデックス値をノート番号に変換した場合、異名同音、すなわち同じ音階が♭表記、♯表記のどちらだったか、という情報は失われます。
- * </p>
- * @param majorCo5 五度圏インデックス値
- * @return ノート番号
- */
- public static int majorCo5ToNoteNumber(int majorCo5) {
- return Music.mod12(Music.toggleCo5(majorCo5));
- }
-}
\ No newline at end of file
+}
int offset = 0;
if( key != null ) {
offset = key.relativeDo();
- if( minKeyOffset < 0 && offset >= Music.mod12(minKeyOffset) ) {
+ if( minKeyOffset < 0 && offset >= Note.mod12(minKeyOffset) ) {
offset -= 12;
}
- else if( minKeyOffset > 0 && offset < Music.mod12(minKeyOffset) ) {
+ else if( minKeyOffset > 0 && offset < Note.mod12(minKeyOffset) ) {
offset += 12;
}
minNote += offset;
maxNote += offset;
}
- int octave = minNote / Music.SEMITONES_PER_OCTAVE;
+ int octave = minNote / Note.SEMITONES_PER_OCTAVE;
noteNumber += 12 * octave;
while( noteNumber > maxNote )
- noteNumber -= Music.SEMITONES_PER_OCTAVE;
+ noteNumber -= Note.SEMITONES_PER_OCTAVE;
while( noteNumber > MIDISpec.MAX_NOTE_NO )
- noteNumber -= Music.SEMITONES_PER_OCTAVE;
+ noteNumber -= Note.SEMITONES_PER_OCTAVE;
while( noteNumber < minNote )
- noteNumber += Music.SEMITONES_PER_OCTAVE;
+ noteNumber += Note.SEMITONES_PER_OCTAVE;
while( noteNumber < 0 )
- noteNumber += Music.SEMITONES_PER_OCTAVE;
+ noteNumber += Note.SEMITONES_PER_OCTAVE;
return noteNumber;
}
public void invertNotesOf(int[] notes, Key key) {
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
add( Box.createHorizontalStrut(5) );
add(velocitySelecter = new VelocitySelecter(keyboardCenterPanel.keyboard.velocityModel));
- add(keySelecter = new KeySignatureSelecter(new Key()));
+ add(keySelecter = new KeySignatureSelecter(Key.C_MAJOR_OR_A_MINOR));
add( keyboardCenterPanel.keyboard.chordDisplay );
add( Box.createHorizontalStrut(5) );
}
import camidion.chordhelper.music.Chord;
import camidion.chordhelper.music.Key;
import camidion.chordhelper.music.MIDISpec;
-import camidion.chordhelper.music.Music;
+import camidion.chordhelper.music.Note;
/**
* Piano Keyboard class for MIDI Chord Helper
public static final Color DARK_PINK = new Color(0xFF,0x50,0x80);
/** 1オクターブあたりの幅 */
- private static float WIDTH_PER_OCTAVE = Music.SEMITONES_PER_OCTAVE * 10;
+ private static float WIDTH_PER_OCTAVE = Note.SEMITONES_PER_OCTAVE * 10;
/** 白鍵のサイズ */
private Dimension whiteKeySize;
/** 黒鍵のサイズ */
int i = noteNumber - getChromaticOffset();
if( i < 0 ) {
octaveRangeModel.setValue(
- octaveRangeModel.getValue() - (-i)/Music.SEMITONES_PER_OCTAVE - 1
+ octaveRangeModel.getValue() - (-i)/Note.SEMITONES_PER_OCTAVE - 1
);
return true;
}
if( i >= keys.length ) {
octaveRangeModel.setValue(
- octaveRangeModel.getValue() + (i-keys.length)/Music.SEMITONES_PER_OCTAVE + 1
+ octaveRangeModel.getValue() + (i-keys.length)/Note.SEMITONES_PER_OCTAVE + 1
);
return true;
}
}
int getMaxSelectable() { return maxSelectable; }
public int getChromaticOffset() {
- return octaveRangeModel.getValue() * Music.SEMITONES_PER_OCTAVE ;
+ return octaveRangeModel.getValue() * Note.SEMITONES_PER_OCTAVE ;
}
public int getOctaves() { return octaveSizeModel.getValue(); }
private int getPerferredOctaves() {
default: break;
}
keyPoint.x = whiteKeySize.width * (
- i / Music.SEMITONES_PER_OCTAVE * 7 + (i12+(is_CDE?1:2))/2
+ i / Note.SEMITONES_PER_OCTAVE * 7 + (i12+(is_CDE?1:2))/2
);
- if( Music.isOnScale(i12,0) ) {
+ if( Key.C_MAJOR_OR_A_MINOR.isOnScale(i12) ) {
k = new PianoKey( keyPoint, whiteKeySize, indicatorSize );
k.isBlack = false;
vWhiteKeys.add(k);