Skip to content

Commit

Permalink
Drop the Media Cache.
Browse files Browse the repository at this point in the history
As noted in the previous commit, this has outlived its usefulness,
DOOM II RPG doesn't leak memory on players anymore (probably true
for other jars as well), and midi load/play performance has been
improved to not need caching of any kind anymore.
  • Loading branch information
AShiningRay committed Oct 19, 2024
1 parent 0bce5f9 commit 5dbc4cf
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 439 deletions.
73 changes: 4 additions & 69 deletions src/javax/microedition/media/Manager.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,27 +45,19 @@ public final class Manager
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;

/* Midi Caching for better performance on certain VMs like OpenJDK 8 with jars that constantly load a similar set of streams. */
private static Map<String, Byte> mediaCache = new HashMap<>();

public static boolean dumpAudioStreams = false;

public static Player createPlayer(InputStream stream, String type) throws IOException, MediaException
{
checkCustomMidi();

stream.mark(1024);
String streamMD5 = generateMD5Hash(stream, 1024);
stream.reset();

if(dumpAudioStreams)
{
stream.mark(1024);
String streamMD5 = generateMD5Hash(stream, 1024);
stream.reset();

// Copy the stream contents into a temporary stream to be saved as file
final ByteArrayOutputStream streamCopy = new ByteArrayOutputStream();
final byte[] copyBuffer = new byte[1024];
Expand Down Expand Up @@ -94,61 +86,9 @@ public static Player createPlayer(InputStream stream, String type) throws IOExce
streamCopy.writeTo(outStream);
}

// return checkMediaCache(streamMD5, stream, type);

return new PlatformPlayer(stream, type);
}

private static Player checkMediaCache(String streamMD5, InputStream stream, String type)
{
/* If we currently have this stream's player cached, return it instantly to avoid creating a new player and its overhead */
if (mediaCache.containsKey(streamMD5))
{
/*
* We're basically "loading up" a new player as far as the MIDlet is concerned,
* so make it seem as such by doing the following steps before returning it to the MIDlet:
* 1 - Stopping the player if it's not stopped (this will probably be removed once media playback loop is more mature)
* 2 - Setting the media playback time back to the start.
* 3 - Setting its state to PREFETCHED for good measure.
*/
mediaPlayers[mediaCache.get(streamMD5)].cacheDeallocate();
mediaPlayers[mediaCache.get(streamMD5)].setMediaTime(0);
return mediaPlayers[mediaCache.get(streamMD5)];
}

// Otherwise, let's create and cache a new one.

// If the index is out of bounds, we reached the end of our cache, go back to the start to find a position to free
if(mediaPlayersIndex >= mediaPlayers.length) { mediaPlayersIndex = 0; }

// Run through the entire cache index to find a suitable position to slot the new player in.
for(; mediaPlayersIndex < mediaPlayers.length; mediaPlayersIndex++)
{
if(mediaPlayers[mediaPlayersIndex] == null) { break; } /* A null position means we can use it right away */

/* Otherwise, we prefer deallocating a position if it is not playing (running). */
else if(mediaPlayers[mediaPlayersIndex] != null && mediaPlayers[mediaPlayersIndex].getState() == Player.PREFETCHED)
{
mediaPlayers[mediaPlayersIndex].cacheDeallocate();
mediaCache.values().remove(mediaPlayersIndex);
break;
}
/* If we ever reach this one, it's because all the other slots are used, and are playing. Deallocate the last cache position as a last resort. */
else if(mediaPlayersIndex == mediaPlayers.length-1)
{
mediaPlayers[mediaPlayersIndex].cacheDeallocate();
mediaCache.values().remove(mediaPlayersIndex);
break;
}
}

mediaPlayers[mediaPlayersIndex] = new PlatformPlayer(stream, type);
mediaCache.put(streamMD5, mediaPlayersIndex);
mediaPlayersIndex++;

return mediaPlayers[mediaCache.get(streamMD5)];
}

public static Player createPlayer(String locator) throws MediaException
{
checkCustomMidi();
Expand All @@ -174,11 +114,6 @@ public static void playTone(int note, int duration, int volume)
System.out.println("Play Tone");
}

public static void updatePlayerNum(byte num)
{
mediaPlayers = new PlatformPlayer[num];
}

