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