• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 package org.drrickorang.loopback;
18 
19 import java.nio.ByteBuffer;
20 import java.nio.ByteOrder;
21 
22 import android.util.Log;
23 
24 
25 /**
26  * Non-blocking pipe where writer writes to the pipe using by knowing the address of "mByteBuffer",
27  * and write to this ByteBuffer directly. On the other hand, reader reads from the pipe using
28  * read(), which converts data in ByteBuffer into shorts.
29  * Data in the pipe are stored in the ByteBuffer array "mByteBuffer".
30  * The write side of a pipe permits overruns; flow control is the caller's responsibility.
31  * TODO move to audio_utils
32  */
33 
34 public class PipeByteBuffer extends Pipe {
35     private static final String TAG = "PipeByteBuffer";
36 
37     private final ByteBuffer mByteBuffer;
38     private int              mFront = 0; // reader's current position
39 
40 
41     /**
42      * The ByteBuffer in this class consists of two sections. The first section is the actual pipe
43      * to store data. This section must have a size in power of 2, and this is enforced by the
44      * constructor through rounding maxSamples up to the nearest power of 2. This second section
45      * is used to store metadata. Currently the only metadata is an integer that stores the rear,
46      * where rear is the writer's current position. The metadata is at the end of ByteBuffer, and is
47      * outside of the actual pipe.
48      * IMPORTANT: The code is designed (in native code) such that metadata won't be overwritten when
49      * the writer writes to the pipe. If changes to the code are required, please make sure the
50      * metadata won't be overwritten.
51      * IMPORTANT: Since a signed integer is used to store rear and mFront, their values should not
52      * exceed 2^31 - 1, or else overflows happens and the positions of read and mFront becomes
53      * incorrect.
54      */
PipeByteBuffer(int maxSamples)55     public PipeByteBuffer(int maxSamples) {
56         super(maxSamples);
57         int extraInt = 1; // used to store rear
58         int extraShort = extraInt * Constant.SHORTS_PER_INT;
59         int numberOfShorts = mMaxValues + extraShort;
60         mByteBuffer = ByteBuffer.allocateDirect(numberOfShorts * Constant.BYTES_PER_SHORT);
61         mByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
62     }
63 
64 
65     /**
66      * Convert data in mByteBuffer into short, and put them into "buffer".
67      * Note: rear and mFront are kept in terms of number of shorts instead of number of bytes.
68      */
69     @Override
read(short[] buffer, int offset, int requiredSamples)70     public int read(short[] buffer, int offset, int requiredSamples) {
71         // first, update the current rear
72         int rear;
73         synchronized (mByteBuffer) {
74             rear = mByteBuffer.getInt(mMaxValues * Constant.BYTES_PER_SHORT);
75         }
76         //log("initial offset: " + offset + "\n initial requiredSamples: " + requiredSamples);
77 
78         // after here, rear may actually be updated further. However, we don't care. If at the point
79         // of checking there's enough data then we will read it. If not just wait until next call
80         // of read.
81         int avail = availableToRead(rear, mFront);
82         if (avail <= 0) {   //return -2 for overrun
83             return avail;
84         }
85 
86         // if not enough samples, just read partial samples
87         if (requiredSamples > avail) {
88             requiredSamples = avail;
89         }
90 
91         // mask the upper bits to get the correct position in the pipe
92         int front = mFront & (mMaxValues - 1);
93         int read = mMaxValues - front;   // total samples from currentIndex until the end of array
94         if (read > requiredSamples) {
95             read = requiredSamples;
96         }
97 
98         int byteBufferFront = front * Constant.BYTES_PER_SHORT; // start reading from here
99         byteBufferToArray(buffer, offset, read, byteBufferFront);
100 
101         if (front + read == mMaxValues) {
102             int samplesLeft = requiredSamples - read;
103             if (samplesLeft > 0) {
104                 byteBufferFront = 0;
105                 byteBufferToArray(buffer, offset + read, read + samplesLeft, byteBufferFront);
106                 read += samplesLeft;
107             }
108         }
109 
110         mFront += read;
111         return read;
112     }
113 
114 
115     /**
116      * Copy mByteBuffer's data (starting from "byteBufferFront") into double array "buffer".
117      * "start" is the starting index of "buffer" and "length" is the amount of samples copying.
118      */
byteBufferToArray(short[] buffer, int start, int length, int byteBufferFront)119     private void byteBufferToArray(short[] buffer, int start, int length, int byteBufferFront) {
120         for (int i = start; i < (start + length); i++) {
121             buffer[i] = mByteBuffer.getShort(byteBufferFront);
122             byteBufferFront += Constant.BYTES_PER_SHORT;
123         }
124     }
125 
126 
127     /** Private function that actually calculate the number of samples available to read. */
availableToRead(int rear, int front)128     private int availableToRead(int rear, int front) {
129         int avail = rear - front;
130         if (avail > mMaxValues) {
131             // Discard 1/16 of the most recent data in pipe to avoid another overrun immediately
132             int oldFront = mFront;
133             mFront = rear - mMaxValues + (mMaxValues >> 5);
134             mSamplesOverrun += mFront - oldFront;
135             ++mOverruns;
136             return OVERRUN;
137         }
138 
139         return avail;
140     }
141 
142 
143     @Override
availableToRead()144     public int availableToRead() {
145         int rear;
146         int avail;
147         synchronized (mByteBuffer) {
148             rear = mByteBuffer.getInt(mMaxValues * Constant.BYTES_PER_SHORT);
149         }
150 
151         avail = availableToRead(rear, mFront);
152         return avail;
153     }
154 
155 
getByteBuffer()156     public ByteBuffer getByteBuffer() {
157         return mByteBuffer;
158     }
159 
160 
161     @Override
flush()162     public void flush() {
163         //set rear and front to zero
164         mFront = 0;
165         synchronized (mByteBuffer) {
166             mByteBuffer.putInt(mMaxValues * Constant.BYTES_PER_SHORT, 0);
167         }
168     }
169 
170 
log(String msg)171     private static void log(String msg) {
172         Log.v(TAG, msg);
173     }
174 
175 }
176