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