Home | History | Annotate | Download | only in midi
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //#define LOG_NDEBUG 0
     18 #define LOG_TAG "MidiExtractor"
     19 #include <utils/Log.h>
     20 
     21 #include "MidiExtractor.h"
     22 
     23 #include <media/MidiIoWrapper.h>
     24 #include <media/stagefright/foundation/ADebug.h>
     25 #include <media/stagefright/MediaBufferGroup.h>
     26 #include <media/stagefright/MediaDefs.h>
     27 #include <media/stagefright/MetaData.h>
     28 #include <media/MediaTrack.h>
     29 #include <libsonivox/eas_reverb.h>
     30 
     31 namespace android {
     32 
     33 // how many Sonivox output buffers to aggregate into one MediaBufferBase
     34 static const int NUM_COMBINE_BUFFERS = 4;
     35 
     36 class MidiSource : public MediaTrack {
     37 
     38 public:
     39     MidiSource(
     40             MidiEngine &engine,
     41             MetaDataBase &trackMetadata);
     42 
     43     virtual status_t start(MetaDataBase *params);
     44     virtual status_t stop();
     45     virtual status_t getFormat(MetaDataBase&);
     46 
     47     virtual status_t read(
     48             MediaBufferBase **buffer, const ReadOptions *options = NULL);
     49 
     50 protected:
     51     virtual ~MidiSource();
     52 
     53 private:
     54     MidiEngine &mEngine;
     55     MetaDataBase &mTrackMetadata;
     56     bool mInitCheck;
     57     bool mStarted;
     58 
     59     status_t init();
     60 
     61     // no copy constructor or assignment
     62     MidiSource(const MidiSource &);
     63     MidiSource &operator=(const MidiSource &);
     64 
     65 };
     66 
     67 
     68 // Midisource
     69 
     70 MidiSource::MidiSource(
     71         MidiEngine &engine,
     72         MetaDataBase &trackMetadata)
     73     : mEngine(engine),
     74       mTrackMetadata(trackMetadata),
     75       mInitCheck(false),
     76       mStarted(false)
     77 {
     78     ALOGV("MidiSource ctor");
     79     mInitCheck = init();
     80 }
     81 
     82 MidiSource::~MidiSource()
     83 {
     84     ALOGV("MidiSource dtor");
     85     if (mStarted) {
     86         stop();
     87     }
     88 }
     89 
     90 status_t MidiSource::start(MetaDataBase * /* params */)
     91 {
     92     ALOGV("MidiSource::start");
     93 
     94     CHECK(!mStarted);
     95     mStarted = true;
     96     mEngine.allocateBuffers();
     97     return OK;
     98 }
     99 
    100 status_t MidiSource::stop()
    101 {
    102     ALOGV("MidiSource::stop");
    103 
    104     CHECK(mStarted);
    105     mStarted = false;
    106     mEngine.releaseBuffers();
    107 
    108     return OK;
    109 }
    110 
    111 status_t MidiSource::getFormat(MetaDataBase &meta)
    112 {
    113     meta = mTrackMetadata;
    114     return OK;
    115 }
    116 
    117 status_t MidiSource::read(
    118         MediaBufferBase **outBuffer, const ReadOptions *options)
    119 {
    120     ALOGV("MidiSource::read");
    121     MediaBufferBase *buffer;
    122     // process an optional seek request
    123     int64_t seekTimeUs;
    124     ReadOptions::SeekMode mode;
    125     if ((NULL != options) && options->getSeekTo(&seekTimeUs, &mode)) {
    126         if (seekTimeUs <= 0LL) {
    127             seekTimeUs = 0LL;
    128         }
    129         mEngine.seekTo(seekTimeUs);
    130     }
    131     buffer = mEngine.readBuffer();
    132     *outBuffer = buffer;
    133     ALOGV("MidiSource::read %p done", this);
    134     return buffer != NULL ? (status_t) OK : (status_t) ERROR_END_OF_STREAM;
    135 }
    136 
    137 status_t MidiSource::init()
    138 {
    139     ALOGV("MidiSource::init");
    140     return OK;
    141 }
    142 
    143 // MidiEngine
    144 
    145 MidiEngine::MidiEngine(DataSourceBase *dataSource,
    146         MetaDataBase *fileMetadata,
    147         MetaDataBase *trackMetadata) :
    148             mGroup(NULL),
    149             mEasData(NULL),
    150             mEasHandle(NULL),
    151             mEasConfig(NULL),
    152             mIsInitialized(false) {
    153     mIoWrapper = new MidiIoWrapper(dataSource);
    154     // spin up a new EAS engine
    155     EAS_I32 temp;
    156     EAS_RESULT result = EAS_Init(&mEasData);
    157 
    158     if (result == EAS_SUCCESS) {
    159         result = EAS_OpenFile(mEasData, mIoWrapper->getLocator(), &mEasHandle);
    160     }
    161     if (result == EAS_SUCCESS) {
    162         result = EAS_Prepare(mEasData, mEasHandle);
    163     }
    164     if (result == EAS_SUCCESS) {
    165         result = EAS_ParseMetaData(mEasData, mEasHandle, &temp);
    166     }
    167 
    168     if (result != EAS_SUCCESS) {
    169         return;
    170     }
    171 
    172     if (fileMetadata != NULL) {
    173         fileMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MIDI);
    174     }
    175 
    176     if (trackMetadata != NULL) {
    177         trackMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
    178         trackMetadata->setInt64(kKeyDuration, 1000ll * temp); // milli->micro
    179         mEasConfig = EAS_Config();
    180         trackMetadata->setInt32(kKeySampleRate, mEasConfig->sampleRate);
    181         trackMetadata->setInt32(kKeyChannelCount, mEasConfig->numChannels);
    182         trackMetadata->setInt32(kKeyPcmEncoding, kAudioEncodingPcm16bit);
    183     }
    184     mIsInitialized = true;
    185 }
    186 
    187 MidiEngine::~MidiEngine() {
    188     if (mEasHandle) {
    189         EAS_CloseFile(mEasData, mEasHandle);
    190     }
    191     if (mEasData) {
    192         EAS_Shutdown(mEasData);
    193     }
    194     delete mGroup;
    195     delete mIoWrapper;
    196 }
    197 
    198 status_t MidiEngine::initCheck() {
    199     return mIsInitialized ? OK : UNKNOWN_ERROR;
    200 }
    201 
    202 status_t MidiEngine::allocateBuffers() {
    203     // select reverb preset and enable
    204     EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET, EAS_PARAM_REVERB_CHAMBER);
    205     EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS, EAS_FALSE);
    206 
    207     mGroup = new MediaBufferGroup;
    208     int bufsize = sizeof(EAS_PCM)
    209             * mEasConfig->mixBufferSize * mEasConfig->numChannels * NUM_COMBINE_BUFFERS;
    210     ALOGV("using %d byte buffer", bufsize);
    211     mGroup->add_buffer(MediaBufferBase::Create(bufsize));
    212     return OK;
    213 }
    214 
    215 status_t MidiEngine::releaseBuffers() {
    216     delete mGroup;
    217     mGroup = NULL;
    218     return OK;
    219 }
    220 
    221 status_t MidiEngine::seekTo(int64_t positionUs) {
    222     ALOGV("seekTo %lld", (long long)positionUs);
    223     EAS_RESULT result = EAS_Locate(mEasData, mEasHandle, positionUs / 1000, false);
    224     return result == EAS_SUCCESS ? OK : UNKNOWN_ERROR;
    225 }
    226 
    227 MediaBufferBase* MidiEngine::readBuffer() {
    228     EAS_STATE state;
    229     EAS_State(mEasData, mEasHandle, &state);
    230     if ((state == EAS_STATE_STOPPED) || (state == EAS_STATE_ERROR)) {
    231         return NULL;
    232     }
    233     MediaBufferBase *buffer;
    234     status_t err = mGroup->acquire_buffer(&buffer);
    235     if (err != OK) {
    236         ALOGE("readBuffer: no buffer");
    237         return NULL;
    238     }
    239     EAS_I32 timeMs;
    240     EAS_GetLocation(mEasData, mEasHandle, &timeMs);
    241     int64_t timeUs = 1000ll * timeMs;
    242     buffer->meta_data().setInt64(kKeyTime, timeUs);
    243 
    244     EAS_PCM* p = (EAS_PCM*) buffer->data();
    245     int numBytesOutput = 0;
    246     for (int i = 0; i < NUM_COMBINE_BUFFERS; i++) {
    247         EAS_I32 numRendered;
    248         EAS_RESULT result = EAS_Render(mEasData, p, mEasConfig->mixBufferSize, &numRendered);
    249         if (result != EAS_SUCCESS) {
    250             ALOGE("EAS_Render returned %ld", result);
    251             break;
    252         }
    253         p += numRendered * mEasConfig->numChannels;
    254         numBytesOutput += numRendered * mEasConfig->numChannels * sizeof(EAS_PCM);
    255     }
    256     buffer->set_range(0, numBytesOutput);
    257     ALOGV("readBuffer: returning %zd in buffer %p", buffer->range_length(), buffer);
    258     return buffer;
    259 }
    260 
    261 
    262 // MidiExtractor
    263 
    264 MidiExtractor::MidiExtractor(
    265         DataSourceBase *dataSource)
    266     : mDataSource(dataSource),
    267       mInitCheck(false)
    268 {
    269     ALOGV("MidiExtractor ctor");
    270     mEngine = new MidiEngine(mDataSource, &mFileMetadata, &mTrackMetadata);
    271     mInitCheck = mEngine->initCheck();
    272 }
    273 
    274 MidiExtractor::~MidiExtractor()
    275 {
    276     ALOGV("MidiExtractor dtor");
    277 }
    278 
    279 size_t MidiExtractor::countTracks()
    280 {
    281     return mInitCheck == OK ? 1 : 0;
    282 }
    283 
    284 MediaTrack *MidiExtractor::getTrack(size_t index)
    285 {
    286     if (mInitCheck != OK || index > 0) {
    287         return NULL;
    288     }
    289     return new MidiSource(*mEngine, mTrackMetadata);
    290 }
    291 
    292 status_t MidiExtractor::getTrackMetaData(
    293         MetaDataBase &meta,
    294         size_t index, uint32_t /* flags */) {
    295     ALOGV("MidiExtractor::getTrackMetaData");
    296     if (mInitCheck != OK || index > 0) {
    297         return UNKNOWN_ERROR;
    298     }
    299     meta = mTrackMetadata;
    300     return OK;
    301 }
    302 
    303 status_t MidiExtractor::getMetaData(MetaDataBase &meta)
    304 {
    305     ALOGV("MidiExtractor::getMetaData");
    306     meta = mFileMetadata;
    307     return OK;
    308 }
    309 
    310 // Sniffer
    311 
    312 bool SniffMidi(DataSourceBase *source, float *confidence)
    313 {
    314     MidiEngine p(source, NULL, NULL);
    315     if (p.initCheck() == OK) {
    316         *confidence = 0.8;
    317         ALOGV("SniffMidi: yes");
    318         return true;
    319     }
    320     ALOGV("SniffMidi: no");
    321     return false;
    322 
    323 }
    324 
    325 extern "C" {
    326 // This is the only symbol that needs to be exported
    327 __attribute__ ((visibility ("default")))
    328 MediaExtractor::ExtractorDef GETEXTRACTORDEF() {
    329     return {
    330         MediaExtractor::EXTRACTORDEF_VERSION,
    331         UUID("ef6cca0a-f8a2-43e6-ba5f-dfcd7c9a7ef2"),
    332         1,
    333         "MIDI Extractor",
    334         [](
    335                 DataSourceBase *source,
    336                 float *confidence,
    337                 void **,
    338                 MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
    339             if (SniffMidi(source, confidence)) {
    340                 return [](
    341                         DataSourceBase *source,
    342                         void *) -> MediaExtractor* {
    343                     return new MidiExtractor(source);};
    344             }
    345             return NULL;
    346         }
    347     };
    348 }
    349 
    350 } // extern "C"
    351 
    352 }  // namespace android
    353