/*
* 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>
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),
mEasJetFileLoc(NULL),
mAudioTrack(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();
mAudioTrack->set(AUDIO_STREAM_MUSIC, //TODO parameterize this
pLibConfig->sampleRate,
AUDIO_FORMAT_PCM_16_BIT,
audio_channel_out_mask_from_count(pLibConfig->numChannels),
mTrackBufferSize,
AUDIO_OUTPUT_FLAG_NONE);
// 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);
}
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::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);
mEasJetFileLoc = (EAS_FILE_LOCATOR) malloc(sizeof(EAS_FILE));
strncpy(mJetFilePath, path, sizeof(mJetFilePath));
mJetFilePath[sizeof(mJetFilePath) - 1] = '\0';
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)
{
ALOGV("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()
{
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()
{
ALOGE("JetPlayer dump: JET file=%s", mEasJetFileLoc->path);
}
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