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