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.std; |
24 |
|
25 |
import java.awt.Color; |
26 |
import java.awt.Font; |
27 |
import java.awt.FontMetrics; |
28 |
import java.awt.GradientPaint; |
29 |
import java.awt.Graphics; |
30 |
import java.awt.Graphics2D; |
31 |
import java.awt.Point; |
32 |
import java.awt.Rectangle; |
33 |
import java.awt.RenderingHints; |
34 |
|
35 |
import java.awt.event.MouseEvent; |
36 |
import java.awt.event.MouseAdapter; |
37 |
|
38 |
import java.awt.geom.RoundRectangle2D; |
39 |
|
40 |
import java.util.Vector; |
41 |
|
42 |
import javax.swing.JPanel; |
43 |
|
44 |
import org.linuxsampler.lscp.event.MidiDataEvent; |
45 |
import org.linuxsampler.lscp.event.MidiDataListener; |
46 |
|
47 |
/** |
48 |
* |
49 |
* @author Grigor Iliev |
50 |
*/ |
51 |
public class PianoRoll extends JPanel implements MidiDataListener { |
52 |
public class Key { |
53 |
private boolean disabled = false; |
54 |
private boolean pressed = false; |
55 |
private boolean keyswitch = false; |
56 |
|
57 |
public |
58 |
Key() { |
59 |
|
60 |
} |
61 |
|
62 |
public boolean |
63 |
isDisabled() { return disabled; } |
64 |
|
65 |
public void |
66 |
setDisabled(boolean b) { disabled = b; } |
67 |
|
68 |
public boolean |
69 |
isPressed() { return pressed; } |
70 |
|
71 |
public void |
72 |
setPressed(boolean b) { pressed = b; } |
73 |
|
74 |
public boolean |
75 |
isKeyswitch() { return keyswitch; } |
76 |
|
77 |
public void |
78 |
setKeyswitch(boolean b) { keyswitch = b; } |
79 |
|
80 |
} |
81 |
|
82 |
|
83 |
private final Vector<Key> keys = new Vector<Key>(); |
84 |
|
85 |
private boolean vertical; |
86 |
private boolean mirror; |
87 |
private boolean octaveLabelsVisible = true; |
88 |
private boolean playingEnabled = true; |
89 |
|
90 |
private int firstKey = -1; |
91 |
private int lastKey = -1; |
92 |
private int whiteKeyCount = 68; |
93 |
|
94 |
private Color borderColor = Color.BLACK; |
95 |
private Color keyColor = Color.WHITE; |
96 |
private Color disabledKeyColor = Color.GRAY; |
97 |
private Color pressedKeyColor = Color.GREEN; |
98 |
private Color keySwitchColor = Color.PINK; |
99 |
private Color blackKeyColor = Color.BLACK; |
100 |
|
101 |
private final Vector<MidiDataListener> listeners = new Vector<MidiDataListener>(); |
102 |
|
103 |
private boolean shouldRepaint = false; |
104 |
|
105 |
/** |
106 |
* Creates a new horizontal, not mirrored <code>PianoRoll</code>. |
107 |
*/ |
108 |
public |
109 |
PianoRoll() { this(false); } |
110 |
|
111 |
/** |
112 |
* Creates a new not mirrored <code>PianoRoll</code>. |
113 |
* @param vertical Specifies whether the piano roll |
114 |
* should be vertical or horizontal |
115 |
*/ |
116 |
public |
117 |
PianoRoll(boolean vertical) { this(vertical, false); } |
118 |
|
119 |
/** |
120 |
* Creates a new instance of <code>PianoRoll</code>. |
121 |
* @param vertical Specifies whether the piano roll |
122 |
* should be vertical or horizontal. |
123 |
* @param mirror Specifies whether to mirror the piano roll. |
124 |
*/ |
125 |
public |
126 |
PianoRoll(boolean vertical, boolean mirror) { |
127 |
this.vertical = vertical; |
128 |
this.mirror = mirror; |
129 |
|
130 |
this.addMouseListener(getHandler()); |
131 |
setKeyRange(0, 127); |
132 |
} |
133 |
|
134 |
/** |
135 |
* Registers the specified listener to be notified when |
136 |
* MIDI event occurs due to user input. |
137 |
* @param l The <code>MidiDataListener</code> to register. |
138 |
*/ |
139 |
public void |
140 |
addMidiDataListener(MidiDataListener l) { listeners.add(l); } |
141 |
|
142 |
/** |
143 |
* Removes the specified listener. |
144 |
* @param l The <code>MidiDataListener</code> to remove. |
145 |
*/ |
146 |
public void |
147 |
removeMidiDataListener(MidiDataListener l) { listeners.remove(l); } |
148 |
|
149 |
private void |
150 |
fireMidiDataEvent(MidiDataEvent e) { |
151 |
for(MidiDataListener l : listeners) l.midiDataArrived(e); |
152 |
} |
153 |
|
154 |
/** |
155 |
* Determines whether the user input is processed |
156 |
* and respective MIDI data events are sent. |
157 |
*/ |
158 |
public boolean |
159 |
isPlayingEnabled() { return playingEnabled; } |
160 |
|
161 |
/** |
162 |
* Sets whether the user input should be processed |
163 |
* and respective MIDI data events should be sent. |
164 |
* @see #isPlayingEnabled |
165 |
*/ |
166 |
public void |
167 |
setPlayingEnabled(boolean b) { playingEnabled = b; } |
168 |
|
169 |
public boolean |
170 |
hasKey(int key) { |
171 |
if(key >= firstKey && key <= lastKey) return true; |
172 |
return false; |
173 |
} |
174 |
|
175 |
public Key |
176 |
getKey(int key) { return keys.get(key - firstKey); } |
177 |
|
178 |
public boolean |
179 |
isKeyDisabled(int key) { |
180 |
return getKey(key).isDisabled(); |
181 |
} |
182 |
|
183 |
public void |
184 |
setKeyDisabled(int key, boolean disable) { |
185 |
Key k = getKey(key); |
186 |
if(k.isDisabled() == disable) return; |
187 |
|
188 |
getKey(key).setDisabled(disable); |
189 |
if(getShouldRepaint()) repaint(); |
190 |
} |
191 |
|
192 |
public boolean |
193 |
isKeyPressed(int key) { |
194 |
return getKey(key).isPressed(); |
195 |
} |
196 |
|
197 |
public void |
198 |
setKeyPressed(int key, boolean press) { |
199 |
Key k = getKey(key); |
200 |
if(k.isPressed() == press) return; |
201 |
|
202 |
getKey(key).setPressed(press); |
203 |
if(getShouldRepaint()) repaint(getKeyRectangle(key)); |
204 |
} |
205 |
|
206 |
public boolean |
207 |
isKeyswitch(int key) { |
208 |
return getKey(key).isKeyswitch(); |
209 |
} |
210 |
|
211 |
public void |
212 |
setKeyswitch(int key, boolean keyswitch) { |
213 |
Key k = getKey(key); |
214 |
if(k.isKeyswitch() == keyswitch) return; |
215 |
|
216 |
getKey(key).setKeyswitch(keyswitch); |
217 |
if(getShouldRepaint()) repaint(getKeyRectangle(key)); |
218 |
} |
219 |
|
220 |
public void |
221 |
setAllKeysPressed(boolean b) { |
222 |
boolean changed = false; |
223 |
for(Key key : keys) { |
224 |
if(key.isPressed() != b) { |
225 |
key.setPressed(b); |
226 |
changed = true; |
227 |
} |
228 |
} |
229 |
|
230 |
if(changed && getShouldRepaint()) repaint(); |
231 |
} |
232 |
|
233 |
public void |
234 |
setAllKeysDisabled(boolean b) { |
235 |
boolean changed = false; |
236 |
for(Key key : keys) { |
237 |
if(key.isDisabled() != b) { |
238 |
key.setDisabled(b); |
239 |
changed = true; |
240 |
} |
241 |
} |
242 |
|
243 |
if(changed && getShouldRepaint()) repaint(); |
244 |
} |
245 |
|
246 |
public Integer[] |
247 |
getKeyswitches() { |
248 |
Vector<Integer> v = new Vector<Integer>(); |
249 |
for(int i = 0; i < keys.size(); i++) { |
250 |
if(keys.get(i).isKeyswitch()) v.add(firstKey + i); |
251 |
} |
252 |
|
253 |
return v.toArray(new Integer[v.size()]); |
254 |
} |
255 |
|
256 |
/** |
257 |
* Keys outside the keyboard range are ignored. |
258 |
* @param keys List of keys |
259 |
*/ |
260 |
public void |
261 |
setKeyswitches(Integer[] keys, boolean keyswitches) { |
262 |
boolean changed = false; |
263 |
for(int k : keys) { |
264 |
if(!hasKey(k)) continue; |
265 |
if(getKey(k).isKeyswitch() != keyswitches) { |
266 |
getKey(k).setKeyswitch(keyswitches); |
267 |
changed = true; |
268 |
} |
269 |
} |
270 |
if(changed && getShouldRepaint()) repaint(); |
271 |
} |
272 |
|
273 |
public Integer[] |
274 |
getEnabledKeys() { |
275 |
Vector<Integer> v = new Vector<Integer>(); |
276 |
for(int i = 0; i < keys.size(); i++) { |
277 |
if(!keys.get(i).isDisabled()) v.add(firstKey + i); |
278 |
} |
279 |
|
280 |
return v.toArray(new Integer[v.size()]); |
281 |
} |
282 |
|
283 |
/** |
284 |
* Enables or disables the specified list of keys. |
285 |
* Keys outside the keyboard range are ignored. |
286 |
* @param keys List of keys |
287 |
*/ |
288 |
public void |
289 |
setDisabled(Integer[] keys, boolean disable) { |
290 |
boolean changed = false; |
291 |
for(int k : keys) { |
292 |
if(!hasKey(k)) continue; |
293 |
if(getKey(k).isDisabled() != disable) { |
294 |
getKey(k).setDisabled(disable); |
295 |
changed = true; |
296 |
} |
297 |
} |
298 |
if(changed && getShouldRepaint()) repaint(); |
299 |
} |
300 |
|
301 |
public void |
302 |
removeAllKeyswitches() { |
303 |
boolean changed = false; |
304 |
for(Key key : keys) { |
305 |
if(key.isKeyswitch()) { |
306 |
key.setKeyswitch(false); |
307 |
changed = true; |
308 |
} |
309 |
} |
310 |
if(changed && getShouldRepaint()) repaint(); |
311 |
} |
312 |
|
313 |
/** |
314 |
* Sets the piano key range which this piano roll will provide. |
315 |
* Note that the specified last key is also included in the piano roll. |
316 |
* Also, the first and the last key should be white keys. If the first key |
317 |
* and/or the last key are not white keys then the range is extended automatically. |
318 |
* @param firstKey Number between 0 and 127 (inclusive) as specified in the MIDI standard. |
319 |
* @param lastKey Number between 0 and 127 (inclusive) as specified in the MIDI standard. |
320 |
* @throws IllegalArgumentException if the specified range is invalid. |
321 |
*/ |
322 |
public void |
323 |
setKeyRange(int firstKey, int lastKey) { |
324 |
if(this.firstKey == firstKey && this.lastKey == lastKey) return; |
325 |
|
326 |
if(firstKey < 0 || firstKey > 127 || lastKey < 0 || lastKey > 127 || firstKey >= lastKey) { |
327 |
throw new IllegalArgumentException("Invalid range: " + firstKey + "-" + lastKey); |
328 |
} |
329 |
|
330 |
/*Integer[] enabledKeys = getEnabledKeys(); |
331 |
Integer[] keyswitches = getKeyswitches();*/ |
332 |
|
333 |
if(!isWhiteKey(firstKey)) firstKey--; |
334 |
if(!isWhiteKey(lastKey)) lastKey++; |
335 |
this.firstKey = firstKey; |
336 |
this.lastKey = lastKey; |
337 |
|
338 |
keys.removeAllElements(); |
339 |
for(int i = 0; i <= lastKey - firstKey; i++) keys.add(new Key()); |
340 |
|
341 |
whiteKeyCount = getWhiteKeyCount(firstKey, lastKey); |
342 |
|
343 |
/*setAllKeysDisabled(true); |
344 |
setDisabled(enabledKeys, false); |
345 |
setKeyswitches(keyswitches, true);*/ |
346 |
|
347 |
if(getShouldRepaint()) repaint(); |
348 |
} |
349 |
|
350 |
public boolean |
351 |
getOctaveLabelsVisible() { |
352 |
return octaveLabelsVisible; |
353 |
} |
354 |
|
355 |
public void |
356 |
setOctaveLabelsVisible(boolean b) { |
357 |
octaveLabelsVisible = b; |
358 |
} |
359 |
|
360 |
/** |
361 |
* Gets the number of white keys int the specified range (inclusive). |
362 |
* @see #setKeyRange |
363 |
*/ |
364 |
private static int |
365 |
getWhiteKeyCount(int firstKey, int lastKey) { |
366 |
int count = 0; |
367 |
for(int j = firstKey; j <= lastKey; j++) { // FIXME: Stupid but works |
368 |
if(isWhiteKey(j)) count++; |
369 |
} |
370 |
|
371 |
return count; |
372 |
} |
373 |
|
374 |
private int |
375 |
getWhiteKeyByNumber(int whiteKey) { |
376 |
int count = 0; |
377 |
for(int j = firstKey; j <= lastKey; j++) { // FIXME: Stupid but works |
378 |
if(isWhiteKey(j)) { |
379 |
if(whiteKey == count) return j; |
380 |
count++; |
381 |
} |
382 |
} |
383 |
|
384 |
return -1; |
385 |
} |
386 |
|
387 |
private int |
388 |
getWhiteKeyCount() { |
389 |
return whiteKeyCount; |
390 |
} |
391 |
|
392 |
/** |
393 |
* Determines whether the specified key is a white key. |
394 |
* @param key Number between 0 and 127 (inclusive) as specified in the MIDI standard. |
395 |
*/ |
396 |
public static boolean |
397 |
isWhiteKey(int key) { |
398 |
if(key < 0 || key > 127) return false; |
399 |
|
400 |
int k = key % 12; |
401 |
if(k == 1 || k == 3 || k == 6 || k == 8 || k == 10) { |
402 |
return false; |
403 |
} |
404 |
|
405 |
return true; |
406 |
} |
407 |
|
408 |
/** |
409 |
* Returns the position (zero-based) of the specified white key in the octave. |
410 |
* @param whiteKey Number between 0 and 127 (inclusive) as specified in the MIDI standard. |
411 |
* @return Number between 0 and 6 (inclusive) and -1 if the |
412 |
* specified key is not a white key. |
413 |
*/ |
414 |
public static int |
415 |
getKeyOctaveIndex(int whiteKey) { |
416 |
if(whiteKey < 0 || whiteKey > 127) return -1; |
417 |
|
418 |
int k = whiteKey % 12; |
419 |
if(k == 1 || k == 3 || k == 6 || k == 8 || k == 10) { |
420 |
return -1; |
421 |
} |
422 |
|
423 |
return getWhiteKeyCount(0, k) - 1; |
424 |
} |
425 |
|
426 |
private Color |
427 |
getKeyColor(int key) { |
428 |
Key k = getKey(key); |
429 |
if(isWhiteKey(key)) { |
430 |
if(k.isPressed()) return pressedKeyColor; |
431 |
if(k.isKeyswitch()) return keySwitchColor; |
432 |
if(k.isDisabled()) return disabledKeyColor; |
433 |
return keyColor; |
434 |
} else { |
435 |
if(k.isPressed()) return Color.GREEN; |
436 |
return blackKeyColor; |
437 |
} |
438 |
} |
439 |
|
440 |
/** |
441 |
* Releases all pressed keys, enables all keys and removes all keyswitches. |
442 |
*/ |
443 |
public void |
444 |
reset() { reset(false); } |
445 |
|
446 |
/** |
447 |
* Releases all pressed keys, enables/disables all keys and removes all keyswitches. |
448 |
* @param dissable Specifies whether all keys should be enabled or disabled |
449 |
*/ |
450 |
public void |
451 |
reset(boolean dissableAllKeys) { |
452 |
boolean b = getShouldRepaint(); |
453 |
setShouldRepaint(false); |
454 |
setAllKeysPressed(false); |
455 |
removeAllKeyswitches(); |
456 |
setAllKeysDisabled(dissableAllKeys); |
457 |
setShouldRepaint(b); |
458 |
if(getShouldRepaint()) repaint(); |
459 |
} |
460 |
|
461 |
public boolean |
462 |
getShouldRepaint() { return shouldRepaint; } |
463 |
|
464 |
public void |
465 |
setShouldRepaint(boolean b) { shouldRepaint = b; } |
466 |
|
467 |
private int |
468 |
getKey(Point p) { |
469 |
double w = getWhiteKeyWidth() + /* space between keys */ 1.0d; |
470 |
if(w == 0) return -1; |
471 |
int whiteKeyNumber = (int) (p.getX() / w); |
472 |
double leftBorder = whiteKeyNumber * w; |
473 |
int key = getWhiteKeyByNumber(whiteKeyNumber); |
474 |
if(key == -1) return -1; |
475 |
|
476 |
double bh = getBlackKeyHeight(); |
477 |
double blackKeyOffset = w / 4.0d; |
478 |
if(p.getY() > bh) return key; |
479 |
if(key != firstKey && !isWhiteKey(key - 1)) { |
480 |
if(p.getX() <= leftBorder + blackKeyOffset) return key - 1; |
481 |
} |
482 |
if(key != lastKey && !isWhiteKey(key + 1)) { |
483 |
if(p.getX() >= leftBorder + 3 * blackKeyOffset - 3) return key + 1; |
484 |
} |
485 |
|
486 |
return key; |
487 |
} |
488 |
|
489 |
private int |
490 |
getVelocity(Point p, boolean whiteKey) { |
491 |
double h = whiteKey ? getWhiteKeyHeight() : getBlackKeyHeight(); |
492 |
int velocity = (int) ((p.getY() / h) * 127.0d + 1); |
493 |
if(velocity < 0) velocity = 0; |
494 |
if(velocity > 127) velocity = 127; |
495 |
return velocity; |
496 |
} |
497 |
|
498 |
private double |
499 |
getWhiteKeyWidth() { |
500 |
double w = getSize().getWidth(); |
501 |
return (w - getWhiteKeyCount()) / getWhiteKeyCount(); |
502 |
} |
503 |
|
504 |
private double |
505 |
getWhiteKeyHeight() { |
506 |
return getSize().getHeight() - 3.0d; |
507 |
} |
508 |
|
509 |
private double |
510 |
getBlackKeyWidth() { |
511 |
return getWhiteKeyWidth() / 2.0d; |
512 |
} |
513 |
|
514 |
private double |
515 |
getBlackKeyHeight() { |
516 |
return getWhiteKeyHeight() / 1.5d; |
517 |
} |
518 |
|
519 |
protected void |
520 |
paintOctaveLabel(Graphics2D g, int octave, int whiteKeyIndex) { |
521 |
double h = getSize().getHeight(); |
522 |
double whiteKeyWidth = getWhiteKeyWidth(); |
523 |
g.setPaint(Color.BLACK); |
524 |
int fsize = (int) (whiteKeyWidth / (1.5 + whiteKeyWidth / 50)); |
525 |
if(fsize < 8) fsize = 8; |
526 |
g.setFont(g.getFont().deriveFont(Font.BOLD, fsize)); |
527 |
|
528 |
float x = (float) (whiteKeyWidth * whiteKeyIndex + whiteKeyIndex); |
529 |
float y = (float) (h - 1); |
530 |
|
531 |
String s = String.valueOf(octave); |
532 |
FontMetrics fm = g.getFontMetrics(); |
533 |
|
534 |
// center text |
535 |
int i = fm.stringWidth(s); |
536 |
if(i < whiteKeyWidth) { |
537 |
x += (whiteKeyWidth - i) / 2; |
538 |
} else { |
539 |
x += 2; |
540 |
} |
541 |
|
542 |
y -= (h / 12); |
543 |
|
544 |
g.drawString(s, x, y); |
545 |
} |
546 |
|
547 |
/** |
548 |
* Gets the rectangle in which the key is drawn and empty rectangle |
549 |
* if the specified key is not shown on the piano roll or is invalid. |
550 |
*/ |
551 |
public Rectangle |
552 |
getKeyRectangle(int key) { |
553 |
Rectangle r = new Rectangle(); |
554 |
if(!hasKey(key)) return r; |
555 |
|
556 |
int whiteKeyIndex = getWhiteKeyCount(firstKey, key) - 1; |
557 |
|
558 |
if(isWhiteKey(key)) { |
559 |
double whiteKeyWidth = getWhiteKeyWidth(); |
560 |
double whiteKeyHeight = getWhiteKeyHeight(); |
561 |
double x = whiteKeyWidth * whiteKeyIndex + whiteKeyIndex; |
562 |
r.setRect(x, 0, whiteKeyWidth, whiteKeyHeight); |
563 |
} else { |
564 |
double blackKeyWidth = getBlackKeyWidth(); |
565 |
double blackKeyHeight = getBlackKeyHeight(); |
566 |
int i = whiteKeyIndex; |
567 |
double x = blackKeyWidth * (2*(i + 1)) - blackKeyWidth * 0.5d + i; |
568 |
r.setRect(x, 0, blackKeyWidth, blackKeyHeight); |
569 |
} |
570 |
|
571 |
return r; |
572 |
} |
573 |
|
574 |
@Override public void |
575 |
paint(Graphics g) { |
576 |
super.paint(g); |
577 |
Graphics2D g2 = (Graphics2D) g; |
578 |
|
579 |
double whiteKeyWidth = getWhiteKeyWidth(); |
580 |
double whiteKeyHeight = getWhiteKeyHeight(); |
581 |
double arcw = whiteKeyWidth/4.0d; |
582 |
double arch = whiteKeyHeight/14.0d; |
583 |
|
584 |
g2.setPaint(keyColor); |
585 |
|
586 |
RoundRectangle2D.Double rect; |
587 |
|
588 |
g2.setRenderingHint ( |
589 |
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF |
590 |
); |
591 |
|
592 |
int i = 0; |
593 |
for(int j = firstKey; j <= lastKey; j++) { |
594 |
if(!isWhiteKey(j)) continue; |
595 |
|
596 |
Color c = getKeyColor(j); |
597 |
if(g2.getPaint() != c) g2.setPaint(c); |
598 |
|
599 |
rect = new RoundRectangle2D.Double ( |
600 |
// If you change this you should also change getKeyRectangle() |
601 |
whiteKeyWidth * i + i, 0, |
602 |
whiteKeyWidth, whiteKeyHeight, |
603 |
arcw, arch |
604 |
); |
605 |
|
606 |
g2.fill(rect); |
607 |
i++; |
608 |
} |
609 |
|
610 |
g2.setRenderingHint ( |
611 |
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON |
612 |
); |
613 |
|
614 |
g2.setStroke(new java.awt.BasicStroke(1.5f)); |
615 |
g2.setPaint(borderColor); |
616 |
|
617 |
i = 0; |
618 |
for(int j = firstKey; j <= lastKey; j++) { |
619 |
if(!isWhiteKey(j)) continue; |
620 |
|
621 |
rect = new RoundRectangle2D.Double ( |
622 |
whiteKeyWidth * i + i, 0, |
623 |
whiteKeyWidth, whiteKeyHeight, |
624 |
arcw, arch |
625 |
); |
626 |
|
627 |
g2.draw(rect); |
628 |
i++; |
629 |
} |
630 |
|
631 |
|
632 |
g2.setStroke(new java.awt.BasicStroke(2.5f)); |
633 |
double blackKeyWidth = getBlackKeyWidth(); |
634 |
double blackKeyHeight = getBlackKeyHeight(); |
635 |
|
636 |
i = 0; |
637 |
for(int j = firstKey; j <= lastKey; j++) { |
638 |
if(getOctaveLabelsVisible() && j % 12 == 0) { |
639 |
int octave = j / 12 - 2; |
640 |
paintOctaveLabel(g2, octave, i); |
641 |
} |
642 |
if(!isWhiteKey(j)) continue; |
643 |
|
644 |
int k = (i + getKeyOctaveIndex(firstKey)) % 7; |
645 |
if(k == 2 || k == 6) { i++; continue; } |
646 |
|
647 |
Color c = (j == lastKey) ? blackKeyColor : getKeyColor(j + 1); |
648 |
if(g2.getPaint() != c) g2.setPaint(c); |
649 |
|
650 |
// If you change this you should also change getKeyRectangle() |
651 |
double x = blackKeyWidth * (2*(i + 1)) - blackKeyWidth * 0.5d + i; |
652 |
rect = new RoundRectangle2D.Double ( |
653 |
// If you change this you should also change getKeyRectangle() |
654 |
x, 0, |
655 |
blackKeyWidth, blackKeyHeight, |
656 |
arcw, arch |
657 |
); |
658 |
/////// |
659 |
|
660 |
boolean pressed = (j == lastKey) ? false : getKey(j + 1).isPressed(); |
661 |
if(!pressed) g2.fill(rect); |
662 |
|
663 |
RoundRectangle2D.Double rect2; |
664 |
rect2 = new RoundRectangle2D.Double ( |
665 |
x, 0, |
666 |
blackKeyWidth, arch, |
667 |
arcw, arch / 1.8d |
668 |
); |
669 |
|
670 |
g2.fill(rect2); |
671 |
g2.setPaint(borderColor); |
672 |
g2.draw(rect); |
673 |
|
674 |
if(pressed) { |
675 |
GradientPaint gr = new GradientPaint ( |
676 |
(float)(x + blackKeyWidth/2), (float)(blackKeyHeight/4), Color.BLACK, |
677 |
(float)(x + blackKeyWidth/2), (float)blackKeyHeight, new Color(0x058a02) |
678 |
); |
679 |
g2.setPaint(gr); |
680 |
g2.fill(rect); |
681 |
} |
682 |
i++; |
683 |
} |
684 |
} |
685 |
|
686 |
@Override public void |
687 |
midiDataArrived(MidiDataEvent e) { |
688 |
switch(e.getType()) { |
689 |
case NOTE_ON: |
690 |
setKeyPressed(e.getNote(), true); |
691 |
break; |
692 |
case NOTE_OFF: |
693 |
setKeyPressed(e.getNote(), false); |
694 |
} |
695 |
} |
696 |
|
697 |
private final Handler handler = new Handler(); |
698 |
|
699 |
private Handler |
700 |
getHandler() { return handler; } |
701 |
|
702 |
private class Handler extends MouseAdapter { |
703 |
private int pressedKey = -1; |
704 |
|
705 |
@Override public void |
706 |
mousePressed(MouseEvent e) { |
707 |
if(!isPlayingEnabled()) return; |
708 |
if(e.getButton() != MouseEvent.BUTTON1) return; |
709 |
|
710 |
int key = getKey(e.getPoint()); |
711 |
if(key == -1) return; |
712 |
pressedKey = key; |
713 |
setKeyPressed(key, true); |
714 |
int velocity = getVelocity(e.getPoint(), isWhiteKey(key)); |
715 |
|
716 |
MidiDataEvent evt = new MidiDataEvent ( |
717 |
PianoRoll.this, MidiDataEvent.Type.NOTE_ON, key, velocity |
718 |
); |
719 |
|
720 |
fireMidiDataEvent(evt); |
721 |
} |
722 |
|
723 |
@Override public void |
724 |
mouseReleased(MouseEvent e) { |
725 |
if(!isPlayingEnabled()) return; |
726 |
if(e.getButton() != MouseEvent.BUTTON1) return; |
727 |
|
728 |
if(pressedKey == -1) return; |
729 |
setKeyPressed(pressedKey, false); |
730 |
|
731 |
int velocity = getVelocity(e.getPoint(), isWhiteKey(pressedKey)); |
732 |
MidiDataEvent evt = new MidiDataEvent ( |
733 |
PianoRoll.this, MidiDataEvent.Type.NOTE_OFF, pressedKey, velocity |
734 |
); |
735 |
|
736 |
pressedKey = -1; |
737 |
|
738 |
fireMidiDataEvent(evt); |
739 |
} |
740 |
} |
741 |
} |