Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 23986da9 authored by Marco Nelissen's avatar Marco Nelissen Committed by android-build-merger
Browse files

Merge "Move JetPlayer to JNI"

am: 77185b7f

Change-Id: I9b30d0356bf1bdb0033f209b5d71f1c6cefa1eaa
parents 625149dd 77185b7f
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -286,7 +286,6 @@ cc_library {
        "IMediaMetadataRetriever.cpp",
        "mediametadataretriever.cpp",
        "MidiDeviceInfo.cpp",
        "JetPlayer.cpp",
        "MediaScanner.cpp",
        "MediaScannerClient.cpp",
        "CharacterEncodingDetector.cpp",
@@ -346,7 +345,6 @@ cc_library {

    static_libs: [
        "libc_malloc_debug_backtrace",  // for memory heap analysis
        "libmedia_midiiowrapper",
    ],

    export_include_dirs: [

media/libmedia/JetPlayer.cpp

deleted100644 → 0
+0 −471
Original line number Diff line number Diff line
/*
 * 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 <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),
        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
+0 −126
Original line number Diff line number Diff line
/*
 * 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.
 */

#ifndef JETPLAYER_H_
#define JETPLAYER_H_

#include <utils/threads.h>

#include <libsonivox/jet.h>
#include <libsonivox/eas_types.h>
#include <media/AudioTrack.h>
#include <media/MidiIoWrapper.h>


namespace android {

typedef void (*jetevent_callback)(int eventType, int val1, int val2, void *cookie);

class JetPlayer {

public:

    // to keep in sync with the JetPlayer class constants
    // defined in frameworks/base/media/java/android/media/JetPlayer.java
    static const int JET_EVENT                   = 1;
    static const int JET_USERID_UPDATE           = 2;
    static const int JET_NUMQUEUEDSEGMENT_UPDATE = 3;
    static const int JET_PAUSE_UPDATE            = 4;

    JetPlayer(void *javaJetPlayer,
            int maxTracks = 32,
            int trackBufferSize = 1200);
    ~JetPlayer();
    int init();
    int release();

    int loadFromFile(const char* url);
    int loadFromFD(const int fd, const long long offset, const long long length);
    int closeFile();
    int play();
    int pause();
    int queueSegment(int segmentNum, int libNum, int repeatCount, int transpose,
            EAS_U32 muteFlags, EAS_U8 userID);
    int setMuteFlags(EAS_U32 muteFlags, bool sync);
    int setMuteFlag(int trackNum, bool muteFlag, bool sync);
    int triggerClip(int clipId);
    int clearQueue();

    void setEventCallback(jetevent_callback callback);

    int getMaxTracks() { return mMaxTracks; };


private:
    int                 render();
    void                fireUpdateOnStatusChange();
    void                fireEventsFromJetQueue();

    JetPlayer() {} // no default constructor
    void dump();
    void dumpJetStatus(S_JET_STATUS* pJetStatus);

    jetevent_callback   mEventCallback;

    void*               mJavaJetPlayerRef;
    Mutex               mMutex; // mutex to sync the render and playback thread with the JET calls
    pid_t               mTid;
    Condition           mCondition;
    volatile bool       mRender;
    bool                mPaused;

    EAS_STATE           mState;
    int*                mMemFailedVar;

    int                 mMaxTracks; // max number of MIDI tracks, usually 32
    EAS_DATA_HANDLE     mEasData;
    MidiIoWrapper*      mIoWrapper;
    EAS_PCM*            mAudioBuffer;// EAS renders the MIDI data into this buffer,
    sp<AudioTrack>      mAudioTrack; // and we play it in this audio track
    int                 mTrackBufferSize;
    S_JET_STATUS        mJetStatus;
    S_JET_STATUS        mPreviousJetStatus;

    class JetPlayerThread : public Thread {
    public:
        JetPlayerThread(JetPlayer *player) : mPlayer(player) {
        }

    protected:
        virtual ~JetPlayerThread() {}

    private:
        JetPlayer *mPlayer;

        bool threadLoop() {
            int result;
            result = mPlayer->render();
            return false;
        }

        JetPlayerThread(const JetPlayerThread &);
        JetPlayerThread &operator=(const JetPlayerThread &);
    };

    sp<JetPlayerThread> mThread;

}; // end class JetPlayer

} // end namespace android



#endif /*JETPLAYER_H_*/