Home | History | Annotate | Download | only in libmediaplayerservice
      1 /* MidiFile.cpp
      2 **
      3 ** Copyright 2007, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 //#define LOG_NDEBUG 0
     19 #define LOG_TAG "MidiFile"
     20 #include "utils/Log.h"
     21 
     22 #include <stdio.h>
     23 #include <assert.h>
     24 #include <limits.h>
     25 #include <unistd.h>
     26 #include <fcntl.h>
     27 #include <sched.h>
     28 #include <utils/threads.h>
     29 #include <libsonivox/eas_reverb.h>
     30 #include <sys/types.h>
     31 #include <sys/stat.h>
     32 #include <unistd.h>
     33 
     34 #include <system/audio.h>
     35 
     36 #include "MidiFile.h"
     37 
     38 // ----------------------------------------------------------------------------
     39 
     40 namespace android {
     41 
     42 // ----------------------------------------------------------------------------
     43 
     44 // The midi engine buffers are a bit small (128 frames), so we batch them up
     45 static const int NUM_BUFFERS = 4;
     46 
     47 // TODO: Determine appropriate return codes
     48 static status_t ERROR_NOT_OPEN = -1;
     49 static status_t ERROR_OPEN_FAILED = -2;
     50 static status_t ERROR_EAS_FAILURE = -3;
     51 static status_t ERROR_ALLOCATE_FAILED = -4;
     52 
     53 static const S_EAS_LIB_CONFIG* pLibConfig = NULL;
     54 
     55 MidiFile::MidiFile() :
     56     mEasData(NULL), mEasHandle(NULL), mAudioBuffer(NULL),
     57     mPlayTime(-1), mDuration(-1), mState(EAS_STATE_ERROR),
     58     mStreamType(AUDIO_STREAM_MUSIC), mLoop(false), mExit(false),
     59     mPaused(false), mRender(false), mTid(-1)
     60 {
     61     LOGV("constructor");
     62 
     63     mFileLocator.path = NULL;
     64     mFileLocator.fd = -1;
     65     mFileLocator.offset = 0;
     66     mFileLocator.length = 0;
     67 
     68     // get the library configuration and do sanity check
     69     if (pLibConfig == NULL)
     70         pLibConfig = EAS_Config();
     71     if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) {
     72         LOGE("EAS library/header mismatch");
     73         goto Failed;
     74     }
     75 
     76     // initialize EAS library
     77     if (EAS_Init(&mEasData) != EAS_SUCCESS) {
     78         LOGE("EAS_Init failed");
     79         goto Failed;
     80     }
     81 
     82     // select reverb preset and enable
     83     EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET, EAS_PARAM_REVERB_CHAMBER);
     84     EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS, EAS_FALSE);
     85 
     86     // create playback thread
     87     {
     88         Mutex::Autolock l(mMutex);
     89         createThreadEtc(renderThread, this, "midithread", ANDROID_PRIORITY_AUDIO);
     90         mCondition.wait(mMutex);
     91         LOGV("thread started");
     92     }
     93 
     94     // indicate success
     95     if (mTid > 0) {
     96         LOGV(" render thread(%d) started", mTid);
     97         mState = EAS_STATE_READY;
     98     }
     99 
    100 Failed:
    101     return;
    102 }
    103 
    104 status_t MidiFile::initCheck()
    105 {
    106     if (mState == EAS_STATE_ERROR) return ERROR_EAS_FAILURE;
    107     return NO_ERROR;
    108 }
    109 
    110 MidiFile::~MidiFile() {
    111     LOGV("MidiFile destructor");
    112     release();
    113 }
    114 
    115 status_t MidiFile::setDataSource(
    116         const char* path, const KeyedVector<String8, String8> *) {
    117     LOGV("MidiFile::setDataSource url=%s", path);
    118     Mutex::Autolock lock(mMutex);
    119 
    120     // file still open?
    121     if (mEasHandle) {
    122         reset_nosync();
    123     }
    124 
    125     // open file and set paused state
    126     mFileLocator.path = strdup(path);
    127     mFileLocator.fd = -1;
    128     mFileLocator.offset = 0;
    129     mFileLocator.length = 0;
    130     EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle);
    131     if (result == EAS_SUCCESS) {
    132         updateState();
    133     }
    134 
    135     if (result != EAS_SUCCESS) {
    136         LOGE("EAS_OpenFile failed: [%d]", (int)result);
    137         mState = EAS_STATE_ERROR;
    138         return ERROR_OPEN_FAILED;
    139     }
    140 
    141     mState = EAS_STATE_OPEN;
    142     mPlayTime = 0;
    143     return NO_ERROR;
    144 }
    145 
    146 status_t MidiFile::setDataSource(int fd, int64_t offset, int64_t length)
    147 {
    148     LOGV("MidiFile::setDataSource fd=%d", fd);
    149     Mutex::Autolock lock(mMutex);
    150 
    151     // file still open?
    152     if (mEasHandle) {
    153         reset_nosync();
    154     }
    155 
    156     // open file and set paused state
    157     mFileLocator.fd = dup(fd);
    158     mFileLocator.offset = offset;
    159     mFileLocator.length = length;
    160     EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle);
    161     updateState();
    162 
    163     if (result != EAS_SUCCESS) {
    164         LOGE("EAS_OpenFile failed: [%d]", (int)result);
    165         mState = EAS_STATE_ERROR;
    166         return ERROR_OPEN_FAILED;
    167     }
    168 
    169     mState = EAS_STATE_OPEN;
    170     mPlayTime = 0;
    171     return NO_ERROR;
    172 }
    173 
    174 status_t MidiFile::prepare()
    175 {
    176     LOGV("MidiFile::prepare");
    177     Mutex::Autolock lock(mMutex);
    178     if (!mEasHandle) {
    179         return ERROR_NOT_OPEN;
    180     }
    181     EAS_RESULT result;
    182     if ((result = EAS_Prepare(mEasData, mEasHandle)) != EAS_SUCCESS) {
    183         LOGE("EAS_Prepare failed: [%ld]", result);
    184         return ERROR_EAS_FAILURE;
    185     }
    186     updateState();
    187     return NO_ERROR;
    188 }
    189 
    190 status_t MidiFile::prepareAsync()
    191 {
    192     LOGV("MidiFile::prepareAsync");
    193     status_t ret = prepare();
    194 
    195     // don't hold lock during callback
    196     if (ret == NO_ERROR) {
    197         sendEvent(MEDIA_PREPARED);
    198     } else {
    199         sendEvent(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ret);
    200     }
    201     return ret;
    202 }
    203 
    204 status_t MidiFile::start()
    205 {
    206     LOGV("MidiFile::start");
    207     Mutex::Autolock lock(mMutex);
    208     if (!mEasHandle) {
    209         return ERROR_NOT_OPEN;
    210     }
    211 
    212     // resuming after pause?
    213     if (mPaused) {
    214         if (EAS_Resume(mEasData, mEasHandle) != EAS_SUCCESS) {
    215             return ERROR_EAS_FAILURE;
    216         }
    217         mPaused = false;
    218         updateState();
    219     }
    220 
    221     mRender = true;
    222 
    223     // wake up render thread
    224     LOGV("  wakeup render thread");
    225     mCondition.signal();
    226     return NO_ERROR;
    227 }
    228 
    229 status_t MidiFile::stop()
    230 {
    231     LOGV("MidiFile::stop");
    232     Mutex::Autolock lock(mMutex);
    233     if (!mEasHandle) {
    234         return ERROR_NOT_OPEN;
    235     }
    236     if (!mPaused && (mState != EAS_STATE_STOPPED)) {
    237         EAS_RESULT result = EAS_Pause(mEasData, mEasHandle);
    238         if (result != EAS_SUCCESS) {
    239             LOGE("EAS_Pause returned error %ld", result);
    240             return ERROR_EAS_FAILURE;
    241         }
    242     }
    243     mPaused = false;
    244     return NO_ERROR;
    245 }
    246 
    247 status_t MidiFile::seekTo(int position)
    248 {
    249     LOGV("MidiFile::seekTo %d", position);
    250     // hold lock during EAS calls
    251     {
    252         Mutex::Autolock lock(mMutex);
    253         if (!mEasHandle) {
    254             return ERROR_NOT_OPEN;
    255         }
    256         EAS_RESULT result;
    257         if ((result = EAS_Locate(mEasData, mEasHandle, position, false))
    258                 != EAS_SUCCESS)
    259         {
    260             LOGE("EAS_Locate returned %ld", result);
    261             return ERROR_EAS_FAILURE;
    262         }
    263         EAS_GetLocation(mEasData, mEasHandle, &mPlayTime);
    264     }
    265     sendEvent(MEDIA_SEEK_COMPLETE);
    266     return NO_ERROR;
    267 }
    268 
    269 status_t MidiFile::pause()
    270 {
    271     LOGV("MidiFile::pause");
    272     Mutex::Autolock lock(mMutex);
    273     if (!mEasHandle) {
    274         return ERROR_NOT_OPEN;
    275     }
    276     if ((mState == EAS_STATE_PAUSING) || (mState == EAS_STATE_PAUSED)) return NO_ERROR;
    277     if (EAS_Pause(mEasData, mEasHandle) != EAS_SUCCESS) {
    278         return ERROR_EAS_FAILURE;
    279     }
    280     mPaused = true;
    281     return NO_ERROR;
    282 }
    283 
    284 bool MidiFile::isPlaying()
    285 {
    286     LOGV("MidiFile::isPlaying, mState=%d", int(mState));
    287     if (!mEasHandle || mPaused) return false;
    288     return (mState == EAS_STATE_PLAY);
    289 }
    290 
    291 status_t MidiFile::getCurrentPosition(int* position)
    292 {
    293     LOGV("MidiFile::getCurrentPosition");
    294     if (!mEasHandle) {
    295         LOGE("getCurrentPosition(): file not open");
    296         return ERROR_NOT_OPEN;
    297     }
    298     if (mPlayTime < 0) {
    299         LOGE("getCurrentPosition(): mPlayTime = %ld", mPlayTime);
    300         return ERROR_EAS_FAILURE;
    301     }
    302     *position = mPlayTime;
    303     return NO_ERROR;
    304 }
    305 
    306 status_t MidiFile::getDuration(int* duration)
    307 {
    308 
    309     LOGV("MidiFile::getDuration");
    310     {
    311         Mutex::Autolock lock(mMutex);
    312         if (!mEasHandle) return ERROR_NOT_OPEN;
    313         *duration = mDuration;
    314     }
    315 
    316     // if no duration cached, get the duration
    317     // don't need a lock here because we spin up a new engine
    318     if (*duration < 0) {
    319         EAS_I32 temp;
    320         EAS_DATA_HANDLE easData = NULL;
    321         EAS_HANDLE easHandle = NULL;
    322         EAS_RESULT result = EAS_Init(&easData);
    323         if (result == EAS_SUCCESS) {
    324             result = EAS_OpenFile(easData, &mFileLocator, &easHandle);
    325         }
    326         if (result == EAS_SUCCESS) {
    327             result = EAS_Prepare(easData, easHandle);
    328         }
    329         if (result == EAS_SUCCESS) {
    330             result = EAS_ParseMetaData(easData, easHandle, &temp);
    331         }
    332         if (easHandle) {
    333             EAS_CloseFile(easData, easHandle);
    334         }
    335         if (easData) {
    336             EAS_Shutdown(easData);
    337         }
    338 
    339         if (result != EAS_SUCCESS) {
    340             return ERROR_EAS_FAILURE;
    341         }
    342 
    343         // cache successful result
    344         mDuration = *duration = int(temp);
    345     }
    346 
    347     return NO_ERROR;
    348 }
    349 
    350 status_t MidiFile::release()
    351 {
    352     LOGV("MidiFile::release");
    353     Mutex::Autolock l(mMutex);
    354     reset_nosync();
    355 
    356     // wait for render thread to exit
    357     mExit = true;
    358     mCondition.signal();
    359 
    360     // wait for thread to exit
    361     if (mAudioBuffer) {
    362         mCondition.wait(mMutex);
    363     }
    364 
    365     // release resources
    366     if (mEasData) {
    367         EAS_Shutdown(mEasData);
    368         mEasData = NULL;
    369     }
    370     return NO_ERROR;
    371 }
    372 
    373 status_t MidiFile::reset()
    374 {
    375     LOGV("MidiFile::reset");
    376     Mutex::Autolock lock(mMutex);
    377     return reset_nosync();
    378 }
    379 
    380 // call only with mutex held
    381 status_t MidiFile::reset_nosync()
    382 {
    383     LOGV("MidiFile::reset_nosync");
    384     // close file
    385     if (mEasHandle) {
    386         EAS_CloseFile(mEasData, mEasHandle);
    387         mEasHandle = NULL;
    388     }
    389     if (mFileLocator.path) {
    390         free((void*)mFileLocator.path);
    391         mFileLocator.path = NULL;
    392     }
    393     if (mFileLocator.fd >= 0) {
    394         close(mFileLocator.fd);
    395     }
    396     mFileLocator.fd = -1;
    397     mFileLocator.offset = 0;
    398     mFileLocator.length = 0;
    399 
    400     mPlayTime = -1;
    401     mDuration = -1;
    402     mLoop = false;
    403     mPaused = false;
    404     mRender = false;
    405     return NO_ERROR;
    406 }
    407 
    408 status_t MidiFile::setLooping(int loop)
    409 {
    410     LOGV("MidiFile::setLooping");
    411     Mutex::Autolock lock(mMutex);
    412     if (!mEasHandle) {
    413         return ERROR_NOT_OPEN;
    414     }
    415     loop = loop ? -1 : 0;
    416     if (EAS_SetRepeat(mEasData, mEasHandle, loop) != EAS_SUCCESS) {
    417         return ERROR_EAS_FAILURE;
    418     }
    419     return NO_ERROR;
    420 }
    421 
    422 status_t MidiFile::createOutputTrack() {
    423     if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels, AUDIO_FORMAT_PCM_16_BIT, 2) != NO_ERROR) {
    424         LOGE("mAudioSink open failed");
    425         return ERROR_OPEN_FAILED;
    426     }
    427     return NO_ERROR;
    428 }
    429 
    430 int MidiFile::renderThread(void* p) {
    431 
    432     return ((MidiFile*)p)->render();
    433 }
    434 
    435 int MidiFile::render() {
    436     EAS_RESULT result = EAS_FAILURE;
    437     EAS_I32 count;
    438     int temp;
    439     bool audioStarted = false;
    440 
    441     LOGV("MidiFile::render");
    442 
    443     // allocate render buffer
    444     mAudioBuffer = new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * NUM_BUFFERS];
    445     if (!mAudioBuffer) {
    446         LOGE("mAudioBuffer allocate failed");
    447         goto threadExit;
    448     }
    449 
    450     // signal main thread that we started
    451     {
    452         Mutex::Autolock l(mMutex);
    453         mTid = gettid();
    454         LOGV("render thread(%d) signal", mTid);
    455         mCondition.signal();
    456     }
    457 
    458     while (1) {
    459         mMutex.lock();
    460 
    461         // nothing to render, wait for client thread to wake us up
    462         while (!mRender && !mExit)
    463         {
    464             LOGV("MidiFile::render - signal wait");
    465             mCondition.wait(mMutex);
    466             LOGV("MidiFile::render - signal rx'd");
    467         }
    468         if (mExit) {
    469             mMutex.unlock();
    470             break;
    471         }
    472 
    473         // render midi data into the input buffer
    474         //LOGV("MidiFile::render - rendering audio");
    475         int num_output = 0;
    476         EAS_PCM* p = mAudioBuffer;
    477         for (int i = 0; i < NUM_BUFFERS; i++) {
    478             result = EAS_Render(mEasData, p, pLibConfig->mixBufferSize, &count);
    479             if (result != EAS_SUCCESS) {
    480                 LOGE("EAS_Render returned %ld", result);
    481             }
    482             p += count * pLibConfig->numChannels;
    483             num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM);
    484         }
    485 
    486         // update playback state and position
    487         // LOGV("MidiFile::render - updating state");
    488         EAS_GetLocation(mEasData, mEasHandle, &mPlayTime);
    489         EAS_State(mEasData, mEasHandle, &mState);
    490         mMutex.unlock();
    491 
    492         // create audio output track if necessary
    493         if (!mAudioSink->ready()) {
    494             LOGV("MidiFile::render - create output track");
    495             if (createOutputTrack() != NO_ERROR)
    496                 goto threadExit;
    497         }
    498 
    499         // Write data to the audio hardware
    500         // LOGV("MidiFile::render - writing to audio output");
    501         if ((temp = mAudioSink->write(mAudioBuffer, num_output)) < 0) {
    502             LOGE("Error in writing:%d",temp);
    503             return temp;
    504         }
    505 
    506         // start audio output if necessary
    507         if (!audioStarted) {
    508             //LOGV("MidiFile::render - starting audio");
    509             mAudioSink->start();
    510             audioStarted = true;
    511         }
    512 
    513         // still playing?
    514         if ((mState == EAS_STATE_STOPPED) || (mState == EAS_STATE_ERROR) ||
    515                 (mState == EAS_STATE_PAUSED))
    516         {
    517             switch(mState) {
    518             case EAS_STATE_STOPPED:
    519             {
    520                 LOGV("MidiFile::render - stopped");
    521                 sendEvent(MEDIA_PLAYBACK_COMPLETE);
    522                 break;
    523             }
    524             case EAS_STATE_ERROR:
    525             {
    526                 LOGE("MidiFile::render - error");
    527                 sendEvent(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN);
    528                 break;
    529             }
    530             case EAS_STATE_PAUSED:
    531                 LOGV("MidiFile::render - paused");
    532                 break;
    533             default:
    534                 break;
    535             }
    536             mAudioSink->stop();
    537             audioStarted = false;
    538             mRender = false;
    539         }
    540     }
    541 
    542 threadExit:
    543     mAudioSink.clear();
    544     if (mAudioBuffer) {
    545         delete [] mAudioBuffer;
    546         mAudioBuffer = NULL;
    547     }
    548     mMutex.lock();
    549     mTid = -1;
    550     mCondition.signal();
    551     mMutex.unlock();
    552     return result;
    553 }
    554 
    555 } // end namespace android
    556