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