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 trackMetadata->setInt32(kKeyPcmEncoding, kAudioEncodingPcm16bit); 182 } 183 mIsInitialized = true; 184} 185 186MidiEngine::~MidiEngine() { 187 if (mEasHandle) { 188 EAS_CloseFile(mEasData, mEasHandle); 189 } 190 if (mEasData) { 191 EAS_Shutdown(mEasData); 192 } 193 delete mGroup; 194 195} 196 197status_t MidiEngine::initCheck() { 198 return mIsInitialized ? OK : UNKNOWN_ERROR; 199} 200 201status_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 214status_t MidiEngine::releaseBuffers() { 215 delete mGroup; 216 mGroup = NULL; 217 return OK; 218} 219 220status_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 226MediaBuffer* 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 263MidiExtractor::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 275MidiExtractor::~MidiExtractor() 276{ 277 ALOGV("MidiExtractor dtor"); 278} 279 280size_t MidiExtractor::countTracks() 281{ 282 return mInitCheck == OK ? 1 : 0; 283} 284 285sp<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 293sp<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 302sp<MetaData> MidiExtractor::getMetaData() 303{ 304 ALOGV("MidiExtractor::getMetaData"); 305 return mFileMetadata; 306} 307 308// Sniffer 309 310bool 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