| /* |
| * 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 |