SndFile.c revision 8c065779232fdd89abace68d2fc7bea786a010d7
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#ifdef USE_SNDFILE
22
23
24/** \brief Called by SndFile.c:audioPlayerTransportUpdate after a play state change or seek,
25 *  and by IOutputMixExt::FillBuffer after each buffer is consumed.
26 */
27
28void SndFile_Callback(SLBufferQueueItf caller, void *pContext)
29{
30    CAudioPlayer *thisAP = (CAudioPlayer *) pContext;
31    object_lock_peek(&thisAP->mObject);
32    SLuint32 state = thisAP->mPlay.mState;
33    object_unlock_peek(&thisAP->mObject);
34    // FIXME should not muck around directly at this low level
35    if (SL_PLAYSTATE_PLAYING != state) {
36        return;
37    }
38    struct SndFile *this = &thisAP->mSndFile;
39    SLresult result;
40    pthread_mutex_lock(&this->mMutex);
41    if (this->mEOF) {
42        pthread_mutex_unlock(&this->mMutex);
43        return;
44    }
45    short *pBuffer = &this->mBuffer[this->mWhich * SndFile_BUFSIZE];
46    if (++this->mWhich >= SndFile_NUMBUFS) {
47        this->mWhich = 0;
48    }
49    sf_count_t count;
50    count = sf_read_short(this->mSNDFILE, pBuffer, (sf_count_t) SndFile_BUFSIZE);
51    pthread_mutex_unlock(&this->mMutex);
52    bool headAtNewPos = false;
53    object_lock_exclusive(&thisAP->mObject);
54    slPlayCallback callback = thisAP->mPlay.mCallback;
55    void *context = thisAP->mPlay.mContext;
56    // make a copy of sample rate so we are absolutely sure we will not divide by zero
57    SLuint32 sampleRateMilliHz = thisAP->mSampleRateMilliHz;
58    if (0 != sampleRateMilliHz) {
59        // this will overflow after 49 days, but no fix possible as it's part of the API
60        thisAP->mPlay.mPosition = (SLuint32) (((long long) thisAP->mPlay.mFramesSinceLastSeek *
61            1000000LL) / sampleRateMilliHz) + thisAP->mPlay.mLastSeekPosition;
62        // make a good faith effort for the mean time between "head at new position" callbacks to
63        // occur at the requested update period, but there will be jitter
64        SLuint32 frameUpdatePeriod = thisAP->mPlay.mFrameUpdatePeriod;
65        if ((0 != frameUpdatePeriod) &&
66            (thisAP->mPlay.mFramesSincePositionUpdate >= frameUpdatePeriod) &&
67            (SL_PLAYEVENT_HEADATNEWPOS & thisAP->mPlay.mEventFlags)) {
68            // if we overrun a requested update period, then reset the clock modulo the
69            // update period so that it appears to the application as one or more lost callbacks,
70            // but no additional jitter
71            if ((thisAP->mPlay.mFramesSincePositionUpdate -= thisAP->mPlay.mFrameUpdatePeriod) >=
72                    frameUpdatePeriod) {
73                thisAP->mPlay.mFramesSincePositionUpdate %= frameUpdatePeriod;
74            }
75            headAtNewPos = true;
76        }
77    }
78    if (0 < count) {
79        object_unlock_exclusive(&thisAP->mObject);
80        SLuint32 size = (SLuint32) (count * sizeof(short));
81        result = IBufferQueue_Enqueue(caller, pBuffer, size);
82        // not much we can do if the Enqueue fails, so we'll just drop the decoded data
83        if (SL_RESULT_SUCCESS != result) {
84            SL_LOGE("enqueue failed 0x%x", (unsigned) result);
85        }
86    } else {
87        // FIXME This is really hosed, you can't do this anymore!
88        // FIXME Need a state PAUSE_WHEN_EMPTY
89        // Should not pause yet - we just ran out of new data to enqueue,
90        // but there may still be (partially) full buffers in the queue.
91        thisAP->mPlay.mState = SL_PLAYSTATE_PAUSED;
92        this->mEOF = SL_BOOLEAN_TRUE;
93        // this would result in a non-monotonically increasing position, so don't do it
94        // thisAP->mPlay.mPosition = thisAP->mPlay.mDuration;
95        object_unlock_exclusive_attributes(&thisAP->mObject, ATTR_TRANSPORT);
96    }
97    // callbacks are called with mutex unlocked
98    if (NULL != callback) {
99        if (headAtNewPos) {
100            (*callback)(&thisAP->mPlay.mItf, context, SL_PLAYEVENT_HEADATNEWPOS);
101        }
102    }
103}
104
105
106/** \brief Check whether the supplied libsndfile format is supported by us */
107
108SLboolean SndFile_IsSupported(const SF_INFO *sfinfo)
109{
110    switch (sfinfo->format & SF_FORMAT_TYPEMASK) {
111    case SF_FORMAT_WAV:
112        break;
113    default:
114        return SL_BOOLEAN_FALSE;
115    }
116    switch (sfinfo->format & SF_FORMAT_SUBMASK) {
117    case SF_FORMAT_PCM_U8:
118    case SF_FORMAT_PCM_16:
119        break;
120    default:
121        return SL_BOOLEAN_FALSE;
122    }
123    switch (sfinfo->samplerate) {
124    case 11025:
125    case 22050:
126    case 44100:
127        break;
128    default:
129        return SL_BOOLEAN_FALSE;
130    }
131    switch (sfinfo->channels) {
132    case 1:
133    case 2:
134        break;
135    default:
136        return SL_BOOLEAN_FALSE;
137    }
138    return SL_BOOLEAN_TRUE;
139}
140
141
142/** \brief Check whether the partially-constructed AudioPlayer is compatible with libsndfile */
143
144SLresult SndFile_checkAudioPlayerSourceSink(CAudioPlayer *this)
145{
146    const SLDataSource *pAudioSrc = &this->mDataSource.u.mSource;
147    SLuint32 locatorType = *(SLuint32 *)pAudioSrc->pLocator;
148    SLuint32 formatType = *(SLuint32 *)pAudioSrc->pFormat;
149    switch (locatorType) {
150    case SL_DATALOCATOR_BUFFERQUEUE:
151        break;
152    case SL_DATALOCATOR_URI:
153        {
154        SLDataLocator_URI *dl_uri = (SLDataLocator_URI *) pAudioSrc->pLocator;
155        SLchar *uri = dl_uri->URI;
156        if (NULL == uri) {
157            return SL_RESULT_PARAMETER_INVALID;
158        }
159        if (!strncmp((const char *) uri, "file:///", 8)) {
160            uri += 8;
161        }
162        switch (formatType) {
163        case SL_DATAFORMAT_NULL:    // OK to omit the data format
164        case SL_DATAFORMAT_MIME:    // we ignore a MIME type if specified
165            break;
166        default:
167            return SL_RESULT_CONTENT_UNSUPPORTED;
168        }
169        this->mSndFile.mPathname = uri;
170        this->mBufferQueue.mNumBuffers = SndFile_NUMBUFS;
171        }
172        break;
173    default:
174        return SL_RESULT_CONTENT_UNSUPPORTED;
175    }
176    this->mSndFile.mWhich = 0;
177    this->mSndFile.mSNDFILE = NULL;
178    // this->mSndFile.mMutex is initialized only when there is a valid mSNDFILE
179    this->mSndFile.mEOF = SL_BOOLEAN_FALSE;
180
181    return SL_RESULT_SUCCESS;
182}
183
184
185/** \brief Called with mutex unlocked for marker and position updates, and play state change */
186
187void audioPlayerTransportUpdate(CAudioPlayer *audioPlayer)
188{
189    // FIXME should use two separate hooks since we have separate attributes TRANSPORT and POSITION
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#endif // USE_SNDFILE
237