• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.util.Arrays;
20 
21 /**
22  * Maintains two ring buffers for recording wav data
23  * At any one time one buffer is available for writing to file while one is recording incoming data
24  */
25 public class WaveDataRingBuffer {
26 
27     public interface ReadableWaveDeck {
writeToFile(AudioFileOutput audioFile)28         boolean writeToFile(AudioFileOutput audioFile);
29     }
30 
31     private WaveDeck mLoadedDeck;
32     private WaveDeck mShelvedDeck;
33 
WaveDataRingBuffer(int size)34     public WaveDataRingBuffer(int size) {
35         if (size < Constant.SAMPLING_RATE_MIN * Constant.BUFFER_TEST_DURATION_SECONDS_MIN) {
36             size = Constant.SAMPLING_RATE_MIN * Constant.BUFFER_TEST_DURATION_SECONDS_MIN;
37         } else if (size > Constant.SAMPLING_RATE_MAX * Constant.BUFFER_TEST_DURATION_SECONDS_MAX) {
38             size = Constant.SAMPLING_RATE_MAX * Constant.BUFFER_TEST_DURATION_SECONDS_MAX;
39         }
40         mLoadedDeck = new WaveDeck(size);
41         mShelvedDeck = new WaveDeck(size);
42     }
43 
writeWaveData(double[] data, int srcPos, int length)44     public synchronized void writeWaveData(double[] data, int srcPos, int length) {
45         mLoadedDeck.writeWaveData(data, srcPos, length);
46     }
47 
getWaveRecord()48     public synchronized double[] getWaveRecord() {
49         return mLoadedDeck.getWaveRecord();
50     }
51 
SwapDecks()52     private void SwapDecks() {
53         WaveDeck temp = mShelvedDeck;
54         mShelvedDeck = mLoadedDeck;
55         mLoadedDeck = temp;
56     }
57 
58     /**
59      * Returns currently writing buffer as writeToFile interface, load erased shelved deck for write
60      * If shelved deck is still being read returns null
61      **/
getWaveDeck()62     public synchronized ReadableWaveDeck getWaveDeck() {
63         if (!mShelvedDeck.isBeingRead()) {
64             SwapDecks();
65             mShelvedDeck.readyForRead();
66             mLoadedDeck.reset();
67             return mShelvedDeck;
68         } else {
69             return null;
70         }
71     }
72 
73     /**
74      * Maintains a recording of wave data of last n seconds
75      */
76     public class WaveDeck implements ReadableWaveDeck {
77 
78         private double[] mWaveRecord;
79         private volatile int mIndex = 0; // between 0 and mWaveRecord.length - 1
80         private boolean mArrayFull = false; // true after mIndex has wrapped
81         private boolean mIsBeingRead = false;
82 
WaveDeck(int size)83         public WaveDeck(int size) {
84             mWaveRecord = new double[size];
85         }
86 
87         /**
88          * Write length number of doubles from data into ring buffer from starting srcPos
89          */
writeWaveData(double[] data, int srcPos, int length)90         public void writeWaveData(double[] data, int srcPos, int length) {
91             if (length > data.length - srcPos) {
92                 // requested to write more data than available
93                 // bad request leave data un-affected
94                 return;
95             }
96 
97             if (length >= mWaveRecord.length) {
98                 // requested write would fill or exceed ring buffer capacity
99                 // fill ring buffer with last segment of requested write
100                 System.arraycopy(data, srcPos + (length - mWaveRecord.length), mWaveRecord, 0,
101                         mWaveRecord.length);
102                 mIndex = 0;
103             } else if (mWaveRecord.length - mIndex > length) {
104                 // write requested data from current offset
105                 System.arraycopy(data, srcPos, mWaveRecord, mIndex, length);
106                 mIndex += length;
107             } else {
108                 // write to available buffer then wrap and overwrite previous records
109                 if (!mArrayFull) {
110                     mArrayFull = true;
111                 }
112 
113                 int availBuff = mWaveRecord.length - mIndex;
114 
115                 System.arraycopy(data, srcPos, mWaveRecord, mIndex, availBuff);
116                 System.arraycopy(data, srcPos + availBuff, mWaveRecord, 0, length - availBuff);
117 
118                 mIndex = length - availBuff;
119 
120             }
121 
122         }
123 
124         /**
125          * Returns a private copy of recorded wave data
126          *
127          * @return double array of wave recording, rearranged with oldest sample at first index
128          */
getWaveRecord()129         public double[] getWaveRecord() {
130             double outputBuffer[] = new double[mWaveRecord.length];
131 
132             if (!mArrayFull) {
133                 //return partially filled sample with trailing zeroes
134                 System.arraycopy(mWaveRecord, 0, outputBuffer, 0, mIndex);
135                 Arrays.fill(outputBuffer, mIndex+1, outputBuffer.length-1, 0);
136             } else {
137                 //copy buffer to contiguous sample and return unwrapped array
138                 System.arraycopy(mWaveRecord, mIndex, outputBuffer, 0, mWaveRecord.length - mIndex);
139                 System.arraycopy(mWaveRecord, 0, outputBuffer, mWaveRecord.length - mIndex, mIndex);
140             }
141 
142             return outputBuffer;
143         }
144 
145         /** Make buffer available for new recording **/
reset()146         public void reset() {
147             mIndex = 0;
148             mArrayFull = false;
149         }
150 
isBeingRead()151         public boolean isBeingRead() {
152             return mIsBeingRead;
153         }
154 
readyForRead()155         private void readyForRead() {
156             mIsBeingRead = true;
157         }
158 
159         @Override
writeToFile(AudioFileOutput audioFile)160         public boolean writeToFile(AudioFileOutput audioFile) {
161             boolean successfulWrite;
162             if (mArrayFull) {
163                 successfulWrite = audioFile.writeRingBufferData(mWaveRecord, mIndex, mIndex);
164             } else {
165                 // Write only filled part of array to file
166                 successfulWrite = audioFile.writeRingBufferData(mWaveRecord, 0, mIndex);
167             }
168 
169             mIsBeingRead = false;
170             return successfulWrite;
171         }
172     }
173 }
174