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