Replace MidiFile player with a Midi extractor

This gets rids of a bunch of special midi handling and replaces it
with an extractor that works with NuPlayer and MediaMetadataRetriever.

Change-Id: I8d0f5bbdde2ca24267cf4d62ab26afe9630e0217
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index cf18a45..0f38c16 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -43,8 +43,6 @@
 template<typename T> class SortedVector;
 
 enum player_type {
-    PV_PLAYER = 1,
-    SONIVOX_PLAYER = 2,
     STAGEFRIGHT_PLAYER = 3,
     NU_PLAYER = 4,
     // Test players are available only in the 'test' and 'eng' builds.
diff --git a/include/media/MidiIoWrapper.h b/include/media/MidiIoWrapper.h
index caf1d75..e6f8cf7 100644
--- a/include/media/MidiIoWrapper.h
+++ b/include/media/MidiIoWrapper.h
@@ -19,12 +19,15 @@
 
 #include <libsonivox/eas_types.h>
 
+#include "media/stagefright/DataSource.h"
+
 namespace android {
 
 class MidiIoWrapper : public RefBase {
 public:
     MidiIoWrapper(const char *path);
     MidiIoWrapper(int fd, off64_t offset, int64_t size);
+    MidiIoWrapper(const sp<DataSource> &source);
 
     ~MidiIoWrapper();
 
@@ -37,6 +40,7 @@
     int mFd;
     off64_t mBase;
     int64_t  mLength;
+    sp<DataSource> mDataSource;
     EAS_FILE mEasFile;
 };
 
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index e67d4d5..1f9ff45 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -36,6 +36,7 @@
 extern const char *MEDIA_MIMETYPE_AUDIO_MPEG;           // layer III
 extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I;
 extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II;
+extern const char *MEDIA_MIMETYPE_AUDIO_MIDI;
 extern const char *MEDIA_MIMETYPE_AUDIO_AAC;
 extern const char *MEDIA_MIMETYPE_AUDIO_QCELP;
 extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS;
diff --git a/media/libmedia/MidiIoWrapper.cpp b/media/libmedia/MidiIoWrapper.cpp
index ef931d2..2181111 100644
--- a/media/libmedia/MidiIoWrapper.cpp
+++ b/media/libmedia/MidiIoWrapper.cpp
@@ -47,6 +47,16 @@
     mLength = size;
 }
 
+MidiIoWrapper::MidiIoWrapper(const sp<DataSource> &source) {
+    mDataSource = source;
+    off64_t l;
+    if (mDataSource->getSize(&l) == OK) {
+        mLength = l;
+    } else {
+        mLength = 0;
+    }
+}
+
 MidiIoWrapper::~MidiIoWrapper() {
     ALOGV("~MidiIoWrapper");
     close(mFd);
@@ -54,6 +64,10 @@
 
 int MidiIoWrapper::readAt(void *buffer, int offset, int size) {
     ALOGV("readAt(%p, %d, %d)", buffer, offset, size);
+
+    if (mDataSource != NULL) {
+        return mDataSource->readAt(offset, buffer, size);
+    }
     lseek(mFd, mBase + offset, SEEK_SET);
     if (offset + size > mLength) {
         size = mLength - offset;
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 2cf5710..9d8fe62 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -15,8 +15,6 @@
     MediaPlayerService.cpp      \
     MediaRecorderClient.cpp     \
     MetadataRetrieverClient.cpp \
-    MidiFile.cpp                \
-    MidiMetadataRetriever.cpp   \
     RemoteDisplay.cpp           \
     SharedLibrary.cpp           \
     StagefrightPlayer.cpp       \
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp
index d149290..89a9ddf 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.cpp
+++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp
@@ -21,7 +21,6 @@
 
 #include <cutils/properties.h>
 #include <media/IMediaPlayer.h>
-#include <media/MidiIoWrapper.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/FileSource.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -30,7 +29,6 @@
 
 #include "MediaPlayerFactory.h"
 
-#include "MidiFile.h"
 #include "TestPlayerStub.h"
 #include "StagefrightPlayer.h"
 #include "nuplayer/NuPlayerDriver.h"
@@ -254,70 +252,6 @@
     }
 };
 
-class SonivoxPlayerFactory : public MediaPlayerFactory::IFactory {
-  public:
-    virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
-                               const char* url,
-                               float curScore) {
-        static const float kOurScore = 0.4;
-        static const char* const FILE_EXTS[] = { ".mid",
-                                                 ".midi",
-                                                 ".smf",
-                                                 ".xmf",
-                                                 ".mxmf",
-                                                 ".imy",
-                                                 ".rtttl",
-                                                 ".rtx",
-                                                 ".ota" };
-        if (kOurScore <= curScore)
-            return 0.0;
-
-        // use MidiFile for MIDI extensions
-        int lenURL = strlen(url);
-        for (int i = 0; i < NELEM(FILE_EXTS); ++i) {
-            int len = strlen(FILE_EXTS[i]);
-            int start = lenURL - len;
-            if (start > 0) {
-                if (!strncasecmp(url + start, FILE_EXTS[i], len)) {
-                    return kOurScore;
-                }
-            }
-        }
-        return 0.0;
-    }
-
-    virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
-                               int fd,
-                               int64_t offset,
-                               int64_t length,
-                               float curScore) {
-        static const float kOurScore = 0.8;
-
-        if (kOurScore <= curScore)
-            return 0.0;
-
-        // Some kind of MIDI?
-        EAS_DATA_HANDLE easdata;
-        sp<MidiIoWrapper> wrapper = new MidiIoWrapper(fd, offset, length);
-        if (EAS_Init(&easdata) == EAS_SUCCESS) {
-            EAS_HANDLE  eashandle;
-            if (EAS_OpenFile(easdata, wrapper->getLocator(), &eashandle) == EAS_SUCCESS) {
-                EAS_CloseFile(easdata, eashandle);
-                EAS_Shutdown(easdata);
-                return kOurScore;
-            }
-            EAS_Shutdown(easdata);
-        }
-
-        return 0.0;
-    }
-
-    virtual sp<MediaPlayerBase> createPlayer() {
-        ALOGV(" create MidiFile");
-        return new MidiFile();
-    }
-};
-
 class TestPlayerFactory : public MediaPlayerFactory::IFactory {
   public:
     virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
@@ -344,7 +278,6 @@
 
     registerFactory_l(new StagefrightPlayerFactory(), STAGEFRIGHT_PLAYER);
     registerFactory_l(new NuPlayerFactory(), NU_PLAYER);
-    registerFactory_l(new SonivoxPlayerFactory(), SONIVOX_PLAYER);
     registerFactory_l(new TestPlayerFactory(), TEST_PLAYER);
 
     sInitComplete = true;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 6ea522c..4d88a7f 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -70,7 +70,6 @@
 #include "MetadataRetrieverClient.h"
 #include "MediaPlayerFactory.h"
 
-#include "MidiFile.h"
 #include "TestPlayerStub.h"
 #include "StagefrightPlayer.h"
 #include "nuplayer/NuPlayerDriver.h"
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index fa28451..715cc0c 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -35,7 +35,6 @@
 #include <media/MediaMetadataRetrieverInterface.h>
 #include <media/MediaPlayerInterface.h>
 #include <private/media/VideoFrame.h>
-#include "MidiMetadataRetriever.h"
 #include "MetadataRetrieverClient.h"
 #include "StagefrightMetadataRetriever.h"
 #include "MediaPlayerFactory.h"
@@ -90,10 +89,6 @@
             p = new StagefrightMetadataRetriever;
             break;
         }
