JetPlayer.java revision 3dec7d563a2f3e1eb967ce2054a00b6620e3558c
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
17package android.media;
18
19
20import java.io.FileDescriptor;
21import java.lang.ref.WeakReference;
22import java.lang.CloneNotSupportedException;
23
24import android.content.res.AssetFileDescriptor;
25import android.os.Looper;
26import android.os.Handler;
27import android.os.Message;
28import android.util.AndroidRuntimeException;
29import android.util.Log;
30
31/**
32 * JetPlayer provides access to JET content playback and control.
33 * <p>
34 * Use <code>JetPlayer.getJetPlayer()</code> to get an instance of this class.
35 *
36 */
37public class JetPlayer
38{
39    //--------------------------------------------
40    // Constants
41    //------------------------
42    /**
43     * The maximum number of simultaneous tracks. Use __link #getMaxTracks()} to
44     * access this value.
45     */
46    private static int MAXTRACKS = 32;
47
48    // to keep in sync with the JetPlayer class constants
49    // defined in frameworks/base/include/media/JetPlayer.h
50    private static final int JET_EVENT                   = 1;
51    private static final int JET_USERID_UPDATE           = 2;
52    private static final int JET_NUMQUEUEDSEGMENT_UPDATE = 3;
53    private static final int JET_PAUSE_UPDATE            = 4;
54
55    // to keep in sync with external/sonivox/arm-wt-22k/lib_src/jet_data.h
56    // Encoding of event information on 32 bits
57    private static final int JET_EVENT_VAL_MASK    = 0x0000007f; // mask for value
58    private static final int JET_EVENT_CTRL_MASK   = 0x00003f80; // mask for controller
59    private static final int JET_EVENT_CHAN_MASK   = 0x0003c000; // mask for channel
60    private static final int JET_EVENT_TRACK_MASK  = 0x00fc0000; // mask for track number
61    private static final int JET_EVENT_SEG_MASK    = 0xff000000; // mask for segment ID
62    private static final int JET_EVENT_CTRL_SHIFT  = 7;  // shift to get controller number to bit 0
63    private static final int JET_EVENT_CHAN_SHIFT  = 14; // shift to get MIDI channel to bit 0
64    private static final int JET_EVENT_TRACK_SHIFT = 18; // shift to get track ID to bit 0
65    private static final int JET_EVENT_SEG_SHIFT   = 24; // shift to get segment ID to bit 0
66
67
68    //--------------------------------------------
69    // Member variables
70    //------------------------
71    private EventHandler            mNativeEventHandler = null;
72
73    /**
74     * Lock to protect status listener updates against status change notifications
75     */
76    private final Object mStatusListenerLock = new Object();
77
78    /**
79     * Lock to protect the event listener updates against event notifications
80     */
81    private final Object mEventListenerLock = new Object();
82
83    private JetStatusUpdateListener mJetStatusUpdateListener = null;
84
85    private JetEventListener mJetEventListener = null;
86
87    private static JetPlayer singletonRef;
88
89
90    //--------------------------------
91    // Used exclusively by native code
92    //--------------------
93    /**
94     * Accessed by native methods: provides access to C++ JetPlayer object
95     */
96    @SuppressWarnings("unused")
97    private int mNativePlayerInJavaObj;
98
99
100    //--------------------------------------------
101    // Constructor, finalize
102    //------------------------
103    public static JetPlayer getJetPlayer() {
104        if (singletonRef == null)
105            singletonRef = new JetPlayer();
106        return singletonRef;
107    }
108
109
110    public Object clone() throws CloneNotSupportedException {
111        // JetPlayer is a singleton class,
112        // so you can't clone a JetPlayer instance
113        throw new CloneNotSupportedException();
114    }
115
116
117    private JetPlayer() {
118
119        native_setup(new WeakReference<JetPlayer>(this),
120                JetPlayer.getMaxTracks(),
121                1200); //TODO parametrize this (?)
122    }
123
124
125    protected void finalize() {
126        native_finalize();
127    }
128
129
130    public void release() {
131        native_release();
132    }
133
134
135    private void createNativeEventHandler() {
136        Looper looper;
137        if ((looper = Looper.myLooper()) != null) {
138            mNativeEventHandler = new EventHandler(this, looper);
139        } else if ((looper = Looper.getMainLooper()) != null) {
140            mNativeEventHandler = new EventHandler(this, looper);
141        } else {
142            mNativeEventHandler = null;
143        }
144    }
145
146
147    //--------------------------------------------
148    // Getters
149    //------------------------
150    /**
151     * Returns the maximum number of simultaneous MIDI tracks supported by the Jet player
152     */
153    public static int getMaxTracks() {
154        return JetPlayer.MAXTRACKS;
155    }
156
157
158    //--------------------------------------------
159    // Jet functionality
160    //------------------------
161    public boolean loadJetFile(String path) {
162        return native_loadJetFromFile(path);
163    }
164
165
166    public boolean loadJetFile(AssetFileDescriptor afd) {
167        long len = afd.getLength();
168        if (len < 0) {
169            throw new AndroidRuntimeException("no length for fd");
170        }
171        return native_loadJetFromFileD(
172                afd.getFileDescriptor(), afd.getStartOffset(), len);
173    }
174
175
176    public boolean closeJetFile() {
177        return native_closeJetFile();
178    }
179
180
181    public boolean play() {
182        return native_playJet();
183    }
184
185
186    public boolean pause() {
187        return native_pauseJet();
188    }
189
190
191    public boolean queueJetSegment(int segmentNum, int libNum, int repeatCount,
192        int transpose, int muteFlags, byte userID) {
193        return native_queueJetSegment(segmentNum, libNum, repeatCount,
194                transpose, muteFlags, userID);
195    }
196
197
198    public boolean queueJetSegmentMuteArray(int segmentNum, int libNum, int repeatCount,
199            int transpose, boolean[] muteArray, byte userID) {
200        if (muteArray.length != JetPlayer.getMaxTracks()) {
201            return false;
202        }
203        return native_queueJetSegmentMuteArray(segmentNum, libNum, repeatCount,
204                transpose, muteArray, userID);
205    }
206
207
208    public boolean setMuteFlags(int muteFlags, boolean sync) {
209        return native_setMuteFlags(muteFlags, sync);
210    }
211
212
213    public boolean setMuteArray(boolean[] muteArray, boolean sync) {
214        if(muteArray.length != JetPlayer.getMaxTracks())
215            return false;
216        return native_setMuteArray(muteArray, sync);
217    }
218
219
220    public boolean setMuteFlag(int trackId, boolean muteFlag, boolean sync) {
221        return native_setMuteFlag(trackId, muteFlag, sync);
222    }
223
224
225    public boolean triggerClip(int clipId) {
226        return native_triggerClip(clipId);
227    }
228
229
230    public boolean clearQueue() {
231        return native_clearQueue();
232    }
233
234
235    //---------------------------------------------------------
236    // Internal class to handle events posted from native code
237    //------------------------
238    private class EventHandler extends Handler
239    {
240        private JetPlayer mJet;
241
242        public EventHandler(JetPlayer jet, Looper looper) {
243            super(looper);
244            mJet = jet;
245        }
246
247        @Override
248        public void handleMessage(Message msg) {
249            switch(msg.what) {
250            case JET_EVENT:
251                synchronized (mEventListenerLock) {
252                    if (mJetEventListener != null) {
253                        // call the appropriate listener after decoding the event parameters
254                        // encoded in msg.arg1
255                        mJetEventListener.onJetEvent(
256                            mJet,
257                            (short)((msg.arg1 & JET_EVENT_SEG_MASK)   >> JET_EVENT_SEG_SHIFT),
258                            (byte) ((msg.arg1 & JET_EVENT_TRACK_MASK) >> JET_EVENT_TRACK_SHIFT),
259                            // JETCreator channel numbers start at 1, but the index starts at 0
260                            // in the .jet files
261                            (byte)(((msg.arg1 & JET_EVENT_CHAN_MASK)  >> JET_EVENT_CHAN_SHIFT) + 1),
262                            (byte) ((msg.arg1 & JET_EVENT_CTRL_MASK)  >> JET_EVENT_CTRL_SHIFT),
263                            (byte)  (msg.arg1 & JET_EVENT_VAL_MASK) );
264                    }
265                }
266                return;
267            case JET_USERID_UPDATE:
268                synchronized (mStatusListenerLock) {
269                    if (mJetStatusUpdateListener != null) {
270                        mJetStatusUpdateListener.onJetUserIdUpdate(mJet, msg.arg1, msg.arg2);
271                    }
272                }
273                return;
274            case JET_NUMQUEUEDSEGMENT_UPDATE:
275                synchronized (mStatusListenerLock) {
276                    if (mJetStatusUpdateListener != null) {
277                        mJetStatusUpdateListener.onJetNumQueuedSegmentUpdate(mJet, msg.arg1);
278                    }
279                }
280                return;
281            case JET_PAUSE_UPDATE:
282                synchronized (mStatusListenerLock) {
283                    if (mJetStatusUpdateListener != null)
284                        mJetStatusUpdateListener.onJetPauseUpdate(mJet, msg.arg1);
285                }
286                return;
287
288            default:
289                loge("Unknown message type " + msg.what);
290                return;
291            }
292        }
293    }
294
295
296    //--------------------------------------------
297    // Jet status update listener
298    //------------------------
299    public void setStatusUpdateListener(JetStatusUpdateListener listener) {
300        synchronized(mStatusListenerLock) {
301            mJetStatusUpdateListener = listener;
302        }
303
304        if ((listener != null) && (mNativeEventHandler == null)) {
305            createNativeEventHandler();
306        }
307    }
308
309    /**
310     * Handles the notification when the JET status is updated.
311     */
312    public interface JetStatusUpdateListener {
313        /**
314         * Callback for when JET's currently playing segment userID is updated.
315         *
316         * @param player the JET player the status update is coming from
317         * @param userId the ID of the currently playing segment
318         * @param repeatCount the repetition count for the segment (0 means it plays once)
319         */
320        void onJetUserIdUpdate(JetPlayer player, int userId, int repeatCount);
321
322        /**
323         * Callback for when JET's number of queued segments is updated.
324         *
325         * @param player the JET player the status update is coming from
326         * @param nbSegments the number of segments in the JET queue
327         */
328        void onJetNumQueuedSegmentUpdate(JetPlayer player, int nbSegments);
329
330        /**
331         * Callback for when JET pause state is updated.
332         *
333         * @param player the JET player the status update is coming from
334         * @param paused indicates whether JET is paused or not
335         */
336        void onJetPauseUpdate(JetPlayer player, int paused);
337    }
338
339
340    //--------------------------------------------
341    // Jet event listener
342    //------------------------
343    public void setEventListener(JetEventListener listener) {
344        synchronized(mEventListenerLock) {
345            mJetEventListener = listener;
346        }
347
348        if ((listener != null) && (mNativeEventHandler == null)) {
349            createNativeEventHandler();
350        }
351    }
352
353    /**
354     * Handles the notification when the JET engine generates an event.
355     */
356    public interface JetEventListener {
357        /**
358         * Callback for when the JET engine generates a new event.
359         *
360         * @param player the JET player the event is coming from
361         * @param segment 8 bit unsigned value
362         * @param track 6 bit unsigned value
363         * @param channel 4 bit unsigned value
364         * @param controller 7 bit unsigned value
365         * @param value 7 bit unsigned value
366         */
367        void onJetEvent(JetPlayer player,
368                short segment, byte track, byte channel, byte controller, byte value);
369    }
370
371
372    //--------------------------------------------
373    // Native methods
374    //------------------------
375    private native final boolean native_setup(Object Jet_this,
376                int maxTracks, int trackBufferSize);
377    private native final void    native_finalize();
378    private native final void    native_release();
379    private native final boolean native_loadJetFromFile(String pathToJetFile);
380    private native final boolean native_loadJetFromFileD(FileDescriptor fd, long offset, long len);
381    private native final boolean native_closeJetFile();
382    private native final boolean native_playJet();
383    private native final boolean native_pauseJet();
384    private native final boolean native_queueJetSegment(int segmentNum, int libNum,
385            int repeatCount, int transpose, int muteFlags, byte userID);
386    private native final boolean native_queueJetSegmentMuteArray(int segmentNum, int libNum,
387            int repeatCount, int transpose, boolean[] muteArray, byte userID);
388    private native final boolean native_setMuteFlags(int muteFlags, boolean sync);
389    private native final boolean native_setMuteArray(boolean[]muteArray, boolean sync);
390    private native final boolean native_setMuteFlag(int trackId, boolean muteFlag, boolean sync);
391    private native final boolean native_triggerClip(int clipId);
392    private native final boolean native_clearQueue();
393
394    //---------------------------------------------------------
395    // Called exclusively by native code
396    //--------------------
397    @SuppressWarnings("unused")
398    private static void postEventFromNative(Object jetplayer_ref,
399            int what, int arg1, int arg2) {
400
401        JetPlayer jet = (JetPlayer)((WeakReference)jetplayer_ref).get();
402
403        if( (jet!=null) && (jet.mNativeEventHandler!=null) ){
404            Message m = jet.mNativeEventHandler.obtainMessage(what, arg1, arg2, null);
405            jet.mNativeEventHandler.sendMessage(m);
406        }
407    }
408
409
410    //---------------------------------------------------------
411    // Utils
412    //--------------------
413    private final static String TAG = "JetPlayer-J";
414
415    private static void logd(String msg) {
416        Log.d(TAG, "[ android.media.JetPlayer ] " + msg);
417    }
418
419    private static void loge(String msg) {
420        Log.e(TAG, "[ android.media.JetPlayer ] " + msg);
421    }
422
423}
424