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