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