/*
* Created on Jan 18, 2006
*
* Based on Sun code see notice below.
*
* http://www.frinika.com
*
* This file is part of Frinika.
*
* Frinika is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* Frinika is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with Frinika; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/*
* @(#)MidiSynth.java 1.15 99/12/03
*
* Copyright (c) 1999 Sun Microsystems, Inc. All Rights Reserved.
*
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear
* facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes.
*/
package com.frinika.sequencer.gui.pianoroll;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Vector;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.ShortMessage;
import javax.swing.JPanel;
import javax.sound.midi.Receiver;
import com.frinika.sequencer.gui.Layout;
import com.frinika.sequencer.model.MidiLane;
import com.frinika.sequencer.model.MidiPart;
import com.frinika.sequencer.model.Part;
public class VirtualPianoVert extends JPanel implements MouseListener,
AdjustmentListener {
/**
*
*/
private static final long serialVersionUID = 1L;
final int ON = 0, OFF = 1;
final Color jfcBlue = new Color(204, 204, 255);
final Color pink = new Color(255, 175, 175);
Vector<Key> blackKeys = new Vector<Key>();
Key keys[] = new Key[128];
Vector<Key> whiteKeys = new Vector<Key>();
Key prevKey;
int lastKeyPress = 0;
final int nNote = 128;
final int nWhiteNote = (nNote / 12) * 7 + 5;
final int nOctave = nNote / 12 + 1;
int whiteKeyWidth, blackKeyDepth = 20;
int keyDepth = 30;
int blackWhiteGap;
PianoRoll pianoRoll;
int timePanelHeight;
private int yScroll;
int yBot;
int noteItemHeight;
public static class Config {
public Config(int keyDepth, int noteHeight, Receiver recv, MidiLane lane) {
this.noteHeight = noteHeight;
this.keyDepth = keyDepth;
this.recv = recv;
this.lane = lane;
}
final int noteHeight;
final int keyDepth;
public Receiver recv;
public MidiLane lane;
public void setReceiver(Receiver recv2) {
recv = recv2;
}
}
Config config = null;
public VirtualPianoVert(Config config) {
super(false);
this.config = config;
this.pianoRoll = null;
this.timePanelHeight = 0;
this.yScroll = 0;
resizeKeys();
addMouseListener(this);
setBackground(Color.ORANGE);
}
public VirtualPianoVert(PianoRoll pr, int yTop, int yScroll) {
super(false);
this.pianoRoll = pr;
this.timePanelHeight = yTop;
this.yScroll = yScroll;
resizeKeys();
addMouseListener(this);
setBackground(Color.ORANGE);
}
// public Dimension getPreferredSize() {
// return new Dimension(50,200);
// }
//
// public Dimension getMinimumSize() {
// return new Dimension(50,200);
// }
private void resizeKeys() {
blackKeys.clear();
whiteKeys.clear();
//keys.clear();
if (config == null) {
noteItemHeight = Layout.getNoteItemHeight();
} else {
noteItemHeight = config.noteHeight;
keyDepth = config.keyDepth;
}
// setLayout(new BorderLayout());
this.whiteKeyWidth = (noteItemHeight * 12) / 7;
this.blackWhiteGap = whiteKeyWidth / 3;
int whiteIDs[] = { 0, 2, 4, 5, 7, 9, 11 };
// yBot = (((nWhiteNote +2) *
// whiteKeyWidth)/noteItemHeight)*noteItemHeight-whiteKeyWidth;
yBot = 129 * noteItemHeight - whiteKeyWidth;
/// You will be the size I want . . . .
setSize(new Dimension(keyDepth, yBot));
// setMinimumSize(new Dimension(keyDepth, yBot));
setPreferredSize(new Dimension(keyDepth, yBot));
setMaximumSize(new Dimension(keyDepth, 20000));
for (int i = 0, y = 0; i < nOctave; i++) {
for (int j = 0; j < 7; j++, y += whiteKeyWidth) {
int keyNum = i * 12 + whiteIDs[j];
if (keyNum >= nNote)
break;
whiteKeys.add(new Key(0, yBot - y, keyDepth, whiteKeyWidth,
keyNum));
}
}
int halfGap = blackWhiteGap / 2;
int yBot1 = yBot + 7 * whiteKeyWidth / 8;
int blackKeyWidth = 2 * whiteKeyWidth / 3;
for (int i = 0, y = 0; i < nOctave; i++, y += whiteKeyWidth) {
int keyNum = i * 12;
if (keyNum >= nNote - 1)
break;
blackKeys.add(new Key(0,
yBot1 - (y += whiteKeyWidth) - halfGap / 2, blackKeyDepth,
blackKeyWidth, keyNum + 1));
if (keyNum >= nNote - 3)
break;
blackKeys.add(new Key(0, yBot1 - (y += whiteKeyWidth) - 3 * halfGap
/ 2, blackKeyDepth, blackKeyWidth, keyNum + 3));
if (keyNum >= nNote - 6)
break;
y += whiteKeyWidth;
blackKeys.add(new Key(0, yBot1 - (y += whiteKeyWidth) - halfGap / 2
+ 1, blackKeyDepth, blackKeyWidth, keyNum + 6));
if (keyNum >= nNote - 8)
break;
blackKeys.add(new Key(0, yBot1 - (y += whiteKeyWidth) - halfGap,
blackKeyDepth, blackKeyWidth, keyNum + 8));
if (keyNum >= nNote - 10)
break;
blackKeys.add(new Key(0, yBot1 - (y += whiteKeyWidth) - 3 * halfGap
/ 2 - 1, blackKeyDepth, blackKeyWidth, keyNum + 10));
}
for (Key key:blackKeys) {
keys[key.kNum]=key;
}
for (Key key:whiteKeys) {
keys[key.kNum]=key;
}
}
public void mousePressed(MouseEvent e) {
prevKey = getKey(e.getPoint());
if (prevKey != null) {
prevKey.on();
repaint();
}
}
public void mouseReleased(MouseEvent e) {
if (prevKey != null) {
prevKey.off();
repaint();
}
}
public void mouseExited(MouseEvent e) {
if (prevKey != null) {
prevKey.off();
repaint();
prevKey = null;
}
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public Key getKey(Point point) {
point.translate(0, -timePanelHeight + yScroll);
for (Key key:blackKeys) {
if (key == null) continue;
if (key.contains(point)) {
return key;
}
}
for (Key key:whiteKeys) {
if (key == null) continue;
if (key.contains(point)) {
return key;
}
}
return null;
}
public void paintComponent(Graphics g) {
//Thread.yield();
super.paintComponent(g);
//Thread.yield();
if ((config == null && noteItemHeight != Layout.getNoteItemHeight())
|| (config != null && noteItemHeight != config.noteHeight))
resizeKeys();
Graphics2D g2 = (Graphics2D) g;
g2.translate(0, timePanelHeight - yScroll);
g2.setColor(Color.PINK);
g2.fillRect(0, 0, keyDepth, yBot);
for (int i = 0; i < whiteKeys.size(); i++) {
Key key = (Key) whiteKeys.get(i);
if (key.isNoteOn() || (config != null && key.kNum == lastKeyPress) ) {
g2.setColor(jfcBlue);
g2.fill(key);
}
g2.setColor(Color.black);
g2.draw(key);
if (key.kNum % 12 == 0) {
// g2.setColor(Color.red);
//
// g2.fill(key);
g2.setColor(Color.BLACK);
g2.drawString(String.valueOf(key.kNum / 12), keyDepth-15, key.y + key.height-1);
// g2.setPaintMode();
}
}
for (int i = 0; i < blackKeys.size(); i++) {
Key key = (Key) blackKeys.get(i);
if (key.isNoteOn()|| (config != null && key.kNum == lastKeyPress) ) {
g2.setColor(jfcBlue);
g2.fill(key);
g2.setColor(Color.black);
g2.draw(key);
} else {
g2.setColor(Color.black);
g2.fill(key);
}
}
g2.translate(0, -(timePanelHeight - yScroll));
//Thread.yield();
}
public class Key extends Rectangle {
/**
*
*/
private static final long serialVersionUID = 1L;
Receiver recv = null;
int chan = 0;
int noteState = OFF;
int kNum;
public Key(int x, int y, int width, int height, int num) {
super(x, y, width, height);
kNum = num;
}
public boolean isNoteOn() {
return noteState == ON;
}
public void on() {
setNoteState(ON);
if (config == null) {
if (pianoRoll == null)
return;
Part focusPart = pianoRoll.getProjectContainer()
.getPartSelection().getFocus();
if (focusPart == null || !(focusPart instanceof MidiPart ))
return;
MidiLane lane = ((MidiLane) (focusPart.getLane()));
recv = lane.getReceiver();
chan = lane.getMidiChannel();
} else {
recv = config.recv;
chan = config.lane.getMidiChannel();
}
if (recv == null)
return;
ShortMessage shm = new ShortMessage();
try {
shm.setMessage(ShortMessage.NOTE_ON, chan, kNum, 100);
} catch (InvalidMidiDataException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// try {
recv.send(shm, -1);
// } catch (MidiUnavailableException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
// }
}
public void off() {
setNoteState(OFF);
// if (pianoRoll == null)
// return;
if (recv == null)
return;
ShortMessage shm = new ShortMessage();
try {
shm.setMessage(ShortMessage.NOTE_ON, chan, kNum, 0);
} catch (InvalidMidiDataException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// try {
recv.send(shm, -1);
// } catch (MidiUnavailableException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
// }
recv = null;
}
public void setNoteState(int state) {
noteState = state;
if (state == ON)
lastKeyPress = kNum;
}
}// End class Key
public void adjustmentValueChanged(AdjustmentEvent e) {
yScroll = e.getValue();
repaint();
}
public int getLastKeytPress() {
return lastKeyPress;
}
public Key getKey(int i) {
return keys[i];
}
}