503 lines
15 KiB
C++
503 lines
15 KiB
C++
|
/*
|
||
|
* Copyright (C) 2008 The Android Open Source Project
|
||
|
* Copyright (c) 2009-2011, The Linux Foundation. All rights reserved.
|
||
|
*
|
||
|
* 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,
|
||
|
0,
|
||
|
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
|
||
|
|