import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
+import java.nio.charset.Charset;
import java.util.Arrays;
import javax.sound.midi.InvalidMidiDataException;
import camidion.chordhelper.midieditor.TimeSignatureSelecter;
import camidion.chordhelper.music.Chord;
import camidion.chordhelper.music.Key;
+import camidion.chordhelper.music.MIDISpec;
import camidion.chordhelper.music.Range;
import camidion.chordhelper.pianokeyboard.MidiKeyboardPanel;
import camidion.chordhelper.pianokeyboard.PianoKeyboardAdapter;
public int addRandomSongToPlaylist(int measureLength) throws InvalidMidiDataException {
NewSequenceDialog d = midiEditor.newSequenceDialog;
d.setRandomChordProgression(measureLength);
- int index = playlistModel.play(d.getMidiSequence());
+ int index = playlistModel.play(d.getMidiSequence(), d.getSelectedCharset());
midiEditor.playlistTable.getSelectionModel().setSelectionInterval(index, index);
return index;
}
URL url = (new URI(midiFileUrl)).toURL();
String filename = url.getFile().replaceFirst("^.*/","");
Sequence sequence = MidiSystem.getSequence(url);
- int index = playlistModel.add(sequence, filename);
+ Charset charset = MIDISpec.getCharsetOf(sequence);
+ if( charset == null ) charset = Charset.defaultCharset();
+ int index = playlistModel.add(sequence, charset, filename);
midiEditor.playlistTable.getSelectionModel().setSelectionInterval(index, index);
return index;
} catch( URISyntaxException|IOException|InvalidMidiDataException e ) {
*/
public static class VersionInfo {
public static final String NAME = "MIDI Chord Helper";
- public static final String VERSION = "Ver.20170517.1";
+ public static final String VERSION = "Ver.20170518.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/";
break;
}
});
- // シーケンサーの再生時間位置、またはシーケンサーにロード中のシーケンスが変更されたときに呼び出されるリスナーを登録
+ // 再生時間位置の移動、シーケンス名の変更、またはシーケンスの入れ替えが発生したときに呼び出されるリスナーを登録
JLabel songTitleLabel = new JLabel();
sequencerModel.addChangeListener(e->{
Sequencer sequencer = sequencerModel.getSequencer();
listenerList.remove(ChangeListener.class, listener);
}
/**
- * 秒位置が変わったことをリスナーに通知します。
- * <p>登録中のすべての {@link ChangeListener} について
+ * 登録中のすべての {@link ChangeListener} の
* {@link ChangeListener#stateChanged(ChangeEvent)}
- * ã\82\92å\91¼ã\81³å\87ºã\81\99ã\81\93ã\81¨ã\81«ã\82\88ã\81£ã\81¦ç\8a¶æ\85\8bã\81®å¤\89å\8c\96ã\82\92é\80\9aç\9f¥ã\81\97ã\81¾ã\81\99ã\80\82
+ * を呼び出します。
* </p>
+ * <p>次のような状態変更を通知したいときに呼び出します。
+ * </p>
+ * <ul>
+ * <li>秒位置の移動</li>
+ * <li>ロードされているシーケンス名の変更</li>
+ * <li>ロードされているシーケンスの入れ替え</li>
+ * </ul>
*/
public void fireStateChanged() {
Object[] listeners = listenerList.getListenerList();
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.Charset;
import java.util.Base64;
import java.util.regex.Pattern;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiSystem;
+import javax.sound.midi.Sequence;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import camidion.chordhelper.ButtonIcon;
import camidion.chordhelper.ChordHelperApplet;
+import camidion.chordhelper.music.MIDISpec;
/**
* Base64テキスト入力ダイアログ
return -1;
}
try (InputStream in = new ByteArrayInputStream(midiData)) {
- int index = playlistTable.getModel().add(MidiSystem.getSequence(in), null);
+ Sequence sequence = MidiSystem.getSequence(in);
+ Charset charset = MIDISpec.getCharsetOf(sequence);
+ if( charset == null ) charset = Charset.defaultCharset();
+ int index = playlistTable.getModel().add(sequence, charset, null);
playlistTable.getSelectionModel().setSelectionInterval(index, index);
return index;
} catch( IOException|InvalidMidiDataException e ) {
--- /dev/null
+package camidion.chordhelper.midieditor;
+
+import java.nio.charset.Charset;
+
+import javax.swing.JComboBox;
+
+public class CharsetComboBox extends JComboBox<Charset> {
+ {
+ Charset.availableCharsets().values().stream().forEach(v->addItem(v));
+ setSelectedItem(Charset.defaultCharset());
+ }
+ public Charset getSelectedCharset() {
+ return (Charset)getSelectedItem();
+ }
+}
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Vector;
"Key: C\nC G/B | Am Em/G | F C/E | Dm7 G7 C % | F G7 | Csus4 C\n";
private JTextArea chordText = new JTextArea(INITIAL_CHORD_STRING, 18, 30);
private JTextField seqNameText = new JTextField();
+ private CharsetComboBox charsetSelecter = new CharsetComboBox();
private JComboBox<Integer> ppqComboBox = new JComboBox<Integer>(PPQList);
private TimeSignatureSelecter timesigSelecter = new TimeSignatureSelecter();
private TempoSelecter tempoSelecter = new TempoSelecter();
@Override
public void actionPerformed(ActionEvent event) {
try {
- int index = playlistTable.play(getMidiSequence());
+ int index = playlistTable.play(getMidiSequence(), getSelectedCharset());
playlistTable.getModel().getSequenceModelList().get(index).setModified(true);
} catch (Exception ex) {
JOptionPane.showMessageDialog(
NewSequenceDialog.this, ex,
ChordHelperApplet.VersionInfo.NAME, JOptionPane.ERROR_MESSAGE);
+ ex.printStackTrace();
}
setVisible(false);
}
};
+ public Charset getSelectedCharset() {
+ return charsetSelecter.getSelectedCharset();
+ }
/**
* 新しいMIDIシーケンスを生成するダイアログを構築します。
* @param playlist シーケンス追加先プレイリスト
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
add(new JLabel("Sequence name:"));
add(seqNameText);
+ add(new JLabel("Character set:"));
+ add(charsetSelecter);
}});
add(new JPanel() {{
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
measureSelecter.getStartMeasurePosition(),
measureSelecter.getEndMeasurePosition(),
firstTrackSpec,
- trackSpecPanel.getTrackSpecs()
+ trackSpecPanel.getTrackSpecs(),
+ charsetSelecter.getSelectedCharset()
);
}
/**
import javax.swing.DefaultCellEditor;
import javax.swing.Icon;
import javax.swing.JButton;
-import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import camidion.chordhelper.ChordHelperApplet;
import camidion.chordhelper.mididevice.MidiSequencerModel;
+import camidion.chordhelper.music.MIDISpec;
/**
* プレイリストビュー(シーケンスリスト)
//
// 文字コード選択をプルダウンにする
getColumnModel().getColumn(PlaylistTableModel.Column.CHARSET.ordinal())
- .setCellEditor(new DefaultCellEditor(new JComboBox<Charset>() {{
- Charset.availableCharsets().values().stream().forEach(v->addItem(v));
- }}));
+ .setCellEditor(new DefaultCellEditor(new CharsetComboBox()));
setAutoCreateColumnsFromModel(false);
//
// Base64画面を開くアクションの生成
while(itr.hasNext()) {
File file = itr.next();
try (FileInputStream in = new FileInputStream(file)) {
- int lastIndex = ((PlaylistTableModel)dataModel).add(MidiSystem.getSequence(in), file.getName());
+ Sequence sequence = MidiSystem.getSequence(in);
+ Charset charset = MIDISpec.getCharsetOf(sequence);
+ if( charset == null ) charset = Charset.defaultCharset();
+ int lastIndex = ((PlaylistTableModel)dataModel).add(sequence, charset, file.getName());
if( firstIndex < 0 ) firstIndex = lastIndex;
} catch(IOException|InvalidMidiDataException e) {
String message = "Could not open as MIDI file "+file+"\n"+e;
/**
* 指定されたシーケンスを追加して再生します。
* @param sequence 再生するシーケンス
+ * @param charset 文字コード
* @return 追加されたシーケンスのインデックス(先頭が 0)
* @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
* @throws IllegalStateException MIDIシーケンサデバイスが閉じている場合
*/
- public int play(Sequence sequence) throws InvalidMidiDataException {
- int index = getModel().play(sequence);
+ public int play(Sequence sequence, Charset charset) throws InvalidMidiDataException {
+ int index = getModel().play(sequence, charset);
selectionModel.setSelectionInterval(index, index);
return index;
}
/**
* 空のトラックリストモデル
*/
- public final SequenceTrackListTableModel emptyTrackListTableModel = new SequenceTrackListTableModel(this, null, null);
+ public final SequenceTrackListTableModel emptyTrackListTableModel = new SequenceTrackListTableModel(this, null, null, null);
/**
* 空のイベントリストモデル
*/
/**
* MIDIシーケンスを追加します。
* @param sequence MIDIシーケンス(nullの場合、シーケンスを自動生成して追加)
+ * @param charset MIDIシーケンス内のテキスト文字コード
* @param filename ファイル名(nullの場合、ファイル名なし)
* @return 追加されたシーケンスのインデックス(先頭が 0)
*/
- public int add(Sequence sequence, String filename) {
- if( sequence == null ) sequence = (new ChordProgression()).toMidiSequence();
- sequenceModelList.add(new SequenceTrackListTableModel(this, sequence, filename));
+ public int add(Sequence sequence, Charset charset, String filename) {
+ if( sequence == null ) {
+ sequence = (new ChordProgression()).toMidiSequence(charset);
+ }
+ sequenceModelList.add(new SequenceTrackListTableModel(this, sequence, charset, filename));
int lastIndex = sequenceModelList.size() - 1;
fireTableRowsInserted(lastIndex, lastIndex);
return lastIndex;
/**
* 指定されたMIDIシーケンスをこのプレイリストに追加し、再生されていなければ追加した曲から再生します。
* @param sequence MIDIシーケンス
+ * @param charset 文字コード
* @return 追加されたシーケンスのインデックス(先頭が 0)
* @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
* @throws IllegalStateException MIDIシーケンサデバイスが閉じている場合
*/
- public int play(Sequence sequence) throws InvalidMidiDataException {
- int lastIndex = add(sequence,"");
+ public int play(Sequence sequence, Charset charset) throws InvalidMidiDataException {
+ int lastIndex = add(sequence, charset, "");
if( ! sequencerModel.getSequencer().isRunning() ) play(lastIndex);
return lastIndex;
}
* MIDIシーケンスとファイル名から {@link SequenceTrackListTableModel} を構築します。
* @param sequenceListTableModel 親のプレイリスト
* @param sequence MIDIシーケンス
+ * @param charset MIDIシーケンスのテキスト文字コード
* @param filename ファイル名
*/
public SequenceTrackListTableModel(
PlaylistTableModel sequenceListTableModel,
Sequence sequence,
+ Charset charset,
String filename
) {
this.sequenceListTableModel = sequenceListTableModel;
+ this.charset = charset;
setSequence(sequence);
setFilename(filename);
}
//
// トラックリストを再構築
Track tracks[] = sequence.getTracks();
- for(Track track : tracks) {
- trackModelList.add(new MidiEventTableModel(this, track));
- }
- // 文字コードの判定
- Charset cs = MIDISpec.getCharsetOf(sequence);
- charset = cs==null ? Charset.defaultCharset() : cs;
+ for(Track track : tracks) trackModelList.add(new MidiEventTableModel(this, track));
//
// トラックが挿入されたことを通知
fireTableRowsInserted(0, tracks.length-1);
*/
@Override
public String toString() {
+ if( sequence == null ) return "";
byte b[] = MIDISpec.getNameBytesOf(sequence);
return b == null ? "" : new String(b, charset);
}
* @return 成功したらtrue
*/
public boolean setName(String name) {
- if( name.equals(toString()) ) return false;
+ if( name.equals(toString()) || sequence == null ) return false;
if( ! MIDISpec.setNameBytesOf(sequence, name.getBytes(charset)) ) return false;
setModified(true);
fireTableDataChanged();
+ if( isOnSequencer() )
+ sequenceListTableModel.getSequencerModel().fireStateChanged();
return true;
}
/**
* @return トラックモデル(見つからない場合null)
*/
public MidiEventTableModel getSelectedTrackModel(ListSelectionModel selectionModel) {
- if( selectionModel.isSelectionEmpty() ) return null;
+ if( sequence == null || selectionModel.isSelectionEmpty() ) return null;
Track tracks[] = sequence.getTracks();
if( tracks.length == 0 ) return null;
Track t = tracks[selectionModel.getMinSelectionIndex()];
* @return トラックのインデックス(先頭 0、トラックが見つからない場合 -1)
*/
public int indexOf(Track track) {
- Track tracks[] = sequence.getTracks();
- for( int i=0; i<tracks.length; i++ ) if( tracks[i] == track ) return i;
+ if( sequence != null ) {
+ Track tracks[] = sequence.getTracks();
+ for( int i=0; i<tracks.length; i++ ) if( tracks[i] == track ) return i;
+ }
return -1;
}
/**
package camidion.chordhelper.music;
+import java.nio.charset.Charset;
+
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
this(ch,name,programNumber);
this.velocity = velocity;
}
- public Track createTrack( Sequence seq, FirstTrackSpec firstTrackSpec ) {
- Track track = super.createTrack( seq, firstTrackSpec );
+ public Track createTrack( Sequence seq, FirstTrackSpec firstTrackSpec, Charset charset ) {
+ Track track = super.createTrack( seq, firstTrackSpec, charset );
if( programNumber >= 0 ) addProgram( programNumber, 0 );
return track;
}
package camidion.chordhelper.music;
+import java.nio.charset.Charset;
+
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
* @param firstTrackSpec 最初のトラック仕様
* @return 生成したトラック
*/
- public Track createTrack( Sequence seq, FirstTrackSpec firstTrackSpec ) {
+ public Track createTrack( Sequence seq, FirstTrackSpec firstTrackSpec, Charset charset ) {
this.firstTrackSpec = firstTrackSpec;
track = (sequence = seq).createTrack();
- if( name != null ) addStringTo( 0x03, name, 0 );
+ if( name != null ) addStringTo( 0x03, name, charset, 0 );
minNoteTicks = (long)( seq.getResolution() >> 2 );
return track;
}
* @return {@link Track#add(MidiEvent)} と同じ
*/
public boolean addMetaEventTo( int type, byte data[], long tickPos ) {
- MetaMessage meta_msg = new MetaMessage();
+ MetaMessage metaMessage = new MetaMessage();
try {
- meta_msg.setMessage( type, data, data.length );
+ metaMessage.setMessage( type, data, data.length );
} catch( InvalidMidiDataException ex ) {
ex.printStackTrace();
return false;
}
- return track.add(new MidiEvent(meta_msg, tickPos));
+ return track.add(new MidiEvent(metaMessage, tickPos));
}
/**
* 文字列をメタイベントとして追加します。
* @param tickPos tick位置
* @return {@link #addMetaEventTo(int, byte[], long)} と同じ
*/
- public boolean addStringTo( int type, String str, long tickPos ) {
+ public boolean addStringTo( int type, String str, Charset charset, long tickPos ) {
if( str == null ) str = "";
- return addMetaEventTo( type, str.getBytes(), tickPos );
+ return addMetaEventTo(type, str.getBytes(charset), tickPos);
}
- public boolean addStringTo( int type, ChordProgression.ChordStroke cs ) {
- return addStringTo(type, cs.chord.toString(), cs.tickRange.startTickPos);
+ public boolean addStringTo( int type, ChordProgression.ChordStroke cs, Charset charset ) {
+ return addStringTo(type, cs.chord.toString(), charset, cs.tickRange.startTickPos);
}
- public boolean addStringTo( int type, ChordProgression.Lyrics lyrics ) {
- return addStringTo(type, lyrics.text, lyrics.startTickPos);
+ public boolean addStringTo( int type, ChordProgression.Lyrics lyrics, Charset charset ) {
+ return addStringTo(type, lyrics.text, charset, lyrics.startTickPos);
}
public boolean addEOT( long tickPos ) {
return addMetaEventTo( 0x2F, new byte[0], tickPos );
}
- public void setChordSymbolText( ChordProgression cp ) {
- cp.setChordSymbolTextTo( this );
+ public void setChordSymbolText(ChordProgression cp, Charset charset) {
+ cp.setChordSymbolTextTo(this, charset);
}
public boolean addSysEx(byte[] data, long tickPos) {
SysexMessage msg = new SysexMessage();
package camidion.chordhelper.music;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
}
}
// コード文字列の書き込み
- public void setChordSymbolTextTo( AbstractTrackSpec ts ) {
+ public void setChordSymbolTextTo( AbstractTrackSpec ts, Charset charset ) {
for( Line line : lines ) {
for( Measure measure : line ) {
if( measure.ticks_per_beat == null ) continue;
for( Object element : measure ) {
if( element instanceof ChordStroke ) {
- ts.addStringTo( 0x01, (ChordStroke)element );
+ ts.addStringTo( 0x01, (ChordStroke)element, charset );
}
}
}
}
}
// 歌詞の書き込み
- public void setLyricsTo( AbstractTrackSpec ts ) {
+ public void setLyricsTo( AbstractTrackSpec ts, Charset charset ) {
for( Line line : lines ) {
for( Measure measure : line ) {
if( measure.ticks_per_beat == null ) continue;
for( Object element : measure ) {
if( element instanceof Lyrics ) {
- ts.addStringTo( 0x05, (Lyrics)element );
+ ts.addStringTo( 0x05, (Lyrics)element, charset );
}
}
}
}
/**
* コード進行をもとに MIDI シーケンスを生成します。
+ * @param charset 文字コード
* @return MIDIシーケンス
*/
- public Sequence toMidiSequence() { return toMidiSequence(48); }
+ public Sequence toMidiSequence(Charset charset) {
+ return toMidiSequence(48, charset);
+ }
/**
- * 指定のタイミング解像度で、
- * コード進行をもとに MIDI シーケンスを生成します。
+ * 指定のタイミング解像度、文字コードで、コード進行をもとに MIDI シーケンスを生成します。
+ * @param ppq 分解能(pulse per quarter)
+ * @param charset 文字コード
* @return MIDIシーケンス
*/
- public Sequence toMidiSequence(int ppq) {
+ public Sequence toMidiSequence(int ppq, Charset charset) {
//
// PPQ = Pulse Per Quarter (TPQN = Tick Per Quearter Note)
//
- return toMidiSequence( ppq, 0, 0, null, null );
+ return toMidiSequence( ppq, 0, 0, null, null, charset );
}
/**
* 小節数、トラック仕様、コード進行をもとに MIDI シーケンスを生成します。
* @param endMeasure 終了小節位置
* @param firstTrack 最初のトラックの仕様
* @param trackSpecs 残りのトラックの仕様
+ * @param charset 文字コード
* @return MIDIシーケンス
*/
public Sequence toMidiSequence(
int ppq, int startMeasure, int endMeasure,
- FirstTrackSpec firstTrack, Vector<AbstractNoteTrackSpec> trackSpecs
+ FirstTrackSpec firstTrack,
+ Vector<AbstractNoteTrackSpec> trackSpecs,
+ Charset charset
) {
Sequence seq;
try {
// マスタートラックの生成
if( firstTrack == null ) firstTrack = new FirstTrackSpec();
firstTrack.key = this.key;
- firstTrack.createTrack( seq, startMeasure, endMeasure );
+ firstTrack.createTrack(seq, charset, startMeasure, endMeasure);
//
// 中身がなければここで終了
if( lines == null || trackSpecs == null ) return seq;
setTickPositions(firstTrack);
//
// コードのテキストと歌詞を書き込む
- setChordSymbolTextTo(firstTrack);
- setLyricsTo(firstTrack);
+ setChordSymbolTextTo(firstTrack, charset);
+ setLyricsTo(firstTrack, charset);
//
// 残りのトラックを生成
for( AbstractNoteTrackSpec ts : trackSpecs ) {
- ts.createTrack(seq, firstTrack);
+ ts.createTrack(seq, firstTrack, charset);
if( ts instanceof DrumTrackSpec ) {
((DrumTrackSpec)ts).addDrums(this);
}
else {
- ((MelodyTrackSpec)ts).addChords(this);
+ ((MelodyTrackSpec)ts).addChords(this, charset);
}
}
return seq;
package camidion.chordhelper.music;
+import java.nio.charset.Charset;
+
import javax.sound.midi.Sequence;
import javax.sound.midi.Track;
if( tempoData != null ) this.tempoData = tempoData;
if( timesigData != null ) this.timesigData = timesigData;
}
- public FirstTrackSpec(String name, byte[] tempoData, byte[] timesigData, Key key) {
- this(name,tempoData,timesigData);
- this.key = key;
- }
- public Track createTrack(Sequence seq) {
- return createTrack( seq, 0, 0 );
+ public Track createTrack(Sequence seq, Charset charset) {
+ return createTrack(seq, charset, 0, 0);
}
- public Track createTrack(Sequence seq, int startMeasurePos, int endMeasurePos) {
+ public Track createTrack(Sequence seq, Charset charset, int startMeasurePos, int endMeasurePos) {
preMeasures = startMeasurePos - 1;
- Track track = super.createTrack( seq, this );
+ Track track = super.createTrack(seq, this, charset);
if( tempoData == null ) tempoData = DEFAULT_TEMPO_DATA;
addTempo(tempoData, 0);
if( timesigData == null ) timesigData = DEFAULT_TIMESIG_DATA;
return Arrays.stream(sequence.getTracks()).anyMatch(t->setNameBytesOf(t,name));
}
/**
- * シーケンスの名前や歌詞など、メタイベントのテキストをもとに文字コードを判定します。
+ * 指定されたMIDIシーケンスからメタイベントのテキスト(名前や歌詞など)を検索し、その文字コードを判定します。
* 判定できなかった場合はnullを返します。
* @param sequence MIDIシーケンス
* @return 文字コード判定結果(またはnull)
package camidion.chordhelper.music;
+import java.nio.charset.Charset;
+
/**
* メロディトラック仕様
*/
/**
* コードの追加
* @param cp コード進行
+ * @param charset 文字コード
*/
- public void addChords( ChordProgression cp ) {
+ public void addChords( ChordProgression cp, Charset charset ) {
int mask;
long tick;
long startTickPos;
// 決定された音符を追加
addNote(startTickPos, tick + minNoteTicks, noteNumber, velocity);
// 歌詞をテキストとして追加
- addStringTo(0x05, MIDISpec.nsx39LyricElements[index], startTickPos);
+ addStringTo(0x05, MIDISpec.nsx39LyricElements[index], charset, startTickPos);
}
else {
// 決定された音符を追加