MonoPipe.cpp revision 6d8aabe8a3be1ac0789d00b82c3ca8b81381f5ab
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 "MonoPipe"
18//#define LOG_NDEBUG 0
19
20#include <cutils/atomic.h>
21#include <cutils/compiler.h>
22#include <utils/Log.h>
23#include "MonoPipe.h"
24#include "roundup.h"
25
26namespace android {
27
28MonoPipe::MonoPipe(size_t maxFrames, NBAIO_Format format, bool writeCanBlock) :
29        NBAIO_Sink(format),
30        mMaxFrames(roundup(maxFrames)),
31        mBuffer(malloc(mMaxFrames * Format_frameSize(format))),
32        mFront(0),
33        mRear(0),
34        mWriteCanBlock(writeCanBlock)
35{
36}
37
38MonoPipe::~MonoPipe()
39{
40    free(mBuffer);
41}
42
43ssize_t MonoPipe::availableToWrite() const
44{
45    if (CC_UNLIKELY(!mNegotiated)) {
46        return NEGOTIATE;
47    }
48    ssize_t ret = mMaxFrames - (mRear - android_atomic_acquire_load(&mFront));
49    ALOG_ASSERT((0 <= ret) && (ret <= mMaxFrames));
50    return ret;
51}
52
53ssize_t MonoPipe::write(const void *buffer, size_t count)
54{
55    if (CC_UNLIKELY(!mNegotiated)) {
56        return NEGOTIATE;
57    }
58    size_t totalFramesWritten = 0;
59    while (count > 0) {
60        size_t avail = availableToWrite();
61        size_t written = avail;
62        if (CC_LIKELY(written > count)) {
63            written = count;
64        }
65        size_t rear = mRear & (mMaxFrames - 1);
66        size_t part1 = mMaxFrames - rear;
67        if (part1 > written) {
68            part1 = written;
69        }
70        if (CC_LIKELY(part1 > 0)) {
71            memcpy((char *) mBuffer + (rear << mBitShift), buffer, part1 << mBitShift);
72            if (CC_UNLIKELY(rear + part1 == mMaxFrames)) {
73                size_t part2 = written - part1;
74                if (CC_LIKELY(part2 > 0)) {
75                    memcpy(mBuffer, (char *) buffer + (part1 << mBitShift), part2 << mBitShift);
76                }
77            }
78            android_atomic_release_store(written + mRear, &mRear);
79            totalFramesWritten += written;
80        }
81        if (!mWriteCanBlock) {
82            break;
83        }
84        count -= written;
85        buffer = (char *) buffer + (written << mBitShift);
86        // Simulate blocking I/O by sleeping at different rates, depending on a throttle.
87        // The throttle tries to keep the pipe about 5/8 full on average, with a slight jitter.
88        uint64_t ns;
89        enum {
90            THROTTLE_VERY_FAST, // pipe is (nearly) empty, fill quickly
91            THROTTLE_FAST,      // pipe is normal, fill at slightly faster rate
92            THROTTLE_NOMINAL,   // pipe is normal, fill at nominal rate
93            THROTTLE_SLOW,      // pipe is normal, fill at slightly slower rate
94            THROTTLE_VERY_SLOW, // pipe is (nearly) full, fill slowly
95        } throttle;
96        avail -= written;
97        // FIXME cache these values to avoid re-computation
98        if (avail >= (mMaxFrames * 3) / 4) {
99            throttle = THROTTLE_VERY_FAST;
100        } else if (avail >= mMaxFrames / 2) {
101            throttle = THROTTLE_FAST;
102        } else if (avail >= (mMaxFrames * 3) / 8) {
103            throttle = THROTTLE_NOMINAL;
104        } else if (avail >= mMaxFrames / 4) {
105            throttle = THROTTLE_SLOW;
106        } else {
107            throttle = THROTTLE_VERY_SLOW;
108        }
109        if (written > 0) {
110            // FIXME cache these values also
111            switch (throttle) {
112            case THROTTLE_VERY_FAST:
113            default:
114                ns = written * ( 500000000 / Format_sampleRate(mFormat));
115                break;
116            case THROTTLE_FAST:
117                ns = written * ( 750000000 / Format_sampleRate(mFormat));
118                break;
119            case THROTTLE_NOMINAL:
120                ns = written * (1000000000 / Format_sampleRate(mFormat));
121                break;
122            case THROTTLE_SLOW:
123                ns = written * (1100000000 / Format_sampleRate(mFormat));
124                break;
125            case THROTTLE_VERY_SLOW:
126                ns = written * (1250000000 / Format_sampleRate(mFormat));
127                break;
128            }
129        } else {
130            ns = mMaxFrames * (250000000 / Format_sampleRate(mFormat));
131        }
132        if (ns > 999999999) {
133            ns = 999999999;
134        }
135        struct timespec sleep;
136        sleep.tv_sec = 0;
137        sleep.tv_nsec = ns;
138        nanosleep(&sleep, NULL);
139    }
140    mFramesWritten += totalFramesWritten;
141    return totalFramesWritten;
142}
143
144}   // namespace android
145