package uk.ac.open.punchingbag.examples; import java.awt.Color; import java.awt.Rectangle; import java.io.File; import java.io.IOException; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MidiEvent; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Sequence; import javax.sound.midi.Sequencer; import javax.sound.midi.ShortMessage; import javax.sound.midi.Synthesizer; import javax.sound.midi.Track; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.DataLine; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; import uk.ac.open.punchingbag.ButtonListener; import uk.ac.open.punchingbag.Contact; import uk.ac.open.punchingbag.PunchingBag; public class Keyboard implements Runnable, ButtonListener { Sequence[] notes = new Sequence[20]; static PunchingBag bag = PunchingBag.getBag(); long lastActionTime = System.currentTimeMillis(); boolean noising = false; public static final int DAMPER_PEDAL = 64; public static final int DAMPER_ON = 127; public static final int DAMPER_OFF = 0; public static final int END_OF_TRACK = 47; Sequencer sequencer; int instrument = 0; static final int[] offsets = { // add these amounts to the base value // A B C D E F G -4, -2, 0, 1, 3, 5, 7 }; public static void main(String[] args) throws InvalidMidiDataException, MidiUnavailableException, IOException { try { // bag.connectToArduinos(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } new SimpleKeyboard(); } public Keyboard() { bag.addButtonListener(this); try { System.out.println("Generating Keys"); genKeys(); System.out.println("Finished Generating Keys"); /*System.out.println("Testing Keys"); for (int i=0; i<5; i++) { System.out.println("Playing " + i); playKey(i); System.out.println("Played " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }*/ } catch (InvalidMidiDataException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MidiUnavailableException e) { // TODO Auto-generated catch block e.printStackTrace(); } new Thread(this).start(); } public void buttonPressed(int x, int y) { if (noising = true) { bag.clearLEDs(); noising = false; } lastActionTime = System.currentTimeMillis(); System.out.println("Button Pressed: " + x + " " + y); bag.fillRect(0, y, 9, 2, Color.red, 500); try { playKey(y - 10); } catch (InvalidMidiDataException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MidiUnavailableException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } void genKeys() throws InvalidMidiDataException, MidiUnavailableException { for (int key = 0; key < 20; key++) { instrument++; // 16 ticks per quarter note. Sequence sequence = new Sequence(Sequence.PPQ, 16); // Add the specified notes to the track Track track = sequence.createTrack(); // Begin with a new track ShortMessage sm = new ShortMessage(); sm.setMessage(ShortMessage.PROGRAM_CHANGE, 0, instrument, 0); track.add(new MidiEvent(sm, 0)); // These values persist and apply to all notes 'till changed int notelength = 16; // default to quarter notes int velocity = 64; // default to middle volume int basekey = 60; // 60 is middle C. Adjusted up and down by octave boolean sustain = false; // is the sustain pedal depressed? int numnotes = 0; // How many notes in current chord? int t = 0; // time in ticks for the composition /* * ShortMessage m = new ShortMessage(); * m.setMessage(ShortMessage.CONTROL_CHANGE, 0, DAMPER_PEDAL, * sustain ? DAMPER_ON : DAMPER_OFF); track.add(new MidiEvent(m, * t)); */ ShortMessage on = new ShortMessage(); on.setMessage(ShortMessage.NOTE_ON, 0, basekey + key, velocity); ShortMessage off = new ShortMessage(); off.setMessage(ShortMessage.NOTE_OFF, 0, basekey + key, velocity); track.add(new MidiEvent(on, t)); track.add(new MidiEvent(off, t + notelength)); notes[key] = sequence; } // Set up the Sequencer and Synthesizer objects sequencer = MidiSystem.getSequencer(); sequencer.open(); Synthesizer synthesizer = MidiSystem.getSynthesizer(); synthesizer.open(); sequencer.getTransmitter().setReceiver(synthesizer.getReceiver()); } void playKey(int key) throws InvalidMidiDataException, MidiUnavailableException, IOException { int tempo = 120; // Specify the sequence to play, and the tempo to play it at sequencer.setSequence(notes[key + 10]); sequencer.setTempoInBPM(tempo); // And start playing now. if (sequencer.isRunning()) { System.out.println("Is Running"); sequencer.stop(); sequencer.setTickPosition(sequencer.getLoopStartPoint()); sequencer.start(); } else { System.out.println("Not Running"); sequencer.setTickPosition(sequencer.getLoopStartPoint()); sequencer.start(); } } @Override public void contact(Contact c) { // TODO Auto-generated method stub } /* * This method parses the specified char[] of notes into a Track. The * musical notation is the following: A-G: A named note; Add b for flat and * # for sharp. +: Move up one octave. Persists. -: Move down one octave. * Persists. /1: Notes are whole notes. Persists 'till changed /2: Half * notes /4: Quarter notes /n: N can also be, 8, 16, 32, 64. s: Toggle * sustain pedal on or off (initially off) >: Louder. Persists <: Softer. * Persists .: Rest. Length depends on current length setting Space: Play * the previous note or notes; notes not separated by spaces are played at * the same time */ public static void addTrack(Sequence s, int instrument, int tempo, char[] notes) throws InvalidMidiDataException { Track track = s.createTrack(); // Begin with a new track // Set the instrument on channel 0 ShortMessage sm = new ShortMessage(); sm.setMessage(ShortMessage.PROGRAM_CHANGE, 0, instrument, 0); track.add(new MidiEvent(sm, 0)); int n = 0; // current character in notes[] array int t = 0; // time in ticks for the composition // These values persist and apply to all notes 'till changed int notelength = 16; // default to quarter notes int velocity = 64; // default to middle volume int basekey = 60; // 60 is middle C. Adjusted up and down by octave boolean sustain = false; // is the sustain pedal depressed? int numnotes = 0; // How many notes in current chord? while (n < notes.length) { char c = notes[n++]; if (c == '+') basekey += 12; // increase octave else if (c == '-') basekey -= 12; // decrease octave else if (c == '>') velocity += 16; // increase volume; else if (c == '<') velocity -= 16; // decrease volume; else if (c == '/') { char d = notes[n++]; if (d == '2') notelength = 32; // half note else if (d == '4') notelength = 16; // quarter note else if (d == '8') notelength = 8; // eighth note else if (d == '3' && notes[n++] == '2') notelength = 2; else if (d == '6' && notes[n++] == '4') notelength = 1; else if (d == '1') { if (n < notes.length && notes[n] == '6') notelength = 4; // 1/16th note else notelength = 64; // whole note } } else if (c == 's') { sustain = !sustain; // Change the sustain setting for channel 0 ShortMessage m = new ShortMessage(); m.setMessage(ShortMessage.CONTROL_CHANGE, 0, DAMPER_PEDAL, sustain ? DAMPER_ON : DAMPER_OFF); track.add(new MidiEvent(m, t)); } else if (c >= 'A' && c <= 'G') { int key = basekey + offsets[c - 'A']; if (n < notes.length) { if (notes[n] == 'b') { // flat key--; n++; } else if (notes[n] == '#') { // sharp key++; n++; } } addNote(track, t, notelength, key, velocity); numnotes++; } else if (c == ' ') { // Spaces separate groups of notes played at the same time. // But we ignore them unless they follow a note or notes. if (numnotes > 0) { t += notelength; numnotes = 0; } } else if (c == '.') { // Rests are like spaces in that they force any previous // note to be output (since they are never part of chords) if (numnotes > 0) { t += notelength; numnotes = 0; } // Now add additional rest time t += notelength; } } } // A convenience method to add a note to the track on channel 0 public static void addNote(Track track, int startTick, int tickLength, int key, int velocity) throws InvalidMidiDataException { ShortMessage on = new ShortMessage(); on.setMessage(ShortMessage.NOTE_ON, 0, key, velocity); ShortMessage off = new ShortMessage(); off.setMessage(ShortMessage.NOTE_OFF, 0, key, velocity); track.add(new MidiEvent(on, startTick)); track.add(new MidiEvent(off, startTick + tickLength)); } @Override public void run() { while (true) { System.out.println("Checking last action time"); if ((System.currentTimeMillis() - lastActionTime) > 20000) { System.out.println("Activating Noise"); bag.noise(new Rectangle(0, 0, 9, 20)); noising = true; } else { System.out.println("Waiting more"); } try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }