Skip to content

Commit

Permalink
PlatformPlayer: More improvements to player state handling.
Browse files Browse the repository at this point in the history
We should try realize() if a jar calls start() directly too. Also
actually close midi on deallocate to fix Castle of Magic's K800i
version. It'll be reopened on realize() and won't break Phoenix
Wright. Other than that, midi just had some small cleanups alongside
wavPlayer, which had its clip setup moved back to realize(). Death
Race only needs the player to be closed and then reopened, and
having wav be ready on realize() is better at the moment.

Copies of both decoded wav and normal wav are kept on the wav player
for cases like Castle of Magic, which deallocate the player and
expect to use it again with the same stream. Midi makes a bit more
sense now as well. It's state handling is a bit more robust overall.
  • Loading branch information
AShiningRay committed Dec 4, 2024
1 parent 0580297 commit 8634f23
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 40 deletions.
75 changes: 37 additions & 38 deletions src/org/recompile/mobile/PlatformPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,9 @@ public void start()
{
if(getState() == Player.CLOSED) { throw new IllegalStateException("Cannot call start() on a CLOSED player."); }

if(getState() == Player.REALIZED || getState() == Player.UNREALIZED) { prefetch(); }
if(getState() == Player.UNREALIZED) { realize(); }

if(getState() == Player.REALIZED) { prefetch(); }

if(getState() == Player.PREFETCHED) { player.start(); }
}
Expand Down Expand Up @@ -243,7 +245,11 @@ public void deallocate()
* as deallocate can be called during the transition from UNREALIZED to REALIZED, and if that happens,
* we can't actually set it as REALIZED, it must be kept as UNREALIZED.
*/
if(state > Player.UNREALIZED) { state = Player.REALIZED; }
if(state > Player.UNREALIZED)
{
player.realize();
state = Player.REALIZED;
}
}

public String getContentType()
Expand Down Expand Up @@ -430,7 +436,6 @@ public midiPlayer(InputStream stream)

synthesizer.open();
receiver = synthesizer.getReceiver();
midi.getTransmitter().setReceiver(receiver);
midiSequence = MidiSystem.getSequence(stream);
}
catch (Exception e)
Expand All @@ -439,24 +444,25 @@ public midiPlayer(InputStream stream)
}
}

public void realize()
{
public void realize() { state = Player.REALIZED; }

public void prefetch()
{
try
{
{
midi = MidiSystem.getSequencer(false);
midi.getTransmitter().setReceiver(receiver);
midi.open();
midi.setSequence(midiSequence);
state = Player.REALIZED;
}
state = Player.PREFETCHED;
}
catch (Exception e)
{
Mobile.log(Mobile.LOG_ERROR, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Could not realize midi stream:" + e.getMessage());
deallocate();
state = Player.UNREALIZED;
{
Mobile.log(Mobile.LOG_ERROR, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Could not prefetch midi stream:" + e.getMessage());
state = Player.REALIZED;
}
}

public void prefetch() { state = Player.PREFETCHED; }

public void start()
{
if(getMediaTime() >= getDuration()) { setMediaTime(0); }
Expand Down Expand Up @@ -490,7 +496,7 @@ public void stop()
notifyListeners(PlayerListener.STOPPED, getMediaTime());
}

public void deallocate() { } // Prefetch does "nothing" in each internal player so deallocate must also do nothing
public void deallocate() { midi.close(); }

public void close()
{
Expand Down Expand Up @@ -561,7 +567,7 @@ private void cleanSequencer()
private class wavPlayer extends audioplayer
{
/* PCM WAV variables */
private InputStream tmpStream;
private byte[] tmpStream;
private AudioInputStream wavStream;
private Clip wavClip;
private int[] wavHeaderData = new int[4];
Expand All @@ -580,52 +586,44 @@ public wavPlayer(InputStream stream)
wavHeaderData = WavImaAdpcmDecoder.readHeader(stream);
stream.reset();

if(wavHeaderData[0] != 17) /* If it's not IMA ADPCM we don't need to do anything to the stream. */
if(wavHeaderData[0] != 17) /* If it's not IMA ADPCM we don't need to do anything to the stream. Just pass it to a local copy */
{
wavStream = AudioSystem.getAudioInputStream(stream);
tmpStream = new byte[stream.available()];
stream.read(tmpStream, 0, stream.available());
}
else /* But if it is IMA ADPCM, we have to decode it manually. */
{
if(Mobile.minLogLevel == Mobile.LOG_DEBUG) /* Print the decoded stream's header for analysis */
{
tmpStream = WavImaAdpcmDecoder.decodeImaAdpcm(stream, wavHeaderData);
tmpStream = WavImaAdpcmDecoder.decodeImaAdpcm(stream, wavHeaderData);

tmpStream.mark(60);
WavImaAdpcmDecoder.readHeader(tmpStream);
tmpStream.reset();

wavStream = AudioSystem.getAudioInputStream(tmpStream);
}
else /* If not debugging, throw the decoded stream into wavStream right away. */
if(Mobile.minLogLevel == Mobile.LOG_DEBUG) /* Print the decoded stream's header for analysis */
{
wavStream = AudioSystem.getAudioInputStream(WavImaAdpcmDecoder.decodeImaAdpcm(stream, wavHeaderData));
wavStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(tmpStream));
wavStream.mark(60);
WavImaAdpcmDecoder.readHeader(wavStream);
wavStream.reset();
}

}

} catch (Exception e) { Mobile.log(Mobile.LOG_ERROR, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Could not prepare wav stream:" + e.getMessage());}
}

public void realize()
{
state = Player.REALIZED;
}

public void prefetch()
{
try
{
wavStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(tmpStream));
wavClip = AudioSystem.getClip();
wavClip.open(wavStream);
state = Player.PREFETCHED;
state = Player.REALIZED;
}
catch (Exception e)
{
Mobile.log(Mobile.LOG_ERROR, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Couldn't realize wav stream: " + e.getMessage());
wavClip.close();
}
}
}

public void prefetch() { state = Player.PREFETCHED; }

public void start()
{
if(getMediaTime() >= getDuration()) { setMediaTime(0); }
Expand Down Expand Up @@ -660,6 +658,7 @@ public void stop()

public void close()
{
wavClip.close();
wavClip = null;
wavStream = null;
wavHeaderData = null;
Expand Down
4 changes: 2 additions & 2 deletions src/org/recompile/mobile/WavImaAdpcmDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ private static final void buildHeader(byte[] buffer, final short numChannels, fi
}

/* Decode the received IMA WAV ADPCM stream into a signed PCM16LE byte array, then return it to PlatformPlayer. */
public static final ByteArrayInputStream decodeImaAdpcm(final InputStream stream, final int[] wavHeaderData) throws IOException
public static final byte[] decodeImaAdpcm(final InputStream stream, final int[] wavHeaderData) throws IOException
{
/* Remove the header from the stream, we shouldn't "decode" it as if it was a sample */
stream.skip(IMAHEADERSIZE);
Expand All @@ -460,6 +460,6 @@ public static final ByteArrayInputStream decodeImaAdpcm(final InputStream stream
final byte[] output = decodeADPCM(input, input.length, (short) wavHeaderData[2], wavHeaderData[3]);
buildHeader(output, (short) wavHeaderData[2], wavHeaderData[1]); /* Builds a new header for the decoded stream. */

return new ByteArrayInputStream(output);
return output;
}
}

0 comments on commit 8634f23

Please sign in to comment.