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 
24 namespace android
25 {
26 
27 static const int MIX_NUM_BUFFERS = 4;
28 static const S_EAS_LIB_CONFIG* pLibConfig = NULL;
29 
30 //-------------------------------------------------------------------------------------------------
JetPlayer(void * javaJetPlayer,int maxTracks,int trackBufferSize)31 JetPlayer::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         mTrackBufferSize(trackBufferSize)
40 {
41     ALOGV("JetPlayer constructor");
42     mPreviousJetStatus.currentUserID = -1;
43     mPreviousJetStatus.segmentRepeatCount = -1;
44     mPreviousJetStatus.numQueuedSegments = -1;
45     mPreviousJetStatus.paused = true;
46 }
47 
48 //-------------------------------------------------------------------------------------------------
~JetPlayer()49 JetPlayer::~JetPlayer()
50 {
51     ALOGV("~JetPlayer");
52     release();
53 
54 }
55 
56 //-------------------------------------------------------------------------------------------------
init()57 int 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 
setEventCallback(jetevent_callback eventCallback)122 void JetPlayer::setEventCallback(jetevent_callback eventCallback)
123 {
124     Mutex::Autolock l(mMutex);
125     mEventCallback = eventCallback;
126 }
127 
128 //-------------------------------------------------------------------------------------------------
release()129 int 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     mIoWrapper.clear();
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 //-------------------------------------------------------------------------------------------------
render()158 int 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 
246 threadExit:
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
fireUpdateOnStatusChange()264 void 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
fireEventsFromJetQueue()306 void 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 //-------------------------------------------------------------------------------------------------
loadFromFile(const char * path)326 int JetPlayer::loadFromFile(const char* path)
327 {
328     ALOGV("JetPlayer::loadFromFile(): path=%s", path);
329 
330     Mutex::Autolock lock(mMutex);
331 
332     mIoWrapper = new MidiIoWrapper(path);
333 
334     EAS_RESULT result = JET_OpenFile(mEasData, mIoWrapper->getLocator());
335     if (result != EAS_SUCCESS)
336         mState = EAS_STATE_ERROR;
337     else
338         mState = EAS_STATE_OPEN;
339     return( result );
340 }
341 
342 
343 //-------------------------------------------------------------------------------------------------
loadFromFD(const int fd,const long long offset,const long long length)344 int JetPlayer::loadFromFD(const int fd, const long long offset, const long long length)
345 {
346     ALOGV("JetPlayer::loadFromFD(): fd=%d offset=%lld length=%lld", fd, offset, length);
347 
348     Mutex::Autolock lock(mMutex);
349 
350     mIoWrapper = new MidiIoWrapper(fd, offset, length);
351 
352     EAS_RESULT result = JET_OpenFile(mEasData, mIoWrapper->getLocator());
353     if (result != EAS_SUCCESS)
354         mState = EAS_STATE_ERROR;
355     else
356         mState = EAS_STATE_OPEN;
357     return( result );
358 }
359 
360 
361 //-------------------------------------------------------------------------------------------------
closeFile()362 int JetPlayer::closeFile()
363 {
364     Mutex::Autolock lock(mMutex);
365     return JET_CloseFile(mEasData);
366 }
367 
368 
369 //-------------------------------------------------------------------------------------------------
play()370 int JetPlayer::play()
371 {
372     ALOGV("JetPlayer::play(): entering");
373     Mutex::Autolock lock(mMutex);
374 
375     EAS_RESULT result = JET_Play(mEasData);
376 
377     mPaused = false;
378     mRender = true;
379 
380     JET_Status(mEasData, &mJetStatus);
381     this->dumpJetStatus(&mJetStatus);
382 
383     fireUpdateOnStatusChange();
384 
385     // wake up render thread
386     ALOGV("JetPlayer::play(): wakeup render thread");
387     mCondition.signal();
388 
389     return result;
390 }
391 
392 //-------------------------------------------------------------------------------------------------
pause()393 int JetPlayer::pause()
394 {
395     Mutex::Autolock lock(mMutex);
396     mPaused = true;
397     EAS_RESULT result = JET_Pause(mEasData);
398 
399     mRender = false;
400 
401     JET_Status(mEasData, &mJetStatus);
402     this->dumpJetStatus(&mJetStatus);
403     fireUpdateOnStatusChange();
404 
405 
406     return result;
407 }
408 
409 
410 //-------------------------------------------------------------------------------------------------
queueSegment(int segmentNum,int libNum,int repeatCount,int transpose,EAS_U32 muteFlags,EAS_U8 userID)411 int JetPlayer::queueSegment(int segmentNum, int libNum, int repeatCount, int transpose,
412         EAS_U32 muteFlags, EAS_U8 userID)
413 {
414     ALOGV("JetPlayer::queueSegment segmentNum=%d, libNum=%d, repeatCount=%d, transpose=%d",
415         segmentNum, libNum, repeatCount, transpose);
416     Mutex::Autolock lock(mMutex);
417     return JET_QueueSegment(mEasData, segmentNum, libNum, repeatCount, transpose, muteFlags,
418             userID);
419 }
420 
421 //-------------------------------------------------------------------------------------------------
setMuteFlags(EAS_U32 muteFlags,bool sync)422 int JetPlayer::setMuteFlags(EAS_U32 muteFlags, bool sync)
423 {
424     Mutex::Autolock lock(mMutex);
425     return JET_SetMuteFlags(mEasData, muteFlags, sync);
426 }
427 
428 //-------------------------------------------------------------------------------------------------
setMuteFlag(int trackNum,bool muteFlag,bool sync)429 int JetPlayer::setMuteFlag(int trackNum, bool muteFlag, bool sync)
430 {
431     Mutex::Autolock lock(mMutex);
432     return JET_SetMuteFlag(mEasData, trackNum, muteFlag, sync);
433 }
434 
435 //-------------------------------------------------------------------------------------------------
triggerClip(int clipId)436 int JetPlayer::triggerClip(int clipId)
437 {
438     ALOGV("JetPlayer::triggerClip clipId=%d", clipId);
439     Mutex::Autolock lock(mMutex);
440     return JET_TriggerClip(mEasData, clipId);
441 }
442 
443 //-------------------------------------------------------------------------------------------------
clearQueue()444 int JetPlayer::clearQueue()
445 {
446     ALOGV("JetPlayer::clearQueue");
447     Mutex::Autolock lock(mMutex);
448     return JET_Clear_Queue(mEasData);
449 }
450 
451 //-------------------------------------------------------------------------------------------------
dump()452 void JetPlayer::dump()
453 {
454 }
455 
dumpJetStatus(S_JET_STATUS * pJetStatus)456 void JetPlayer::dumpJetStatus(S_JET_STATUS* pJetStatus)
457 {
458     if (pJetStatus!=NULL)
459         ALOGV(">> current JET player status: userID=%d segmentRepeatCount=%d numQueuedSegments=%d "
460                 "paused=%d",
461                 pJetStatus->currentUserID, pJetStatus->segmentRepeatCount,
462                 pJetStatus->numQueuedSegments, pJetStatus->paused);
463     else
464         ALOGE(">> JET player status is NULL");
465 }
466 
467 
468 } // end namespace android
469