OSDN Git Service

New MIDI Sequence画面のTrack切替をコンボボックスから横長リストに変更して使いやすくした
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / midieditor / TempoSelecter.java
1 package camidion.chordhelper.midieditor;
2
3 import java.awt.Component;
4 import java.awt.event.MouseAdapter;
5 import java.awt.event.MouseEvent;
6
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;
14
15 import camidion.chordhelper.ButtonIcon;
16 import camidion.chordhelper.music.MIDISpec;
17
18 /**
19  * テンポ選択(QPM: Quarter Per Minute)
20  */
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
27         ) {{
28                 setVerticalAlignment(JLabel.CENTER);
29         }};
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));
37                 add(tempoLabel);
38                 add(Box.createHorizontalStrut(5));
39                 add(tempoSpinner);
40                 add(tempoValueLabel);
41                 setEditable(true);
42                 tempoLabel.addMouseListener(new MouseAdapter() {
43                         @Override
44                         public void mousePressed(MouseEvent e) {
45                                 Component obj = e.getComponent();
46                                 if(obj == tempoLabel && isEditable()) {
47                                         //
48                                         // Adjust tempo by interval time between two clicks (tapping)
49                                         //
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 );
58                                         }
59                                 }
60                         }
61                 });
62         }
63         private long prevBeatMicrosecondPosition = 0;
64         private boolean editable;
65         /**
66          * 編集可能かどうかを返します。
67          * @return 編集可能ならtrue
68          */
69         public boolean isEditable() { return editable; }
70         /**
71          * 編集可能かどうかを設定します。
72          * @param editable 編集可能ならtrue
73          */
74         public void setEditable( boolean editable ) {
75                 this.editable = editable;
76                 tempoSpinner.setVisible( editable );
77                 tempoValueLabel.setVisible( !editable );
78                 if( !editable ) {
79                         // Copy spinner's value to label
80                         tempoValueLabel.setText(
81                                 ""+tempoSpinnerModel.getNumber().intValue()
82                         );
83                 }
84                 tempoLabel.setToolTipText(
85                         editable ?
86                         "Tap rhythmically here to adjust tempo - ここをクリックしてリズムをとるとテンポを合わせられます"
87                         : null
88                 );
89         }
90         /**
91          * テンポを返します。
92          * @return テンポ [BPM](QPM)
93          */
94         public int getTempoInQpm() {
95                 return tempoSpinnerModel.getNumber().intValue();
96         }
97         /**
98          * テンポをMIDIメタメッセージのバイト列として返します。
99          * @return MIDIメタメッセージのバイト列
100          */
101         public byte[] getTempoByteArray() {
102                 return MIDISpec.qpmTempoToByteArray(getTempoInQpm());
103         }
104         /**
105          * テンポを設定します。
106          * @param qpm BPM(QPM)の値
107          */
108         public void setTempo(int qpm) {
109                 tempoSpinnerModel.setValue(new Integer(qpm));
110                 tempoValueLabel.setText(""+qpm);
111         }
112         /**
113          * MIDIメタメッセージのバイト列からテンポを設定します。
114          * @param msgdata MIDIメタメッセージのバイト列(null を指定した場合はデフォルトに戻る)
115          */
116         public void setTempo(byte msgdata[]) {
117                 if( msgdata==null ) clear(); else setTempo(MIDISpec.byteArrayToQpmTempo(msgdata));
118         }
119         /**
120          * MIDIシーケンスの特定tick位置におけるテンポを設定します。
121          * @param tickIndex MIDIシーケンスのtickインデックス
122          * @param tickPosition tick位置
123          */
124         public void setTempoAt(SequenceTickIndex tickIndex, long tickPosition) {
125                 MetaMessage msg = tickIndex.lastMetaMessageAt(
126                         SequenceTickIndex.MetaMessageType.TEMPO,
127                         tickPosition
128                 );
129                 setTempo(msg==null ? null : msg.getData());
130         }
131         public void clear() { setTempo(DEFAULT_QPM); }
132 }