setText("MIDI note No." + noteNumber + " : " + pn);
}
else {
- String ns = NoteSymbol.noteNoToSymbol(noteNumber);
+ String ns = NoteSymbol.noteNumberToSymbol(noteNumber);
double f = Music.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.20161127.1";
+ public static final String VERSION = "Ver.20161205.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/";
new DefaultComboBoxModel<String>() {
{
for( int i = 0; i<=0x7F; i++ ) addElement(
- String.format(
- "0x%02X : %d : %s", i, i, NoteSymbol.noteNoToSymbol(i)
- )
+ String.format("0x%02X : %d : %s", i, i, NoteSymbol.noteNumberToSymbol(i))
);
// Center note C
setSelectedItem(getElementAt(60));
/** 長13度(長6度の1オクターブ上) */
THIRTEENTH(21, OffsetIndex.THIRTEENTH);
- private Interval(int offset, OffsetIndex offsetIndex) {
- this.offset = offset;
+ private Interval(int chromaticOffset, OffsetIndex offsetIndex) {
+ this.chromaticOffset = chromaticOffset;
this.offsetIndex = offsetIndex;
}
private OffsetIndex offsetIndex;
- private int offset;
+ private int chromaticOffset;
/**
* 半音差を返します。
* @return 半音差
*/
- public int getChromaticOffset() { return offset; }
+ public int getChromaticOffset() { return chromaticOffset; }
/**
* 対応するインデックスを返します。
* @return 対応するインデックス
* Swing の {@link JLabel#setText(String)} は HTML で指定できるので、
* 文字の大きさに変化をつけることができます。
*
- * @param color_name 色のHTML表現(色名または #RRGGBB 形式)
+ * @param colorName 色のHTML表現(色名または #RRGGBB 形式)
* @return コード名のHTML
*/
- public String toHtmlString(String color_name) {
- String small_tag = "<span style=\"font-size: 120%\">";
- String end_of_small_tag = "</span>";
+ public String toHtmlString(String colorName) {
+ String span = "<span style=\"font-size: 120%\">";
+ String endSpan = "</span>";
String root = rootNoteSymbol.toString();
- String formatted_root = (root.length() == 1) ? root + small_tag :
- root.replace("#",small_tag+"<sup>#</sup>").
- replace("b",small_tag+"<sup>b</sup>").
- replace("x",small_tag+"<sup>x</sup>");
- String formatted_bass = "";
+ String formattedRoot = (root.length() == 1) ? root + span :
+ root.replace("#",span+"<sup>#</sup>").
+ replace("b",span+"<sup>b</sup>").
+ replace("x",span+"<sup>x</sup>");
+ String formattedBass = "";
if( ! rootNoteSymbol.equals(bassNoteSymbol) ) {
String bass = bassNoteSymbol.toString();
- formatted_bass = (bass.length() == 1) ? bass + small_tag :
- bass.replace("#",small_tag+"<sup>#</sup>").
- replace("b",small_tag+"<sup>b</sup>").
- replace("x",small_tag+"<sup>x</sup>");
- formatted_bass = "/" + formatted_bass + end_of_small_tag;
+ formattedBass = (bass.length() == 1) ? bass + span :
+ bass.replace("#",span+"<sup>#</sup>").
+ replace("b",span+"<sup>b</sup>").
+ replace("x",span+"<sup>x</sup>");
+ formattedBass = "/" + formattedBass + endSpan;
}
String suffix = symbolSuffix().
replace("-5","<sup>-5</sup>").
replace("+5","<sup>+5</sup>");
return
"<html>" +
- "<span style=\"color: " + color_name + "; font-size: 170% ; white-space: nowrap ;\">" +
- formatted_root + suffix + end_of_small_tag + formatted_bass +
+ "<span style=\"color: " + colorName + "; font-size: 170% ; white-space: nowrap ;\">" +
+ formattedRoot + suffix + endSpan + formattedBass +
"</span>" +
"</html>" ;
}
* @return コードの説明(英語)
*/
public String toName() {
- String chord_name = rootNoteSymbol.toStringIn(NoteSymbolLanguage.NAME) + nameSuffix() ;
+ String name = rootNoteSymbol.toStringIn(NoteSymbolLanguage.NAME) + nameSuffix() ;
if( ! rootNoteSymbol.equals(bassNoteSymbol) ) {
- chord_name += " on " + bassNoteSymbol.toStringIn(NoteSymbolLanguage.NAME);
+ name += " on " + bassNoteSymbol.toStringIn(NoteSymbolLanguage.NAME);
}
- return chord_name;
+ return name;
}
/**
* コードネームの音名を除いた部分(サフィックス)を組み立てて返します。
/**
* 調号が空のキー(ハ長調またはイ短調)を構築します。
*/
- public Key() { setKey(0, MAJOR_OR_MINOR); }
+ public Key() { }
/**
* 指定の五度圏インデックス値を持つ調を、
* メジャーとマイナーを指定せずに構築します。
/**
* C、Am のような文字列から調を構築します。
* @param keySymbol 調を表す文字列
- * @throw IllegalArgumentException 調を表す文字列が不正の場合
+ * @throw IllegalArgumentException 引数が空文字列の場合、または音名で始まっていない場合
*/
public Key(String keySymbol) throws IllegalArgumentException {
boolean isMinor = keySymbol.matches(".*m");
- setKey((new NoteSymbol(keySymbol)).toCo5(isMinor), isMinor);
+ setKey((new NoteSymbol(keySymbol)).toCo5ForKey(isMinor), isMinor);
}
/**
* 指定されたコードと同名の調を構築します。
*/
public Key(Chord chord) {
boolean isMinor = chord.isSet(Chord.Interval.MINOR);
- setKey(chord.rootNoteSymbol().toCo5(isMinor), isMinor);
+ setKey(chord.rootNoteSymbol().toCo5ForKey(isMinor), isMinor);
}
@Override
public Key clone() { return new Key(co5, majorMinor); }
* 相対ドの音階を返します。
* @return 相対ドの音階(0~11)
*/
- public int relativeDo() { return NoteSymbol.toNoteNumber(co5); }
+ public int relativeDo() { return NoteSymbol.majorCo5ToNoteNumber(co5); }
/**
* この調のルート音を返します。
* メジャーキーの場合は相対ド、
str += getPercussionName(data1);
}
else {
- str += NoteSymbol.noteNoToSymbol(data1);
+ str += NoteSymbol.noteNumberToSymbol(data1);
}
str +="] Velocity=" + data2;
break;
public class NoteSymbol implements Cloneable {
private static final int INDEX_OF_A = NoteSymbolLanguage.SYMBOL.indexOf("A");
private static final int INDEX_OF_C = NoteSymbolLanguage.SYMBOL.indexOf("C");
- /**
- * メジャーキー基準の五度圏インデックス値
- */
+ /** メジャーキー基準の五度圏インデックス値 */
private int majorCo5;
- /**
- * ノート番号(0~11)
- */
+ /** ノート番号(0~11) */
private int noteNumber;
- /**
- * 音名 C(ハ音)を構築します。
- */
- public NoteSymbol() {
- }
+ /** 音名 C(ハ音)を構築します。 */
+ public NoteSymbol() { }
/**
* 五度圏インデックス値(メジャーキー基準)から音名を構築します。
* @param majorCo5 五度圏インデックス値
*/
public NoteSymbol(int majorCo5) {
- noteNumber = toNoteNumber(this.majorCo5 = majorCo5);
+ noteNumber = majorCo5ToNoteNumber(this.majorCo5 = majorCo5);
}
/**
* 音名を文字列から構築します。
* @param noteSymbol 音名の文字列
- * @throws IllegalArgumentException
- * 指定の音名が空、またはA~Gの範囲を外れている場合
+ * @throws IllegalArgumentException 引数が空文字列の場合、または音名で始まっていない場合
*/
public NoteSymbol(String noteSymbol) {
this(NoteSymbolLanguage.SYMBOL.indexOf(noteSymbol) - INDEX_OF_C);
*/
public int toCo5() { return majorCo5; }
/**
- * メジャーかマイナーかを指定し、対応する五度圏インデックス値を返します。
- * <p>マイナーの場合、
- * メジャー基準の五度圏インデックス値から3が差し引かれます。
- * 例えば、C major の場合は調号が0個なのに対し、
- * C minor のときは調号の♭が3個に増えますが、
+ * メジャー/マイナーキーに対応する五度圏インデックス値を返します。
+ * <p>マイナーの場合、メジャー基準の五度圏インデックス値から3が差し引かれます。
+ * 例えば、C major の場合は調号が0個なのに対し、C minor のときは調号の♭が3個に増えますが、
* 3を差し引くことによってこのズレが補正されます。
* </p>
*
* @param isMinor マイナーのときtrue
* @return 五度圏インデックス値
*/
- public int toCo5(boolean isMinor) { return isMinor ? majorCo5 - 3 : majorCo5; }
+ public int toCo5ForKey(boolean isMinor) { return isMinor ? majorCo5 - 3 : majorCo5; }
/**
* ノート番号(0~11)を返します。
* <p>これはMIDIノート番号からオクターブ情報を抜いた値と同じです。
* @return 文字列表現
*/
public String toStringIn(NoteSymbolLanguage language) {
- return toStringIn(language, false);
+ return language.stringOf(majorCo5 + INDEX_OF_C);
}
/**
- * 指定した言語モードとメジャーマイナー種別における文字列表現を返します。
- * <p>マイナーが指定された場合、五度圏インデックス値を3つ進めた音階として返します。
- * 例えば、{@link #toCo5()} の戻り値が0の場合、
- * メジャーが指定されていれば C を返しますが、
- * マイナーが指定されると A を返します。
- * これにより、同じ五度圏インデックス値0で C と Am の両方のルート音を導出できます。
- * </p>
+ * 指定した言語モードにおける、マイナーキー用の文字列表現を返します。
+ * マイナーキー用の文字列では、例えば五度圏インデックスが0の場合、Cの代わりにAを返します。
* @param language 言語モード
- * @param isMinor マイナーのときtrue
* @return 文字列表現
*/
- public String toStringIn(NoteSymbolLanguage language, boolean isMinor) {
- int index = majorCo5 + (isMinor ? INDEX_OF_A : INDEX_OF_C);
- if( index < 0 || index >= 35 ) {
- // インデックスOB発生の恐れがある場合
- // 調号 5b ~ 6# の範囲に収まるルート音となるような異名同音(enharmonic)に置き換える
- index = Music.mod12(index); // returns 0(Fbb) ... 7(Fb) 8(Cb) 9(Gb) 10(Db) 11(Ab)
- if( isMinor ) {
- // 18(Am)
- if( index == 0 )
- index += Music.SEMITONES_PER_OCTAVE * 2; // 0(Fbbm) -> 24(D#m 5#)
- else
- index += Music.SEMITONES_PER_OCTAVE; // 1(Cbbm) -> 13(Bbm 5b)
- }
- else {
- // 15(C)
- if( index < 10 ) // 0(Fbb) -> 12(Eb 3b), 9(Gb) -> 21(F# 6#)
- index += Music.SEMITONES_PER_OCTAVE;
- }
- }
- return language.stringOf(index);
+ public String toMinorKeyRootStringIn(NoteSymbolLanguage language) {
+ return language.stringOf(majorCo5 + INDEX_OF_A);
}
/**
* 指定の最大文字数の範囲で、MIDIノート番号が示す音名を返します。
* ダブルシャープ、ダブルフラットを使った表現は返しません。
* </p>
* @param noteNumber MIDIノート番号
- * @param maxChars 最大文字数
+ * @param maxChars 最大文字数(最大がない場合は負数を指定)
* @return MIDIノート番号が示す音名
*/
public static String noteNumberToSymbol(int noteNumber, int maxChars) {
if( co5 == 11 ) co5 -= Music.SEMITONES_PER_OCTAVE; // E# -> F
if( co5 < 6 ) return (new NoteSymbol(co5)).toString(); // F C G D A E B
- if( maxChars >= "F# / Gb".length() ) return
+ if( maxChars < 0 || maxChars >= "F# / Gb".length() ) return
(new NoteSymbol(co5)).toString() + " / " +
(new NoteSymbol(co5 - Music.SEMITONES_PER_OCTAVE)).toString();
return (new NoteSymbol(co5)).toString(); // C# Eb F# Ab Bb
}
/**
- * 最大256文字の範囲で、MIDIノート番号が示す音名を返します。
- * <p>内部的には
- * {@link #noteNumberToSymbol(int, int)} を呼び出しているだけです。
+ * MIDIノート番号が示す音名を返します。
+ * 内部的には{@link #noteNumberToSymbol(int,int)}を呼び出しているだけです。
* </p>
- * @param noteNo MIDIノート番号
+ * @param noteNumber MIDIノート番号
* @return MIDIノート番号が示す音名
*/
- public static String noteNoToSymbol(int noteNo) {
- return noteNumberToSymbol(noteNo, 256);
+ public static String noteNumberToSymbol(int noteNumber) {
+ return noteNumberToSymbol(noteNumber, -1);
}
/**
* 指定された五度圏インデックス値(メジャーキー基準)を
* </p>
* @return ノート番号(0~11)
*/
- public static int toNoteNumber(int majorCo5) {
+ public static int majorCo5ToNoteNumber(int majorCo5) {
return Music.mod12(Music.reverseCo5(majorCo5));
}
}
\ No newline at end of file
import java.util.Objects;
/**
- * 音階シンボルの言語モードによる違いを定義します。
- * <p>各音階にはインデックス値が割り当てられます。
- * このインデックス値は、五度圏順で並んだ変化記号のない7音階(FCGDAEB)に対するインデックス値
- * x (0~6) と、
- * 変化記号(ダブルフラット、フラット、なし、シャープ、ダブルシャープ)に対するインデックス値
- * y (0~4) に基づいて、次の式で計算されます。</p>
- * <pre>インデックス値 = 5y + x</pre>
- * <p>このインデックス値の範囲は下記のように 0~34 となります。</p>
+ * 音階や調を表すシンボルの言語モードによる違いを定義します。
+ * <p>音名には、下記のような五度圏順のインデックス値(0~34)が割り当てられます。
* <pre>Fbb=0, Cbb=1, .. Bb=13, F=14, C=15, .. B=20, F#=21, C#=22, .. B#=27, Fx=28, .. Bx=34</pre>
*/
public enum NoteSymbolLanguage {
/**
- * シンボル表記(Bb, F#)
+ * 記号表記(Bb, F#)
*/
SYMBOL(Arrays.asList("bb","b","","#","x"),"","m"),
/**
* 日本名表記(変ロ, 嬰ヘ)
*/
IN_JAPANESE(Arrays.asList("重変","変","","嬰","重嬰"),"長調","短調");
- /**
- * ♭や♯の表記を、半音下がる数が多いほうから順に並べたリスト
- */
- private List<String> sharpFlatList;
- /**
- * 音名を五度圏順で並べた7文字
- */
- private String notes;
- /**
- * メジャーを表す文字列
- */
- private String major;
- /**
- * マイナーを表す文字列
- */
- private String minor;
- /**
- * メジャーとマイナーを併記する場合の区切り文字
- */
- private String majorMinorDelimiter;
-
+ //
private NoteSymbolLanguage(List<String> sharpFlatList, String major, String minor) {
if( (this.sharpFlatList = sharpFlatList).contains("変") ) {
this.notes = "ヘハトニイホロ";
this.minor = minor;
}
/**
+ * ♭や♯の表記を、半音下がる数が多いほうから順に並べたリスト
+ */
+ private List<String> sharpFlatList;
+ /**
+ * 引数の先頭にある、♭や♯などの変化記号のインデックス(0~4)を返します。
+ * <p>変化記号がない場合、2 を返します。それ以外は次の値を返します。</p>
+ * <ul>
+ * <li>ダブルシャープ:4</li>
+ * <li>シャープ:3</li>
+ * <li>フラット:1</li>
+ * <li>ダブルフラット:0</li>
+ * </ul>
+ * @param s 変化記号で始まる文字列
+ * @return インデックス
+ */
+ private int sharpFlatIndexOf(String s) {
+ int index = 0;
+ for( String sharpFlat : sharpFlatList ) {
+ if( ! sharpFlat.isEmpty() && s.startsWith(sharpFlat) ) return index;
+ index++;
+ }
+ return 2;
+ }
+ /**
+ * 音名を五度圏順で並べた7文字
+ */
+ private String notes;
+ /**
* インデックス値に該当する音名を返します。
* @param index インデックス値(定義は{@link NoteSymbolLanguage}参照)
* @return 音名(例:Bb、B flat、変ロ)
- * @throws IndexOutOfBoundsException インデックスが範囲を外れている場合
+ * @throws IndexOutOfBoundsException インデックス値が範囲を外れている場合
*/
public String stringOf(int index) {
- int sharpFlatIndex = index / 7; // 0 1 2 3 4
- int noteSymbolIndex = index - sharpFlatIndex * 7; // 0 1 2 3 4 5 6
- String note = notes.substring(noteSymbolIndex, noteSymbolIndex+1);
+ int sharpFlatIndex = index / 7;
+ char note = notes.charAt(index - sharpFlatIndex * 7);
String sharpFlat = sharpFlatList.get(sharpFlatIndex);
return this == IN_JAPANESE ? sharpFlat + note : note + sharpFlat;
}
/**
- * 調の文字列表現を返します。メジャー/マイナーの区別が不明な場合、両方の表現を返します。
- * @param note 音名
- * @param majorMinor -1:マイナー 0:不明 1:メジャー
- * @return 調の文字列表現
- */
- public String keyStringOf(NoteSymbol note, int majorMinor) {
- String majorString = note.toStringIn(this, false) + major;
- if( majorMinor > 0 ) {
- return majorString;
- }
- else {
- String minorString = note.toStringIn(this, true) + minor;
- return majorMinor < 0 ? minorString : majorString + majorMinorDelimiter + minorString ;
- }
- }
- /**
- * 音名の文字列(英字のみ)に対するインデックス値を返します。
- * 音名は通常大文字ですが、小文字も認識します。
+ * 音名に対するインデックス値を返します。
+ * 音名は通常、英大文字(ABCDEFG)ですが、英小文字(abcdefg)も認識します。
+ * 日本語名(イロハニホヘト)はサポートしていません。
*
- * @param noteSymbol é\9f³å\90\8dã\81®文字列
+ * @param noteSymbol é\9f³å\90\8dã\81§å§\8bã\81¾ã\82\8b文字列
* @return インデックス値(定義は{@link NoteSymbolLanguage}参照)
- * @throws
- * UnsupportedOperationException このオブジェクトが {@link #IN_JAPANESE} の場合
- * @throws
- * NullPointerException 指定された音名がnullの場合
- * @throws
- * IllegalArgumentException 指定された音名が空、または[A~G、a~g]の範囲を外れている場合
+ * @throws UnsupportedOperationException このオブジェクトが {@link #IN_JAPANESE} の場合
+ * @throws NullPointerException 引数がnullの場合
+ * @throws IllegalArgumentException 引数が空文字列の場合、または音名で始まっていない場合
*/
public int indexOf(String noteSymbol) {
if( this == IN_JAPANESE ) throw new UnsupportedOperationException();
Objects.requireNonNull(noteSymbol,"Musical note symbol must not be null");
String trimmed = noteSymbol.trim();
if( trimmed.isEmpty() ) throw new IllegalArgumentException("Empty musical note symbol");
- char prefix = trimmed.charAt(0);
- int index = notes.indexOf(Character.toUpperCase(prefix));
- if( index < 0 ) {
- throw new IllegalArgumentException("Unknown musical note symbol ["+noteSymbol+"] not in ["+notes+"]");
- }
- String suffix = trimmed.substring(1);
- for( String sf : sharpFlatList ) {
- if( suffix.startsWith(sf) ) break; // bb が先にヒットするので b と間違える心配はない
- index += 7;
+ int noteIndex = notes.indexOf(Character.toUpperCase(trimmed.charAt(0)));
+ if( noteIndex < 0 ) throw new IllegalArgumentException(
+ "Unknown musical note symbol ["+noteSymbol+"] not in ["+notes+"]");
+ return 7 * sharpFlatIndexOf(trimmed.substring(1)) + noteIndex;
+ }
+ /**
+ * メジャーを表す文字列
+ */
+ private String major;
+ /**
+ * マイナーを表す文字列
+ */
+ private String minor;
+ /**
+ * メジャーとマイナーを併記する場合の区切り文字
+ */
+ private String majorMinorDelimiter;
+ /**
+ * 調の文字列表現を返します。メジャー/マイナーの区別が不明な場合、両方の表現を返します。
+ * @param note 音名
+ * @param majorMinor 負数:マイナー 0:不明 正数:メジャー
+ * @return 調の文字列表現
+ */
+ public String keyStringOf(NoteSymbol note, int majorMinor) {
+ String s = "";
+ if( majorMinor >= 0 ) {
+ s = note.toStringIn(this) + major;
+ if( majorMinor > 0 ) return s;
+ s += majorMinorDelimiter;
}
- return index;
+ return s + note.toMinorKeyRootStringIn(this) + minor;
}
}