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