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         trackMetadata->setInt32(kKeyPcmEncoding, kAudioEncodingPcm16bit);
    182     }
    183     mIsInitialized = true;
    184 }
    185 
    186 MidiEngine::~MidiEngine() {
    187     if (mEasHandle) {
    188         EAS_CloseFile(mEasData, mEasHandle);
    189     }
    190     if (mEasData) {
    191         EAS_Shutdown(mEasData);
    192     }
    193     delete mGroup;
    194 
    195 }
    196 
    197 status_t MidiEngine::initCheck() {
    198     return mIsInitialized ? OK : UNKNOWN_ERROR;
    199 }
    200 
    201 status_t MidiEngine::allocateBuffers() {
    202     // select reverb preset and enable
    203     EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET, EAS_PARAM_REVERB_CHAMBER);
    204     EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS, EAS_FALSE);
    205 
    206     mGroup = new MediaBufferGroup;
    207     int bufsize = sizeof(EAS_PCM)
    208             * mEasConfig->mixBufferSize * mEasConfig->numChannels * NUM_COMBINE_BUFFERS;
    209     ALOGV("using %d byte buffer", bufsize);
    210     mGroup->add_buffer(new MediaBuffer(bufsize));
    211     return OK;
    212 }
    213 
    214 status_t MidiEngine::releaseBuffers() {
    215     delete mGroup;
    216     mGroup = NULL;
    217     return OK;
    218 }
    219 
    220 status_t MidiEngine::seekTo(int64_t positionUs) {
    221     ALOGV("seekTo %lld", (long long)positionUs);
    222     EAS_RESULT result = EAS_Locate(mEasData, mEasHandle, positionUs / 1000, false);
    223     return result == EAS_SUCCESS ? OK : UNKNOWN_ERROR;
    224 }
    225 
    226 MediaBuffer* MidiEngine::readBuffer() {
    227     EAS_STATE state;
    228     EAS_State(mEasData, mEasHandle, &state);
    229     if ((state == EAS_STATE_STOPPED) || (state == EAS_STATE_ERROR)) {
    230         return NULL;
    231     }
    232     MediaBuffer *buffer;
    233     status_t err = mGroup->acquire_buffer(&buffer);
    234     if (err != OK) {
    235         ALOGE("readBuffer: no buffer");
    236         return NULL;
    237     }
    238     EAS_I32 timeMs;
    239     EAS_GetLocation(mEasData, mEasHandle, &timeMs);
    240     int64_t timeUs = 1000ll * timeMs;
    241     buffer->meta_data()->setInt64(kKeyTime, timeUs);
    242 
    243     EAS_PCM* p = (EAS_PCM*) buffer->data();
    244     int numBytesOutput = 0;
    245     for (int i = 0; i < NUM_COMBINE_BUFFERS; i++) {
    246         EAS_I32 numRendered;
    247         EAS_RESULT result = EAS_Render(mEasData, p, mEasConfig->mixBufferSize, &numRendered);
    248         if (result != EAS_SUCCESS) {
    249             ALOGE("EAS_Render returned %ld", result);
    250             break;
    251         }
    252         p += numRendered * mEasConfig->numChannels;
    253         numBytesOutput += numRendered * mEasConfig->numChannels * sizeof(EAS_PCM);
    254     }
    255     buffer->set_range(0, numBytesOutput);
    256     ALOGV("readBuffer: returning %zd in buffer %p", buffer->range_length(), buffer);
    257     return buffer;
    258 }
    259 
    260 
    261 // MidiExtractor
    262 
    263 MidiExtractor::MidiExtractor(
    264         const sp<DataSource> &dataSource)
    265     : mDataSource(dataSource),
    266       mInitCheck(false)
    267 {
    268     ALOGV("MidiExtractor ctor");
    269     mFileMetadata = new MetaData;
    270     mTrackMetadata = new MetaData;
    271     mEngine = new MidiEngine(mDataSource, mFileMetadata, mTrackMetadata);
    272     mInitCheck = mEngine->initCheck();
    273 }
    274 
    275 MidiExtractor::~MidiExtractor()
    276 {
    277     ALOGV("MidiExtractor dtor");
    278 }
    279 
    280 size_t MidiExtractor::countTracks()
    281 {
    282     return mInitCheck == OK ? 1 : 0;
    283 }
    284 
    285 sp<IMediaSource> MidiExtractor::getTrack(size_t index)
    286 {
    287     if (mInitCheck != OK || index > 0) {
    288         return NULL;
    289     }
    290     return new MidiSource(mEngine, mTrackMetadata);
    291 }
    292 
    293 sp<MetaData> MidiExtractor::getTrackMetaData(
    294         size_t index, uint32_t /* flags */) {
    295     ALOGV("MidiExtractor::getTrackMetaData");
    296     if (mInitCheck != OK || index > 0) {
    297         return NULL;
    298     }
    299     return mTrackMetadata;
    300 }
    301 
    302 sp<MetaData> MidiExtractor::getMetaData()
    303 {
    304     ALOGV("MidiExtractor::getMetaData");
    305     return mFileMetadata;
    306 }
    307 
    308 // Sniffer
    309 
    310 bool SniffMidi(
    311         const sp<DataSource> &source, String8 *mimeType, float *confidence,
    312         sp<AMessage> *)
    313 {
    314     sp<MidiEngine> p = new MidiEngine(source, NULL, NULL);
    315     if (p->initCheck() == OK) {
    316         *mimeType = MEDIA_MIMETYPE_AUDIO_MIDI;
    317         *confidence = 0.8;
    318         ALOGV("SniffMidi: yes");
    319         return true;
    320     }
    321     ALOGV("SniffMidi: no");
    322     return false;
    323 
    324 }
    325 
    326 }  // namespace android
    327