/** * player.java * * Generates timestaped MIDI events and adds them to a MIDI Track * Manages time by keepin track of the accumulated tick count * Manages channels used by this player * * This base abtract class has methods to generate common utility MIDI events * such as SYSEX and meta events, and "playing rests" * * The abstract "play()" method which generates the actual musical MIDI events * is to be implementd by specific types of player * * This module is part of Karl Brown's MIDI programming project. * */ package MidiApps; import java.io.*; import java.util.*; import javax.sound.midi.*; /** * * @author Karl Brown * @version 1.0 * last updated 2/16/2003 */ public abstract class player { static byte NOTE_ON =(byte) 0x90; static byte NOTE_OFF =(byte) 0x80; static int OMNI_OFF = 0x7C; static int OMNI_ON = 0x7D; static int POLY_OFF = 0x7E; static int POLY_ON = 0x7F; tracer tr; //we can use channels 0-9 & 11-15; channel 10 is always drums static int MAXNOTES = 15; static int MAXCHANS = 15; int midiPitch[] = new int[MAXNOTES]; int velOn; int velOff; long duration = 0; long tickCount = 0; //long tickOn; //long tickOff; int channel[] = new int[MAXCHANS]; int chanCount; int pitchCount; int patch; Track t; ShortMessage mm; SysexMessage sm; MetaMessage mt; MidiEvent me; public void setTracer(tracer trc) { tr = trc; } public void setVelOn(int velIn) { velOn = velIn; } public void setVelOff(int velIn) { velOff = velIn; } public void setDur(long durIn) { duration = durIn; } public long getDuration() { return duration; } public long getTickCount() { return tickCount; } public void addChannel(int chanIn) { channel[chanCount] = chanIn; chanCount++; } public void resetChannel(int chanIn) { for (int i = 0; i < MAXNOTES; i++) { channel[i] = 0; } chanCount = 0; } public void addMidiPitch(int pitchIn) { midiPitch[pitchCount] = pitchIn; pitchCount++; } public void resetPitch() { for (int i = 0; i < MAXNOTES; i++) { midiPitch[i] = 0; } pitchCount = 0; } public void setTrack(Track trackIn) { t = trackIn; } public void setTick(long tickIn) { tickCount = tickIn; } // SYSEX EVENTS /** * * generate General MIDI On sysex event * to tell MIDI instrument to use General MIDI sound set * */ public void generalMidiOn() { try { // General MIDI sysex; byte[] b = {(byte)0xF0, (byte)0x7E, (byte)0x7F, (byte)0x09, (byte)0x01, (byte)0xF7}; SysexMessage sm = new SysexMessage(); sm.setMessage(b, 6); MidiEvent me = new MidiEvent(sm,getTickCount()); t.add(me); } //try catch(Exception e) { tr.trace("player::generalMidiOn: Exception caught:"); tr.trace(e.toString()); } //catch } public void generalMidiOn(long tickIn) { setTick(tickIn); generalMidiOn(); } // META EVENTS /** * * generate MIDI tempo meta event to set length of each tick * beginning with current timestamp until nexttempo event or end of piece. * */ public void setTempo(byte byte1, byte byte2, byte byte3 ) { // set tempo (meta event); try { mt = new MetaMessage(); byte[] bt = {byte1, (byte)byte2, (byte)byte3}; mt.setMessage(0x51 ,bt, 3); me = new MidiEvent(mt,getTickCount()); t.add(me); } //try catch(Exception e) { tr.trace("player::setTempo: Exception caught:"); tr.trace(e.toString()); } //catch } /** * * generate MIDI meta event to set track name * */ public void setTrackName(String nameIn ) { // set track name (meta event); try { mt = new MetaMessage(); String trackName = new String(nameIn); mt.setMessage(0x03 ,trackName.getBytes(), trackName.length()); me = new MidiEvent(mt,getTickCount()); t.add(me); } //try catch(Exception e) { tr.trace("player::setTrackName: Exception caught:"); tr.trace(e.toString()); } //catch } /** * * generate MIDI meta event to set end of track * */ public void endOfTrack() { // set end of track (meta event); try { mt = new MetaMessage(); byte[] bet = {}; mt.setMessage(0x2F,bet,0); me = new MidiEvent(mt, getTickCount()); t.add(me); } //try catch(Exception e) { tr.trace("player::endOfTrack: Exception caught:"); tr.trace(e.toString()); } //catch } //CHANNEL EVENTS /** * * generate Omni On channel mode event * to specified channel * */ public void omniOn(int chan) { try { // set omni on; mm = new ShortMessage(); mm.setMessage((byte)(0xB0 + (byte)chan), (byte)OMNI_ON,0x00); me = new MidiEvent(mm,getTickCount()); t.add(me); } //try catch(Exception e) { tr.trace("player::omniOn: Exception caught:"); tr.trace(e.toString()); } //catch } /** * * generate Omni On channel mode event * to all channels managed by this player * */ public void omniOn() { for (int i = 0; i < chanCount; i++) { omniOn(channel[i]); } } /** * * generate Omni Off channel mode event * to specified channel * */ public void omniOff(int chan) { try { // set omni off; mm = new ShortMessage(); mm.setMessage((byte)(0xB0 + (byte)chan),(byte)OMNI_OFF,0x00); me = new MidiEvent(mm,getTickCount()); t.add(me); } //try catch(Exception e) { tr.trace("player::omniOff: Exception caught:"); tr.trace(e.toString()); } //catch } /** * * generate Omni Off channel mode event * to all channels managed by this player * */ public void omniOff() { for (int i = 0; i < chanCount; i++) { omniOff(channel[i]); } } /** * * generate Poly On channel mode event * to specified channel * */ public void polyOn(int chan) { try { // set poly on; mm = new ShortMessage(); mm.setMessage((byte)(0xB0 + (byte)chan), (byte)POLY_OFF, 0x00); me = new MidiEvent(mm,getTickCount()); t.add(me); } //try catch(Exception e) { tr.trace("player::polyOn: Exception caught:"); tr.trace(e.toString()); } //catch } /** * * generate Poly On channel mode event * to all channels managed by this player * */ public void polyOn() { for (int i = 0; i < chanCount; i++) { polyOn(channel[i]); } } /** * * generate Poly Off channel mode event * to specified channel * */ public void polyOff(int chan) { try { // set poly off; mm = new ShortMessage(); mm.setMessage((byte)(0xB0 + (byte)chan), (byte)POLY_OFF,0x00); me = new MidiEvent(mm,getTickCount()); t.add(me); } //try catch(Exception e) { tr.trace("player::polyOff: Exception caught:"); tr.trace(e.toString()); } //catch } /** * * generate Poly Off channel mode event * to all channels managed by this player * */ public void polyOff() { for (int i = 0; i < chanCount; i++) { polyOff(channel[i]); } } /** * * generate Program Change channel voice event * to specified channel * */ public void setPatch(int patch, int chan) { try { // set instrument on channel; mm = new ShortMessage(); mm.setMessage((byte)(0xC0 + (byte)chan), (byte)patch, 0x00); me = new MidiEvent(mm,getTickCount()); t.add(me); // tr.trace("player::setPatch: Patch set: " + patch + " on chan: " + chan); } //try catch(Exception e) { tr.trace("player::setPatch: Exception caught:"); tr.trace(e.toString()); } //catch } /** * * generate Program Change channel voice event * to all channels managed by this player * */ public void setPatch(int patchIn) { patch = patchIn; for (int i = 0; i < chanCount; i++) { setPatch(patch, channel[i]); } } /** * * Increment the tick count managed by this player, * which is used to set the timespamp of MIDI events * */ protected void incTickCount(long countIn) { tickCount += countIn; } /** * * "Play" a rest not by generating a MIDI event, * but rather by incrementing the tick count before the next event * by the specified length * */ public void playRest(long durIn) { setDur(durIn); playRest(); } /** * * "Play" a rest not by generating a MIDI event, * but rather by incrementing the tick count before the next event * by the length already set in ticks * */ public void playRest() { incTickCount(duration); } /** * * Abtract class method to play one or more notes * to be instantialted by specific type of player * The implemented method must: * Generate all pitch bend and note on events timestamped by current tick count * Increment the tick count by the length of the note or chord * Generate all note off events timestamped by the incremented tick count. * the specified duration * */ public abstract void play(); }