Skip to content

Commit

Permalink
Manager + PlatformPlayer: Revamp dumps, try loading media locators
Browse files Browse the repository at this point in the history
Opening media files from the jar when it sent a locator instead of
the inputstream and its type directly wasn't supported yet.

Also, media checks were simplified in both platformPlayer and
Manager. Although the inclusion of locators will end up with manager
dumping them with the incorrect extension depending on the name at
the moment. As a bonus, Nokia Sound also now exports the
original OTA tone sequence as well.
  • Loading branch information
AShiningRay committed Dec 12, 2024
1 parent 38ae8ab commit 0dfa65c
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 54 deletions.
5 changes: 3 additions & 2 deletions src/com/nokia/mid/sound/Sound.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,12 @@ public void init(byte[] data, int type)
try
{
if(player == null || !isPrevPlayerTone) // check for null because release() can be called after all.
{
{
if(Manager.dumpAudioStreams) { Manager.dumpAudioStream(new ByteArrayInputStream(data), "audio/x-tone-seq"); } // Dump original OTA as well
player = Manager.createPlayer(new ByteArrayInputStream(convertToMidi(data)), "audio/x-tone-seq");
isPrevPlayerTone = true;
}
else
else
{
player.deallocate();
((ToneControl) player.getControl("ToneControl")).setSequence(convertToMidi(data));
Expand Down
95 changes: 60 additions & 35 deletions src/javax/microedition/media/Manager.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
public class Manager
{
public static final String TONE_DEVICE_LOCATOR = "device://tone";
public static final String MIDI_DEVICE_LOCATOR = "device://midi";

/* Custom MIDI variables */
public static boolean useCustomMidi = false;
Expand All @@ -64,40 +65,7 @@ public static synchronized Player createPlayer(InputStream stream, String type)
* only a lot of testing will be able to determine which is preferable, or if we'd need a config toggle to alternate both.
*/

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];
int copyLength;
while ((copyLength = stream.read(copyBuffer)) > -1 ) { streamCopy.write(copyBuffer, 0, copyLength); }
streamCopy.flush();

// Make sure the initial stream will still be available for FreeJ2ME
stream = new ByteArrayInputStream(streamCopy.toByteArray());

// And save the copy to the specified dir

OutputStream outStream;
String dumpPath = "." + File.separatorChar + "FreeJ2MEDumps" + File.separatorChar + "Audio" + File.separatorChar + Mobile.getPlatform().loader.suitename + File.separatorChar;
File dumpFile = new File(dumpPath);

if (!dumpFile.isDirectory()) { dumpFile.mkdirs(); }

if(type.equalsIgnoreCase("audio/x-mid") || type.equalsIgnoreCase("audio/mid") || type.equalsIgnoreCase("audio/midi") || type.equalsIgnoreCase("sp-midi") || type.equalsIgnoreCase("audio/spmidi"))
{ dumpFile = new File(dumpPath + "Stream_" + streamMD5 + ".mid");}
else if(type.equalsIgnoreCase("audio/x-wav") || type.equalsIgnoreCase("audio/wav")) { dumpFile = new File(dumpPath + "Stream_" + streamMD5 + ".wav");}
else if(type.equalsIgnoreCase("audio/mpeg") || type.equalsIgnoreCase("audio/mp3")) { dumpFile = new File(dumpPath + "Stream_" + streamMD5 + ".mp3");}
else if(type.equalsIgnoreCase("audio/x-tone-seq")) { dumpFile = new File(dumpPath + "Stream_" + streamMD5 + ".mid");} // Tone Seq should arrive converted to midi

outStream = new FileOutputStream(dumpFile);

streamCopy.writeTo(outStream);
}
if(dumpAudioStreams) { stream = dumpAudioStream(stream, type); }

return new PlatformPlayer(stream, type);
}
Expand All @@ -119,8 +87,17 @@ public static Player createPlayer(String locator) throws MediaException

if(locator == null) { throw new IllegalArgumentException("Cannot create a player with a null locator"); }

InputStream stream = Mobile.getPlatform().loader.getResourceAsStream(locator);

if(dumpAudioStreams && !locator.equals(Manager.TONE_DEVICE_LOCATOR) && !locator.equals(Manager.MIDI_DEVICE_LOCATOR))
{
dumpAudioStream(stream, locator); // Using the locator, we can try find out what this is by parsing the file extension
}

Mobile.log(Mobile.LOG_WARNING, Manager.class.getPackage().getName() + "." + Manager.class.getSimpleName() + ": " + "Create Player "+locator);
return new PlatformPlayer(locator);
if(!locator.equals(Manager.TONE_DEVICE_LOCATOR) && !locator.equals(Manager.MIDI_DEVICE_LOCATOR)) { return new PlatformPlayer(stream, ""); } // Empty type, let PlatformPlayer handle it
else { return new PlatformPlayer(locator); } // If it's a dedicated locator, PlatformPlayer can handle it directly

}

public static String[] getSupportedContentTypes(String protocol)
Expand Down Expand Up @@ -177,6 +154,54 @@ public static void playTone(int note, int duration, int volume) throws MediaExce
}).start();
}

