OSDN Git Service

・文字コード判定方法の改善
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / midieditor / NewSequenceDialog.java
1 package camidion.chordhelper.midieditor;
2
3 import java.awt.Dimension;
4 import java.awt.Graphics;
5 import java.awt.Graphics2D;
6 import java.awt.GridLayout;
7 import java.awt.Insets;
8 import java.awt.Point;
9 import java.awt.Rectangle;
10 import java.awt.event.ActionEvent;
11 import java.awt.event.ActionListener;
12 import java.awt.event.ComponentEvent;
13 import java.awt.event.ComponentListener;
14 import java.awt.event.InputEvent;
15 import java.awt.event.MouseEvent;
16 import java.awt.event.MouseListener;
17 import java.util.ArrayList;
18 import java.util.Vector;
19
20 import javax.sound.midi.InvalidMidiDataException;
21 import javax.sound.midi.MidiChannel;
22 import javax.sound.midi.Sequence;
23 import javax.swing.AbstractAction;
24 import javax.swing.Action;
25 import javax.swing.BoxLayout;
26 import javax.swing.JButton;
27 import javax.swing.JCheckBox;
28 import javax.swing.JComboBox;
29 import javax.swing.JComponent;
30 import javax.swing.JDialog;
31 import javax.swing.JLabel;
32 import javax.swing.JOptionPane;
33 import javax.swing.JPanel;
34 import javax.swing.JScrollPane;
35 import javax.swing.JSpinner;
36 import javax.swing.JTabbedPane;
37 import javax.swing.JTextArea;
38 import javax.swing.JTextField;
39 import javax.swing.SpinnerNumberModel;
40 import javax.swing.event.ChangeEvent;
41 import javax.swing.event.ChangeListener;
42
43 import camidion.chordhelper.ButtonIcon;
44 import camidion.chordhelper.ChordHelperApplet;
45 import camidion.chordhelper.mididevice.VirtualMidiDevice;
46 import camidion.chordhelper.music.AbstractNoteTrackSpec;
47 import camidion.chordhelper.music.ChordProgression;
48 import camidion.chordhelper.music.DrumTrackSpec;
49 import camidion.chordhelper.music.FirstTrackSpec;
50 import camidion.chordhelper.music.MelodyTrackSpec;
51 import camidion.chordhelper.music.Range;
52 import camidion.chordhelper.pianokeyboard.PianoKeyboardListener;
53 import camidion.chordhelper.pianokeyboard.PianoKeyboardPanel;
54
55 /**
56  * 新しいMIDIシーケンスを生成するダイアログ
57  */
58 public class NewSequenceDialog extends JDialog {
59         private static final Insets ZERO_INSETS = new Insets(0,0,0,0);
60         private static final Integer[] PPQList = {
61                 48,60,80,96,120,160,192,240,320,384,480,960
62         };
63         private static final String INITIAL_CHORD_STRING =
64                 "Key: C\nC G/B | Am Em/G | F C/E | Dm7 G7 C % | F G7 | Csus4 C\n";
65         private JTextArea chordText = new JTextArea(INITIAL_CHORD_STRING, 18, 30);
66         private JTextField seqNameText = new JTextField();
67         private JComboBox<Integer> ppqComboBox = new JComboBox<Integer>(PPQList);
68         private TimeSignatureSelecter timesigSelecter = new TimeSignatureSelecter();
69         private TempoSelecter tempoSelecter = new TempoSelecter();
70         private MeasureSelecter measureSelecter = new MeasureSelecter();
71         private TrackSpecPanel trackSpecPanel = new TrackSpecPanel() {{
72                 DrumTrackSpec dts = new DrumTrackSpec(9, "Percussion track");
73                 dts.velocity = 127;
74                 addTrackSpec(dts);
75                 MelodyTrackSpec mts;
76                 mts = new MelodyTrackSpec(2, "Bass track", new Range(36,48));
77                 mts.isBass = true;
78                 mts.velocity = 96;
79                 addTrackSpec(mts);
80                 mts =  new MelodyTrackSpec(1, "Chord track", new Range(60,72));
81                 addTrackSpec(mts);
82                 mts = new MelodyTrackSpec(0, "Melody track", new Range(60,84));
83                 mts.randomMelody = true;
84                 mts.beatPattern = 0xFFFF;
85                 mts.continuousBeatPattern = 0x820A;
86                 addTrackSpec(mts);
87         }};
88         /**
89          * ダイアログを開くアクション
90          */
91         public Action openAction = new AbstractAction("New") {
92                 {
93                         String tooltip = "Generate new song - 新しい曲を生成";
94                         putValue(Action.SHORT_DESCRIPTION, tooltip);
95                 }
96                 @Override
97                 public void actionPerformed(ActionEvent e) { setVisible(true); }
98         };
99         private PlaylistTableModel playlist;
100         /**
101          * MIDIシーケンス生成アクション
102          */
103         public Action generateAction = new AbstractAction(
104                 "Generate & Add to PlayList", new ButtonIcon(ButtonIcon.EJECT_ICON)
105         ) {
106                 @Override
107                 public void actionPerformed(ActionEvent event) {
108                         try {
109                                 playlist.addSequenceAndPlay(getMidiSequence());
110                         } catch (InvalidMidiDataException ex) {
111                                 ex.printStackTrace();
112                                 JOptionPane.showMessageDialog(
113                                         NewSequenceDialog.this, ex.getMessage(),
114                                         ChordHelperApplet.VersionInfo.NAME, JOptionPane.ERROR_MESSAGE);
115                         }
116                         setVisible(false);
117                 }
118         };
119         /**
120          * 新しいMIDIシーケンスを生成するダイアログを構築します。
121          * @param playlist シーケンス追加先プレイリスト
122          * @param midiOutDevice 操作音を出力するMIDI出力デバイス
123          */
124         public NewSequenceDialog(PlaylistTableModel playlist, VirtualMidiDevice midiOutDevice) {
125                 this.playlist = playlist;
126                 trackSpecPanel.setChannels(midiOutDevice.getChannels());
127                 setTitle("Generate new sequence - " + ChordHelperApplet.VersionInfo.NAME);
128                 add(new JTabbedPane() {{
129                         add("Sequence", new JPanel() {{
130                                 setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
131                                 add(new JPanel() {{
132                                         setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
133                                         add(new JLabel("Sequence name:"));
134                                         add(seqNameText);
135                                 }});
136                                 add(new JPanel() {{
137                                         setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
138                                         add(new JLabel("Resolution in PPQ ="));
139                                         add(ppqComboBox);
140                                         add(measureSelecter);
141                                 }});
142                                 add(new JButton("Randomize (Tempo, Time signature, Chord progression)") {{
143                                         setMargin(ZERO_INSETS);
144                                         addActionListener(new ActionListener() {
145                                                 @Override
146                                                 public void actionPerformed(ActionEvent e) {
147                                                         setRandomChordProgression(
148                                                                 measureSelecter.getMeasureDuration()
149                                                         );
150                                                 }
151                                         });
152                                 }});
153                                 add(new JPanel() {{
154                                         setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
155                                         add(tempoSelecter);
156                                         add(new JPanel() {{
157                                                 add(new JLabel("Time signature ="));
158                                                 add(timesigSelecter);
159                                         }});
160                                 }});
161                                 add(new JPanel() {{
162                                         setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
163                                         add(new JLabel("Chord progression :"));
164                                         add(new JLabel("Transpose"));
165                                         add(new JButton(" + Up ") {{
166                                                 setMargin(ZERO_INSETS);
167                                                 addActionListener(new ActionListener() {
168                                                         @Override
169                                                         public void actionPerformed(ActionEvent e) {
170                                                                 ChordProgression cp = createChordProgression();
171                                                                 cp.transpose(1);
172                                                                 setChordProgression(cp);
173                                                         }
174                                                 });
175                                         }});
176                                         add(new JButton(" - Down ") {{
177                                                 setMargin(ZERO_INSETS);
178                                                 addActionListener(new ActionListener() {
179                                                         @Override
180                                                         public void actionPerformed(ActionEvent e) {
181                                                                 ChordProgression cp = createChordProgression();
182                                                                 cp.transpose(-1);
183                                                                 setChordProgression(cp);
184                                                         }
185                                                 });
186                                         }});
187                                         add(new JButton(" Enharmonic ") {{
188                                                 setMargin(ZERO_INSETS);
189                                                 addActionListener(new ActionListener() {
190                                                         @Override
191                                                         public void actionPerformed(ActionEvent e) {
192                                                                 ChordProgression cp = createChordProgression();
193                                                                 cp.toggleEnharmonically();
194                                                                 setChordProgression(cp);
195                                                         }
196                                                 });
197                                         }});
198                                         add(new JButton("Relative key") {{
199                                                 setMargin(ZERO_INSETS);
200                                                 addActionListener(new ActionListener() {
201                                                         @Override
202                                                         public void actionPerformed(ActionEvent e) {
203                                                                 ChordProgression cp = createChordProgression();
204                                                                 cp.toggleKeyMajorMinor();
205                                                                 setChordProgression(cp);
206                                                         }
207                                                 });
208                                         }});
209                                 }});
210                                 add(new JScrollPane(chordText));
211                                 add(new JPanel() {{
212                                         setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
213                                         add(new JButton(generateAction){{setMargin(ZERO_INSETS);}});
214                                 }});
215                         }});
216                         add("Track", trackSpecPanel);
217                 }});
218                 setBounds( 250, 200, 600, 540 );
219         }
220         /**
221          * 新しいコード進行を生成して返します。
222          * @return 新しいコード進行
223          */
224         private ChordProgression createChordProgression() {
225                 return new ChordProgression(chordText.getText());
226         }
227         /**
228          * MIDIシーケンスを生成して返します。
229          * @return MIDIシーケンス
230          */
231         public Sequence getMidiSequence() {
232                 FirstTrackSpec firstTrackSpec = new FirstTrackSpec(
233                         seqNameText.getText(),
234                         tempoSelecter.getTempoByteArray(),
235                         timesigSelecter.getByteArray()
236                 );
237                 return createChordProgression().toMidiSequence(
238                         (int)ppqComboBox.getSelectedItem(),
239                         measureSelecter.getStartMeasurePosition(),
240                         measureSelecter.getEndMeasurePosition(),
241                         firstTrackSpec,
242                         trackSpecPanel.getTrackSpecs()
243                 );
244         }
245         /**
246          * コード進行を設定します。テキスト欄に反映されます。
247          * @param cp コード進行
248          */
249         public void setChordProgression(ChordProgression cp) {
250                 chordText.setText(cp.toString());
251         }
252         /**
253          * テンポ・拍子・コード進行をランダムに設定
254          * @param measureLength 小節数
255          */
256         public void setRandomChordProgression(int measureLength) {
257                 tempoSelecter.setTempo( 80 + (int)(Math.random() * 100) );
258                 int timesig_upper = 4;
259                 int timesig_lower_index = 2;
260                 switch( (int)(Math.random() * 10) ) {
261                         case 0: timesig_upper = 3; break; // 3/4
262                 }
263                 timesigSelecter.setValue((byte)timesig_upper, (byte)timesig_lower_index);
264                 setChordProgression(new ChordProgression(measureLength, timesig_upper));
265         }
266         /**
267          * トラック設定画面
268          */
269         private static class TrackSpecPanel extends JPanel
270                 implements PianoKeyboardListener, ActionListener, ChangeListener
271         {
272                 JComboBox<AbstractNoteTrackSpec> trackSelecter = new JComboBox<>();
273                 JLabel trackTypeLabel = new JLabel();
274                 JTextField nameTextField = new JTextField(20);
275                 MidiChannelComboSelecter chSelecter =
276                         new MidiChannelComboSelecter("MIDI Channel:");
277                 MidiProgramSelecter pgSelecter = new MidiProgramSelecter();
278                 MidiProgramFamilySelecter pgFamilySelecter =
279                         new MidiProgramFamilySelecter(pgSelecter) {{
280                                 pgSelecter.setFamilySelecter(pgFamilySelecter);
281                         }};
282                 PianoKeyboardPanel keyboardPanel = new PianoKeyboardPanel() {{
283                         keyboard.octaveSizeModel.setValue(6);
284                         keyboard.setPreferredSize(new Dimension(400,40));
285                         keyboard.setMaxSelectable(2);
286                 }};
287                 JPanel rangePanel = new JPanel() {{
288                         add( new JLabel("Range:") );
289                         add(keyboardPanel);
290                 }};
291                 JCheckBox randomMelodyCheckbox = new JCheckBox("Random melody");
292                 JCheckBox bassCheckbox = new JCheckBox("Bass note");
293                 JCheckBox randomLyricCheckbox = new JCheckBox("Random lyrics");
294                 JCheckBox nsx39Checkbox = new JCheckBox("NSX-39");;
295                 BeatPadPanel beatPadPanel = new BeatPadPanel(this);
296                 private MidiChannel[] midiChannels;
297
298                 public TrackSpecPanel() {
299                         nameTextField.addActionListener(this);
300                         keyboardPanel.keyboard.addPianoKeyboardListener(this);
301                         add(new JPanel() {{
302                                 add(new JLabel("Track select:"));
303                                 add(trackSelecter);
304                         }});
305                         add(trackTypeLabel);
306                         add(new JPanel() {{
307                                 add(new JLabel("Track name (Press [Enter] key to change):"));
308                                 add(nameTextField);
309                         }});
310                         add(chSelecter);
311                         add(new VelocitySelecter(keyboardPanel.keyboard.velocityModel));
312                         add(new JPanel() {{
313                                 add(pgFamilySelecter);
314                                 add(pgSelecter);
315                         }});
316                         add(rangePanel);
317                         bassCheckbox.addChangeListener(this);
318                         add(bassCheckbox);
319                         randomMelodyCheckbox.addChangeListener(this);
320                         add(randomMelodyCheckbox);
321                         randomLyricCheckbox.addChangeListener(this);
322                         add(randomLyricCheckbox);
323                         nsx39Checkbox.addChangeListener(this);
324                         add(nsx39Checkbox);
325                         add(beatPadPanel);
326                         trackSelecter.addActionListener(this);
327                         chSelecter.comboBox.addActionListener(this);
328                         keyboardPanel.keyboard.velocityModel.addChangeListener(
329                                 new ChangeListener() {
330                                         public void stateChanged(ChangeEvent e) {
331                                                 AbstractNoteTrackSpec ants = getTrackSpec();
332                                                 ants.velocity = keyboardPanel.keyboard.velocityModel.getValue();
333                                         }
334                                 }
335                         );
336                         pgSelecter.addActionListener(this);
337                 }
338                 @Override
339                 public void stateChanged(ChangeEvent e) {
340                         Object src = e.getSource();
341                         if( src == bassCheckbox ) {
342                                 AbstractNoteTrackSpec ants = getTrackSpec();
343                                 if( ants instanceof MelodyTrackSpec ) {
344                                         MelodyTrackSpec mts = (MelodyTrackSpec)ants;
345                                         mts.isBass = bassCheckbox.isSelected();
346                                 }
347                         }
348                         else if( src == randomMelodyCheckbox ) {
349                                 AbstractNoteTrackSpec ants = getTrackSpec();
350                                 if( ants instanceof MelodyTrackSpec ) {
351                                         MelodyTrackSpec mts = (MelodyTrackSpec)ants;
352                                         mts.randomMelody = randomMelodyCheckbox.isSelected();
353                                 }
354                         }
355                         else if( src == randomLyricCheckbox ) {
356                                 AbstractNoteTrackSpec ants = getTrackSpec();
357                                 if( ants instanceof MelodyTrackSpec ) {
358                                         MelodyTrackSpec mts = (MelodyTrackSpec)ants;
359                                         mts.randomLyric = randomLyricCheckbox.isSelected();
360                                 }
361                         }
362                         else if( src == nsx39Checkbox ) {
363                                 AbstractNoteTrackSpec ants = getTrackSpec();
364                                 if( ants instanceof MelodyTrackSpec ) {
365                                         MelodyTrackSpec mts = (MelodyTrackSpec)ants;
366                                         mts.nsx39 = nsx39Checkbox.isSelected();
367                                 }
368                         }
369                 }
370                 @Override
371                 public void actionPerformed(ActionEvent e) {
372                         Object src = e.getSource();
373                         AbstractNoteTrackSpec ants;
374                         if( src == nameTextField ) {
375                                 getTrackSpec().name = nameTextField.getText();
376                         }
377                         else if( src == trackSelecter ) {
378                                 ants = (AbstractNoteTrackSpec)(trackSelecter.getSelectedItem());
379                                 String trackTypeString = "Track type: " + (
380                                         ants instanceof DrumTrackSpec ? "Percussion" :
381                                         ants instanceof MelodyTrackSpec ? "Melody" : "(Unknown)"
382                                 );
383                                 trackTypeLabel.setText(trackTypeString);
384                                 nameTextField.setText(ants.name);
385                                 chSelecter.setSelectedChannel(ants.midiChannel);
386                                 keyboardPanel.keyboard.velocityModel.setValue(ants.velocity);
387                                 pgSelecter.setProgram(ants.programNumber);
388                                 keyboardPanel.keyboard.clear();
389                                 if( ants instanceof DrumTrackSpec ) {
390                                         rangePanel.setVisible(false);
391                                         randomMelodyCheckbox.setVisible(false);
392                                         randomLyricCheckbox.setVisible(false);
393                                         nsx39Checkbox.setVisible(false);
394                                         bassCheckbox.setVisible(false);
395                                 }
396                                 else if( ants instanceof MelodyTrackSpec ) {
397                                         MelodyTrackSpec ts = (MelodyTrackSpec)ants;
398                                         rangePanel.setVisible(true);
399                                         keyboardPanel.keyboard.setSelectedNote(ts.range.min_note);
400                                         keyboardPanel.keyboard.setSelectedNote(ts.range.max_note);
401                                         keyboardPanel.keyboard.autoScroll(ts.range.min_note);
402                                         randomMelodyCheckbox.setSelected(ts.randomMelody);
403                                         randomLyricCheckbox.setSelected(ts.randomLyric);
404                                         bassCheckbox.setSelected(ts.isBass);
405                                         randomMelodyCheckbox.setVisible(true);
406                                         randomLyricCheckbox.setVisible(true);
407                                         nsx39Checkbox.setVisible(true);
408                                         bassCheckbox.setVisible(true);
409                                 }
410                                 beatPadPanel.setTrackSpec(ants);
411                         }
412                         else if( src == chSelecter.comboBox ) {
413                                 getTrackSpec().midiChannel = chSelecter.getSelectedChannel();
414                         }
415                         else if( src == pgSelecter ) {
416                                 getTrackSpec().programNumber = pgSelecter.getProgram();
417                         }
418                 }
419                 @Override
420                 public void pianoKeyPressed(int n, InputEvent e) {
421                         noteOn(n);
422                         AbstractNoteTrackSpec ants = getTrackSpec();
423                         if( ants instanceof MelodyTrackSpec ) {
424                                 MelodyTrackSpec ts = (MelodyTrackSpec)ants;
425                                 ts.range = new Range(keyboardPanel.keyboard.getSelectedNotes());
426                         }
427                 }
428                 @Override
429                 public void pianoKeyReleased(int n, InputEvent e) { noteOff(n); }
430                 public void octaveMoved(ChangeEvent event) {}
431                 public void octaveResized(ChangeEvent event) {}
432                 public void noteOn(int n) {
433                         if( midiChannels == null ) return;
434                         MidiChannel mc = midiChannels[chSelecter.getSelectedChannel()];
435                         mc.noteOn( n, keyboardPanel.keyboard.velocityModel.getValue() );
436                 }
437                 public void noteOff(int n) {
438                         if( midiChannels == null ) return;
439                         MidiChannel mc = midiChannels[chSelecter.getSelectedChannel()];
440                         mc.noteOff( n, keyboardPanel.keyboard.velocityModel.getValue() );
441                 }
442                 public void setChannels( MidiChannel midiChannels[] ) {
443                         this.midiChannels = midiChannels;
444                 }
445                 public AbstractNoteTrackSpec getTrackSpec() {
446                         Object trackSpecObj = trackSelecter.getSelectedItem();
447                         AbstractNoteTrackSpec ants = (AbstractNoteTrackSpec)trackSpecObj;
448                         ants.name = nameTextField.getText();
449                         return ants;
450                 }
451                 public Vector<AbstractNoteTrackSpec> getTrackSpecs() {
452                         Vector<AbstractNoteTrackSpec> trackSpecs = new Vector<>();
453                         int i=0, n_items = trackSelecter.getItemCount();
454                         while( i < n_items ) {
455                                 trackSpecs.add((AbstractNoteTrackSpec)trackSelecter.getItemAt(i++));
456                         }
457                         return trackSpecs;
458                 }
459                 public void addTrackSpec(AbstractNoteTrackSpec trackSpec) {
460                         trackSelecter.addItem(trackSpec);
461                 }
462         }
463         private static class MeasureSelecter extends JPanel {
464                 public MeasureSelecter() {
465                         setLayout(new GridLayout(2,3));
466                         add(new JLabel());
467                         add(new JLabel("Start",JLabel.CENTER));
468                         add(new JLabel("End",JLabel.CENTER));
469                         add(new JLabel("Measure",JLabel.RIGHT));
470                         add(new JSpinner(startModel));
471                         add(new JSpinner(endModel));
472                 }
473                 private SpinnerNumberModel startModel = new SpinnerNumberModel( 3, 1, 9999, 1 );
474                 private SpinnerNumberModel endModel = new SpinnerNumberModel( 8, 1, 9999, 1 );
475                 public int getStartMeasurePosition() {
476                         return startModel.getNumber().intValue();
477                 }
478                 public int getEndMeasurePosition() {
479                         return endModel.getNumber().intValue();
480                 }
481                 public int getMeasureDuration() {
482                         return getEndMeasurePosition() - getStartMeasurePosition() + 1;
483                 }
484         }
485         //////////////////////////////////////////////////////////////////
486         //
487         // □=□=□=□=□=□=□=□=
488         // □=□=□=□=□=□=□=□=
489         //
490         private static class BeatPadPanel extends JPanel implements ActionListener {
491                 PianoKeyboardListener piano_keyboard_listener;
492                 JPanel percussion_selecters_panel;
493                 java.util.List<JComboBox<String>> percussionSelecters =
494                         new ArrayList<JComboBox<String>>() {
495                                 {
496                                         for( int i=0; i < DrumTrackSpec.defaultPercussions.length; i++  ) {
497                                                 add(new JComboBox<String>());
498                                         }
499                                 }
500                         };
501                 BeatPad beat_pad;
502
503                 public BeatPadPanel(PianoKeyboardListener pkl) {
504                         piano_keyboard_listener = pkl;
505                         percussion_selecters_panel = new JPanel();
506                         percussion_selecters_panel.setLayout(
507                                 new BoxLayout( percussion_selecters_panel, BoxLayout.Y_AXIS )
508                         );
509                         for( JComboBox<String> cb : percussionSelecters ) {
510                                 percussion_selecters_panel.add(cb);
511                                 cb.addActionListener(this);
512                         }
513                         add( percussion_selecters_panel );
514                         add( beat_pad = new BeatPad(pkl) );
515                         beat_pad.setPreferredSize( new Dimension(400,200) );
516                         setLayout( new BoxLayout( this, BoxLayout.X_AXIS ) );
517                 }
518                 public void actionPerformed(ActionEvent e) {
519                         Object src = e.getSource();
520                         for( JComboBox<String> cb : percussionSelecters ) {
521                                 if( src != cb ) continue;
522                                 int note_no = (
523                                         (DrumTrackSpec.PercussionComboBoxModel)cb.getModel()
524                                 ).getSelectedNoteNo();
525                                 piano_keyboard_listener.pianoKeyPressed(note_no,(InputEvent)null);
526                         }
527                 }
528                 public void setTrackSpec( AbstractNoteTrackSpec ants ) {
529                         beat_pad.setTrackSpec(ants);
530                         if( ants instanceof DrumTrackSpec ) {
531                                 DrumTrackSpec dts = (DrumTrackSpec)ants;
532                                 int i=0;
533                                 for( JComboBox<String> cb : percussionSelecters ) {
534                                         cb.setModel(dts.models[i++]);
535                                 }
536                                 percussion_selecters_panel.setVisible(true);
537                         }
538                         else if( ants instanceof MelodyTrackSpec ) {
539                                 percussion_selecters_panel.setVisible(false);
540                         }
541                 }
542         }
543         private static class BeatPad extends JComponent implements MouseListener, ComponentListener {
544                 PianoKeyboardListener piano_keyboard_listener;
545                 private int on_note_no = -1;
546                 AbstractNoteTrackSpec track_spec;
547
548                 public static final int MAX_BEATS = 16;
549                 Rectangle beat_buttons[][];
550                 Rectangle continuous_beat_buttons[][];
551
552                 public BeatPad(PianoKeyboardListener pkl) {
553                         piano_keyboard_listener = pkl;
554                         addMouseListener(this);
555                         addComponentListener(this);
556                         // addMouseMotionListener(this);
557                 }
558                 public void paint(Graphics g) {
559                         super.paint(g);
560                         Graphics2D g2 = (Graphics2D) g;
561                         Rectangle r;
562                         int note, beat, mask;
563
564                         if( track_spec instanceof DrumTrackSpec ) {
565                                 DrumTrackSpec dts = (DrumTrackSpec)track_spec;
566                                 for( note=0; note<dts.beat_patterns.length; note++ ) {
567                                         for( beat=0, mask=0x8000; beat<MAX_BEATS; beat++, mask >>>= 1 ) {
568                                                 r = beat_buttons[note][beat];
569                                                 if( (dts.beat_patterns[note] & mask) != 0 )
570                                                         g2.fillRect( r.x, r.y, r.width, r.height );
571                                                 else
572                                                         g2.drawRect( r.x, r.y, r.width, r.height );
573                                         }
574                                 }
575                         }
576                         else if( track_spec instanceof MelodyTrackSpec ) {
577                                 MelodyTrackSpec mts = (MelodyTrackSpec)track_spec;
578                                 for( beat=0, mask=0x8000; beat<MAX_BEATS; beat++, mask >>>= 1 ) {
579                                         r = beat_buttons[0][beat];
580                                         if( (mts.beatPattern & mask) != 0 )
581                                                 g2.fillRect( r.x, r.y, r.width, r.height );
582                                         else
583                                                 g2.drawRect( r.x, r.y, r.width, r.height );
584                                         r = continuous_beat_buttons[0][beat];
585                                         if( (mts.continuousBeatPattern & mask) != 0 )
586                                                 g2.fillRect( r.x, r.y, r.width, r.height );
587                                         else
588                                                 g2.drawRect( r.x, r.y, r.width, r.height );
589                                 }
590                         }
591
592                 }
593                 public void componentShown(ComponentEvent e) { }
594                 public void componentHidden(ComponentEvent e) { }
595                 public void componentMoved(ComponentEvent e) { }
596                 public void componentResized(ComponentEvent e) {
597                         sizeChanged();
598                 }
599                 public void mousePressed(MouseEvent e) {
600                         catchEvent(e);
601                         if( on_note_no >= 0 ) {
602                                 piano_keyboard_listener.pianoKeyPressed( on_note_no ,(InputEvent)e );
603                         }
604                 }
605                 public void mouseReleased(MouseEvent e) {
606                         if( on_note_no >= 0 ) {
607                                 piano_keyboard_listener.pianoKeyReleased( on_note_no ,(InputEvent)e );
608                         }
609                         on_note_no = -1;
610                 }
611                 public void mouseEntered(MouseEvent e) {
612                         if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) {
613                                 catchEvent(e);
614                         }
615                 }
616                 public void mouseExited(MouseEvent e) { }
617                 public void mouseClicked(MouseEvent e) { }
618                 private void sizeChanged() {
619                         int beat, note, width, height;
620                         Dimension d = getSize();
621                         int num_notes = 1;
622                         if( track_spec instanceof DrumTrackSpec ) {
623                                 DrumTrackSpec dts = (DrumTrackSpec)track_spec;
624                                 num_notes = dts.models.length;
625                         }
626                         beat_buttons = new Rectangle[num_notes][];
627                         continuous_beat_buttons = new Rectangle[num_notes][];
628                         for( note=0; note<beat_buttons.length; note++ ) {
629                                 beat_buttons[note] = new Rectangle[MAX_BEATS];
630                                 continuous_beat_buttons[note] = new Rectangle[MAX_BEATS];
631                                 for( beat=0; beat<MAX_BEATS; beat++ ) {
632                                         width = (d.width * 3) / (MAX_BEATS * 4);
633                                         height = d.height / num_notes - 1;
634                                         beat_buttons[note][beat] = new Rectangle(
635                                                 beat * d.width / MAX_BEATS,
636                                                 note * height,
637                                                 width,
638                                                 height
639                                         );
640                                         width = d.width / (MAX_BEATS * 3);
641                                         continuous_beat_buttons[note][beat] = new Rectangle(
642                                                 (beat+1) * d.width / MAX_BEATS - width + 1,
643                                                 note * height + height / 3,
644                                                 width-1,
645                                                 height / 3
646                                         );
647                                 }
648                         }
649                 }
650                 private void catchEvent(MouseEvent e) {
651                         Point point = e.getPoint();
652                         int note, beat, mask;
653
654                         // ビートパターンのビットを反転
655                         if( track_spec instanceof DrumTrackSpec ) {
656                                 DrumTrackSpec dts = (DrumTrackSpec)track_spec;
657                                 for( note=0; note<dts.beat_patterns.length; note++ ) {
658                                         for( beat=0, mask=0x8000; beat<MAX_BEATS; beat++, mask >>>= 1 ) {
659                                                 if( beat_buttons[note][beat].contains(point) ) {
660                                                         dts.beat_patterns[note] ^= mask;
661                                                         on_note_no = dts.models[note].getSelectedNoteNo();
662                                                         repaint(); return;
663                                                 }
664                                         }
665                                 }
666                         }
667                         else if( track_spec instanceof MelodyTrackSpec ) {
668                                 MelodyTrackSpec mts = (MelodyTrackSpec)track_spec;
669                                 for( beat=0, mask=0x8000; beat<MAX_BEATS; beat++, mask >>>= 1 ) {
670                                         if( beat_buttons[0][beat].contains(point) ) {
671                                                 mts.beatPattern ^= mask;
672                                                 repaint(); return;
673                                         }
674                                         if( continuous_beat_buttons[0][beat].contains(point) ) {
675                                                 mts.continuousBeatPattern ^= mask;
676                                                 repaint(); return;
677                                         }
678                                 }
679                         }
680                 }
681                 public void setTrackSpec( AbstractNoteTrackSpec ants ) {
682                         track_spec = ants;
683                         sizeChanged();
684                         repaint();
685                 }
686         }
687 }