Skip to content

Instantly share code, notes, and snippets.

@kabachuha
Created June 3, 2018 13:56
Show Gist options
  • Save kabachuha/578b76e5cecb597936a2b1dc09e9f03a to your computer and use it in GitHub Desktop.
Save kabachuha/578b76e5cecb597936a2b1dc09e9f03a to your computer and use it in GitHub Desktop.
Arduino-based musical keyboard + interpreter in java(using JSSC for Serial Port) + gui
import java.awt.AWTException;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.TrayIcon.MessageType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.BitSet;
import javax.imageio.ImageIO;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Synthesizer;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import jssc.SerialPort;
import jssc.SerialPortEvent;
import jssc.SerialPortEventListener;
import jssc.SerialPortException;
import jssc.SerialPortList;
public class Main {
final static int FsizeX = 720;
final static int FsizeY = 250;
final static long RESET_NOTES = 0b1000000000000000000000000000000000000000000000000000000000000011L;
static SerialPort serial_port = null;
public static void main(String[] args) throws Exception
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("SynthManager");
frame.setSize(FsizeX, FsizeY);
frame.setLayout(null);
try {
KeysImages.setup();
}catch(Exception e)
{
System.err.println("Failed to load key images!");
System.err.println(e.toString());
System.exit(1);
return;
}
setupFrame(frame);
frame.setVisible(true);
Runtime.getRuntime().addShutdownHook(new Shutdowner());
}
static void stopPort()
{
updateNotestates(RESET_NOTES);
if(serial_port != null)
{
try {
serial_port.closePort();
popupMessage("Closed SP: "+serial_port.getPortName(), "", MessageType.INFO);
}catch(Exception e)
{
System.err.println("UNABLE TO CLOSE SERIAL PORT!");
popupMessage("UNable to close SP: "+serial_port.getPortName(), "", MessageType.ERROR);
}
finally
{
serial_port = null;
}
}
}
static class Shutdowner extends Thread
{
public Shutdowner()
{
}
@Override
public void run()
{
System.out.println("Shutting down");
updateNotestates(RESET_NOTES);
try {
serial_port.closePort();
}catch(Exception e)
{
System.err.println("UNABLE TO CLOSE SERIAL PORT!");
}
finally
{
serial_port = null;
}
if(midi != null)
{
midi.close();
midi=null;
}
}
}
static KeyboardView kv;
static void setupFrame(JFrame frame)
{
JLabel midi_info = new JLabel();
midi_info.setBounds(135, -1, 400, 40);
JComboBox<String> midi_selector = new JComboBox<>();
midi_selector.setBounds(5, 5, 130, 30);
midi_selector.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent arg0) {
for(MidiDevice.Info info : MidiSystem.getMidiDeviceInfo())
if(info.getName().equals(midi_selector.getSelectedItem()))
{
MidiDevice device = null;
try {
device = MidiSystem.getMidiDevice(info);
} catch (MidiUnavailableException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (device != null && ! (device instanceof Sequencer) && ! (device instanceof Synthesizer) && device.getClass().getSimpleName().contains("Out"))
{
midi_info.setText(info.getVendor()+"�: "+info.getDescription());
if(midi != null)
{
updateNotestates(RESET_NOTES);
midi.close();
midi = null;
}
try {
midi = MidiSystem.getMidiDevice(info);
midi.open();
popupMessage("MIDI", "Connected to MIDI!", MessageType.INFO);
} catch (MidiUnavailableException e) {
popupMessage("MIDI", "Unable to make MIDI connection!", MessageType.ERROR);
e.printStackTrace();
}
break;
}
}
}
});
JComboBox<String> serial_selector = new JComboBox<>();
serial_selector.setBounds(5, 40, 130, 30);
serial_selector.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent arg0) {
}
});
JButton update_button = new JButton("Update");
JButton connect_to_serial = new JButton("Attach");
connect_to_serial.setEnabled(false);
update_button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
updateNotestates(RESET_NOTES);
if(midi != null)
{
midi.close();
midi=null;
}
midi_selector.removeAllItems();
midi_info.setText("Midi port not selected!");
for(MidiDevice.Info info : MidiSystem.getMidiDeviceInfo())
{
try {
MidiDevice device = MidiSystem.getMidiDevice(info);
if ( ! (device instanceof Sequencer) && ! (device instanceof Synthesizer) && device.getClass().getSimpleName().contains("Out"))
midi_selector.addItem(info.getName());
}catch(Exception c)
{
}
}
boolean dupport = false;
connect_to_serial.setEnabled(false);
serial_selector.removeAllItems();
for(String info : SerialPortList.getPortNames())
{
if(info.equals(serial_selector.getSelectedItem()))
dupport = true;
serial_selector.addItem(info);
}
connect_to_serial.setEnabled(true);
if(!dupport)
{
stopPort();
}
}
});
update_button.setBounds(FsizeX-100, 5, 80, 20);
connect_to_serial.setBounds(140, 45, 80, 20);
connect_to_serial.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
stopPort();
serial_port = new SerialPort((String)serial_selector.getSelectedItem());
try {
if(serial_port.openPort())
popupMessage("Serial Port opened!", "", MessageType.INFO);
else
{
popupMessage("Failed to open SP!", "", MessageType.ERROR);
return;
}
serial_port.setParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
serial_port.addEventListener(new ArduinoListener());
serial_port.writeString("K");
} catch (SerialPortException e1) {
e1.printStackTrace();
stopPort();
}
}
});
JSlider noteoner = new JSlider(0, 127);
noteoner.setBounds(220, 40, 460, 20);
JLabel onerlab = new JLabel("VelON "+velocityOn);
onerlab.setBounds(500, 75, 60, 20);
noteoner.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
velocityOn = noteoner.getValue();
onerlab.setText("VelON "+velocityOn);
}
});
JSlider noteoffer = new JSlider(0, 127);
noteoffer.setBounds(220, 60, 460, 20);
JLabel offerlab = new JLabel("VelOFF "+velocityOff);
offerlab.setBounds(600, 75, 65, 20);
noteoffer.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
velocityOff = noteoffer.getValue();
offerlab.setText("VelOFF "+velocityOff);
}
});
kv = new KeyboardView();
kv.setBounds(5, 100, 800, 100);
frame.add(midi_info);
frame.add(noteoner);
frame.add(noteoffer);
frame.add(onerlab);
frame.add(offerlab);
frame.add(midi_selector);
frame.add(serial_selector);
frame.add(connect_to_serial);
frame.add(kv);
frame.add(update_button);
}
static TrayIcon trayIcon= null;
static void setupTrayIcon()
{
if (SystemTray.isSupported())
{
SystemTray tray = SystemTray.getSystemTray();
Image image = Toolkit.getDefaultToolkit().createImage("icon.png");
trayIcon = new TrayIcon(image, "SynthManager");
trayIcon.setImageAutoSize(true);
try {
tray.add(trayIcon);
} catch (AWTException e) {
e.printStackTrace();
}
}
}
static void popupMessage(String tag, String text, MessageType status)
{
if (trayIcon != null)
{
trayIcon.displayMessage(tag, text, status);
}
else
{
setupTrayIcon();
trayIcon.displayMessage(tag, text, status);
}
}
static boolean getBit(long src, int position)
{
return ((src >> position) & 1L) > 0;
}
static boolean getBit(byte src, int position)
{
return ((src >> position) & 1) > 0;
}
static long getiBit(long src, int position)
{
return ((src >> position) & 1L);
}
static long setBit(long src, int pos, boolean value)
{
if(value)
return src | (1 << pos);
else
return src & ~(1 << pos);
}
static int velocityOn = 64;
static int velocityOff = 64;
static long notestates = 0L;
private static MidiDevice midi;
static class KeysImages
{
private static BufferedImage keyL;
private static BufferedImage keyLA;
private static BufferedImage keyA;
private static BufferedImage keyAA;
private static BufferedImage keyM;
private static BufferedImage keyMA;
private static BufferedImage keyR;
private static BufferedImage keyRA;
public static void setup() throws IOException
{
keyL = l("keyL10x50");
keyLA = l("keyL10x50_activated");
keyA = l("keyAlt6x26");
keyAA = l("keyAlt6x26_activated");
keyR = l("keyR10x50");
keyRA = l("keyR10x50_activated");
keyM = l("keyM9x50");
keyMA = l("keyM9x50_activated");
}
private static BufferedImage l(String name) throws IOException
{
return ImageIO.read(new File("images/"+name+".png"));
}
public static BufferedImage getKeyImg(int pos, boolean pressed)
{
pos = pos % 12;
if(pos == 0 || pos == 5)
return pressed ? keyLA : keyL;
if(pos == 1 || pos == 3 || pos == 6 || pos == 8 || pos == 10)
return pressed ? keyAA : keyA;
if(pos == 4 || pos == 11)
return pressed ? keyRA : keyR;
return pressed ? keyMA : keyM;
}
}
static void updateNotestates(long new_notes)
{
if(midi != null)
{
try
{
if(midi.isOpen())
{
Receiver in = midi.getReceiver();
for(int i = 3; i < 63; i++)
{
if(getiBit(new_notes, i) > getiBit(notestates, i))
in.send(noteMsg(i, true), -1);
if(getiBit(new_notes, i) < getiBit(notestates, i))
in.send(noteMsg(i, false), -1);
}
}
}
catch(Exception ex)
{
System.err.println("Failed to update notes from "+Long.toBinaryString(notestates)+" to "+Long.toBinaryString(new_notes));
}
}
notestates = new_notes;
kv.repaint();
}
static MidiMessage noteMsg(int note, boolean on) throws InvalidMidiDataException
{
return new ShortMessage(on ? ShortMessage.NOTE_ON : ShortMessage.NOTE_OFF, 36+note, on ? velocityOn : velocityOff);
}
static class KeyboardView extends JPanel{
public void paint(Graphics g)
{
int pointer = 0;
for(int octave = 0; octave < 5; octave++)
{
for(int note = 0; note < 12; note++)
{
BufferedImage img = KeysImages.getKeyImg(note, getBit(notestates, octave*12+note+2));
if(note == 1 || note == 3 || note == 6 || note == 8 || note == 10)
pointer -= img.getWidth() / 2;
g.drawImage(img, pointer, 0, img.getWidth(), img.getHeight(), null);
if(note == 1 || note == 3 || note == 6 || note == 8 || note == 10)
pointer += img.getWidth() / 2;
else
pointer += img.getWidth();
}
}
BufferedImage img = KeysImages.getKeyImg(0, getBit(notestates, 62));
g.drawImage(img, pointer, 0, img.getWidth(), img.getHeight(), null);
}
}
static class ArduinoListener implements SerialPortEventListener {
BitSet bs = new BitSet(64);
public ArduinoListener()
{
}
@Override
public void serialEvent(SerialPortEvent e) {
if(e.isBREAK() || e.isERR())
{
System.out.println("SerialPort broken!");
stopPort();
return;
}
if(e.isRXCHAR())
{
try
{
for(int i = 0; i < e.getEventValue(); i++)
{
for(int z = 0; z < 56; z++)
bs.set(z, bs.get(z+8));
byte b = serial_port.readBytes(1)[0];
for(int z = 0; z < 8; z++)
bs.set(56+z, getBit(b, z));
System.out.print(Long.toBinaryString(bs.toLongArray()[0]));
System.out.print(bs.get(0));
System.out.print(bs.get(1));
System.out.println(bs.get(63));
if(bs.get(0) && bs.get(1) && bs.get(63))
{
updateNotestates(bs.toLongArray()[0]);
}
}
}
catch(Exception eo)
{
System.err.println(eo.toString());
eo.printStackTrace();
}
}
}
}
}
#define DELAY 90
#define O_1 2
#define O_2 3
#define O_3 4
#define O_4 5
#define O_5 6
#define O_6 7
#define O_7 8
#define O_8 9
#define I_1 10
#define I_2 11
#define I_3 A0
#define I_4 A5
#define I_5 A4
#define I_6 A3
#define I_7 A2
#define I_8 A1
uint8_t getOut(byte id)
{
if(id == 0)
return O_1;
if(id == 1)
return O_2;
if(id == 2)
return O_3;
if(id == 3)
return O_4;
if(id == 4)
return O_5;
if(id == 5)
return O_6;
if(id == 6)
return O_7;
if(id == 7)
return O_8;
return -1;
}
uint8_t getIn(byte id)
{
if(id == 0)
return I_1;
if(id == 1)
return I_2;
if(id == 2)
return I_3;
if(id == 3)
return I_4;
if(id == 4)
return I_5;
if(id == 5)
return I_6;
if(id == 6)
return I_7;
if(id == 7)
return I_8;
return -1;
}
byte setBit(byte src, int pos, bool value)
{
if(value)
return src | (1 << pos);
else
return src & ~(1 << pos);
}
byte msg_0 = 3;
byte msg_1 = 0;
byte msg_2 = 0;
byte msg_3 = 0;
byte msg_4 = 0;
byte msg_5 = 0;
byte msg_6 = 0;
byte msg_7 = 128;
void setup() {
pinMode(13, OUTPUT);
digitalWrite(13, LOW);
for(int i = 0; i < 8; i++)
{
pinMode(getOut(i), OUTPUT);
}
for(int i = 0; i < 8; i++)
pinMode(getIn(i), INPUT);
Serial.begin(9600);
}
void set_msg(int pos, bool val)
{
if(pos >=0 && pos <= 6)
{
msg_0 = setBit(msg_0, pos+2, val);
}
else
if(pos < 61)
{
pos = pos-6;
if(pos / 8 == 0)
{
msg_1 = setBit(msg_1, pos%8, val);
}
if(pos / 8 == 1)
{
msg_2 = setBit(msg_2, pos%8, val);
}
if(pos / 8 == 2)
{
msg_3 = setBit(msg_3, pos%8, val);
}
if(pos / 8 == 3)
{
msg_4 = setBit(msg_4, pos%8, val);
}
if(pos / 8 == 4)
{
msg_5 = setBit(msg_5, pos%8, val);
}
if(pos / 8 == 5)
{
msg_6 = setBit(msg_6, pos%8, val);
}
if(pos / 8 == 6)
{
msg_7 = setBit(msg_7, pos%8, val);
}
}
}
int o;
int j;
void loop()
{
for(o = 0; o < 8; o++)
{
digitalWrite(getOut(o), HIGH);
delay(5);
for(j = 0; j < 2; j++)
{
set_msg(o+8*j, digitalRead(getIn(j)));
}
for(j = 2; j < 8; j++)
{
set_msg(o+8*j, analogRead(getIn(j))>500);
}
digitalWrite(getOut(o), LOW);
}
Serial.write(msg_0);
Serial.write(msg_1);
Serial.write(msg_2);
Serial.write(msg_3);
Serial.write(msg_4);
Serial.write(msg_5);
Serial.write(msg_6);
Serial.write(msg_7);
delay(DELAY);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment