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