From 1b2bf7968f75b6da7bff85881639d0d669bd09b6 Mon Sep 17 00:00:00 2001 From: AShiningRay Date: Wed, 13 Nov 2024 20:40:32 -0300 Subject: [PATCH] Flesh out sprintpcs.media a bit more. 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) --- src/com/sprintpcs/media/Clip.java | 19 ++++--- src/com/sprintpcs/media/DualTone.java | 14 ++++-- src/com/sprintpcs/media/Player.java | 56 +++++++++++++++------ src/com/sprintpcs/media/PlayerListener.java | 31 ++++++++++++ 4 files changed, 92 insertions(+), 28 deletions(-) create mode 100644 src/com/sprintpcs/media/PlayerListener.java diff --git a/src/com/sprintpcs/media/Clip.java b/src/com/sprintpcs/media/Clip.java index 5206be27..3aeb5f51 100644 --- a/src/com/sprintpcs/media/Clip.java +++ b/src/com/sprintpcs/media/Clip.java @@ -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; @@ -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; @@ -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; diff --git a/src/com/sprintpcs/media/DualTone.java b/src/com/sprintpcs/media/DualTone.java index 546ae98d..fdbb47fd 100644 --- a/src/com/sprintpcs/media/DualTone.java +++ b/src/com/sprintpcs/media/DualTone.java @@ -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(); @@ -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) diff --git a/src/com/sprintpcs/media/Player.java b/src/com/sprintpcs/media/Player.java index 87a6dbcf..a9cec150 100644 --- a/src/com/sprintpcs/media/Player.java +++ b/src/com/sprintpcs/media/Player.java @@ -27,24 +27,25 @@ 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); } @@ -52,34 +53,59 @@ public static void play(Clip clip, int 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()); + } } } \ No newline at end of file diff --git a/src/com/sprintpcs/media/PlayerListener.java b/src/com/sprintpcs/media/PlayerListener.java new file mode 100644 index 00000000..1938f9ff --- /dev/null +++ b/src/com/sprintpcs/media/PlayerListener.java @@ -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); +} \ No newline at end of file