From 89fa4ad53f2f4d57adbc97ae1149fc00c9b6f3c5 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 19:31:44 -0800 Subject: auto import from //depot/cupcake/@135843 --- media/libmediaplayerservice/MidiFile.cpp | 558 +++++++++++++++++++++++++++++++ 1 file changed, 558 insertions(+) create mode 100644 media/libmediaplayerservice/MidiFile.cpp (limited to 'media/libmediaplayerservice/MidiFile.cpp') diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp new file mode 100644 index 0000000..d03caa5 --- /dev/null +++ b/media/libmediaplayerservice/MidiFile.cpp @@ -0,0 +1,558 @@ +/* MidiFile.cpp +** +** Copyright 2007, 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 "MidiFile" +#include "utils/Log.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MidiFile.h" + +#ifdef HAVE_GETTID +static pid_t myTid() { return gettid(); } +#else +static pid_t myTid() { return getpid(); } +#endif + +// ---------------------------------------------------------------------------- + +namespace android { + +// ---------------------------------------------------------------------------- + +// The midi engine buffers are a bit small (128 frames), so we batch them up +static const int NUM_BUFFERS = 4; + +// TODO: Determine appropriate return codes +static status_t ERROR_NOT_OPEN = -1; +static status_t ERROR_OPEN_FAILED = -2; +static status_t ERROR_EAS_FAILURE = -3; +static status_t ERROR_ALLOCATE_FAILED = -4; + +static const S_EAS_LIB_CONFIG* pLibConfig = NULL; + +MidiFile::MidiFile() : + mEasData(NULL), mEasHandle(NULL), mAudioBuffer(NULL), + mPlayTime(-1), mDuration(-1), mState(EAS_STATE_ERROR), + mStreamType(AudioSystem::MUSIC), mLoop(false), mExit(false), + mPaused(false), mRender(false), mTid(-1) +{ + LOGV("constructor"); + + mFileLocator.path = NULL; + mFileLocator.fd = -1; + mFileLocator.offset = 0; + mFileLocator.length = 0; + + // get the library configuration and do sanity check + if (pLibConfig == NULL) + pLibConfig = EAS_Config(); + if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) { + LOGE("EAS library/header mismatch"); + goto Failed; + } + + // initialize EAS library + if (EAS_Init(&mEasData) != EAS_SUCCESS) { + LOGE("EAS_Init failed"); + goto Failed; + } + + // select reverb preset and enable + EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET, EAS_PARAM_REVERB_CHAMBER); + EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS, EAS_FALSE); + + // create playback thread + { + Mutex::Autolock l(mMutex); + createThreadEtc(renderThread, this, "midithread"); + mCondition.wait(mMutex); + LOGV("thread started"); + } + + // indicate success + if (mTid > 0) { + LOGV(" render thread(%d) started", mTid); + mState = EAS_STATE_READY; + } + +Failed: + return; +} + +status_t MidiFile::initCheck() +{ + if (mState == EAS_STATE_ERROR) return ERROR_EAS_FAILURE; + return NO_ERROR; +} + +MidiFile::~MidiFile() { + LOGV("MidiFile destructor"); + release(); +} + +status_t MidiFile::setDataSource(const char* path) +{ + LOGV("MidiFile::setDataSource url=%s", path); + Mutex::Autolock lock(mMutex); + + // file still open? + if (mEasHandle) { + reset_nosync(); + } + + // open file and set paused state + mFileLocator.path = strdup(path); + mFileLocator.fd = -1; + mFileLocator.offset = 0; + mFileLocator.length = 0; + EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle); + if (result == EAS_SUCCESS) { + updateState(); + } + + if (result != EAS_SUCCESS) { + LOGE("EAS_OpenFile failed: [%d]", (int)result); + mState = EAS_STATE_ERROR; + return ERROR_OPEN_FAILED; + } + + mState = EAS_STATE_OPEN; + mPlayTime = 0; + return NO_ERROR; +} + +status_t MidiFile::setDataSource(int fd, int64_t offset, int64_t length) +{ + LOGV("MidiFile::setDataSource fd=%d", fd); + Mutex::Autolock lock(mMutex); + + // file still open? + if (mEasHandle) { + reset_nosync(); + } + + // open file and set paused state + mFileLocator.fd = dup(fd); + mFileLocator.offset = offset; + mFileLocator.length = length; + EAS_RESULT result = EAS_OpenFile(mEasData, &mFileLocator, &mEasHandle); + updateState(); + + if (result != EAS_SUCCESS) { + LOGE("EAS_OpenFile failed: [%d]", (int)result); + mState = EAS_STATE_ERROR; + return ERROR_OPEN_FAILED; + } + + mState = EAS_STATE_OPEN; + mPlayTime = 0; + return NO_ERROR; +} + +status_t MidiFile::prepare() +{ + LOGV("MidiFile::prepare"); + Mutex::Autolock lock(mMutex); + if (!mEasHandle) { + return ERROR_NOT_OPEN; + } + EAS_RESULT result; + if ((result = EAS_Prepare(mEasData, mEasHandle)) != EAS_SUCCESS) { + LOGE("EAS_Prepare failed: [%ld]", result); + return ERROR_EAS_FAILURE; + } + updateState(); + return NO_ERROR; +} + +status_t MidiFile::prepareAsync() +{ + LOGV("MidiFile::prepareAsync"); + status_t ret = prepare(); + + // don't hold lock during callback + if (ret == NO_ERROR) { + sendEvent(MEDIA_PREPARED); + } else { + sendEvent(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ret); + } + return ret; +} + +status_t MidiFile::start() +{ + LOGV("MidiFile::start"); + Mutex::Autolock lock(mMutex); + if (!mEasHandle) { + return ERROR_NOT_OPEN; + } + + // resuming after pause? + if (mPaused) { + if (EAS_Resume(mEasData, mEasHandle) != EAS_SUCCESS) { + return ERROR_EAS_FAILURE; + } + mPaused = false; + updateState(); + } + + mRender = true; + + // wake up render thread + LOGV(" wakeup render thread"); + mCondition.signal(); + return NO_ERROR; +} + +status_t MidiFile::stop() +{ + LOGV("MidiFile::stop"); + Mutex::Autolock lock(mMutex); + if (!mEasHandle) { + return ERROR_NOT_OPEN; + } + if (!mPaused && (mState != EAS_STATE_STOPPED)) { + EAS_RESULT result = EAS_Pause(mEasData, mEasHandle); + if (result != EAS_SUCCESS) { + LOGE("EAS_Pause returned error %ld", result); + return ERROR_EAS_FAILURE; + } + } + mPaused = false; + return NO_ERROR; +} + +status_t MidiFile::seekTo(int position) +{ + LOGV("MidiFile::seekTo %d", position); + // hold lock during EAS calls + { + Mutex::Autolock lock(mMutex); + if (!mEasHandle) { + return ERROR_NOT_OPEN; + } + EAS_RESULT result; + if ((result = EAS_Locate(mEasData, mEasHandle, position, false)) + != EAS_SUCCESS) + { + LOGE("EAS_Locate returned %ld", result); + return ERROR_EAS_FAILURE; + } + EAS_GetLocation(mEasData, mEasHandle, &mPlayTime); + } + sendEvent(MEDIA_SEEK_COMPLETE); + return NO_ERROR; +} + +status_t MidiFile::pause() +{ + LOGV("MidiFile::pause"); + Mutex::Autolock lock(mMutex); + if (!mEasHandle) { + return ERROR_NOT_OPEN; + } + if ((mState == EAS_STATE_PAUSING) || (mState == EAS_STATE_PAUSED)) return NO_ERROR; + if (EAS_Pause(mEasData, mEasHandle) != EAS_SUCCESS) { + return ERROR_EAS_FAILURE; + } + mPaused = true; + return NO_ERROR; +} + +bool MidiFile::isPlaying() +{ + LOGV("MidiFile::isPlaying, mState=%d", int(mState)); + if (!mEasHandle || mPaused) return false; + return (mState == EAS_STATE_PLAY); +} + +status_t MidiFile::getCurrentPosition(int* position) +{ + LOGV("MidiFile::getCurrentPosition"); + if (!mEasHandle) { + LOGE("getCurrentPosition(): file not open"); + return ERROR_NOT_OPEN; + } + if (mPlayTime < 0) { + LOGE("getCurrentPosition(): mPlayTime = %ld", mPlayTime); + return ERROR_EAS_FAILURE; + } + *position = mPlayTime; + return NO_ERROR; +} + +status_t MidiFile::getDuration(int* duration) +{ + + LOGV("MidiFile::getDuration"); + { + Mutex::Autolock lock(mMutex); + if (!mEasHandle) return ERROR_NOT_OPEN; + *duration = mDuration; + } + + // if no duration cached, get the duration + // don't need a lock here because we spin up a new engine + if (*duration < 0) { + EAS_I32 temp; + EAS_DATA_HANDLE easData = NULL; + EAS_HANDLE easHandle = NULL; + EAS_RESULT result = EAS_Init(&easData); + if (result == EAS_SUCCESS) { + result = EAS_OpenFile(easData, &mFileLocator, &easHandle); + } + if (result == EAS_SUCCESS) { + result = EAS_Prepare(easData, easHandle); + } + if (result == EAS_SUCCESS) { + result = EAS_ParseMetaData(easData, easHandle, &temp); + } + if (easHandle) { + EAS_CloseFile(easData, easHandle); + } + if (easData) { + EAS_Shutdown(easData); + } + + if (result != EAS_SUCCESS) { + return ERROR_EAS_FAILURE; + } + + // cache successful result + mDuration = *duration = int(temp); + } + + return NO_ERROR; +} + +status_t MidiFile::release() +{ + LOGV("MidiFile::release"); + Mutex::Autolock l(mMutex); + reset_nosync(); + + // wait for render thread to exit + mExit = true; + mCondition.signal(); + + // wait for thread to exit + if (mAudioBuffer) { + mCondition.wait(mMutex); + } + + // release resources + if (mEasData) { + EAS_Shutdown(mEasData); + mEasData = NULL; + } + return NO_ERROR; +} + +status_t MidiFile::reset() +{ + LOGV("MidiFile::reset"); + Mutex::Autolock lock(mMutex); + return reset_nosync(); +} + +// call only with mutex held +status_t MidiFile::reset_nosync() +{ + LOGV("MidiFile::reset_nosync"); + // close file + if (mEasHandle) { + EAS_CloseFile(mEasData, mEasHandle); + mEasHandle = NULL; + } + if (mFileLocator.path) { + free((void*)mFileLocator.path); + mFileLocator.path = NULL; + } + if (mFileLocator.fd >= 0) { + close(mFileLocator.fd); + } + mFileLocator.fd = -1; + mFileLocator.offset = 0; + mFileLocator.length = 0; + + mPlayTime = -1; + mDuration = -1; + mLoop = false; + mPaused = false; + mRender = false; + return NO_ERROR; +} + +status_t MidiFile::setLooping(int loop) +{ + LOGV("MidiFile::setLooping"); + Mutex::Autolock lock(mMutex); + if (!mEasHandle) { + return ERROR_NOT_OPEN; + } + loop = loop ? -1 : 0; + if (EAS_SetRepeat(mEasData, mEasHandle, loop) != EAS_SUCCESS) { + return ERROR_EAS_FAILURE; + } + return NO_ERROR; +} + +status_t MidiFile::createOutputTrack() { + if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels, AudioSystem::PCM_16_BIT, 2) != NO_ERROR) { + LOGE("mAudioSink open failed"); + return ERROR_OPEN_FAILED; + } + return NO_ERROR; +} + +int MidiFile::renderThread(void* p) { + + return ((MidiFile*)p)->render(); +} + +int MidiFile::render() { + EAS_RESULT result = EAS_FAILURE; + EAS_I32 count; + int temp; + bool audioStarted = false; + + LOGV("MidiFile::render"); + + // allocate render buffer + mAudioBuffer = new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * NUM_BUFFERS]; + if (!mAudioBuffer) { + LOGE("mAudioBuffer allocate failed"); + goto threadExit; + } + + // signal main thread that we started + { + Mutex::Autolock l(mMutex); + mTid = myTid(); + LOGV("render thread(%d) signal", mTid); + mCondition.signal(); + } + + while (1) { + mMutex.lock(); + + // nothing to render, wait for client thread to wake us up + while (!mRender && !mExit) + { + LOGV("MidiFile::render - signal wait"); + mCondition.wait(mMutex); + LOGV("MidiFile::render - signal rx'd"); + } + if (mExit) { + mMutex.unlock(); + break; + } + + // render midi data into the input buffer + //LOGV("MidiFile::render - rendering audio"); + int num_output = 0; + EAS_PCM* p = mAudioBuffer; + for (int i = 0; i < NUM_BUFFERS; i++) { + result = EAS_Render(mEasData, p, pLibConfig->mixBufferSize, &count); + if (result != EAS_SUCCESS) { + LOGE("EAS_Render returned %ld", result); + } + p += count * pLibConfig->numChannels; + num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM); + } + + // update playback state and position + // LOGV("MidiFile::render - updating state"); + EAS_GetLocation(mEasData, mEasHandle, &mPlayTime); + EAS_State(mEasData, mEasHandle, &mState); + mMutex.unlock(); + + // create audio output track if necessary + if (!mAudioSink->ready()) { + LOGV("MidiFile::render - create output track"); + if (createOutputTrack() != NO_ERROR) + goto threadExit; + } + + // Write data to the audio hardware + // LOGV("MidiFile::render - writing to audio output"); + if ((temp = mAudioSink->write(mAudioBuffer, num_output)) < 0) { + LOGE("Error in writing:%d",temp); + return temp; + } + + // start audio output if necessary + if (!audioStarted) { + //LOGV("MidiFile::render - starting audio"); + mAudioSink->start(); + audioStarted = true; + } + + // still playing? + if ((mState == EAS_STATE_STOPPED) || (mState == EAS_STATE_ERROR) || + (mState == EAS_STATE_PAUSED)) + { + switch(mState) { + case EAS_STATE_STOPPED: + { + LOGV("MidiFile::render - stopped"); + sendEvent(MEDIA_PLAYBACK_COMPLETE); + break; + } + case EAS_STATE_ERROR: + { + LOGE("MidiFile::render - error"); + sendEvent(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN); + break; + } + case EAS_STATE_PAUSED: + LOGV("MidiFile::render - paused"); + break; + default: + break; + } + mAudioSink->stop(); + audioStarted = false; + mRender = false; + } + } + +threadExit: + mAudioSink.clear(); + if (mAudioBuffer) { + delete [] mAudioBuffer; + mAudioBuffer = NULL; + } + mMutex.lock(); + mTid = -1; + mCondition.signal(); + mMutex.unlock(); + return result; +} + +} // end namespace android -- cgit v1.1