/[svn]/jsampler/trunk/src/org/jsampler/view/fantasia/PianoKeyboardPane.java
ViewVC logotype

Contents of /jsampler/trunk/src/org/jsampler/view/fantasia/PianoKeyboardPane.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1778 - (show annotations) (download)
Sun Sep 28 20:38:36 2008 UTC (15 years, 6 months ago) by iliev
File size: 16032 byte(s)
* Fantasia: Improved look and feel
* Fantasia: Added buttons for increasing/decreasing the key number
  of the MIDI keyboard (Ctrl+Up Arrow/Ctrl+Down Arrow)
* Fantasia: Added buttons for scrolling left/right on the MIDI keyboard
  (Ctrl+Left Arrow/Ctrl+Right Arrow)
* Added key bindings for triggering MIDI notes using the computer keyboard
  (from a to ' for the white keys and from 0 to 7 for changing the octaves)
* Notes are now triggered when dragging the mouse over the MIDI keyboard

1 /*
2 * JSampler - a java front-end for LinuxSampler
3 *
4 * Copyright (C) 2005-2008 Grigor Iliev <grigor@grigoriliev.com>
5 *
6 * This file is part of JSampler.
7 *
8 * JSampler is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation.
11 *
12 * JSampler is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with JSampler; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 * MA 02111-1307 USA
21 */
22
23 package org.jsampler.view.fantasia;
24
25 import java.awt.AlphaComposite;
26 import java.awt.Color;
27 import java.awt.Composite;
28 import java.awt.Dimension;
29 import java.awt.GradientPaint;
30 import java.awt.Graphics;
31 import java.awt.Graphics2D;
32 import java.awt.GridBagConstraints;
33 import java.awt.GridBagLayout;
34 import java.awt.Insets;
35 import java.awt.Paint;
36 import java.awt.RenderingHints;
37
38 import java.awt.event.ActionEvent;
39 import java.awt.event.ActionListener;
40 import java.awt.event.FocusEvent;
41 import java.awt.event.FocusListener;
42 import java.awt.event.MouseEvent;
43 import java.awt.event.MouseAdapter;
44
45 import java.awt.geom.Line2D;
46 import java.awt.geom.Rectangle2D;
47
48 import java.beans.PropertyChangeEvent;
49 import java.beans.PropertyChangeListener;
50
51 import javax.swing.BorderFactory;
52 import javax.swing.Box;
53 import javax.swing.BoxLayout;
54 import javax.swing.ImageIcon;
55 import javax.swing.JButton;
56 import javax.swing.JPanel;
57 import javax.swing.JToggleButton;
58
59 import javax.swing.event.ListSelectionEvent;
60 import javax.swing.event.ListSelectionListener;
61
62 import net.sf.juife.event.TaskEvent;
63 import net.sf.juife.event.TaskListener;
64
65 import org.jsampler.CC;
66 import org.jsampler.SamplerChannelModel;
67
68 import org.jsampler.event.SamplerChannelAdapter;
69 import org.jsampler.event.SamplerChannelEvent;
70 import org.jsampler.event.SamplerChannelListListener;
71 import org.jsampler.event.SamplerChannelListEvent;
72
73 import org.jsampler.view.JSChannel;
74 import org.jsampler.view.std.JSPianoRoll;
75
76 import org.linuxsampler.lscp.Instrument;
77 import org.linuxsampler.lscp.SamplerChannel;
78
79 import org.linuxsampler.lscp.event.MidiDataEvent;
80 import org.linuxsampler.lscp.event.MidiDataListener;
81
82 import static javax.swing.Action.SMALL_ICON;
83 import static org.jsampler.task.Global.GetFileInstrument;
84 import static org.jsampler.view.fantasia.FantasiaI18n.i18n;
85
86 /**
87 *
88 * @author Grigor Iliev
89 */
90 public class PianoKeyboardPane extends FantasiaPanel
91 implements ListSelectionListener, SamplerChannelListListener {
92
93 protected final JToggleButton btnPower = new PowerButton();
94 private final FantasiaLabel lDisplay = new FantasiaLabel(" ", true);
95 private final JSPianoRoll pianoRoll = new JSPianoRoll();
96 private SamplerChannelModel channel = null;
97
98 private String file = null;
99 private int index = -1;
100
101 public
102 PianoKeyboardPane() {
103 pianoRoll.actionDecreaseKeyNumber.putValue(SMALL_ICON, Res.gfxBtnDecrease);
104 pianoRoll.actionIncreaseKeyNumber.putValue(SMALL_ICON, Res.gfxBtnIncrease);
105 pianoRoll.actionScrollLeft.putValue(SMALL_ICON, Res.gfxBtnScrollLeft);
106 pianoRoll.actionScrollRight.putValue(SMALL_ICON, Res.gfxBtnScrollRight);
107
108 setOpaque(false);
109
110 GridBagLayout gridbag = new GridBagLayout();
111 GridBagConstraints c = new GridBagConstraints();
112
113 setLayout(gridbag);
114
115 setBorder(BorderFactory.createEmptyBorder(0, 3, 5, 3));
116
117 c.fill = GridBagConstraints.NONE;
118
119 c.gridx = 0;
120 c.gridy = 0;
121 c.anchor = GridBagConstraints.NORTHWEST;
122 c.insets = new Insets(3, 3, 3, 3);
123 gridbag.setConstraints(btnPower, c);
124 add(btnPower);
125
126 JPanel p = createVSeparator();
127 c.gridx = 1;
128 c.gridy = 0;
129 c.gridheight = 2;
130 c.insets = new Insets(3, 0, 5, 3);
131 c.fill = GridBagConstraints.VERTICAL;
132 c.weighty = 1.0;
133 gridbag.setConstraints(p, c);
134 add(p);
135
136 p = new KeyRangePropsPane();
137
138 c.gridx = 2;
139 c.gridy = 0;
140 c.gridheight = 1;
141 c.insets = new Insets(0, 0, 0, 0);
142 c.fill = GridBagConstraints.NONE;
143 c.weighty = 0.0;
144 c.anchor = GridBagConstraints.WEST;
145 gridbag.setConstraints(p, c);
146 add(p);
147
148 p = new KeyRangePropsPane();
149
150 c.gridx = 6;
151 c.gridy = 0;
152 c.anchor = GridBagConstraints.EAST;
153 c.insets = new Insets(0, 0, 0, 10);
154 gridbag.setConstraints(p, c);
155 add(p);
156
157 lDisplay.setPreferredSize(new Dimension(300, lDisplay.getPreferredSize().height));
158
159 c.gridx = 4;
160 c.gridy = 0;
161 c.insets = new Insets(3, 3, 3, 12);
162 c.fill = GridBagConstraints.NONE;
163 c.weightx = 0.0;
164 gridbag.setConstraints(lDisplay, c);
165 add(lDisplay);
166
167 p = new JPanel();
168 p.setOpaque(false);
169 c.gridx = 3;
170 c.gridy = 0;
171 c.insets = new Insets(0, 0, 0, 0);
172 c.fill = GridBagConstraints.HORIZONTAL;
173 c.weightx = 1.0;
174 gridbag.setConstraints(p, c);
175 add(p);
176
177 p = new JPanel();
178 p.setOpaque(false);
179 c.gridx = 5;
180 c.gridy = 0;
181 gridbag.setConstraints(p, c);
182 add(p);
183
184 pianoRoll.setFocusable(false);
185 pianoRoll.setBackground(new Color(0x2e2e2e));
186 //pianoRoll.setOpaque(false);
187
188 disablePianoRoll();
189
190 c.gridx = 2;
191 c.gridy = 1;
192 c.gridwidth = 5;
193 c.insets = new Insets(0, 3, 0, 12);
194 c.fill = GridBagConstraints.BOTH;
195 c.weightx = 1.0;
196 c.weighty = 1.0;
197 gridbag.setConstraints(pianoRoll, c);
198 add(pianoRoll);
199
200 CC.getSamplerModel().addSamplerChannelListListener(this);
201 pianoRoll.addMidiDataListener(getHandler());
202
203 updateKeyRange();
204
205 PropertyChangeListener l = new PropertyChangeListener() {
206 public void
207 propertyChange(PropertyChangeEvent e) {
208 updateKeyRange();
209 }
210 };
211
212 CC.preferences().addPropertyChangeListener("midiKeyboard.firstKey", l);
213 CC.preferences().addPropertyChangeListener("midiKeyboard.lastKey", l);
214
215 addFocusListener(getHandler());
216
217 MouseAdapter l2 = new MouseAdapter() {
218 public void
219 mouseClicked(MouseEvent e) { requestFocusInWindow(); }
220 };
221
222 addKeyListener(pianoRoll.getKeyListener());
223 pianoRoll.registerKeys(this);
224
225 addMouseListener(l2);
226 pianoRoll.addMouseListener(l2);
227 lDisplay.addMouseListener(l2);
228 }
229
230 public JSPianoRoll
231 getPianoRoll() { return pianoRoll; }
232
233 private void
234 updateKeyRange() {
235 int firstKey = CC.preferences().getIntProperty("midiKeyboard.firstKey");
236 int lastKey = CC.preferences().getIntProperty("midiKeyboard.lastKey");
237 pianoRoll.setKeyRange(firstKey, lastKey);
238 }
239
240 @Override public void
241 valueChanged(ListSelectionEvent e) {
242 if(e.getValueIsAdjusting()) return;
243
244 JSChannel[] chnS = CC.getMainFrame().getChannelsPane(0).getSelectedChannels();
245 if(chnS == null || chnS.length == 0) {
246 disconnectChannel();
247 return;
248 }
249
250 if(chnS[0].getModel() == channel) return;
251 disconnectChannel();
252 connectChannel(chnS[0].getModel());
253 }
254
255 private void
256 updateInstrumentInfo() {
257 final GetFileInstrument i = new GetFileInstrument(file, index);
258
259 i.addTaskListener(new TaskListener() {
260 public void
261 taskPerformed(TaskEvent e) {
262 if(i.doneWithErrors()) return;
263 updatePianoKeyboard(i.getResult());
264 }
265 });
266
267 CC.getTaskQueue().add(i);
268 }
269
270 private void
271 disablePianoRoll() {
272 pianoRoll.reset(true);
273 pianoRoll.setPlayingEnabled(false);
274 }
275
276 private void
277 updatePianoKeyboard(Instrument instr) {
278 // The selected channel may have changed before this update
279 // due to concurency, so also checking whether file is null
280 if(instr == null || file == null) {
281 disablePianoRoll();
282 return;
283 }
284
285 pianoRoll.setPlayingEnabled(true);
286
287 pianoRoll.setShouldRepaint(false);
288 pianoRoll.reset(true);
289 pianoRoll.setDisabled(instr.getKeyMapping(), false);
290 pianoRoll.setKeyswitches(instr.getKeyswitchMapping(), true);
291 pianoRoll.setShouldRepaint(true);
292 pianoRoll.repaint();
293 }
294
295 private void
296 connectChannel(SamplerChannelModel chn) {
297 channel = chn;
298 channel.addMidiDataListener(pianoRoll);
299 channel.addSamplerChannelListener(getHandler());
300
301 updateDisplay();
302
303 file = channel.getChannelInfo().getInstrumentFile();
304 if(file == null) {
305 disablePianoRoll();
306 return;
307 }
308 index = channel.getChannelInfo().getInstrumentIndex();
309 updateInstrumentInfo();
310 }
311
312 private void
313 disconnectChannel() {
314 if(channel != null) {
315 channel.removeMidiDataListener(pianoRoll);
316 channel.removeSamplerChannelListener(getHandler());
317 channel = null;
318 file = null;
319 index = -1;
320 disablePianoRoll();
321 }
322
323 updateDisplay();
324 }
325
326 @Override public void
327 channelAdded(SamplerChannelListEvent e) {
328 updateDisplay();
329 }
330
331 @Override public void
332 channelRemoved(SamplerChannelListEvent e) {
333 if(e.getChannelModel() == channel) {
334 disconnectChannel();
335 }
336
337 updateDisplay();
338 }
339
340 private JPanel
341 createVSeparator() {
342 PixmapPane p = new PixmapPane(Res.gfxVLine);
343 p.setAlignmentY(JPanel.TOP_ALIGNMENT);
344 p.setPreferredSize(new Dimension(2, 60));
345 p.setMaximumSize(new Dimension(2, Short.MAX_VALUE));
346 return p;
347 }
348
349 private Color color1 = new Color(0x7a7a7a);
350 private Color color2 = new Color(0x5e5e5e);
351 private Color color3 = new Color(0x2e2e2e);
352
353 @Override
354 public void
355 paintComponent(Graphics g) {
356 Graphics2D g2 = (Graphics2D)g;
357
358 Paint oldPaint = g2.getPaint();
359 Composite oldComposite = g2.getComposite();
360
361 Insets insets = this.getInsets();
362 double x1 = insets.left;
363 double y1 = insets.top;
364
365 double w = getSize().getWidth();
366 double x2 = w - insets.right - 1;
367 double h = getSize().getHeight();
368 double y2 = h - insets.bottom - 1;
369
370 FantasiaPainter.paintGradient(g2, x1, y1, x2, y2 - 10, color1, color2);
371
372 g2.setRenderingHint (
373 RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF
374 );
375
376 double y3 = y2 - 10;
377 if(y3 < 0) y3 = 0;
378
379 Rectangle2D.Double rect = new Rectangle2D.Double(x1, y3, x2 - x1 + 1, 11);
380
381 GradientPaint gr = new GradientPaint (
382 0.0f, (float)y3, color2,
383 0.0f, (float)h, color3
384 );
385
386 g2.setPaint(gr);
387 g2.fill(rect);
388
389 drawOutBorder(g2, x1, y1, x2, y2);
390
391 double prX = pianoRoll.getLocation().getX();
392 double prY = pianoRoll.getLocation().getY();
393 drawInBorder (
394 g2, prX - 2, prY - 2,
395 prX + pianoRoll.getSize().getWidth() + 1, prY + pianoRoll.getSize().getHeight() - 3
396 );
397
398 g2.setPaint(oldPaint);
399 g2.setComposite(oldComposite);
400 }
401
402 private void
403 drawOutBorder(Graphics2D g2, double x1, double y1, double x2, double y2) {
404 AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.40f);
405 g2.setComposite(ac);
406
407 g2.setPaint(Color.WHITE);
408 Line2D.Double l = new Line2D.Double(x1, y1, x2, y1);
409 g2.draw(l);
410
411 g2.setComposite(ac.derive(0.20f));
412 l = new Line2D.Double(x1, y1 + 1, x2, y1 + 1);
413 g2.draw(l);
414
415 g2.setComposite(ac.derive(0.255f));
416
417 l = new Line2D.Double(x1, y1, x1, y2);
418 g2.draw(l);
419
420 g2.setComposite(ac.derive(0.40f));
421 g2.setPaint(Color.BLACK);
422
423 //l = new Line2D.Double(x1, y2, x2, y2);
424 //g2.draw(l);
425
426 g2.setComposite(ac.derive(0.20f));
427
428 l = new Line2D.Double(x2, y1, x2, y2);
429 g2.draw(l);
430 }
431
432 private void
433 drawInBorder(Graphics2D g2, double x1, double y1, double x2, double y2) {
434 AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.40f);
435 g2.setComposite(ac);
436
437 g2.setPaint(Color.WHITE);
438 Line2D.Double l = new Line2D.Double(x1, y2, x2, y2);
439 //g2.draw(l);
440
441 g2.setComposite(ac.derive(0.255f));
442
443 l = new Line2D.Double(x2 - 1, y1 + 1, x2 - 1, y2 + 1);
444 g2.draw(l);
445
446 g2.setComposite(ac.derive(0.13f));
447
448 l = new Line2D.Double(x2, y1, x2, y2);
449 g2.draw(l);
450
451 g2.setComposite(ac.derive(0.20f));
452 g2.setPaint(Color.BLACK);
453
454 l = new Line2D.Double(x1, y1, x2, y1);
455 g2.draw(l);
456
457 g2.setComposite(ac.derive(0.40f));
458 g2.setPaint(Color.BLACK);
459
460 l = new Line2D.Double(x1 + 1, y1 + 1, x2 - 1, y1 + 1);
461 g2.draw(l);
462
463 g2.setComposite(ac.derive(0.20f));
464
465 l = new Line2D.Double(x1, y1, x1, y2);
466 g2.draw(l);
467
468 g2.setComposite(ac.derive(0.40f));
469
470 l = new Line2D.Double(x1 + 1, y1 + 1, x1 + 1, y2 + 1);
471 g2.draw(l);
472 }
473
474 private void
475 updateDisplay() {
476 // TODO: called too often?
477 if(channel == null) {
478 lDisplay.setText(i18n.getLabel("PianoKeyboardPane.noChannel"));
479 return;
480 }
481
482 SamplerChannel sc = channel.getChannelInfo();
483 int i = CC.getSamplerModel().getChannelIndex(channel) + 1;
484
485 StringBuffer sb = new StringBuffer();
486 sb.append(i18n.getLabel("PianoKeyboardPane.channel", i)).append(" - ");
487
488 int status = sc.getInstrumentStatus();
489 if(status >= 0 && status < 100) {
490 sb.append(i18n.getLabel("ChannelScreen.loadingInstrument", status));
491 } else if(status == -1) {
492 sb.append(i18n.getLabel("PianoKeyboardPane.noInstrument"));
493 } else if(status < -1) {
494 sb.append(i18n.getLabel("ChannelScreen.errorLoadingInstrument"));
495 } else {
496 if(sc.getInstrumentName() != null) sb.append(sc.getInstrumentName());
497 }
498
499 lDisplay.setText(sb.toString());
500 }
501
502
503 class KeyRangePropsPane extends JPanel {
504 private final JButton btnIncrease =
505 new PixmapButton(pianoRoll.actionIncreaseKeyNumber, Res.gfxBtnIncrease);
506
507 private final JButton btnDecrease =
508 new PixmapButton(pianoRoll.actionDecreaseKeyNumber, Res.gfxBtnDecrease);
509
510 private final JButton btnScrollLeft =
511 new PixmapButton(pianoRoll.actionScrollLeft, Res.gfxBtnScrollLeft);
512
513 private final JButton btnScrollRight =
514 new PixmapButton(pianoRoll.actionScrollRight, Res.gfxBtnScrollRight);
515
516 KeyRangePropsPane() {
517 setOpaque(false);
518 setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
519
520 btnIncrease.setFocusable(false);
521 btnDecrease.setFocusable(false);
522 btnScrollLeft.setFocusable(false);
523 btnScrollRight.setFocusable(false);
524
525
526 btnDecrease.setPressedIcon(Res.gfxBtnDecreaseRO);
527 add(btnDecrease);
528
529 btnIncrease.setPressedIcon(Res.gfxBtnIncreaseRO);
530 add(btnIncrease);
531
532 add(Box.createRigidArea(new Dimension(6, 0)));
533
534 btnScrollLeft.setPressedIcon(Res.gfxBtnScrollLeftRO);
535 add(btnScrollLeft);
536
537 btnScrollRight.setPressedIcon(Res.gfxBtnScrollRightRO);
538 add(btnScrollRight);
539 }
540 }
541
542
543 public static class PowerButton extends PixmapToggleButton implements ActionListener {
544 PowerButton() {
545 this(Res.gfxPowerOff, Res.gfxPowerOn);
546 }
547
548 PowerButton(ImageIcon defaultIcon, ImageIcon selectedIcon) {
549 super(defaultIcon, selectedIcon);
550
551 setSelected(true);
552 addActionListener(this);
553 }
554
555 @Override
556 public void
557 actionPerformed(ActionEvent e) {
558 boolean b = isSelected();
559 MainFrame frm = (MainFrame)CC.getMainFrame();
560 if(frm == null) return;
561 frm.setMidiKeyboardVisible(b);
562 }
563
564 @Override
565 public boolean
566 contains(int x, int y) { return (x - 11)*(x - 11) + (y - 11)*(y - 11) < 71; }
567 }
568
569 private final Handler handler = new Handler();
570
571 private Handler
572 getHandler() { return handler; }
573
574 private class Handler extends SamplerChannelAdapter implements FocusListener, MidiDataListener {
575 @Override
576 public void
577 midiDataArrived(MidiDataEvent e) {
578 if(channel == null) return;
579 channel.sendBackendMidiData(e);
580 }
581
582 @Override
583 public void
584 channelChanged(SamplerChannelEvent e) {
585 updateDisplay();
586
587 String newFile = channel.getChannelInfo().getInstrumentFile();
588 int newIndex = channel.getChannelInfo().getInstrumentIndex();
589
590 if(channel.getChannelInfo().getInstrumentStatus() != 100) {
591 //don't use disablePianoRoll because of unnecessary repainting
592 pianoRoll.setAllKeysPressed(false);
593 pianoRoll.removeAllKeyswitches();
594 pianoRoll.setAllKeysDisabled(true);
595 pianoRoll.setPlayingEnabled(false);
596 return;
597 }
598
599 if(newFile == null) {
600 if(file != null) disablePianoRoll();
601 file = null;
602 index = -1;
603 return;
604 }
605
606 if(newFile.equals(file) && newIndex == index) return;
607
608 file = newFile;
609 index = newIndex;
610 updateInstrumentInfo();
611 }
612
613 @Override
614 public void
615 focusGained(FocusEvent e) {
616
617 }
618
619 @Override
620 public void
621 focusLost(FocusEvent e) {
622
623 }
624 }
625 }

  ViewVC Help
Powered by ViewVC