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