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