private static String generateMD5Hash(InputStream stream, int byteCount)
{
try
Expand Down
101 changes: 42 additions & 59 deletions src/libretro/freej2me_libretro.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ int phoneType; /* 0=standard, 1=nokia, 2=siemens, 3=motorola, 4=sonyEricsson */
int gameFPS; /* Auto(0), 60, 30, 15 */
int soundEnabled; /* also acts as a boolean */
int customMidi; /* Also acts as a boolean */
int midiCacheSize; /* Maximum amount of MIDI Players allowed on FreeJ2ME at any given time */
int dumpAudioStreams;
/* Variables used to manage the pointer speed when controlled from an analog stick */
int pointerXSpeed = 8;
Expand Down Expand Up @@ -287,12 +286,12 @@ int read_from_pipe(void* pipe, void *data, int datasize)
/* Function to check the core's config states in the libretro frontend */
static void check_variables(bool first_time_startup)
{
struct retro_variable var = {0};
struct retro_variable var = {0};


var.key = "freej2me_resolution";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
var.key = "freej2me_resolution";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
char *resChar;
char str[100];
snprintf(str, sizeof(str), "%s", var.value);
Expand All @@ -301,83 +300,69 @@ static void check_variables(bool first_time_startup)
if (resChar) { screenRes[0] = strtoul(resChar, NULL, 0); }
resChar = strtok(NULL, "x");
if (resChar) { screenRes[1] = strtoul(resChar, NULL, 0); }
}
}


var.key = "freej2me_halvecanvasres";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
var.key = "freej2me_halvecanvasres";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "off")) { halveCanvasRes = 0; }
else if (!strcmp(var.value, "on")) { halveCanvasRes = 1; }
}
}


var.key = "freej2me_rotate";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
var.key = "freej2me_rotate";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "off")) { rotateScreen = 0; }
else if (!strcmp(var.value, "on")) { rotateScreen = 1; }
}
}


var.key = "freej2me_phone";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
var.key = "freej2me_phone";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "Standard")) { phoneType = 0; }
else if (!strcmp(var.value, "Nokia")) { phoneType = 1; }
else if (!strcmp(var.value, "Siemens")) { phoneType = 2; }
else if (!strcmp(var.value, "Motorola")) { phoneType = 3; }
else if (!strcmp(var.value, "SonyEricsson")) { phoneType = 4; }
}
}


var.key = "freej2me_fps";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
var.key = "freej2me_fps";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "Auto")) { gameFPS = 0; }
else if (!strcmp(var.value, "60")) { gameFPS = 60; }
else if (!strcmp(var.value, "30")) { gameFPS = 30; }
else if (!strcmp(var.value, "15")) { gameFPS = 15; }
}
}


var.key = "freej2me_sound";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
var.key = "freej2me_sound";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "off")) { soundEnabled = 0; }
else if (!strcmp(var.value, "on")) { soundEnabled = 1; }
}
}


var.key = "freej2me_midifont";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "off")) { customMidi = 0; }
else if (!strcmp(var.value, "on")) { customMidi = 1; }
}

var.key = "freej2me_mediacachesize";
var.key = "freej2me_midifont";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "48")) { midiCacheSize = 48; }
else if (!strcmp(var.value, "1")) { midiCacheSize = 1; }
else if (!strcmp(var.value, "2")) { midiCacheSize = 2; }
else if (!strcmp(var.value, "4")) { midiCacheSize = 4; }
else if (!strcmp(var.value, "8")) { midiCacheSize = 8; }
else if (!strcmp(var.value, "16")) { midiCacheSize = 16; }
else if (!strcmp(var.value, "32")) { midiCacheSize = 32; }
else if (!strcmp(var.value, "64")) { midiCacheSize = 64; }
else if (!strcmp(var.value, "96")) { midiCacheSize = 96; }
if (!strcmp(var.value, "off")) { customMidi = 0; }
else if (!strcmp(var.value, "on")) { customMidi = 1; }
}

var.key = "freej2me_dumpaudiostreams";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "off")) { dumpAudioStreams = 0; }
else if (!strcmp(var.value, "on")) { dumpAudioStreams = 1; }
}
}

var.key = "freej2me_pointertype";
var.key = "freej2me_pointertype";
if (Environ(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "Mouse"))
Expand Down Expand Up @@ -457,7 +442,7 @@ static void check_variables(bool first_time_startup)
/* Prepare a string to pass those core options to the Java app */
options_update = malloc(sizeof(char) * PIPE_MAX_LEN);

