IOutputMixExt.c revision 18abcc4b70fab1f84d6fbebac3a8e34480a6c4d3
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#include <math.h> 21 22#ifdef USE_OUTPUTMIXEXT 23 24// Used by SDL but not specific to or dependent on SDL 25 26// Summary of the gain, as an optimization for the mixer 27 28typedef enum { 29 GAIN_MUTE = 0, // mValue == 0.0f within epsilon 30 GAIN_UNITY = 1, // mValue == 1.0f within epsilon 31 GAIN_OTHER = 2 // 0.0f < mValue < 1.0f 32} Summary; 33 34 35/** Check whether a track has any data for us to read. */ 36 37static SLboolean track_check(Track *track) 38{ 39 SLboolean trackHasData = SL_BOOLEAN_FALSE; 40 41 CAudioPlayer *audioPlayer = track->mAudioPlayer; 42 if (NULL != audioPlayer) { 43 44 // track is initialized 45 46 object_lock_exclusive(&audioPlayer->mObject); 47 48 SLboolean doBroadcast = SL_BOOLEAN_FALSE; 49 const BufferHeader *oldFront; 50 51 if (audioPlayer->mBufferQueue.mClearRequested) { 52 // application thread(s) that call BufferQueue::Clear while mixer is active 53 // is active will block synchronously until mixer acknowledges the Clear request 54 audioPlayer->mBufferQueue.mFront = &audioPlayer->mBufferQueue.mArray[0]; 55 audioPlayer->mBufferQueue.mRear = &audioPlayer->mBufferQueue.mArray[0]; 56 audioPlayer->mBufferQueue.mState.count = 0; 57 audioPlayer->mBufferQueue.mClearRequested = SL_BOOLEAN_FALSE; 58 track->mReader = NULL; 59 track->mAvail = 0; 60 doBroadcast = SL_BOOLEAN_TRUE; 61 } 62 63 switch (audioPlayer->mPlay.mState) { 64 65 case SL_PLAYSTATE_PLAYING: // continue playing current track data 66 if (0 < track->mAvail) { 67 trackHasData = SL_BOOLEAN_TRUE; 68 break; 69 } 70 71 // try to get another buffer from queue 72 oldFront = audioPlayer->mBufferQueue.mFront; 73 if (oldFront != audioPlayer->mBufferQueue.mRear) { 74 assert(0 < audioPlayer->mBufferQueue.mState.count); 75 track->mReader = oldFront->mBuffer; 76 track->mAvail = oldFront->mSize; 77 // note that the buffer stays on the queue while we are reading 78 audioPlayer->mPlay.mState = SL_PLAYSTATE_PLAYING; 79 trackHasData = SL_BOOLEAN_TRUE; 80 } else { 81 // no buffers on queue, so playable but not playing 82 // NTH should be able to call a desperation callback when completely starved, 83 // or call less often than every buffer based on high/low water-marks 84 } 85 break; 86 87 case SL_PLAYSTATE_STOPPING: // application thread(s) called Play::SetPlayState(STOPPED) 88 audioPlayer->mPlay.mPosition = (SLmillisecond) 0; 89 audioPlayer->mPlay.mState = SL_PLAYSTATE_STOPPED; 90 oldFront = audioPlayer->mBufferQueue.mFront; 91 if (oldFront != audioPlayer->mBufferQueue.mRear) { 92 assert(0 < audioPlayer->mBufferQueue.mState.count); 93 track->mReader = oldFront->mBuffer; 94 track->mAvail = oldFront->mSize; 95 } 96 doBroadcast = SL_BOOLEAN_TRUE; 97 break; 98 99 case SL_PLAYSTATE_STOPPED: // idle 100 case SL_PLAYSTATE_PAUSED: // idle 101 break; 102 103 default: 104 assert(SL_BOOLEAN_FALSE); 105 break; 106 } 107 108 if (doBroadcast) 109 object_cond_broadcast(&audioPlayer->mObject); 110 111 object_unlock_exclusive(&audioPlayer->mObject); 112 113 } 114 115 return trackHasData; 116 117} 118 119static void IOutputMixExt_FillBuffer(SLOutputMixExtItf self, void *pBuffer, SLuint32 size) 120{ 121 SL_ENTER_INTERFACE_VOID 122 123 // Force to be a multiple of a frame, assumes stereo 16-bit PCM 124 size &= ~3; 125 IOutputMixExt *thisExt = (IOutputMixExt *) self; 126 IOutputMix *this = &((COutputMix *) InterfaceToIObject(thisExt))->mOutputMix; 127 unsigned activeMask = this->mActiveMask; 128 SLboolean mixBufferHasData = SL_BOOLEAN_FALSE; 129 while (activeMask) { 130 unsigned i = ctz(activeMask); 131 assert(MAX_TRACK > i); 132 activeMask &= ~(1 << i); 133 Track *track = &this->mTracks[i]; 134 135 // track is allocated 136 137 if (!track_check(track)) 138 continue; 139 140 // track is playing 141 void *dstWriter = pBuffer; 142 unsigned desired = size; 143 SLboolean trackContributedToMix = SL_BOOLEAN_FALSE; 144 float gains[STEREO_CHANNELS]; 145 Summary summaries[STEREO_CHANNELS]; 146 unsigned channel; 147 for (channel = 0; channel < STEREO_CHANNELS; ++channel) { 148 float gain = track->mGains[channel]; 149 gains[channel] = gain; 150 Summary summary; 151 if (gain <= 0.001) 152 summary = GAIN_MUTE; 153 else if (gain >= 0.999) 154 summary = GAIN_UNITY; 155 else 156 summary = GAIN_OTHER; 157 summaries[channel] = summary; 158 } 159 while (desired > 0) { 160 unsigned actual = desired; 161 if (track->mAvail < actual) 162 actual = track->mAvail; 163 // force actual to be a frame multiple 164 if (actual > 0) { 165 assert(NULL != track->mReader); 166 stereo *mixBuffer = (stereo *) dstWriter; 167 const stereo *source = (const stereo *) track->mReader; 168 unsigned j; 169 if (GAIN_MUTE != summaries[0] || GAIN_MUTE != summaries[1]) { 170 if (mixBufferHasData) { 171 // apply gain during add 172 if (GAIN_UNITY != summaries[0] || GAIN_UNITY != summaries[1]) { 173 for (j = 0; j < actual; j += sizeof(stereo), ++mixBuffer, ++source) { 174 mixBuffer->left += (short) (source->left * track->mGains[0]); 175 mixBuffer->right += (short) (source->right * track->mGains[1]); 176 } 177 // no gain adjustment needed, so do a simple add 178 } else { 179 for (j = 0; j < actual; j += sizeof(stereo), ++mixBuffer, ++source) { 180 mixBuffer->left += source->left; 181 mixBuffer->right += source->right; 182 } 183 } 184 } else { 185 // apply gain during copy 186 if (GAIN_UNITY != summaries[0] || GAIN_UNITY != summaries[1]) { 187 for (j = 0; j < actual; j += sizeof(stereo), ++mixBuffer, ++source) { 188 mixBuffer->left = (short) (source->left * track->mGains[0]); 189 mixBuffer->right = (short) (source->right * track->mGains[1]); 190 } 191 // no gain adjustment needed, so do a simple copy 192 } else { 193 memcpy(dstWriter, track->mReader, actual); 194 } 195 } 196 trackContributedToMix = SL_BOOLEAN_TRUE; 197 } 198 dstWriter = (char *) dstWriter + actual; 199 desired -= actual; 200 track->mReader = (char *) track->mReader + actual; 201 track->mAvail -= actual; 202 if (track->mAvail == 0) { 203 IBufferQueue *bufferQueue = &track->mAudioPlayer->mBufferQueue; 204 interface_lock_exclusive(bufferQueue); 205 const BufferHeader *oldFront, *newFront, *rear; 206 oldFront = bufferQueue->mFront; 207 rear = bufferQueue->mRear; 208 // a buffer stays on queue while playing, so it better still be there 209 assert(oldFront != rear); 210 newFront = oldFront; 211 if (++newFront == &bufferQueue->mArray[bufferQueue->mNumBuffers + 1]) 212 newFront = bufferQueue->mArray; 213 bufferQueue->mFront = (BufferHeader *) newFront; 214 assert(0 < bufferQueue->mState.count); 215 --bufferQueue->mState.count; 216 if (newFront != rear) { 217 // we don't acknowledge application requests between buffers 218 // within the same mixer frame 219 assert(0 < bufferQueue->mState.count); 220 track->mReader = newFront->mBuffer; 221 track->mAvail = newFront->mSize; 222 } 223 // else we would set play state to playable but not playing during next mixer 224 // frame if the queue is still empty at that time 225 ++bufferQueue->mState.playIndex; 226 slBufferQueueCallback callback = bufferQueue->mCallback; 227 void *context = bufferQueue->mContext; 228 interface_unlock_exclusive(bufferQueue); 229 // The callback function is called on each buffer completion 230 if (NULL != callback) { 231 (*callback)((SLBufferQueueItf) bufferQueue, context); 232 // Maybe it enqueued another buffer, or maybe it didn't. 233 // We will find out later during the next mixer frame. 234 } 235 } 236 // no lock, but safe b/c noone else updates this field 237 track->mFrameCounter += actual >> 2; // sizeof(short) * STEREO_CHANNELS 238 // NTH Calling the callback too often, should depend on requested update period 239 // FIXME need a lock to get these pointers, and called too often 240 if (track->mAudioPlayer->mPlay.mCallback) 241 (*track->mAudioPlayer->mPlay.mCallback)(&track->mAudioPlayer->mPlay.mItf, 242 track->mAudioPlayer->mPlay.mContext, SL_PLAYEVENT_HEADMOVING); 243 continue; 244 } 245 // we need more data: desired > 0 but actual == 0 246 if (track_check(track)) 247 continue; 248 // underflow: clear out rest of partial buffer (NTH synthesize comfort noise) 249 if (!mixBufferHasData && trackContributedToMix) 250 memset(dstWriter, 0, actual); 251 break; 252 } 253 if (trackContributedToMix) 254 mixBufferHasData = SL_BOOLEAN_TRUE; 255 } 256 // No active tracks, so output silence 257 if (!mixBufferHasData) 258 memset(pBuffer, 0, size); 259 260 SL_LEAVE_INTERFACE_VOID 261} 262 263 264static const struct SLOutputMixExtItf_ IOutputMixExt_Itf = { 265 IOutputMixExt_FillBuffer 266}; 267 268void IOutputMixExt_init(void *self) 269{ 270 IOutputMixExt *this = (IOutputMixExt *) self; 271 this->mItf = &IOutputMixExt_Itf; 272} 273 274SLresult IOutputMixExt_checkAudioPlayerSourceSink(CAudioPlayer *this) 275{ 276 this->mTrack = NULL; 277 const SLDataSink *pAudioSnk = &this->mDataSink.u.mSink; 278 Track *track = NULL; 279 switch (*(SLuint32 *)pAudioSnk->pLocator) { 280 case SL_DATALOCATOR_OUTPUTMIX: 281 { 282 // pAudioSnk->pFormat is ignored 283 IOutputMix *om = &((COutputMix *) ((SLDataLocator_OutputMix *) 284 pAudioSnk->pLocator)->outputMix)->mOutputMix; 285 // allocate an entry within OutputMix for this track 286 interface_lock_exclusive(om); 287 unsigned availMask = ~om->mActiveMask; 288 if (!availMask) { 289 interface_unlock_exclusive(om); 290 // All track slots full in output mix 291 return SL_RESULT_MEMORY_FAILURE; 292 } 293 unsigned i = ctz(availMask); 294 assert(MAX_TRACK > i); 295 om->mActiveMask |= 1 << i; 296 track = &om->mTracks[i]; 297 track->mAudioPlayer = NULL; // only field that is accessed before full initialization 298 interface_unlock_exclusive(om); 299 this->mTrack = track; 300 } 301 break; 302 default: 303 return SL_RESULT_CONTENT_UNSUPPORTED; 304 } 305 306 // FIXME Wrong place for this initialization; should first pre-allocate a track slot 307 // using OutputMixExt.mTrackCount, then initialize full audio player, then do track bit 308 // allocation, initialize rest of track, and doubly-link track to player (currently single). 309 assert(NULL != track); 310 track->mBufferQueue = &this->mBufferQueue; 311 track->mAudioPlayer = this; 312 track->mReader = NULL; 313 track->mAvail = 0; 314 track->mGains[0] = 1.0f; 315 track->mGains[1] = 1.0f; 316 track->mFrameCounter = 0; 317 return SL_RESULT_SUCCESS; 318} 319 320void audioPlayerGainUpdate(CAudioPlayer *audioPlayer) 321{ 322 // FIXME need a lock on the track while updating gain 323 Track *track = audioPlayer->mTrack; 324 325 if (NULL == track) 326 return; 327 328 SLboolean mute = audioPlayer->mVolume.mMute; 329 SLuint8 muteMask = audioPlayer->mMuteMask; 330 SLuint8 soloMask = audioPlayer->mSoloMask; 331 SLmillibel level = audioPlayer->mVolume.mLevel; 332 SLboolean enableStereoPosition = audioPlayer->mVolume.mEnableStereoPosition; 333 SLpermille stereoPosition = audioPlayer->mVolume.mStereoPosition; 334 335 if (soloMask) 336 muteMask |= ~soloMask; 337 if (mute || !(~muteMask & 3)) { 338 track->mGains[0] = 0.0f; 339 track->mGains[1] = 0.0f; 340 } else { 341 float playerGain = powf(10.0f, level / 2000.0f); 342 unsigned channel; 343 for (channel = 0; channel < STEREO_CHANNELS; ++channel) { 344 float gain; 345 if (muteMask & (1 << channel)) 346 gain = 0.0f; 347 else { 348 gain = playerGain; 349 if (enableStereoPosition) { 350 switch (channel) { 351 case 0: 352 if (stereoPosition > 0) 353 gain *= (1000 - stereoPosition) / 1000.0f; 354 break; 355 case 1: 356 if (stereoPosition < 0) 357 gain *= (1000 + stereoPosition) / 1000.0f; 358 break; 359 default: 360 assert(SL_BOOLEAN_FALSE); 361 break; 362 } 363 } 364 } 365 track->mGains[channel] = gain; 366 } 367 } 368} 369 370#endif // USE_OUTPUTMIXEXT 371