package org.jpc.emulator.peripheral;
import org.jpc.j2se.Option;
import javax.sound.midi.*;
import java.io.InputStream;
import java.util.logging.*;
public class Midi
{
private static final Logger Log = Logger.getLogger(Midi.class.getName());
static final private int SYSEX_SIZE = 1024;
static final private int RAWBUF = 1024;
static final private byte[] MIDI_evt_len = new byte[] {
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x00
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x10
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x20
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x30
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x40
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x50
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x60
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x70
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0x80
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0x90
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xa0
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xb0
2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, // 0xc0
2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, // 0xd0
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xe0
0,2,3,2, 0,0,1,0, 1,0,1,1, 1,0,1,0 // 0xf0
};
static private class _midi {
int status;
int cmd_len;
int cmd_pos;
byte[] cmd_buf = new byte[8];
byte[] rt_buf = new byte[8];
public static class Sysex {
byte[] buf = new byte[SYSEX_SIZE];
int used;
int delay;
long start;
}
public Sysex sysex = new Sysex();
Receiver handler;
MidiDevice device;
}
static private _midi midi;
static private ShortMessage msg = new ShortMessage();
static private SysexMessage sysex_msg = new SysexMessage();
static public void MIDI_RawOutByte(/*Bit8u*/int data)
{
if (midi.sysex.start!=0) {
/*Bit32u*/long passed_ticks = System.currentTimeMillis() - midi.sysex.start;
if (passed_ticks < midi.sysex.delay) try {Thread.sleep(midi.sysex.delay - passed_ticks);} catch (InterruptedException e){}
}
/* Test for a realtime MIDI message */
if (data>=0xf8) {
try {msg.setMessage(data);} catch (Exception e) {}
midi.handler.send(msg, -1);
return;
}
/* Test for a active sysex tranfer */
if (midi.status==0xf0) {
if ((data&0x80)==0) {
if (midi.sysex.used<(SYSEX_SIZE-1)) midi.sysex.buf[midi.sysex.used++]=(byte)data;
return;
} else {
midi.sysex.buf[midi.sysex.used++]=(byte)0xf7;
if ((midi.sysex.start!=0) && (midi.sysex.used >= 4) && (midi.sysex.used <= 9) && (midi.sysex.buf[1] == 0x41) && (midi.sysex.buf[3] == 0x16)) {
Log.log(Level.WARNING,"MIDI:Skipping invalid MT-32 SysEx midi message (too short to contain a checksum)");
} else {
// LOG(LOG_ALL,LOG_NORMAL)("Play sysex; address:%02X %02X %02X, length:%4d, delay:%3d", midi.sysex.buf[5], midi.sysex.buf[6], midi.sysex.buf[7], midi.sysex.used, midi.sysex.delay);
try {sysex_msg.setMessage(midi.sysex.buf, midi.sysex.used);} catch (Exception e) {}
midi.handler.send(sysex_msg, -1);
if (midi.sysex.start!=0) {
if (midi.sysex.buf[5] == 0x7F) {
midi.sysex.delay = 290; // All Parameters reset
} else if (midi.sysex.buf[5] == 0x10 && midi.sysex.buf[6] == 0x00 && midi.sysex.buf[7] == 0x04) {
midi.sysex.delay = 145; // Viking Child
} else if (midi.sysex.buf[5] == 0x10 && midi.sysex.buf[6] == 0x00 && midi.sysex.buf[7] == 0x01) {
midi.sysex.delay = 30; // Dark Sun 1
} else midi.sysex.delay = (/*Bitu*/int)(((float)(midi.sysex.used) * 1.25f) * 1000.0f / 3125.0f) + 2;
midi.sysex.start = System.currentTimeMillis();
}
}
Log.log(Level.INFO,"Sysex message size "+midi.sysex.used);
// if (CaptureState & CAPTURE_MIDI) {
// CAPTURE_AddMidi( true, midi.sysex.used-1, &midi.sysex.buf[1]);
// }
}
}
if ((data&0x80)!=0) {
midi.status=data;
midi.cmd_pos=0;
midi.cmd_len=MIDI_evt_len[data];
if (midi.status==0xf0) {
midi.sysex.buf[0]=(byte)0xf0;
midi.sysex.used=1;
}
}
if (midi.cmd_len!=0) {
midi.cmd_buf[midi.cmd_pos++]=(byte)data;
if (midi.cmd_pos >= midi.cmd_len) {
// if (CaptureState & CAPTURE_MIDI) {
// CAPTURE_AddMidi(false, midi.cmd_len, midi.cmd_buf);
// }
try {msg.setMessage(midi.cmd_buf[0], midi.cmd_buf[1], midi.cmd_buf[2]);} catch (Exception e) {}
midi.handler.send(msg, -1);
midi.cmd_pos=1; //Use Running status
}
}
}
static public boolean MIDI_Available() {
return midi.device != null;
}
public Midi() {
String dev = Option.mididevice.value("default");
String conf = Option.midiconfig.value("");
/* If device = "default" go for first handler that works */
// MAPPER_AddHandler(MIDI_SaveRawEvent,MK_f8,MMOD1|MMOD2,"caprawmidi","Cap MIDI");
midi.sysex.delay = 0;
midi.sysex.start = 0;
if (conf.contains("delaysysex")) {
midi.sysex.start = System.currentTimeMillis();
conf = conf.replace("delaysysex", "").trim();
Log.log(Level.INFO, "MIDI:Using delayed SysEx processing");
}
midi.status=0x00;
midi.cmd_pos=0;
midi.cmd_len=0;
MidiDevice.Info[] devices = null;
try {
devices = MidiSystem.getMidiDeviceInfo();
} catch (Exception e) {
}
boolean def = dev.equalsIgnoreCase("default");
if (!def) {
if (devices != null) {
for (int i=0;i<devices.length;i++) {
if (devices[i].getName().equalsIgnoreCase(dev)) {
try {
MidiDevice device = MidiSystem.getMidiDevice(devices[0]);
device.open();
midi.handler = device.getReceiver();
midi.device = device;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
if (midi.handler==null) {
Log.log(Level.INFO, "MIDI:Can't find device:"+dev+", finding default handler.");
}
}
if (midi.handler == null) {
Synthesizer synth = null;
Soundbank soundbank = null;
try {
synth=MidiSystem.getSynthesizer();
} catch (Exception e) {
}
if (synth != null) {
String fileName = "resources/soundbank-deluxe.gm";
InputStream is = Midi.class.getResourceAsStream(fileName);
if (is == null) {
fileName = "resources/soundbank-mid.gm";
is = Midi.class.getResourceAsStream(fileName);
}
if (is == null) {
fileName = "resources/soundbank-min.gm";
is = Midi.class.getResourceAsStream(fileName);
}
if (is != null) {
try {
soundbank = MidiSystem.getSoundbank(is);
} catch (Exception e) {
}
try {is.close();} catch (Exception e) {}
}
if (soundbank == null)
{
fileName = "default";
try {
soundbank = synth.getDefaultSoundbank();
} catch (Exception e) {}
}
if (soundbank != null) {
try {
synth.open();
if (synth.isSoundbankSupported(soundbank) && synth.loadAllInstruments(soundbank)) {
Log.log(Level.INFO, "MIDI: Using Soundbank: "+fileName);
midi.handler = synth.getReceiver();
midi.device = synth;
} else {
synth.close();
}
} catch (Exception e) {
}
}
}
}
if (devices != null) {
for (int i=0;i<devices.length && midi.handler == null;i++) {
try {
MidiDevice device = MidiSystem.getMidiDevice(devices[i]);
device.open();
midi.handler = device.getReceiver();
midi.device = device;
Log.log(Level.INFO, "MIDI:Opened device:"+devices[i].getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
if (midi.device == null) {
Log.log(Level.INFO, "MIDI:Can't find any device");
}
}
private static Midi test;
public static void MIDI_Destroy() {
if(midi.device!=null) {
midi.handler.close();
midi.device.close();
}
test = null;
midi = null;
}
public static void MIDI_Init() {
midi = new _midi();
test = new Midi();
}
}