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