blob: eed37435fc9d2b91bf7b47e96404a125f90ce0fd [file] [log] [blame]
Phil Burk0433d8f2018-11-21 16:41:25 -08001/*
2 * Copyright 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#ifndef NATIVEOBOE_MULTICHANNEL_RECORDING_H
18#define NATIVEOBOE_MULTICHANNEL_RECORDING_H
19
20#include <memory.h>
21#include <unistd.h>
22#include <sys/types.h>
23
Phil Burk81ca4742019-04-24 07:53:36 -070024/**
25 * Store multi-channel audio data in float format.
26 * The most recent data will be saved.
27 * Old data may be overwritten.
28 *
29 * Note that this is not thread safe. Do not read and write from separate threads.
30 */
Phil Burk0433d8f2018-11-21 16:41:25 -080031class MultiChannelRecording {
32public:
33 MultiChannelRecording(int32_t channelCount, int32_t maxFrames)
34 : mChannelCount(channelCount)
35 , mMaxFrames(maxFrames) {
36 mData = new float[channelCount * maxFrames];
37 }
38
39 ~MultiChannelRecording() {
40 delete[] mData;
41 }
42
43 void rewind() {
Phil Burk81ca4742019-04-24 07:53:36 -070044 mReadCursorFrames = mWriteCursorFrames - getSizeInFrames();
45 }
46
47 void clear() {
48 mReadCursorFrames = 0;
49 mWriteCursorFrames = 0;
Phil Burk0433d8f2018-11-21 16:41:25 -080050 }
51
Phil Burk327361c2019-04-13 14:45:27 -070052 int32_t getChannelCount() {
53 return mChannelCount;
54 }
55
56 int32_t getSizeInFrames() {
Phil Burk81ca4742019-04-24 07:53:36 -070057 return (int32_t) std::min(mWriteCursorFrames, static_cast<int64_t>(mMaxFrames));
Phil Burk327361c2019-04-13 14:45:27 -070058 }
59
Phil Burk81ca4742019-04-24 07:53:36 -070060 int32_t getReadIndex() {
61 return mReadCursorFrames % mMaxFrames;
62 }
63 int32_t getWriteIndex() {
64 return mWriteCursorFrames % mMaxFrames;
Phil Burk327361c2019-04-13 14:45:27 -070065 }
66
Phil Burk0433d8f2018-11-21 16:41:25 -080067 /**
Phil Burk717a4532019-06-01 08:47:02 -070068 * Write numFrames from the short buffer into the recording.
69 * Overwrite old data if necessary.
Phil Burk327361c2019-04-13 14:45:27 -070070 * Convert shorts to floats.
Phil Burk0433d8f2018-11-21 16:41:25 -080071 *
72 * @param buffer
73 * @param numFrames
74 * @return number of frames actually written.
75 */
76 int32_t write(int16_t *buffer, int32_t numFrames) {
Phil Burk81ca4742019-04-24 07:53:36 -070077 int32_t framesLeft = numFrames;
78 while (framesLeft > 0) {
79 int32_t indexFrame = getWriteIndex();
80 // contiguous writes
Phil Burk717a4532019-06-01 08:47:02 -070081 int32_t framesToEndOfBuffer = mMaxFrames - indexFrame;
82 int32_t framesNow = std::min(framesLeft, framesToEndOfBuffer);
Phil Burk81ca4742019-04-24 07:53:36 -070083 int32_t numSamples = framesNow * mChannelCount;
84 int32_t sampleIndex = indexFrame * mChannelCount;
85
Phil Burk0433d8f2018-11-21 16:41:25 -080086 for (int i = 0; i < numSamples; i++) {
Phil Burk717a4532019-06-01 08:47:02 -070087 mData[sampleIndex++] = *buffer++ * (1.0f / 32768);
Phil Burk0433d8f2018-11-21 16:41:25 -080088 }
Phil Burk81ca4742019-04-24 07:53:36 -070089
90 mWriteCursorFrames += framesNow;
91 framesLeft -= framesNow;
Phil Burk0433d8f2018-11-21 16:41:25 -080092 }
Phil Burk717a4532019-06-01 08:47:02 -070093 return numFrames - framesLeft;
Phil Burk0433d8f2018-11-21 16:41:25 -080094 }
95
96 /**
Phil Burk81ca4742019-04-24 07:53:36 -070097 * Write all numFrames from the float buffer into the recording.
98 * Overwrite old data if full.
Phil Burk0433d8f2018-11-21 16:41:25 -080099 * @param buffer
100 * @param numFrames
101 * @return number of frames actually written.
102 */
103 int32_t write(float *buffer, int32_t numFrames) {
Phil Burk81ca4742019-04-24 07:53:36 -0700104 int32_t framesLeft = numFrames;
105 while (framesLeft > 0) {
106 int32_t indexFrame = getWriteIndex();
107 // contiguous writes
108 int32_t framesToEnd = mMaxFrames - indexFrame;
109 int32_t framesNow = std::min(framesLeft, framesToEnd);
110 int32_t numSamples = framesNow * mChannelCount;
111 int32_t sampleIndex = indexFrame * mChannelCount;
112
113 memcpy(&mData[sampleIndex],
Phil Burk0433d8f2018-11-21 16:41:25 -0800114 buffer,
115 (numSamples * sizeof(float)));
Phil Burk717a4532019-06-01 08:47:02 -0700116 buffer += numSamples;
Phil Burk81ca4742019-04-24 07:53:36 -0700117 mWriteCursorFrames += framesNow;
118 framesLeft -= framesNow;
Phil Burk0433d8f2018-11-21 16:41:25 -0800119 }
120 return numFrames;
121 }
122
123 /**
124 * Read numFrames from the recording into the buffer, if there is enough data.
Phil Burk9f3a33f2019-04-17 16:03:14 -0700125 * Start at the cursor position, aligned up to the next frame.
Phil Burk0433d8f2018-11-21 16:41:25 -0800126 * @param buffer
127 * @param numFrames
128 * @return number of frames actually read.
129 */
130 int32_t read(float *buffer, int32_t numFrames) {
Phil Burk81ca4742019-04-24 07:53:36 -0700131 int32_t framesRead = 0;
132 int32_t framesLeft = std::min(numFrames,
133 std::min(mMaxFrames, (int32_t)(mWriteCursorFrames - mReadCursorFrames)));
134 while (framesLeft > 0) {
135 int32_t indexFrame = getReadIndex();
136 // contiguous reads
137 int32_t framesToEnd = mMaxFrames - indexFrame;
138 int32_t framesNow = std::min(framesLeft, framesToEnd);
139 int32_t numSamples = framesNow * mChannelCount;
140 int32_t sampleIndex = indexFrame * mChannelCount;
141
Phil Burk0433d8f2018-11-21 16:41:25 -0800142 memcpy(buffer,
Phil Burk81ca4742019-04-24 07:53:36 -0700143 &mData[sampleIndex],
Phil Burk0433d8f2018-11-21 16:41:25 -0800144 (numSamples * sizeof(float)));
Phil Burk81ca4742019-04-24 07:53:36 -0700145
146 mReadCursorFrames += framesNow;
147 framesLeft -= framesNow;
148 framesRead += framesNow;
Phil Burk0433d8f2018-11-21 16:41:25 -0800149 }
Phil Burk81ca4742019-04-24 07:53:36 -0700150 return framesRead;
Phil Burk0433d8f2018-11-21 16:41:25 -0800151 }
152
153private:
154 float *mData = nullptr;
Phil Burk81ca4742019-04-24 07:53:36 -0700155 int64_t mReadCursorFrames = 0;
156 int64_t mWriteCursorFrames = 0; // monotonically increasing
Phil Burk0433d8f2018-11-21 16:41:25 -0800157 const int32_t mChannelCount;
158 const int32_t mMaxFrames;
159};
160
161#endif //NATIVEOBOE_MULTICHANNEL_RECORDING_H