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