summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/common_audio/wav_file.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/common_audio/wav_file.cc')
-rw-r--r--third_party/libwebrtc/common_audio/wav_file.cc290
1 files changed, 290 insertions, 0 deletions
diff --git a/third_party/libwebrtc/common_audio/wav_file.cc b/third_party/libwebrtc/common_audio/wav_file.cc
new file mode 100644
index 0000000000..127c9c0757
--- /dev/null
+++ b/third_party/libwebrtc/common_audio/wav_file.cc
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "common_audio/wav_file.h"
+
+#include <errno.h>
+
+#include <algorithm>
+#include <array>
+#include <cstdio>
+#include <type_traits>
+#include <utility>
+
+#include "common_audio/include/audio_util.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/system/arch.h"
+
+namespace webrtc {
+namespace {
+
+static_assert(std::is_trivially_destructible<WavFormat>::value, "");
+
+// Checks whether the format is supported or not.
+bool FormatSupported(WavFormat format) {
+ // Only PCM and IEEE Float formats are supported.
+ return format == WavFormat::kWavFormatPcm ||
+ format == WavFormat::kWavFormatIeeeFloat;
+}
+
+// Doesn't take ownership of the file handle and won't close it.
+class WavHeaderFileReader : public WavHeaderReader {
+ public:
+ explicit WavHeaderFileReader(FileWrapper* file) : file_(file) {}
+
+ WavHeaderFileReader(const WavHeaderFileReader&) = delete;
+ WavHeaderFileReader& operator=(const WavHeaderFileReader&) = delete;
+
+ size_t Read(void* buf, size_t num_bytes) override {
+ size_t count = file_->Read(buf, num_bytes);
+ pos_ += count;
+ return count;
+ }
+ bool SeekForward(uint32_t num_bytes) override {
+ bool success = file_->SeekRelative(num_bytes);
+ if (success) {
+ pos_ += num_bytes;
+ }
+ return success;
+ }
+ int64_t GetPosition() override { return pos_; }
+
+ private:
+ FileWrapper* file_;
+ int64_t pos_ = 0;
+};
+
+constexpr size_t kMaxChunksize = 4096;
+
+} // namespace
+
+WavReader::WavReader(absl::string_view filename)
+ : WavReader(FileWrapper::OpenReadOnly(filename)) {}
+
+WavReader::WavReader(FileWrapper file) : file_(std::move(file)) {
+ RTC_CHECK(file_.is_open())
+ << "Invalid file. Could not create file handle for wav file.";
+
+ WavHeaderFileReader readable(&file_);
+ size_t bytes_per_sample;
+ RTC_CHECK(ReadWavHeader(&readable, &num_channels_, &sample_rate_, &format_,
+ &bytes_per_sample, &num_samples_in_file_,
+ &data_start_pos_));
+ num_unread_samples_ = num_samples_in_file_;
+ RTC_CHECK(FormatSupported(format_)) << "Non-implemented wav-format";
+}
+
+void WavReader::Reset() {
+ RTC_CHECK(file_.SeekTo(data_start_pos_))
+ << "Failed to set position in the file to WAV data start position";
+ num_unread_samples_ = num_samples_in_file_;
+}
+
+size_t WavReader::ReadSamples(const size_t num_samples,
+ int16_t* const samples) {
+#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
+#error "Need to convert samples to big-endian when reading from WAV file"
+#endif
+
+ size_t num_samples_left_to_read = num_samples;
+ size_t next_chunk_start = 0;
+ while (num_samples_left_to_read > 0 && num_unread_samples_ > 0) {
+ const size_t chunk_size = std::min(
+ std::min(kMaxChunksize, num_samples_left_to_read), num_unread_samples_);
+ size_t num_bytes_read;
+ size_t num_samples_read;
+ if (format_ == WavFormat::kWavFormatIeeeFloat) {
+ std::array<float, kMaxChunksize> samples_to_convert;
+ num_bytes_read = file_.Read(samples_to_convert.data(),
+ chunk_size * sizeof(samples_to_convert[0]));
+ num_samples_read = num_bytes_read / sizeof(samples_to_convert[0]);
+
+ for (size_t j = 0; j < num_samples_read; ++j) {
+ samples[next_chunk_start + j] = FloatToS16(samples_to_convert[j]);
+ }
+ } else {
+ RTC_CHECK_EQ(format_, WavFormat::kWavFormatPcm);
+ num_bytes_read = file_.Read(&samples[next_chunk_start],
+ chunk_size * sizeof(samples[0]));
+ num_samples_read = num_bytes_read / sizeof(samples[0]);
+ }
+ RTC_CHECK(num_samples_read == 0 || (num_bytes_read % num_samples_read) == 0)
+ << "Corrupt file: file ended in the middle of a sample.";
+ RTC_CHECK(num_samples_read == chunk_size || file_.ReadEof())
+ << "Corrupt file: payload size does not match header.";
+
+ next_chunk_start += num_samples_read;
+ num_unread_samples_ -= num_samples_read;
+ num_samples_left_to_read -= num_samples_read;
+ }
+
+ return num_samples - num_samples_left_to_read;
+}
+
+size_t WavReader::ReadSamples(const size_t num_samples, float* const samples) {
+#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
+#error "Need to convert samples to big-endian when reading from WAV file"
+#endif
+
+ size_t num_samples_left_to_read = num_samples;
+ size_t next_chunk_start = 0;
+ while (num_samples_left_to_read > 0 && num_unread_samples_ > 0) {
+ const size_t chunk_size = std::min(
+ std::min(kMaxChunksize, num_samples_left_to_read), num_unread_samples_);
+ size_t num_bytes_read;
+ size_t num_samples_read;
+ if (format_ == WavFormat::kWavFormatPcm) {
+ std::array<int16_t, kMaxChunksize> samples_to_convert;
+ num_bytes_read = file_.Read(samples_to_convert.data(),
+ chunk_size * sizeof(samples_to_convert[0]));
+ num_samples_read = num_bytes_read / sizeof(samples_to_convert[0]);
+
+ for (size_t j = 0; j < num_samples_read; ++j) {
+ samples[next_chunk_start + j] =
+ static_cast<float>(samples_to_convert[j]);
+ }
+ } else {
+ RTC_CHECK_EQ(format_, WavFormat::kWavFormatIeeeFloat);
+ num_bytes_read = file_.Read(&samples[next_chunk_start],
+ chunk_size * sizeof(samples[0]));
+ num_samples_read = num_bytes_read / sizeof(samples[0]);
+
+ for (size_t j = 0; j < num_samples_read; ++j) {
+ samples[next_chunk_start + j] =
+ FloatToFloatS16(samples[next_chunk_start + j]);
+ }
+ }
+ RTC_CHECK(num_samples_read == 0 || (num_bytes_read % num_samples_read) == 0)
+ << "Corrupt file: file ended in the middle of a sample.";
+ RTC_CHECK(num_samples_read == chunk_size || file_.ReadEof())
+ << "Corrupt file: payload size does not match header.";
+
+ next_chunk_start += num_samples_read;
+ num_unread_samples_ -= num_samples_read;
+ num_samples_left_to_read -= num_samples_read;
+ }
+
+ return num_samples - num_samples_left_to_read;
+}
+
+void WavReader::Close() {
+ file_.Close();
+}
+
+WavWriter::WavWriter(absl::string_view filename,
+ int sample_rate,
+ size_t num_channels,
+ SampleFormat sample_format)
+ // Unlike plain fopen, OpenWriteOnly takes care of filename utf8 ->
+ // wchar conversion on windows.
+ : WavWriter(FileWrapper::OpenWriteOnly(filename),
+ sample_rate,
+ num_channels,
+ sample_format) {}
+
+WavWriter::WavWriter(FileWrapper file,
+ int sample_rate,
+ size_t num_channels,
+ SampleFormat sample_format)
+ : sample_rate_(sample_rate),
+ num_channels_(num_channels),
+ num_samples_written_(0),
+ format_(sample_format == SampleFormat::kInt16
+ ? WavFormat::kWavFormatPcm
+ : WavFormat::kWavFormatIeeeFloat),
+ file_(std::move(file)) {
+ // Handle errors from the OpenWriteOnly call in above constructor.
+ RTC_CHECK(file_.is_open()) << "Invalid file. Could not create wav file.";
+
+ RTC_CHECK(CheckWavParameters(num_channels_, sample_rate_, format_,
+ num_samples_written_));
+
+ // Write a blank placeholder header, since we need to know the total number
+ // of samples before we can fill in the real data.
+ static const uint8_t blank_header[MaxWavHeaderSize()] = {0};
+ RTC_CHECK(file_.Write(blank_header, WavHeaderSize(format_)));
+}
+
+void WavWriter::WriteSamples(const int16_t* samples, size_t num_samples) {
+#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
+#error "Need to convert samples to little-endian when writing to WAV file"
+#endif
+
+ for (size_t i = 0; i < num_samples; i += kMaxChunksize) {
+ const size_t num_remaining_samples = num_samples - i;
+ const size_t num_samples_to_write =
+ std::min(kMaxChunksize, num_remaining_samples);
+
+ if (format_ == WavFormat::kWavFormatPcm) {
+ RTC_CHECK(
+ file_.Write(&samples[i], num_samples_to_write * sizeof(samples[0])));
+ } else {
+ RTC_CHECK_EQ(format_, WavFormat::kWavFormatIeeeFloat);
+ std::array<float, kMaxChunksize> converted_samples;
+ for (size_t j = 0; j < num_samples_to_write; ++j) {
+ converted_samples[j] = S16ToFloat(samples[i + j]);
+ }
+ RTC_CHECK(
+ file_.Write(converted_samples.data(),
+ num_samples_to_write * sizeof(converted_samples[0])));
+ }
+
+ num_samples_written_ += num_samples_to_write;
+ RTC_CHECK_GE(num_samples_written_,
+ num_samples_to_write); // detect size_t overflow
+ }
+}
+
+void WavWriter::WriteSamples(const float* samples, size_t num_samples) {
+#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
+#error "Need to convert samples to little-endian when writing to WAV file"
+#endif
+
+ for (size_t i = 0; i < num_samples; i += kMaxChunksize) {
+ const size_t num_remaining_samples = num_samples - i;
+ const size_t num_samples_to_write =
+ std::min(kMaxChunksize, num_remaining_samples);
+
+ if (format_ == WavFormat::kWavFormatPcm) {
+ std::array<int16_t, kMaxChunksize> converted_samples;
+ for (size_t j = 0; j < num_samples_to_write; ++j) {
+ converted_samples[j] = FloatS16ToS16(samples[i + j]);
+ }
+ RTC_CHECK(
+ file_.Write(converted_samples.data(),
+ num_samples_to_write * sizeof(converted_samples[0])));
+ } else {
+ RTC_CHECK_EQ(format_, WavFormat::kWavFormatIeeeFloat);
+ std::array<float, kMaxChunksize> converted_samples;
+ for (size_t j = 0; j < num_samples_to_write; ++j) {
+ converted_samples[j] = FloatS16ToFloat(samples[i + j]);
+ }
+ RTC_CHECK(
+ file_.Write(converted_samples.data(),
+ num_samples_to_write * sizeof(converted_samples[0])));
+ }
+
+ num_samples_written_ += num_samples_to_write;
+ RTC_CHECK(num_samples_written_ >=
+ num_samples_to_write); // detect size_t overflow
+ }
+}
+
+void WavWriter::Close() {
+ RTC_CHECK(file_.Rewind());
+ std::array<uint8_t, MaxWavHeaderSize()> header;
+ size_t header_size;
+ WriteWavHeader(num_channels_, sample_rate_, format_, num_samples_written_,
+ header.data(), &header_size);
+ RTC_CHECK(file_.Write(header.data(), header_size));
+ RTC_CHECK(file_.Close());
+}
+
+} // namespace webrtc