import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
+import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.AccessControlException;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaMessage;
+import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.swing.Box;
public int addRandomSongToPlaylist(int measureLength) throws InvalidMidiDataException {
NewSequenceDialog d = midiEditor.newSequenceDialog;
d.setRandomChordProgression(measureLength);
- return playlistModel.addSequenceAndPlay(d.getMidiSequence());
+ return playlistModel.play(d.getMidiSequence());
}
/**
* URLで指定されたMIDIファイルをプレイリストへ追加します。
*/
public int addToPlaylist(String midiFileUrl) {
try {
- return playlistModel.addSequenceFromURL(midiFileUrl);
+ URL url = (new URI(midiFileUrl)).toURL();
+ String filename = url.getFile().replaceFirst("^.*/","");
+ Sequence sequence = MidiSystem.getSequence(url);
+ return playlistModel.add(sequence, filename);
} catch( URISyntaxException|IOException|InvalidMidiDataException e ) {
midiEditor.showWarning(e);
} catch( AccessControlException e ) {
public int addToPlaylistBase64(String base64EncodedText, String filename) {
Base64Dialog d = midiEditor.base64Dialog;
d.setBase64Data(base64EncodedText);
- try {
- return playlistModel.addSequence(d.getMIDIData(), filename);
- } catch (Exception e) {
- midiEditor.showWarning(e);
- return -1;
- }
+ return d.addToPlaylist();
}
/**
* プレイリスト上で現在選択されているMIDIシーケンスを、
* 現在シーケンサにロードされているMIDIデータを
* Base64テキストに変換した結果を返します。
* @return MIDIデータをBase64テキストに変換した結果
+ * @throws IOException MIDIデータの読み込みに失敗した場合
*/
- public String getMidiDataBase64() {
+ public String getMidiDataBase64() throws IOException {
SequenceTrackListTableModel sequenceModel = sequencerModel.getSequenceTrackListTableModel();
midiEditor.base64Dialog.setMIDIData(sequenceModel.getMIDIdata());
return midiEditor.base64Dialog.getBase64Data();
*/
public void setChordDiagramVisible(boolean isVisible) {
keyboardSplitPane.resetToPreferredSizes();
- if( ! isVisible )
- keyboardSplitPane.setDividerLocation((double)1.0);
+ if( ! isVisible ) keyboardSplitPane.setDividerLocation((double)1.0);
}
/**
* コードダイヤグラムをギターモードに変更します。
*/
public static class VersionInfo {
public static final String NAME = "MIDI Chord Helper";
- public static final String VERSION = "Ver.20170320.1";
+ public static final String VERSION = "Ver.20170324.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/";
// コードダイアグラム、コードボタン、ピアノ鍵盤のセットアップ
CapoComboBoxModel capoComboBoxModel = new CapoComboBoxModel();
chordDiagram = new ChordDiagram(capoComboBoxModel);
- chordMatrix = new ChordMatrix(capoComboBoxModel) {{
- addChordMatrixListener(new ChordMatrixListener(){
- public void keySignatureChanged() {
- Key capoKey = getKeySignatureCapo();
- keyboardPanel.keySelecter.setSelectedKey(capoKey);
- keyboardPanel.keyboardCenterPanel.keyboard.setKeySignature(capoKey);
- }
- public void chordChanged() { chordOn(); }
- });
- capoSelecter.checkbox.addItemListener(e->{
+ chordMatrix = new ChordMatrix(capoComboBoxModel) {
+ private void clearChord() {
chordOn();
keyboardPanel.keyboardCenterPanel.keyboard.chordDisplay.clear();
chordDiagram.clear();
- });
- capoSelecter.valueSelecter.addActionListener(e->{
- chordOn();
- keyboardPanel.keyboardCenterPanel.keyboard.chordDisplay.clear();
- chordDiagram.clear();
- });
- }};
+ }
+ {
+ addChordMatrixListener(new ChordMatrixListener(){
+ public void keySignatureChanged() {
+ Key capoKey = getKeySignatureCapo();
+ keyboardPanel.keySelecter.setSelectedKey(capoKey);
+ keyboardPanel.keyboardCenterPanel.keyboard.setKeySignature(capoKey);
+ }
+ public void chordChanged() { chordOn(); }
+ });
+ capoSelecter.checkbox.addItemListener(e->clearChord());
+ capoSelecter.valueSelecter.addActionListener(e->clearChord());
+ }
+ };
keysigLabel = new KeySignatureLabel() {{
addMouseListener(new MouseAdapter() {
@Override
- public void mousePressed(MouseEvent e) { chordMatrix.setKeySignature(getKey()); }
+ public void mousePressed(MouseEvent e) {
+ chordMatrix.setKeySignature(getKey());
+ }
});
}};
keyboardPanel = new MidiKeyboardPanel(chordMatrix) {{
@Override
public void pianoKeyPressed(int n, InputEvent e) { chordDiagram.clear(); }
});
- keySelecter.getKeysigCombobox().addActionListener(
- e -> chordMatrix.setKeySignature(
- keySelecter.getSelectedKey().transposedKey(
- -chordMatrix.capoSelecter.getCapo()
- )
- )
- );
+ keySelecter.getKeysigCombobox().addActionListener(e->chordMatrix.setKeySignature(
+ keySelecter.getSelectedKey().transposedKey(-chordMatrix.capoSelecter.getCapo())
+ ));
keyboardCenterPanel.keyboard.setPreferredSize(new Dimension(571, 80));
}};
VirtualMidiDevice guiMidiDevice = keyboardPanel.keyboardCenterPanel.keyboard.midiDevice;
sequencerModel.addChangeListener(e->{
SequenceTrackListTableModel sequenceTableModel = sequencerModel.getSequenceTrackListTableModel();
int loadedSequenceIndex = playlistModel.indexOfSequenceOnSequencer();
- songTitleLabel.setText(
- "<html>"+(
- loadedSequenceIndex < 0 ? "[No MIDI file loaded]" :
- "MIDI file " + loadedSequenceIndex + ": " + (
- sequenceTableModel == null ||
- sequenceTableModel.toString() == null ||
- sequenceTableModel.toString().isEmpty() ?
- "[Untitled]" :
- "<font color=maroon>"+sequenceTableModel+"</font>"
- )
- )+"</html>"
- );
+ songTitleLabel.setText("<html>"+(
+ loadedSequenceIndex < 0 ? "[No MIDI file loaded]" :
+ "MIDI file " + loadedSequenceIndex + ": " + (
+ sequenceTableModel == null ||
+ sequenceTableModel.toString().isEmpty() ?
+ "[Untitled]" :
+ "<font color=maroon>"+sequenceTableModel+"</font>"
+ )
+ )+"</html>");
Sequencer sequencer = sequencerModel.getSequencer();
chordMatrix.setPlaying(sequencer.isRunning());
if( sequenceTableModel != null ) {
add( Box.createHorizontalStrut(10) );
}});
add(new JPanel() {{
- add(new JButton(midiDeviceDialog.openAction));
+ add(new JButton(midiDeviceDialog.getOpenAction()));
add(new JButton(about.getOpenAction()));
}});
}});
//
// アプレットのパラメータにMIDIファイルのURLが指定されていたら
// それを再生する
- String midi_url = getParameter("midi_file");
+ String midiUrl = getParameter("midi_file");
System.gc();
- if( midi_url != null ) {
- addToPlaylist(midi_url);
+ if( midiUrl != null ) {
+ addToPlaylist(midiUrl);
try {
play();
} catch (InvalidMidiDataException ex) {
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
+import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
-import java.util.Vector;
+import java.util.stream.Collectors;
import javax.swing.JFrame;
import javax.swing.JLabel;
* MIDI Chord Helper を Java アプリとして起動します。
*/
public class MidiChordHelper {
- private static List<File> fileList = new Vector<File>();
/**
* MIDI Chord Helper を Java アプリとして起動します。
* @param args コマンドライン引数
* @throws Exception 何らかの異常が発生した場合にスローされる
*/
public static void main(String[] args) throws Exception {
- for(String arg : args) fileList.add(new File(arg));
+ List<File> fileList = Arrays.asList(args).stream().map(arg -> new File(arg)).collect(Collectors.toList());
SwingUtilities.invokeLater(()->new AppletFrame(new ChordHelperApplet(), fileList));
}
private static class AppletFrame extends JFrame implements AppletStub, AppletContext {
setFilenameToTitle(applet.sequencerModel.getSequenceTrackListTableModel());
}
});
- applet.midiEditor.loadAndPlay(fileList);
+ applet.midiEditor.play(fileList);
}
@Override
public boolean isActive() { return true; }
Graphics2D g2 = (Graphics2D)g;
g2.setStroke(stroke);
g2.setColor(color);
- synchronized(queue) {
- Iterator<QueueEntry> i = queue.iterator();
- while(i.hasNext()) {
- QueueEntry entry = i.next();
- entry.shape.draw(g2, entry);
- }
- }
+ synchronized(queue) { queue.forEach(e -> e.shape.draw(g2, e)); }
}
/**
* 指定された長方形領域({@link Rectangle})の中央から図形の表示を開始します。
return;
}
point = SwingUtilities.convertPoint(source, point, this);
- synchronized (queue) { queue.add(new QueueEntry(point)); }
+ synchronized(queue) { queue.add(new QueueEntry(point)); }
timer.start();
prevStartedAt = startedAt;
}
/**
* MIDIデバイスモデルからフレームを割り出すためのマップ
*/
- private Map<MidiDeviceModel, MidiDeviceFrame> frameMap = new HashMap<>();
+ private Map<MidiDeviceModel, MidiDeviceFrame> frameOfModel = new HashMap<>();
/**
- * MIDIデバイスモデルに対応するMIDIデバイスフレームを返すマップを返します。
- */
- public Map<MidiDeviceModel, MidiDeviceFrame> getFrameMap() {
- return frameMap;
- }
- /**
- * ツリー上で選択状態が変わったとき、このデスクトップ上のフレームの選択状態に反映します。
+ * MIDIデバイスツリービューで選択状態が変わったとき、
+ * このデスクトップで表示しているフレームの選択状態に反映します。
*/
@Override
public void valueChanged(TreeSelectionEvent tse) {
- TreePath treePath = tse.getNewLeadSelectionPath();
- if( treePath != null ) {
- Object lastSelected = treePath.getLastPathComponent();
- if( lastSelected instanceof MidiDeviceModel ) {
- MidiDeviceModel deviceModel = (MidiDeviceModel)lastSelected;
- if( deviceModel.getMidiDevice().isOpen() ) {
+ TreePath selectedTreePath = tse.getNewLeadSelectionPath();
+ if( selectedTreePath != null ) {
+ Object selectedLeaf = selectedTreePath.getLastPathComponent();
+ if( selectedLeaf instanceof MidiDeviceModel ) {
+ MidiDeviceModel selectedModel = (MidiDeviceModel)selectedLeaf;
+ if( selectedModel.getMidiDevice().isOpen() ) {
// 開いているMIDIデバイスがツリー上で選択されたら、対応するフレームを選択
- MidiDeviceFrame deviceFrame = frameMap.get(deviceModel);
- if( deviceFrame == null ) return;
- deviceFrame.toFront();
- try {
- deviceFrame.setSelected(true);
- } catch( PropertyVetoException ex ) {
- ex.printStackTrace();
+ MidiDeviceFrame deviceFrame = frameOfModel.get(selectedModel);
+ if( deviceFrame != null ) {
+ deviceFrame.toFront();
+ try {
+ deviceFrame.setSelected(true);
+ } catch( PropertyVetoException ex ) {
+ ex.printStackTrace();
+ }
+ return;
}
- return;
}
}
}
// それ以外が選択されたら、現在選択されているフレームを非選択
JInternalFrame frame = getSelectedFrame();
- if( ! (frame instanceof MidiDeviceFrame) ) return;
- try {
+ if( frame instanceof MidiDeviceFrame ) try {
((MidiDeviceFrame)frame).setSelected(false);
} catch( PropertyVetoException ex ) {
ex.printStackTrace();
public void treeNodesRemoved(TreeModelEvent e) { }
@Override
public void treeStructureChanged(TreeModelEvent e) {
- // USBから切断されてツリーから消滅したMIDIデバイスモデルのフレームを削除
- frameMap.keySet().stream().filter(m-> ! deviceTreeModel.contains(m))
- .collect(Collectors.toList()).stream().map(m-> frameMap.remove(m))
- .filter(f-> f != null).forEach(f-> remove(f));
+ // ツリーから削除されたMIDIデバイスモデルのフレームを削除
+ frameOfModel.keySet().stream()
+ .filter(m-> ! deviceTreeModel.contains(m))
+ .collect(Collectors.toSet()).stream()
+ .map(m-> frameOfModel.remove(m))
+ .filter(f-> f != null)
+ .forEach(f-> remove(f));
//
// ツリーに追加されたMIDIデバイスモデルのフレームを生成
- deviceTreeModel.stream().filter(dm -> ! frameMap.containsKey(dm)).forEach(dm->{
- MidiDeviceFrame frame = new MidiDeviceFrame(dm, cablePane);
- frameMap.put(dm, frame);
- //
- // トランスミッタリストモデルが変化したときにMIDIケーブルを再描画
- TransmitterListModel txListModel = dm.getTransmitterListModel();
- if( txListModel != null ) txListModel.addListDataListener(cablePane.midiConnecterListDataListener);
- //
- // レシーバリストモデルが変化したときにMIDIケーブルを再描画
- ReceiverListModel rxListModel = dm.getReceiverListModel();
- if( rxListModel != null ) rxListModel.addListDataListener(cablePane.midiConnecterListDataListener);
- //
- // デバイスフレームが開閉したときの動作
- frame.addInternalFrameListener(cablePane.midiDeviceFrameListener);
- frame.addInternalFrameListener(deviceTreeView.midiDeviceFrameListener);
- frame.addInternalFrameListener(deviceInfoPane.midiDeviceFrameListener);
- //
- // 移動または変形時の動作
- frame.addComponentListener(cablePane.midiDeviceFrameComponentListener);
- //
- // サイズを設定したフレームをデスクトップに追加
- frame.setSize(250, dm.getInOutType() == MidiDeviceInOutType.MIDI_IN_OUT ? 90 : 70);
- add(frame);
- //
- // デバイスが開いていたら表示
- if( dm.getMidiDevice().isOpen() ) frame.setVisible(true);
- });
+ deviceTreeModel.stream()
+ .filter(dm -> ! frameOfModel.containsKey(dm))
+ .forEach(dm->{
+ MidiDeviceFrame frame = new MidiDeviceFrame(dm, cablePane);
+ frameOfModel.put(dm, frame);
+ //
+ // トランスミッタリストモデルが変化したときにMIDIケーブルを再描画
+ TransmitterListModel txListModel = dm.getTransmitterListModel();
+ if( txListModel != null ) txListModel.addListDataListener(cablePane.midiConnecterListDataListener);
+ //
+ // レシーバリストモデルが変化したときにMIDIケーブルを再描画
+ ReceiverListModel rxListModel = dm.getReceiverListModel();
+ if( rxListModel != null ) rxListModel.addListDataListener(cablePane.midiConnecterListDataListener);
+ //
+ // デバイスフレームが開閉したときの動作
+ frame.addInternalFrameListener(cablePane.midiDeviceFrameListener);
+ frame.addInternalFrameListener(deviceTreeView.midiDeviceFrameListener);
+ frame.addInternalFrameListener(deviceInfoPane.midiDeviceFrameListener);
+ //
+ // 移動または変形時の動作
+ frame.addComponentListener(cablePane.midiDeviceFrameComponentListener);
+ //
+ // サイズを設定したフレームをデスクトップに追加
+ frame.setSize(250, dm.getInOutType() == MidiDeviceInOutType.MIDI_IN_OUT ? 90 : 70);
+ add(frame);
+ //
+ // デバイスが開いていたら表示
+ if( dm.getMidiDevice().isOpen() ) frame.setVisible(true);
+ });
}
};
deviceTreeModel.addTreeModelListener(treeModelListener);
int toY = 10;
for( MidiDeviceModel deviceModel : deviceTreeModel ) {
if( ! deviceModel.getMidiDevice().isOpen() ) continue;
- frameMap.get(deviceModel).setLocation(toX, toY);
+ frameOfModel.get(deviceModel).setLocation(toX, toY);
toX = (toX == 10 ? 270 : 10);
toY += 70;
}
MidiDeviceModel deviceModel = null;
try {
deviceModel = (MidiDeviceModel)support.getTransferable().getTransferData(flavor);
- MidiDeviceFrame deviceFrame = frameMap.get(deviceModel);
+ MidiDeviceFrame deviceFrame = frameOfModel.get(deviceModel);
if( deviceFrame == null ) return false;
deviceModel.open();
if( ! deviceModel.getMidiDevice().isOpen() ) {
*/
public class MidiDeviceDialog extends JDialog {
public static final Icon MIDI_CONNECTER_ICON = new ButtonIcon(ButtonIcon.MIDI_CONNECTOR_ICON);
- /**
- * MIDIデバイスダイアログを開くアクション
- */
- public Action openAction = new AbstractAction() {
+ private Action openAction = new AbstractAction() {
{
putValue(NAME, "MIDI device connection");
putValue(SHORT_DESCRIPTION, "MIDIデバイス間の接続を編集");
public void actionPerformed(ActionEvent event) { setVisible(true); }
};
/**
+ * MIDIデバイスダイアログを開くアクションを返します。
+ */
+ public Action getOpenAction() { return openAction; }
+ /**
* MIDIデバイスダイアログを構築します。
* @param deviceTreeModel デバイスツリーモデル
*/
setTitle(openAction.getValue(Action.NAME).toString());
setBounds( 300, 300, 820, 540 );
MidiDeviceTreeView deviceTreeView = new MidiDeviceTreeView(deviceTreeModel);
- final MidiDeviceInfoPane deviceInfoPane = new MidiDeviceInfoPane();
+ MidiDeviceInfoPane deviceInfoPane = new MidiDeviceInfoPane();
deviceTreeView.addTreeSelectionListener(deviceInfoPane);
MidiDeviceDesktopPane desktopPane = new MidiDeviceDesktopPane(deviceTreeView, deviceInfoPane, this);
deviceTreeView.addTreeSelectionListener(desktopPane);
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
/**
* シーケンサの現在位置(分:秒)を表示するビュー
*/
public class SequencerTimeView extends JPanel {
- private TimeLabel timePositionLabel = new TimePositionLabel();
- private TimeLabel timeLengthLabel = new TimeLengthLabel();
- /**
- * シーケンサの現在位置と曲の長さを(分:秒)で表示するビューを構築します。
- * @param model MIDIシーケンサモデル
- */
- public SequencerTimeView(MidiSequencerModel model) {
- setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
- add(timePositionLabel);
- add(timeLengthLabel);
- model.addChangeListener(new ChangeListener() {
- @Override
- public void stateChanged(ChangeEvent e) {
- Object source = e.getSource();
- if( source instanceof MidiSequencerModel ) {
- MidiSequencerModel model = (MidiSequencerModel)source;
- timeLengthLabel.changeTimeInSecond(model.getMaximum()/1000);
- timePositionLabel.changeTimeInSecond(model.getValue()/1000);
- }
- }
- });
- }
private static abstract class TimeLabel extends JLabel {
{ setTimeInSecond(0); }
protected String toTimeString(int sec) {
if(valueInSec != sec) setTimeInSecond(sec);
}
}
- private static class TimePositionLabel extends TimeLabel {
+ private TimeLabel timePositionLabel = new TimeLabel() {
{
setFont( getFont().deriveFont(getFont().getSize2D() + 4) );
setForeground( new Color(0x80,0x00,0x00) );
setToolTipText("Time position - 現在位置(分:秒)");
}
- }
- private static class TimeLengthLabel extends TimeLabel {
- {
- setToolTipText("Time length - 曲の長さ(分:秒)");
- }
+ };
+ private TimeLabel timeLengthLabel = new TimeLabel() {
+ { setToolTipText("Time length - 曲の長さ(分:秒)"); }
protected String toTimeString(int sec) {
return "/"+super.toTimeString(sec);
}
+ };
+ /**
+ * シーケンサの現在位置と曲の長さを(分:秒)で表示するビューを構築します。
+ * @param model MIDIシーケンサモデル
+ */
+ public SequencerTimeView(MidiSequencerModel model) {
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+ add(timePositionLabel);
+ add(timeLengthLabel);
+ model.addChangeListener(e->{
+ Object source = e.getSource();
+ if( source instanceof MidiSequencerModel ) {
+ MidiSequencerModel sourceModel = (MidiSequencerModel)source;
+ timeLengthLabel.changeTimeInSecond(sourceModel.getMaximum()/1000);
+ timePositionLabel.changeTimeInSecond(sourceModel.getValue()/1000);
+ }
+ });
}
}
package camidion.chordhelper.midieditor;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.Base64;
import java.util.regex.Pattern;
+import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.MidiSystem;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
base64TextArea.requestFocusInWindow();
}
/**
+ * 入力されたBase64テキストをデコードし、MIDIシーケンスとしてプレイリストに追加します。
+ * @return プレイリストに追加されたMIDIシーケンスのインデックス(先頭が0)、追加に失敗した場合は -1
+ */
+ public int addToPlaylist() {
+ byte[] midiData = null;
+ try {
+ midiData = getMIDIData();
+ } catch(Exception e) {
+ error("Base64デコードに失敗しました。\n"+e);
+ return -1;
+ }
+ try (InputStream in = new ByteArrayInputStream(midiData)) {
+ return midiEditor.sequenceListTable.getModel().add(MidiSystem.getSequence(in), null);
+ } catch( IOException|InvalidMidiDataException e ) {
+ error("Base64デコードされたデータが正しいMIDI形式になっていません。\n"+e);
+ return -1;
+ }
+ }
+ /**
* Base64デコードアクション
*/
public Action addBase64Action = new AbstractAction("Add to PlayList", new ButtonIcon(ButtonIcon.EJECT_ICON)) {
{ putValue(Action.SHORT_DESCRIPTION, "Base64デコードして、プレイリストへ追加"); }
@Override
public void actionPerformed(ActionEvent event) {
- byte[] midiData = null;
- try {
- midiData = getMIDIData();
- } catch(Exception e) {
- error("Base64デコードに失敗しました。\n"+e);
- return;
- }
- try {
- midiEditor.sequenceListTable.getModel().addSequence(midiData, null);
- } catch(Exception e) {
- error("Base64デコードされたデータが正しいMIDI形式になっていません。\n"+e);
- return;
- }
+ addToPlaylist();
setVisible(false);
}
};
base64TextArea.setText(null);
base64TextArea.append(base64Data);
}
+ /**
+ * テキスト文字列を設定します(例外が発生したときのメッセージ出力用)。
+ * @param text テキスト文字列
+ */
+ public void setText(String text) {
+ base64TextArea.setText(text);
+ }
}
/**
* 入力している内容からMIDIメッセージを生成して返します。
* @param charset 文字コード
- * @return 入力している内容から生成したMIDIメッセージ
+ * @return 入力している内容から生成したMIDIメッセージ(生成できなかった場合はnull)
*/
public MidiMessage getMessage(Charset charset) {
int msgStatus = statusText.getValue();
*/
public class MidiProgramSelecter extends JComboBox<String> {
private int family;
- private MidiProgramFamilySelecter family_selecter = null;
+ private MidiProgramFamilySelecter familySelecter = null;
public MidiProgramSelecter() {
setFamily(-1);
}
public void setFamilySelecter( MidiProgramFamilySelecter mpfs ) {
- family_selecter = mpfs;
+ familySelecter = mpfs;
}
public void setFamily( int family ) {
int program_no = getProgram();
}
else {
if( family >= 0 ) setFamily(-1);
- if( family_selecter != null ) family_selecter.setSelectedIndex(0);
+ if( familySelecter != null ) familySelecter.setSelectedIndex(0);
if( program_no < getItemCount() ) setSelectedIndex(program_no);
}
}
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.AccessControlException;
import java.util.Arrays;
import java.util.EventObject;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
-import javax.sound.midi.Sequence;
+import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.swing.AbstractAction;
@Override
public boolean importData(TransferSupport support) {
try {
- loadAndPlay((List<File>)support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor));
+ play((List<File>)support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor));
return true;
} catch (Exception e) { showError(e); return false; }
}
};
/**
- * 複数のMIDIファイルを読み込み、再生されていなかったら再生します。
+ * 指定されたリストに格納されたMIDIファイルを読み込んで再生します。
* すでに再生されていた場合、このエディタダイアログを表示します。
- *
* @param fileList 読み込むMIDIファイルのリスト
- * @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
- * @see #loadAndPlay(File)
*/
- public void loadAndPlay(List<File> fileList) {
- int indexOfAddedTop = -1;
+ public void play(List<File> fileList) {
PlaylistTableModel playlist = sequenceListTable.getModel();
- try {
- indexOfAddedTop = playlist.addSequences(fileList);
- } catch(IOException|InvalidMidiDataException e) {
- showWarning(e);
- } catch(AccessControlException e) {
- showError(e);
+ int firstIndex = -1;
+ Iterator<File> itr = fileList.iterator();
+ while(itr.hasNext()) {
+ File file = itr.next();
+ try (FileInputStream in = new FileInputStream(file)) {
+ int lastIndex = playlist.add(MidiSystem.getSequence(in), file.getName());
+ if( firstIndex < 0 ) firstIndex = lastIndex;
+ } catch(IOException|InvalidMidiDataException e) {
+ String message = "Could not open as MIDI file "+file+"\n"+e;
+ if( ! itr.hasNext() ) { showWarning(message); break; }
+ if( ! confirm(message + "\n\nContinue to open next file ?") ) break;
+ } catch(AccessControlException e) {
+ showError(e); break;
+ }
}
MidiSequencerModel sequencerModel = playlist.getSequencerModel();
if( sequencerModel.getSequencer().isRunning() ) {
openAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, command));
return;
}
- if( indexOfAddedTop >= 0 ) {
+ if( firstIndex >= 0 ) {
try {
- playlist.loadToSequencer(indexOfAddedTop);
+ playlist.loadToSequencer(firstIndex);
} catch (InvalidMidiDataException e) { showError(e); return; }
sequencerModel.start();
}
}
- /**
- * 1件のMIDIファイルを読み込み、再生されていなかったら再生します。
- * すでに再生されていた場合、このエディタダイアログを表示します。
- *
- * @param file 読み込むMIDIファイル
- * @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
- * @see #loadAndPlay(List) loadAndPlay(List<File>)
- */
- public void loadAndPlay(File file) throws InvalidMidiDataException {
- loadAndPlay(Arrays.asList(file));
- }
private static final Insets ZERO_INSETS = new Insets(0,0,0,0);
private static final Icon deleteIcon = new ButtonIcon(ButtonIcon.X_ICON);
byte[] data = null;
String filename = null;
if( mstm != null ) {
- data = mstm.getMIDIdata();
filename = mstm.getFilename();
+ try {
+ data = mstm.getMIDIdata();
+ } catch (IOException ioe) {
+ base64Dialog.setText("File["+filename+"]:"+ioe.toString());
+ base64Dialog.setVisible(true);
+ return;
+ }
}
base64Dialog.setMIDIData(data, filename);
base64Dialog.setVisible(true);
@Override
public void actionPerformed(ActionEvent event) {
if( showOpenDialog((Component)event.getSource()) != JFileChooser.APPROVE_OPTION ) return;
- try {
- loadAndPlay(getSelectedFile());
- } catch (InvalidMidiDataException ex) { showError(ex); }
+ play(Arrays.asList(getSelectedFile()));
}
};
};
long tick = tickPositionModel.getTickPosition();
MidiMessageForm form = eventDialog.midiMessageForm;
SequenceTrackListTableModel seqModel = trackModel.getParent();
- MidiEvent newMidiEvent = new MidiEvent(form.getMessage(seqModel.charset), tick);
+ MidiMessage msg = form.getMessage(seqModel.charset);
+ if( msg == null ) {
+ return false;
+ }
+ MidiEvent newMidiEvent = new MidiEvent(msg, tick);
if( midiEventsToBeOverwritten != null ) {
// 上書き消去するための選択済イベントがあった場合
trackModel.removeMidiEvents(midiEventsToBeOverwritten);
@Override
public void actionPerformed(ActionEvent event) {
try {
- int index = playlist.addSequenceAndPlay(getMidiSequence());
- playlist.getSequenceModelList().get(index).setModified(true);
+ playlist.getSequenceModelList().get(playlist.play(getMidiSequence())).setModified(true);
} catch (InvalidMidiDataException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(
package camidion.chordhelper.midieditor;
import java.awt.event.ActionEvent;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import javax.sound.midi.InvalidMidiDataException;
-import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultListSelectionModel;
import javax.swing.Icon;
-import javax.swing.JOptionPane;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.table.AbstractTableModel;
import camidion.chordhelper.ButtonIcon;
-import camidion.chordhelper.ChordHelperApplet;
import camidion.chordhelper.mididevice.MidiSequencerModel;
import camidion.chordhelper.music.ChordProgression;
sequencerModel.addChangeListener(mmssPosition);
// EOF(0x2F)が来たら曲の終わりなので次の曲へ進める
sequencerModel.getSequencer().addMetaEventListener(msg->{
- if(msg.getType() == 0x2F) SwingUtilities.invokeLater(()->goNext());
+ if(msg.getType() == 0x2F) SwingUtilities.invokeLater(()->{
+ try {
+ goNext();
+ } catch (InvalidMidiDataException e) {
+ throw new RuntimeException("Could not play next sequence after end-of-track",e);
+ }
+ });
});
emptyTrackListTableModel = new SequenceTrackListTableModel(this, null, null);
emptyEventListTableModel = new TrackEventListTableModel(emptyTrackListTableModel, null);
* <p>リピートモードの場合は同じ曲をもう一度再生、そうでない場合は次の曲へ進んで再生します。
* 次の曲がなければ、そこで停止します。いずれの場合も曲の先頭へ戻ります。
* </p>
+ * @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
*/
- private void goNext() {
+ private void goNext() throws InvalidMidiDataException {
// とりあえず曲の先頭へ戻る
sequencerModel.getSequencer().setMicrosecondPosition(0);
if( (Boolean)toggleRepeatAction.getValue(Action.SELECTED_KEY) || loadNext(1) ) {
);
putValue(LARGE_ICON_KEY, new ButtonIcon(ButtonIcon.TOP_ICON));
}
+ @Override
public void actionPerformed(ActionEvent event) {
- if( sequencerModel.getSequencer().getTickPosition() <= 40 ) loadNext(-1);
+ if( sequencerModel.getSequencer().getTickPosition() <= 40 ) {
+ try {
+ loadNext(-1);
+ } catch (InvalidMidiDataException e) {
+ throw new RuntimeException("Could not play previous sequence",e);
+ }
+ }
sequencerModel.setValue(0);
}
};
putValue(LARGE_ICON_KEY, new ButtonIcon(ButtonIcon.BOTTOM_ICON));
}
public void actionPerformed(ActionEvent event) {
- if(loadNext(1)) sequencerModel.setValue(0);
+ try {
+ if(loadNext(1)) sequencerModel.setValue(0);
+ } catch (InvalidMidiDataException e) {
+ throw new RuntimeException("Could not play next sequence",e);
+ }
}
};
/**
* @return 全シーケンスの合計時間長 [秒]
*/
public int getTotalTimeInSeconds() {
- int total = 0;
- long usec;
- for( SequenceTrackListTableModel m : sequenceModelList ) {
- usec = m.getSequence().getMicrosecondLength();
- total += (int)( (usec < 0 ? usec += 0x100000000L : usec)/1000L/1000L );
- }
- return total;
+ return sequenceModelList.stream().mapToInt(m->{
+ long usec = m.getSequence().getMicrosecondLength();
+ return (int)( (usec < 0 ? usec += 0x100000000L : usec)/1000L/1000L );
+ }).sum();
}
/**
* 選択されたMIDIシーケンスのテーブルモデルを返します。
* @param filename ファイル名(nullの場合、ファイル名なし)
* @return 追加されたシーケンスのインデックス(先頭が 0)
*/
- public int addSequence(Sequence sequence, String filename) {
+ public int add(Sequence sequence, String filename) {
if( sequence == null ) sequence = (new ChordProgression()).toMidiSequence();
sequenceModelList.add(new SequenceTrackListTableModel(this, sequence, filename));
//
return lastIndex;
}
/**
- * バイト列とファイル名からMIDIシーケンスを追加します。
- * @param data バイト列(nullの場合、シーケンスを自動生成して追加)
- * @param filename ファイル名
- * @return 追加先インデックス(先頭が 0)
- * @throws IOException バイト列の読み込みに失敗した場合
- * @throws InvalidMidiDataException MIDIデータが正しくない場合
- */
- public int addSequence(byte[] data, String filename) throws IOException, InvalidMidiDataException {
- Sequence sequence = null;
- if( data != null ) {
- try (InputStream in = new ByteArrayInputStream(data)) {
- sequence = MidiSystem.getSequence(in);
- } catch( IOException|InvalidMidiDataException e ) {
- throw e;
- }
- }
- return addSequence(sequence, filename);
- }
- /**
- * MIDIファイルを追加します。
- * @param midiFile MIDIファイル(nullの場合、シーケンスを自動生成して追加)
- * @return 追加先インデックス(先頭が 0)
- * @throws IOException ファイル入出力に失敗した場合
- * @throws InvalidMidiDataException ファイル内のMIDIデータが正しくない場合
- */
- public int addSequence(File midiFile) throws InvalidMidiDataException, IOException {
- Sequence sequence = null;
- String filename = null;
- if( midiFile != null ) {
- try (FileInputStream in = new FileInputStream(midiFile)) {
- sequence = MidiSystem.getSequence(in);
- } catch( IOException|InvalidMidiDataException e ) {
- throw e;
- }
- filename = midiFile.getName();
- }
- return addSequence(sequence, filename);
- }
- /**
- * MIDIシーケンスを追加し、再生されていなかった場合は追加したシーケンスから再生を開始します。
+ * 指定されたMIDIシーケンスをこのプレイリストに追加して再生します。
* @param sequence MIDIシーケンス
* @return 追加されたシーケンスのインデックス(先頭が 0)
* @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
*/
- public int addSequenceAndPlay(Sequence sequence) throws InvalidMidiDataException {
- int lastIndex = addSequence(sequence,"");
+ public int play(Sequence sequence) throws InvalidMidiDataException {
+ int lastIndex = add(sequence,"");
if( ! sequencerModel.getSequencer().isRunning() ) {
loadToSequencer(lastIndex);
sequencerModel.start();
}
return lastIndex;
}
- /**
- * 複数のMIDIファイルを追加します。
- * @param fileList 追加するMIDIファイルのリスト
- * @return 追加先の最初のインデックス(先頭が 0、追加されなかった場合は -1)
- * @throws InvalidMidiDataException ファイル内のMIDIデータが正しくない場合
- * @throws IOException ファイル入出力に失敗した場合
- */
- public int addSequences(List<File> fileList) throws InvalidMidiDataException, IOException {
- int firstIndex = -1;
- for( File file : fileList ) {
- int lastIndex = addSequence(file);
- if( firstIndex == -1 ) firstIndex = lastIndex;
- }
- return firstIndex;
- }
- /**
- * URLから読み込んだMIDIシーケンスを追加します。
- * @param midiFileUrl MIDIファイルのURL
- * @return 追加先インデックス(先頭が 0、失敗した場合は -1)
- * @throws URISyntaxException URLの形式に誤りがある場合
- * @throws IOException 入出力に失敗した場合
- * @throws InvalidMidiDataException MIDIデータが正しくない場合
- */
- public int addSequenceFromURL(String midiFileUrl)
- throws URISyntaxException, IOException, InvalidMidiDataException
- {
- URL url = (new URI(midiFileUrl)).toURL();
- return addSequence(MidiSystem.getSequence(url), url.getFile().replaceFirst("^.*/",""));
- }
/**
* 選択されたシーケンスを除去します。
/**
* 引数で示された数だけ次へ進めたシーケンスをロードします。
* @param offset 進みたいシーケンス数
- * @return true:ロード成功、false:これ以上進めない
+ * @return 以前と異なるインデックスのシーケンスをロードできた場合true
+ * @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
*/
- public boolean loadNext(int offset) {
+ private boolean loadNext(int offset) throws InvalidMidiDataException {
int loadedIndex = indexOfSequenceOnSequencer();
- int index = (loadedIndex < 0 ? 0 : loadedIndex + offset);
- if( index < 0 || index >= sequenceModelList.size() ) return false;
- try {
- loadToSequencer(index);
- } catch (InvalidMidiDataException ex) {
- JOptionPane.showMessageDialog(null, ex, ChordHelperApplet.VersionInfo.NAME, JOptionPane.ERROR_MESSAGE);
- return false;
+ int newIndex = loadedIndex + offset;
+ if( newIndex < 0 ) newIndex = 0; else {
+ int sz = sequenceModelList.size();
+ if( newIndex >= sz ) newIndex = sz - 1;
}
+ if( newIndex == loadedIndex ) return false;
+ loadToSequencer(newIndex);
return true;
}
}
* @return ファイル名
*/
public String getFilename() { return filename; }
+ /**
+ * このシーケンスを表す文字列としてシーケンス名を返します。シーケンス名がない場合は空文字列を返します。
+ */
@Override
public String toString() {
byte b[] = MIDISpec.getNameBytesOf(sequence);
}
/**
* このシーケンスのMIDIデータのバイト列を返します。
- * @return MIDIデータのバイト列(失敗した場合null)
+ * @return MIDIデータのバイト列(ない場合はnull)
+ * @throws IOException バイト列の出力に失敗した場合
*/
- public byte[] getMIDIdata() {
+ public byte[] getMIDIdata() throws IOException {
if( sequence == null || sequence.getTracks().length == 0 ) {
return null;
}
MidiSystem.write(sequence, 1, out);
return out.toByteArray();
} catch ( IOException e ) {
- e.printStackTrace();
- return null;
+ throw e;
}
}
/**
/**
* トラック名のバイト列を返します。
* @param track MIDIトラック
- * @return トラック名のバイト列
+ * @return トラック名のバイト列(トラック名が見つからない場合はnull)
*/
public static byte[] getNameBytesOf(Track track) {
MidiEvent midiEvent;
}
/**
* シーケンス名のバイト列を返します。
- * <p>トラック名の入った最初のトラックにあるトラック名を
- * シーケンス名として返します。
- * </p>
+ * <p>トラック名の入った最初のトラックにあるトラック名をシーケンス名として返します。</p>
* @param seq MIDIシーケンス
- * @return シーケンス名のバイト列
+ * @return シーケンス名のバイト列(見つからない場合はnull)
*/
public static byte[] getNameBytesOf(Sequence seq) {
// Returns name of the MIDI sequence.
public void actionPerformed(ActionEvent e) {
VirtualMidiDevice vmd = keyboardCenterPanel.keyboard.midiDevice;
MidiMessage msg = eventDialog.midiMessageForm.getMessage(Charset.defaultCharset());
- vmd.sendMidiMessage(msg);
+ if( msg != null ) vmd.sendMidiMessage(msg);
}
},
keyboardCenterPanel.keyboard.midiChComboboxModel.getSelectedChannel()