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 
MidiSource(const sp<MidiEngine> & engine,const sp<MetaData> & trackMetadata)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 
~MidiSource()82 MidiSource::~MidiSource()
83 {
84     ALOGV("MidiSource dtor");
85     if (mStarted) {
86         stop();
87     }
88 }
89 
start(MetaData *)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 
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()111 sp<MetaData> MidiSource::getFormat()
112 {
113     return mTrackMetadata;
114 }
115 
read(MediaBuffer ** outBuffer,const ReadOptions * options)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 
init()136 status_t MidiSource::init()
137 {
138     ALOGV("MidiSource::init");
139     return OK;
140 }
141 
142 // MidiEngine
143 
MidiEngine(const sp<DataSource> & dataSource,const sp<MetaData> & fileMetadata,const sp<MetaData> & trackMetadata)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 
~MidiEngine()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 
initCheck()197 status_t MidiEngine::initCheck() {
198     return mIsInitialized ? OK : UNKNOWN_ERROR;
199 }
200 
allocateBuffers()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 
releaseBuffers()214 status_t MidiEngine::releaseBuffers() {
215     delete mGroup;
216     mGroup = NULL;
217     return OK;
218 }
219 
seekTo(int64_t positionUs)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 
readBuffer()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 
MidiExtractor(const sp<DataSource> & dataSource)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 
~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 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 
getTrackMetaData(size_t index,uint32_t)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 
getMetaData()302 sp<MetaData> MidiExtractor::getMetaData()
303 {
304     ALOGV("MidiExtractor::getMetaData");
305     return mFileMetadata;
306 }
307 
308 // Sniffer
309 
SniffMidi(const sp<DataSource> & source,String8 * mimeType,float * confidence,sp<AMessage> *)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