-        case SONIVOX_PLAYER:
-            ALOGV("create midi metadata retriever");
-            p = new MidiMetadataRetriever();
-            break;
         default:
             // TODO:
             // support for TEST_PLAYER
diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp
deleted file mode 100644
index 205d44f..0000000
--- a/media/libmediaplayerservice/MidiFile.cpp
+++ /dev/null
@@ -1,541 +0,0 @@
-/* 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 <stdio.h>
-#include <assert.h>
-#include <limits.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sched.h>
-#include <utils/threads.h>
-#include <libsonivox/eas_reverb.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <system/audio.h>
-
-#include "MidiFile.h"
-
-// ----------------------------------------------------------------------------
-
-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(AUDIO_STREAM_MUSIC), mLoop(false), mExit(false),
-    mPaused(false), mRender(false), mTid(-1)
-{
-    ALOGV("constructor");
-
-    // get the library configuration and do sanity check
-    if (pLibConfig == NULL)
-        pLibConfig = EAS_Config();
-    if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) {
-        ALOGE("EAS library/header mismatch");
-        goto Failed;
-    }
-
-    // initialize EAS library
-    if (EAS_Init(&mEasData) != EAS_SUCCESS) {
-        ALOGE("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);
-        mThread = new MidiFileThread(this);
-        mThread->run("midithread", ANDROID_PRIORITY_AUDIO);
-        mCondition.wait(mMutex);
-        ALOGV("thread started");
-    }
-
-    // indicate success
-    if (mTid > 0) {
-        ALOGV(" 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() {
-    ALOGV("MidiFile destructor");
-    release();
-}
-
-status_t MidiFile::setDataSource(
-        const sp<IMediaHTTPService> & /*httpService*/,
-        const char* path,
-        const KeyedVector<String8, String8> *) {
-    ALOGV("MidiFile::setDataSource url=%s", path);
-    Mutex::Autolock lock(mMutex);
-
-    // file still open?
-    if (mEasHandle) {
-        reset_nosync();
-    }
-
-    // open file and set paused state
-    mIoWrapper = new MidiIoWrapper(path);
-    EAS_RESULT result = EAS_OpenFile(mEasData, mIoWrapper->getLocator(), &mEasHandle);
-    if (result == EAS_SUCCESS) {
-        updateState();
-    }
-
-    if (result != EAS_SUCCESS) {
-        ALOGE("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)
-{
-    ALOGV("MidiFile::setDataSource fd=%d", fd);
-    Mutex::Autolock lock(mMutex);
-
-    // file still open?
-    if (mEasHandle) {
-        reset_nosync();
-    }
-
-    // open file and set paused state
-    mIoWrapper = new MidiIoWrapper(fd, offset, length);
-    EAS_RESULT result = EAS_OpenFile(mEasData, mIoWrapper->getLocator(), &mEasHandle);
-    updateState();
-
-    if (result != EAS_SUCCESS) {
-        ALOGE("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()
-{
-    ALOGV("MidiFile::prepare");
-    Mutex::Autolock lock(mMutex);
-    if (!mEasHandle) {
-        return ERROR_NOT_OPEN;
-    }
-    EAS_RESULT result;
-    if ((result = EAS_Prepare(mEasData, mEasHandle)) != EAS_SUCCESS) {
-        ALOGE("EAS_Prepare failed: [%ld]", result);
-        return ERROR_EAS_FAILURE;
-    }
-    updateState();
-    return NO_ERROR;
-}
-
-status_t MidiFile::prepareAsync()
-{
-    ALOGV("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()
-{
-    ALOGV("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;
-    if (mState == EAS_STATE_PLAY) {
-        sendEvent(MEDIA_STARTED);
-    }
-
-    // wake up render thread
-    ALOGV("  wakeup render thread");
-    mCondition.signal();
-    return NO_ERROR;
-}
-
-status_t MidiFile::stop()
-{
-    ALOGV("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) {
-            ALOGE("EAS_Pause returned error %ld", result);
-            return ERROR_EAS_FAILURE;
-        }
-    }
-    mPaused = false;
-    sendEvent(MEDIA_STOPPED);
-    return NO_ERROR;
-}
-
-status_t MidiFile::seekTo(int position)
-{
-    ALOGV("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)
-        {
-            ALOGE("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()
-{
-    ALOGV("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;
-    sendEvent(MEDIA_PAUSED);
-    return NO_ERROR;
-}
-
-bool MidiFile::isPlaying()
-{
-    ALOGV("MidiFile::isPlaying, mState=%d", int(mState));
-    if (!mEasHandle || mPaused) return false;
-    return (mState == EAS_STATE_PLAY);
-}
-
-status_t MidiFile::getCurrentPosition(int* position)
-{
-    ALOGV("MidiFile::getCurrentPosition");
-    if (!mEasHandle) {
-        ALOGE("getCurrentPosition(): file not open");
-        return ERROR_NOT_OPEN;
-    }
-    if (mPlayTime < 0) {
-        ALOGE("getCurrentPosition(): mPlayTime = %ld", mPlayTime);
-        return ERROR_EAS_FAILURE;
-    }
-    *position = mPlayTime;
-    return NO_ERROR;
-}
-
-status_t MidiFile::getDuration(int* duration)
-{
-
-    ALOGV("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, mIoWrapper->getLocator(), &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()
-{
-    ALOGV("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()
-{
-    ALOGV("MidiFile::reset");
-    Mutex::Autolock lock(mMutex);
-    return reset_nosync();
-}
-
-// call only with mutex held
-status_t MidiFile::reset_nosync()
-{
-    ALOGV("MidiFile::reset_nosync");
-    sendEvent(MEDIA_STOPPED);
-    // close file
-    if (mEasHandle) {
-        EAS_CloseFile(mEasData, mEasHandle);
-        mEasHandle = NULL;
-    }
-
-    mIoWrapper.clear();
-    mPlayTime = -1;
-    mDuration = -1;
-    mLoop = false;
-    mPaused = false;
-    mRender = false;
-    return NO_ERROR;
-}
-
-status_t MidiFile::setLooping(int loop)
-{
-    ALOGV("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,
-            CHANNEL_MASK_USE_CHANNEL_ORDER, AUDIO_FORMAT_PCM_16_BIT, 2 /*bufferCount*/) != NO_ERROR) {
-        ALOGE("mAudioSink open failed");
-        return ERROR_OPEN_FAILED;
-    }
-    return NO_ERROR;
-}
-
-int MidiFile::render() {
-    EAS_RESULT result = EAS_FAILURE;
-    EAS_I32 count;
-    int temp;
-    bool audioStarted = false;
-
-    ALOGV("MidiFile::render");
-
-    // allocate render buffer
-    mAudioBuffer = new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * NUM_BUFFERS];
-    if (!mAudioBuffer) {
-        ALOGE("mAudioBuffer allocate failed");
-        goto threadExit;
-    }
-
-    // signal main thread that we started
-    {
-        Mutex::Autolock l(mMutex);
-        mTid = gettid();
-        ALOGV("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)
-        {
-            ALOGV("MidiFile::render - signal wait");
-            mCondition.wait(mMutex);
-            ALOGV("MidiFile::render - signal rx'd");
-        }
-        if (mExit) {
-            mMutex.unlock();
-            break;
-        }
-
-        // render midi data into the input buffer
-        //ALOGV("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) {
-                ALOGE("EAS_Render returned %ld", result);
-            }
-            p += count * pLibConfig->numChannels;
-            num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM);
-        }
-
-        // update playback state and position
-        // ALOGV("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()) {
-            ALOGV("MidiFile::render - create output track");
-            if (createOutputTrack() != NO_ERROR)
-                goto threadExit;
-        }
-
-        // Write data to the audio hardware
-        // ALOGV("MidiFile::render - writing to audio output");
-        if ((temp = mAudioSink->write(mAudioBuffer, num_output)) < 0) {
-            ALOGE("Error in writing:%d",temp);
-            return temp;
-        }
-
-        // start audio output if necessary
-        if (!audioStarted) {
-            //ALOGV("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:
-            {
-                ALOGV("MidiFile::render - stopped");
-                sendEvent(MEDIA_PLAYBACK_COMPLETE);
-                break;
-            }
-            case EAS_STATE_ERROR:
-            {
-                ALOGE("MidiFile::render - error");
-                sendEvent(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN);
-                break;
-            }
-            case EAS_STATE_PAUSED:
-                ALOGV("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
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
deleted file mode 100644
index 48a42aa..0000000
--- a/media/libmediaplayerservice/MidiFile.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
-**
-** Copyright 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 ANDROID_MIDIFILE_H
-#define ANDROID_MIDIFILE_H
-
-#include <media/MediaPlayerInterface.h>
-#include <libsonivox/eas.h>
-#include <media/MidiIoWrapper.h>
-
-namespace android {
-
-// Note that the name MidiFile is misleading; this actually represents a MIDI file player
-class MidiFile : public MediaPlayerInterface {
-public:
-                        MidiFile();
-                        ~MidiFile();
-
-    virtual status_t    initCheck();
-
-    virtual status_t    setDataSource(
-            const sp<IMediaHTTPService> &httpService,
-            const char* path,
-            const KeyedVector<String8, String8> *headers);
-
-    virtual status_t    setDataSource(int fd, int64_t offset, int64_t length);
-    virtual status_t    setVideoSurfaceTexture(
-                                const sp<IGraphicBufferProducer>& /*bufferProducer*/)
-                            { return UNKNOWN_ERROR; }
-    virtual status_t    prepare();
-    virtual status_t    prepareAsync();
-    virtual status_t    start();
-    virtual status_t    stop();
-    virtual status_t    seekTo(int msec);
-    virtual status_t    pause();
-    virtual bool        isPlaying();
-    virtual status_t    getCurrentPosition(int* msec);
-    virtual status_t    getDuration(int* msec);
-    virtual status_t    release();
-    virtual status_t    reset();
-    virtual status_t    setLooping(int loop);
-    virtual player_type playerType() { return SONIVOX_PLAYER; }
-    virtual status_t    invoke(const Parcel& /*request*/, Parcel* /*reply*/) {
-        return INVALID_OPERATION;
-    }
-    virtual status_t    setParameter(int /*key*/, const Parcel &/*request*/) {
-        return INVALID_OPERATION;
-    }
-    virtual status_t    getParameter(int /*key*/, Parcel* /*reply*/) {
-        return INVALID_OPERATION;
-    }
-
-private:
-            status_t    createOutputTrack();
-            status_t    reset_nosync();
-            int         render();
-            void        updateState(){ EAS_State(mEasData, mEasHandle, &mState); }
-
-    Mutex               mMutex;
-    Condition           mCondition;
-    EAS_DATA_HANDLE     mEasData;
-    EAS_HANDLE          mEasHandle;
-    EAS_PCM*            mAudioBuffer;
-    EAS_I32             mPlayTime;
-    EAS_I32             mDuration;
-    EAS_STATE           mState;
-    sp<MidiIoWrapper>   mIoWrapper;
-    audio_stream_type_t mStreamType;
-    bool                mLoop;
-    volatile bool       mExit;
-    bool                mPaused;
-    volatile bool       mRender;
-    pid_t               mTid;
-
-    class MidiFileThread : public Thread {
-    public:
-        MidiFileThread(MidiFile *midiPlayer) : mMidiFile(midiPlayer) {
-        }
-
-    protected:
-        virtual ~MidiFileThread() {}
-
-    private:
-        MidiFile *mMidiFile;
-
-        bool threadLoop() {
-            int result;
-            result = mMidiFile->render();
-            return false;
-        }
-
-        MidiFileThread(const MidiFileThread &);
-        MidiFileThread &operator=(const MidiFileThread &);
-    };
-
-    sp<MidiFileThread> mThread;
-};
-
-}; // namespace android
-
-#endif // ANDROID_MIDIFILE_H
diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.cpp b/media/libmediaplayerservice/MidiMetadataRetriever.cpp
deleted file mode 100644
index f3cf6ef..0000000
--- a/media/libmediaplayerservice/MidiMetadataRetriever.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
-**
-** Copyright 2009, 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 "MidiMetadataRetriever"
-#include <utils/Log.h>
-
-#include "MidiMetadataRetriever.h"
-#include <media/mediametadataretriever.h>
-
-#include <media/IMediaHTTPService.h>
-
-namespace android {
-
-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;
-
-void MidiMetadataRetriever::clearMetadataValues()
-{
-    ALOGV("clearMetadataValues");
-    mMetadataValues[0][0] = '\0';
-}
-
-status_t MidiMetadataRetriever::setDataSource(
-        const sp<IMediaHTTPService> &httpService,
-        const char *url,
-        const KeyedVector<String8, String8> *headers)
-{
-    ALOGV("setDataSource: %s", url? url: "NULL pointer");
-    Mutex::Autolock lock(mLock);
-    clearMetadataValues();
-    if (mMidiPlayer == 0) {
-        mMidiPlayer = new MidiFile();
-    }
-    return mMidiPlayer->setDataSource(httpService, url, headers);
-}
-
-status_t MidiMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length)
-{
-    ALOGV("setDataSource: fd(%d), offset(%lld), and length(%lld)", fd, offset, length);
-    Mutex::Autolock lock(mLock);
-    clearMetadataValues();
-    if (mMidiPlayer == 0) {
-        mMidiPlayer = new MidiFile();
-    }
-    return mMidiPlayer->setDataSource(fd, offset, length);;
-}
-
-const char* MidiMetadataRetriever::extractMetadata(int keyCode)
-{
-    ALOGV("extractMetdata: key(%d)", keyCode);
-    Mutex::Autolock lock(mLock);
-    if (mMidiPlayer == 0 || mMidiPlayer->initCheck() != NO_ERROR) {
-        ALOGE("Midi player is not initialized yet");
-        return NULL;
-    }
-    switch (keyCode) {
-    case METADATA_KEY_DURATION:
-        {
-            if (mMetadataValues[0][0] == '\0') {
-                int duration = -1;
-                if (mMidiPlayer->getDuration(&duration) != NO_ERROR) {
-                    ALOGE("failed to get duration");
-                    return NULL;
-                }
-                snprintf(mMetadataValues[0], MAX_METADATA_STRING_LENGTH, "%d", duration);
-            }
-
-            ALOGV("duration: %s ms", mMetadataValues[0]);
-            return mMetadataValues[0];
-        }
-    default:
-        ALOGE("Unsupported key code (%d)", keyCode);
-        return NULL;
-    }
-    return NULL;
-}
-
-};
-
diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.h b/media/libmediaplayerservice/MidiMetadataRetriever.h
deleted file mode 100644
index b8214ee..0000000
--- a/media/libmediaplayerservice/MidiMetadataRetriever.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-**
-** Copyright 2009, 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 ANDROID_MIDIMETADATARETRIEVER_H
-#define ANDROID_MIDIMETADATARETRIEVER_H
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <media/MediaMetadataRetrieverInterface.h>
-
-#include "MidiFile.h"
-
-namespace android {
-
-class MidiMetadataRetriever : public MediaMetadataRetrieverInterface {
-public:
-                                   MidiMetadataRetriever() {}
-                                   ~MidiMetadataRetriever() {}
-
-    virtual status_t                setDataSource(
-            const sp<IMediaHTTPService> &httpService,
-            const char *url,
-            const KeyedVector<String8, String8> *headers);
-
-    virtual status_t                setDataSource(int fd, int64_t offset, int64_t length);
-    virtual const char*             extractMetadata(int keyCode);
-
-private:
-    static const uint32_t MAX_METADATA_STRING_LENGTH = 128;
-    void clearMetadataValues();
-
-    Mutex               mLock;
-    sp<MidiFile>        mMidiPlayer;
-    char                mMetadataValues[1][MAX_METADATA_STRING_LENGTH];
-};
-
-}; // namespace android
-
-#endif // ANDROID_MIDIMETADATARETRIEVER_H
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 7bfd2fb..2629afc 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -36,6 +36,7 @@
         MediaCodecSource.cpp              \
         MediaDefs.cpp                     \
         MediaExtractor.cpp                \
+        MidiExtractor.cpp                 \
         http/MediaHTTP.cpp                \
         MediaMuxer.cpp                    \
         MediaSource.cpp                   \
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index c99db84..f7dcf35 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -22,6 +22,7 @@
 #include "include/DRMExtractor.h"
 #include "include/FLACExtractor.h"
 #include "include/HTTPBase.h"
+#include "include/MidiExtractor.h"
 #include "include/MP3Extractor.h"
 #include "include/MPEG2PSExtractor.h"
 #include "include/MPEG2TSExtractor.h"
@@ -172,6 +173,7 @@
     RegisterSniffer_l(SniffAAC);
     RegisterSniffer_l(SniffMPEG2PS);
     RegisterSniffer_l(SniffWVM);
+    RegisterSniffer_l(SniffMidi);
 
     char value[PROPERTY_VALUE_MAX];
     if (property_get("drm.service.enabled", value, NULL)
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index d48dd84..fde6fbd 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -34,6 +34,7 @@
 const char *MEDIA_MIMETYPE_AUDIO_MPEG = "audio/mpeg";
 const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I = "audio/mpeg-L1";
 const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II = "audio/mpeg-L2";
+const char *MEDIA_MIMETYPE_AUDIO_MIDI = "audio/midi";
 const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
 const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp";
 const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 9ab6611..e21fe6e 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -29,6 +29,7 @@
 #include "include/WVMExtractor.h"
 #include "include/FLACExtractor.h"
 #include "include/AACExtractor.h"
+#include "include/MidiExtractor.h"
 
 #include "matroska/MatroskaExtractor.h"
 
@@ -116,6 +117,8 @@
         ret = new AACExtractor(source, meta);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
         ret = new MPEG2PSExtractor(source);
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MIDI)) {
+        ret = new MidiExtractor(source);
     }
 
     if (ret != NULL) {
diff --git a/media/libstagefright/MidiExtractor.cpp b/media/libstagefright/MidiExtractor.cpp
new file mode 100644
index 0000000..66fab77
--- /dev/null
+++ b/media/libstagefright/MidiExtractor.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2014 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 "MidiExtractor"
+#include <utils/Log.h>
+
+#include "include/MidiExtractor.h"
+
+#include <media/MidiIoWrapper.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaSource.h>
+#include <libsonivox/eas_reverb.h>
+
+namespace android {
+
+// how many Sonivox output buffers to aggregate into one MediaBuffer
+static const int NUM_COMBINE_BUFFERS = 4;
+
+class MidiSource : public MediaSource {
+
+public:
+    MidiSource(
+            const sp<MidiEngine> &engine,
+            const sp<MetaData> &trackMetadata);
+
+    virtual status_t start(MetaData *params);
+    virtual status_t stop();
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+    virtual ~MidiSource();
+
+private:
+    sp<MidiEngine> mEngine;
+    sp<MetaData> mTrackMetadata;
+    bool mInitCheck;
+    bool mStarted;
+
+    status_t init();
+
+    // no copy constructor or assignment
+    MidiSource(const MidiSource &);
+    MidiSource &operator=(const MidiSource &);
+
+};
+
+
+// Midisource
+
+MidiSource::MidiSource(
+        const sp<MidiEngine> &engine,
+        const sp<MetaData> &trackMetadata)
+    : mEngine(engine),
+      mTrackMetadata(trackMetadata),
+      mInitCheck(false),
+      mStarted(false)
+{
+    ALOGV("MidiSource ctor");
+    mInitCheck = init();
+}
+
+MidiSource::~MidiSource()
+{
+    ALOGV("MidiSource dtor");
+    if (mStarted) {
+        stop();
+    }
+}
+
+status_t MidiSource::start(MetaData * /* params */)
+{
+    ALOGV("MidiSource::start");
+
+    CHECK(!mStarted);
+    mStarted = true;
+    mEngine->allocateBuffers();
+    return OK;
+}
+
+status_t MidiSource::stop()
+{
+    ALOGV("MidiSource::stop");
+
+    CHECK(mStarted);
+    mStarted = false;
+    mEngine->releaseBuffers();
+
+    return OK;
+}
+
+sp<MetaData> MidiSource::getFormat()
+{
+    return mTrackMetadata;
+}
+
+status_t MidiSource::read(
+        MediaBuffer **outBuffer, const ReadOptions *options)
+{
+    ALOGV("MidiSource::read");
+    MediaBuffer *buffer;
+    // process an optional seek request
+    int64_t seekTimeUs;
+    ReadOptions::SeekMode mode;
+    if ((NULL != options) && options->getSeekTo(&seekTimeUs, &mode)) {
+        if (seekTimeUs <= 0LL) {
+            seekTimeUs = 0LL;
+        }
+        mEngine->seekTo(seekTimeUs);
+    }
+    buffer = mEngine->readBuffer();
+    *outBuffer = buffer;
+    ALOGV("MidiSource::read %p done", this);
+    return buffer != NULL ? (status_t) OK : (status_t) ERROR_END_OF_STREAM;
+}
+
+status_t MidiSource::init()
+{
+    ALOGV("MidiSource::init");
+    return OK;
+}
+
+// MidiEngine
+
+MidiEngine::MidiEngine(const sp<DataSource> &dataSource,
+        const sp<MetaData> &fileMetadata,
+        const sp<MetaData> &trackMetadata) :
+            mGroup(NULL),
+            mEasData(NULL),
+            mEasHandle(NULL),
+            mEasConfig(NULL),
+            mIsInitialized(false) {
+    mIoWrapper = new MidiIoWrapper(dataSource);
+    // spin up a new EAS engine
+    EAS_I32 temp;
+    EAS_RESULT result = EAS_Init(&mEasData);
+
+    if (result == EAS_SUCCESS) {
+        result = EAS_OpenFile(mEasData, mIoWrapper->getLocator(), &mEasHandle);
+    }
+    if (result == EAS_SUCCESS) {
+        result = EAS_Prepare(mEasData, mEasHandle);
+    }
+    if (result == EAS_SUCCESS) {
+        result = EAS_ParseMetaData(mEasData, mEasHandle, &temp);
+    }
+
+    if (result != EAS_SUCCESS) {
+        return;
+    }
+
+    if (fileMetadata != NULL) {
+        fileMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MIDI);
+    }
+
+    if (trackMetadata != NULL) {
+        trackMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+        trackMetadata->setInt64(kKeyDuration, 1000ll * temp); // milli->micro
+        mEasConfig = EAS_Config();
+        trackMetadata->setInt32(kKeySampleRate, mEasConfig->sampleRate);
+        trackMetadata->setInt32(kKeyChannelCount, mEasConfig->numChannels);
+    }
+    mIsInitialized = true;
+}
+
+MidiEngine::~MidiEngine() {
+    if (mEasHandle) {
+        EAS_CloseFile(mEasData, mEasHandle);
+    }
+    if (mEasData) {
+        EAS_Shutdown(mEasData);
+    }
+    delete mGroup;
+
+}
+
+status_t MidiEngine::initCheck() {
+    return mIsInitialized ? OK : UNKNOWN_ERROR;
+}
+
+status_t MidiEngine::allocateBuffers() {
+    // 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);
+
+    mGroup = new MediaBufferGroup;
+    int bufsize = sizeof(EAS_PCM)
+            * mEasConfig->mixBufferSize * mEasConfig->numChannels * NUM_COMBINE_BUFFERS;
+    ALOGV("using %d byte buffer", bufsize);
+    mGroup->add_buffer(new MediaBuffer(bufsize));
+    return OK;
+}
+
+status_t MidiEngine::releaseBuffers() {
+    delete mGroup;
+    mGroup = NULL;
+    return OK;
+}
+
+status_t MidiEngine::seekTo(int64_t positionUs) {
+    ALOGV("seekTo %lld", positionUs);
+    EAS_RESULT result = EAS_Locate(mEasData, mEasHandle, positionUs / 1000, false);
+    return result == EAS_SUCCESS ? OK : UNKNOWN_ERROR;
+}
+
+MediaBuffer* MidiEngine::readBuffer() {
+    EAS_STATE state;
+    EAS_State(mEasData, mEasHandle, &state);
+    if ((state == EAS_STATE_STOPPED) || (state == EAS_STATE_ERROR)) {
+        return NULL;
+    }
+    MediaBuffer *buffer;
+    status_t err = mGroup->acquire_buffer(&buffer);
+    if (err != OK) {
+        ALOGE("readBuffer: no buffer");
+        return NULL;
+    }
+    EAS_I32 timeMs;
+    EAS_GetLocation(mEasData, mEasHandle, &timeMs);
+    int64_t timeUs = 1000ll * timeMs;
+    buffer->meta_data()->setInt64(kKeyTime, timeUs);
+
+    EAS_PCM* p = (EAS_PCM*) buffer->data();
+    int numBytesOutput = 0;
+    for (int i = 0; i < NUM_COMBINE_BUFFERS; i++) {
+        EAS_I32 numRendered;
+        EAS_RESULT result = EAS_Render(mEasData, p, mEasConfig->mixBufferSize, &numRendered);
+        if (result != EAS_SUCCESS) {
+            ALOGE("EAS_Render returned %ld", result);
+            break;
+        }
+        p += numRendered * mEasConfig->numChannels;
+        numBytesOutput += numRendered * mEasConfig->numChannels * sizeof(EAS_PCM);
+    }
+    buffer->set_range(0, numBytesOutput);
+    ALOGV("readBuffer: returning %zd in buffer %p", buffer->range_length(), buffer);
+    return buffer;
+}
+
+
+// MidiExtractor
+
+MidiExtractor::MidiExtractor(
+        const sp<DataSource> &dataSource)
+    : mDataSource(dataSource),
+      mInitCheck(false)
+{
+    ALOGV("MidiExtractor ctor");
+    mFileMetadata = new MetaData;
+    mTrackMetadata = new MetaData;
+    mEngine = new MidiEngine(mDataSource, mFileMetadata, mTrackMetadata);
+    mInitCheck = mEngine->initCheck();
+}
+
+MidiExtractor::~MidiExtractor()
+{
+    ALOGV("MidiExtractor dtor");
+}
+
+size_t MidiExtractor::countTracks()
+{
+    return mInitCheck == OK ? 1 : 0;
+}
+
+sp<MediaSource> MidiExtractor::getTrack(size_t index)
+{
+    if (mInitCheck != OK || index > 0) {
+        return NULL;
+    }
+    return new MidiSource(mEngine, mTrackMetadata);
+}
+
+sp<MetaData> MidiExtractor::getTrackMetaData(
+        size_t index, uint32_t /* flags */) {
+    ALOGV("MidiExtractor::getTrackMetaData");
+    if (mInitCheck != OK || index > 0) {
+        return NULL;
+    }
+    return mTrackMetadata;
+}
+
+sp<MetaData> MidiExtractor::getMetaData()
+{
+    ALOGV("MidiExtractor::getMetaData");
+    return mFileMetadata;
+}
+
+// Sniffer
+
+bool SniffMidi(
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *)
+{
+    sp<MidiEngine> p = new MidiEngine(source, NULL, NULL);
+    if (p->initCheck() == OK) {
+        *mimeType = MEDIA_MIMETYPE_AUDIO_MIDI;
+        *confidence = 0.8;
+        ALOGV("SniffMidi: yes");
+        return true;
+    }
+    ALOGV("SniffMidi: no");
+    return false;
+
+}
+
+}  // namespace android
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 6ded3a7..db33e83 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -26,12 +26,8 @@
 
 #include <media/IMediaHTTPService.h>
 #include <media/mediametadataretriever.h>
-#include <media/MidiIoWrapper.h>
 #include <private/media/VideoFrame.h>
 
-// Sonivox includes
-#include <libsonivox/eas.h>
-
 namespace android {
 
 StagefrightMediaScanner::StagefrightMediaScanner() {}
@@ -58,50 +54,6 @@
     return false;
 }
 
-static MediaScanResult HandleMIDI(
-        const char *filename, MediaScannerClient *client) {
-    // get the library configuration and do sanity check
-    const S_EAS_LIB_CONFIG* pLibConfig = EAS_Config();
-    if ((pLibConfig == NULL) || (LIB_VERSION != pLibConfig->libVersion)) {
-        ALOGE("EAS library/header mismatch\n");
-        return MEDIA_SCAN_RESULT_ERROR;
-    }
-    EAS_I32 temp;
-
-    // spin up a new EAS engine
-    EAS_DATA_HANDLE easData = NULL;
-    EAS_HANDLE easHandle = NULL;
-    EAS_RESULT result = EAS_Init(&easData);
-    MidiIoWrapper wrapper(filename);
-    if (result == EAS_SUCCESS) {
-        result = EAS_OpenFile(easData, wrapper.getLocator(), &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 MEDIA_SCAN_RESULT_SKIPPED;
-    }
-
-    char buffer[20];
-    sprintf(buffer, "%ld", temp);
-    status_t status = client->addStringTag("duration", buffer);
-    if (status != OK) {
-        return MEDIA_SCAN_RESULT_ERROR;
-    }
-    return MEDIA_SCAN_RESULT_OK;
-}
-
 MediaScanResult StagefrightMediaScanner::processFile(
         const char *path, const char *mimeType,
         MediaScannerClient &client) {
@@ -127,18 +79,6 @@
         return MEDIA_SCAN_RESULT_SKIPPED;
     }
 
-    if (!strcasecmp(extension, ".mid")
-            || !strcasecmp(extension, ".smf")
-            || !strcasecmp(extension, ".imy")
-            || !strcasecmp(extension, ".midi")
-            || !strcasecmp(extension, ".xmf")
-            || !strcasecmp(extension, ".rtttl")
-            || !strcasecmp(extension, ".rtx")
-            || !strcasecmp(extension, ".ota")
-            || !strcasecmp(extension, ".mxmf")) {
-        return HandleMIDI(path, &client);
-    }
-
     sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever);
 
     int fd = open(path, O_RDONLY | O_LARGEFILE);
diff --git a/media/libstagefright/include/MidiExtractor.h b/media/libstagefright/include/MidiExtractor.h
new file mode 100644
index 0000000..9a2abc0
--- /dev/null
+++ b/media/libstagefright/include/MidiExtractor.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 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 MIDI_EXTRACTOR_H_
+#define MIDI_EXTRACTOR_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/MidiIoWrapper.h>
+#include <utils/String8.h>
+#include <libsonivox/eas.h>
+
+namespace android {
+
+class MidiEngine : public RefBase {
+public:
+    MidiEngine(const sp<DataSource> &dataSource,
+            const sp<MetaData> &fileMetadata,
+            const sp<MetaData> &trackMetadata);
+    ~MidiEngine();
+
+    status_t initCheck();
+
+    status_t allocateBuffers();
+    status_t releaseBuffers();
+    status_t seekTo(int64_t positionUs);
+    MediaBuffer* readBuffer();
+private:
+    sp<MidiIoWrapper> mIoWrapper;
+    MediaBufferGroup *mGroup;
+    EAS_DATA_HANDLE mEasData;
+    EAS_HANDLE mEasHandle;
+    const S_EAS_LIB_CONFIG* mEasConfig;
+    bool mIsInitialized;
+};
+
+class MidiExtractor : public MediaExtractor {
+
+public:
+    // Extractor assumes ownership of source
+    MidiExtractor(const sp<DataSource> &source);
+
+    virtual size_t countTracks();
+    virtual sp<MediaSource> getTrack(size_t index);
+    virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+    virtual sp<MetaData> getMetaData();
+
+protected:
+    virtual ~MidiExtractor();
+
+private:
+    sp<DataSource> mDataSource;
+    status_t mInitCheck;
+    sp<MetaData> mFileMetadata;
+
+    // There is only one track
+    sp<MetaData> mTrackMetadata;
+
+    sp<MidiEngine> mEngine;
+
+    EAS_DATA_HANDLE     mEasData;
+    EAS_HANDLE          mEasHandle;
+    EAS_PCM*            mAudioBuffer;
+    EAS_I32             mPlayTime;
+    EAS_I32             mDuration;
+    EAS_STATE           mState;
+    EAS_FILE            mFileLocator;
+
+    MidiExtractor(const MidiExtractor &);
+    MidiExtractor &operator=(const MidiExtractor &);
+
+};
+
+bool SniffMidi(const sp<DataSource> &source, String8 *mimeType,
+        float *confidence, sp<AMessage> *);
+
+}  // namespace android
+
+#endif  // MIDI_EXTRACTOR_H_