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