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