Skip to content

Commit

Permalink
Flesh out sprintpcs.media a bit more.
Browse files Browse the repository at this point in the history
Should be mostly conformant now, after examining a few jars, it
appears that the constructor for Clip and DualTone is mostly similar,
so the "unknown" arguments were actually priority and vibration.

PlayerListener and some additional player methods are also here,
although the PlayerListener cycle isn't fully implemented (END_OF_DATA
events aren't issued at all, since that would require messing with
PlatformPlayer's internals quite a bit, and i want Nokia Tone support
before that, as it might conflict)
  • Loading branch information
AShiningRay committed Nov 14, 2024
1 parent 696d37d commit 1b2bf79
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 28 deletions.
19 changes: 11 additions & 8 deletions src/com/sprintpcs/media/Clip.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ public class Clip

public Clip(String locator, String contentType, int priority, int vibration) throws IOException
{
Mobile.log(Mobile.LOG_WARNING, Clip.class.getPackage().getName() + "." + Clip.class.getSimpleName() + ": " + "Clip (String, String, int, int)");

this.locator = locator;
this.contentType = contentType;
this.priority = priority;
Expand All @@ -45,8 +43,6 @@ public Clip(String locator, String contentType, int priority, int vibration) thr

public Clip(byte[] stream, String contentType, int priority, int vibration) throws IOException
{
Mobile.log(Mobile.LOG_WARNING, Clip.class.getPackage().getName() + "." + Clip.class.getSimpleName() + ": " + "Clip (byte[], String, int, int)");

this.stream = stream;
this.contentType = contentType;
this.priority = priority;
Expand All @@ -59,14 +55,21 @@ public Clip(byte[] stream, String contentType, int priority, int vibration) thro

protected Player getPlayer() throws MediaException
{
Mobile.log(Mobile.LOG_WARNING, Clip.class.getPackage().getName() + "." + Clip.class.getSimpleName() + ": " + "GetPlayer()");

Player player = null;
try
{
if (stream != null) { player = Manager.createPlayer(new ByteArrayInputStream(stream), contentType); }
else { player = Manager.createPlayer(locator); }
}
else
{
// JAMDAT's Solitaire Deluxe prepends "resource:" before the actual resource location, also is the only sprint jar i've seen do this (and load data with locators)
if(locator.contains(":"))
{
int lastIndex = locator.lastIndexOf(":");

return Manager.createPlayer(Mobile.getResourceAsStream(null, locator.substring(lastIndex + 1)), contentType);
}
}
}
catch (IOException e) { Mobile.log(Mobile.LOG_WARNING, Clip.class.getPackage().getName() + "." + Clip.class.getSimpleName() + ": " + "failed to getPlayer: " + e.getMessage()); }

return player;
Expand Down
14 changes: 9 additions & 5 deletions src/com/sprintpcs/media/DualTone.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,18 @@ public class DualTone
private static final int PPQ = 24; // Ticks per quarter note
private static final int BPM = 120; // Default BPM, not sure if DTFM changes this
private static final double TICKS_PER_MILLISECOND = PPQ / (BPM * 4.0); // 4 quarter notes in a minute


byte[] sequence;
int loops;

/* x-frequencies (high freqs?), y-frequencies (low freqs?), note duration, unknown, loops? */
int priority, vibration;

/* Implementation Idea: Dual Tone can just be made a single midi sequence with two tracks, one for each tone sequence. */
public DualTone(int[] x_frequencies, int[] y_frequencies, int[] durations, int unknown, int loops)
public DualTone(int[] x_frequencies, int[] y_frequencies, int[] durations, int priority, int vibration)
{
try
{
this.priority = priority;
this.vibration = vibration;

Sequence midiSequence = new Sequence(Sequence.PPQ, PPQ);
Track trackA = midiSequence.createTrack();
Track trackB = midiSequence.createTrack();
Expand Down Expand Up @@ -85,6 +85,10 @@ public DualTone(int[] x_frequencies, int[] y_frequencies, int[] durations, int u
}
}

public int getPriority() { return priority; }

public int getVibration() { return vibration; }

private int convertFreqToNote(int frequency)
{
return (int) (Math.round(Math.log((double) frequency / 8.176) * SEMITONE_CONST)); // Adjust to MIDI note A4 (440 Hz = note 69)
Expand Down
56 changes: 41 additions & 15 deletions src/com/sprintpcs/media/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,59 +27,85 @@
public class Player
{
private static javax.microedition.media.Player player;
private static PlayerListener listener;
private static int priority;

public static void play(Clip clip, int repeat)
{
Mobile.log(Mobile.LOG_WARNING, Player.class.getPackage().getName() + "." + Player.class.getSimpleName() + ": " + "Untested");

if (repeat < -1) { throw new IllegalArgumentException("Invalid repeat value was received"); }

if (clip.getPriority() < priority)
{
Mobile.log(Mobile.LOG_WARNING, Player.class.getPackage().getName() + "." + Player.class.getSimpleName() + ": " + "Clip priority check.");
Mobile.log(Mobile.LOG_INFO, Player.class.getPackage().getName() + "." + Player.class.getSimpleName() + ": " + "new Clip priority lower than current media.");
return;
}

priority = clip.getPriority();

if (player != null) { player.close(); }

try { player = clip.getPlayer(); }
catch (Exception e) { Mobile.log(Mobile.LOG_WARNING, Player.class.getPackage().getName() + "." + Player.class.getSimpleName() + ": " + "failed to play media: " + e.getMessage()); }
catch (Exception e) { Mobile.log(Mobile.LOG_WARNING, Player.class.getPackage().getName() + "." + Player.class.getSimpleName() + ": " + "failed to prepare Clip media: " + e.getMessage()); }


if (repeat != -1 /* Clip.LOOP_CONTINUOUSLY */) { player.setLoopCount(repeat+1); }
else { player.setLoopCount(repeat); }

Mobile.vibrationDuration = clip.getVibration();

try { player.start(); }
catch (Exception e) { e.printStackTrace(); }
try
{
player.start();
listener.playerUpdate(PlayerListener.STARTED, player.getMediaTime());
}
catch (Exception e) { Mobile.log(Mobile.LOG_WARNING, Player.class.getPackage().getName() + "." + Player.class.getSimpleName() + ": " + "failed to play Clip media: " + e.getMessage()); }
}

public static void play(DualTone dTone, int repeat) // I assume the second argument is repeat, there's no documentation for it
{
if (repeat < -1) { throw new IllegalArgumentException("Invalid repeat value was received"); }

if (dTone.getPriority() < priority)
{
Mobile.log(Mobile.LOG_INFO, Player.class.getPackage().getName() + "." + Player.class.getSimpleName() + ": " + "new DualTone priority lower than current media.");
return;
}

priority = dTone.getPriority();

if (player != null) { player.close(); }

try { player = Manager.createPlayer(new ByteArrayInputStream(dTone.sequence), "audio/mid"); }
catch (Exception e) { Mobile.log(Mobile.LOG_WARNING, Player.class.getPackage().getName() + "." + Player.class.getSimpleName() + ": " + "failed to prepare tone media: " + e.getMessage()); }
catch (Exception e) { Mobile.log(Mobile.LOG_WARNING, Player.class.getPackage().getName() + "." + Player.class.getSimpleName() + ": " + "failed to prepare DualTone media: " + e.getMessage()); }


if (repeat != -1 /* Clip.LOOP_CONTINUOUSLY */) { player.setLoopCount(repeat+1); }
else { player.setLoopCount(repeat); }

Mobile.vibrationDuration = dTone.getVibration();

try { player.start(); }
try
{
player.start();
listener.playerUpdate(PlayerListener.STARTED, player.getMediaTime());
}
catch (Exception e)
{
Mobile.log(Mobile.LOG_WARNING, Player.class.getPackage().getName() + "." + Player.class.getSimpleName() + ": " + "failed to play media: " + e.getMessage());
Mobile.log(Mobile.LOG_WARNING, Player.class.getPackage().getName() + "." + Player.class.getSimpleName() + ": " + "failed to play DualTone media: " + e.getMessage());
}
}

public static void stop() {
if (player != null) {
player.close();
player = null;
}
public static void resume() { player.start(); }

public static void addPlayerListener(PlayerListener playerListener) { listener = playerListener; }

public static void removePlayerListener(PlayerListener playerListener) { listener = null; }

public static void stop()
{
if (player != null)
{
player.stop();
listener.playerUpdate(PlayerListener.STOPPED, player.getMediaTime());
}
}
}
31 changes: 31 additions & 0 deletions src/com/sprintpcs/media/PlayerListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
This file is part of FreeJ2ME.
FreeJ2ME is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FreeJ2ME is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FreeJ2ME. If not, see http://www.gnu.org/licenses/
*/
package com.sprintpcs.media;

public interface PlayerListener extends javax.microedition.media.PlayerListener
{
public static final int AUDIO_DEVICE_UNAVAILABLE = 0;
public static final int END_OF_DATA = 1;
public static final int ERROR = 2;
public static final int STARTED = 3;
public static final int STOPPED = 4;
public static final int PAUSED = 5;
public static final int RESUME = 6;
public static final int PREEMPTED = 7;

public void playerUpdate(int event, Object eventData);
}

0 comments on commit 1b2bf79

Please sign in to comment.