snprintf(options_update, PIPE_MAX_LEN, "FJ2ME_LR_OPTS:|%lux%lu|%d|%d|%d|%d|%d|%d|%d|%d", screenRes[0], screenRes[1], halveCanvasRes, rotateScreen, phoneType, gameFPS, soundEnabled, customMidi, midiCacheSize, dumpAudioStreams);
snprintf(options_update, PIPE_MAX_LEN, "FJ2ME_LR_OPTS:|%lux%lu|%d|%d|%d|%d|%d|%d|%d", screenRes[0], screenRes[1], halveCanvasRes, rotateScreen, phoneType, gameFPS, soundEnabled, customMidi, dumpAudioStreams);
optstrlen = strlen(options_update);

/* 0xD = 13, which is the special case where the java app will receive the updated configs */
Expand Down Expand Up @@ -516,7 +501,7 @@ void retro_init(void)
*/
check_variables(true);

char resArg[2][4], halveCanvas[2], rotateArg[2], phoneArg[2], fpsArg[3], soundArg[2], midiArg[2], mediaCacheArg[3], dumpAudioArg[2];
char resArg[2][4], halveCanvas[2], rotateArg[2], phoneArg[2], fpsArg[3], soundArg[2], midiArg[2], dumpAudioArg[2];
sprintf(resArg[0], "%lu", screenRes[0]); /* Libretro config Width */
sprintf(resArg[1], "%lu", screenRes[1]); /* Libretro config Height */
sprintf(halveCanvas, "%d", halveCanvasRes);
Expand All @@ -525,7 +510,6 @@ void retro_init(void)
sprintf(fpsArg, "%d", gameFPS);
sprintf(soundArg, "%d", soundEnabled);
sprintf(midiArg, "%d", customMidi);
sprintf(mediaCacheArg,"%d", midiCacheSize);
sprintf(dumpAudioArg, "%d", dumpAudioStreams);

/* start java process */
Expand All @@ -551,12 +535,11 @@ void retro_init(void)
params[8] = strdup(fpsArg);
params[9] = strdup(soundArg);
params[10] = strdup(midiArg);
params[11] = strdup(mediaCacheArg);
params[12] = strdup(dumpAudioArg);
params[13] = NULL; // Null-terminate the array
params[11] = strdup(dumpAudioArg);
params[12] = NULL; // Null-terminate the array

log_fn(RETRO_LOG_INFO, "Passing params: %s | %s | %s | %s | %s | %s | %s | %s | %s | %s\n", *(params+3),
*(params+4), *(params+5), *(params+6), *(params+7), *(params+8), *(params+9), *(params+10), *(params+11), *(params+12));
*(params+4), *(params+5), *(params+6), *(params+7), *(params+8), *(params+9), *(params+10), *(params+11));
}

log_fn(RETRO_LOG_INFO, "Preparing to open FreeJ2ME-Plus' Java app (make sure freej2me-lr.jar is inside system/).\n");
Expand Down Expand Up @@ -1052,8 +1035,8 @@ pid_t javaOpen(char *cmd, char **params)
log_fn(RETRO_LOG_INFO, "Setting up java app's process and pipes...\n");

log_fn(RETRO_LOG_INFO, "Opening: %s %s %s ...\n", *(params+0), *(params+1), *(params+2));
log_fn(RETRO_LOG_INFO, "Params: %s | %s | %s | %s | %s | %s | %s | %s | %s | %s\n", *(params+3),
*(params+4), *(params+5), *(params+6), *(params+7), *(params+8), *(params+9), *(params+10), *(params+11), *(params+12));
log_fn(RETRO_LOG_INFO, "Params: %s | %s | %s | %s | %s | %s | %s | %s | %s\n", *(params+3),
*(params+4), *(params+5), *(params+6), *(params+7), *(params+8), *(params+9), *(params+10), *(params+11));
}
else { log_fn(RETRO_LOG_INFO, "\n\nRESTARTING!!!\n\n"); restarting = false; }

Expand Down Expand Up @@ -1182,7 +1165,7 @@ void javaOpen(char *cmd, char **params)
sprintf(cmdWin, "javaw -jar %s", cmd);

