tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

wav_file.cc (10741B)


      1 /*
      2 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "common_audio/wav_file.h"
     12 
     13 #include <algorithm>
     14 #include <array>
     15 #include <cstdint>
     16 #include <cstdio>
     17 #include <type_traits>
     18 #include <utility>
     19 
     20 #include "absl/strings/string_view.h"
     21 #include "common_audio/include/audio_util.h"
     22 #include "common_audio/wav_header.h"
     23 #include "rtc_base/checks.h"
     24 #include "rtc_base/system/arch.h"
     25 #include "rtc_base/system/file_wrapper.h"
     26 
     27 namespace webrtc {
     28 namespace {
     29 
     30 static_assert(std::is_trivially_destructible<WavFormat>::value, "");
     31 
     32 // Checks whether the format is supported or not.
     33 bool FormatSupported(WavFormat format) {
     34  // Only PCM and IEEE Float formats are supported.
     35  return format == WavFormat::kWavFormatPcm ||
     36         format == WavFormat::kWavFormatIeeeFloat;
     37 }
     38 
     39 // Doesn't take ownership of the file handle and won't close it.
     40 class WavHeaderFileReader : public WavHeaderReader {
     41 public:
     42  explicit WavHeaderFileReader(FileWrapper* file) : file_(file) {}
     43 
     44  WavHeaderFileReader(const WavHeaderFileReader&) = delete;
     45  WavHeaderFileReader& operator=(const WavHeaderFileReader&) = delete;
     46 
     47  size_t Read(void* buf, size_t num_bytes) override {
     48    size_t count = file_->Read(buf, num_bytes);
     49    pos_ += count;
     50    return count;
     51  }
     52  bool SeekForward(uint32_t num_bytes) override {
     53    bool success = file_->SeekRelative(num_bytes);
     54    if (success) {
     55      pos_ += num_bytes;
     56    }
     57    return success;
     58  }
     59  int64_t GetPosition() override { return pos_; }
     60 
     61 private:
     62  FileWrapper* file_;
     63  int64_t pos_ = 0;
     64 };
     65 
     66 constexpr size_t kMaxChunksize = 4096;
     67 
     68 }  // namespace
     69 
     70 WavReader::WavReader(absl::string_view filename)
     71    : WavReader(FileWrapper::OpenReadOnly(filename)) {}
     72 
     73 WavReader::WavReader(FileWrapper file) : file_(std::move(file)) {
     74  RTC_CHECK(file_.is_open())
     75      << "Invalid file. Could not create file handle for wav file.";
     76 
     77  WavHeaderFileReader readable(&file_);
     78  size_t bytes_per_sample;
     79  RTC_CHECK(ReadWavHeader(&readable, &num_channels_, &sample_rate_, &format_,
     80                          &bytes_per_sample, &num_samples_in_file_,
     81                          &data_start_pos_));
     82  num_unread_samples_ = num_samples_in_file_;
     83  RTC_CHECK(FormatSupported(format_)) << "Non-implemented wav-format";
     84 }
     85 
     86 void WavReader::Reset() {
     87  RTC_CHECK(file_.SeekTo(data_start_pos_))
     88      << "Failed to set position in the file to WAV data start position";
     89  num_unread_samples_ = num_samples_in_file_;
     90 }
     91 
     92 size_t WavReader::ReadSamples(const size_t num_samples,
     93                              int16_t* const samples) {
     94 #ifndef WEBRTC_ARCH_LITTLE_ENDIAN
     95 #error "Need to convert samples to big-endian when reading from WAV file"
     96 #endif
     97 
     98  size_t num_samples_left_to_read = num_samples;
     99  size_t next_chunk_start = 0;
    100  while (num_samples_left_to_read > 0 && num_unread_samples_ > 0) {
    101    const size_t chunk_size = std::min(
    102        std::min(kMaxChunksize, num_samples_left_to_read), num_unread_samples_);
    103    size_t num_bytes_read;
    104    size_t num_samples_read;
    105    if (format_ == WavFormat::kWavFormatIeeeFloat) {
    106      std::array<float, kMaxChunksize> samples_to_convert;
    107      num_bytes_read = file_.Read(samples_to_convert.data(),
    108                                  chunk_size * sizeof(samples_to_convert[0]));
    109      num_samples_read = num_bytes_read / sizeof(samples_to_convert[0]);
    110 
    111      for (size_t j = 0; j < num_samples_read; ++j) {
    112        samples[next_chunk_start + j] = FloatToS16(samples_to_convert[j]);
    113      }
    114    } else {
    115      RTC_CHECK_EQ(format_, WavFormat::kWavFormatPcm);
    116      num_bytes_read = file_.Read(&samples[next_chunk_start],
    117                                  chunk_size * sizeof(samples[0]));
    118      num_samples_read = num_bytes_read / sizeof(samples[0]);
    119    }
    120    RTC_CHECK(num_samples_read == 0 || (num_bytes_read % num_samples_read) == 0)
    121        << "Corrupt file: file ended in the middle of a sample.";
    122    RTC_CHECK(num_samples_read == chunk_size || file_.ReadEof())
    123        << "Corrupt file: payload size does not match header.";
    124 
    125    next_chunk_start += num_samples_read;
    126    num_unread_samples_ -= num_samples_read;
    127    num_samples_left_to_read -= num_samples_read;
    128  }
    129 
    130  return num_samples - num_samples_left_to_read;
    131 }
    132 
    133 size_t WavReader::ReadSamples(const size_t num_samples, float* const samples) {
    134 #ifndef WEBRTC_ARCH_LITTLE_ENDIAN
    135 #error "Need to convert samples to big-endian when reading from WAV file"
    136 #endif
    137 
    138  size_t num_samples_left_to_read = num_samples;
    139  size_t next_chunk_start = 0;
    140  while (num_samples_left_to_read > 0 && num_unread_samples_ > 0) {
    141    const size_t chunk_size = std::min(
    142        std::min(kMaxChunksize, num_samples_left_to_read), num_unread_samples_);
    143    size_t num_bytes_read;
    144    size_t num_samples_read;
    145    if (format_ == WavFormat::kWavFormatPcm) {
    146      std::array<int16_t, kMaxChunksize> samples_to_convert;
    147      num_bytes_read = file_.Read(samples_to_convert.data(),
    148                                  chunk_size * sizeof(samples_to_convert[0]));
    149      num_samples_read = num_bytes_read / sizeof(samples_to_convert[0]);
    150 
    151      for (size_t j = 0; j < num_samples_read; ++j) {
    152        samples[next_chunk_start + j] =
    153            static_cast<float>(samples_to_convert[j]);
    154      }
    155    } else {
    156      RTC_CHECK_EQ(format_, WavFormat::kWavFormatIeeeFloat);
    157      num_bytes_read = file_.Read(&samples[next_chunk_start],
    158                                  chunk_size * sizeof(samples[0]));
    159      num_samples_read = num_bytes_read / sizeof(samples[0]);
    160 
    161      for (size_t j = 0; j < num_samples_read; ++j) {
    162        samples[next_chunk_start + j] =
    163            FloatToFloatS16(samples[next_chunk_start + j]);
    164      }
    165    }
    166    RTC_CHECK(num_samples_read == 0 || (num_bytes_read % num_samples_read) == 0)
    167        << "Corrupt file: file ended in the middle of a sample.";
    168    RTC_CHECK(num_samples_read == chunk_size || file_.ReadEof())
    169        << "Corrupt file: payload size does not match header.";
    170 
    171    next_chunk_start += num_samples_read;
    172    num_unread_samples_ -= num_samples_read;
    173    num_samples_left_to_read -= num_samples_read;
    174  }
    175 
    176  return num_samples - num_samples_left_to_read;
    177 }
    178 
    179 void WavReader::Close() {
    180  file_.Close();
    181 }
    182 
    183 WavWriter::WavWriter(absl::string_view filename,
    184                     int sample_rate,
    185                     size_t num_channels,
    186                     SampleFormat sample_format)
    187    // Unlike plain fopen, OpenWriteOnly takes care of filename utf8 ->
    188    // wchar conversion on windows.
    189    : WavWriter(FileWrapper::OpenWriteOnly(filename),
    190                sample_rate,
    191                num_channels,
    192                sample_format) {}
    193 
    194 WavWriter::WavWriter(FileWrapper file,
    195                     int sample_rate,
    196                     size_t num_channels,
    197                     SampleFormat sample_format)
    198    : sample_rate_(sample_rate),
    199      num_channels_(num_channels),
    200      num_samples_written_(0),
    201      format_(sample_format == SampleFormat::kInt16
    202                  ? WavFormat::kWavFormatPcm
    203                  : WavFormat::kWavFormatIeeeFloat),
    204      file_(std::move(file)) {
    205  // Handle errors from the OpenWriteOnly call in above constructor.
    206  RTC_CHECK(file_.is_open()) << "Invalid file. Could not create wav file.";
    207 
    208  RTC_CHECK(CheckWavParameters(num_channels_, sample_rate_, format_,
    209                               num_samples_written_));
    210 
    211  // Write a blank placeholder header, since we need to know the total number
    212  // of samples before we can fill in the real data.
    213  static const uint8_t blank_header[MaxWavHeaderSize()] = {0};
    214  RTC_CHECK(file_.Write(blank_header, WavHeaderSize(format_)));
    215 }
    216 
    217 void WavWriter::WriteSamples(const int16_t* samples, size_t num_samples) {
    218 #ifndef WEBRTC_ARCH_LITTLE_ENDIAN
    219 #error "Need to convert samples to little-endian when writing to WAV file"
    220 #endif
    221 
    222  for (size_t i = 0; i < num_samples; i += kMaxChunksize) {
    223    const size_t num_remaining_samples = num_samples - i;
    224    const size_t num_samples_to_write =
    225        std::min(kMaxChunksize, num_remaining_samples);
    226 
    227    if (format_ == WavFormat::kWavFormatPcm) {
    228      RTC_CHECK(
    229          file_.Write(&samples[i], num_samples_to_write * sizeof(samples[0])));
    230    } else {
    231      RTC_CHECK_EQ(format_, WavFormat::kWavFormatIeeeFloat);
    232      std::array<float, kMaxChunksize> converted_samples;
    233      for (size_t j = 0; j < num_samples_to_write; ++j) {
    234        converted_samples[j] = S16ToFloat(samples[i + j]);
    235      }
    236      RTC_CHECK(
    237          file_.Write(converted_samples.data(),
    238                      num_samples_to_write * sizeof(converted_samples[0])));
    239    }
    240 
    241    num_samples_written_ += num_samples_to_write;
    242    RTC_CHECK_GE(num_samples_written_,
    243                 num_samples_to_write);  // detect size_t overflow
    244  }
    245 }
    246 
    247 void WavWriter::WriteSamples(const float* samples, size_t num_samples) {
    248 #ifndef WEBRTC_ARCH_LITTLE_ENDIAN
    249 #error "Need to convert samples to little-endian when writing to WAV file"
    250 #endif
    251 
    252  for (size_t i = 0; i < num_samples; i += kMaxChunksize) {
    253    const size_t num_remaining_samples = num_samples - i;
    254    const size_t num_samples_to_write =
    255        std::min(kMaxChunksize, num_remaining_samples);
    256 
    257    if (format_ == WavFormat::kWavFormatPcm) {
    258      std::array<int16_t, kMaxChunksize> converted_samples;
    259      for (size_t j = 0; j < num_samples_to_write; ++j) {
    260        converted_samples[j] = FloatS16ToS16(samples[i + j]);
    261      }
    262      RTC_CHECK(
    263          file_.Write(converted_samples.data(),
    264                      num_samples_to_write * sizeof(converted_samples[0])));
    265    } else {
    266      RTC_CHECK_EQ(format_, WavFormat::kWavFormatIeeeFloat);
    267      std::array<float, kMaxChunksize> converted_samples;
    268      for (size_t j = 0; j < num_samples_to_write; ++j) {
    269        converted_samples[j] = FloatS16ToFloat(samples[i + j]);
    270      }
    271      RTC_CHECK(
    272          file_.Write(converted_samples.data(),
    273                      num_samples_to_write * sizeof(converted_samples[0])));
    274    }
    275 
    276    num_samples_written_ += num_samples_to_write;
    277    RTC_CHECK(num_samples_written_ >=
    278              num_samples_to_write);  // detect size_t overflow
    279  }
    280 }
    281 
    282 void WavWriter::Close() {
    283  RTC_CHECK(file_.Rewind());
    284  std::array<uint8_t, MaxWavHeaderSize()> header;
    285  size_t header_size;
    286  WriteWavHeader(num_channels_, sample_rate_, format_, num_samples_written_,
    287                 header.data(), &header_size);
    288  RTC_CHECK(file_.Write(header.data(), header_size));
    289  RTC_CHECK(file_.Close());
    290 }
    291 
    292 }  // namespace webrtc