xref: /aosp_15_r20/cts/apps/CtsVerifier/src/com/android/cts/verifier/audio/analyzers/InfiniteRecording.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright (C) 2022 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 package com.android.cts.verifier.audio.analyzers;
17 
18 import java.util.concurrent.atomic.AtomicInteger;
19 
20 /**
21  * Provides a circular buffer for audio data.
22  */
23 public class InfiniteRecording {
24 
25     private float[] mData;
26     private AtomicInteger mWritten = new AtomicInteger();
27     private int     mMaxSamples;
28 
InfiniteRecording(int maxSamples)29     public InfiniteRecording(int maxSamples) {
30         mMaxSamples = maxSamples;
31         mData = new float[mMaxSamples];
32     }
33 
34     /**
35      *
36      * @param buffer
37      * @param position
38      * @param count
39      * @return
40      */
readFrom(float[] buffer, int position, int count)41     public int readFrom(float[] buffer, int position, int count) {
42         final int maxPosition = mWritten.get();
43         position = Math.min(position, maxPosition);
44         int numToRead = Math.min(count, mMaxSamples);
45         numToRead = Math.min(numToRead, maxPosition - position);
46         if (numToRead == 0) {
47             return 0;
48         }
49 
50         // We may need to read in two parts if it wraps.
51         final int offset = position % mMaxSamples;
52         final int firstReadSize = Math.min(numToRead, mMaxSamples - offset);
53         // copy (InputIterator first, InputIterator last, OutputIterator result)
54         // std::copy(&mData[offset], &mData[offset + firstReadSize], buffer);
55         // arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
56         System.arraycopy(mData, offset, buffer, 0, firstReadSize);
57         if (firstReadSize < numToRead) {
58             // Second read needed.
59             // std::copy(&mData[0], &mData[numToRead - firstReadSize], &buffer[firstReadSize]);
60             System.arraycopy(mData, 0, buffer, firstReadSize, numToRead - firstReadSize);
61         }
62         return numToRead;
63     }
64 
65     /**
66      * Get all the available audio data recorded during the analysis.
67      * @return recorded data
68      */
readAll()69     public float[] readAll() {
70         int numWritten = getTotalWritten();
71         int numAvailable = getAvailable();
72         float[] data = new float[numAvailable];
73         readFrom(data, numWritten - numAvailable, numAvailable);
74         return data;
75     }
76 
77     /**
78      *
79      * @param sample
80      */
write(float sample)81     public void write(float sample) {
82         final int position = mWritten.get();
83         final int offset = position % mMaxSamples;
84         mData[offset] = sample;
85         mWritten.incrementAndGet();
86     }
87 
88     /**
89      *
90      * @return total number of samples written
91      */
getTotalWritten()92     public int getTotalWritten() {
93         return mWritten.get();
94     }
95 
96     /**
97      * Get the maximum number of frames that are available to read.
98      *
99      * @return total number of samples written or maxSamples
100      */
getAvailable()101     public int getAvailable() {
102         return Math.min(mMaxSamples, mWritten.get());
103     }
104 
105     /**
106      * Erase the previously recorded data.
107      */
clear()108     public void clear() {
109         mWritten.set(0);
110     }
111 };
112