MidiExtractor.cpp revision faf4efc6a41e88adf85f76f48f020a6d681f5ff1
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    }
182    mIsInitialized = true;
183}
184
185MidiEngine::~MidiEngine() {
186    if (mEasHandle) {
187        EAS_CloseFile(mEasData, mEasHandle);
188    }
189    if (mEasData) {
190        EAS_Shutdown(mEasData);
191    }
192    delete mGroup;
193
194}
195
196status_t MidiEngine::initCheck() {
197    return mIsInitialized ? OK : UNKNOWN_ERROR;
198}
199
200status_t MidiEngine::allocateBuffers() {
201    // select reverb preset and enable
202    EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET, EAS_PARAM_REVERB_CHAMBER);
203    EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS, EAS_FALSE);
204
205    mGroup = new MediaBufferGroup;
206    int bufsize = sizeof(EAS_PCM)
207            * mEasConfig->mixBufferSize * mEasConfig->numChannels * NUM_COMBINE_BUFFERS;
208    ALOGV("using %d byte buffer", bufsize);
209    mGroup->add_buffer(new MediaBuffer(bufsize));
210    return OK;
211}
212
213status_t MidiEngine::releaseBuffers() {
214    delete mGroup;
215    mGroup = NULL;
216    return OK;
217}
218
219status_t MidiEngine::seekTo(int64_t positionUs) {
220    ALOGV("seekTo %lld", positionUs);
221    EAS_RESULT result = EAS_Locate(mEasData, mEasHandle, positionUs / 1000, false);
222    return result == EAS_SUCCESS ? OK : UNKNOWN_ERROR;
223}
224
225MediaBuffer* MidiEngine::readBuffer() {
226    EAS_STATE state;
227    EAS_State(mEasData, mEasHandle, &state);
228    if ((state == EAS_STATE_STOPPED) || (state == EAS_STATE_ERROR)) {
229        return NULL;
230    }
231    MediaBuffer *buffer;
232    status_t err = mGroup->acquire_buffer(&buffer);
233    if (err != OK) {
234        ALOGE("readBuffer: no buffer");
235        return NULL;
236    }
237    EAS_I32 timeMs;
238    EAS_GetLocation(mEasData, mEasHandle, &timeMs);
239    int64_t timeUs = 1000ll * timeMs;
240    buffer->meta_data()->setInt64(kKeyTime, timeUs);
241
242    EAS_PCM* p = (EAS_PCM*) buffer->data();
243    int numBytesOutput = 0;
244    for (int i = 0; i < NUM_COMBINE_BUFFERS; i++) {
245        EAS_I32 numRendered;
246        EAS_RESULT result = EAS_Render(mEasData, p, mEasConfig->mixBufferSize, &numRendered);
247        if (result != EAS_SUCCESS) {
248            ALOGE("EAS_Render returned %ld", result);
249            break;
250        }
251        p += numRendered * mEasConfig->numChannels;
252        numBytesOutput += numRendered * mEasConfig->numChannels * sizeof(EAS_PCM);
253    }
254    buffer->set_range(0, numBytesOutput);
255    ALOGV("readBuffer: returning %zd in buffer %p", buffer->range_length(), buffer);
256    return buffer;
257}
258
259
260// MidiExtractor
261
262MidiExtractor::MidiExtractor(
263        const sp<DataSource> &dataSource)
264    : mDataSource(dataSource),
265      mInitCheck(false)
266{
267    ALOGV("MidiExtractor ctor");
268    mFileMetadata = new MetaData;
269    mTrackMetadata = new MetaData;
270    mEngine = new MidiEngine(mDataSource, mFileMetadata, mTrackMetadata);
271    mInitCheck = mEngine->initCheck();
272}
273
274MidiExtractor::~MidiExtractor()
275{
276    ALOGV("MidiExtractor dtor");
277}
278
279size_t MidiExtractor::countTracks()
280{
281    return mInitCheck == OK ? 1 : 0;
282}
283
284sp<MediaSource> MidiExtractor::getTrack(size_t index)
285{
286    if (mInitCheck != OK || index > 0) {
287        return NULL;
288    }
289    return new MidiSource(mEngine, mTrackMetadata);
290}
291
292sp<MetaData> MidiExtractor::getTrackMetaData(
293        size_t index, uint32_t /* flags */) {
294    ALOGV("MidiExtractor::getTrackMetaData");
295    if (mInitCheck != OK || index > 0) {
296        return NULL;
297    }
298    return mTrackMetadata;
299}
300
301sp<MetaData> MidiExtractor::getMetaData()
302{
303    ALOGV("MidiExtractor::getMetaData");
304    return mFileMetadata;
305}
306
307// Sniffer
308
309bool SniffMidi(
310        const sp<DataSource> &source, String8 *mimeType, float *confidence,
311        sp<AMessage> *)
312{
313    sp<MidiEngine> p = new MidiEngine(source, NULL, NULL);
314    if (p->initCheck() == OK) {
315        *mimeType = MEDIA_MIMETYPE_AUDIO_MIDI;
316        *confidence = 0.8;
317        ALOGV("SniffMidi: yes");
318        return true;
319    }
320    ALOGV("SniffMidi: no");
321    return false;
322
323}
324
325}  // namespace android
326