• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media;
18 
19 
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.content.res.AssetFileDescriptor;
22 import android.os.Build;
23 import android.os.Handler;
24 import android.os.Looper;
25 import android.os.Message;
26 import android.util.AndroidRuntimeException;
27 import android.util.Log;
28 
29 import java.io.FileDescriptor;
30 import java.lang.ref.WeakReference;
31 
32 /**
33  * JetPlayer provides access to JET content playback and control.
34  *
35  * <p>Please refer to the JET Creator User Manual for a presentation of the JET interactive
36  * music concept and how to use the JetCreator tool to create content to be player by JetPlayer.
37  *
38  * <p>Use of the JetPlayer class is based around the playback of a number of JET segments
39  * sequentially added to a playback FIFO queue. The rendering of the MIDI content stored in each
40  * segment can be dynamically affected by two mechanisms:
41  * <ul>
42  * <li>tracks in a segment can be muted or unmuted at any moment, individually or through
43  *    a mask (to change the mute state of multiple tracks at once)</li>
44  * <li>parts of tracks in a segment can be played at predefined points in the segment, in order
45  *    to maintain synchronization with the other tracks in the segment. This is achieved through
46  *    the notion of "clips", which can be triggered at any time, but that will play only at the
47  *    right time, as authored in the corresponding JET file.</li>
48  * </ul>
49  * As a result of the rendering and playback of the JET segments, the user of the JetPlayer instance
50  * can receive notifications from the JET engine relative to:
51  * <ul>
52  * <li>the playback state,</li>
53  * <li>the number of segments left to play in the queue,</li>
54  * <li>application controller events (CC80-83) to mark points in the MIDI segments.</li>
55  * </ul>
56  * Use {@link #getJetPlayer()} to construct a JetPlayer instance. JetPlayer is a singleton class.
57  * </p>
58  *
59  * <div class="special reference">
60  * <h3>Developer Guides</h3>
61  * <p>For more information about how to use JetPlayer, read the
62  * <a href="{@docRoot}guide/topics/media/jetplayer.html">JetPlayer</a> developer guide.</p></div>
63  */
64 public class JetPlayer
65 {
66     //--------------------------------------------
67     // Constants
68     //------------------------
69     /**
70      * The maximum number of simultaneous tracks. Use {@link #getMaxTracks()} to
71      * access this value.
72      */
73     private static int MAXTRACKS = 32;
74 
75     // to keep in sync with the JetPlayer class constants
76     // defined in frameworks/base/include/media/JetPlayer.h
77     private static final int JET_EVENT                   = 1;
78     private static final int JET_USERID_UPDATE           = 2;
79     private static final int JET_NUMQUEUEDSEGMENT_UPDATE = 3;
80     private static final int JET_PAUSE_UPDATE            = 4;
81 
82     // to keep in sync with external/sonivox/arm-wt-22k/lib_src/jet_data.h
83     // Encoding of event information on 32 bits
84     private static final int JET_EVENT_VAL_MASK    = 0x0000007f; // mask for value
85     private static final int JET_EVENT_CTRL_MASK   = 0x00003f80; // mask for controller
86     private static final int JET_EVENT_CHAN_MASK   = 0x0003c000; // mask for channel
87     private static final int JET_EVENT_TRACK_MASK  = 0x00fc0000; // mask for track number
88     private static final int JET_EVENT_SEG_MASK    = 0xff000000; // mask for segment ID
89     private static final int JET_EVENT_CTRL_SHIFT  = 7;  // shift to get controller number to bit 0
90     private static final int JET_EVENT_CHAN_SHIFT  = 14; // shift to get MIDI channel to bit 0
91     private static final int JET_EVENT_TRACK_SHIFT = 18; // shift to get track ID to bit 0
92     private static final int JET_EVENT_SEG_SHIFT   = 24; // shift to get segment ID to bit 0
93 
94     // to keep in sync with values used in external/sonivox/arm-wt-22k/Android.mk
95     // Jet rendering audio parameters
96     private static final int JET_OUTPUT_RATE = 22050; // _SAMPLE_RATE_22050 in Android.mk
97     private static final int JET_OUTPUT_CHANNEL_CONFIG =
98             AudioFormat.CHANNEL_OUT_STEREO; // NUM_OUTPUT_CHANNELS=2 in Android.mk
99 
100 
101     //--------------------------------------------
102     // Member variables
103     //------------------------
104     /**
105      * Handler for jet events and status updates coming from the native code
106      */
107     private NativeEventHandler mEventHandler = null;
108 
109     /**
110      * Looper associated with the thread that creates the AudioTrack instance
111      */
112     private Looper mInitializationLooper = null;
113 
114     /**
115      * Lock to protect the event listener updates against event notifications
116      */
117     private final Object mEventListenerLock = new Object();
118 
119     private OnJetEventListener mJetEventListener = null;
120 
121     private static JetPlayer singletonRef;
122 
123     static {
124         System.loadLibrary("media_jni");
125     }
126 
127     //--------------------------------
128     // Used exclusively by native code
129     //--------------------
130     /**
131      * Accessed by native methods: provides access to C++ JetPlayer object
132      */
133     @SuppressWarnings("unused")
134     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
135     private long mNativePlayerInJavaObj;
136 
137 
138     //--------------------------------------------
139     // Constructor, finalize
140     //------------------------
141     /**
142      * Factory method for the JetPlayer class.
143      * @return the singleton JetPlayer instance
144      */
getJetPlayer()145     public static JetPlayer getJetPlayer() {
146         if (singletonRef == null) {
147             singletonRef = new JetPlayer();
148         }
149         return singletonRef;
150     }
151 
152     /**
153      * Cloning a JetPlayer instance is not supported. Calling clone() will generate an exception.
154      */
clone()155     public Object clone() throws CloneNotSupportedException {
156         // JetPlayer is a singleton class,
157         // so you can't clone a JetPlayer instance
158         throw new CloneNotSupportedException();
159     }
160 
161 
JetPlayer()162     private JetPlayer() {
163 
164         // remember which looper is associated with the JetPlayer instanciation
165         if ((mInitializationLooper = Looper.myLooper()) == null) {
166             mInitializationLooper = Looper.getMainLooper();
167         }
168 
169         int buffSizeInBytes = AudioTrack.getMinBufferSize(JET_OUTPUT_RATE,
170                 JET_OUTPUT_CHANNEL_CONFIG, AudioFormat.ENCODING_PCM_16BIT);
171 
172         if ((buffSizeInBytes != AudioTrack.ERROR)
173                 && (buffSizeInBytes != AudioTrack.ERROR_BAD_VALUE)) {
174 
175             native_setup(new WeakReference<JetPlayer>(this),
176                     JetPlayer.getMaxTracks(),
177                     // bytes to frame conversion:
178                     // 1200 == minimum buffer size in frames on generation 1 hardware
179                     Math.max(1200, buffSizeInBytes /
180                             (AudioFormat.getBytesPerSample(AudioFormat.ENCODING_PCM_16BIT) *
181                             2 /*channels*/)));
182         }
183     }
184 
185 
finalize()186     protected void finalize() {
187         native_finalize();
188     }
189 
190 
191     /**
192      * Stops the current JET playback, and releases all associated native resources.
193      * The object can no longer be used and the reference should be set to null
194      * after a call to release().
195      */
release()196     public void release() {
197         native_release();
198         singletonRef = null;
199     }
200 
201 
202     //--------------------------------------------
203     // Getters
204     //------------------------
205     /**
206      * Returns the maximum number of simultaneous MIDI tracks supported by JetPlayer
207      */
getMaxTracks()208     public static int getMaxTracks() {
209         return JetPlayer.MAXTRACKS;
210     }
211 
212 
213     //--------------------------------------------
214     // Jet functionality
215     //------------------------
216     /**
217      * Loads a .jet file from a given path.
218      * @param path the path to the .jet file, for instance "/sdcard/mygame/music.jet".
219      * @return true if loading the .jet file was successful, false if loading failed.
220      */
loadJetFile(String path)221     public boolean loadJetFile(String path) {
222         return native_loadJetFromFile(path);
223     }
224 
225 
226     /**
227      * Loads a .jet file from an asset file descriptor.
228      * @param afd the asset file descriptor.
229      * @return true if loading the .jet file was successful, false if loading failed.
230      */
loadJetFile(AssetFileDescriptor afd)231     public boolean loadJetFile(AssetFileDescriptor afd) {
232         long len = afd.getLength();
233         if (len < 0) {
234             throw new AndroidRuntimeException("no length for fd");
235         }
236         return native_loadJetFromFileD(
237                 afd.getFileDescriptor(), afd.getStartOffset(), len);
238     }
239 
240     /**
241      * Closes the resource containing the JET content.
242      * @return true if successfully closed, false otherwise.
243      */
closeJetFile()244     public boolean closeJetFile() {
245         return native_closeJetFile();
246     }
247 
248 
249     /**
250      * Starts playing the JET segment queue.
251      * @return true if rendering and playback is successfully started, false otherwise.
252      */
play()253     public boolean play() {
254         return native_playJet();
255     }
256 
257 
258     /**
259      * Pauses the playback of the JET segment queue.
260      * @return true if rendering and playback is successfully paused, false otherwise.
261      */
pause()262     public boolean pause() {
263         return native_pauseJet();
264     }
265 
266 
267     /**
268      * Queues the specified segment in the JET queue.
269      * @param segmentNum the identifier of the segment.
270      * @param libNum the index of the sound bank associated with the segment. Use -1 to indicate
271      *    that no sound bank (DLS file) is associated with this segment, in which case JET will use
272      *    the General MIDI library.
273      * @param repeatCount the number of times the segment will be repeated. 0 means the segment will
274      *    only play once. -1 means the segment will repeat indefinitely.
275      * @param transpose the amount of pitch transposition. Set to 0 for normal playback.
276      *    Range is -12 to +12.
277      * @param muteFlags a bitmask to specify which MIDI tracks will be muted during playback. Bit 0
278      *    affects track 0, bit 1 affects track 1 etc.
279      * @param userID a value specified by the application that uniquely identifies the segment.
280      *    this value is received in the
281      *    {@link OnJetEventListener#onJetUserIdUpdate(JetPlayer, int, int)} event listener method.
282      *    Normally, the application will keep a byte value that is incremented each time a new
283      *    segment is queued up. This can be used to look up any special characteristics of that
284      *    track including trigger clips and mute flags.
285      * @return true if the segment was successfully queued, false if the queue is full or if the
286      *    parameters are invalid.
287      */
queueJetSegment(int segmentNum, int libNum, int repeatCount, int transpose, int muteFlags, byte userID)288     public boolean queueJetSegment(int segmentNum, int libNum, int repeatCount,
289         int transpose, int muteFlags, byte userID) {
290         return native_queueJetSegment(segmentNum, libNum, repeatCount,
291                 transpose, muteFlags, userID);
292     }
293 
294 
295     /**
296      * Queues the specified segment in the JET queue.
297      * @param segmentNum the identifier of the segment.
298      * @param libNum the index of the soundbank associated with the segment. Use -1 to indicate that
299      *    no sound bank (DLS file) is associated with this segment, in which case JET will use
300      *    the General MIDI library.
301      * @param repeatCount the number of times the segment will be repeated. 0 means the segment will
302      *    only play once. -1 means the segment will repeat indefinitely.
303      * @param transpose the amount of pitch transposition. Set to 0 for normal playback.
304      *    Range is -12 to +12.
305      * @param muteArray an array of booleans to specify which MIDI tracks will be muted during
306      *    playback. The value at index 0 affects track 0, value at index 1 affects track 1 etc.
307      *    The length of the array must be {@link #getMaxTracks()} for the call to succeed.
308      * @param userID a value specified by the application that uniquely identifies the segment.
309      *    this value is received in the
310      *    {@link OnJetEventListener#onJetUserIdUpdate(JetPlayer, int, int)} event listener method.
311      *    Normally, the application will keep a byte value that is incremented each time a new
312      *    segment is queued up. This can be used to look up any special characteristics of that
313      *    track including trigger clips and mute flags.
314      * @return true if the segment was successfully queued, false if the queue is full or if the
315      *    parameters are invalid.
316      */
queueJetSegmentMuteArray(int segmentNum, int libNum, int repeatCount, int transpose, boolean[] muteArray, byte userID)317     public boolean queueJetSegmentMuteArray(int segmentNum, int libNum, int repeatCount,
318             int transpose, boolean[] muteArray, byte userID) {
319         if (muteArray.length != JetPlayer.getMaxTracks()) {
320             return false;
321         }
322         return native_queueJetSegmentMuteArray(segmentNum, libNum, repeatCount,
323                 transpose, muteArray, userID);
324     }
325 
326 
327     /**
328      * Modifies the mute flags.
329      * @param muteFlags a bitmask to specify which MIDI tracks are muted. Bit 0 affects track 0,
330      *    bit 1 affects track 1 etc.
331      * @param sync if false, the new mute flags will be applied as soon as possible by the JET
332      *    render and playback engine. If true, the mute flags will be updated at the start of the
333      *    next segment. If the segment is repeated, the flags will take effect the next time
334      *    segment is repeated.
335      * @return true if the mute flags were successfully updated, false otherwise.
336      */
setMuteFlags(int muteFlags, boolean sync)337     public boolean setMuteFlags(int muteFlags, boolean sync) {
338         return native_setMuteFlags(muteFlags, sync);
339     }
340 
341 
342     /**
343      * Modifies the mute flags for the current active segment.
344      * @param muteArray an array of booleans to specify which MIDI tracks are muted. The value at
345      *    index 0 affects track 0, value at index 1 affects track 1 etc.
346      *    The length of the array must be {@link #getMaxTracks()} for the call to succeed.
347      * @param sync if false, the new mute flags will be applied as soon as possible by the JET
348      *    render and playback engine. If true, the mute flags will be updated at the start of the
349      *    next segment. If the segment is repeated, the flags will take effect the next time
350      *    segment is repeated.
351      * @return true if the mute flags were successfully updated, false otherwise.
352      */
setMuteArray(boolean[] muteArray, boolean sync)353     public boolean setMuteArray(boolean[] muteArray, boolean sync) {
354         if(muteArray.length != JetPlayer.getMaxTracks())
355             return false;
356         return native_setMuteArray(muteArray, sync);
357     }
358 
359 
360     /**
361      * Mutes or unmutes a single track.
362      * @param trackId the index of the track to mute.
363      * @param muteFlag set to true to mute, false to unmute.
364      * @param sync if false, the new mute flags will be applied as soon as possible by the JET
365      *    render and playback engine. If true, the mute flag will be updated at the start of the
366      *    next segment. If the segment is repeated, the flag will take effect the next time
367      *    segment is repeated.
368      * @return true if the mute flag was successfully updated, false otherwise.
369      */
setMuteFlag(int trackId, boolean muteFlag, boolean sync)370     public boolean setMuteFlag(int trackId, boolean muteFlag, boolean sync) {
371         return native_setMuteFlag(trackId, muteFlag, sync);
372     }
373 
374 
375     /**
376      * Schedules the playback of a clip.
377      * This will automatically update the mute flags in sync with the JET Clip Marker (controller
378      * 103). The parameter clipID must be in the range of 0-63. After the call to triggerClip, when
379      * JET next encounters a controller event 103 with bits 0-5 of the value equal to clipID and
380      * bit 6 set to 1, it will automatically unmute the track containing the controller event.
381      * When JET encounters the complementary controller event 103 with bits 0-5 of the value equal
382      * to clipID and bit 6 set to 0, it will mute the track again.
383      * @param clipId the identifier of the clip to trigger.
384      * @return true if the clip was successfully triggered, false otherwise.
385      */
triggerClip(int clipId)386     public boolean triggerClip(int clipId) {
387         return native_triggerClip(clipId);
388     }
389 
390 
391     /**
392      * Empties the segment queue, and clears all clips that are scheduled for playback.
393      * @return true if the queue was successfully cleared, false otherwise.
394      */
clearQueue()395     public boolean clearQueue() {
396         return native_clearQueue();
397     }
398 
399 
400     //---------------------------------------------------------
401     // Internal class to handle events posted from native code
402     //------------------------
403     private class NativeEventHandler extends Handler
404     {
405         private JetPlayer mJet;
406 
NativeEventHandler(JetPlayer jet, Looper looper)407         public NativeEventHandler(JetPlayer jet, Looper looper) {
408             super(looper);
409             mJet = jet;
410         }
411 
412         @Override
handleMessage(Message msg)413         public void handleMessage(Message msg) {
414             OnJetEventListener listener = null;
415             synchronized (mEventListenerLock) {
416                 listener = mJet.mJetEventListener;
417             }
418             switch(msg.what) {
419             case JET_EVENT:
420                 if (listener != null) {
421                     // call the appropriate listener after decoding the event parameters
422                     // encoded in msg.arg1
423                     mJetEventListener.onJetEvent(
424                             mJet,
425                             (short)((msg.arg1 & JET_EVENT_SEG_MASK)   >> JET_EVENT_SEG_SHIFT),
426                             (byte) ((msg.arg1 & JET_EVENT_TRACK_MASK) >> JET_EVENT_TRACK_SHIFT),
427                             // JETCreator channel numbers start at 1, but the index starts at 0
428                             // in the .jet files
429                             (byte)(((msg.arg1 & JET_EVENT_CHAN_MASK)  >> JET_EVENT_CHAN_SHIFT) + 1),
430                             (byte) ((msg.arg1 & JET_EVENT_CTRL_MASK)  >> JET_EVENT_CTRL_SHIFT),
431                             (byte)  (msg.arg1 & JET_EVENT_VAL_MASK) );
432                 }
433                 return;
434             case JET_USERID_UPDATE:
435                 if (listener != null) {
436                     listener.onJetUserIdUpdate(mJet, msg.arg1, msg.arg2);
437                 }
438                 return;
439             case JET_NUMQUEUEDSEGMENT_UPDATE:
440                 if (listener != null) {
441                     listener.onJetNumQueuedSegmentUpdate(mJet, msg.arg1);
442                 }
443                 return;
444             case JET_PAUSE_UPDATE:
445                 if (listener != null)
446                     listener.onJetPauseUpdate(mJet, msg.arg1);
447                 return;
448 
449             default:
450                 loge("Unknown message type " + msg.what);
451                 return;
452             }
453         }
454     }
455 
456 
457     //--------------------------------------------
458     // Jet event listener
459     //------------------------
460     /**
461      * Sets the listener JetPlayer notifies when a JET event is generated by the rendering and
462      * playback engine.
463      * Notifications will be received in the same thread as the one in which the JetPlayer
464      * instance was created.
465      * @param listener
466      */
setEventListener(OnJetEventListener listener)467     public void setEventListener(OnJetEventListener listener) {
468         setEventListener(listener, null);
469     }
470 
471     /**
472      * Sets the listener JetPlayer notifies when a JET event is generated by the rendering and
473      * playback engine.
474      * Use this method to receive JET events in the Handler associated with another
475      * thread than the one in which you created the JetPlayer instance.
476      * @param listener
477      * @param handler the Handler that will receive the event notification messages.
478      */
setEventListener(OnJetEventListener listener, Handler handler)479     public void setEventListener(OnJetEventListener listener, Handler handler) {
480         synchronized(mEventListenerLock) {
481 
482             mJetEventListener = listener;
483 
484             if (listener != null) {
485                 if (handler != null) {
486                     mEventHandler = new NativeEventHandler(this, handler.getLooper());
487                 } else {
488                     // no given handler, use the looper the AudioTrack was created in
489                     mEventHandler = new NativeEventHandler(this, mInitializationLooper);
490                 }
491             } else {
492                 mEventHandler = null;
493             }
494 
495         }
496     }
497 
498 
499     /**
500      * Handles the notification when the JET engine generates an event.
501      */
502     public interface OnJetEventListener {
503         /**
504          * Callback for when the JET engine generates a new event.
505          *
506          * @param player the JET player the event is coming from
507          * @param segment 8 bit unsigned value
508          * @param track 6 bit unsigned value
509          * @param channel 4 bit unsigned value
510          * @param controller 7 bit unsigned value
511          * @param value 7 bit unsigned value
512          */
onJetEvent(JetPlayer player, short segment, byte track, byte channel, byte controller, byte value)513         void onJetEvent(JetPlayer player,
514                 short segment, byte track, byte channel, byte controller, byte value);
515         /**
516          * Callback for when JET's currently playing segment's userID is updated.
517          *
518          * @param player the JET player the status update is coming from
519          * @param userId the ID of the currently playing segment
520          * @param repeatCount the repetition count for the segment (0 means it plays once)
521          */
onJetUserIdUpdate(JetPlayer player, int userId, int repeatCount)522         void onJetUserIdUpdate(JetPlayer player, int userId, int repeatCount);
523 
524         /**
525          * Callback for when JET's number of queued segments is updated.
526          *
527          * @param player the JET player the status update is coming from
528          * @param nbSegments the number of segments in the JET queue
529          */
onJetNumQueuedSegmentUpdate(JetPlayer player, int nbSegments)530         void onJetNumQueuedSegmentUpdate(JetPlayer player, int nbSegments);
531 
532         /**
533          * Callback for when JET pause state is updated.
534          *
535          * @param player the JET player the status update is coming from
536          * @param paused indicates whether JET is paused (1) or not (0)
537          */
onJetPauseUpdate(JetPlayer player, int paused)538         void onJetPauseUpdate(JetPlayer player, int paused);
539     }
540 
541 
542     //--------------------------------------------
543     // Native methods
544     //------------------------
native_setup(Object Jet_this, int maxTracks, int trackBufferSize)545     private native final boolean native_setup(Object Jet_this,
546                 int maxTracks, int trackBufferSize);
native_finalize()547     private native final void    native_finalize();
native_release()548     private native final void    native_release();
native_loadJetFromFile(String pathToJetFile)549     private native final boolean native_loadJetFromFile(String pathToJetFile);
native_loadJetFromFileD(FileDescriptor fd, long offset, long len)550     private native final boolean native_loadJetFromFileD(FileDescriptor fd, long offset, long len);
native_closeJetFile()551     private native final boolean native_closeJetFile();
native_playJet()552     private native final boolean native_playJet();
native_pauseJet()553     private native final boolean native_pauseJet();
native_queueJetSegment(int segmentNum, int libNum, int repeatCount, int transpose, int muteFlags, byte userID)554     private native final boolean native_queueJetSegment(int segmentNum, int libNum,
555             int repeatCount, int transpose, int muteFlags, byte userID);
native_queueJetSegmentMuteArray(int segmentNum, int libNum, int repeatCount, int transpose, boolean[] muteArray, byte userID)556     private native final boolean native_queueJetSegmentMuteArray(int segmentNum, int libNum,
557             int repeatCount, int transpose, boolean[] muteArray, byte userID);
native_setMuteFlags(int muteFlags, boolean sync)558     private native final boolean native_setMuteFlags(int muteFlags, boolean sync);
native_setMuteArray(boolean[]muteArray, boolean sync)559     private native final boolean native_setMuteArray(boolean[]muteArray, boolean sync);
native_setMuteFlag(int trackId, boolean muteFlag, boolean sync)560     private native final boolean native_setMuteFlag(int trackId, boolean muteFlag, boolean sync);
native_triggerClip(int clipId)561     private native final boolean native_triggerClip(int clipId);
native_clearQueue()562     private native final boolean native_clearQueue();
563 
564     //---------------------------------------------------------
565     // Called exclusively by native code
566     //--------------------
567     @SuppressWarnings("unused")
568     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
postEventFromNative(Object jetplayer_ref, int what, int arg1, int arg2)569     private static void postEventFromNative(Object jetplayer_ref,
570             int what, int arg1, int arg2) {
571         //logd("Event posted from the native side: event="+ what + " args="+ arg1+" "+arg2);
572         JetPlayer jet = (JetPlayer)((WeakReference)jetplayer_ref).get();
573 
574         if ((jet != null) && (jet.mEventHandler != null)) {
575             Message m =
576                 jet.mEventHandler.obtainMessage(what, arg1, arg2, null);
577             jet.mEventHandler.sendMessage(m);
578         }
579 
580     }
581 
582 
583     //---------------------------------------------------------
584     // Utils
585     //--------------------
586     private final static String TAG = "JetPlayer-J";
587 
logd(String msg)588     private static void logd(String msg) {
589         Log.d(TAG, "[ android.media.JetPlayer ] " + msg);
590     }
591 
loge(String msg)592     private static void loge(String msg) {
593         Log.e(TAG, "[ android.media.JetPlayer ] " + msg);
594     }
595 
596 }
597