From d050f7c0374259464086de048e332f8d34c1e768 Mon Sep 17 00:00:00 2001 From: AShiningRay Date: Wed, 16 Oct 2024 18:56:02 -0300 Subject: [PATCH] media: Rework custom midi implementation Now it only sets up the midi soundfont and synth a single time inside Manager, with PlatformPlayer then re-using it on any loaded midi streams. This should vastly reduce memory usage and improve performance on jars that use a myriad of different midi. --- src/javax/microedition/media/Manager.java | 52 ++++++++++++++++ src/org/recompile/freej2me/Anbu.java | 12 ++-- src/org/recompile/freej2me/FreeJ2ME.java | 10 +-- src/org/recompile/freej2me/Libretro.java | 65 ++++++++++---------- src/org/recompile/mobile/PlatformPlayer.java | 62 ++++--------------- 5 files changed, 109 insertions(+), 92 deletions(-) diff --git a/src/javax/microedition/media/Manager.java b/src/javax/microedition/media/Manager.java index d495ba18..4ada9bba 100644 --- a/src/javax/microedition/media/Manager.java +++ b/src/javax/microedition/media/Manager.java @@ -21,12 +21,17 @@ import java.io.InputStream; import java.io.File; import java.io.FileOutputStream; +import java.io.FilenameFilter; import java.io.IOException; import java.io.OutputStream; import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; +import javax.sound.midi.MidiSystem; +import javax.sound.midi.Soundbank; +import javax.sound.midi.Synthesizer; + import org.recompile.mobile.Mobile; import org.recompile.mobile.PlatformPlayer; @@ -34,6 +39,14 @@ public final class Manager { public static final String TONE_DEVICE_LOCATOR = "device://tone"; + /* Custom MIDI variables */ + public static boolean useCustomMidi = false; + public static boolean hasLoadedCustomMidi = false; + public static File soundfontDir = new File("freej2me_system" + File.separatorChar + "customMIDI" + File.separatorChar); + private static Soundbank customSoundfont; + public static Synthesizer customSynth; + + /* Default max amount of players in FreeJ2ME's config */ public static PlatformPlayer mediaPlayers[] = new PlatformPlayer[32]; public static byte mediaPlayersIndex = 0; @@ -45,6 +58,8 @@ public final class Manager public static Player createPlayer(InputStream stream, String type) throws IOException, MediaException { + checkCustomMidi(); + stream.mark(1024); String streamMD5 = generateMD5Hash(stream, 1024); stream.reset(); @@ -130,6 +145,7 @@ else if(mediaPlayersIndex == mediaPlayers.length-1) public static Player createPlayer(String locator) throws MediaException { + checkCustomMidi(); System.out.println("Create Player "+locator); return new PlatformPlayer(locator); } @@ -176,4 +192,40 @@ private static String generateMD5Hash(InputStream stream, int byteCount) return null; } + + private static final void checkCustomMidi() + { + /* + * Check if the user wants to run a custom MIDI soundfont. Also, there's no harm + * in checking if the directory exists again. + */ + if(!useCustomMidi || hasLoadedCustomMidi) { return; } + + /* Get the first sf2 soundfont in the directory */ + String[] fontfile = soundfontDir.list(new FilenameFilter() + { + @Override + public boolean accept(File f, String soundfont ) { return soundfont.toLowerCase().endsWith(".sf2"); } + }); + + /* + * Only really set the player to use a custom midi soundfont if there is + * at least one inside the directory. + */ + if(fontfile != null && fontfile.length > 0) + { + try + { + // Load the first .sf2 font available, if there's none that's valid, don't set any and use JVM's default + customSoundfont = MidiSystem.getSoundbank(new File(soundfontDir, fontfile[0])); + customSynth = MidiSystem.getSynthesizer(); + customSynth.open(); + customSynth.loadAllInstruments(customSoundfont); + + hasLoadedCustomMidi = true; // We have now loaded the custom midi soundfont, mark as such so we don't waste time entering here again + } + catch (Exception e) { System.out.println("Manager -> Could not load soundfont: " + e.getMessage());} + } + else { System.out.println("PlatformPlayer: Custom MIDI enabled but there's no soundfont in" + (soundfontDir.getPath() + File.separatorChar)); } + } } diff --git a/src/org/recompile/freej2me/Anbu.java b/src/org/recompile/freej2me/Anbu.java index c7b7cbdf..6fc18d70 100644 --- a/src/org/recompile/freej2me/Anbu.java +++ b/src/org/recompile/freej2me/Anbu.java @@ -27,6 +27,8 @@ import java.io.OutputStream; import java.lang.ProcessBuilder; +import javax.microedition.media.Manager; + public class Anbu { @@ -78,10 +80,10 @@ public Anbu(String args[]) */ try { - if(!PlatformPlayer.soundfontDir.isDirectory()) + if(!Manager.soundfontDir.isDirectory()) { - PlatformPlayer.soundfontDir.mkdirs(); - File dummyFile = new File(PlatformPlayer.soundfontDir.getPath() + File.separatorChar + "Put your sf2 bank here"); + Manager.soundfontDir.mkdirs(); + File dummyFile = new File(Manager.soundfontDir.getPath() + File.separatorChar + "Put your sf2 bank here"); dummyFile.createNewFile(); } } @@ -419,8 +421,8 @@ void settingsChanged() if(rotate.equals("off")) { rotateDisplay = false; } String midiSoundfont = config.settings.get("soundfont"); - if(midiSoundfont.equals("Custom")) { PlatformPlayer.customMidi = true; } - else if(midiSoundfont.equals("Default")) { PlatformPlayer.customMidi = false; } + if(midiSoundfont.equals("Custom")) { Manager.useCustomMidi = true; } + else if(midiSoundfont.equals("Default")) { Manager.useCustomMidi = false; } if (Mobile.nokia) { System.setProperty("microedition.platform", "Nokia6233/05.10"); } else if (Mobile.sonyEricsson) diff --git a/src/org/recompile/freej2me/FreeJ2ME.java b/src/org/recompile/freej2me/FreeJ2ME.java index 55373f6f..856e5d4b 100644 --- a/src/org/recompile/freej2me/FreeJ2ME.java +++ b/src/org/recompile/freej2me/FreeJ2ME.java @@ -100,10 +100,10 @@ public void windowClosing(WindowEvent e) */ try { - if(!PlatformPlayer.soundfontDir.isDirectory()) + if(!Manager.soundfontDir.isDirectory()) { - PlatformPlayer.soundfontDir.mkdirs(); - File dummyFile = new File(PlatformPlayer.soundfontDir.getPath() + File.separatorChar + "Put your sf2 bank here"); + Manager.soundfontDir.mkdirs(); + File dummyFile = new File(Manager.soundfontDir.getPath() + File.separatorChar + "Put your sf2 bank here"); dummyFile.createNewFile(); } } @@ -410,8 +410,8 @@ private void settingsChanged() if(rotate.equals("off")) { rotateDisplay = false; } String midiSoundfont = config.settings.get("soundfont"); - if(midiSoundfont.equals("Custom")) { PlatformPlayer.customMidi = true; } - else if(midiSoundfont.equals("Default")) { PlatformPlayer.customMidi = false; } + if(midiSoundfont.equals("Custom")) { Manager.useCustomMidi = true; } + else if(midiSoundfont.equals("Default")) { Manager.useCustomMidi = false; } if(config.settings.get("halveCanvasRes").equals("on")) { w /= 2; h /= 2; } diff --git a/src/org/recompile/freej2me/Libretro.java b/src/org/recompile/freej2me/Libretro.java index dd31d192..52147144 100644 --- a/src/org/recompile/freej2me/Libretro.java +++ b/src/org/recompile/freej2me/Libretro.java @@ -22,18 +22,17 @@ import java.awt.Canvas; import java.awt.Graphics2D; import java.awt.image.BufferedImage; -import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferByte; import java.util.Timer; import java.util.TimerTask; -import javax.microedition.midlet.MIDlet; - import java.io.File; import java.io.IOException; import java.net.URL; import javax.microedition.media.Manager; +import javax.microedition.midlet.MIDlet; public class Libretro { @@ -65,7 +64,7 @@ public class Libretro private boolean[] pressedKeys = new boolean[128]; private byte[] frameBuffer = new byte[800*800*3]; - private byte[] RGBframeBuffer = new byte[800*800*3]; + private byte[] RGBframeBuffer = new byte[800*800*3]; private byte[] frameHeader = new byte[]{(byte)0xFE, 0, 0, 0, 0, 0}; private int mousex; @@ -103,10 +102,10 @@ public Libretro(String args[]) */ try { - if(!PlatformPlayer.soundfontDir.isDirectory()) + if(!Manager.soundfontDir.isDirectory()) { - PlatformPlayer.soundfontDir.mkdirs(); - File dummyFile = new File(PlatformPlayer.soundfontDir.getPath() + File.separatorChar + "Put your sf2 bank here"); + Manager.soundfontDir.mkdirs(); + File dummyFile = new File(Manager.soundfontDir.getPath() + File.separatorChar + "Put your sf2 bank here"); dummyFile.createNewFile(); } } @@ -135,7 +134,7 @@ public Libretro(String args[]) if(Integer.parseInt(args[6]) == 0) { soundEnabled = false; } - if(Integer.parseInt(args[7]) == 1) { PlatformPlayer.customMidi = true; } + if(Integer.parseInt(args[7]) == 1) { Manager.useCustomMidi = true; } maxmidistreams = Integer.parseInt(args[8]); Manager.updatePlayerNum((byte) maxmidistreams); @@ -145,7 +144,7 @@ public Libretro(String args[]) /* Once it finishes parsing all arguments, it's time to set up freej2me-lr */ - surface = new BufferedImage(lcdWidth, lcdHeight, BufferedImage.TYPE_3BYTE_BGR); // libretro display + surface = new BufferedImage(lcdWidth, lcdHeight, BufferedImage.TYPE_3BYTE_BGR); // libretro display gc = (Graphics2D)surface.getGraphics(); Mobile.setPlatform(new MobilePlatform(lcdWidth, lcdHeight)); @@ -328,7 +327,7 @@ public void run() config.settings.put("fps", ""+limitFPS); - if(!PlatformPlayer.customMidi) { config.settings.put("soundfont", "Default"); } + if(!Manager.useCustomMidi) { config.settings.put("soundfont", "Default"); } else { config.settings.put("soundfont", "Custom"); } config.settings.put("maxmidistreams", ""+maxmidistreams); @@ -413,29 +412,29 @@ public void run() break; case 15: - /* Send Frame to Libretro */ + /* Send Frame to Libretro */ try { - frameBuffer = ((DataBufferByte) surface.getRaster().getDataBuffer()).getData(); - - final int bufferLength = frameBuffer.length; - - /* - * Convert BGR into RGB. Has a negligible performance impact compared to not doing this at all - * and sending the BGR array straight to libretro... and is faster than using getRGB(). - * - * Copying from the original BGR array to a separate RGB array uses a bit more memory, but - * works correctly compared to just swapping the channels on the orignal array, where they - * still unknowingly end up incorrect from time to time. Runtime performance is pretty much - * the same for both methods. - */ - for(int i=0; i>8)&0xFF); frameHeader[2] = (byte)((lcdWidth)&0xFF); @@ -443,7 +442,7 @@ public void run() frameHeader[4] = (byte)((lcdHeight)&0xFF); System.out.write(frameHeader, 0, 6); - System.out.write(RGBframeBuffer, 0, bufferLength); + System.out.write(RGBframeBuffer, 0, bufferLength); System.out.flush(); } catch (Exception e) @@ -492,8 +491,8 @@ private void settingsChanged() if(rotate.equals("off")) { rotateDisplay = false; frameHeader[5] = (byte)0; } String midiSoundfont = config.settings.get("soundfont"); - if(midiSoundfont.equals("Custom")) { PlatformPlayer.customMidi = true; } - else if(midiSoundfont.equals("Default")) { PlatformPlayer.customMidi = false; } + if(midiSoundfont.equals("Custom")) { Manager.useCustomMidi = true; } + else if(midiSoundfont.equals("Default")) { Manager.useCustomMidi = false; } halveCanvasRes = false; if(config.settings.get("halveCanvasRes").equals("on")) @@ -508,7 +507,7 @@ private void settingsChanged() lcdWidth = w; lcdHeight = h; Mobile.getPlatform().resizeLCD(w, h); - surface = new BufferedImage(lcdWidth, lcdHeight, BufferedImage.TYPE_3BYTE_BGR); // libretro display + surface = new BufferedImage(lcdWidth, lcdHeight, BufferedImage.TYPE_3BYTE_BGR); // libretro display gc = (Graphics2D)surface.getGraphics(); } diff --git a/src/org/recompile/mobile/PlatformPlayer.java b/src/org/recompile/mobile/PlatformPlayer.java index 1b768a28..020d98da 100644 --- a/src/org/recompile/mobile/PlatformPlayer.java +++ b/src/org/recompile/mobile/PlatformPlayer.java @@ -24,8 +24,6 @@ import java.util.Vector; import javax.sound.midi.MidiSystem; import javax.sound.midi.Sequencer; -import javax.sound.midi.Soundbank; -import javax.sound.midi.Synthesizer; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; @@ -35,6 +33,7 @@ import javax.microedition.media.Control; import javax.microedition.media.Controllable; +import javax.microedition.media.Manager; public class PlatformPlayer implements Player { @@ -49,10 +48,6 @@ public class PlatformPlayer implements Player private Control[] controls; - public static boolean customMidi = false; - - public static File soundfontDir = new File("freej2me_system" + File.separatorChar + "customMIDI" + File.separatorChar); - public PlatformPlayer(InputStream stream, String type) { listeners = new Vector(); @@ -265,8 +260,6 @@ public void deallocate() { } private class midiPlayer extends audioplayer { private Sequencer midi; - private Soundbank soundfont; - Synthesizer synth; private long tick = 0L; @@ -274,54 +267,25 @@ public midiPlayer(InputStream stream) { try { - midi = MidiSystem.getSequencer(); - - /* - * Check if the user wants to run a custom MIDI soundfont. Also, there's no harm - * in checking if the directory exists again. - */ - if(customMidi) - { - /* Get the first sf2 soundfont in the directory */ - String[] fontfile = soundfontDir.list(new FilenameFilter() - { - @Override - public boolean accept(File f, String soundfont ) - { - return soundfont.toLowerCase().endsWith(".sf2"); - } - }); - - /* - * Only really set the player to use a custom midi soundfont if there is one - * inside the directory. - */ - if(fontfile.length > 0) - { - for(int i = 0; i < fontfile.length; i++) - { - // Load the first .sf2 font, if there's none that's valid, don't set any and use JVM's default - if(fontfile[i].toLowerCase().endsWith(".sf2")) - { - soundfont = MidiSystem.getSoundbank(new File(soundfontDir.getPath() + File.separatorChar + fontfile[i])); - midi = MidiSystem.getSequencer(false); - synth = MidiSystem.getSynthesizer(); - synth.open(); - synth.loadAllInstruments(soundfont); - midi.getTransmitter().setReceiver(synth.getReceiver()); - break; - } - } - } else { System.out.println("PlatformPlayer: Custom MIDI enabled but there's no soundfont in" + (soundfontDir.getPath() + File.separatorChar)); } - } + /* Open the midi stream without a receiver, so that we can load up the custom soundfont if available */ + midi = MidiSystem.getSequencer(false); midi.open(); + + if(Manager.useCustomMidi && Manager.hasLoadedCustomMidi) + { + midi.getTransmitter().setReceiver(Manager.customSynth.getReceiver()); + } + else + { + midi.getTransmitter().setReceiver(MidiSystem.getReceiver()); + } + midi.setSequence(stream); state = Player.PREFETCHED; } catch (Exception e) { System.out.println("Couldn't load midi file:" + e.getMessage()); - if(customMidi) { synth.close(); } midi.close(); } }