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