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