Skip to content

Commit

Permalink
PlatformPlayer + Nokia Sound: Make nokia sound player more robust
Browse files Browse the repository at this point in the history
FreeJ2ME now handles get/setGain, and also properly handles
SoundListener change events, as well as checking for the player
state on play/resume/stop calls. play() is now conformant to the
Nokia documentation, which dictates that the media should ALWAYS
play from the start here (otherwise it would act like resume()).
  • Loading branch information
AShiningRay committed Dec 9, 2024
1 parent 4b422fc commit beebdd4
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 18 deletions.
27 changes: 21 additions & 6 deletions src/com/nokia/mid/sound/Sound.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import javax.sound.midi.Track;

import org.recompile.mobile.Mobile;
import org.recompile.mobile.PlatformPlayer;

/* Using references from http://www.j2megame.org/j2meapi/Nokia_UI_API_1_1/com/nokia/mid/sound/Sound.html */
public class Sound
Expand Down Expand Up @@ -62,7 +63,6 @@ public class Sound
private static final double SEMITONE_CONST = 17.31234049066755; // 1/(ln(2^(1/12)))

private Player player;
private SoundListener soundListener;

private static int parsePos = 0; // Used exclusively as a marker for OTA/OTT Parsing
private static boolean[] toneBitArray;
Expand All @@ -82,8 +82,6 @@ public class Sound

public static int getConcurrentSoundCount(int type) { return 1; }

public int getGain() { return 0; }

public int getState()
{
int state = player.getState();
Expand Down Expand Up @@ -158,19 +156,36 @@ public void init(int freq, long duration)

public void play(int loop)
{
if(getState() == SOUND_PLAYING) { player.stop(); }
if(getState() == SOUND_UNINITIALIZED) { return; }
if(loop < 0) { throw new IllegalArgumentException("Cannot play media, invalid loop value received"); }
else if(loop == 0) { loop = -1; }

player.setLoopCount(loop);
player.setMediaTime(0); // A play call always makes the media play from the beginning.
player.start();
}

public void release() { player.close(); }

public void resume() { player.start(); }
public void resume()
{
if(getState() == SOUND_UNINITIALIZED || getState() == SOUND_PLAYING) { return; }
player.start();
}

public void setGain(int gain) { }
public void setGain(int gain)
{
// Gain goes from 0 to 255, while setLevel works from 0 to 100
((PlatformPlayer.volumeControl)player.getControl("VolumeControl")).setLevel((int) (gain / 255 * 100));
}

public int getGain()
{
return (int) ((((PlatformPlayer.volumeControl)player.getControl("VolumeControl")).getLevel() / 100) * 255);
}

public void setSoundListener(SoundListener soundListener) { this.soundListener = soundListener; }
public void setSoundListener(SoundListener soundListener) { ((PlatformPlayer) player).setSoundListener(this, soundListener); }

public void stop() { player.stop(); }

Expand Down
43 changes: 31 additions & 12 deletions src/org/recompile/mobile/PlatformPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;

import com.nokia.mid.sound.Sound;
import com.nokia.mid.sound.SoundListener;

import javax.microedition.media.Player;
import javax.microedition.media.PlayerListener;
import javax.microedition.media.control.ToneControl;
Expand All @@ -73,6 +77,9 @@ public class PlatformPlayer implements Player

private Vector<PlayerListener> listeners;

private SoundListener nokiaListener;
private Sound nokiaSound;

private Control[] controls;

// Manager already sets these two
Expand Down Expand Up @@ -257,6 +264,15 @@ public void addPlayerListener(PlayerListener playerListener)
listeners.add(playerListener);
}

public void setSoundListener(Sound sound, SoundListener listener) // Nokia Sound only has methods to set the Sound Listener, not remove it
{
if(getState() == Player.CLOSED) { throw new IllegalStateException("Cannot add SoundListener to an UNINITIALIZED Sound"); }
if(listener == null) { return; }

nokiaListener = listener;
nokiaSound = sound;
}

public void removePlayerListener(PlayerListener playerListener)
{
if(getState() == Player.CLOSED) { throw new IllegalStateException("Cannot remove PlayerListener from a CLOSED player"); }
Expand All @@ -271,6 +287,13 @@ private void notifyListeners(String event, Object eventData)
{
listeners.get(i).playerUpdate(this, event, eventData);
}

if(nokiaListener != null)
{
if(event == PlayerListener.CLOSED) { nokiaListener.soundStateChanged(nokiaSound, Sound.SOUND_UNINITIALIZED); }
else if(event == PlayerListener.STARTED) { { nokiaListener.soundStateChanged(nokiaSound, Sound.SOUND_PLAYING); } }
else if(event == PlayerListener.STOPPED || event == PlayerListener.END_OF_MEDIA) { { nokiaListener.soundStateChanged(nokiaSound, Sound.SOUND_STOPPED); } }
}
}

public void deallocate()
Expand Down Expand Up @@ -363,14 +386,10 @@ public Control getControl(String controlType)
{
if(getState() == Player.CLOSED || getState() == Player.UNREALIZED) { throw new IllegalStateException("Cannot call getControl(), as the player is either CLOSED or UNREALIZED."); }

if(controlType.equals("VolumeControl")) { return controls[0]; }
if(controlType.equals("TempoControl")) { return controls[1]; }
if(controlType.equals("MIDIControl")) { return controls[2]; }
if(controlType.equals("ToneControl")) { return controls[3]; }
if(controlType.equals("javax.microedition.media.control.VolumeControl")) { return controls[0]; }
if(controlType.equals("javax.microedition.media.control.TempoControl")) { return controls[1]; }
if(controlType.equals("javax.microedition.media.control.MIDIControl")) { return controls[2]; }
if(controlType.equals("javax.microedition.media.control.ToneControl")) { return controls[3]; }
if(controlType.contains("VolumeControl")) { return controls[0]; }
if(controlType.contains("TempoControl")) { return controls[1]; }
if(controlType.contains("MIDIControl")) { return controls[2]; }
if(controlType.contains("ToneControl")) { return controls[3]; }

return null;
}
Expand Down Expand Up @@ -831,7 +850,7 @@ public boolean isRunning()
// Controls //

/* midiControl is untested */
private class midiControl implements javax.microedition.media.control.MIDIControl
public class midiControl implements javax.microedition.media.control.MIDIControl
{
private midiPlayer player;

Expand Down Expand Up @@ -1070,7 +1089,7 @@ public void shortMidiEvent(int type, int data1, int data2)
}
}

private class volumeControl implements javax.microedition.media.control.VolumeControl
public class volumeControl implements javax.microedition.media.control.VolumeControl
{
private int level = 100;
private boolean muted = false;
Expand Down Expand Up @@ -1166,7 +1185,7 @@ public void setMute(boolean mute)
}

/* This one hasn't been tested yet, no jar was found at the time it was implemented */
private class tempoControl implements javax.microedition.media.control.TempoControl
public class tempoControl implements javax.microedition.media.control.TempoControl
{
/* MAX_RATE and MIN_RATE follow the JSR-135 docs guaranteed values, no matter if our player has a wider range */
private final int MAX_RATE = 300000;
Expand Down Expand Up @@ -1243,7 +1262,7 @@ public int setRate(int millirate)
}

/* ToneControl is also almost entirely untested right now, couldn't find a jar that uses setSequence() */
private class toneControl implements javax.microedition.media.control.ToneControl
public class toneControl implements javax.microedition.media.control.ToneControl
{
private midiPlayer player;

Expand Down

0 comments on commit beebdd4

Please sign in to comment.