MidiExtractor.cpp revision faf4efc6a41e88adf85f76f48f020a6d681f5ff1
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 31namespace android { 32 33// how many Sonivox output buffers to aggregate into one MediaBuffer 34static const int NUM_COMBINE_BUFFERS = 4; 35 36class MidiSource : public MediaSource { 37 38public: 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 50protected: 51 virtual ~MidiSource(); 52 53private: 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 70MidiSource::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 82MidiSource::~MidiSource() 83{ 84 ALOGV("MidiSource dtor"); 85 if (mStarted) { 86 stop(); 87 } 88} 89 90status_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 100status_t MidiSource::stop() 101{ 102 ALOGV("MidiSource::stop"); 103 104 CHECK(mStarted); 105 mStarted = false; 106 mEngine->releaseBuffers(); 107 108 return OK; 109} 110 111sp<MetaData> MidiSource::getFormat() 112{ 113 return mTrackMetadata; 114} 115 116status_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 136status_t MidiSource::init() 137{ 138 ALOGV("MidiSource::init"); 139 return OK; 140} 141 142// MidiEngine 143 144MidiEngine::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 185MidiEngine::~MidiEngine() { 186 if (mEasHandle) { 187 EAS_CloseFile(mEasData, mEasHandle); 188 } 189 if (mEasData) { 190 EAS_Shutdown(mEasData); 191 } 192 delete mGroup; 193 194} 195 196status_t MidiEngine::initCheck() { 197 return mIsInitialized ? OK : UNKNOWN_ERROR; 198} 199 200status_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 213status_t MidiEngine::releaseBuffers() { 214 delete mGroup; 215 mGroup = NULL; 216 return OK; 217} 218 219status_t MidiEngine::seekTo(int64_t positionUs) { 220 ALOGV("seekTo %lld", positionUs); 221 EAS_RESULT result = EAS_Locate(mEasData, mEasHandle, positionUs / 1000, false); 222 return result == EAS_SUCCESS ? OK : UNKNOWN_ERROR; 223} 224 225MediaBuffer* 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 262MidiExtractor::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 274MidiExtractor::~MidiExtractor() 275{ 276 ALOGV("MidiExtractor dtor"); 277} 278 279size_t MidiExtractor::countTracks() 280{ 281 return mInitCheck == OK ? 1 : 0; 282} 283 284sp<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 292sp<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 301sp<MetaData> MidiExtractor::getMetaData() 302{ 303 ALOGV("MidiExtractor::getMetaData"); 304 return mFileMetadata; 305} 306 307// Sniffer 308 309bool 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