• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MidiSource(MidiEngine & engine,MetaDataBase & trackMetadata)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 
~MidiSource()82 MidiSource::~MidiSource()
83 {
84     ALOGV("MidiSource dtor");
85     if (mStarted) {
86         stop();
87     }
88 }
89 
start(MetaDataBase *)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 
stop()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 
getFormat(MetaDataBase & meta)111 status_t MidiSource::getFormat(MetaDataBase &meta)
112 {
113     meta = mTrackMetadata;
114     return OK;
115 }
116 
read(MediaBufferBase ** outBuffer,const ReadOptions * options)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 
init()137 status_t MidiSource::init()
138 {
139     ALOGV("MidiSource::init");
140     return OK;
141 }
142 
143 // MidiEngine
144 
MidiEngine(DataSourceBase * dataSource,MetaDataBase * fileMetadata,MetaDataBase * trackMetadata)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 
~MidiEngine()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 
initCheck()198 status_t MidiEngine::initCheck() {
199     return mIsInitialized ? OK : UNKNOWN_ERROR;
200 }
201 
allocateBuffers()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 
releaseBuffers()215 status_t MidiEngine::releaseBuffers() {
216     delete mGroup;
217     mGroup = NULL;
218     return OK;
219 }
220 
seekTo(int64_t positionUs)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 
readBuffer()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, numBytesOutput = %d", result, numBytesOutput);
251             buffer->release();
252             return NULL; // Stop processing to prevent infinite loops.
253         }
254         p += numRendered * mEasConfig->numChannels;
255         numBytesOutput += numRendered * mEasConfig->numChannels * sizeof(EAS_PCM);
256     }
257     buffer->set_range(0, numBytesOutput);
258     ALOGV("readBuffer: returning %zd in buffer %p", buffer->range_length(), buffer);
259     return buffer;
260 }
261 
262 
263 // MidiExtractor
264 
MidiExtractor(DataSourceBase * dataSource)265 MidiExtractor::MidiExtractor(
266         DataSourceBase *dataSource)
267     : mDataSource(dataSource),
268       mInitCheck(false)
269 {
270     ALOGV("MidiExtractor ctor");
271     mEngine = new MidiEngine(mDataSource, &mFileMetadata, &mTrackMetadata);
272     mInitCheck = mEngine->initCheck();
273 }
274 
~MidiExtractor()275 MidiExtractor::~MidiExtractor()
276 {
277     ALOGV("MidiExtractor dtor");
278 }
279 
countTracks()280 size_t MidiExtractor::countTracks()
281 {
282     return mInitCheck == OK ? 1 : 0;
283 }
284 
getTrack(size_t index)285 MediaTrack *MidiExtractor::getTrack(size_t index)
286 {
287     if (mInitCheck != OK || index > 0) {
288         return NULL;
289     }
290     return new MidiSource(*mEngine, mTrackMetadata);
291 }
292 
getTrackMetaData(MetaDataBase & meta,size_t index,uint32_t)293 status_t MidiExtractor::getTrackMetaData(
294         MetaDataBase &meta,
295         size_t index, uint32_t /* flags */) {
296     ALOGV("MidiExtractor::getTrackMetaData");
297     if (mInitCheck != OK || index > 0) {
298         return UNKNOWN_ERROR;
299     }
300     meta = mTrackMetadata;
301     return OK;
302 }
303 
getMetaData(MetaDataBase & meta)304 status_t MidiExtractor::getMetaData(MetaDataBase &meta)
305 {
306     ALOGV("MidiExtractor::getMetaData");
307     meta = mFileMetadata;
308     return OK;
309 }
310 
311 // Sniffer
312 
SniffMidi(DataSourceBase * source,float * confidence)313 bool SniffMidi(DataSourceBase *source, float *confidence)
314 {
315     MidiEngine p(source, NULL, NULL);
316     if (p.initCheck() == OK) {
317         *confidence = 0.8;
318         ALOGV("SniffMidi: yes");
319         return true;
320     }
321     ALOGV("SniffMidi: no");
322     return false;
323 
324 }
325 
326 extern "C" {
327 // This is the only symbol that needs to be exported
328 __attribute__ ((visibility ("default")))
GETEXTRACTORDEF()329 MediaExtractor::ExtractorDef GETEXTRACTORDEF() {
330     return {
331         MediaExtractor::EXTRACTORDEF_VERSION,
332         UUID("ef6cca0a-f8a2-43e6-ba5f-dfcd7c9a7ef2"),
333         1,
334         "MIDI Extractor",
335         [](
336                 DataSourceBase *source,
337                 float *confidence,
338                 void **,
339                 MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
340             if (SniffMidi(source, confidence)) {
341                 return [](
342                         DataSourceBase *source,
343                         void *) -> MediaExtractor* {
344                     return new MidiExtractor(source);};
345             }
346             return NULL;
347         }
348     };
349 }
350 
351 } // extern "C"
352 
353 }  // namespace android
354