IOutputMixExt.c revision d07ed7df4ec9338f97f12627690d58ed9b34f25b
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/* OutputMixExt implementation */ 18 19#include "sles_allinclusive.h" 20 21#ifdef USE_OUTPUTMIXEXT 22 23// Used by SDL but not specific to or dependent on SDL 24 25static void IOutputMixExt_FillBuffer(SLOutputMixExtItf self, void *pBuffer, SLuint32 size) 26{ 27 // Force to be a multiple of a frame, assumes stereo 16-bit PCM 28 size &= ~3; 29 IOutputMixExt *thisExt = (IOutputMixExt *) self; 30 IOutputMix *this = &((COutputMix *) InterfaceToIObject(thisExt))->mOutputMix; 31 unsigned activeMask = this->mActiveMask; 32 SLboolean mixBufferHasData = SL_BOOLEAN_FALSE; 33 while (activeMask) { 34 unsigned i = ctz(activeMask); 35 assert(MAX_TRACK > i); 36 activeMask &= ~(1 << i); 37 struct Track *track = &this->mTracks[i]; 38 // track is allocated 39 IPlay *play = track->mPlay; 40 if (NULL == play) 41 continue; 42 // track is initialized 43 if (SL_PLAYSTATE_PLAYING != play->mState) 44 continue; 45 // track is playing 46 void *dstWriter = pBuffer; 47 unsigned desired = size; 48 SLboolean trackContributedToMix = SL_BOOLEAN_FALSE; 49 IBufferQueue *bufferQueue = track->mBufferQueue; 50 while (desired > 0) { 51 const BufferHeader *oldFront, *newFront, *rear; 52 unsigned actual = desired; 53 if (track->mAvail < actual) 54 actual = track->mAvail; 55 // force actual to be a frame multiple 56 if (actual > 0) { 57 // FIXME check for either mute or volume 0 58 // in which case skip the input buffer processing 59 assert(NULL != track->mReader); 60 // FIXME && gain == 1.0 61 if (mixBufferHasData) { 62 stereo *mixBuffer = (stereo *) dstWriter; 63 const stereo *source = (const stereo *) track->mReader; 64 unsigned j; 65 for (j = 0; j < actual; j += sizeof(stereo), ++mixBuffer, 66 ++source) { 67 // apply gain here 68 mixBuffer->left += source->left; 69 mixBuffer->right += source->right; 70 } 71 } else { 72 // apply gain during copy 73 memcpy(dstWriter, track->mReader, actual); 74 trackContributedToMix = SL_BOOLEAN_TRUE; 75 } 76 dstWriter = (char *) dstWriter + actual; 77 desired -= actual; 78 track->mReader = (char *) track->mReader + actual; 79 track->mAvail -= actual; 80 if (track->mAvail == 0) { 81 if (NULL != bufferQueue) { 82 interface_lock_exclusive(bufferQueue); 83 oldFront = bufferQueue->mFront; 84 rear = bufferQueue->mRear; 85 assert(oldFront != rear); 86 newFront = oldFront; 87 if (++newFront == &bufferQueue->mArray[bufferQueue->mNumBuffers + 1]) 88 newFront = bufferQueue->mArray; 89 bufferQueue->mFront = (BufferHeader *) newFront; 90 assert(0 < bufferQueue->mState.count); 91 --bufferQueue->mState.count; 92 // FIXME here or in Enqueue? 93 ++bufferQueue->mState.playIndex; 94 interface_unlock_exclusive(bufferQueue); 95 // FIXME a good time to do an early warning 96 // callback depending on buffer count 97 } 98 } 99 continue; 100 } 101 // actual == 0 102 if (NULL != bufferQueue) { 103 interface_lock_shared(bufferQueue); 104 oldFront = bufferQueue->mFront; 105 rear = bufferQueue->mRear; 106 if (oldFront != rear) { 107got_one: 108 assert(0 < bufferQueue->mState.count); 109 track->mReader = oldFront->mBuffer; 110 track->mAvail = oldFront->mSize; 111 interface_unlock_shared(bufferQueue); 112 continue; 113 } 114 // FIXME should be able to configure when to 115 // kick off the callback e.g. high/low water-marks etc. 116 // need data but none available, attempt a desperate callback 117 slBufferQueueCallback callback = bufferQueue->mCallback; 118 void *context = bufferQueue->mContext; 119 interface_unlock_shared(bufferQueue); 120 if (NULL != callback) { 121 (*callback)((SLBufferQueueItf) bufferQueue, context); 122 // if lucky, the callback enqueued a buffer 123 interface_lock_shared(bufferQueue); 124 if (rear != bufferQueue->mRear) 125 goto got_one; 126 interface_unlock_shared(bufferQueue); 127 // unlucky, queue still empty, the callback failed 128 } 129 // here on underflow due to no callback, or failed callback 130 // FIXME underflow, send silence (or previous buffer?) 131 // we did a callback to try to kick start again but failed 132 // should log this 133 } 134 // no buffer queue or underflow, clear out rest of partial buffer 135 if (!mixBufferHasData && trackContributedToMix) 136 memset(dstWriter, 0, actual); 137 break; 138 } 139 if (trackContributedToMix) 140 mixBufferHasData = SL_BOOLEAN_TRUE; 141 } 142 // No active tracks, so output silence 143 if (!mixBufferHasData) 144 memset(pBuffer, 0, size); 145} 146 147static const struct SLOutputMixExtItf_ IOutputMixExt_Itf = { 148 IOutputMixExt_FillBuffer 149}; 150 151void IOutputMixExt_init(void *self) 152{ 153 IOutputMixExt *this = (IOutputMixExt *) self; 154 this->mItf = &IOutputMixExt_Itf; 155} 156 157SLresult IOutputMixExt_checkAudioPlayerSourceSink(CAudioPlayer *this) 158{ 159 const SLDataSink *pAudioSnk = &this->mDataSink.u.mSink; 160 struct Track *track = NULL; 161 switch (*(SLuint32 *)pAudioSnk->pLocator) { 162 case SL_DATALOCATOR_OUTPUTMIX: 163 { 164 // pAudioSnk->pFormat is ignored 165 IOutputMix *om = &((COutputMix *) ((SLDataLocator_OutputMix *) pAudioSnk->pLocator)->outputMix)->mOutputMix; 166 // allocate an entry within OutputMix for this track 167 interface_lock_exclusive(om); 168 unsigned availMask = ~om->mActiveMask; 169 if (!availMask) { 170 interface_unlock_exclusive(om); 171 // All track slots full in output mix 172 return SL_RESULT_MEMORY_FAILURE; 173 } 174 unsigned i = ctz(availMask); 175 assert(MAX_TRACK > i); 176 om->mActiveMask |= 1 << i; 177 track = &om->mTracks[i]; 178 track->mPlay = NULL; // only field that is accessed before full initialization 179 interface_unlock_exclusive(om); 180 } 181 break; 182 default: 183 return SL_RESULT_CONTENT_UNSUPPORTED; 184 } 185 186 // FIXME Wrong place for this initialization; should first pre-allocate a track slot 187 // using OutputMixExt.mTrackCount, then initialize full audio player, then do track bit 188 // allocation, initialize rest of track, and doubly-link track to player (currently single). 189 assert(NULL != track); 190 track->mBufferQueue = &this->mBufferQueue; 191 track->mPlay = &this->mPlay; 192 track->mReader = NULL; 193 track->mAvail = 0; 194 return SL_RESULT_SUCCESS; 195} 196 197#endif // USE_OUTPUTMIXEXT 198