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