diff --git a/webrtc/common_audio/meson.build b/webrtc/common_audio/meson.build index 1299bcd..d8892ac 100644 --- a/webrtc/common_audio/meson.build +++ b/webrtc/common_audio/meson.build @@ -55,8 +55,6 @@ common_audio_sources = [ 'vad/vad_gmm.c', 'vad/vad_sp.c', 'vad/webrtc_vad.c', - 'wav_file.cc', - 'wav_header.cc', 'window_generator.cc', ] diff --git a/webrtc/common_audio/wav_file.cc b/webrtc/common_audio/wav_file.cc deleted file mode 100644 index 127c9c0..0000000 --- a/webrtc/common_audio/wav_file.cc +++ /dev/null @@ -1,290 +0,0 @@ -/* - * 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 - -#include -#include -#include -#include -#include - -#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::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 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 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(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 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 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 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 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 diff --git a/webrtc/common_audio/wav_file.h b/webrtc/common_audio/wav_file.h deleted file mode 100644 index 72a4db7..0000000 --- a/webrtc/common_audio/wav_file.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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. - */ - -#ifndef COMMON_AUDIO_WAV_FILE_H_ -#define COMMON_AUDIO_WAV_FILE_H_ - -#include - -#include -#include - -#include "common_audio/wav_header.h" -#include "rtc_base/system/file_wrapper.h" - -namespace webrtc { - -// Interface to provide access WAV file parameters. -class WavFile { - public: - enum class SampleFormat { kInt16, kFloat }; - - virtual ~WavFile() {} - - virtual int sample_rate() const = 0; - virtual size_t num_channels() const = 0; - virtual size_t num_samples() const = 0; -}; - -// Simple C++ class for writing 16-bit integer and 32 bit floating point PCM WAV -// files. All error handling is by calls to RTC_CHECK(), making it unsuitable -// for anything but debug code. -class WavWriter final : public WavFile { - public: - // Opens a new WAV file for writing. - WavWriter(absl::string_view filename, - int sample_rate, - size_t num_channels, - SampleFormat sample_format = SampleFormat::kInt16); - WavWriter(FileWrapper file, - int sample_rate, - size_t num_channels, - SampleFormat sample_format = SampleFormat::kInt16); - - // Closes the WAV file, after writing its header. - ~WavWriter() { Close(); } - - WavWriter(const WavWriter&) = delete; - WavWriter& operator=(const WavWriter&) = delete; - - // Write additional samples to the file. Each sample is in the range - // [-32768.0,32767.0], and there must be the previously specified number of - // interleaved channels. - void WriteSamples(const float* samples, size_t num_samples); - void WriteSamples(const int16_t* samples, size_t num_samples); - - int sample_rate() const override { return sample_rate_; } - size_t num_channels() const override { return num_channels_; } - size_t num_samples() const override { return num_samples_written_; } - - private: - void Close(); - const int sample_rate_; - const size_t num_channels_; - size_t num_samples_written_; - WavFormat format_; - FileWrapper file_; -}; - -// Follows the conventions of WavWriter. -class WavReader final : public WavFile { - public: - // Opens an existing WAV file for reading. - explicit WavReader(absl::string_view filename); - explicit WavReader(FileWrapper file); - - // Close the WAV file. - ~WavReader() { Close(); } - - WavReader(const WavReader&) = delete; - WavReader& operator=(const WavReader&) = delete; - - // Resets position to the beginning of the file. - void Reset(); - - // Returns the number of samples read. If this is less than requested, - // verifies that the end of the file was reached. - size_t ReadSamples(size_t num_samples, float* samples); - size_t ReadSamples(size_t num_samples, int16_t* samples); - - int sample_rate() const override { return sample_rate_; } - size_t num_channels() const override { return num_channels_; } - size_t num_samples() const override { return num_samples_in_file_; } - - private: - void Close(); - int sample_rate_; - size_t num_channels_; - WavFormat format_; - size_t num_samples_in_file_; - size_t num_unread_samples_; - FileWrapper file_; - int64_t - data_start_pos_; // Position in the file immediately after WAV header. -}; - -} // namespace webrtc - -#endif // COMMON_AUDIO_WAV_FILE_H_ diff --git a/webrtc/common_audio/wav_header.cc b/webrtc/common_audio/wav_header.cc deleted file mode 100644 index bca209a..0000000 --- a/webrtc/common_audio/wav_header.cc +++ /dev/null @@ -1,435 +0,0 @@ -/* - * 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. - */ - -// Based on the WAV file format documentation at -// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ and -// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html - -#include "common_audio/wav_header.h" - -#include -#include -#include - -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" -#include "rtc_base/sanitizer.h" -#include "rtc_base/system/arch.h" - -namespace webrtc { -namespace { - -#ifndef WEBRTC_ARCH_LITTLE_ENDIAN -#error "Code not working properly for big endian platforms." -#endif - -#pragma pack(2) -struct ChunkHeader { - uint32_t ID; - uint32_t Size; -}; -static_assert(sizeof(ChunkHeader) == 8, "ChunkHeader size"); - -#pragma pack(2) -struct RiffHeader { - ChunkHeader header; - uint32_t Format; -}; -static_assert(sizeof(RiffHeader) == sizeof(ChunkHeader) + 4, "RiffHeader size"); - -// We can't nest this definition in WavHeader, because VS2013 gives an error -// on sizeof(WavHeader::fmt): "error C2070: 'unknown': illegal sizeof operand". -#pragma pack(2) -struct FmtPcmSubchunk { - ChunkHeader header; - uint16_t AudioFormat; - uint16_t NumChannels; - uint32_t SampleRate; - uint32_t ByteRate; - uint16_t BlockAlign; - uint16_t BitsPerSample; -}; -static_assert(sizeof(FmtPcmSubchunk) == 24, "FmtPcmSubchunk size"); -const uint32_t kFmtPcmSubchunkSize = - sizeof(FmtPcmSubchunk) - sizeof(ChunkHeader); - -// Pack struct to avoid additional padding bytes. -#pragma pack(2) -struct FmtIeeeFloatSubchunk { - ChunkHeader header; - uint16_t AudioFormat; - uint16_t NumChannels; - uint32_t SampleRate; - uint32_t ByteRate; - uint16_t BlockAlign; - uint16_t BitsPerSample; - uint16_t ExtensionSize; -}; -static_assert(sizeof(FmtIeeeFloatSubchunk) == 26, "FmtIeeeFloatSubchunk size"); -const uint32_t kFmtIeeeFloatSubchunkSize = - sizeof(FmtIeeeFloatSubchunk) - sizeof(ChunkHeader); - -// Simple PCM wav header. It does not include chunks that are not essential to -// read audio samples. -#pragma pack(2) -struct WavHeaderPcm { - RiffHeader riff; - FmtPcmSubchunk fmt; - struct { - ChunkHeader header; - } data; -}; -static_assert(sizeof(WavHeaderPcm) == kPcmWavHeaderSize, - "no padding in header"); - -// IEEE Float Wav header, includes extra chunks necessary for proper non-PCM -// WAV implementation. -#pragma pack(2) -struct WavHeaderIeeeFloat { - RiffHeader riff; - FmtIeeeFloatSubchunk fmt; - struct { - ChunkHeader header; - uint32_t SampleLength; - } fact; - struct { - ChunkHeader header; - } data; -}; -static_assert(sizeof(WavHeaderIeeeFloat) == kIeeeFloatWavHeaderSize, - "no padding in header"); - -uint32_t PackFourCC(char a, char b, char c, char d) { - uint32_t packed_value = - static_cast(a) | static_cast(b) << 8 | - static_cast(c) << 16 | static_cast(d) << 24; - return packed_value; -} - -std::string ReadFourCC(uint32_t x) { - return std::string(reinterpret_cast(&x), 4); -} - -uint16_t MapWavFormatToHeaderField(WavFormat format) { - switch (format) { - case WavFormat::kWavFormatPcm: - return 1; - case WavFormat::kWavFormatIeeeFloat: - return 3; - case WavFormat::kWavFormatALaw: - return 6; - case WavFormat::kWavFormatMuLaw: - return 7; - } - RTC_CHECK_NOTREACHED(); -} - -WavFormat MapHeaderFieldToWavFormat(uint16_t format_header_value) { - if (format_header_value == 1) { - return WavFormat::kWavFormatPcm; - } - if (format_header_value == 3) { - return WavFormat::kWavFormatIeeeFloat; - } - - RTC_CHECK(false) << "Unsupported WAV format"; -} - -uint32_t RiffChunkSize(size_t bytes_in_payload, size_t header_size) { - return static_cast(bytes_in_payload + header_size - - sizeof(ChunkHeader)); -} - -uint32_t ByteRate(size_t num_channels, - int sample_rate, - size_t bytes_per_sample) { - return static_cast(num_channels * sample_rate * bytes_per_sample); -} - -uint16_t BlockAlign(size_t num_channels, size_t bytes_per_sample) { - return static_cast(num_channels * bytes_per_sample); -} - -// Finds a chunk having the sought ID. If found, then `readable` points to the -// first byte of the sought chunk data. If not found, the end of the file is -// reached. -bool FindWaveChunk(ChunkHeader* chunk_header, - WavHeaderReader* readable, - const std::string sought_chunk_id) { - RTC_DCHECK_EQ(sought_chunk_id.size(), 4); - while (true) { - if (readable->Read(chunk_header, sizeof(*chunk_header)) != - sizeof(*chunk_header)) - return false; // EOF. - if (ReadFourCC(chunk_header->ID) == sought_chunk_id) - return true; // Sought chunk found. - // Ignore current chunk by skipping its payload. - if (!readable->SeekForward(chunk_header->Size)) - return false; // EOF or error. - } -} - -bool ReadFmtChunkData(FmtPcmSubchunk* fmt_subchunk, WavHeaderReader* readable) { - // Reads "fmt " chunk payload. - if (readable->Read(&(fmt_subchunk->AudioFormat), kFmtPcmSubchunkSize) != - kFmtPcmSubchunkSize) - return false; - const uint32_t fmt_size = fmt_subchunk->header.Size; - if (fmt_size != kFmtPcmSubchunkSize) { - // There is an optional two-byte extension field permitted to be present - // with PCM, but which must be zero. - int16_t ext_size; - if (kFmtPcmSubchunkSize + sizeof(ext_size) != fmt_size) - return false; - if (readable->Read(&ext_size, sizeof(ext_size)) != sizeof(ext_size)) - return false; - if (ext_size != 0) - return false; - } - return true; -} - -void WritePcmWavHeader(size_t num_channels, - int sample_rate, - size_t bytes_per_sample, - size_t num_samples, - uint8_t* buf, - size_t* header_size) { - RTC_CHECK(buf); - RTC_CHECK(header_size); - *header_size = kPcmWavHeaderSize; - auto header = rtc::MsanUninitialized({}); - const size_t bytes_in_payload = bytes_per_sample * num_samples; - - header.riff.header.ID = PackFourCC('R', 'I', 'F', 'F'); - header.riff.header.Size = RiffChunkSize(bytes_in_payload, *header_size); - header.riff.Format = PackFourCC('W', 'A', 'V', 'E'); - header.fmt.header.ID = PackFourCC('f', 'm', 't', ' '); - header.fmt.header.Size = kFmtPcmSubchunkSize; - header.fmt.AudioFormat = MapWavFormatToHeaderField(WavFormat::kWavFormatPcm); - header.fmt.NumChannels = static_cast(num_channels); - header.fmt.SampleRate = sample_rate; - header.fmt.ByteRate = ByteRate(num_channels, sample_rate, bytes_per_sample); - header.fmt.BlockAlign = BlockAlign(num_channels, bytes_per_sample); - header.fmt.BitsPerSample = static_cast(8 * bytes_per_sample); - header.data.header.ID = PackFourCC('d', 'a', 't', 'a'); - header.data.header.Size = static_cast(bytes_in_payload); - - // Do an extra copy rather than writing everything to buf directly, since buf - // might not be correctly aligned. - memcpy(buf, &header, *header_size); -} - -void WriteIeeeFloatWavHeader(size_t num_channels, - int sample_rate, - size_t bytes_per_sample, - size_t num_samples, - uint8_t* buf, - size_t* header_size) { - RTC_CHECK(buf); - RTC_CHECK(header_size); - *header_size = kIeeeFloatWavHeaderSize; - auto header = rtc::MsanUninitialized({}); - const size_t bytes_in_payload = bytes_per_sample * num_samples; - - header.riff.header.ID = PackFourCC('R', 'I', 'F', 'F'); - header.riff.header.Size = RiffChunkSize(bytes_in_payload, *header_size); - header.riff.Format = PackFourCC('W', 'A', 'V', 'E'); - header.fmt.header.ID = PackFourCC('f', 'm', 't', ' '); - header.fmt.header.Size = kFmtIeeeFloatSubchunkSize; - header.fmt.AudioFormat = - MapWavFormatToHeaderField(WavFormat::kWavFormatIeeeFloat); - header.fmt.NumChannels = static_cast(num_channels); - header.fmt.SampleRate = sample_rate; - header.fmt.ByteRate = ByteRate(num_channels, sample_rate, bytes_per_sample); - header.fmt.BlockAlign = BlockAlign(num_channels, bytes_per_sample); - header.fmt.BitsPerSample = static_cast(8 * bytes_per_sample); - header.fmt.ExtensionSize = 0; - header.fact.header.ID = PackFourCC('f', 'a', 'c', 't'); - header.fact.header.Size = 4; - header.fact.SampleLength = static_cast(num_channels * num_samples); - header.data.header.ID = PackFourCC('d', 'a', 't', 'a'); - header.data.header.Size = static_cast(bytes_in_payload); - - // Do an extra copy rather than writing everything to buf directly, since buf - // might not be correctly aligned. - memcpy(buf, &header, *header_size); -} - -// Returns the number of bytes per sample for the format. -size_t GetFormatBytesPerSample(WavFormat format) { - switch (format) { - case WavFormat::kWavFormatPcm: - // Other values may be OK, but for now we're conservative. - return 2; - case WavFormat::kWavFormatALaw: - case WavFormat::kWavFormatMuLaw: - return 1; - case WavFormat::kWavFormatIeeeFloat: - return 4; - } - RTC_CHECK_NOTREACHED(); -} - -bool CheckWavParameters(size_t num_channels, - int sample_rate, - WavFormat format, - size_t bytes_per_sample, - size_t num_samples) { - // num_channels, sample_rate, and bytes_per_sample must be positive, must fit - // in their respective fields, and their product must fit in the 32-bit - // ByteRate field. - if (num_channels == 0 || sample_rate <= 0 || bytes_per_sample == 0) - return false; - if (static_cast(sample_rate) > std::numeric_limits::max()) - return false; - if (num_channels > std::numeric_limits::max()) - return false; - if (static_cast(bytes_per_sample) * 8 > - std::numeric_limits::max()) - return false; - if (static_cast(sample_rate) * num_channels * bytes_per_sample > - std::numeric_limits::max()) - return false; - - // format and bytes_per_sample must agree. - switch (format) { - case WavFormat::kWavFormatPcm: - // Other values may be OK, but for now we're conservative: - if (bytes_per_sample != 1 && bytes_per_sample != 2) - return false; - break; - case WavFormat::kWavFormatALaw: - case WavFormat::kWavFormatMuLaw: - if (bytes_per_sample != 1) - return false; - break; - case WavFormat::kWavFormatIeeeFloat: - if (bytes_per_sample != 4) - return false; - break; - default: - return false; - } - - // The number of bytes in the file, not counting the first ChunkHeader, must - // be less than 2^32; otherwise, the ChunkSize field overflows. - const size_t header_size = kPcmWavHeaderSize - sizeof(ChunkHeader); - const size_t max_samples = - (std::numeric_limits::max() - header_size) / bytes_per_sample; - if (num_samples > max_samples) - return false; - - // Each channel must have the same number of samples. - if (num_samples % num_channels != 0) - return false; - - return true; -} - -} // namespace - -bool CheckWavParameters(size_t num_channels, - int sample_rate, - WavFormat format, - size_t num_samples) { - return CheckWavParameters(num_channels, sample_rate, format, - GetFormatBytesPerSample(format), num_samples); -} - -void WriteWavHeader(size_t num_channels, - int sample_rate, - WavFormat format, - size_t num_samples, - uint8_t* buf, - size_t* header_size) { - RTC_CHECK(buf); - RTC_CHECK(header_size); - - const size_t bytes_per_sample = GetFormatBytesPerSample(format); - RTC_CHECK(CheckWavParameters(num_channels, sample_rate, format, - bytes_per_sample, num_samples)); - if (format == WavFormat::kWavFormatPcm) { - WritePcmWavHeader(num_channels, sample_rate, bytes_per_sample, num_samples, - buf, header_size); - } else { - RTC_CHECK_EQ(format, WavFormat::kWavFormatIeeeFloat); - WriteIeeeFloatWavHeader(num_channels, sample_rate, bytes_per_sample, - num_samples, buf, header_size); - } -} - -bool ReadWavHeader(WavHeaderReader* readable, - size_t* num_channels, - int* sample_rate, - WavFormat* format, - size_t* bytes_per_sample, - size_t* num_samples, - int64_t* data_start_pos) { - // Read using the PCM header, even though it might be float Wav file - auto header = rtc::MsanUninitialized({}); - - // Read RIFF chunk. - if (readable->Read(&header.riff, sizeof(header.riff)) != sizeof(header.riff)) - return false; - if (ReadFourCC(header.riff.header.ID) != "RIFF") - return false; - if (ReadFourCC(header.riff.Format) != "WAVE") - return false; - - // Find "fmt " and "data" chunks. While the official Wave file specification - // does not put requirements on the chunks order, it is uncommon to find the - // "data" chunk before the "fmt " one. The code below fails if this is not the - // case. - if (!FindWaveChunk(&header.fmt.header, readable, "fmt ")) { - RTC_LOG(LS_ERROR) << "Cannot find 'fmt ' chunk."; - return false; - } - if (!ReadFmtChunkData(&header.fmt, readable)) { - RTC_LOG(LS_ERROR) << "Cannot read 'fmt ' chunk."; - return false; - } - if (!FindWaveChunk(&header.data.header, readable, "data")) { - RTC_LOG(LS_ERROR) << "Cannot find 'data' chunk."; - return false; - } - - // Parse needed fields. - *format = MapHeaderFieldToWavFormat(header.fmt.AudioFormat); - *num_channels = header.fmt.NumChannels; - *sample_rate = header.fmt.SampleRate; - *bytes_per_sample = header.fmt.BitsPerSample / 8; - const size_t bytes_in_payload = header.data.header.Size; - if (*bytes_per_sample == 0) - return false; - *num_samples = bytes_in_payload / *bytes_per_sample; - - const size_t header_size = *format == WavFormat::kWavFormatPcm - ? kPcmWavHeaderSize - : kIeeeFloatWavHeaderSize; - - if (header.riff.header.Size < RiffChunkSize(bytes_in_payload, header_size)) - return false; - if (header.fmt.ByteRate != - ByteRate(*num_channels, *sample_rate, *bytes_per_sample)) - return false; - if (header.fmt.BlockAlign != BlockAlign(*num_channels, *bytes_per_sample)) - return false; - - if (!CheckWavParameters(*num_channels, *sample_rate, *format, - *bytes_per_sample, *num_samples)) { - return false; - } - - *data_start_pos = readable->GetPosition(); - return true; -} - -} // namespace webrtc diff --git a/webrtc/common_audio/wav_header.h b/webrtc/common_audio/wav_header.h deleted file mode 100644 index a1aa942..0000000 --- a/webrtc/common_audio/wav_header.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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. - */ - -#ifndef COMMON_AUDIO_WAV_HEADER_H_ -#define COMMON_AUDIO_WAV_HEADER_H_ - -#include -#include - -#include - -#include "rtc_base/checks.h" - -namespace webrtc { - -// Interface providing header reading functionality. -class WavHeaderReader { - public: - // Returns the number of bytes read. - virtual size_t Read(void* buf, size_t num_bytes) = 0; - virtual bool SeekForward(uint32_t num_bytes) = 0; - virtual ~WavHeaderReader() = default; - virtual int64_t GetPosition() = 0; -}; - -// Possible WAV formats. -enum class WavFormat { - kWavFormatPcm = 1, // PCM, each sample of size bytes_per_sample. - kWavFormatIeeeFloat = 3, // IEEE float. - kWavFormatALaw = 6, // 8-bit ITU-T G.711 A-law. - kWavFormatMuLaw = 7, // 8-bit ITU-T G.711 mu-law. -}; - -// Header sizes for supported WAV formats. -constexpr size_t kPcmWavHeaderSize = 44; -constexpr size_t kIeeeFloatWavHeaderSize = 58; - -// Returns the size of the WAV header for the specified format. -constexpr size_t WavHeaderSize(WavFormat format) { - if (format == WavFormat::kWavFormatPcm) { - return kPcmWavHeaderSize; - } - RTC_CHECK_EQ(format, WavFormat::kWavFormatIeeeFloat); - return kIeeeFloatWavHeaderSize; -} - -// Returns the maximum size of the supported WAV formats. -constexpr size_t MaxWavHeaderSize() { - return std::max(WavHeaderSize(WavFormat::kWavFormatPcm), - WavHeaderSize(WavFormat::kWavFormatIeeeFloat)); -} - -// Return true if the given parameters will make a well-formed WAV header. -bool CheckWavParameters(size_t num_channels, - int sample_rate, - WavFormat format, - size_t num_samples); - -// Write a kWavHeaderSize bytes long WAV header to buf. The payload that -// follows the header is supposed to have the specified number of interleaved -// channels and contain the specified total number of samples of the specified -// type. The size of the header is returned in header_size. CHECKs the input -// parameters for validity. -void WriteWavHeader(size_t num_channels, - int sample_rate, - WavFormat format, - size_t num_samples, - uint8_t* buf, - size_t* header_size); - -// Read a WAV header from an implemented WavHeaderReader and parse the values -// into the provided output parameters. WavHeaderReader is used because the -// header can be variably sized. Returns false if the header is invalid. -bool ReadWavHeader(WavHeaderReader* readable, - size_t* num_channels, - int* sample_rate, - WavFormat* format, - size_t* bytes_per_sample, - size_t* num_samples, - int64_t* data_start_pos); - -} // namespace webrtc - -#endif // COMMON_AUDIO_WAV_HEADER_H_