blob: f034164a2589d71d8e0184f6ef29e08ec04fc308 [file] [log] [blame]
Glenn Kastena8190fc2012-12-03 17:06:56 -08001/*
2 * Copyright (C) 2007 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#define LOG_TAG "AudioTrackShared"
18//#define LOG_NDEBUG 0
19
20#include <private/media/AudioTrackShared.h>
21#include <utils/Log.h>
Glenn Kasten9f80dd22012-12-18 15:57:32 -080022extern "C" {
23#include "../private/bionic_futex.h"
24}
Glenn Kastena8190fc2012-12-03 17:06:56 -080025
26namespace android {
27
28audio_track_cblk_t::audio_track_cblk_t()
Glenn Kasten9f80dd22012-12-18 15:57:32 -080029 : server(0), frameCount_(0), mFutex(0), mMinimum(0),
30 mVolumeLR(0x10001000), mSampleRate(0), mSendLevel(0), mName(0), flags(0)
31{
32 memset(&u, 0, sizeof(u));
33}
34
35// ---------------------------------------------------------------------------
36
37Proxy::Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize,
38 bool isOut, bool clientInServer)
39 : mCblk(cblk), mBuffers(buffers), mFrameCount(frameCount), mFrameSize(frameSize),
40 mFrameCountP2(roundup(frameCount)), mIsOut(isOut), mClientInServer(clientInServer),
41 mIsShutdown(false)
Glenn Kastena8190fc2012-12-03 17:06:56 -080042{
43}
44
Glenn Kasten9f80dd22012-12-18 15:57:32 -080045// ---------------------------------------------------------------------------
46
47ClientProxy::ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
48 size_t frameSize, bool isOut, bool clientInServer)
49 : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), mEpoch(0)
Glenn Kastena8190fc2012-12-03 17:06:56 -080050{
Glenn Kastena8190fc2012-12-03 17:06:56 -080051}
52
Glenn Kasten9f80dd22012-12-18 15:57:32 -080053const struct timespec ClientProxy::kForever = {INT_MAX /*tv_sec*/, 0 /*tv_nsec*/};
54const struct timespec ClientProxy::kNonBlocking = {0 /*tv_sec*/, 0 /*tv_nsec*/};
55
56#define MEASURE_NS 10000000 // attempt to provide accurate timeouts if requested >= MEASURE_NS
57
58// To facilitate quicker recovery from server failure, this value limits the timeout per each futex
59// wait. However it does not protect infinite timeouts. If defined to be zero, there is no limit.
60// FIXME May not be compatible with audio tunneling requirements where timeout should be in the
61// order of minutes.
62#define MAX_SEC 5
63
64status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,
65 struct timespec *elapsed)
Glenn Kastena8190fc2012-12-03 17:06:56 -080066{
Glenn Kasten9f80dd22012-12-18 15:57:32 -080067 if (buffer == NULL || buffer->mFrameCount == 0) {
68 ALOGE("%s BAD_VALUE", __func__);
69 return BAD_VALUE;
Glenn Kastena8190fc2012-12-03 17:06:56 -080070 }
Glenn Kasten9f80dd22012-12-18 15:57:32 -080071 struct timespec total; // total elapsed time spent waiting
72 total.tv_sec = 0;
73 total.tv_nsec = 0;
74 bool measure = elapsed != NULL; // whether to measure total elapsed time spent waiting
Glenn Kastena8190fc2012-12-03 17:06:56 -080075
Glenn Kasten9f80dd22012-12-18 15:57:32 -080076 status_t status;
77 enum {
78 TIMEOUT_ZERO, // requested == NULL || *requested == 0
79 TIMEOUT_INFINITE, // *requested == infinity
80 TIMEOUT_FINITE, // 0 < *requested < infinity
81 TIMEOUT_CONTINUE, // additional chances after TIMEOUT_FINITE
82 } timeout;
83 if (requested == NULL) {
84 timeout = TIMEOUT_ZERO;
85 } else if (requested->tv_sec == 0 && requested->tv_nsec == 0) {
86 timeout = TIMEOUT_ZERO;
87 } else if (requested->tv_sec == INT_MAX) {
88 timeout = TIMEOUT_INFINITE;
Glenn Kastena8190fc2012-12-03 17:06:56 -080089 } else {
Glenn Kasten9f80dd22012-12-18 15:57:32 -080090 timeout = TIMEOUT_FINITE;
91 if (requested->tv_sec > 0 || requested->tv_nsec >= MEASURE_NS) {
92 measure = true;
93 }
Glenn Kastena8190fc2012-12-03 17:06:56 -080094 }
Glenn Kasten9f80dd22012-12-18 15:57:32 -080095 struct timespec before;
96 bool beforeIsValid = false;
97 audio_track_cblk_t* cblk = mCblk;
98 bool ignoreInitialPendingInterrupt = true;
99 // check for shared memory corruption
100 if (mIsShutdown) {
101 status = NO_INIT;
102 goto end;
103 }
104 for (;;) {
105 int32_t flags = android_atomic_and(~CBLK_INTERRUPT, &cblk->flags);
106 // check for track invalidation by server, or server death detection
107 if (flags & CBLK_INVALID) {
108 ALOGV("Track invalidated");
109 status = DEAD_OBJECT;
110 goto end;
111 }
112 // check for obtainBuffer interrupted by client
113 if (!ignoreInitialPendingInterrupt && (flags & CBLK_INTERRUPT)) {
114 ALOGV("obtainBuffer() interrupted by client");
115 status = -EINTR;
116 goto end;
117 }
118 ignoreInitialPendingInterrupt = false;
119 // compute number of frames available to write (AudioTrack) or read (AudioRecord)
120 int32_t front;
121 int32_t rear;
122 if (mIsOut) {
123 // The barrier following the read of mFront is probably redundant.
124 // We're about to perform a conditional branch based on 'filled',
125 // which will force the processor to observe the read of mFront
126 // prior to allowing data writes starting at mRaw.
127 // However, the processor may support speculative execution,
128 // and be unable to undo speculative writes into shared memory.
129 // The barrier will prevent such speculative execution.
130 front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
131 rear = cblk->u.mStreaming.mRear;
Glenn Kastena8190fc2012-12-03 17:06:56 -0800132 } else {
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800133 // On the other hand, this barrier is required.
134 rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
135 front = cblk->u.mStreaming.mFront;
136 }
137 ssize_t filled = rear - front;
138 // pipe should not be overfull
139 if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
140 ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
141 mIsShutdown = true;
142 status = NO_INIT;
143 goto end;
144 }
145 // don't allow filling pipe beyond the nominal size
146 size_t avail = mIsOut ? mFrameCount - filled : filled;
147 if (avail > 0) {
148 // 'avail' may be non-contiguous, so return only the first contiguous chunk
149 size_t part1;
150 if (mIsOut) {
151 rear &= mFrameCountP2 - 1;
152 part1 = mFrameCountP2 - rear;
153 } else {
154 front &= mFrameCountP2 - 1;
155 part1 = mFrameCountP2 - front;
Glenn Kastena8190fc2012-12-03 17:06:56 -0800156 }
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800157 if (part1 > avail) {
158 part1 = avail;
Glenn Kastena8190fc2012-12-03 17:06:56 -0800159 }
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800160 if (part1 > buffer->mFrameCount) {
161 part1 = buffer->mFrameCount;
162 }
163 buffer->mFrameCount = part1;
164 buffer->mRaw = part1 > 0 ?
165 &((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL;
166 buffer->mNonContig = avail - part1;
167 // mUnreleased = part1;
168 status = NO_ERROR;
169 break;
170 }
171 struct timespec remaining;
172 const struct timespec *ts;
173 switch (timeout) {
174 case TIMEOUT_ZERO:
175 status = WOULD_BLOCK;
176 goto end;
177 case TIMEOUT_INFINITE:
178 ts = NULL;
179 break;
180 case TIMEOUT_FINITE:
181 timeout = TIMEOUT_CONTINUE;
182 if (MAX_SEC == 0) {
183 ts = requested;
184 break;
185 }
186 // fall through
187 case TIMEOUT_CONTINUE:
188 // FIXME we do not retry if requested < 10ms? needs documentation on this state machine
189 if (!measure || requested->tv_sec < total.tv_sec ||
190 (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) {
191 status = TIMED_OUT;
192 goto end;
193 }
194 remaining.tv_sec = requested->tv_sec - total.tv_sec;
195 if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0) {
196 remaining.tv_nsec += 1000000000;
197 remaining.tv_sec++;
198 }
199 if (0 < MAX_SEC && MAX_SEC < remaining.tv_sec) {
200 remaining.tv_sec = MAX_SEC;
201 remaining.tv_nsec = 0;
202 }
203 ts = &remaining;
204 break;
205 default:
206 LOG_FATAL("%s timeout=%d", timeout);
207 ts = NULL;
208 break;
209 }
210 int32_t old = android_atomic_dec(&cblk->mFutex);
211 if (old <= 0) {
212 int rc;
213 if (measure && !beforeIsValid) {
214 clock_gettime(CLOCK_MONOTONIC, &before);
215 beforeIsValid = true;
216 }
217 int ret = __futex_syscall4(&cblk->mFutex,
218 mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old - 1, ts);
219 // update total elapsed time spent waiting
220 if (measure) {
221 struct timespec after;
222 clock_gettime(CLOCK_MONOTONIC, &after);
223 total.tv_sec += after.tv_sec - before.tv_sec;
224 long deltaNs = after.tv_nsec - before.tv_nsec;
225 if (deltaNs < 0) {
226 deltaNs += 1000000000;
227 total.tv_sec--;
228 }
229 if ((total.tv_nsec += deltaNs) >= 1000000000) {
230 total.tv_nsec -= 1000000000;
231 total.tv_sec++;
232 }
233 before = after;
234 beforeIsValid = true;
235 }
236 switch (ret) {
237 case 0: // normal wakeup by server, or by binderDied()
238 case -EWOULDBLOCK: // benign race condition with server
239 case -EINTR: // wait was interrupted by signal or other spurious wakeup
240 case -ETIMEDOUT: // time-out expired
241 break;
242 default:
243 ALOGE("%s unexpected error %d", __func__, ret);
244 status = -ret;
245 goto end;
246 }
247 }
248 }
249
250end:
251 if (status != NO_ERROR) {
252 buffer->mFrameCount = 0;
253 buffer->mRaw = NULL;
254 buffer->mNonContig = 0;
255 }
256 if (elapsed != NULL) {
257 *elapsed = total;
258 }
259 if (requested == NULL) {
260 requested = &kNonBlocking;
261 }
262 if (measure) {
263 ALOGV("requested %d.%03d elapsed %d.%03d", requested->tv_sec, requested->tv_nsec / 1000000,
264 total.tv_sec, total.tv_nsec / 1000000);
265 }
266 return status;
267}
268
269void ClientProxy::releaseBuffer(Buffer* buffer)
270{
271 size_t stepCount = buffer->mFrameCount;
272 // FIXME
273 // check mUnreleased
274 // verify that stepCount <= frameCount returned by the last obtainBuffer()
275 // verify stepCount not > total frame count of pipe
276 if (stepCount == 0) {
277 return;
278 }
279 audio_track_cblk_t* cblk = mCblk;
280 // Both of these barriers are required
281 if (mIsOut) {
282 int32_t rear = cblk->u.mStreaming.mRear;
283 android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
284 } else {
285 int32_t front = cblk->u.mStreaming.mFront;
286 android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront);
287 }
288}
289
290void ClientProxy::binderDied()
291{
292 audio_track_cblk_t* cblk = mCblk;
293 if (!(android_atomic_or(CBLK_INVALID, &cblk->flags) & CBLK_INVALID)) {
294 // it seems that a FUTEX_WAKE_PRIVATE will not wake a FUTEX_WAIT, even within same process
295 (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
296 1);
297 }
298}
299
300void ClientProxy::interrupt()
301{
302 audio_track_cblk_t* cblk = mCblk;
303 if (!(android_atomic_or(CBLK_INTERRUPT, &cblk->flags) & CBLK_INTERRUPT)) {
304 (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
305 1);
306 }
307}
308
309size_t ClientProxy::getMisalignment()
310{
311 audio_track_cblk_t* cblk = mCblk;
312 return (mFrameCountP2 - (mIsOut ? cblk->u.mStreaming.mRear : cblk->u.mStreaming.mFront)) &
313 (mFrameCountP2 - 1);
314}
315
316// ---------------------------------------------------------------------------
317
318void AudioTrackClientProxy::flush()
319{
320 mCblk->u.mStreaming.mFlush++;
321}
322
323// ---------------------------------------------------------------------------
324
325StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers,
326 size_t frameCount, size_t frameSize)
327 : AudioTrackClientProxy(cblk, buffers, frameCount, frameSize),
328 mMutator(&cblk->u.mStatic.mSingleStateQueue), mBufferPosition(0)
329{
330}
331
332void StaticAudioTrackClientProxy::flush()
333{
334 LOG_FATAL("static flush");
335}
336
337void StaticAudioTrackClientProxy::setLoop(size_t loopStart, size_t loopEnd, int loopCount)
338{
339 StaticAudioTrackState newState;
340 newState.mLoopStart = loopStart;
341 newState.mLoopEnd = loopEnd;
342 newState.mLoopCount = loopCount;
343 mBufferPosition = loopStart;
344 (void) mMutator.push(newState);
345}
346
347size_t StaticAudioTrackClientProxy::getBufferPosition()
348{
349 size_t bufferPosition;
350 if (mMutator.ack()) {
351 bufferPosition = mCblk->u.mStatic.mBufferPosition;
352 if (bufferPosition > mFrameCount) {
353 bufferPosition = mFrameCount;
Glenn Kastena8190fc2012-12-03 17:06:56 -0800354 }
355 } else {
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800356 bufferPosition = mBufferPosition;
Glenn Kastena8190fc2012-12-03 17:06:56 -0800357 }
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800358 return bufferPosition;
Glenn Kastena8190fc2012-12-03 17:06:56 -0800359}
360
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800361// ---------------------------------------------------------------------------
362
363ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
364 size_t frameSize, bool isOut, bool clientInServer)
365 : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), mUnreleased(0),
366 mAvailToClient(0), mFlush(0), mDeferWake(false)
Glenn Kastena8190fc2012-12-03 17:06:56 -0800367{
Glenn Kastena8190fc2012-12-03 17:06:56 -0800368}
369
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800370status_t ServerProxy::obtainBuffer(Buffer* buffer)
371{
372 if (mIsShutdown) {
373 buffer->mFrameCount = 0;
374 buffer->mRaw = NULL;
375 buffer->mNonContig = 0;
376 mUnreleased = 0;
377 return NO_INIT;
378 }
379 audio_track_cblk_t* cblk = mCblk;
380 // compute number of frames available to write (AudioTrack) or read (AudioRecord),
381 // or use previous cached value from framesReady(), with added barrier if it omits.
382 int32_t front;
383 int32_t rear;
384 // See notes on barriers at ClientProxy::obtainBuffer()
385 if (mIsOut) {
386 int32_t flush = cblk->u.mStreaming.mFlush;
387 rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
388 if (flush != mFlush) {
389 front = rear;
390 mFlush = flush;
391 } else {
392 front = cblk->u.mStreaming.mFront;
393 }
394 } else {
395 front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
396 rear = cblk->u.mStreaming.mRear;
397 }
398 ssize_t filled = rear - front;
399 // pipe should not already be overfull
400 if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
401 ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
402 mIsShutdown = true;
403 }
404 if (mIsShutdown) {
405 buffer->mFrameCount = 0;
406 buffer->mRaw = NULL;
407 buffer->mNonContig = 0;
408 mUnreleased = 0;
409 return NO_INIT;
410 }
411 // don't allow filling pipe beyond the nominal size
412 size_t availToServer;
413 if (mIsOut) {
414 availToServer = filled;
415 mAvailToClient = mFrameCount - filled;
416 } else {
417 availToServer = mFrameCount - filled;
418 mAvailToClient = filled;
419 }
420 // 'availToServer' may be non-contiguous, so return only the first contiguous chunk
421 size_t part1;
422 if (mIsOut) {
423 front &= mFrameCountP2 - 1;
424 part1 = mFrameCountP2 - front;
425 } else {
426 rear &= mFrameCountP2 - 1;
427 part1 = mFrameCountP2 - rear;
428 }
429 if (part1 > availToServer) {
430 part1 = availToServer;
431 }
432 size_t ask = buffer->mFrameCount;
433 if (part1 > ask) {
434 part1 = ask;
435 }
436 // is assignment redundant in some cases?
437 buffer->mFrameCount = part1;
438 buffer->mRaw = part1 > 0 ?
439 &((char *) mBuffers)[(mIsOut ? front : rear) * mFrameSize] : NULL;
440 buffer->mNonContig = availToServer - part1;
441 mUnreleased = part1;
442 // optimization to avoid waking up the client too early
443 // FIXME need to test for recording
444 mDeferWake = part1 < ask && availToServer >= ask;
445 return part1 > 0 ? NO_ERROR : WOULD_BLOCK;
446}
447
448void ServerProxy::releaseBuffer(Buffer* buffer)
449{
450 if (mIsShutdown) {
451 buffer->mFrameCount = 0;
452 buffer->mRaw = NULL;
453 buffer->mNonContig = 0;
454 return;
455 }
456 size_t stepCount = buffer->mFrameCount;
457 LOG_ALWAYS_FATAL_IF(stepCount > mUnreleased);
458 if (stepCount == 0) {
459 buffer->mRaw = NULL;
460 buffer->mNonContig = 0;
461 return;
462 }
463 mUnreleased -= stepCount;
464 audio_track_cblk_t* cblk = mCblk;
465 if (mIsOut) {
466 int32_t front = cblk->u.mStreaming.mFront;
467 android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront);
468 } else {
469 int32_t rear = cblk->u.mStreaming.mRear;
470 android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
471 }
472
473 mCblk->server += stepCount;
474
475 size_t half = mFrameCount / 2;
476 if (half == 0) {
477 half = 1;
478 }
479 size_t minimum = cblk->mMinimum;
480 if (minimum == 0) {
481 minimum = mIsOut ? half : 1;
482 } else if (minimum > half) {
483 minimum = half;
484 }
485 if (!mDeferWake && mAvailToClient + stepCount >= minimum) {
486 ALOGV("mAvailToClient=%u stepCount=%u minimum=%u", mAvailToClient, stepCount, minimum);
487 // could client be sleeping, or not need this increment and counter overflows?
488 int32_t old = android_atomic_inc(&cblk->mFutex);
489 if (old == -1) {
490 (void) __futex_syscall3(&cblk->mFutex,
491 mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
492 }
493 }
494
495 buffer->mFrameCount = 0;
496 buffer->mRaw = NULL;
497 buffer->mNonContig = 0;
498}
499
500// ---------------------------------------------------------------------------
501
502size_t AudioTrackServerProxy::framesReady()
503{
504 LOG_ALWAYS_FATAL_IF(!mIsOut);
505
506 if (mIsShutdown) {
507 return 0;
508 }
509 audio_track_cblk_t* cblk = mCblk;
510 // the acquire might not be necessary since not doing a subsequent read
511 int32_t rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
512 ssize_t filled = rear - cblk->u.mStreaming.mFront;
513 // pipe should not already be overfull
514 if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
515 ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
516 mIsShutdown = true;
517 return 0;
518 }
519 // cache this value for later use by obtainBuffer(), with added barrier
520 // and racy if called by normal mixer thread
521 // ignores flush(), so framesReady() may report a larger mFrameCount than obtainBuffer()
522 return filled;
523}
524
525// ---------------------------------------------------------------------------
526
527StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
528 size_t frameCount, size_t frameSize)
529 : AudioTrackServerProxy(cblk, buffers, frameCount, frameSize),
530 mObserver(&cblk->u.mStatic.mSingleStateQueue), mPosition(0),
531 mEnd(frameCount), mFramesReadyIsCalledByMultipleThreads(false)
532{
533 mState.mLoopStart = 0;
534 mState.mLoopEnd = 0;
535 mState.mLoopCount = 0;
536}
537
538void StaticAudioTrackServerProxy::framesReadyIsCalledByMultipleThreads()
539{
540 mFramesReadyIsCalledByMultipleThreads = true;
541}
542
543size_t StaticAudioTrackServerProxy::framesReady()
544{
545 // FIXME
546 // This is racy if called by normal mixer thread,
547 // as we're reading 2 independent variables without a lock.
548 // Can't call mObserver.poll(), as we might be called from wrong thread.
549 // If looping is enabled, should return a higher number (since includes non-contiguous).
550 size_t position = mPosition;
551 if (!mFramesReadyIsCalledByMultipleThreads) {
552 ssize_t positionOrStatus = pollPosition();
553 if (positionOrStatus >= 0) {
554 position = (size_t) positionOrStatus;
555 }
556 }
557 size_t end = mEnd;
558 return position < end ? end - position : 0;
559}
560
561ssize_t StaticAudioTrackServerProxy::pollPosition()
562{
563 size_t position = mPosition;
564 StaticAudioTrackState state;
565 if (mObserver.poll(state)) {
566 bool valid = false;
567 size_t loopStart = state.mLoopStart;
568 size_t loopEnd = state.mLoopEnd;
569 if (state.mLoopCount == 0) {
570 if (loopStart > mFrameCount) {
571 loopStart = mFrameCount;
572 }
573 // ignore loopEnd
574 mPosition = position = loopStart;
575 mEnd = mFrameCount;
576 mState.mLoopCount = 0;
577 valid = true;
578 } else {
579 if (loopStart < loopEnd && loopEnd <= mFrameCount &&
580 loopEnd - loopStart >= MIN_LOOP) {
581 if (!(loopStart <= position && position < loopEnd)) {
582 mPosition = position = loopStart;
583 }
584 mEnd = loopEnd;
585 mState = state;
586 valid = true;
587 }
588 }
589 if (!valid) {
590 ALOGE("%s client pushed an invalid state, shutting down", __func__);
591 mIsShutdown = true;
592 return (ssize_t) NO_INIT;
593 }
594 mCblk->u.mStatic.mBufferPosition = position;
595 }
596 return (ssize_t) position;
597}
598
599status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer)
600{
601 if (mIsShutdown) {
602 buffer->mFrameCount = 0;
603 buffer->mRaw = NULL;
604 buffer->mNonContig = 0;
605 mUnreleased = 0;
606 return NO_INIT;
607 }
608 ssize_t positionOrStatus = pollPosition();
609 if (positionOrStatus < 0) {
610 buffer->mFrameCount = 0;
611 buffer->mRaw = NULL;
612 buffer->mNonContig = 0;
613 mUnreleased = 0;
614 return (status_t) positionOrStatus;
615 }
616 size_t position = (size_t) positionOrStatus;
617 size_t avail;
618 if (position < mEnd) {
619 avail = mEnd - position;
620 size_t wanted = buffer->mFrameCount;
621 if (avail < wanted) {
622 buffer->mFrameCount = avail;
623 } else {
624 avail = wanted;
625 }
626 buffer->mRaw = &((char *) mBuffers)[position * mFrameSize];
627 } else {
628 avail = 0;
629 buffer->mFrameCount = 0;
630 buffer->mRaw = NULL;
631 }
632 buffer->mNonContig = 0; // FIXME should be > 0 for looping
633 mUnreleased = avail;
634 return NO_ERROR;
635}
636
637void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer)
638{
639 size_t stepCount = buffer->mFrameCount;
640 LOG_ALWAYS_FATAL_IF(stepCount > mUnreleased);
641 if (stepCount == 0) {
642 buffer->mRaw = NULL;
643 buffer->mNonContig = 0;
644 return;
645 }
646 mUnreleased -= stepCount;
647 audio_track_cblk_t* cblk = mCblk;
648 size_t position = mPosition;
649 size_t newPosition = position + stepCount;
650 int32_t setFlags = 0;
651 if (!(position <= newPosition && newPosition <= mFrameCount)) {
652 ALOGW("%s newPosition %u outside [%u, %u]", __func__, newPosition, position, mFrameCount);
653 newPosition = mFrameCount;
654 } else if (mState.mLoopCount != 0 && newPosition == mState.mLoopEnd) {
655 if (mState.mLoopCount == -1 || --mState.mLoopCount != 0) {
656 newPosition = mState.mLoopStart;
657 setFlags = CBLK_LOOP_CYCLE;
658 } else {
659 mEnd = mFrameCount; // this is what allows playback to continue after the loop
660 setFlags = CBLK_LOOP_FINAL;
661 }
662 }
663 if (newPosition == mFrameCount) {
664 setFlags |= CBLK_BUFFER_END;
665 }
666 mPosition = newPosition;
667
668 cblk->server += stepCount;
669 cblk->u.mStatic.mBufferPosition = newPosition;
670 if (setFlags != 0) {
671 (void) android_atomic_or(setFlags, &cblk->flags);
672 // this would be a good place to wake a futex
673 }
674
675 buffer->mFrameCount = 0;
676 buffer->mRaw = NULL;
677 buffer->mNonContig = 0;
678}
679
680// ---------------------------------------------------------------------------
681
Glenn Kastena8190fc2012-12-03 17:06:56 -0800682} // namespace android