public static final InputStream dumpAudioStream(InputStream stream, String type)
{
try
{
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];
int copyLength;
while ((copyLength = stream.read(copyBuffer)) > -1 ) { streamCopy.write(copyBuffer, 0, copyLength); }
streamCopy.flush();

// Make sure the initial stream will still be available for FreeJ2ME
stream = new ByteArrayInputStream(streamCopy.toByteArray());

// And save the copy to the specified dir
OutputStream outStream;
String dumpPath = "." + File.separatorChar + "FreeJ2MEDumps" + File.separatorChar + "Audio" + File.separatorChar + Mobile.getPlatform().loader.suitename + File.separatorChar;
File dumpFile = new File(dumpPath);

if (!dumpFile.isDirectory()) { dumpFile.mkdirs(); }

// TODO: Locators will break this sometimes, since file names might also contain those substrings.
if(type.toLowerCase().contains("mid") ) { dumpFile = new File(dumpPath + "Stream_" + streamMD5 + ".mid"); }
else if(type.toLowerCase().contains("wav")) { dumpFile = new File(dumpPath + "Stream_" + streamMD5 + ".wav"); }
else if(type.toLowerCase().contains("mp")) { dumpFile = new File(dumpPath + "Stream_" + streamMD5 + ".mp3"); }
else if(type.equalsIgnoreCase("audio/x-tone-seq"))
{
stream.mark(4);
byte[] data = new byte[4];
stream.read(data);
if((data[0] == 'M' && data[1] == 'T' && data[2] == 'h' && data[3] == 'd') ) { dumpFile = new File(dumpPath + "Stream_" + streamMD5 + "_Decoded.mid"); } // Tone Seq is converted to midi, save it as "decoded"
else { dumpFile = new File(dumpPath + "Stream_" + streamMD5 + ".ota"); } // Only nokia OTA tones are going to be dumped in their original form right now. Dual Tone and MelodyComposer have no format of their own.
stream.reset();
}

outStream = new FileOutputStream(dumpFile);

streamCopy.writeTo(outStream);
}
catch (Exception e) { Mobile.log(Mobile.LOG_ERROR, Manager.class.getPackage().getName() + "." + Manager.class.getSimpleName() + ": " + "Failed to dump media: " + e.getMessage()); }

return stream;
}

private static String generateMD5Hash(InputStream stream, int byteCount)
{
try
Expand Down
25 changes: 8 additions & 17 deletions src/org/recompile/mobile/PlatformPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,29 +97,20 @@ public PlatformPlayer(InputStream stream, String type)
else
{
// Midi player will also play tones, as these are converted to midi in pretty much all cases at the moment
if(contentType.equalsIgnoreCase("audio/x-mid") || contentType.equalsIgnoreCase("audio/mid") || contentType.equalsIgnoreCase("audio/midi") || contentType.equalsIgnoreCase("sp-midi") || contentType.equalsIgnoreCase("audio/spmidi") || contentType.equalsIgnoreCase("audio/x-tone-seq"))
{
player = new midiPlayer(stream);
}
else if(contentType.equalsIgnoreCase("audio/x-wav") || contentType.equalsIgnoreCase("audio/wav"))
{
player = new wavPlayer(stream);
}
else if(contentType.equalsIgnoreCase("audio/mpeg") || contentType.equalsIgnoreCase("audio/mp3"))
{
player = new MP3Player(stream);
}
if(contentType.toLowerCase().contains("mid") || contentType.toLowerCase().contains("tone")) { player = new midiPlayer(stream); }
else if(contentType.toLowerCase().contains("wav")) { player = new wavPlayer(stream); }
else if(contentType.toLowerCase().contains("mp")) { player = new MP3Player(stream); } // MP1, MP2, MP3, MPEG, etc. No other J2ME format has those two letters in sequence.
else /* If the stream doesn't have an accompanying type or its a type we don't have an explicit player for, do everything we can to try and load it */
{
Mobile.log(Mobile.LOG_WARNING, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Audio type <'" + contentType + "'> doesn't match any supported ones. Trying to find what it is...");
Mobile.log(Mobile.LOG_DEBUG, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Audio type <'" + contentType + "'> doesn't match any supported ones. Trying to find what it is...");
try
{
final byte[] data = new byte[stream.available()];
stream.read(data, 0, stream.available());

if(data.length >= 4 && data[0] == 'M' && data[1] == 'T' && data[2] == 'h' && data[3] == 'd')
{
Mobile.log(Mobile.LOG_WARNING, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Format is MIDI!");
Mobile.log(Mobile.LOG_DEBUG, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Format is MIDI!");
player = new midiPlayer(new ByteArrayInputStream(data));
contentType = "audio/mid";
}
Expand All @@ -132,13 +123,13 @@ else if (data.length >= 15 && data[8] == 'Q' && data[9] == 'L' && data[10] == 'C
}
else if(data.length >= 4 && data[0] == 'R' && data[1] == 'I' && data[2] == 'F' && data[3] == 'F')
{
Mobile.log(Mobile.LOG_WARNING, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Format is WAV!");
Mobile.log(Mobile.LOG_DEBUG, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Format is WAV!");
player = new wavPlayer(new ByteArrayInputStream(data));
contentType = "audio/wav";
}
else if(data.length >= 3 && data[0] == 'I' && data[1] == 'D' && data[2] == '3' || ((data[0] == (byte) 0xFF) && (data[1] & 0xE0) == 0xE0)) // Check for MPEG files WITH and WITHOUT the ID3 tag
{
Mobile.log(Mobile.LOG_WARNING, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Format is MPEG!");
Mobile.log(Mobile.LOG_DEBUG, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Format is MPEG!");
player = new MP3Player(new ByteArrayInputStream(data));
contentType = "audio/mpeg";
}
Expand Down Expand Up @@ -199,7 +190,7 @@ else if(data.length >= 9 && data[0] == '#' && data[1] == '!' && data[2] == 'A' &

public PlatformPlayer(String locator)
{
if(locator.equals(Manager.TONE_DEVICE_LOCATOR))
if(locator.equals(Manager.TONE_DEVICE_LOCATOR) || locator.equals(Manager.MIDI_DEVICE_LOCATOR))
{
player = new midiPlayer();
listeners = new Vector<PlayerListener>();
Expand Down

0 comments on commit 0dfa65c

Please sign in to comment.