IOutputMixExt.c revision d07ed7df4ec9338f97f12627690d58ed9b34f25b
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
21#ifdef USE_OUTPUTMIXEXT
22
23// Used by SDL but not specific to or dependent on SDL
24
25static void IOutputMixExt_FillBuffer(SLOutputMixExtItf self, void *pBuffer, SLuint32 size)
26{
27    // Force to be a multiple of a frame, assumes stereo 16-bit PCM
28    size &= ~3;
29    IOutputMixExt *thisExt = (IOutputMixExt *) self;
30    IOutputMix *this = &((COutputMix *) InterfaceToIObject(thisExt))->mOutputMix;
31    unsigned activeMask = this->mActiveMask;
32    SLboolean mixBufferHasData = SL_BOOLEAN_FALSE;
33    while (activeMask) {
34        unsigned i = ctz(activeMask);
35        assert(MAX_TRACK > i);
36        activeMask &= ~(1 << i);
37        struct Track *track = &this->mTracks[i];
38        // track is allocated
39        IPlay *play = track->mPlay;
40        if (NULL == play)
41            continue;
42        // track is initialized
43        if (SL_PLAYSTATE_PLAYING != play->mState)
44            continue;
45        // track is playing
46        void *dstWriter = pBuffer;
47        unsigned desired = size;
48        SLboolean trackContributedToMix = SL_BOOLEAN_FALSE;
49        IBufferQueue *bufferQueue = track->mBufferQueue;
50        while (desired > 0) {
51            const BufferHeader *oldFront, *newFront, *rear;
52            unsigned actual = desired;
53            if (track->mAvail < actual)
54                actual = track->mAvail;
55            // force actual to be a frame multiple
56            if (actual > 0) {
57                // FIXME check for either mute or volume 0
58                // in which case skip the input buffer processing
59                assert(NULL != track->mReader);
60                // FIXME && gain == 1.0
61                if (mixBufferHasData) {
62                    stereo *mixBuffer = (stereo *) dstWriter;
63                    const stereo *source = (const stereo *) track->mReader;
64                    unsigned j;
65                    for (j = 0; j < actual; j += sizeof(stereo), ++mixBuffer,
66                        ++source) {
67                        // apply gain here
68                        mixBuffer->left += source->left;
69                        mixBuffer->right += source->right;
70                    }
71                } else {
72                    // apply gain during copy
73                    memcpy(dstWriter, track->mReader, actual);
74                    trackContributedToMix = SL_BOOLEAN_TRUE;
75                }
76                dstWriter = (char *) dstWriter + actual;
77                desired -= actual;
78                track->mReader = (char *) track->mReader + actual;
79                track->mAvail -= actual;
80                if (track->mAvail == 0) {
81                    if (NULL != bufferQueue) {
82                        interface_lock_exclusive(bufferQueue);
83                        oldFront = bufferQueue->mFront;
84                        rear = bufferQueue->mRear;
85                        assert(oldFront != rear);
86                        newFront = oldFront;
87                        if (++newFront == &bufferQueue->mArray[bufferQueue->mNumBuffers + 1])
88                            newFront = bufferQueue->mArray;
89                        bufferQueue->mFront = (BufferHeader *) newFront;
90                        assert(0 < bufferQueue->mState.count);
91                        --bufferQueue->mState.count;
92                        // FIXME here or in Enqueue?
93                        ++bufferQueue->mState.playIndex;
94                        interface_unlock_exclusive(bufferQueue);
95                        // FIXME a good time to do an early warning
96                        // callback depending on buffer count
97                    }
98                }
99                continue;
100            }
101            // actual == 0
102            if (NULL != bufferQueue) {
103                interface_lock_shared(bufferQueue);
104                oldFront = bufferQueue->mFront;
105                rear = bufferQueue->mRear;
106                if (oldFront != rear) {
107got_one:
108                    assert(0 < bufferQueue->mState.count);
109                    track->mReader = oldFront->mBuffer;
110                    track->mAvail = oldFront->mSize;
111                    interface_unlock_shared(bufferQueue);
112                    continue;
113                }
114                // FIXME should be able to configure when to
115                // kick off the callback e.g. high/low water-marks etc.
116                // need data but none available, attempt a desperate callback
117                slBufferQueueCallback callback = bufferQueue->mCallback;
118                void *context = bufferQueue->mContext;
119                interface_unlock_shared(bufferQueue);
120                if (NULL != callback) {
121                    (*callback)((SLBufferQueueItf) bufferQueue, context);
122                    // if lucky, the callback enqueued a buffer
123                    interface_lock_shared(bufferQueue);
124                    if (rear != bufferQueue->mRear)
125                        goto got_one;
126                    interface_unlock_shared(bufferQueue);
127                    // unlucky, queue still empty, the callback failed
128                }
129                // here on underflow due to no callback, or failed callback
130                // FIXME underflow, send silence (or previous buffer?)
131                // we did a callback to try to kick start again but failed
132                // should log this
133            }
134            // no buffer queue or underflow, clear out rest of partial buffer
135            if (!mixBufferHasData && trackContributedToMix)
136                memset(dstWriter, 0, actual);
137            break;
138        }
139        if (trackContributedToMix)
140            mixBufferHasData = SL_BOOLEAN_TRUE;
141    }
142    // No active tracks, so output silence
143    if (!mixBufferHasData)
144        memset(pBuffer, 0, size);
145}
146
147static const struct SLOutputMixExtItf_ IOutputMixExt_Itf = {
148    IOutputMixExt_FillBuffer
149};
150
151void IOutputMixExt_init(void *self)
152{
153    IOutputMixExt *this = (IOutputMixExt *) self;
154    this->mItf = &IOutputMixExt_Itf;
155}
156
157SLresult IOutputMixExt_checkAudioPlayerSourceSink(CAudioPlayer *this)
158{
159    const SLDataSink *pAudioSnk = &this->mDataSink.u.mSink;
160    struct Track *track = NULL;
161    switch (*(SLuint32 *)pAudioSnk->pLocator) {
162    case SL_DATALOCATOR_OUTPUTMIX:
163        {
164        // pAudioSnk->pFormat is ignored
165        IOutputMix *om = &((COutputMix *) ((SLDataLocator_OutputMix *) pAudioSnk->pLocator)->outputMix)->mOutputMix;
166        // allocate an entry within OutputMix for this track
167        interface_lock_exclusive(om);
168        unsigned availMask = ~om->mActiveMask;
169        if (!availMask) {
170            interface_unlock_exclusive(om);
171            // All track slots full in output mix
172            return SL_RESULT_MEMORY_FAILURE;
173        }
174        unsigned i = ctz(availMask);
175        assert(MAX_TRACK > i);
176        om->mActiveMask |= 1 << i;
177        track = &om->mTracks[i];
178        track->mPlay = NULL;    // only field that is accessed before full initialization
179        interface_unlock_exclusive(om);
180        }
181        break;
182    default:
183        return SL_RESULT_CONTENT_UNSUPPORTED;
184    }
185
186    // FIXME Wrong place for this initialization; should first pre-allocate a track slot
187    // using OutputMixExt.mTrackCount, then initialize full audio player, then do track bit
188    // allocation, initialize rest of track, and doubly-link track to player (currently single).
189    assert(NULL != track);
190    track->mBufferQueue = &this->mBufferQueue;
191    track->mPlay = &this->mPlay;
192    track->mReader = NULL;
193    track->mAvail = 0;
194    return SL_RESULT_SUCCESS;
195}
196
197#endif // USE_OUTPUTMIXEXT
198