aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Baines <cbaines8@gmail.com>2011-09-16 12:07:52 +0100
committerChristopher Baines <cbaines8@gmail.com>2011-09-16 12:07:52 +0100
commit792fa79119050016b992f1947e93abdd17d0a6d3 (patch)
tree743f141b09d51e29a354957e7697f8068a398359
parent4fa115f2270fde1acfb9da36e8b47166190f03da (diff)
downloadpunchingbag-792fa79119050016b992f1947e93abdd17d0a6d3.tar
punchingbag-792fa79119050016b992f1947e93abdd17d0a6d3.tar.gz
Started working on midi stuff
-rw-r--r--PunchingBag/src/uk/ac/open/punchingbag/PunchingBagGUI.java6
-rw-r--r--PunchingBag/src/uk/ac/open/punchingbag/examples/SimpleKeyboard.java209
2 files changed, 206 insertions, 9 deletions
diff --git a/PunchingBag/src/uk/ac/open/punchingbag/PunchingBagGUI.java b/PunchingBag/src/uk/ac/open/punchingbag/PunchingBagGUI.java
index 4acb2d0..e03c4e6 100644
--- a/PunchingBag/src/uk/ac/open/punchingbag/PunchingBagGUI.java
+++ b/PunchingBag/src/uk/ac/open/punchingbag/PunchingBagGUI.java
@@ -64,7 +64,7 @@ public class PunchingBagGUI implements MouseListener, MouseMotionListener,
Debug, Menu
};
- Mode currentMode = Mode.Menu;
+ Mode currentMode = Mode.Debug;
JFrame frame;
@@ -146,8 +146,8 @@ public class PunchingBagGUI implements MouseListener, MouseMotionListener,
programOptionsPanel.add(new JButton("Debug"));
frame.add(bagDebugingPanel);
- frame.add(new DisplayPanel(), BorderLayout.CENTER);
- frame.add(programOptionsPanel, BorderLayout.SOUTH);
+ //frame.add(new DisplayPanel(), BorderLayout.CENTER);
+ //frame.add(programOptionsPanel, BorderLayout.SOUTH);
setMode(currentMode);
diff --git a/PunchingBag/src/uk/ac/open/punchingbag/examples/SimpleKeyboard.java b/PunchingBag/src/uk/ac/open/punchingbag/examples/SimpleKeyboard.java
index a8d32ff..2d91735 100644
--- a/PunchingBag/src/uk/ac/open/punchingbag/examples/SimpleKeyboard.java
+++ b/PunchingBag/src/uk/ac/open/punchingbag/examples/SimpleKeyboard.java
@@ -4,6 +4,17 @@ import java.awt.Color;
import java.io.File;
import java.io.IOException;
+import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.MetaEventListener;
+import javax.sound.midi.MetaMessage;
+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;
@@ -16,7 +27,7 @@ import uk.ac.open.punchingbag.Contact;
import uk.ac.open.punchingbag.PunchingBag;
public class SimpleKeyboard implements ButtonListener {
-
+
String soundDir = System.getProperty("user.dir")
+ System.getProperty("file.separator");
@@ -24,13 +35,89 @@ public class SimpleKeyboard implements ButtonListener {
new File(soundDir + "C5.wav"), new File(soundDir + "D5.wav"),
new File(soundDir + "E5.wav"), new File(soundDir + "F5.wav"),
new File(soundDir + "G5.wav") };
-
+
static PunchingBag bag = PunchingBag.getBag();
- public static void main(String[] args) {
+ 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;
+
+ 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 {
new SimpleKeyboard();
+
+ int instrument = 0;
+ int tempo = 120;
+ String filename = null;
+
+ // Parse the options
+ // -i <instrument number> default 0, a piano. Allowed values: 0-127
+ // -t <beats per minute> default tempo is 120 quarter notes per minute
+ // -o <filename> save to a midi file instead of playing
+ int a = 0;
+ while (a < args.length) {
+ if (args[a].equals("-i")) {
+ instrument = Integer.parseInt(args[a + 1]);
+ a += 2;
+ } else if (args[a].equals("-t")) {
+ tempo = Integer.parseInt(args[a + 1]);
+ a += 2;
+ } else if (args[a].equals("-o")) {
+ filename = args[a + 1];
+ a += 2;
+ } else
+ break;
+ }
+
+ char[] notes = args[a].toCharArray();
+
+ // 16 ticks per quarter note.
+ Sequence sequence = new Sequence(Sequence.PPQ, 16);
+
+ // Add the specified notes to the track
+ addTrack(sequence, instrument, tempo, notes);
+
+ if (filename == null) { // no filename, so play the notes
+ // Set up the Sequencer and Synthesizer objects
+ Sequencer sequencer = MidiSystem.getSequencer();
+ sequencer.open();
+ Synthesizer synthesizer = MidiSystem.getSynthesizer();
+ synthesizer.open();
+ sequencer.getTransmitter().setReceiver(synthesizer.getReceiver());
+
+ // Specify the sequence to play, and the tempo to play it at
+ sequencer.setSequence(sequence);
+ sequencer.setTempoInBPM(tempo);
+
+ // Let us know when it is done playing
+ sequencer.addMetaEventListener(new MetaEventListener() {
+ public void meta(MetaMessage m) {
+ // A message of this type is automatically sent
+ // when we reach the end of the track
+ if (m.getType() == END_OF_TRACK)
+ System.exit(0);
+ }
+ });
+ // And start playing now.
+ sequencer.start();
+ } else { // A file name was specified, so save the notes
+ int[] allowedTypes = MidiSystem.getMidiFileTypes(sequence);
+ if (allowedTypes.length == 0) {
+ System.err.println("No supported MIDI file types.");
+ } else {
+ MidiSystem.write(sequence, allowedTypes[0], new File(filename));
+ System.exit(0);
+ }
+ }
}
-
+
public SimpleKeyboard() {
bag.addButtonListener(this);
try {
@@ -63,7 +150,7 @@ public class SimpleKeyboard implements ButtonListener {
playKey(0);
}
}
-
+
void playKey(int key) {
try {
AudioInputStream sound = AudioSystem.getAudioInputStream(keys[key]);
@@ -87,8 +174,118 @@ public class SimpleKeyboard implements ButtonListener {
@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));
+ }
}