1 package camidion.chordhelper.midieditor;
3 import java.awt.Component;
4 import java.awt.event.MouseAdapter;
5 import java.awt.event.MouseEvent;
7 import javax.sound.midi.MetaMessage;
8 import javax.swing.Box;
9 import javax.swing.BoxLayout;
10 import javax.swing.JLabel;
11 import javax.swing.JPanel;
12 import javax.swing.JSpinner;
13 import javax.swing.SpinnerNumberModel;
15 import camidion.chordhelper.ButtonIcon;
16 import camidion.chordhelper.music.MIDISpec;
19 * テンポ選択(QPM: Quarter Per Minute)
21 public class TempoSelecter extends JPanel {
22 static final int DEFAULT_QPM = 120;
23 protected SpinnerNumberModel tempoSpinnerModel =
24 new SpinnerNumberModel(DEFAULT_QPM, 1, 999, 1);
25 private JLabel tempoLabel = new JLabel(
26 "=", new ButtonIcon(ButtonIcon.QUARTER_NOTE_ICON), JLabel.CENTER
28 setVerticalAlignment(JLabel.CENTER);
30 private JLabel tempoValueLabel = new JLabel(""+DEFAULT_QPM);
31 private JSpinner tempoSpinner = new JSpinner(tempoSpinnerModel);
32 public TempoSelecter() {
33 String tooltip = "Tempo in quatrers per minute - テンポ(1分あたりの四分音符の数)";
34 tempoSpinner.setToolTipText(tooltip);
35 tempoValueLabel.setToolTipText(tooltip);
36 setLayout(new BoxLayout(this,BoxLayout.X_AXIS));
38 add(Box.createHorizontalStrut(5));
42 tempoLabel.addMouseListener(new MouseAdapter() {
44 public void mousePressed(MouseEvent e) {
45 Component obj = e.getComponent();
46 if(obj == tempoLabel && isEditable()) {
48 // Adjust tempo by interval time between two clicks (tapping)
50 long currentMicrosecond = System.nanoTime()/1000;
51 // midi_ch_selecter.noteOn( 9, 37, 100 );
52 long interval_us = currentMicrosecond - prevBeatMicrosecondPosition;
53 prevBeatMicrosecondPosition = currentMicrosecond;
54 if( interval_us < 2000000L /* Shorter than 2 sec only */ ) {
55 int tempo_in_bpm = (int)(240000000L / interval_us) >> 2; // n/4拍子の場合のみを想定
56 int old_tempo_in_bpm = getTempoInQpm();
57 setTempo( ( tempo_in_bpm + old_tempo_in_bpm * 2 ) / 3 );
63 private long prevBeatMicrosecondPosition = 0;
64 private boolean editable;
69 public boolean isEditable() { return editable; }
72 * @param editable 編集可能ならtrue
74 public void setEditable( boolean editable ) {
75 this.editable = editable;
76 tempoSpinner.setVisible( editable );
77 tempoValueLabel.setVisible( !editable );
79 // Copy spinner's value to label
80 tempoValueLabel.setText(
81 ""+tempoSpinnerModel.getNumber().intValue()
84 tempoLabel.setToolTipText(
86 "Tap rhythmically here to adjust tempo - ここをクリックしてリズムをとるとテンポを合わせられます"
92 * @return テンポ [BPM](QPM)
94 public int getTempoInQpm() {
95 return tempoSpinnerModel.getNumber().intValue();
98 * テンポをMIDIメタメッセージのバイト列として返します。
99 * @return MIDIメタメッセージのバイト列
101 public byte[] getTempoByteArray() {
102 return MIDISpec.qpmTempoToByteArray(getTempoInQpm());
106 * @param qpm BPM(QPM)の値
108 public void setTempo(int qpm) {
109 tempoSpinnerModel.setValue(new Integer(qpm));
110 tempoValueLabel.setText(""+qpm);
113 * MIDIメタメッセージのバイト列からテンポを設定します。
114 * @param msgdata MIDIメタメッセージのバイト列(null を指定した場合はデフォルトに戻る)
116 public void setTempo(byte msgdata[]) {
117 if( msgdata==null ) clear(); else setTempo(MIDISpec.byteArrayToQpmTempo(msgdata));
120 * MIDIシーケンスの特定tick位置におけるテンポを設定します。
121 * @param tickIndex MIDIシーケンスのtickインデックス
122 * @param tickPosition tick位置
124 public void setTempoAt(SequenceTickIndex tickIndex, long tickPosition) {
125 MetaMessage msg = tickIndex.lastMetaMessageAt(
126 SequenceTickIndex.MetaMessageType.TEMPO,
129 setTempo(msg==null ? null : msg.getData());
131 public void clear() { setTempo(DEFAULT_QPM); }