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     ALOGV("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         ALOGE("EAS library/header mismatch");
     73         goto Failed;
     74     }
     75 
     76     // initialize EAS library
     77     if (EAS_Init(&mEasData) != EAS_SUCCESS) {
     78         ALOGE("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         mThread = new MidiFileThread(this);
     90         mThread->run("midithread", ANDROID_PRIORITY_AUDIO);
     91         mCondition.wait(mMutex);
     92         ALOGV("thread started");
     93     }
     94 
     95     // indicate success
     96     if (mTid > 0) {
     97         ALOGV(" render thread(%d) started", mTid);
     98         mState = EAS_STATE_READY;
     99     }
    100 
    101 Failed:
    102     return;
    103 }
    104 
    105 status_t MidiFile::initCheck()
    106 {
    107     if (mState == EAS_STATE_ERROR) return ERROR_EAS_FAILURE;
    108     return NO_ERROR;
    109 }
    110 
    111 MidiFile::~MidiFile() {
    112     ALOGV("MidiFile destructor");
    113     release();
    114 }
    115 
    116 status_t MidiFile::setDataSource(
    117         const char* path, const KeyedVector<String8, String8> *) {
    118     ALOGV("MidiFile::setDataSource url=%s", path);
    119     Mutex::Autolock lock(mMutex);
    120 
    121     // file still open?
    122     if (mEasHandle) {
    123         reset_nosync();
    124     }
    125 
    126     // open file and set paused state
    127     mFileLocator.path = strdup(path);
    128     mFileLocator.fd = -1;
    129     mFileLocator.offset = 0;
    130     mFileLocator.length = 0;
    131     EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle);
    132     if (result == EAS_SUCCESS) {
    133         updateState();
    134     }
    135 
    136     if (result != EAS_SUCCESS) {
    137         ALOGE("EAS_OpenFile failed: [%d]", (int)result);
    138         mState = EAS_STATE_ERROR;
    139         return ERROR_OPEN_FAILED;
    140     }
    141 
    142     mState = EAS_STATE_OPEN;
    143     mPlayTime = 0;
    144     return NO_ERROR;
    145 }
    146 
    147 status_t MidiFile::setDataSource(int fd, int64_t offset, int64_t length)
    148 {
    149     ALOGV("MidiFile::setDataSource fd=%d", fd);
    150     Mutex::Autolock lock(mMutex);
    151 
    152     // file still open?
    153     if (mEasHandle) {
    154         reset_nosync();
    155     }
    156 
    157     // open file and set paused state
    158     mFileLocator.fd = dup(fd);
    159     mFileLocator.offset = offset;
    160     mFileLocator.length = length;
    161     EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle);
    162     updateState();
    163 
    164     if (result != EAS_SUCCESS) {
    165         ALOGE("EAS_OpenFile failed: [%d]", (int)result);
    166         mState = EAS_STATE_ERROR;
    167         return ERROR_OPEN_FAILED;
    168     }
    169 
    170     mState = EAS_STATE_OPEN;
    171     mPlayTime = 0;
    172     return NO_ERROR;
    173 }
    174 
    175 status_t MidiFile::prepare()
    176 {
    177     ALOGV("MidiFile::prepare");
    178     Mutex::Autolock lock(mMutex);
    179     if (!mEasHandle) {
    180         return ERROR_NOT_OPEN;
    181     }
    182     EAS_RESULT result;
    183     if ((result = EAS_Prepare(mEasData, mEasHandle)) != EAS_SUCCESS) {
    184         ALOGE("EAS_Prepare failed: [%ld]", result);
    185         return ERROR_EAS_FAILURE;
    186     }
    187     updateState();
    188     return NO_ERROR;
    189 }
    190 
    191 status_t MidiFile::prepareAsync()
    192 {
    193     ALOGV("MidiFile::prepareAsync");
    194     status_t ret = prepare();
    195 
    196     // don't hold lock during callback
    197     if (ret == NO_ERROR) {
    198         sendEvent(MEDIA_PREPARED);
    199     } else {
    200         sendEvent(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ret);
    201     }
    202     return ret;
    203 }
    204 
    205 status_t MidiFile::start()
    206 {
    207     ALOGV("MidiFile::start");
    208     Mutex::Autolock lock(mMutex);
    209     if (!mEasHandle) {
    210         return ERROR_NOT_OPEN;
    211     }
    212 
    213     // resuming after pause?
    214     if (mPaused) {
    215         if (EAS_Resume(mEasData, mEasHandle) != EAS_SUCCESS) {
    216             return ERROR_EAS_FAILURE;
    217         }
    218         mPaused = false;
    219         updateState();
    220     }
    221 
    222     mRender = true;
    223 
    224     // wake up render thread
    225     ALOGV("  wakeup render thread");
    226     mCondition.signal();
    227     return NO_ERROR;
    228 }
    229 
    230 status_t MidiFile::stop()
    231 {
    232     ALOGV("MidiFile::stop");
    233     Mutex::Autolock lock(mMutex);
    234     if (!mEasHandle) {
    235         return ERROR_NOT_OPEN;
    236     }
    237     if (!mPaused && (mState != EAS_STATE_STOPPED)) {
    238         EAS_RESULT result = EAS_Pause(mEasData, mEasHandle);
    239         if (result != EAS_SUCCESS) {
    240             ALOGE("EAS_Pause returned error %ld", result);
    241             return ERROR_EAS_FAILURE;
    242         }
    243     }
    244     mPaused = false;
    245     return NO_ERROR;
    246 }
    247 
    248 status_t MidiFile::seekTo(int position)
    249 {
    250     ALOGV("MidiFile::seekTo %d", position);
    251     // hold lock during EAS calls
    252     {
    253         Mutex::Autolock lock(mMutex);
    254         if (!mEasHandle) {
    255             return ERROR_NOT_OPEN;
    256         }
    257         EAS_RESULT result;
    258         if ((result = EAS_Locate(mEasData, mEasHandle, position, false))
    259                 != EAS_SUCCESS)
    260         {
    261             ALOGE("EAS_Locate returned %ld", result);
    262             return ERROR_EAS_FAILURE;
    263         }
    264         EAS_GetLocation(mEasData, mEasHandle, &mPlayTime);
    265     }
    266     sendEvent(MEDIA_SEEK_COMPLETE);
    267     return NO_ERROR;
    268 }
    269 
    270 status_t MidiFile::pause()
    271 {
    272     ALOGV("MidiFile::pause");
    273     Mutex::Autolock lock(mMutex);
    274     if (!mEasHandle) {
    275         return ERROR_NOT_OPEN;
    276     }
    277     if ((mState == EAS_STATE_PAUSING) || (mState == EAS_STATE_PAUSED)) return NO_ERROR;
    278     if (EAS_Pause(mEasData, mEasHandle) != EAS_SUCCESS) {
    279         return ERROR_EAS_FAILURE;
    280     }
    281     mPaused = true;
    282     return NO_ERROR;
    283 }
    284 
    285 bool MidiFile::isPlaying()
    286 {
    287     ALOGV("MidiFile::isPlaying, mState=%d", int(mState));
    288     if (!mEasHandle || mPaused) return false;
    289     return (mState == EAS_STATE_PLAY);
    290 }
    291 
    292 status_t MidiFile::getCurrentPosition(int* position)
    293 {
    294     ALOGV("MidiFile::getCurrentPosition");
    295     if (!mEasHandle) {
    296         ALOGE("getCurrentPosition(): file not open");
    297         return ERROR_NOT_OPEN;
    298     }
    299     if (mPlayTime < 0) {
    300         ALOGE("getCurrentPosition(): mPlayTime = %ld", mPlayTime);
    301         return ERROR_EAS_FAILURE;
    302     }
    303     *position = mPlayTime;
    304     return NO_ERROR;
    305 }
    306 
    307 status_t MidiFile::getDuration(int* duration)
    308 {
    309 
    310     ALOGV("MidiFile::getDuration");
    311     {
    312         Mutex::Autolock lock(mMutex);
    313         if (!mEasHandle) return ERROR_NOT_OPEN;
    314         *duration = mDuration;
    315     }
    316 
    317     // if no duration cached, get the duration
    318     // don't need a lock here because we spin up a new engine
    319     if (*duration < 0) {
    320         EAS_I32 temp;
    321         EAS_DATA_HANDLE easData = NULL;
    322         EAS_HANDLE easHandle = NULL;
    323         EAS_RESULT result = EAS_Init(&easData);
    324         if (result == EAS_SUCCESS) {
    325             result = EAS_OpenFile(easData, &mFileLocator, &easHandle);
    326         }
    327         if (result == EAS_SUCCESS) {
    328             result = EAS_Prepare(easData, easHandle);
    329         }
    330         if (result == EAS_SUCCESS) {
    331             result = EAS_ParseMetaData(easData, easHandle, &temp);
    332         }
    333         if (easHandle) {
    334             EAS_CloseFile(easData, easHandle);
    335         }
    336         if (easData) {
    337             EAS_Shutdown(easData);
    338         }
    339 
    340         if (result != EAS_SUCCESS) {
    341             return ERROR_EAS_FAILURE;
    342         }
    343 
    344         // cache successful result
    345         mDuration = *duration = int(temp);
    346     }
    347 
    348     return NO_ERROR;
    349 }
    350 
    351 status_t MidiFile::release()
    352 {
    353     ALOGV("MidiFile::release");
    354     Mutex::Autolock l(mMutex);
    355     reset_nosync();
    356 
    357     // wait for render thread to exit
    358     mExit = true;
    359     mCondition.signal();
    360 
    361     // wait for thread to exit
    362     if (mAudioBuffer) {
    363         mCondition.wait(mMutex);
    364     }
    365 
    366     // release resources
    367     if (mEasData) {
    368         EAS_Shutdown(mEasData);
    369         mEasData = NULL;
    370     }
    371     return NO_ERROR;
    372 }
    373 
    374 status_t MidiFile::reset()
    375 {
    376     ALOGV("MidiFile::reset");
    377     Mutex::Autolock lock(mMutex);
    378     return reset_nosync();
    379 }
    380 
    381 // call only with mutex held
    382 status_t MidiFile::reset_nosync()
    383 {
    384     ALOGV("MidiFile::reset_nosync");
    385     // close file
    386     if (mEasHandle) {
    387         EAS_CloseFile(mEasData, mEasHandle);
    388         mEasHandle = NULL;
    389     }
    390     if (mFileLocator.path) {
    391         free((void*)mFileLocator.path);
    392         mFileLocator.path = NULL;
    393     }
    394     if (mFileLocator.fd >= 0) {
    395         close(mFileLocator.fd);
    396     }
    397     mFileLocator.fd = -1;
    398     mFileLocator.offset = 0;
    399     mFileLocator.length = 0;
    400 
    401     mPlayTime = -1;
    402     mDuration = -1;
    403     mLoop = false;
    404     mPaused = false;
    405     mRender = false;
    406     return NO_ERROR;
    407 }
    408 
    409 status_t MidiFile::setLooping(int loop)
    410 {
    411     ALOGV("MidiFile::setLooping");
    412     Mutex::Autolock lock(mMutex);
    413     if (!mEasHandle) {
    414         return ERROR_NOT_OPEN;
    415     }
    416     loop = loop ? -1 : 0;
    417     if (EAS_SetRepeat(mEasData, mEasHandle, loop) != EAS_SUCCESS) {
    418         return ERROR_EAS_FAILURE;
    419     }
    420     return NO_ERROR;
    421 }
    422 
    423 status_t MidiFile::createOutputTrack() {
    424     if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels,
    425             CHANNEL_MASK_USE_CHANNEL_ORDER, AUDIO_FORMAT_PCM_16_BIT, 2) != NO_ERROR) {
    426         ALOGE("mAudioSink open failed");
    427         return ERROR_OPEN_FAILED;
    428     }
    429     return NO_ERROR;
    430 }
    431 
    432 int MidiFile::render() {
    433     EAS_RESULT result = EAS_FAILURE;
    434     EAS_I32 count;
    435     int temp;
    436     bool audioStarted = false;
    437 
    438     ALOGV("MidiFile::render");
    439 
    440     // allocate render buffer
    441     mAudioBuffer = new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * NUM_BUFFERS];
    442     if (!mAudioBuffer) {
    443         ALOGE("mAudioBuffer allocate failed");
    444         goto threadExit;
    445     }
    446 
    447     // signal main thread that we started
    448     {
    449         Mutex::Autolock l(mMutex);
    450         mTid = gettid();
    451         ALOGV("render thread(%d) signal", mTid);
    452         mCondition.signal();
    453     }
    454 
    455     while (1) {
    456         mMutex.lock();
    457 
    458         // nothing to render, wait for client thread to wake us up
    459         while (!mRender && !mExit)
    460         {
    461             ALOGV("MidiFile::render - signal wait");
    462             mCondition.wait(mMutex);
    463             ALOGV("MidiFile::render - signal rx'd");
    464         }
    465         if (mExit) {
    466             mMutex.unlock();
    467             break;
    468         }
    469 
    470         // render midi data into the input buffer
    471         //ALOGV("MidiFile::render - rendering audio");
    472         int num_output = 0;
    473         EAS_PCM* p = mAudioBuffer;
    474         for (int i = 0; i < NUM_BUFFERS; i++) {
    475             result = EAS_Render(mEasData, p, pLibConfig->mixBufferSize, &count);
    476             if (result != EAS_SUCCESS) {
    477                 ALOGE("EAS_Render returned %ld", result);
    478             }
    479             p += count * pLibConfig->numChannels;
    480             num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM);
    481         }
    482 
    483         // update playback state and position
    484         // ALOGV("MidiFile::render - updating state");
    485         EAS_GetLocation(mEasData, mEasHandle, &mPlayTime);
    486         EAS_State(mEasData, mEasHandle, &mState);
    487         mMutex.unlock();
    488 
    489         // create audio output track if necessary
    490         if (!mAudioSink->ready()) {
    491             ALOGV("MidiFile::render - create output track");
    492             if (createOutputTrack() != NO_ERROR)
    493                 goto threadExit;
    494         }
    495 
    496         // Write data to the audio hardware
    497         // ALOGV("MidiFile::render - writing to audio output");
    498         if ((temp = mAudioSink->write(mAudioBuffer, num_output)) < 0) {
    499             ALOGE("Error in writing:%d",temp);
    500             return temp;
    501         }
    502 
    503         // start audio output if necessary
    504         if (!audioStarted) {
    505             //ALOGV("MidiFile::render - starting audio");
    506             mAudioSink->start();
    507             audioStarted = true;
    508         }
    509 
    510         // still playing?
    511         if ((mState == EAS_STATE_STOPPED) || (mState == EAS_STATE_ERROR) ||
    512                 (mState == EAS_STATE_PAUSED))
    513         {
    514             switch(mState) {
    515             case EAS_STATE_STOPPED:
    516             {
    517                 ALOGV("MidiFile::render - stopped");
    518                 sendEvent(MEDIA_PLAYBACK_COMPLETE);
    519                 break;
    520             }
    521             case EAS_STATE_ERROR:
    522             {
    523                 ALOGE("MidiFile::render - error");
    524                 sendEvent(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN);
    525                 break;
    526             }
    527             case EAS_STATE_PAUSED:
    528                 ALOGV("MidiFile::render - paused");
    529                 break;
    530             default:
    531                 break;
    532             }
    533             mAudioSink->stop();
    534             audioStarted = false;
    535             mRender = false;
    536         }
    537     }
    538 
    539 threadExit:
    540     mAudioSink.clear();
    541     if (mAudioBuffer) {
    542         delete [] mAudioBuffer;
    543         mAudioBuffer = NULL;
    544     }
    545     mMutex.lock();
    546     mTid = -1;
    547     mCondition.signal();
    548     mMutex.unlock();
    549     return result;
    550 }
    551 
    552 } // end namespace android
    553