1/*
2 * Copyright (C) 2010 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/** \brief libsndfile integration */
18
19#include "sles_allinclusive.h"
20
21
22/** \brief Called by SndFile.c:audioPlayerTransportUpdate after a play state change or seek,
23 *  and by IOutputMixExt::FillBuffer after each buffer is consumed.
24 */
25
26void SndFile_Callback(SLBufferQueueItf caller, void *pContext)
27{
28    CAudioPlayer *thisAP = (CAudioPlayer *) pContext;
29    object_lock_peek(&thisAP->mObject);
30    SLuint32 state = thisAP->mPlay.mState;
31    object_unlock_peek(&thisAP->mObject);
32    if (SL_PLAYSTATE_PLAYING != state) {
33        return;
34    }
35    struct SndFile *thiz = &thisAP->mSndFile;
36    SLresult result;
37    pthread_mutex_lock(&thiz->mMutex);
38    if (thiz->mEOF) {
39        pthread_mutex_unlock(&thiz->mMutex);
40        return;
41    }
42    short *pBuffer = &thiz->mBuffer[thiz->mWhich * SndFile_BUFSIZE];
43    if (++thiz->mWhich >= SndFile_NUMBUFS) {
44        thiz->mWhich = 0;
45    }
46    sf_count_t count;
47    count = sf_read_short(thiz->mSNDFILE, pBuffer, (sf_count_t) SndFile_BUFSIZE);
48    pthread_mutex_unlock(&thiz->mMutex);
49    bool headAtNewPos = false;
50    object_lock_exclusive(&thisAP->mObject);
51    slPlayCallback callback = thisAP->mPlay.mCallback;
52    void *context = thisAP->mPlay.mContext;
53    // make a copy of sample rate so we are absolutely sure we will not divide by zero
54    SLuint32 sampleRateMilliHz = thisAP->mSampleRateMilliHz;
55    if (UNKNOWN_SAMPLERATE != sampleRateMilliHz) {
56        // this will overflow after 49 days, but no fix possible as it's part of the API
57        thisAP->mPlay.mPosition = (SLuint32) (((long long) thisAP->mPlay.mFramesSinceLastSeek *
58            1000000LL) / sampleRateMilliHz) + thisAP->mPlay.mLastSeekPosition;
59        // make a good faith effort for the mean time between "head at new position" callbacks to
60        // occur at the requested update period, but there will be jitter
61        SLuint32 frameUpdatePeriod = thisAP->mPlay.mFrameUpdatePeriod;
62        if ((0 != frameUpdatePeriod) &&
63            (thisAP->mPlay.mFramesSincePositionUpdate >= frameUpdatePeriod) &&
64            (SL_PLAYEVENT_HEADATNEWPOS & thisAP->mPlay.mEventFlags)) {
65            // if we overrun a requested update period, then reset the clock modulo the
66            // update period so that it appears to the application as one or more lost callbacks,
67            // but no additional jitter
68            if ((thisAP->mPlay.mFramesSincePositionUpdate -= thisAP->mPlay.mFrameUpdatePeriod) >=
69                    frameUpdatePeriod) {
70                thisAP->mPlay.mFramesSincePositionUpdate %= frameUpdatePeriod;
71            }
72            headAtNewPos = true;
73        }
74    }
75    if (0 < count) {
76        object_unlock_exclusive(&thisAP->mObject);
77        SLuint32 size = (SLuint32) (count * sizeof(short));
78        result = IBufferQueue_Enqueue(caller, pBuffer, size);
79        // not much we can do if the Enqueue fails, so we'll just drop the decoded data
80        if (SL_RESULT_SUCCESS != result) {
81            SL_LOGE("enqueue failed 0x%x", result);
82        }
83    } else {
84        thisAP->mPlay.mState = SL_PLAYSTATE_PAUSED;
85        thiz->mEOF = SL_BOOLEAN_TRUE;
86        // this would result in a non-monotonically increasing position, so don't do it
87        // thisAP->mPlay.mPosition = thisAP->mPlay.mDuration;
88        object_unlock_exclusive_attributes(&thisAP->mObject, ATTR_TRANSPORT);
89    }
90    // callbacks are called with mutex unlocked
91    if (NULL != callback) {
92        if (headAtNewPos) {
93            (*callback)(&thisAP->mPlay.mItf, context, SL_PLAYEVENT_HEADATNEWPOS);
94        }
95    }
96}
97
98
99/** \brief Check whether the supplied libsndfile format is supported by us */
100
101SLboolean SndFile_IsSupported(const SF_INFO *sfinfo)
102{
103    switch (sfinfo->format & SF_FORMAT_TYPEMASK) {
104    case SF_FORMAT_WAV:
105        break;
106    default:
107        return SL_BOOLEAN_FALSE;
108    }
109    switch (sfinfo->format & SF_FORMAT_SUBMASK) {
110    case SF_FORMAT_PCM_U8:
111    case SF_FORMAT_PCM_16:
112        break;
113    default:
114        return SL_BOOLEAN_FALSE;
115    }
116    switch (sfinfo->samplerate) {
117    case 11025:
118    case 22050:
119    case 44100:
120        break;
121    default:
122        return SL_BOOLEAN_FALSE;
123    }
124    switch (sfinfo->channels) {
125    case 1:
126    case 2:
127        break;
128    default:
129        return SL_BOOLEAN_FALSE;
130    }
131    return SL_BOOLEAN_TRUE;
132}
133
134
135/** \brief Check whether the partially-constructed AudioPlayer is compatible with libsndfile */
136
137SLresult SndFile_checkAudioPlayerSourceSink(CAudioPlayer *thiz)
138{
139    const SLDataSource *pAudioSrc = &thiz->mDataSource.u.mSource;
140    SLuint32 locatorType = *(SLuint32 *)pAudioSrc->pLocator;
141    SLuint32 formatType = *(SLuint32 *)pAudioSrc->pFormat;
142    switch (locatorType) {
143    case SL_DATALOCATOR_BUFFERQUEUE:
144#ifdef ANDROID
145    case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
146#endif
147        break;
148    case SL_DATALOCATOR_URI:
149        {
150        SLDataLocator_URI *dl_uri = (SLDataLocator_URI *) pAudioSrc->pLocator;
151        SLchar *uri = dl_uri->URI;
152        if (NULL == uri) {
153            return SL_RESULT_PARAMETER_INVALID;
154        }
155        if (!strncmp((const char *) uri, "file:///", 8)) {
156            uri += 8;
157        }
158        switch (formatType) {
159        case SL_DATAFORMAT_NULL:    // OK to omit the data format
160        case SL_DATAFORMAT_MIME:    // we ignore a MIME type if specified
161            break;
162        case SL_DATAFORMAT_PCM:
163        case XA_DATAFORMAT_RAWIMAGE:
164            return SL_RESULT_CONTENT_UNSUPPORTED;
165        default:
166            // an invalid data format is detected earlier during the deep copy
167            assert(false);
168            return SL_RESULT_INTERNAL_ERROR;
169        }
170        thiz->mSndFile.mPathname = uri;
171        thiz->mBufferQueue.mNumBuffers = SndFile_NUMBUFS;
172        }
173        break;
174    default:
175        return SL_RESULT_CONTENT_UNSUPPORTED;
176    }
177    thiz->mSndFile.mWhich = 0;
178    thiz->mSndFile.mSNDFILE = NULL;
179    // thiz->mSndFile.mMutex is initialized only when there is a valid mSNDFILE
180    thiz->mSndFile.mEOF = SL_BOOLEAN_FALSE;
181
182    return SL_RESULT_SUCCESS;
183}
184
185
186/** \brief Called with mutex unlocked for marker and position updates, and play state change */
187
188void audioPlayerTransportUpdate(CAudioPlayer *audioPlayer)
189{
190
191    if (NULL != audioPlayer->mSndFile.mSNDFILE) {
192
193        object_lock_exclusive(&audioPlayer->mObject);
194        SLboolean empty = 0 == audioPlayer->mBufferQueue.mState.count;
195        // FIXME a made-up number that should depend on player state and prefetch status
196        audioPlayer->mPrefetchStatus.mLevel = 1000;
197        SLmillisecond pos = audioPlayer->mSeek.mPos;
198        if (SL_TIME_UNKNOWN != pos) {
199            audioPlayer->mSeek.mPos = SL_TIME_UNKNOWN;
200            // trim seek position to the current known duration
201            if (pos > audioPlayer->mPlay.mDuration) {
202                pos = audioPlayer->mPlay.mDuration;
203            }
204            audioPlayer->mPlay.mLastSeekPosition = pos;
205            audioPlayer->mPlay.mFramesSinceLastSeek = 0;
206            // seek postpones the next head at new position callback
207            audioPlayer->mPlay.mFramesSincePositionUpdate = 0;
208        }
209        object_unlock_exclusive(&audioPlayer->mObject);
210
211        if (SL_TIME_UNKNOWN != pos) {
212
213            // discard any enqueued buffers for the old position
214            IBufferQueue_Clear(&audioPlayer->mBufferQueue.mItf);
215            empty = SL_BOOLEAN_TRUE;
216
217            pthread_mutex_lock(&audioPlayer->mSndFile.mMutex);
218            // FIXME why void?
219            (void) sf_seek(audioPlayer->mSndFile.mSNDFILE, (sf_count_t) (((long long) pos *
220                audioPlayer->mSndFile.mSfInfo.samplerate) / 1000LL), SEEK_SET);
221            audioPlayer->mSndFile.mEOF = SL_BOOLEAN_FALSE;
222            audioPlayer->mSndFile.mWhich = 0;
223            pthread_mutex_unlock(&audioPlayer->mSndFile.mMutex);
224
225        }
226
227        // FIXME only on seek or play state change (STOPPED, PAUSED) -> PLAYING
228        if (empty) {
229            SndFile_Callback(&audioPlayer->mBufferQueue.mItf, audioPlayer);
230        }
231
232    }
233
234}
235
236
237/** \brief Called by CAudioPlayer_Realize */
238
239SLresult SndFile_Realize(CAudioPlayer *thiz)
240{
241    SLresult result = SL_RESULT_SUCCESS;
242    if (NULL != thiz->mSndFile.mPathname) {
243        thiz->mSndFile.mSfInfo.format = 0;
244        thiz->mSndFile.mSNDFILE = sf_open(
245            (const char *) thiz->mSndFile.mPathname, SFM_READ, &thiz->mSndFile.mSfInfo);
246        if (NULL == thiz->mSndFile.mSNDFILE) {
247            result = SL_RESULT_CONTENT_NOT_FOUND;
248        } else if (!SndFile_IsSupported(&thiz->mSndFile.mSfInfo)) {
249            sf_close(thiz->mSndFile.mSNDFILE);
250            thiz->mSndFile.mSNDFILE = NULL;
251            result = SL_RESULT_CONTENT_UNSUPPORTED;
252        } else {
253            int ok;
254            ok = pthread_mutex_init(&thiz->mSndFile.mMutex, (const pthread_mutexattr_t *) NULL);
255            assert(0 == ok);
256            SLBufferQueueItf bufferQueue = &thiz->mBufferQueue.mItf;
257            IBufferQueue *thisBQ = (IBufferQueue *) bufferQueue;
258            IBufferQueue_RegisterCallback(&thisBQ->mItf, SndFile_Callback, thiz);
259            thiz->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_SUFFICIENTDATA;
260            // this is the initial duration; will update when a new maximum position is detected
261            thiz->mPlay.mDuration = (SLmillisecond) (((long long) thiz->mSndFile.mSfInfo.frames *
262                1000LL) / thiz->mSndFile.mSfInfo.samplerate);
263            thiz->mNumChannels = thiz->mSndFile.mSfInfo.channels;
264            thiz->mSampleRateMilliHz = thiz->mSndFile.mSfInfo.samplerate * 1000;
265#ifdef USE_OUTPUTMIXEXT
266            thiz->mPlay.mFrameUpdatePeriod = ((long long) thiz->mPlay.mPositionUpdatePeriod *
267                (long long) thiz->mSampleRateMilliHz) / 1000000LL;
268#endif
269        }
270    }
271    return result;
272}
273
274
275/** \brief Called by CAudioPlayer_Destroy */
276
277void SndFile_Destroy(CAudioPlayer *thiz)
278{
279    if (NULL != thiz->mSndFile.mSNDFILE) {
280        sf_close(thiz->mSndFile.mSNDFILE);
281        thiz->mSndFile.mSNDFILE = NULL;
282        int ok;
283        ok = pthread_mutex_destroy(&thiz->mSndFile.mMutex);
284        assert(0 == ok);
285    }
286}
287