1/*
2 * Copyright (C) 2012 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 "AudioBufferProviderSource"
18//#define LOG_NDEBUG 0
19
20#include <cutils/compiler.h>
21#include <utils/Log.h>
22#include <media/nbaio/AudioBufferProviderSource.h>
23
24namespace android {
25
26AudioBufferProviderSource::AudioBufferProviderSource(AudioBufferProvider *provider,
27                                                     const NBAIO_Format& format) :
28    NBAIO_Source(format), mProvider(provider), mConsumed(0)
29{
30    ALOG_ASSERT(provider != NULL);
31    ALOG_ASSERT(Format_isValid(format));
32}
33
34AudioBufferProviderSource::~AudioBufferProviderSource()
35{
36    if (mBuffer.raw != NULL) {
37        mProvider->releaseBuffer(&mBuffer);
38    }
39}
40
41ssize_t AudioBufferProviderSource::availableToRead()
42{
43    if (CC_UNLIKELY(!mNegotiated)) {
44        return NEGOTIATE;
45    }
46    return mBuffer.raw != NULL ? mBuffer.frameCount - mConsumed : 0;
47}
48
49ssize_t AudioBufferProviderSource::read(void *buffer, size_t count)
50{
51    if (CC_UNLIKELY(!mNegotiated)) {
52        return NEGOTIATE;
53    }
54    if (CC_UNLIKELY(mBuffer.raw == NULL)) {
55        mBuffer.frameCount = count;
56        status_t status = mProvider->getNextBuffer(&mBuffer);
57        if (status != OK) {
58            return status == NOT_ENOUGH_DATA ? (ssize_t) WOULD_BLOCK : (ssize_t) status;
59        }
60        ALOG_ASSERT(mBuffer.raw != NULL);
61        // mConsumed is 0 either from constructor or after releaseBuffer()
62    }
63    size_t available = mBuffer.frameCount - mConsumed;
64    if (CC_UNLIKELY(count > available)) {
65        count = available;
66    }
67    // count could be zero, either because count was zero on entry or
68    // available is zero, but both are unlikely so don't check for that
69    memcpy(buffer, (char *) mBuffer.raw + (mConsumed * mFrameSize), count * mFrameSize);
70    if (CC_UNLIKELY((mConsumed += count) >= mBuffer.frameCount)) {
71        mProvider->releaseBuffer(&mBuffer);
72        mBuffer.raw = NULL;
73        mConsumed = 0;
74    }
75    mFramesRead += count;
76    // For better responsiveness with large values of count,
77    // return a short count rather than continuing with next buffer.
78    // This gives the caller a chance to interpolate other actions.
79    return count;
80}
81
82ssize_t AudioBufferProviderSource::readVia(readVia_t via, size_t total, void *user, size_t block)
83{
84    if (CC_UNLIKELY(!mNegotiated)) {
85        return NEGOTIATE;
86    }
87    if (CC_UNLIKELY(block == 0)) {
88        block = ~0;
89    }
90    for (size_t accumulator = 0; ; ) {
91        ALOG_ASSERT(accumulator <= total);
92        size_t count = total - accumulator;
93        if (CC_UNLIKELY(count == 0)) {
94            return accumulator;
95        }
96        if (CC_LIKELY(count > block)) {
97            count = block;
98        }
99        // 1 <= count <= block
100        if (CC_UNLIKELY(mBuffer.raw == NULL)) {
101            mBuffer.frameCount = count;
102            status_t status = mProvider->getNextBuffer(&mBuffer);
103            if (CC_LIKELY(status == OK)) {
104                ALOG_ASSERT(mBuffer.raw != NULL && mBuffer.frameCount <= count);
105                // mConsumed is 0 either from constructor or after releaseBuffer()
106                continue;
107            }
108            // FIXME simplify logic - does the initial count and block checks again for no reason;
109            //       don't you just want to fall through to the size_t available line?
110            if (CC_LIKELY(status == NOT_ENOUGH_DATA)) {
111                status = WOULD_BLOCK;
112            }
113            return accumulator > 0 ? accumulator : (ssize_t) status;
114        }
115        size_t available = mBuffer.frameCount - mConsumed;
116        if (CC_UNLIKELY(count > available)) {
117            count = available;
118        }
119        if (CC_LIKELY(count > 0)) {
120            ssize_t ret = via(user, (char *) mBuffer.raw + (mConsumed * mFrameSize), count);
121
122            if (CC_UNLIKELY(ret <= 0)) {
123                if (CC_LIKELY(accumulator > 0)) {
124                    return accumulator;
125                }
126                return ret;
127            }
128            ALOG_ASSERT((size_t) ret <= count);
129            mFramesRead += ret;
130            accumulator += ret;
131            if (CC_LIKELY((mConsumed += ret) < mBuffer.frameCount)) {
132                continue;
133            }
134        }
135        mProvider->releaseBuffer(&mBuffer);
136        mBuffer.raw = NULL;
137        mConsumed = 0;
138        // don't get next buffer until we really need it
139    }
140}
141
142}   // namespace android
143