blob: eed37435fc9d2b91bf7b47e96404a125f90ce0fd [file] [log] [blame]
/*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NATIVEOBOE_MULTICHANNEL_RECORDING_H
#define NATIVEOBOE_MULTICHANNEL_RECORDING_H
#include <memory.h>
#include <unistd.h>
#include <sys/types.h>
/**
* Store multi-channel audio data in float format.
* The most recent data will be saved.
* Old data may be overwritten.
*
* Note that this is not thread safe. Do not read and write from separate threads.
*/
class MultiChannelRecording {
public:
MultiChannelRecording(int32_t channelCount, int32_t maxFrames)
: mChannelCount(channelCount)
, mMaxFrames(maxFrames) {
mData = new float[channelCount * maxFrames];
}
~MultiChannelRecording() {
delete[] mData;
}
void rewind() {
mReadCursorFrames = mWriteCursorFrames - getSizeInFrames();
}
void clear() {
mReadCursorFrames = 0;
mWriteCursorFrames = 0;
}
int32_t getChannelCount() {
return mChannelCount;
}
int32_t getSizeInFrames() {
return (int32_t) std::min(mWriteCursorFrames, static_cast<int64_t>(mMaxFrames));
}
int32_t getReadIndex() {
return mReadCursorFrames % mMaxFrames;
}
int32_t getWriteIndex() {
return mWriteCursorFrames % mMaxFrames;
}
/**
* Write numFrames from the short buffer into the recording.
* Overwrite old data if necessary.
* Convert shorts to floats.
*
* @param buffer
* @param numFrames
* @return number of frames actually written.
*/
int32_t write(int16_t *buffer, int32_t numFrames) {
int32_t framesLeft = numFrames;
while (framesLeft > 0) {
int32_t indexFrame = getWriteIndex();
// contiguous writes
int32_t framesToEndOfBuffer = mMaxFrames - indexFrame;
int32_t framesNow = std::min(framesLeft, framesToEndOfBuffer);
int32_t numSamples = framesNow * mChannelCount;
int32_t sampleIndex = indexFrame * mChannelCount;
for (int i = 0; i < numSamples; i++) {
mData[sampleIndex++] = *buffer++ * (1.0f / 32768);
}
mWriteCursorFrames += framesNow;
framesLeft -= framesNow;
}
return numFrames - framesLeft;
}
/**
* Write all numFrames from the float buffer into the recording.
* Overwrite old data if full.
* @param buffer
* @param numFrames
* @return number of frames actually written.
*/
int32_t write(float *buffer, int32_t numFrames) {
int32_t framesLeft = numFrames;
while (framesLeft > 0) {
int32_t indexFrame = getWriteIndex();
// contiguous writes
int32_t framesToEnd = mMaxFrames - indexFrame;
int32_t framesNow = std::min(framesLeft, framesToEnd);
int32_t numSamples = framesNow * mChannelCount;
int32_t sampleIndex = indexFrame * mChannelCount;
memcpy(&mData[sampleIndex],
buffer,
(numSamples * sizeof(float)));
buffer += numSamples;
mWriteCursorFrames += framesNow;
framesLeft -= framesNow;
}
return numFrames;
}
/**
* Read numFrames from the recording into the buffer, if there is enough data.
* Start at the cursor position, aligned up to the next frame.
* @param buffer
* @param numFrames
* @return number of frames actually read.
*/
int32_t read(float *buffer, int32_t numFrames) {
int32_t framesRead = 0;
int32_t framesLeft = std::min(numFrames,
std::min(mMaxFrames, (int32_t)(mWriteCursorFrames - mReadCursorFrames)));
while (framesLeft > 0) {
int32_t indexFrame = getReadIndex();
// contiguous reads
int32_t framesToEnd = mMaxFrames - indexFrame;
int32_t framesNow = std::min(framesLeft, framesToEnd);
int32_t numSamples = framesNow * mChannelCount;
int32_t sampleIndex = indexFrame * mChannelCount;
memcpy(buffer,
&mData[sampleIndex],
(numSamples * sizeof(float)));
mReadCursorFrames += framesNow;
framesLeft -= framesNow;
framesRead += framesNow;
}
return framesRead;
}
private:
float *mData = nullptr;
int64_t mReadCursorFrames = 0;
int64_t mWriteCursorFrames = 0; // monotonically increasing
const int32_t mChannelCount;
const int32_t mMaxFrames;
};
#endif //NATIVEOBOE_MULTICHANNEL_RECORDING_H