Move JetPlayer implemention and JNI
The JetPlayer implementation in libmedia was only used by the
JetPlayer JNI code in libandroid_runtime. This change moves
both implementation and JNI to libmedia_jni. This reduces
libandroid_runtime's dependency on libmedia, and results in
a net size reduction of the libraries involved.
Test: atest JetPlayerTest
Change-Id: I028c774fdea167924b064855267254c15f22fddb
diff --git a/media/jni/JetPlayer.cpp b/media/jni/JetPlayer.cpp
new file mode 100644
index 0000000..358feb7
--- /dev/null
+++ b/media/jni/JetPlayer.cpp
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "JetPlayer-C"
+
+#include <utils/Log.h>
+#include "JetPlayer.h"
+
+
+namespace android
+{
+
+static const int MIX_NUM_BUFFERS = 4;
+static const S_EAS_LIB_CONFIG* pLibConfig = NULL;
+
+//-------------------------------------------------------------------------------------------------
+JetPlayer::JetPlayer(void *javaJetPlayer, int maxTracks, int trackBufferSize) :
+ mEventCallback(NULL),
+ mJavaJetPlayerRef(javaJetPlayer),
+ mTid(-1),
+ mRender(false),
+ mPaused(false),
+ mMaxTracks(maxTracks),
+ mEasData(NULL),
+ mIoWrapper(NULL),
+ mTrackBufferSize(trackBufferSize)
+{
+ ALOGV("JetPlayer constructor");
+ mPreviousJetStatus.currentUserID = -1;
+ mPreviousJetStatus.segmentRepeatCount = -1;
+ mPreviousJetStatus.numQueuedSegments = -1;
+ mPreviousJetStatus.paused = true;
+}
+
+//-------------------------------------------------------------------------------------------------
+JetPlayer::~JetPlayer()
+{
+ ALOGV("~JetPlayer");
+ release();
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::init()
+{
+ //Mutex::Autolock lock(&mMutex);
+
+ EAS_RESULT result;
+
+ // retrieve the EAS library settings
+ if (pLibConfig == NULL)
+ pLibConfig = EAS_Config();
+ if (pLibConfig == NULL) {
+ ALOGE("JetPlayer::init(): EAS library configuration could not be retrieved, aborting.");
+ return EAS_FAILURE;
+ }
+
+ // init the EAS library
+ result = EAS_Init(&mEasData);
+ if (result != EAS_SUCCESS) {
+ ALOGE("JetPlayer::init(): Error initializing Sonivox EAS library, aborting.");
+ mState = EAS_STATE_ERROR;
+ return result;
+ }
+ // init the JET library with the default app event controller range
+ result = JET_Init(mEasData, NULL, sizeof(S_JET_CONFIG));
+ if (result != EAS_SUCCESS) {
+ ALOGE("JetPlayer::init(): Error initializing JET library, aborting.");
+ mState = EAS_STATE_ERROR;
+ return result;
+ }
+
+ // create the output AudioTrack
+ mAudioTrack = new AudioTrack();
+ status_t status = mAudioTrack->set(AUDIO_STREAM_MUSIC, //TODO parameterize this
+ pLibConfig->sampleRate,
+ AUDIO_FORMAT_PCM_16_BIT,
+ audio_channel_out_mask_from_count(pLibConfig->numChannels),
+ (size_t) mTrackBufferSize,
+ AUDIO_OUTPUT_FLAG_NONE);
+ if (status != OK) {
+ ALOGE("JetPlayer::init(): Error initializing JET library; AudioTrack error %d", status);
+ mAudioTrack.clear();
+ mState = EAS_STATE_ERROR;
+ return EAS_FAILURE;
+ }
+
+ // create render and playback thread
+ {
+ Mutex::Autolock l(mMutex);
+ ALOGV("JetPlayer::init(): trying to start render thread");
+ mThread = new JetPlayerThread(this);
+ mThread->run("jetRenderThread", ANDROID_PRIORITY_AUDIO);
+ mCondition.wait(mMutex);
+ }
+ if (mTid > 0) {
+ // render thread started, we're ready
+ ALOGV("JetPlayer::init(): render thread(%d) successfully started.", mTid);
+ mState = EAS_STATE_READY;
+ } else {
+ ALOGE("JetPlayer::init(): failed to start render thread.");
+ mState = EAS_STATE_ERROR;
+ return EAS_FAILURE;
+ }
+
+ return EAS_SUCCESS;
+}
+
+void JetPlayer::setEventCallback(jetevent_callback eventCallback)
+{
+ Mutex::Autolock l(mMutex);
+ mEventCallback = eventCallback;
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::release()
+{
+ ALOGV("JetPlayer::release()");
+ Mutex::Autolock lock(mMutex);
+ mPaused = true;
+ mRender = false;
+ if (mEasData) {
+ JET_Pause(mEasData);
+ JET_CloseFile(mEasData);
+ JET_Shutdown(mEasData);
+ EAS_Shutdown(mEasData);
+ }
+ delete mIoWrapper;
+ mIoWrapper = NULL;
+ if (mAudioTrack != 0) {
+ mAudioTrack->stop();
+ mAudioTrack->flush();
+ mAudioTrack.clear();
+ }
+ if (mAudioBuffer) {
+ delete mAudioBuffer;
+ mAudioBuffer = NULL;
+ }
+ mEasData = NULL;
+
+ return EAS_SUCCESS;
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::render() {
+ EAS_RESULT result = EAS_FAILURE;
+ EAS_I32 count;
+ int temp;
+ bool audioStarted = false;
+
+ ALOGV("JetPlayer::render(): entering");
+
+ // allocate render buffer
+ mAudioBuffer =
+ new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * MIX_NUM_BUFFERS];
+
+ // signal main thread that we started
+ {
+ Mutex::Autolock l(mMutex);
+ mTid = gettid();
+ ALOGV("JetPlayer::render(): render thread(%d) signal", mTid);
+ mCondition.signal();
+ }
+
+ while (1) {
+
+ mMutex.lock(); // [[[[[[[[ LOCK ---------------------------------------
+
+ if (mEasData == NULL) {
+ mMutex.unlock();
+ ALOGV("JetPlayer::render(): NULL EAS data, exiting render.");
+ goto threadExit;
+ }
+
+ // nothing to render, wait for client thread to wake us up
+ while (!mRender)
+ {
+ ALOGV("JetPlayer::render(): signal wait");
+ if (audioStarted) {
+ mAudioTrack->pause();
+ // we have to restart the playback once we start rendering again
+ audioStarted = false;
+ }
+ mCondition.wait(mMutex);
+ ALOGV("JetPlayer::render(): signal rx'd");
+ }
+
+ // render midi data into the input buffer
+ int num_output = 0;
+ EAS_PCM* p = mAudioBuffer;
+ for (int i = 0; i < MIX_NUM_BUFFERS; i++) {
+ result = EAS_Render(mEasData, p, pLibConfig->mixBufferSize, &count);
+ if (result != EAS_SUCCESS) {
+ ALOGE("JetPlayer::render(): EAS_Render returned error %ld", result);
+ }
+ p += count * pLibConfig->numChannels;
+ num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM);
+
+ // send events that were generated (if any) to the event callback
+ fireEventsFromJetQueue();
+ }
+
+ // update playback state
+ //ALOGV("JetPlayer::render(): updating state");
+ JET_Status(mEasData, &mJetStatus);
+ fireUpdateOnStatusChange();
+ mPaused = mJetStatus.paused;
+
+ mMutex.unlock(); // UNLOCK ]]]]]]]] -----------------------------------
+
+ // check audio output track
+ if (mAudioTrack == NULL) {
+ ALOGE("JetPlayer::render(): output AudioTrack was not created");
+ goto threadExit;
+ }
+
+ // Write data to the audio hardware
+ //ALOGV("JetPlayer::render(): writing to audio output");
+ if ((temp = mAudioTrack->write(mAudioBuffer, num_output)) < 0) {
+ ALOGE("JetPlayer::render(): Error in writing:%d",temp);
+ return temp;
+ }
+
+ // start audio output if necessary
+ if (!audioStarted) {
+ ALOGV("JetPlayer::render(): starting audio playback");
+ mAudioTrack->start();
+ audioStarted = true;
+ }
+
+ }//while (1)
+
+threadExit:
+ if (mAudioTrack != NULL) {
+ mAudioTrack->stop();
+ mAudioTrack->flush();
+ }
+ delete [] mAudioBuffer;
+ mAudioBuffer = NULL;
+ mMutex.lock();
+ mTid = -1;
+ mCondition.signal();
+ mMutex.unlock();
+ return result;
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// fire up an update if any of the status fields has changed
+// precondition: mMutex locked
+void JetPlayer::fireUpdateOnStatusChange()
+{
+ if ( (mJetStatus.currentUserID != mPreviousJetStatus.currentUserID)
+ ||(mJetStatus.segmentRepeatCount != mPreviousJetStatus.segmentRepeatCount) ) {
+ if (mEventCallback) {
+ mEventCallback(
+ JetPlayer::JET_USERID_UPDATE,
+ mJetStatus.currentUserID,
+ mJetStatus.segmentRepeatCount,
+ mJavaJetPlayerRef);
+ }
+ mPreviousJetStatus.currentUserID = mJetStatus.currentUserID;
+ mPreviousJetStatus.segmentRepeatCount = mJetStatus.segmentRepeatCount;
+ }
+
+ if (mJetStatus.numQueuedSegments != mPreviousJetStatus.numQueuedSegments) {
+ if (mEventCallback) {
+ mEventCallback(
+ JetPlayer::JET_NUMQUEUEDSEGMENT_UPDATE,
+ mJetStatus.numQueuedSegments,
+ -1,
+ mJavaJetPlayerRef);
+ }
+ mPreviousJetStatus.numQueuedSegments = mJetStatus.numQueuedSegments;
+ }
+
+ if (mJetStatus.paused != mPreviousJetStatus.paused) {
+ if (mEventCallback) {
+ mEventCallback(JetPlayer::JET_PAUSE_UPDATE,
+ mJetStatus.paused,
+ -1,
+ mJavaJetPlayerRef);
+ }
+ mPreviousJetStatus.paused = mJetStatus.paused;
+ }
+
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// fire up all the JET events in the JET engine queue (until the queue is empty)
+// precondition: mMutex locked
+void JetPlayer::fireEventsFromJetQueue()
+{
+ if (!mEventCallback) {
+ // no callback, just empty the event queue
+ while (JET_GetEvent(mEasData, NULL, NULL)) { }
+ return;
+ }
+
+ EAS_U32 rawEvent;
+ while (JET_GetEvent(mEasData, &rawEvent, NULL)) {
+ mEventCallback(
+ JetPlayer::JET_EVENT,
+ rawEvent,
+ -1,
+ mJavaJetPlayerRef);
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::loadFromFile(const char* path)
+{
+ ALOGV("JetPlayer::loadFromFile(): path=%s", path);
+
+ Mutex::Autolock lock(mMutex);
+
+ delete mIoWrapper;
+ mIoWrapper = new MidiIoWrapper(path);
+
+ EAS_RESULT result = JET_OpenFile(mEasData, mIoWrapper->getLocator());
+ if (result != EAS_SUCCESS)
+ mState = EAS_STATE_ERROR;
+ else
+ mState = EAS_STATE_OPEN;
+ return( result );
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::loadFromFD(const int fd, const long long offset, const long long length)
+{
+ ALOGV("JetPlayer::loadFromFD(): fd=%d offset=%lld length=%lld", fd, offset, length);
+
+ Mutex::Autolock lock(mMutex);
+
+ delete mIoWrapper;
+ mIoWrapper = new MidiIoWrapper(fd, offset, length);
+
+ EAS_RESULT result = JET_OpenFile(mEasData, mIoWrapper->getLocator());
+ if (result != EAS_SUCCESS)
+ mState = EAS_STATE_ERROR;
+ else
+ mState = EAS_STATE_OPEN;
+ return( result );
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::closeFile()
+{
+ Mutex::Autolock lock(mMutex);
+ return JET_CloseFile(mEasData);
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::play()
+{
+ ALOGV("JetPlayer::play(): entering");
+ Mutex::Autolock lock(mMutex);
+
+ EAS_RESULT result = JET_Play(mEasData);
+
+ mPaused = false;
+ mRender = true;
+
+ JET_Status(mEasData, &mJetStatus);
+ this->dumpJetStatus(&mJetStatus);
+
+ fireUpdateOnStatusChange();
+
+ // wake up render thread
+ ALOGV("JetPlayer::play(): wakeup render thread");
+ mCondition.signal();
+
+ return result;
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::pause()
+{
+ Mutex::Autolock lock(mMutex);
+ mPaused = true;
+ EAS_RESULT result = JET_Pause(mEasData);
+
+ mRender = false;
+
+ JET_Status(mEasData, &mJetStatus);
+ this->dumpJetStatus(&mJetStatus);
+ fireUpdateOnStatusChange();
+
+
+ return result;
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::queueSegment(int segmentNum, int libNum, int repeatCount, int transpose,
+ EAS_U32 muteFlags, EAS_U8 userID)
+{
+ ALOGV("JetPlayer::queueSegment segmentNum=%d, libNum=%d, repeatCount=%d, transpose=%d",
+ segmentNum, libNum, repeatCount, transpose);
+ Mutex::Autolock lock(mMutex);
+ return JET_QueueSegment(mEasData, segmentNum, libNum, repeatCount, transpose, muteFlags,
+ userID);
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::setMuteFlags(EAS_U32 muteFlags, bool sync)
+{
+ Mutex::Autolock lock(mMutex);
+ return JET_SetMuteFlags(mEasData, muteFlags, sync);
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::setMuteFlag(int trackNum, bool muteFlag, bool sync)
+{
+ Mutex::Autolock lock(mMutex);
+ return JET_SetMuteFlag(mEasData, trackNum, muteFlag, sync);
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::triggerClip(int clipId)
+{
+ ALOGV("JetPlayer::triggerClip clipId=%d", clipId);
+ Mutex::Autolock lock(mMutex);
+ return JET_TriggerClip(mEasData, clipId);
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::clearQueue()
+{
+ ALOGV("JetPlayer::clearQueue");
+ Mutex::Autolock lock(mMutex);
+ return JET_Clear_Queue(mEasData);
+}
+
+//-------------------------------------------------------------------------------------------------
+void JetPlayer::dump()
+{
+}
+
+void JetPlayer::dumpJetStatus(S_JET_STATUS* pJetStatus)
+{
+ if (pJetStatus!=NULL)
+ ALOGV(">> current JET player status: userID=%d segmentRepeatCount=%d numQueuedSegments=%d "
+ "paused=%d",
+ pJetStatus->currentUserID, pJetStatus->segmentRepeatCount,
+ pJetStatus->numQueuedSegments, pJetStatus->paused);
+ else
+ ALOGE(">> JET player status is NULL");
+}
+
+
+} // end namespace android