From 0d549e29ddb50074dca74a1d8c2dca05ed7e6c1a Mon Sep 17 00:00:00 2001 From: Robert Wu <85952307+robertwu1@users.noreply.github.com> Date: Wed, 28 May 2025 15:48:09 -0700 Subject: [PATCH 1/3] OboeTester: Add MultiChannelFileRecording --- .../app/src/main/cpp/FullDuplexAnalyzer.h | 6 +- .../main/cpp/InputStreamCallbackAnalyzer.h | 6 +- .../src/main/cpp/MultiChannelFileRecording.h | 267 ++++++++++++++++++ .../app/src/main/cpp/NativeAudioContext.h | 18 +- .../app/src/main/cpp/PlayRecordingCallback.h | 6 +- .../app/src/main/cpp/jni-bridge.cpp | 8 + .../oboetester/TestAudioActivity.java | 17 ++ 7 files changed, 313 insertions(+), 15 deletions(-) create mode 100644 apps/OboeTester/app/src/main/cpp/MultiChannelFileRecording.h diff --git a/apps/OboeTester/app/src/main/cpp/FullDuplexAnalyzer.h b/apps/OboeTester/app/src/main/cpp/FullDuplexAnalyzer.h index 82b1abedc..c7d2bc05f 100644 --- a/apps/OboeTester/app/src/main/cpp/FullDuplexAnalyzer.h +++ b/apps/OboeTester/app/src/main/cpp/FullDuplexAnalyzer.h @@ -23,7 +23,7 @@ #include "oboe/Oboe.h" #include "analyzer/LatencyAnalyzer.h" #include "FullDuplexStreamWithConversion.h" -#include "MultiChannelRecording.h" +#include "MultiChannelFileRecording.h" class FullDuplexAnalyzer : public FullDuplexStreamWithConversion { public: @@ -48,7 +48,7 @@ class FullDuplexAnalyzer : public FullDuplexStreamWithConversion { return mLoopbackProcessor; } - void setRecording(MultiChannelRecording *recording) { + void setRecording(MultiChannelFileRecording *recording) { mRecording = recording; } @@ -61,7 +61,7 @@ class FullDuplexAnalyzer : public FullDuplexStreamWithConversion { } private: - MultiChannelRecording *mRecording = nullptr; + MultiChannelFileRecording *mRecording = nullptr; LoopbackProcessor * const mLoopbackProcessor; diff --git a/apps/OboeTester/app/src/main/cpp/InputStreamCallbackAnalyzer.h b/apps/OboeTester/app/src/main/cpp/InputStreamCallbackAnalyzer.h index 07c4f9794..fb20d1696 100644 --- a/apps/OboeTester/app/src/main/cpp/InputStreamCallbackAnalyzer.h +++ b/apps/OboeTester/app/src/main/cpp/InputStreamCallbackAnalyzer.h @@ -26,7 +26,7 @@ #include "analyzer/PeakDetector.h" #include "FormatConverterBox.h" -#include "MultiChannelRecording.h" +#include "MultiChannelFileRecording.h" #include "OboeTesterStreamCallback.h" class InputStreamCallbackAnalyzer : public OboeTesterStreamCallback { @@ -58,7 +58,7 @@ class InputStreamCallbackAnalyzer : public OboeTesterStreamCallback { void *audioData, int numFrames) override; - void setRecording(MultiChannelRecording *recording) { + void setRecording(MultiChannelFileRecording *recording) { mRecording = recording; } @@ -75,7 +75,7 @@ class InputStreamCallbackAnalyzer : public OboeTesterStreamCallback { public: int32_t mNumChannels = 0; std::unique_ptr mPeakDetectors; - MultiChannelRecording *mRecording = nullptr; + MultiChannelFileRecording *mRecording = nullptr; private: std::unique_ptr mInputConverter; diff --git a/apps/OboeTester/app/src/main/cpp/MultiChannelFileRecording.h b/apps/OboeTester/app/src/main/cpp/MultiChannelFileRecording.h new file mode 100644 index 000000000..99fa30bf5 --- /dev/null +++ b/apps/OboeTester/app/src/main/cpp/MultiChannelFileRecording.h @@ -0,0 +1,267 @@ +/* + * Copyright 2025 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_FILE_RECORDING_H +#define NATIVEOBOE_MULTICHANNEL_FILE_RECORDING_H + +#include // For int32_t and int64_t +#include // For std::min and std::max +#include // For memcpy +#include // For std::fstream +#include // For std::string +#include // For temporary buffer in int16_t write +#include // For std::runtime_error +#include // For std::remove + +/** + * @class MultiChannelFileRecording + * @brief Stores multi-channel audio data in float format directly to a file + * on disk, allowing for conceptually "infinite" recording and playback, + * limited only by available disk space. + * + * This class provides file-like operations, managing read and write positions + * within the underlying file. Data is always appended during write operations. + * + * Note that this class is NOT thread-safe. Do not read and write from separate + * threads without external synchronization. + */ +class MultiChannelFileRecording { +public: + /** + * @brief Constructs a new MultiChannelFileRecording object, opening or creating + * a file for audio storage. + * @param channelCount The number of audio channels (e.g., 1 for mono, 2 for stereo). + * @param filename The path to the file where audio data will be stored. + * @throws std::runtime_error if the file cannot be opened. + */ + MultiChannelFileRecording(int32_t channelCount, const std::string& filename) + : mChannelCount(channelCount) + , mFilename(filename) { + // Open the file in binary mode for both reading and writing. + // std::ios::ate sets the initial position to the end of the file. + // This is useful to determine the initial file size (mWriteCursorFrames). + mFileStream.open(mFilename, std::ios::binary | std::ios::in | std::ios::out | std::ios::ate); + + if (!mFileStream.is_open()) { + // Attempt to create the file if it doesn't exist and opening failed. + // This is a common pattern when you need a file to exist for R/W. + mFileStream.clear(); // Clear any error flags from previous open attempt + mFileStream.open(mFilename, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc); // Create/truncate + if (!mFileStream.is_open()) { + throw std::runtime_error("MultiChannelFileRecording: Failed to open or create file: " + mFilename); + } + } + + // Get the current file size in bytes to initialize mWriteCursorFrames. + // For std::ios::ate, tellp() returns the end position. + int64_t fileSizeInBytes = mFileStream.tellp(); + mWriteCursorFrames = fileSizeInBytes / (mChannelCount * sizeof(float)); + + // Rewind read cursor to the beginning of the file. + mReadCursorFrames = 0; + } + + /** + * @brief Destroys the MultiChannelFileRecording object and closes the file. + */ + ~MultiChannelFileRecording() { + if (mFileStream.is_open()) { + mFileStream.close(); + } + } + + /** + * @brief Resets the read cursor to the beginning of the file. + */ + void rewind() { + mReadCursorFrames = 0; + } + + /** + * @brief Clears the recording by truncating the file to zero size. + * This effectively deletes all recorded audio data. + * The read and write cursors are reset to zero. + * @throws std::runtime_error if the file cannot be cleared. + */ + void clear() { + // Close the current stream. + if (mFileStream.is_open()) { + mFileStream.close(); + } + + // Remove the file from disk. + if (std::remove(mFilename.c_str()) != 0) { + throw std::runtime_error("MultiChannelFileRecording: Failed to remove file during clear: " + mFilename); + } + + // Re-open the file in truncate mode to create an empty file. + mFileStream.open(mFilename, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc); + if (!mFileStream.is_open()) { + throw std::runtime_error("MultiChannelFileRecording: Failed to re-open file after clear: " + mFilename); + } + + // Reset cursors as the file is now empty. + mReadCursorFrames = 0; + mWriteCursorFrames = 0; + } + + /** + * @brief Gets the number of channels in the recording. + * @return The channel count. + */ + int32_t getChannelCount() { + return mChannelCount; + } + + /** + * @brief Gets the total number of frames currently stored in the file. + * This represents the total length of the recording. + * @return The number of frames currently in the file. + */ + int64_t getSizeInFrames() { + return mWriteCursorFrames; + } + + /** + * @brief Writes 'numFrames' from a 16-bit integer buffer into the recording file. + * The 16-bit samples are converted to floats and stored. Data is appended + * to the end of the file. + * @param buffer A pointer to the source 16-bit audio data. + * @param numFrames The number of frames to write. + * @return The number of frames actually written (should be 'numFrames'). + * @throws std::runtime_error if the write operation fails. + */ + int32_t write(int16_t *buffer, int32_t numFrames) { + if (!mFileStream.is_open()) { + throw std::runtime_error("MultiChannelFileRecording: File is not open for writing."); + } + + // Create a temporary buffer for float conversion. + std::vector floatBuffer(numFrames * mChannelCount); + for (int i = 0; i < numFrames * mChannelCount; i++) { + floatBuffer[i] = static_cast(buffer[i]) * (1.0f / 32768.0f); + } + + // Seek to the end of the file before writing (append mode). + // For fstream, seekp(0, std::ios::end) is needed as ios::app is not always consistent across systems. + mFileStream.seekp(0, std::ios::end); + + // Write the data. + mFileStream.write(reinterpret_cast(floatBuffer.data()), + numFrames * mChannelCount * sizeof(float)); + + if (!mFileStream) { + throw std::runtime_error("MultiChannelFileRecording: Failed to write data to file (int16_t conversion)."); + } + + mWriteCursorFrames += numFrames; // Update the conceptual write cursor (file size) + return numFrames; + } + + /** + * @brief Writes 'numFrames' from a float buffer into the recording file. + * Data is appended to the end of the file. + * @param buffer A pointer to the source float audio data. + * @param numFrames The number of frames to write. + * @return The number of frames actually written (should be 'numFrames'). + * @throws std::runtime_error if the write operation fails. + */ + int32_t write(float *buffer, int32_t numFrames) { + if (!mFileStream.is_open()) { + throw std::runtime_error("MultiChannelFileRecording: File is not open for writing."); + } + + // Seek to the end of the file before writing (append mode). + mFileStream.seekp(0, std::ios::end); + + // Write the data. + mFileStream.write(reinterpret_cast(buffer), + numFrames * mChannelCount * sizeof(float)); + + if (!mFileStream) { + throw std::runtime_error("MultiChannelFileRecording: Failed to write data to file (float)."); + } + + mWriteCursorFrames += numFrames; // Update the conceptual write cursor (file size) + return numFrames; + } + + /** + * @brief Reads 'numFrames' from the recording file into the provided float buffer. + * Reading starts from the current conceptual read cursor position. + * @param buffer A pointer to the destination float buffer. + * @param numFrames The maximum number of frames to read. + * @return The number of frames actually read. This may be less than 'numFrames' + * if insufficient data is available from the current read position to the end of the file. + * @throws std::runtime_error if the read operation fails. + */ + int32_t read(float *buffer, int32_t numFrames) { + if (!mFileStream.is_open()) { + throw std::runtime_error("MultiChannelFileRecording: File is not open for reading."); + } + + // Calculate available frames from current read cursor to end of file. + int64_t availableFrames = mWriteCursorFrames - mReadCursorFrames; + int32_t framesToRead = (int32_t) std::min(static_cast(numFrames), availableFrames); + + if (framesToRead <= 0) { + return 0; // No frames to read + } + + // Seek to the current read cursor position. + mFileStream.seekg(mReadCursorFrames * mChannelCount * sizeof(float)); + + // Read the data. + mFileStream.read(reinterpret_cast(buffer), + framesToRead * mChannelCount * sizeof(float)); + + if (!mFileStream && !mFileStream.eof()) { // Check for read error, but not EOF + throw std::runtime_error("MultiChannelFileRecording: Failed to read data from file."); + } + + mReadCursorFrames += framesToRead; // Advance conceptual read cursor + return framesToRead; + } + + /** + * @brief Seeks the read cursor to a specific conceptual frame position within the file. + * The position is clamped to within the valid range of the file (0 to mWriteCursorFrames). + * @param position The target conceptual frame position (64-bit). + */ + void seek(int64_t position) { + // Clamp the position to be within the file bounds (0 to mWriteCursorFrames). + mReadCursorFrames = std::max(static_cast(0), position); + mReadCursorFrames = std::min(mReadCursorFrames, mWriteCursorFrames); + } + + /** + * @brief Returns the current conceptual read cursor position. + * @return The 64-bit conceptual frame position of the read cursor. + */ + int64_t tell() { + return mReadCursorFrames; + } + +private: + std::fstream mFileStream; // File stream for I/O + const int32_t mChannelCount; // Number of audio channels + const std::string mFilename; // Name of the file being recorded to/read from + + int64_t mReadCursorFrames = 0; // Conceptual read cursor (64-bit, file offset in frames) + int64_t mWriteCursorFrames = 0; // Conceptual write cursor (64-bit, current file size in frames) +}; + +#endif //NATIVEOBOE_MULTICHANNEL_FILE_RECORDING_H diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h index 1ca4a69e1..e9a1ec937 100644 --- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h +++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h @@ -50,7 +50,7 @@ #include "analyzer/GlitchAnalyzer.h" #include "analyzer/DataPathAnalyzer.h" #include "InputStreamCallbackAnalyzer.h" -#include "MultiChannelRecording.h" +#include "MultiChannelFileRecording.h" #include "OboeStreamCallbackProxy.h" #include "OboeTools.h" #include "PlayRecordingCallback.h" @@ -314,6 +314,10 @@ class ActivityContext { virtual void setupMemoryBuffer([[maybe_unused]] std::unique_ptr& buffer, [[maybe_unused]] int length) {} + void setRecordingFileName(const char *filename) { + mRecordingFileName = filename; + } + protected: std::shared_ptr getInputStream(); std::shared_ptr getOutputStream(); @@ -321,8 +325,8 @@ class ActivityContext { void freeStreamIndex(int32_t streamIndex); virtual void createRecording() { - mRecording = std::make_unique(mChannelCount, - SECONDS_TO_RECORD * mSampleRate); + mRecording = std::make_unique(mChannelCount, + mRecordingFileName); } virtual void finishOpen(bool isInput, std::shared_ptr &oboeStream) {} @@ -334,7 +338,7 @@ class ActivityContext { AudioStreamGateway audioStreamGateway; OboeStreamCallbackProxy oboeCallbackProxy; - std::unique_ptr mRecording{}; + std::unique_ptr mRecording{}; int32_t mNextStreamHandle = 0; std::unordered_map> mOboeStreams; @@ -342,6 +346,8 @@ class ActivityContext { int32_t mChannelCount = 0; // TODO per stream int32_t mSampleRate = 0; // TODO per stream + std::string mRecordingFileName; + std::atomic threadEnabled{false}; std::thread *dataThread = nullptr; // FIXME never gets deleted @@ -537,8 +543,8 @@ class ActivityFullDuplex : public ActivityContext { protected: void createRecording() override { - mRecording = std::make_unique(2, // output and input - SECONDS_TO_RECORD * mSampleRate); + mRecording = std::make_unique(2, // output and input + mRecordingFileName); } }; diff --git a/apps/OboeTester/app/src/main/cpp/PlayRecordingCallback.h b/apps/OboeTester/app/src/main/cpp/PlayRecordingCallback.h index 8ea49ea7e..04466ead2 100644 --- a/apps/OboeTester/app/src/main/cpp/PlayRecordingCallback.h +++ b/apps/OboeTester/app/src/main/cpp/PlayRecordingCallback.h @@ -19,14 +19,14 @@ #include "oboe/Oboe.h" -#include "MultiChannelRecording.h" +#include "MultiChannelFileRecording.h" class PlayRecordingCallback : public oboe::AudioStreamCallback { public: PlayRecordingCallback() {} ~PlayRecordingCallback() = default; - void setRecording(MultiChannelRecording *recording) { + void setRecording(MultiChannelFileRecording *recording) { mRecording = recording; } @@ -39,7 +39,7 @@ class PlayRecordingCallback : public oboe::AudioStreamCallback { int numFrames); private: - MultiChannelRecording *mRecording = nullptr; + MultiChannelFileRecording *mRecording = nullptr; }; diff --git a/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp b/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp index f2fb9cc94..fd3603645 100644 --- a/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp +++ b/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp @@ -994,6 +994,14 @@ Java_com_mobileer_oboetester_TestAudioActivity_setDefaultAudioValues(JNIEnv *env oboe::DefaultStreamValues::FramesPerBurst = audio_manager_frames_per_burst; } +JNIEXPORT void JNICALL +Java_com_mobileer_oboetester_TestAudioActivity_setRecordingFileName(JNIEnv *env, jobject thiz, + jstring filePath) { + const char *filePathStr = env->GetStringUTFChars(filePath, nullptr); + engine.getCurrentActivity()->setRecordingFileName(filePathStr); + env->ReleaseStringUTFChars(filePath, filePathStr); +} + static TestErrorCallback sErrorCallbackTester; JNIEXPORT void JNICALL diff --git a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java index afcf50cc1..16c98b0d4 100644 --- a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java +++ b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java @@ -50,6 +50,9 @@ import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Locale; @@ -657,6 +660,8 @@ public void openAudio() throws IOException { updateNativeAudioParameters(); + setRecordingFileName(); + if (!isTestConfiguredUsingBundle()) { applyConfigurationViewsToModels(); } @@ -957,6 +962,18 @@ File maybeWriteTestResult(String resultString) { return fileWritten; } + void setRecordingFileName() { + // Get the cache directory + File cacheDir = getCacheDir(); + + File outputFile = new File(cacheDir, "recording_" + System.currentTimeMillis()); + String filePath = outputFile.getAbsolutePath(); + + setRecordingFileName(filePath); + } + + native void setRecordingFileName(String filePath); + void setupMp3BufferFromFile() { try { InputStream inputStream = this.getResources().openRawResource(MP3_RES_ID); From ed237717bf75c9548e351b836b6b56c11cd1dd92 Mon Sep 17 00:00:00 2001 From: Robert Wu <85952307+robertwu1@users.noreply.github.com> Date: Wed, 28 May 2025 16:23:54 -0700 Subject: [PATCH 2/3] update comments --- .../src/main/cpp/MultiChannelFileRecording.h | 34 +++++++++++-------- .../oboetester/TestAudioActivity.java | 3 -- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/OboeTester/app/src/main/cpp/MultiChannelFileRecording.h b/apps/OboeTester/app/src/main/cpp/MultiChannelFileRecording.h index 99fa30bf5..9984cf188 100644 --- a/apps/OboeTester/app/src/main/cpp/MultiChannelFileRecording.h +++ b/apps/OboeTester/app/src/main/cpp/MultiChannelFileRecording.h @@ -17,20 +17,19 @@ #ifndef NATIVEOBOE_MULTICHANNEL_FILE_RECORDING_H #define NATIVEOBOE_MULTICHANNEL_FILE_RECORDING_H -#include // For int32_t and int64_t -#include // For std::min and std::max -#include // For memcpy -#include // For std::fstream -#include // For std::string -#include // For temporary buffer in int16_t write -#include // For std::runtime_error -#include // For std::remove +#include +#include +#include +#include +#include +#include +#include +#include /** * @class MultiChannelFileRecording * @brief Stores multi-channel audio data in float format directly to a file - * on disk, allowing for conceptually "infinite" recording and playback, - * limited only by available disk space. + * on disk. * * This class provides file-like operations, managing read and write positions * within the underlying file. Data is always appended during write operations. @@ -53,15 +52,18 @@ class MultiChannelFileRecording { // Open the file in binary mode for both reading and writing. // std::ios::ate sets the initial position to the end of the file. // This is useful to determine the initial file size (mWriteCursorFrames). - mFileStream.open(mFilename, std::ios::binary | std::ios::in | std::ios::out | std::ios::ate); + mFileStream.open( + mFilename, std::ios::binary | std::ios::in | std::ios::out | std::ios::ate); if (!mFileStream.is_open()) { // Attempt to create the file if it doesn't exist and opening failed. // This is a common pattern when you need a file to exist for R/W. mFileStream.clear(); // Clear any error flags from previous open attempt - mFileStream.open(mFilename, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc); // Create/truncate + // Create/truncate + mFileStream.open(mFilename, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc); if (!mFileStream.is_open()) { - throw std::runtime_error("MultiChannelFileRecording: Failed to open or create file: " + mFilename); + throw std::runtime_error( + "MultiChannelFileRecording: Failed to open or create file: " + mFilename); } } @@ -104,13 +106,15 @@ class MultiChannelFileRecording { // Remove the file from disk. if (std::remove(mFilename.c_str()) != 0) { - throw std::runtime_error("MultiChannelFileRecording: Failed to remove file during clear: " + mFilename); + throw std::runtime_error( + "MultiChannelFileRecording: Failed to remove file during clear: " + mFilename); } // Re-open the file in truncate mode to create an empty file. mFileStream.open(mFilename, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc); if (!mFileStream.is_open()) { - throw std::runtime_error("MultiChannelFileRecording: Failed to re-open file after clear: " + mFilename); + throw std::runtime_error( + "MultiChannelFileRecording: Failed to re-open file after clear: " + mFilename); } // Reset cursors as the file is now empty. diff --git a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java index 16c98b0d4..f77b90c18 100644 --- a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java +++ b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java @@ -50,9 +50,6 @@ import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Locale; From fa7bbba2da5d27c2bf942d8d6ca1887c9eee683e Mon Sep 17 00:00:00 2001 From: Robert Wu <85952307+robertwu1@users.noreply.github.com> Date: Wed, 28 May 2025 16:23:54 -0700 Subject: [PATCH 3/3] update comments --- .../app/src/main/cpp/MultiChannelFileRecording.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/OboeTester/app/src/main/cpp/MultiChannelFileRecording.h b/apps/OboeTester/app/src/main/cpp/MultiChannelFileRecording.h index 9984cf188..eed58ee2f 100644 --- a/apps/OboeTester/app/src/main/cpp/MultiChannelFileRecording.h +++ b/apps/OboeTester/app/src/main/cpp/MultiChannelFileRecording.h @@ -154,7 +154,7 @@ class MultiChannelFileRecording { } // Create a temporary buffer for float conversion. - std::vector floatBuffer(numFrames * mChannelCount); + std::vector floatBuffer(static_cast(numFrames) * mChannelCount); for (int i = 0; i < numFrames * mChannelCount; i++) { floatBuffer[i] = static_cast(buffer[i]) * (1.0f / 32768.0f); } @@ -165,7 +165,7 @@ class MultiChannelFileRecording { // Write the data. mFileStream.write(reinterpret_cast(floatBuffer.data()), - numFrames * mChannelCount * sizeof(float)); + static_cast(numFrames) * mChannelCount * sizeof(float)); if (!mFileStream) { throw std::runtime_error("MultiChannelFileRecording: Failed to write data to file (int16_t conversion)."); @@ -193,7 +193,7 @@ class MultiChannelFileRecording { // Write the data. mFileStream.write(reinterpret_cast(buffer), - numFrames * mChannelCount * sizeof(float)); + static_cast(numFrames) * mChannelCount * sizeof(float)); if (!mFileStream) { throw std::runtime_error("MultiChannelFileRecording: Failed to write data to file (float)."); @@ -230,7 +230,7 @@ class MultiChannelFileRecording { // Read the data. mFileStream.read(reinterpret_cast(buffer), - framesToRead * mChannelCount * sizeof(float)); + static_cast(framesToRead) * mChannelCount * sizeof(float)); if (!mFileStream && !mFileStream.eof()) { // Check for read error, but not EOF throw std::runtime_error("MultiChannelFileRecording: Failed to read data from file.");