/* * 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 <utils/threads.h> #include <media/JetPlayer.h> #ifdef HAVE_GETTID static pid_t myTid() { return gettid(); } #else static pid_t myTid() { return getpid(); } #endif namespace android { static const int MIX_NUM_BUFFERS = 4; static const S_EAS_LIB_CONFIG* pLibConfig = NULL; //------------------------------------------------------------------------------------------------- JetPlayer::JetPlayer(jobject javaJetPlayer, int maxTracks, int trackBufferSize) : mEventCallback(NULL), mJavaJetPlayerRef(javaJetPlayer), mTid(-1), mRender(false), mPaused(false), mMaxTracks(maxTracks), mEasData(NULL), mEasJetFileLoc(NULL), mAudioTrack(NULL), mTrackBufferSize(trackBufferSize) { LOGV("JetPlayer constructor"); mPreviousJetStatus.currentUserID = -1; mPreviousJetStatus.segmentRepeatCount = -1; mPreviousJetStatus.numQueuedSegments = -1; mPreviousJetStatus.paused = true; } //------------------------------------------------------------------------------------------------- JetPlayer::~JetPlayer() { LOGV("~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) { LOGE("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) { LOGE("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) { LOGE("JetPlayer::init(): Error initializing JET library, aborting."); mState = EAS_STATE_ERROR; return result; } // create the output AudioTrack mAudioTrack = new AudioTrack(); mAudioTrack->set(AudioSystem::MUSIC, //TODO parametrize this pLibConfig->sampleRate, 1, // format = PCM 16bits per sample, (pLibConfig->numChannels == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO, mTrackBufferSize, 0); // create render and playback thread { Mutex::Autolock l(mMutex); LOGV("JetPlayer::init(): trying to start render thread"); createThreadEtc(renderThread, this, "jetRenderThread", ANDROID_PRIORITY_AUDIO); mCondition.wait(mMutex); } if (mTid > 0) { // render thread started, we're ready LOGV("JetPlayer::init(): render thread(%d) successfully started.", mTid); mState = EAS_STATE_READY; } else { LOGE("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() { LOGV("JetPlayer::release()"); Mutex::Autolock lock(mMutex); mPaused = true; mRender = false; if (mEasData) { JET_Pause(mEasData); JET_CloseFile(mEasData); JET_Shutdown(mEasData); EAS_Shutdown(mEasData); } if (mEasJetFileLoc) { free(mEasJetFileLoc); mEasJetFileLoc = NULL; } if (mAudioTrack) { mAudioTrack->stop(); mAudioTrack->flush(); delete mAudioTrack; mAudioTrack = NULL; } if (mAudioBuffer) { delete mAudioBuffer; mAudioBuffer = NULL; } mEasData = NULL; return EAS_SUCCESS; } //------------------------------------------------------------------------------------------------- int JetPlayer::renderThread(void* p) { return ((JetPlayer*)p)->render(); } //------------------------------------------------------------------------------------------------- int JetPlayer::render() { EAS_RESULT result = EAS_FAILURE; EAS_I32 count; int temp; bool audioStarted = false; LOGV("JetPlayer::render(): entering"); // allocate render buffer mAudioBuffer = new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * MIX_NUM_BUFFERS]; if (!mAudioBuffer) { LOGE("JetPlayer::render(): mAudioBuffer allocate failed"); goto threadExit; } // signal main thread that we started { Mutex::Autolock l(mMutex); mTid = myTid(); LOGV("JetPlayer::render(): render thread(%d) signal", mTid); mCondition.signal(); } while (1) { mMutex.lock(); // [[[[[[[[ LOCK --------------------------------------- if (mEasData == NULL) { mMutex.unlock(); LOGV("JetPlayer::render(): NULL EAS data, exiting render."); goto threadExit; } // nothing to render, wait for client thread to wake us up while (!mRender) { LOGV("JetPlayer::render(): signal wait"); if (audioStarted) { mAudioTrack->pause(); // we have to restart the playback once we start rendering again audioStarted = false; } mCondition.wait(mMutex); LOGV("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) { LOGE("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 //LOGV("JetPlayer::render(): updating state"); JET_Status(mEasData, &mJetStatus); fireUpdateOnStatusChange(); mPaused = mJetStatus.paused; mMutex.unlock(); // UNLOCK ]]]]]]]] ----------------------------------- // check audio output track if (mAudioTrack == NULL) { LOGE("JetPlayer::render(): output AudioTrack was not created"); goto threadExit; } // Write data to the audio hardware //LOGV("JetPlayer::render(): writing to audio output"); if ((temp = mAudioTrack->write(mAudioBuffer, num_output)) < 0) { LOGE("JetPlayer::render(): Error in writing:%d",temp); return temp; } // start audio output if necessary if (!audioStarted) { LOGV("JetPlayer::render(): starting audio playback"); mAudioTrack->start(); audioStarted = true; } }//while (1) threadExit: if (mAudioTrack) { mAudioTrack->stop(); mAudioTrack->flush(); } if (mAudioBuffer) { 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) { LOGV("JetPlayer::loadFromFile(): path=%s", path); Mutex::Autolock lock(mMutex); mEasJetFileLoc = (EAS_FILE_LOCATOR) malloc(sizeof(EAS_FILE)); memset(mJetFilePath, 0, 256); strncpy(mJetFilePath, path, strlen(path)); mEasJetFileLoc->path = mJetFilePath; mEasJetFileLoc->fd = 0; mEasJetFileLoc->length = 0; mEasJetFileLoc->offset = 0; EAS_RESULT result = JET_OpenFile(mEasData, mEasJetFileLoc); 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) { LOGV("JetPlayer::loadFromFD(): fd=%d offset=%lld length=%lld", fd, offset, length); Mutex::Autolock lock(mMutex); mEasJetFileLoc = (EAS_FILE_LOCATOR) malloc(sizeof(EAS_FILE)); mEasJetFileLoc->fd = fd; mEasJetFileLoc->offset = offset; mEasJetFileLoc->length = length; mEasJetFileLoc->path = NULL; EAS_RESULT result = JET_OpenFile(mEasData, mEasJetFileLoc); 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() { LOGV("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 LOGV("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) { LOGV("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) { LOGV("JetPlayer::triggerClip clipId=%d", clipId); Mutex::Autolock lock(mMutex); return JET_TriggerClip(mEasData, clipId); } //------------------------------------------------------------------------------------------------- int JetPlayer::clearQueue() { LOGV("JetPlayer::clearQueue"); Mutex::Autolock lock(mMutex); return JET_Clear_Queue(mEasData); } //------------------------------------------------------------------------------------------------- void JetPlayer::dump() { LOGE("JetPlayer dump: JET file=%s", mEasJetFileLoc->path); } void JetPlayer::dumpJetStatus(S_JET_STATUS* pJetStatus) { if(pJetStatus!=NULL) LOGV(">> current JET player status: userID=%d segmentRepeatCount=%d numQueuedSegments=%d paused=%d", pJetStatus->currentUserID, pJetStatus->segmentRepeatCount, pJetStatus->numQueuedSegments, pJetStatus->paused); else LOGE(">> JET player status is NULL"); } } // end namespace android