log_fn(RETRO_LOG_INFO, "Opening: %s \n", cmd);
for (int i = 3; i <= 12; i++) /* There are 10 cmd arguments for now */
for (int i = 3; i <= 11; i++)
{
//log_fn(RETRO_LOG_INFO, "Processing arg %d: %s \n", i, *(params+i));
sprintf(cmdWin, "%s %s", cmdWin, *(params+i));
Expand All @@ -1195,8 +1178,8 @@ void javaOpen(char *cmd, char **params)
log_fn(RETRO_LOG_INFO, "Setting up java app's process and pipes...\n");

log_fn(RETRO_LOG_INFO, "Opening: %s %s %s ...\n", *(params+0), *(params+1), *(params+2));
log_fn(RETRO_LOG_INFO, "Params: %s | %s | %s | %s | %s | %s | %s | %s | %s | %s\n", *(params+3),
*(params+4), *(params+5), *(params+6), *(params+7), *(params+8), *(params+9), *(params+10), *(params+11), *(params+12));
log_fn(RETRO_LOG_INFO, "Params: %s | %s | %s | %s | %s | %s | %s | %s | %s\n", *(params+3),
*(params+4), *(params+5), *(params+6), *(params+7), *(params+8), *(params+9), *(params+10), *(params+11));
}
else { log_fn(RETRO_LOG_INFO, "\n\nRESTARTING!!!\n\n"); restarting = false; }

Expand Down
42 changes: 0 additions & 42 deletions src/libretro/freej2me_libretro.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,27 +197,6 @@ struct retro_core_option_v2_definition core_options[] =
},
"Default"
},
{
"freej2me_mediacachesize",
"Virtual Phone Settings > Media Cache Size",
"Media Cache Size",
"FreeJ2ME uses a media caching system to improve performance on certain VMs like OpenJDK 8 with apps that constantly allocate a similar set of media streams instead of keeping them allocated all the time (often to save precious memory). Most modern platforms should be able to hold the default of 48 unique streams in memory at once, which is also more than most J2ME apps would realistically load on real hardware. Feel free to adjust if your platform is starved for memory and/or you know the jar you're running only really uses a few slots of media but cycles the data it loads there.",
"FreeJ2ME uses a media caching system to improve performance on certain VMs like OpenJDK 8 with apps that constantly allocate a similar set of media streams instead of keeping them allocated all the time (often to save precious memory). Most modern platforms should be able to hold the default of 48 unique streams in memory at once, which is also more than most J2ME apps would realistically load on real hardware. Feel free to adjust if your platform is starved for memory and/or you know the jar you're running only really uses a few slots of media but cycles the data it loads there.",
"vphone_settings",
{
{ "1", "1" },
{ "2", "2" },
{ "4", "4" },
{ "8", "8" },
{ "16", "16" },
{ "32", "32" },
{ "48", "48" },
{ "64", "64" },
{ "96", "96" },
{ NULL, NULL },
},
"48"
},
{
"freej2me_dumpaudiostreams",
"Advanced Settings > Dump Audio Streams",
Expand Down Expand Up @@ -460,23 +439,6 @@ struct retro_core_option_definition core_options_v1 [] =
},
"Default"
},
{
"freej2me_mediacachesize",
"Media Cache Size",
"FreeJ2ME uses a media caching system to improve performance on certain VMs like OpenJDK 8 with apps that constantly allocate a similar set of media streams instead of keeping them allocated all the time (often to save precious memory). Most modern platforms should be able to hold the default of 48 unique streams in memory at once, which is also more than most J2ME apps would realistically load on real hardware. Feel free to adjust if your platform is starved for memory and/or you know the jar you're running only really uses a few slots of media but cycles the data it loads there.", {
{ "1", "1" },
{ "2", "2" },
{ "4", "4" },
{ "8", "8" },
{ "16", "16" },
{ "32", "32" },
{ "48", "48" },
{ "64", "64" },
{ "96", "96" },
{ NULL, NULL },
},
"48"
},
{
"freej2me_dumpaudiostreams",
"Dump Audio Streams",
Expand Down Expand Up @@ -614,10 +576,6 @@ static const struct retro_variable vars[] =
"freej2me_midifont",
"MIDI Soundfont; off|on"
},
{ /* Max MIDI Players */
"freej2me_mediacachesize",
"Media Cache Size: 48|1|2|4|8|16|32|64|96"
},
{ /* Dump Audio Streams */
"freej2me_dumpaudiostreams",
"Dump Audio Streams: off|on"
Expand Down
Loading

0 comments on commit 5dbc4cf

Please sign in to comment.