SndFile.c revision 3d81b8ca5d3cee893672beb76e00849d4f3fa8b8
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/* libsndfile integration */
18
19#include "sles_allinclusive.h"
20
21#ifdef USE_SNDFILE
22
23void SndFile_Callback(SLBufferQueueItf caller, void *pContext)
24{
25    CAudioPlayer *thisAP = (CAudioPlayer *) pContext;
26    object_lock_peek(&thisAP->mObject);
27    SLuint32 state = thisAP->mPlay.mState;
28    object_unlock_peek(&thisAP->mObject);
29    // FIXME should not muck around directly at this low level
30    if (SL_PLAYSTATE_PLAYING != state)
31        return;
32    struct SndFile *this = &thisAP->mSndFile;
33    SLresult result;
34    pthread_mutex_lock(&this->mMutex);
35    if ((NULL != this->mRetryBuffer) && (0 < this->mRetrySize)) {
36        result = (*caller)->Enqueue(caller, this->mRetryBuffer, this->mRetrySize);
37        if (SL_RESULT_BUFFER_INSUFFICIENT == result) {
38            pthread_mutex_unlock(&this->mMutex);
39            return;     // what, again?
40        }
41        assert(SL_RESULT_SUCCESS == result);
42        this->mRetryBuffer = NULL;
43        this->mRetrySize = 0;
44        pthread_mutex_unlock(&this->mMutex);
45        return;
46    }
47    if (this->mEOF) {
48        pthread_mutex_unlock(&this->mMutex);
49        return;
50    }
51    short *pBuffer = &this->mBuffer[this->mWhich * SndFile_BUFSIZE];
52    if (++this->mWhich >= SndFile_NUMBUFS)
53        this->mWhich = 0;
54    sf_count_t count;
55    count = sf_read_short(this->mSNDFILE, pBuffer, (sf_count_t) SndFile_BUFSIZE);
56    if (0 >= count)
57        this->mEOF = SL_BOOLEAN_TRUE;
58    pthread_mutex_unlock(&this->mMutex);
59    if (0 < count) {
60        SLuint32 size = (SLuint32) (count * sizeof(short));
61        result = (*caller)->Enqueue(caller, pBuffer, size);
62        // this should not happen, but if it does, who will call us to kick off again?
63        if (SL_RESULT_BUFFER_INSUFFICIENT == result) {
64            this->mRetryBuffer = pBuffer;
65            this->mRetrySize = size;
66            return;
67        }
68        assert(SL_RESULT_SUCCESS == result);
69    } else {
70        object_lock_exclusive(&thisAP->mObject);
71        // FIXME This is really hosed, you can't do this anymore!
72        // FIXME Need a state PAUSE_WHEN_EMPTY
73        // Should not pause yet - we just ran out of new data to enqueue,
74        // but there may still be (partially) full buffers in the queue.
75        thisAP->mPlay.mState = SL_PLAYSTATE_PAUSED;
76        thisAP->mPlay.mPosition = thisAP->mPlay.mDuration;
77        object_unlock_exclusive_attributes(&thisAP->mObject, ATTR_TRANSPORT);
78    }
79}
80
81SLboolean SndFile_IsSupported(const SF_INFO *sfinfo)
82{
83    switch (sfinfo->format & SF_FORMAT_TYPEMASK) {
84    case SF_FORMAT_WAV:
85        break;
86    default:
87        return SL_BOOLEAN_FALSE;
88    }
89    switch (sfinfo->format & SF_FORMAT_SUBMASK) {
90    case SF_FORMAT_PCM_U8:
91    case SF_FORMAT_PCM_16:
92        break;
93    default:
94        return SL_BOOLEAN_FALSE;
95    }
96    switch (sfinfo->samplerate) {
97    case 11025:
98    case 22050:
99    case 44100:
100        break;
101    default:
102        return SL_BOOLEAN_FALSE;
103    }
104    switch (sfinfo->channels) {
105    case 1:
106    case 2:
107        break;
108    default:
109        return SL_BOOLEAN_FALSE;
110    }
111    return SL_BOOLEAN_TRUE;
112}
113
114SLresult SndFile_checkAudioPlayerSourceSink(CAudioPlayer *this)
115{
116    const SLDataSource *pAudioSrc = &this->mDataSource.u.mSource;
117    SLuint32 locatorType = *(SLuint32 *)pAudioSrc->pLocator;
118    SLuint32 formatType = *(SLuint32 *)pAudioSrc->pFormat;
119    switch (locatorType) {
120    case SL_DATALOCATOR_BUFFERQUEUE:
121        break;
122    case SL_DATALOCATOR_URI:
123        {
124        SLDataLocator_URI *dl_uri = (SLDataLocator_URI *) pAudioSrc->pLocator;
125        SLchar *uri = dl_uri->URI;
126        if (NULL == uri)
127            return SL_RESULT_PARAMETER_INVALID;
128        if (!strncmp((const char *) uri, "file:///", 8))
129            uri += 8;
130        switch (formatType) {
131        case SL_DATAFORMAT_NULL:    // OK to omit the data format
132        case SL_DATAFORMAT_MIME:    // we ignore a MIME type if specified
133            break;
134        default:
135            return SL_RESULT_CONTENT_UNSUPPORTED;
136        }
137        this->mSndFile.mPathname = uri;
138        this->mBufferQueue.mNumBuffers = SndFile_NUMBUFS;
139        }
140        break;
141    default:
142        return SL_RESULT_CONTENT_UNSUPPORTED;
143    }
144    const SLDataSink *pAudioSnk = &this->mDataSink.u.mSink;
145    this->mSndFile.mWhich = 0;
146    this->mSndFile.mSNDFILE = NULL;
147    // this->mSndFile.mMutex is initialized only when there is a valid mSNDFILE
148    this->mSndFile.mEOF = SL_BOOLEAN_FALSE;
149    this->mSndFile.mRetryBuffer = NULL;
150    this->mSndFile.mRetrySize = 0;
151    if (SL_DATALOCATOR_OUTPUTMIX == ((SLDataLocator_OutputMix *)pAudioSnk->pLocator)->locatorType) {
152         // FIXME possible race between the earlier check and here - should atomically link these
153         this->mEffectSend.mOutputMix = ((SLDataLocator_OutputMix *)pAudioSnk->pLocator)->outputMix;
154    }
155
156    return SL_RESULT_SUCCESS;
157}
158
159// called with mutex unlocked for marker and position updates, and play state change
160// FIXME should use two separate hooks since we have separate attributes TRANSPORT and POSITION
161
162void audioPlayerTransportUpdate(CAudioPlayer *audioPlayer)
163{
164
165    if (NULL != audioPlayer->mSndFile.mSNDFILE) {
166
167        object_lock_exclusive(&audioPlayer->mObject);
168        SLmillisecond pos = audioPlayer->mSeek.mPos;
169        audioPlayer->mSeek.mPos = SL_TIME_UNKNOWN;
170        SLboolean empty = 0 == audioPlayer->mBufferQueue.mState.count;
171        // FIXME a made-up number that should depend on player state and prefetch status
172        audioPlayer->mPrefetchStatus.mLevel = 1000;
173        object_unlock_exclusive(&audioPlayer->mObject);
174
175        if (SL_TIME_UNKNOWN != pos) {
176
177            // discard any enqueued buffers for the old position
178            (*&audioPlayer->mBufferQueue.mItf)->Clear(&audioPlayer->mBufferQueue.mItf);
179            empty = SL_BOOLEAN_TRUE;
180
181            pthread_mutex_lock(&audioPlayer->mSndFile.mMutex);
182            (void) sf_seek(audioPlayer->mSndFile.mSNDFILE, (sf_count_t) (((long long) pos *
183                audioPlayer->mSndFile.mSfInfo.samplerate) / 1000LL), SEEK_SET);
184            audioPlayer->mSndFile.mEOF = SL_BOOLEAN_FALSE;
185            audioPlayer->mSndFile.mRetryBuffer = NULL;
186            audioPlayer->mSndFile.mRetrySize = 0;
187            audioPlayer->mSndFile.mWhich = 0;
188            pthread_mutex_unlock(&audioPlayer->mSndFile.mMutex);
189
190        }
191
192        // FIXME only on seek or play state change (STOPPED, PAUSED) -> PLAYING
193        if (empty) {
194            SndFile_Callback(&audioPlayer->mBufferQueue.mItf, audioPlayer);
195        }
196
197    }
198
199}
200
201#endif // USE_SNDFILE
202