Home | History | Annotate | Download | only in libstagefright
      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 "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/stagefright/MediaSource.h>
     29 #include <libsonivox/eas_reverb.h>
     30 
     31 namespace android {
     32 
     33 // how many Sonivox output buffers to aggregate into one MediaBuffer
     34 static const int NUM_COMBINE_BUFFERS = 4;
     35 
     36 class MidiSource : public MediaSource {
     37 
     38 public:
     39     MidiSource(
     40             const sp<MidiEngine> &engine,
     41             const sp<MetaData> &trackMetadata);
     42 
     43     virtual status_t start(MetaData *params);
     44     virtual status_t stop();
     45     virtual sp<MetaData> getFormat();
     46 
     47     virtual status_t read(
     48             MediaBuffer **buffer, const ReadOptions *options = NULL);
     49 
     50 protected:
     51     virtual ~MidiSource();
     52 
     53 private:
     54     sp<MidiEngine> mEngine;
     55     sp<MetaData> 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         const sp<MidiEngine> &engine,
     72         const sp<MetaData> &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(MetaData * /* 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 sp<MetaData> MidiSource::getFormat()
    112 {
    113     return mTrackMetadata;
    114 }
    115 
    116 status_t MidiSource::read(
    117         MediaBuffer **outBuffer, const ReadOptions *options)
    118 {
    119     ALOGV("MidiSource::read");
    120     MediaBuffer *buffer;
    121     // process an optional seek request
    122     int64_t seekTimeUs;
    123     ReadOptions::SeekMode mode;
    124     if ((NULL != options) && options->getSeekTo(&seekTimeUs, &mode)) {
    125         if (seekTimeUs <= 0LL) {
    126             seekTimeUs = 0LL;
    127         }
    128         mEngine->seekTo(seekTimeUs);
    129     }
    130     buffer = mEngine->readBuffer();
    131     *outBuffer = buffer;
    132     ALOGV("MidiSource::read %p done", this);
    133     return buffer != NULL ? (status_t) OK : (status_t) ERROR_END_OF_STREAM;
    134 }
    135 
    136 status_t MidiSource::init()
    137 {
    138     ALOGV("MidiSource::init");
    139     return OK;
    140 }
    141 
    142 // MidiEngine
    143 
    144 MidiEngine::MidiEngine(const sp<DataSource> &dataSource,
    145         const sp<MetaData> &fileMetadata,
    146         const sp<MetaData> &trackMetadata) :
    147             mGroup(NULL),
    148             mEasData(NULL),
    149             mEasHandle(NULL),
    150             mEasConfig(NULL),
    151             mIsInitialized(false) {
    152     mIoWrapper = new MidiIoWrapper(dataSource);
    153     // spin up a new EAS engine
    154     EAS_I32 temp;
    155     EAS_RESULT result = EAS_Init(&mEasData);
    156 
    157     if (result == EAS_SUCCESS) {
    158         result = EAS_OpenFile(mEasData, mIoWrapper->getLocator(), &mEasHandle);
    159     }
    160     if (result == EAS_SUCCESS) {
    161         result = EAS_Prepare(mEasData, mEasHandle);
    162     }
    163     if (result == EAS_SUCCESS) {
    164         result = EAS_ParseMetaData(mEasData, mEasHandle, &temp);
    165     }
    166 
    167     if (result != EAS_SUCCESS) {
    168         return;
    169     }
    170 
    171     if (fileMetadata != NULL) {
    172         fileMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MIDI);
    173     }
    174 
    175     if (trackMetadata != NULL) {
    176         trackMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
    177         trackMetadata->setInt64(kKeyDuration, 1000ll * temp); // milli->micro
    178         mEasConfig = EAS_Config();
    179         trackMetadata->setInt32(kKeySampleRate, mEasConfig->sampleRate);
    180         trackMetadata->setInt32(kKeyChannelCount, mEasConfig->numChannels);
    181     }
    182     mIsInitialized = true;
    183 }
    184 
    185 MidiEngine::~MidiEngine() {
    186     if (mEasHandle) {
    187         EAS_CloseFile(mEasData, mEasHandle);
    188     }
    189     if (mEasData) {
    190         EAS_Shutdown(mEasData);
    191     }
    192     delete mGroup;
    193 
    194 }
    195 
    196 status_t MidiEngine::initCheck() {
    197     return mIsInitialized ? OK : UNKNOWN_ERROR;
    198 }
    199 
    200 status_t MidiEngine::allocateBuffers() {
    201     // select reverb preset and enable
    202     EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET, EAS_PARAM_REVERB_CHAMBER);
    203     EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS, EAS_FALSE);
    204 
    205     mGroup = new MediaBufferGroup;
    206     int bufsize = sizeof(EAS_PCM)
    207             * mEasConfig->mixBufferSize * mEasConfig->numChannels * NUM_COMBINE_BUFFERS;
    208     ALOGV("using %d byte buffer", bufsize);
    209     mGroup->add_buffer(new MediaBuffer(bufsize));
    210     return OK;
    211 }
    212 
    213 status_t MidiEngine::releaseBuffers() {
    214     delete mGroup;
    215     mGroup = NULL;
    216     return OK;
    217 }
    218 
    219 status_t MidiEngine::seekTo(int64_t positionUs) {
    220     ALOGV("seekTo %lld", (long long)positionUs);
    221     EAS_RESULT result = EAS_Locate(mEasData, mEasHandle, positionUs / 1000, false);
    222     return result == EAS_SUCCESS ? OK : UNKNOWN_ERROR;
    223 }
    224 
    225 MediaBuffer* MidiEngine::readBuffer() {
    226     EAS_STATE state;
    227     EAS_State(mEasData, mEasHandle, &state);
    228     if ((state == EAS_STATE_STOPPED) || (state == EAS_STATE_ERROR)) {
    229         return NULL;
    230     }
    231     MediaBuffer *buffer;
    232     status_t err = mGroup->acquire_buffer(&buffer);
    233     if (err != OK) {
    234         ALOGE("readBuffer: no buffer");
    235         return NULL;
    236     }
    237     EAS_I32 timeMs;
    238     EAS_GetLocation(mEasData, mEasHandle, &timeMs);
    239     int64_t timeUs = 1000ll * timeMs;
    240     buffer->meta_data()->setInt64(kKeyTime, timeUs);
    241 
    242     EAS_PCM* p = (EAS_PCM*) buffer->data();
    243     int numBytesOutput = 0;
    244     for (int i = 0; i < NUM_COMBINE_BUFFERS; i++) {
    245         EAS_I32 numRendered;
    246         EAS_RESULT result = EAS_Render(mEasData, p, mEasConfig->mixBufferSize, &numRendered);
    247         if (result != EAS_SUCCESS) {
    248             ALOGE("EAS_Render returned %ld", result);
    249             break;
    250         }
    251         p += numRendered * mEasConfig->numChannels;
    252         numBytesOutput += numRendered * mEasConfig->numChannels * sizeof(EAS_PCM);
    253     }
    254     buffer->set_range(0, numBytesOutput);
    255     ALOGV("readBuffer: returning %zd in buffer %p", buffer->range_length(), buffer);
    256     return buffer;
    257 }
    258 
    259 
    260 // MidiExtractor
    261 
    262 MidiExtractor::MidiExtractor(
    263         const sp<DataSource> &dataSource)
    264     : mDataSource(dataSource),
    265       mInitCheck(false)
    266 {
    267     ALOGV("MidiExtractor ctor");
    268     mFileMetadata = new MetaData;
    269     mTrackMetadata = new MetaData;
    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 sp<MediaSource> 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 sp<MetaData> MidiExtractor::getTrackMetaData(
    293         size_t index, uint32_t /* flags */) {
    294     ALOGV("MidiExtractor::getTrackMetaData");
    295     if (mInitCheck != OK || index > 0) {
    296         return NULL;
    297     }
    298     return mTrackMetadata;
    299 }
    300 
    301 sp<MetaData> MidiExtractor::getMetaData()
    302 {
    303     ALOGV("MidiExtractor::getMetaData");
    304     return mFileMetadata;
    305 }
    306 
    307 // Sniffer
    308 
    309 bool SniffMidi(
    310         const sp<DataSource> &source, String8 *mimeType, float *confidence,
    311         sp<AMessage> *)
    312 {
    313     sp<MidiEngine> p = new MidiEngine(source, NULL, NULL);
    314     if (p->initCheck() == OK) {
    315         *mimeType = MEDIA_MIMETYPE_AUDIO_MIDI;
    316         *confidence = 0.8;
    317         ALOGV("SniffMidi: yes");
    318         return true;
    319     }
    320     ALOGV("SniffMidi: no");
    321     return false;
    322 
    323 }
    324 
    325 }  // namespace android
    326