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
23// OutputMixExt is used by SDL, but is not specific to or dependent on SDL
24
25
26// stereo is a frame consisting of a pair of 16-bit PCM samples
27
28typedef struct {
29    short left;
30    short right;
31} stereo;
32
33
34/** \brief Summary of the gain, as an optimization for the mixer */
35
36typedef enum {
37    GAIN_MUTE  = 0,  // mValue == 0.0f within epsilon
38    GAIN_UNITY = 1,  // mValue == 1.0f within epsilon
39    GAIN_OTHER = 2   // 0.0f < mValue < 1.0f
40} Summary;
41
42
43/** \brief Check whether a track has any data for us to read */
44
45static SLboolean track_check(Track *track)
46{
47    assert(NULL != track);
48    SLboolean trackHasData = SL_BOOLEAN_FALSE;
49
50    CAudioPlayer *audioPlayer = track->mAudioPlayer;
51    if (NULL != audioPlayer) {
52
53        // track is initialized
54
55        // FIXME This lock could block and result in stuttering;
56        // a trylock with retry or lockless solution would be ideal
57        object_lock_exclusive(&audioPlayer->mObject);
58        assert(audioPlayer->mTrack == track);
59
60        SLuint32 framesMixed = track->mFramesMixed;
61        if (0 != framesMixed) {
62            track->mFramesMixed = 0;
63            audioPlayer->mPlay.mFramesSinceLastSeek += framesMixed;
64            audioPlayer->mPlay.mFramesSincePositionUpdate += framesMixed;
65        }
66
67        SLboolean doBroadcast = SL_BOOLEAN_FALSE;
68        const BufferHeader *oldFront;
69
70        if (audioPlayer->mBufferQueue.mClearRequested) {
71            // application thread(s) that call BufferQueue::Clear while mixer is active
72            // will block synchronously until mixer acknowledges the Clear request
73            audioPlayer->mBufferQueue.mFront = &audioPlayer->mBufferQueue.mArray[0];
74            audioPlayer->mBufferQueue.mRear = &audioPlayer->mBufferQueue.mArray[0];
75            audioPlayer->mBufferQueue.mState.count = 0;
76            audioPlayer->mBufferQueue.mState.playIndex = 0;
77            audioPlayer->mBufferQueue.mClearRequested = SL_BOOLEAN_FALSE;
78            track->mReader = NULL;
79            track->mAvail = 0;
80            doBroadcast = SL_BOOLEAN_TRUE;
81        }
82
83        if (audioPlayer->mDestroyRequested) {
84            // an application thread that calls Object::Destroy while mixer is active will block
85            // synchronously in the PreDestroy hook until mixer acknowledges the Destroy request
86            COutputMix *outputMix = CAudioPlayer_GetOutputMix(audioPlayer);
87            unsigned i = track - outputMix->mOutputMixExt.mTracks;
88            assert( /* 0 <= i && */ i < MAX_TRACK);
89            unsigned mask = 1 << i;
90            track->mAudioPlayer = NULL;
91            assert(outputMix->mOutputMixExt.mActiveMask & mask);
92            outputMix->mOutputMixExt.mActiveMask &= ~mask;
93            audioPlayer->mTrack = NULL;
94            audioPlayer->mDestroyRequested = SL_BOOLEAN_FALSE;
95            doBroadcast = SL_BOOLEAN_TRUE;
96            goto broadcast;
97        }
98
99        switch (audioPlayer->mPlay.mState) {
100
101        case SL_PLAYSTATE_PLAYING:  // continue playing current track data
102            if (0 < track->mAvail) {
103                trackHasData = SL_BOOLEAN_TRUE;
104                break;
105            }
106
107            // try to get another buffer from queue
108            oldFront = audioPlayer->mBufferQueue.mFront;
109            if (oldFront != audioPlayer->mBufferQueue.mRear) {
110                assert(0 < audioPlayer->mBufferQueue.mState.count);
111                track->mReader = oldFront->mBuffer;
112                track->mAvail = oldFront->mSize;
113                // note that the buffer stays on the queue while we are reading
114                audioPlayer->mPlay.mState = SL_PLAYSTATE_PLAYING;
115                trackHasData = SL_BOOLEAN_TRUE;
116            } else {
117                // no buffers on queue, so playable but not playing
118                // NTH should be able to call a desperation callback when completely starved,
119                // or call less often than every buffer based on high/low water-marks
120            }
121
122            // copy gains from audio player to track
123            track->mGains[0] = audioPlayer->mGains[0];
124            track->mGains[1] = audioPlayer->mGains[1];
125            break;
126
127        case SL_PLAYSTATE_STOPPING: // application thread(s) called Play::SetPlayState(STOPPED)
128            audioPlayer->mPlay.mPosition = (SLmillisecond) 0;
129            audioPlayer->mPlay.mFramesSinceLastSeek = 0;
130            audioPlayer->mPlay.mFramesSincePositionUpdate = 0;
131            audioPlayer->mPlay.mLastSeekPosition = 0;
132            audioPlayer->mPlay.mState = SL_PLAYSTATE_STOPPED;
133            // stop cancels a pending seek
134            audioPlayer->mSeek.mPos = SL_TIME_UNKNOWN;
135            oldFront = audioPlayer->mBufferQueue.mFront;
136            if (oldFront != audioPlayer->mBufferQueue.mRear) {
137                assert(0 < audioPlayer->mBufferQueue.mState.count);
138                track->mReader = oldFront->mBuffer;
139                track->mAvail = oldFront->mSize;
140            }
141            doBroadcast = SL_BOOLEAN_TRUE;
142            break;
143
144        case SL_PLAYSTATE_STOPPED:  // idle
145        case SL_PLAYSTATE_PAUSED:   // idle
146            break;
147
148        default:
149            assert(SL_BOOLEAN_FALSE);
150            break;
151        }
152
153broadcast:
154        if (doBroadcast) {
155            object_cond_broadcast(&audioPlayer->mObject);
156        }
157
158        object_unlock_exclusive(&audioPlayer->mObject);
159
160    }
161
162    return trackHasData;
163
164}
165
166
167/** \brief This is the track mixer: fill the specified 16-bit stereo PCM buffer */
168
169void IOutputMixExt_FillBuffer(SLOutputMixExtItf self, void *pBuffer, SLuint32 size)
170{
171    SL_ENTER_INTERFACE_VOID
172
173    // Force to be a multiple of a frame, assumes stereo 16-bit PCM
174    size &= ~3;
175    SLboolean mixBufferHasData = SL_BOOLEAN_FALSE;
176    IOutputMixExt *thiz = (IOutputMixExt *) self;
177    IObject *thisObject = thiz->mThis;
178    // This lock should never block, except when the application destroys the output mix object
179    object_lock_exclusive(thisObject);
180    unsigned activeMask;
181    // If the output mix is marked for destruction, then acknowledge the request
182    if (thiz->mDestroyRequested) {
183        IEngine *thisEngine = &thisObject->mEngine->mEngine;
184        interface_lock_exclusive(thisEngine);
185        assert(&thisEngine->mOutputMix->mObject == thisObject);
186        thisEngine->mOutputMix = NULL;
187        // Note we don't attempt to connect another output mix, even if there is one
188        interface_unlock_exclusive(thisEngine);
189        // Acknowledge the destroy request, and notify the pre-destroy hook
190        thiz->mDestroyRequested = SL_BOOLEAN_FALSE;
191        object_cond_broadcast(thisObject);
192        activeMask = 0;
193    } else {
194        activeMask = thiz->mActiveMask;
195    }
196    while (activeMask) {
197        unsigned i = ctz(activeMask);
198        assert(MAX_TRACK > i);
199        activeMask &= ~(1 << i);
200        Track *track = &thiz->mTracks[i];
201
202        // track is allocated
203
204        if (!track_check(track)) {
205            continue;
206        }
207
208        // track is playing
209        void *dstWriter = pBuffer;
210        unsigned desired = size;
211        SLboolean trackContributedToMix = SL_BOOLEAN_FALSE;
212        float gains[STEREO_CHANNELS];
213        Summary summaries[STEREO_CHANNELS];
214        unsigned channel;
215        for (channel = 0; channel < STEREO_CHANNELS; ++channel) {
216            float gain = track->mGains[channel];
217            gains[channel] = gain;
218            Summary summary;
219            if (gain <= 0.001) {
220                summary = GAIN_MUTE;
221            } else if (gain >= 0.999) {
222                summary = GAIN_UNITY;
223            } else {
224                summary = GAIN_OTHER;
225            }
226            summaries[channel] = summary;
227        }
228        while (desired > 0) {
229            unsigned actual = desired;
230            if (track->mAvail < actual) {
231                actual = track->mAvail;
232            }
233            // force actual to be a frame multiple
234            if (actual > 0) {
235                assert(NULL != track->mReader);
236                stereo *mixBuffer = (stereo *) dstWriter;
237                const stereo *source = (const stereo *) track->mReader;
238                unsigned j;
239                if (GAIN_MUTE != summaries[0] || GAIN_MUTE != summaries[1]) {
240                    if (mixBufferHasData) {
241                        // apply gain during add
242                        if (GAIN_UNITY != summaries[0] || GAIN_UNITY != summaries[1]) {
243                            for (j = 0; j < actual; j += sizeof(stereo), ++mixBuffer, ++source) {
244                                mixBuffer->left += (short) (source->left * track->mGains[0]);
245                                mixBuffer->right += (short) (source->right * track->mGains[1]);
246                            }
247                        // no gain adjustment needed, so do a simple add
248                        } else {
249                            for (j = 0; j < actual; j += sizeof(stereo), ++mixBuffer, ++source) {
250                                mixBuffer->left += source->left;
251                                mixBuffer->right += source->right;
252                            }
253                        }
254                    } else {
255                        // apply gain during copy
256                        if (GAIN_UNITY != summaries[0] || GAIN_UNITY != summaries[1]) {
257                            for (j = 0; j < actual; j += sizeof(stereo), ++mixBuffer, ++source) {
258                                mixBuffer->left = (short) (source->left * track->mGains[0]);
259                                mixBuffer->right = (short) (source->right * track->mGains[1]);
260                            }
261                        // no gain adjustment needed, so do a simple copy
262                        } else {
263                            memcpy(dstWriter, track->mReader, actual);
264                        }
265                    }
266                    trackContributedToMix = SL_BOOLEAN_TRUE;
267                }
268                dstWriter = (char *) dstWriter + actual;
269                desired -= actual;
270                track->mReader = (char *) track->mReader + actual;
271                track->mAvail -= actual;
272                if (track->mAvail == 0) {
273                    IBufferQueue *bufferQueue = &track->mAudioPlayer->mBufferQueue;
274                    interface_lock_exclusive(bufferQueue);
275                    const BufferHeader *oldFront, *newFront, *rear;
276                    oldFront = bufferQueue->mFront;
277                    rear = bufferQueue->mRear;
278                    // a buffer stays on queue while playing, so it better still be there
279                    assert(oldFront != rear);
280                    newFront = oldFront;
281                    if (++newFront == &bufferQueue->mArray[bufferQueue->mNumBuffers + 1]) {
282                        newFront = bufferQueue->mArray;
283                    }
284                    bufferQueue->mFront = (BufferHeader *) newFront;
285                    assert(0 < bufferQueue->mState.count);
286                    --bufferQueue->mState.count;
287                    if (newFront != rear) {
288                        // we don't acknowledge application requests between buffers
289                        // within the same mixer frame
290                        assert(0 < bufferQueue->mState.count);
291                        track->mReader = newFront->mBuffer;
292                        track->mAvail = newFront->mSize;
293                    }
294                    // else we would set play state to playable but not playing during next mixer
295                    // frame if the queue is still empty at that time
296                    ++bufferQueue->mState.playIndex;
297                    slBufferQueueCallback callback = bufferQueue->mCallback;
298                    void *context = bufferQueue->mContext;
299                    interface_unlock_exclusive(bufferQueue);
300                    // The callback function is called on each buffer completion
301                    if (NULL != callback) {
302                        (*callback)((SLBufferQueueItf) bufferQueue, context);
303                        // Maybe it enqueued another buffer, or maybe it didn't.
304                        // We will find out later during the next mixer frame.
305                    }
306                }
307                // no lock, but safe because noone else updates this field
308                track->mFramesMixed += actual >> 2;    // sizeof(short) * STEREO_CHANNELS
309                continue;
310            }
311            // we need more data: desired > 0 but actual == 0
312            if (track_check(track)) {
313                continue;
314            }
315            // underflow: clear out rest of partial buffer (NTH synthesize comfort noise)
316            if (!mixBufferHasData && trackContributedToMix) {
317                memset(dstWriter, 0, actual);
318            }
319            break;
320        }
321        if (trackContributedToMix) {
322            mixBufferHasData = SL_BOOLEAN_TRUE;
323        }
324    }
325    object_unlock_exclusive(thisObject);
326    // No active tracks, so output silence
327    if (!mixBufferHasData) {
328        memset(pBuffer, 0, size);
329    }
330
331    SL_LEAVE_INTERFACE_VOID
332}
333
334
335static const struct SLOutputMixExtItf_ IOutputMixExt_Itf = {
336    IOutputMixExt_FillBuffer
337};
338
339void IOutputMixExt_init(void *self)
340{
341    IOutputMixExt *thiz = (IOutputMixExt *) self;
342    thiz->mItf = &IOutputMixExt_Itf;
343    thiz->mActiveMask = 0;
344    Track *track = &thiz->mTracks[0];
345    unsigned i;
346    for (i = 0; i < MAX_TRACK; ++i, ++track) {
347        track->mAudioPlayer = NULL;
348    }
349    thiz->mDestroyRequested = SL_BOOLEAN_FALSE;
350}
351
352
353/** \brief Called by Engine::CreateAudioPlayer to allocate a track */
354
355SLresult IOutputMixExt_checkAudioPlayerSourceSink(CAudioPlayer *thiz)
356{
357    thiz->mTrack = NULL;
358
359    // check the source for compatibility
360    switch (thiz->mDataSource.mLocator.mLocatorType) {
361    case SL_DATALOCATOR_BUFFERQUEUE:
362#ifdef ANDROID
363    case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
364#endif
365        switch (thiz->mDataSource.mFormat.mFormatType) {
366        case SL_DATAFORMAT_PCM:
367#ifdef USE_SDL
368            // SDL is hard-coded to 44.1 kHz, and there is no sample rate converter
369            if (SL_SAMPLINGRATE_44_1 != thiz->mDataSource.mFormat.mPCM.samplesPerSec)
370                return SL_RESULT_CONTENT_UNSUPPORTED;
371#endif
372            break;
373        default:
374            break;
375        }
376        break;
377    default:
378        break;
379    }
380
381    // check the sink for compatibility
382    const SLDataSink *pAudioSnk = &thiz->mDataSink.u.mSink;
383    Track *track = NULL;
384    switch (*(SLuint32 *)pAudioSnk->pLocator) {
385    case SL_DATALOCATOR_OUTPUTMIX:
386        {
387        // pAudioSnk->pFormat is ignored
388        IOutputMixExt *omExt = &((COutputMix *) ((SLDataLocator_OutputMix *)
389            pAudioSnk->pLocator)->outputMix)->mOutputMixExt;
390        // allocate an entry within OutputMix for this track
391        interface_lock_exclusive(omExt);
392        unsigned availMask = ~omExt->mActiveMask;
393        if (!availMask) {
394            interface_unlock_exclusive(omExt);
395            // All track slots full in output mix
396            return SL_RESULT_MEMORY_FAILURE;
397        }
398        unsigned i = ctz(availMask);
399        assert(MAX_TRACK > i);
400        omExt->mActiveMask |= 1 << i;
401        track = &omExt->mTracks[i];
402        track->mAudioPlayer = NULL;    // only field that is accessed before full initialization
403        interface_unlock_exclusive(omExt);
404        thiz->mTrack = track;
405        thiz->mGains[0] = 1.0f;
406        thiz->mGains[1] = 1.0f;
407        thiz->mDestroyRequested = SL_BOOLEAN_FALSE;
408        }
409        break;
410    default:
411        return SL_RESULT_CONTENT_UNSUPPORTED;
412    }
413
414    assert(NULL != track);
415    track->mBufferQueue = &thiz->mBufferQueue;
416    track->mAudioPlayer = thiz;
417    track->mReader = NULL;
418    track->mAvail = 0;
419    track->mGains[0] = 1.0f;
420    track->mGains[1] = 1.0f;
421    track->mFramesMixed = 0;
422    return SL_RESULT_SUCCESS;
423}
424
425
426/** \brief Called when a gain-related field (mute, solo, volume, stereo position, etc.) updated */
427
428void audioPlayerGainUpdate(CAudioPlayer *audioPlayer)
429{
430    SLboolean mute = audioPlayer->mVolume.mMute;
431    SLuint8 muteMask = audioPlayer->mMuteMask;
432    SLuint8 soloMask = audioPlayer->mSoloMask;
433    SLmillibel level = audioPlayer->mVolume.mLevel;
434    SLboolean enableStereoPosition = audioPlayer->mVolume.mEnableStereoPosition;
435    SLpermille stereoPosition = audioPlayer->mVolume.mStereoPosition;
436
437    if (soloMask) {
438        muteMask |= ~soloMask;
439    }
440    if (mute || !(~muteMask & 3)) {
441        audioPlayer->mGains[0] = 0.0f;
442        audioPlayer->mGains[1] = 0.0f;
443    } else {
444        float playerGain = powf(10.0f, level / 2000.0f);
445        unsigned channel;
446        for (channel = 0; channel < STEREO_CHANNELS; ++channel) {
447            float gain;
448            if (muteMask & (1 << channel)) {
449                gain = 0.0f;
450            } else {
451                gain = playerGain;
452                if (enableStereoPosition) {
453                    switch (channel) {
454                    case 0:
455                        if (stereoPosition > 0) {
456                            gain *= (1000 - stereoPosition) / 1000.0f;
457                        }
458                        break;
459                    case 1:
460                        if (stereoPosition < 0) {
461                            gain *= (1000 + stereoPosition) / 1000.0f;
462                        }
463                        break;
464                    default:
465                        assert(SL_BOOLEAN_FALSE);
466                        break;
467                    }
468                }
469            }
470            audioPlayer->mGains[channel] = gain;
471        }
472    }
473}
474