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