JetPlayer.cpp revision c2f1f07084818942352c6bbfb36af9b6b330eb4e
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//#define LOG_NDEBUG 0
18#define LOG_TAG "JetPlayer-C"
19
20#include <utils/Log.h>
21#include <utils/threads.h>
22
23#include <media/JetPlayer.h>
24
25
26#ifdef HAVE_GETTID
27static pid_t myTid() { return gettid(); }
28#else
29static pid_t myTid() { return getpid(); }
30#endif
31
32
33namespace android
34{
35
36static const int MIX_NUM_BUFFERS = 4;
37static const S_EAS_LIB_CONFIG* pLibConfig = NULL;
38
39//-------------------------------------------------------------------------------------------------
40JetPlayer::JetPlayer(jobject javaJetPlayer, int maxTracks, int trackBufferSize) :
41        mEventCallback(NULL),
42        mJavaJetPlayerRef(javaJetPlayer),
43        mTid(-1),
44        mRender(false),
45        mPaused(false),
46        mMaxTracks(maxTracks),
47        mEasData(NULL),
48        mEasJetFileLoc(NULL),
49        mAudioTrack(NULL),
50        mTrackBufferSize(trackBufferSize)
51{
52    LOGV("JetPlayer constructor");
53    mPreviousJetStatus.currentUserID = -1;
54    mPreviousJetStatus.segmentRepeatCount = -1;
55    mPreviousJetStatus.numQueuedSegments = -1;
56    mPreviousJetStatus.paused = true;
57}
58
59//-------------------------------------------------------------------------------------------------
60JetPlayer::~JetPlayer()
61{
62    LOGV("~JetPlayer");
63    release();
64
65}
66
67//-------------------------------------------------------------------------------------------------
68int JetPlayer::init()
69{
70    //Mutex::Autolock lock(&mMutex);
71
72    EAS_RESULT result;
73
74    // retrieve the EAS library settings
75    if (pLibConfig == NULL)
76        pLibConfig = EAS_Config();
77    if (pLibConfig == NULL) {
78        LOGE("JetPlayer::init(): EAS library configuration could not be retrieved, aborting.");
79        return EAS_FAILURE;
80    }
81
82    // init the EAS library
83    result = EAS_Init(&mEasData);
84    if( result != EAS_SUCCESS) {
85        LOGE("JetPlayer::init(): Error initializing Sonivox EAS library, aborting.");
86        mState = EAS_STATE_ERROR;
87        return result;
88    }
89    // init the JET library with the default app event controller range
90    result = JET_Init(mEasData, NULL, sizeof(S_JET_CONFIG));
91    if( result != EAS_SUCCESS) {
92        LOGE("JetPlayer::init(): Error initializing JET library, aborting.");
93        mState = EAS_STATE_ERROR;
94        return result;
95    }
96
97    // create the output AudioTrack
98    mAudioTrack = new AudioTrack();
99    mAudioTrack->set(AudioSystem::MUSIC,  //TODO parametrize this
100            pLibConfig->sampleRate,
101            1, // format = PCM 16bits per sample,
102            (pLibConfig->numChannels == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
103            mTrackBufferSize,
104            0);
105
106    // create render and playback thread
107    {
108        Mutex::Autolock l(mMutex);
109        LOGV("JetPlayer::init(): trying to start render thread");
110        createThreadEtc(renderThread, this, "jetRenderThread", ANDROID_PRIORITY_AUDIO);
111        mCondition.wait(mMutex);
112    }
113    if (mTid > 0) {
114        // render thread started, we're ready
115        LOGV("JetPlayer::init(): render thread(%d) successfully started.", mTid);
116        mState = EAS_STATE_READY;
117    } else {
118        LOGE("JetPlayer::init(): failed to start render thread.");
119        mState = EAS_STATE_ERROR;
120        return EAS_FAILURE;
121    }
122
123    return EAS_SUCCESS;
124}
125
126void JetPlayer::setEventCallback(jetevent_callback eventCallback)
127{
128    Mutex::Autolock l(mMutex);
129    mEventCallback = eventCallback;
130}
131
132//-------------------------------------------------------------------------------------------------
133int JetPlayer::release()
134{
135    LOGV("JetPlayer::release()");
136    Mutex::Autolock lock(mMutex);
137    mPaused = true;
138    mRender = false;
139    if (mEasData) {
140        JET_Pause(mEasData);
141        JET_CloseFile(mEasData);
142        JET_Shutdown(mEasData);
143        EAS_Shutdown(mEasData);
144    }
145    if (mEasJetFileLoc) {
146        free(mEasJetFileLoc);
147        mEasJetFileLoc = NULL;
148    }
149    if (mAudioTrack) {
150        mAudioTrack->stop();
151        mAudioTrack->flush();
152        delete mAudioTrack;
153        mAudioTrack = NULL;
154    }
155    if (mAudioBuffer) {
156        delete mAudioBuffer;
157        mAudioBuffer = NULL;
158    }
159    mEasData = NULL;
160
161    return EAS_SUCCESS;
162}
163
164
165//-------------------------------------------------------------------------------------------------
166int JetPlayer::renderThread(void* p) {
167
168    return ((JetPlayer*)p)->render();
169}
170
171//-------------------------------------------------------------------------------------------------
172int JetPlayer::render() {
173    EAS_RESULT result = EAS_FAILURE;
174    EAS_I32 count;
175    int temp;
176    bool audioStarted = false;
177
178    LOGV("JetPlayer::render(): entering");
179
180    // allocate render buffer
181    mAudioBuffer =
182        new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * MIX_NUM_BUFFERS];
183    if (!mAudioBuffer) {
184        LOGE("JetPlayer::render(): mAudioBuffer allocate failed");
185        goto threadExit;
186    }
187
188    // signal main thread that we started
189    {
190        Mutex::Autolock l(mMutex);
191        mTid = myTid();
192        LOGV("JetPlayer::render(): render thread(%d) signal", mTid);
193        mCondition.signal();
194    }
195
196   while (1) {
197
198        mMutex.lock(); // [[[[[[[[ LOCK ---------------------------------------
199
200        if (mEasData == NULL) {
201            mMutex.unlock();
202            LOGV("JetPlayer::render(): NULL EAS data, exiting render.");
203            goto threadExit;
204        }
205
206        // nothing to render, wait for client thread to wake us up
207        while (!mRender)
208        {
209            LOGV("JetPlayer::render(): signal wait");
210            if (audioStarted) {
211                mAudioTrack->pause();
212                // we have to restart the playback once we start rendering again
213                audioStarted = false;
214            }
215            mCondition.wait(mMutex);
216            LOGV("JetPlayer::render(): signal rx'd");
217        }
218
219        // render midi data into the input buffer
220        int num_output = 0;
221        EAS_PCM* p = mAudioBuffer;
222        for (int i = 0; i < MIX_NUM_BUFFERS; i++) {
223            result = EAS_Render(mEasData, p, pLibConfig->mixBufferSize, &count);
224            if (result != EAS_SUCCESS) {
225                LOGE("JetPlayer::render(): EAS_Render returned error %ld", result);
226            }
227            p += count * pLibConfig->numChannels;
228            num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM);
229
230             // send events that were generated (if any) to the event callback
231            fireEventsFromJetQueue();
232        }
233
234        // update playback state
235        //LOGV("JetPlayer::render(): updating state");
236        JET_Status(mEasData, &mJetStatus);
237        fireUpdateOnStatusChange();
238        mPaused = mJetStatus.paused;
239
240        mMutex.unlock(); // UNLOCK ]]]]]]]] -----------------------------------
241
242        // check audio output track
243        if (mAudioTrack == NULL) {
244            LOGE("JetPlayer::render(): output AudioTrack was not created");
245            goto threadExit;
246        }
247
248        // Write data to the audio hardware
249        //LOGV("JetPlayer::render(): writing to audio output");
250        if ((temp = mAudioTrack->write(mAudioBuffer, num_output)) < 0) {
251            LOGE("JetPlayer::render(): Error in writing:%d",temp);
252            return temp;
253        }
254
255        // start audio output if necessary
256        if (!audioStarted) {
257            LOGV("JetPlayer::render(): starting audio playback");
258            mAudioTrack->start();
259            audioStarted = true;
260        }
261
262    }//while (1)
263
264threadExit:
265    if (mAudioTrack) {
266        mAudioTrack->stop();
267        mAudioTrack->flush();
268    }
269    if (mAudioBuffer) {
270        delete [] mAudioBuffer;
271        mAudioBuffer = NULL;
272    }
273    mMutex.lock();
274    mTid = -1;
275    mCondition.signal();
276    mMutex.unlock();
277    return result;
278}
279
280
281//-------------------------------------------------------------------------------------------------
282// fire up an update if any of the status fields has changed
283// precondition: mMutex locked
284void JetPlayer::fireUpdateOnStatusChange()
285{
286    if(  (mJetStatus.currentUserID      != mPreviousJetStatus.currentUserID)
287       ||(mJetStatus.segmentRepeatCount != mPreviousJetStatus.segmentRepeatCount) ) {
288        if(mEventCallback)  {
289            mEventCallback(
290                JetPlayer::JET_USERID_UPDATE,
291                mJetStatus.currentUserID,
292                mJetStatus.segmentRepeatCount,
293                mJavaJetPlayerRef);
294        }
295        mPreviousJetStatus.currentUserID      = mJetStatus.currentUserID;
296        mPreviousJetStatus.segmentRepeatCount = mJetStatus.segmentRepeatCount;
297    }
298
299    if(mJetStatus.numQueuedSegments != mPreviousJetStatus.numQueuedSegments) {
300        if(mEventCallback)  {
301            mEventCallback(
302                JetPlayer::JET_NUMQUEUEDSEGMENT_UPDATE,
303                mJetStatus.numQueuedSegments,
304                -1,
305                mJavaJetPlayerRef);
306        }
307        mPreviousJetStatus.numQueuedSegments  = mJetStatus.numQueuedSegments;
308    }
309
310    if(mJetStatus.paused != mPreviousJetStatus.paused) {
311        if(mEventCallback)  {
312            mEventCallback(JetPlayer::JET_PAUSE_UPDATE,
313                mJetStatus.paused,
314                -1,
315                mJavaJetPlayerRef);
316        }
317        mPreviousJetStatus.paused = mJetStatus.paused;
318    }
319
320}
321
322
323//-------------------------------------------------------------------------------------------------
324// fire up all the JET events in the JET engine queue (until the queue is empty)
325// precondition: mMutex locked
326void JetPlayer::fireEventsFromJetQueue()
327{
328    if(!mEventCallback) {
329        // no callback, just empty the event queue
330        while (JET_GetEvent(mEasData, NULL, NULL)) { }
331        return;
332    }
333
334    EAS_U32 rawEvent;
335    while (JET_GetEvent(mEasData, &rawEvent, NULL)) {
336        mEventCallback(
337            JetPlayer::JET_EVENT,
338            rawEvent,
339            -1,
340            mJavaJetPlayerRef);
341    }
342}
343
344
345//-------------------------------------------------------------------------------------------------
346int JetPlayer::loadFromFile(const char* path)
347{
348    LOGV("JetPlayer::loadFromFile(): path=%s", path);
349
350    Mutex::Autolock lock(mMutex);
351
352    mEasJetFileLoc = (EAS_FILE_LOCATOR) malloc(sizeof(EAS_FILE));
353    memset(mJetFilePath, 0, 256);
354    strncpy(mJetFilePath, path, strlen(path));
355    mEasJetFileLoc->path = mJetFilePath;
356
357    mEasJetFileLoc->fd = 0;
358    mEasJetFileLoc->length = 0;
359    mEasJetFileLoc->offset = 0;
360
361    EAS_RESULT result = JET_OpenFile(mEasData, mEasJetFileLoc);
362    if(result != EAS_SUCCESS)
363        mState = EAS_STATE_ERROR;
364    else
365        mState = EAS_STATE_OPEN;
366    return( result );
367}
368
369
370//-------------------------------------------------------------------------------------------------
371int JetPlayer::loadFromFD(const int fd, const long long offset, const long long length)
372{
373    LOGV("JetPlayer::loadFromFD(): fd=%d offset=%lld length=%lld", fd, offset, length);
374
375    Mutex::Autolock lock(mMutex);
376
377    mEasJetFileLoc = (EAS_FILE_LOCATOR) malloc(sizeof(EAS_FILE));
378    mEasJetFileLoc->fd = fd;
379    mEasJetFileLoc->offset = offset;
380    mEasJetFileLoc->length = length;
381    mEasJetFileLoc->path = NULL;
382
383    EAS_RESULT result = JET_OpenFile(mEasData, mEasJetFileLoc);
384    if(result != EAS_SUCCESS)
385        mState = EAS_STATE_ERROR;
386    else
387        mState = EAS_STATE_OPEN;
388    return( result );
389}
390
391
392//-------------------------------------------------------------------------------------------------
393int JetPlayer::closeFile()
394{
395    Mutex::Autolock lock(mMutex);
396    return JET_CloseFile(mEasData);
397}
398
399
400//-------------------------------------------------------------------------------------------------
401int JetPlayer::play()
402{
403    LOGV("JetPlayer::play(): entering");
404    Mutex::Autolock lock(mMutex);
405
406    EAS_RESULT result = JET_Play(mEasData);
407
408    mPaused = false;
409    mRender = true;
410
411    JET_Status(mEasData, &mJetStatus);
412    this->dumpJetStatus(&mJetStatus);
413
414    fireUpdateOnStatusChange();
415
416    // wake up render thread
417    LOGV("JetPlayer::play(): wakeup render thread");
418    mCondition.signal();
419
420    return result;
421}
422
423//-------------------------------------------------------------------------------------------------
424int JetPlayer::pause()
425{
426    Mutex::Autolock lock(mMutex);
427    mPaused = true;
428    EAS_RESULT result = JET_Pause(mEasData);
429
430    mRender = false;
431
432    JET_Status(mEasData, &mJetStatus);
433    this->dumpJetStatus(&mJetStatus);
434    fireUpdateOnStatusChange();
435
436
437    return result;
438}
439
440
441//-------------------------------------------------------------------------------------------------
442int JetPlayer::queueSegment(int segmentNum, int libNum, int repeatCount, int transpose,
443        EAS_U32 muteFlags, EAS_U8 userID)
444{
445    LOGV("JetPlayer::queueSegment segmentNum=%d, libNum=%d, repeatCount=%d, transpose=%d",
446        segmentNum, libNum, repeatCount, transpose);
447    Mutex::Autolock lock(mMutex);
448    return JET_QueueSegment(mEasData, segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
449}
450
451//-------------------------------------------------------------------------------------------------
452int JetPlayer::setMuteFlags(EAS_U32 muteFlags, bool sync)
453{
454    Mutex::Autolock lock(mMutex);
455    return JET_SetMuteFlags(mEasData, muteFlags, sync);
456}
457
458//-------------------------------------------------------------------------------------------------
459int JetPlayer::setMuteFlag(int trackNum, bool muteFlag, bool sync)
460{
461    Mutex::Autolock lock(mMutex);
462    return JET_SetMuteFlag(mEasData, trackNum, muteFlag, sync);
463}
464
465//-------------------------------------------------------------------------------------------------
466int JetPlayer::triggerClip(int clipId)
467{
468    LOGV("JetPlayer::triggerClip clipId=%d", clipId);
469    Mutex::Autolock lock(mMutex);
470    return JET_TriggerClip(mEasData, clipId);
471}
472
473//-------------------------------------------------------------------------------------------------
474int JetPlayer::clearQueue()
475{
476    LOGV("JetPlayer::clearQueue");
477    Mutex::Autolock lock(mMutex);
478    return JET_Clear_Queue(mEasData);
479}
480
481//-------------------------------------------------------------------------------------------------
482void JetPlayer::dump()
483{
484    LOGE("JetPlayer dump: JET file=%s", mEasJetFileLoc->path);
485}
486
487void JetPlayer::dumpJetStatus(S_JET_STATUS* pJetStatus)
488{
489    if(pJetStatus!=NULL)
490        LOGV(">> current JET player status: userID=%d segmentRepeatCount=%d numQueuedSegments=%d paused=%d",
491                pJetStatus->currentUserID, pJetStatus->segmentRepeatCount,
492                pJetStatus->numQueuedSegments, pJetStatus->paused);
493    else
494        LOGE(">> JET player status is NULL");
495}
496
497
498} // end namespace android
499
500