IOutputMixExt.c revision f51dba65751107c930759938775b75531ec1f330
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// OutputMixExt is used by SDL, but is not specific to or dependent on SDL 25 26/** \brief 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/** \brief Check whether a track has any data for us to read */ 36 37static SLboolean track_check(Track *track) 38{ 39 assert(NULL != track); 40 SLboolean trackHasData = SL_BOOLEAN_FALSE; 41 42 CAudioPlayer *audioPlayer = track->mAudioPlayer; 43 if (NULL != audioPlayer) { 44 45 // track is initialized 46 47 // FIXME This lock could block and result in stuttering; 48 // a trylock with retry or lockless solution would be ideal 49 object_lock_exclusive(&audioPlayer->mObject); 50 assert(audioPlayer->mTrack == track); 51 52 SLuint32 framesMixed = track->mFramesMixed; 53 if (0 != framesMixed) { 54 track->mFramesMixed = 0; 55 audioPlayer->mPlay.mFramesSinceLastSeek += framesMixed; 56 audioPlayer->mPlay.mFramesSincePositionUpdate += framesMixed; 57 } 58 59 SLboolean doBroadcast = SL_BOOLEAN_FALSE; 60 const BufferHeader *oldFront; 61 62 if (audioPlayer->mBufferQueue.mClearRequested) { 63 // application thread(s) that call BufferQueue::Clear while mixer is active 64 // will block synchronously until mixer acknowledges the Clear request 65 audioPlayer->mBufferQueue.mFront = &audioPlayer->mBufferQueue.mArray[0]; 66 audioPlayer->mBufferQueue.mRear = &audioPlayer->mBufferQueue.mArray[0]; 67 audioPlayer->mBufferQueue.mState.count = 0; 68 audioPlayer->mBufferQueue.mState.playIndex = 0; 69 audioPlayer->mBufferQueue.mClearRequested = SL_BOOLEAN_FALSE; 70 track->mReader = NULL; 71 track->mAvail = 0; 72 doBroadcast = SL_BOOLEAN_TRUE; 73 } 74 75 if (audioPlayer->mDestroyRequested) { 76 // an application thread that calls Object::Destroy while mixer is active will block 77 // synchronously in the PreDestroy hook until mixer acknowledges the Destroy request 78 COutputMix *outputMix = CAudioPlayer_GetOutputMix(audioPlayer); 79 unsigned i = track - outputMix->mOutputMixExt.mTracks; 80 assert( /* 0 <= i && */ i < MAX_TRACK); 81 unsigned mask = 1 << i; 82 track->mAudioPlayer = NULL; 83 assert(outputMix->mOutputMixExt.mActiveMask & mask); 84 outputMix->mOutputMixExt.mActiveMask &= ~mask; 85 audioPlayer->mTrack = NULL; 86 audioPlayer->mDestroyRequested = SL_BOOLEAN_FALSE; 87 doBroadcast = SL_BOOLEAN_TRUE; 88 goto broadcast; 89 } 90 91 switch (audioPlayer->mPlay.mState) { 92 93 case SL_PLAYSTATE_PLAYING: // continue playing current track data 94 if (0 < track->mAvail) { 95 trackHasData = SL_BOOLEAN_TRUE; 96 break; 97 } 98 99 // try to get another buffer from queue 100 oldFront = audioPlayer->mBufferQueue.mFront; 101 if (oldFront != audioPlayer->mBufferQueue.mRear) { 102 assert(0 < audioPlayer->mBufferQueue.mState.count); 103 track->mReader = oldFront->mBuffer; 104 track->mAvail = oldFront->mSize; 105 // note that the buffer stays on the queue while we are reading 106 audioPlayer->mPlay.mState = SL_PLAYSTATE_PLAYING; 107 trackHasData = SL_BOOLEAN_TRUE; 108 } else { 109 // no buffers on queue, so playable but not playing 110 // NTH should be able to call a desperation callback when completely starved, 111 // or call less often than every buffer based on high/low water-marks 112 } 113 114 // copy gains from audio player to track 115 track->mGains[0] = audioPlayer->mGains[0]; 116 track->mGains[1] = audioPlayer->mGains[1]; 117 break; 118 119 case SL_PLAYSTATE_STOPPING: // application thread(s) called Play::SetPlayState(STOPPED) 120 audioPlayer->mPlay.mPosition = (SLmillisecond) 0; 121 audioPlayer->mPlay.mFramesSinceLastSeek = 0; 122 audioPlayer->mPlay.mFramesSincePositionUpdate = 0; 123 audioPlayer->mPlay.mLastSeekPosition = 0; 124 audioPlayer->mPlay.mState = SL_PLAYSTATE_STOPPED; 125 // stop cancels a pending seek 126 audioPlayer->mSeek.mPos = SL_TIME_UNKNOWN; 127 oldFront = audioPlayer->mBufferQueue.mFront; 128 if (oldFront != audioPlayer->mBufferQueue.mRear) { 129 assert(0 < audioPlayer->mBufferQueue.mState.count); 130 track->mReader = oldFront->mBuffer; 131 track->mAvail = oldFront->mSize; 132 } 133 doBroadcast = SL_BOOLEAN_TRUE; 134 break; 135 136 case SL_PLAYSTATE_STOPPED: // idle 137 case SL_PLAYSTATE_PAUSED: // idle 138 break; 139 140 default: 141 assert(SL_BOOLEAN_FALSE); 142 break; 143 } 144 145broadcast: 146 if (doBroadcast) { 147 object_cond_broadcast(&audioPlayer->mObject); 148 } 149 150 object_unlock_exclusive(&audioPlayer->mObject); 151 152 } 153 154 return trackHasData; 155 156} 157 158 159/** \brief This is the track mixer: fill the specified 16-bit stereo PCM buffer */ 160 161void IOutputMixExt_FillBuffer(SLOutputMixExtItf self, void *pBuffer, SLuint32 size) 162{ 163 SL_ENTER_INTERFACE_VOID 164 165 // Force to be a multiple of a frame, assumes stereo 16-bit PCM 166 size &= ~3; 167 SLboolean mixBufferHasData = SL_BOOLEAN_FALSE; 168 IOutputMixExt *this = (IOutputMixExt *) self; 169 IObject *thisObject = this->mThis; 170 // This lock should never block, except when the application destroys the output mix object 171 object_lock_exclusive(thisObject); 172 unsigned activeMask; 173 // If the output mix is marked for destruction, then acknowledge the request 174 if (this->mDestroyRequested) { 175 IEngine *thisEngine = thisObject->mEngine; 176 interface_lock_exclusive(thisEngine); 177 assert(&thisEngine->mOutputMix->mObject == thisObject); 178 thisEngine->mOutputMix = NULL; 179 // Note we don't attempt to connect another output mix, even if there is one 180 interface_unlock_exclusive(thisEngine); 181 // Acknowledge the destroy request, and notify the pre-destroy hook 182 this->mDestroyRequested = SL_BOOLEAN_FALSE; 183 object_cond_broadcast(thisObject); 184 activeMask = 0; 185 } else { 186 activeMask = this->mActiveMask; 187 } 188 while (activeMask) { 189 unsigned i = ctz(activeMask); 190 assert(MAX_TRACK > i); 191 activeMask &= ~(1 << i); 192 Track *track = &this->mTracks[i]; 193 194 // track is allocated 195 196 if (!track_check(track)) { 197 continue; 198 } 199 200 // track is playing 201 void *dstWriter = pBuffer; 202 unsigned desired = size; 203 SLboolean trackContributedToMix = SL_BOOLEAN_FALSE; 204 float gains[STEREO_CHANNELS]; 205 Summary summaries[STEREO_CHANNELS]; 206 unsigned channel; 207 for (channel = 0; channel < STEREO_CHANNELS; ++channel) { 208 float gain = track->mGains[channel]; 209 gains[channel] = gain; 210 Summary summary; 211 if (gain <= 0.001) { 212 summary = GAIN_MUTE; 213 } else if (gain >= 0.999) { 214 summary = GAIN_UNITY; 215 } else { 216 summary = GAIN_OTHER; 217 } 218 summaries[channel] = summary; 219 } 220 while (desired > 0) { 221 unsigned actual = desired; 222 if (track->mAvail < actual) { 223 actual = track->mAvail; 224 } 225 // force actual to be a frame multiple 226 if (actual > 0) { 227 assert(NULL != track->mReader); 228 stereo *mixBuffer = (stereo *) dstWriter; 229 const stereo *source = (const stereo *) track->mReader; 230 unsigned j; 231 if (GAIN_MUTE != summaries[0] || GAIN_MUTE != summaries[1]) { 232 if (mixBufferHasData) { 233 // apply gain during add 234 if (GAIN_UNITY != summaries[0] || GAIN_UNITY != summaries[1]) { 235 for (j = 0; j < actual; j += sizeof(stereo), ++mixBuffer, ++source) { 236 mixBuffer->left += (short) (source->left * track->mGains[0]); 237 mixBuffer->right += (short) (source->right * track->mGains[1]); 238 } 239 // no gain adjustment needed, so do a simple add 240 } else { 241 for (j = 0; j < actual; j += sizeof(stereo), ++mixBuffer, ++source) { 242 mixBuffer->left += source->left; 243 mixBuffer->right += source->right; 244 } 245 } 246 } else { 247 // apply gain during copy 248 if (GAIN_UNITY != summaries[0] || GAIN_UNITY != summaries[1]) { 249 for (j = 0; j < actual; j += sizeof(stereo), ++mixBuffer, ++source) { 250 mixBuffer->left = (short) (source->left * track->mGains[0]); 251 mixBuffer->right = (short) (source->right * track->mGains[1]); 252 } 253 // no gain adjustment needed, so do a simple copy 254 } else { 255 memcpy(dstWriter, track->mReader, actual); 256 } 257 } 258 trackContributedToMix = SL_BOOLEAN_TRUE; 259 } 260 dstWriter = (char *) dstWriter + actual; 261 desired -= actual; 262 track->mReader = (char *) track->mReader + actual; 263 track->mAvail -= actual; 264 if (track->mAvail == 0) { 265 IBufferQueue *bufferQueue = &track->mAudioPlayer->mBufferQueue; 266 interface_lock_exclusive(bufferQueue); 267 const BufferHeader *oldFront, *newFront, *rear; 268 oldFront = bufferQueue->mFront; 269 rear = bufferQueue->mRear; 270 // a buffer stays on queue while playing, so it better still be there 271 assert(oldFront != rear); 272 newFront = oldFront; 273 if (++newFront == &bufferQueue->mArray[bufferQueue->mNumBuffers + 1]) { 274 newFront = bufferQueue->mArray; 275 } 276 bufferQueue->mFront = (BufferHeader *) newFront; 277 assert(0 < bufferQueue->mState.count); 278 --bufferQueue->mState.count; 279 if (newFront != rear) { 280 // we don't acknowledge application requests between buffers 281 // within the same mixer frame 282 assert(0 < bufferQueue->mState.count); 283 track->mReader = newFront->mBuffer; 284 track->mAvail = newFront->mSize; 285 } 286 // else we would set play state to playable but not playing during next mixer 287 // frame if the queue is still empty at that time 288 ++bufferQueue->mState.playIndex; 289 slBufferQueueCallback callback = bufferQueue->mCallback; 290 void *context = bufferQueue->mContext; 291 interface_unlock_exclusive(bufferQueue); 292 // The callback function is called on each buffer completion 293 if (NULL != callback) { 294 (*callback)((SLBufferQueueItf) bufferQueue, context); 295 // Maybe it enqueued another buffer, or maybe it didn't. 296 // We will find out later during the next mixer frame. 297 } 298 } 299 // no lock, but safe because noone else updates this field 300 track->mFramesMixed += actual >> 2; // sizeof(short) * STEREO_CHANNELS 301 continue; 302 } 303 // we need more data: desired > 0 but actual == 0 304 if (track_check(track)) { 305 continue; 306 } 307 // underflow: clear out rest of partial buffer (NTH synthesize comfort noise) 308 if (!mixBufferHasData && trackContributedToMix) { 309 memset(dstWriter, 0, actual); 310 } 311 break; 312 } 313 if (trackContributedToMix) { 314 mixBufferHasData = SL_BOOLEAN_TRUE; 315 } 316 } 317 object_unlock_exclusive(thisObject); 318 // No active tracks, so output silence 319 if (!mixBufferHasData) { 320 memset(pBuffer, 0, size); 321 } 322 323 SL_LEAVE_INTERFACE_VOID 324} 325 326 327static const struct SLOutputMixExtItf_ IOutputMixExt_Itf = { 328 IOutputMixExt_FillBuffer 329}; 330 331void IOutputMixExt_init(void *self) 332{ 333 IOutputMixExt *this = (IOutputMixExt *) self; 334 this->mItf = &IOutputMixExt_Itf; 335 this->mActiveMask = 0; 336 Track *track = &this->mTracks[0]; 337 unsigned i; 338 for (i = 0; i < MAX_TRACK; ++i, ++track) { 339 track->mAudioPlayer = NULL; 340 } 341 this->mDestroyRequested = SL_BOOLEAN_FALSE; 342} 343 344 345/** \brief Called by Engine::CreateAudioPlayer to allocate a track */ 346 347SLresult IOutputMixExt_checkAudioPlayerSourceSink(CAudioPlayer *this) 348{ 349 this->mTrack = NULL; 350 const SLDataSink *pAudioSnk = &this->mDataSink.u.mSink; 351 Track *track = NULL; 352 switch (*(SLuint32 *)pAudioSnk->pLocator) { 353 case SL_DATALOCATOR_OUTPUTMIX: 354 { 355 // pAudioSnk->pFormat is ignored 356 IOutputMixExt *omExt = &((COutputMix *) ((SLDataLocator_OutputMix *) 357 pAudioSnk->pLocator)->outputMix)->mOutputMixExt; 358 // allocate an entry within OutputMix for this track 359 interface_lock_exclusive(omExt); 360 unsigned availMask = ~omExt->mActiveMask; 361 if (!availMask) { 362 interface_unlock_exclusive(omExt); 363 // All track slots full in output mix 364 return SL_RESULT_MEMORY_FAILURE; 365 } 366 unsigned i = ctz(availMask); 367 assert(MAX_TRACK > i); 368 omExt->mActiveMask |= 1 << i; 369 track = &omExt->mTracks[i]; 370 track->mAudioPlayer = NULL; // only field that is accessed before full initialization 371 interface_unlock_exclusive(omExt); 372 this->mTrack = track; 373 this->mGains[0] = 1.0f; 374 this->mGains[1] = 1.0f; 375 this->mDestroyRequested = SL_BOOLEAN_FALSE; 376 } 377 break; 378 default: 379 return SL_RESULT_CONTENT_UNSUPPORTED; 380 } 381 382 // FIXME Wrong place for this initialization; should first pre-allocate a track slot 383 // using OutputMixExt.mTrackCount, then initialize full audio player, then do track bit 384 // allocation, initialize rest of track, and doubly-link track to player (currently single). 385 assert(NULL != track); 386 track->mBufferQueue = &this->mBufferQueue; 387 track->mAudioPlayer = this; 388 track->mReader = NULL; 389 track->mAvail = 0; 390 track->mGains[0] = 1.0f; 391 track->mGains[1] = 1.0f; 392 track->mFramesMixed = 0; 393 return SL_RESULT_SUCCESS; 394} 395 396 397/** \brief Called when a gain-related field (mute, solo, volume, stereo position, etc.) updated */ 398 399void audioPlayerGainUpdate(CAudioPlayer *audioPlayer) 400{ 401 SLboolean mute = audioPlayer->mVolume.mMute; 402 SLuint8 muteMask = audioPlayer->mMuteMask; 403 SLuint8 soloMask = audioPlayer->mSoloMask; 404 SLmillibel level = audioPlayer->mVolume.mLevel; 405 SLboolean enableStereoPosition = audioPlayer->mVolume.mEnableStereoPosition; 406 SLpermille stereoPosition = audioPlayer->mVolume.mStereoPosition; 407 408 if (soloMask) { 409 muteMask |= ~soloMask; 410 } 411 if (mute || !(~muteMask & 3)) { 412 audioPlayer->mGains[0] = 0.0f; 413 audioPlayer->mGains[1] = 0.0f; 414 } else { 415 float playerGain = powf(10.0f, level / 2000.0f); 416 unsigned channel; 417 for (channel = 0; channel < STEREO_CHANNELS; ++channel) { 418 float gain; 419 if (muteMask & (1 << channel)) { 420 gain = 0.0f; 421 } else { 422 gain = playerGain; 423 if (enableStereoPosition) { 424 switch (channel) { 425 case 0: 426 if (stereoPosition > 0) { 427 gain *= (1000 - stereoPosition) / 1000.0f; 428 } 429 break; 430 case 1: 431 if (stereoPosition < 0) { 432 gain *= (1000 + stereoPosition) / 1000.0f; 433 } 434 break; 435 default: 436 assert(SL_BOOLEAN_FALSE); 437 break; 438 } 439 } 440 } 441 audioPlayer->mGains[channel] = gain; 442 } 443 } 444} 445 446 447#endif // USE_OUTPUTMIXEXT 448