blob: 30968c25c90fecc8988fd2b09accb5f0e6f9978b [file] [log] [blame]
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +00001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "common_audio/wav_file.h"
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000012
Yves Gerey988cc082018-10-23 12:03:01 +020013#include <errno.h>
Jonas Olssona4d87372019-07-05 19:08:33 +020014
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000015#include <algorithm>
16#include <cstdio>
Alessio Bazzicaa33c7af2018-11-08 12:16:11 +010017#include <type_traits>
Niels Möller19a1d502019-06-13 14:08:24 +020018#include <utility>
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000019
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "common_audio/include/audio_util.h"
21#include "common_audio/wav_header.h"
22#include "rtc_base/checks.h"
Artem Titove62f6002018-03-19 15:40:00 +010023#include "rtc_base/logging.h"
Niels Möllera12c42a2018-07-25 16:05:48 +020024#include "rtc_base/system/arch.h"
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000025
26namespace webrtc {
Alessio Bazzicaa33c7af2018-11-08 12:16:11 +010027namespace {
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000028
29// We write 16-bit PCM WAV files.
Alessio Bazzicaa33c7af2018-11-08 12:16:11 +010030constexpr WavFormat kWavFormat = kWavFormatPcm;
31static_assert(std::is_trivially_destructible<WavFormat>::value, "");
32constexpr size_t kBytesPerSample = 2;
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000033
andrew@webrtc.org048c5022014-12-16 20:17:21 +000034// Doesn't take ownership of the file handle and won't close it.
35class ReadableWavFile : public ReadableWav {
36 public:
Niels Möller7ba3b812019-08-06 09:58:56 +020037 explicit ReadableWavFile(FileWrapper* file) : file_(file) {}
Alessio Bazzicaa33c7af2018-11-08 12:16:11 +010038 ReadableWavFile(const ReadableWavFile&) = delete;
39 ReadableWavFile& operator=(const ReadableWavFile&) = delete;
Mirko Bonadei91df0912018-07-17 11:08:15 +020040 size_t Read(void* buf, size_t num_bytes) override {
Niels Möller7ba3b812019-08-06 09:58:56 +020041 size_t count = file_->Read(buf, num_bytes);
42 pos_ += count;
43 return count;
andrew@webrtc.org048c5022014-12-16 20:17:21 +000044 }
Alessio Bazzicaa33c7af2018-11-08 12:16:11 +010045 bool SeekForward(uint32_t num_bytes) override {
Niels Möller7ba3b812019-08-06 09:58:56 +020046 bool success = file_->SeekRelative(num_bytes);
47 if (success) {
48 pos_ += num_bytes;
49 }
50 return success;
Alessio Bazzicaa33c7af2018-11-08 12:16:11 +010051 }
Niels Möller7ba3b812019-08-06 09:58:56 +020052 int64_t GetPosition() { return pos_; }
andrew@webrtc.org048c5022014-12-16 20:17:21 +000053
54 private:
Niels Möller7ba3b812019-08-06 09:58:56 +020055 FileWrapper* file_;
56 int64_t pos_ = 0;
andrew@webrtc.org048c5022014-12-16 20:17:21 +000057};
58
Alessio Bazzicaa33c7af2018-11-08 12:16:11 +010059} // namespace
60
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000061WavReader::WavReader(const std::string& filename)
Niels Möller7ba3b812019-08-06 09:58:56 +020062 : WavReader(FileWrapper::OpenReadOnly(filename)) {}
Artem Titove62f6002018-03-19 15:40:00 +010063
Niels Möller7ba3b812019-08-06 09:58:56 +020064WavReader::WavReader(FileWrapper file) : file_(std::move(file)) {
65 RTC_CHECK(file_.is_open())
Artem Titove62f6002018-03-19 15:40:00 +010066 << "Invalid file. Could not create file handle for wav file.";
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000067
Niels Möller7ba3b812019-08-06 09:58:56 +020068 ReadableWavFile readable(&file_);
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000069 WavFormat format;
pkasting25702cb2016-01-08 13:50:27 -080070 size_t bytes_per_sample;
henrikg91d6ede2015-09-17 00:24:34 -070071 RTC_CHECK(ReadWavHeader(&readable, &num_channels_, &sample_rate_, &format,
72 &bytes_per_sample, &num_samples_));
andrew@webrtc.org048c5022014-12-16 20:17:21 +000073 num_samples_remaining_ = num_samples_;
henrikg91d6ede2015-09-17 00:24:34 -070074 RTC_CHECK_EQ(kWavFormat, format);
75 RTC_CHECK_EQ(kBytesPerSample, bytes_per_sample);
Niels Möller7ba3b812019-08-06 09:58:56 +020076 data_start_pos_ = readable.GetPosition();
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000077}
78
79WavReader::~WavReader() {
80 Close();
81}
82
Artem Titov153056b2019-04-16 16:49:32 +020083void WavReader::Reset() {
Niels Möller7ba3b812019-08-06 09:58:56 +020084 RTC_CHECK(file_.SeekTo(data_start_pos_))
Artem Titov153056b2019-04-16 16:49:32 +020085 << "Failed to set position in the file to WAV data start position";
86 num_samples_remaining_ = num_samples_;
87}
88
peahd1f718b2016-02-22 02:13:28 -080089int WavReader::sample_rate() const {
90 return sample_rate_;
91}
92
93size_t WavReader::num_channels() const {
94 return num_channels_;
95}
96
97size_t WavReader::num_samples() const {
98 return num_samples_;
99}
100
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000101size_t WavReader::ReadSamples(size_t num_samples, int16_t* samples) {
102#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
103#error "Need to convert samples to big-endian when reading from WAV file"
104#endif
andrew@webrtc.org048c5022014-12-16 20:17:21 +0000105 // There could be metadata after the audio; ensure we don't read it.
pkasting25702cb2016-01-08 13:50:27 -0800106 num_samples = std::min(num_samples, num_samples_remaining_);
Niels Möller7ba3b812019-08-06 09:58:56 +0200107 const size_t num_bytes = num_samples * sizeof(*samples);
108 const size_t read_bytes = file_.Read(samples, num_bytes);
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000109 // If we didn't read what was requested, ensure we've reached the EOF.
Niels Möller7ba3b812019-08-06 09:58:56 +0200110 RTC_CHECK(read_bytes == num_bytes || file_.ReadEof());
111 RTC_CHECK_EQ(read_bytes % 2, 0)
112 << "End of file in the middle of a 16-bit sample";
113 const size_t read_samples = read_bytes / 2;
114 RTC_CHECK_LE(read_samples, num_samples_remaining_);
115 num_samples_remaining_ -= read_samples;
116 return read_samples;
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000117}
118
119size_t WavReader::ReadSamples(size_t num_samples, float* samples) {
120 static const size_t kChunksize = 4096 / sizeof(uint16_t);
121 size_t read = 0;
122 for (size_t i = 0; i < num_samples; i += kChunksize) {
123 int16_t isamples[kChunksize];
124 size_t chunk = std::min(kChunksize, num_samples - i);
125 chunk = ReadSamples(chunk, isamples);
126 for (size_t j = 0; j < chunk; ++j)
127 samples[i + j] = isamples[j];
128 read += chunk;
129 }
130 return read;
131}
132
133void WavReader::Close() {
Niels Möller7ba3b812019-08-06 09:58:56 +0200134 file_.Close();
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000135}
136
Artem Titove62f6002018-03-19 15:40:00 +0100137WavWriter::WavWriter(const std::string& filename,
138 int sample_rate,
Peter Kasting69558702016-01-12 16:26:35 -0800139 size_t num_channels)
Niels Möller7ba3b812019-08-06 09:58:56 +0200140 // Unlike plain fopen, OpenWriteOnly takes care of filename utf8 ->
Artem Titove62f6002018-03-19 15:40:00 +0100141 // wchar conversion on windows.
Niels Möller19a1d502019-06-13 14:08:24 +0200142 : WavWriter(FileWrapper::OpenWriteOnly(filename),
143 sample_rate,
144 num_channels) {}
Artem Titove62f6002018-03-19 15:40:00 +0100145
Niels Möller19a1d502019-06-13 14:08:24 +0200146WavWriter::WavWriter(FileWrapper file, int sample_rate, size_t num_channels)
147 : sample_rate_(sample_rate),
148 num_channels_(num_channels),
149 num_samples_(0),
150 file_(std::move(file)) {
Niels Möller7ba3b812019-08-06 09:58:56 +0200151 // Handle errors from the OpenWriteOnly call in above constructor.
Niels Möller19a1d502019-06-13 14:08:24 +0200152 RTC_CHECK(file_.is_open()) << "Invalid file. Could not create wav file.";
Artem Titove62f6002018-03-19 15:40:00 +0100153
henrikg91d6ede2015-09-17 00:24:34 -0700154 RTC_CHECK(CheckWavParameters(num_channels_, sample_rate_, kWavFormat,
155 kBytesPerSample, num_samples_));
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000156
157 // Write a blank placeholder header, since we need to know the total number
158 // of samples before we can fill in the real data.
159 static const uint8_t blank_header[kWavHeaderSize] = {0};
Niels Möller19a1d502019-06-13 14:08:24 +0200160 RTC_CHECK(file_.Write(blank_header, kWavHeaderSize));
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000161}
162
163WavWriter::~WavWriter() {
164 Close();
165}
166
peahd1f718b2016-02-22 02:13:28 -0800167int WavWriter::sample_rate() const {
168 return sample_rate_;
169}
170
171size_t WavWriter::num_channels() const {
172 return num_channels_;
173}
174
175size_t WavWriter::num_samples() const {
176 return num_samples_;
177}
178
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000179void WavWriter::WriteSamples(const int16_t* samples, size_t num_samples) {
180#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
181#error "Need to convert samples to little-endian when writing to WAV file"
182#endif
Niels Möller19a1d502019-06-13 14:08:24 +0200183 RTC_CHECK(file_.Write(samples, sizeof(*samples) * num_samples));
184 num_samples_ += num_samples;
185 RTC_CHECK(num_samples_ >= num_samples); // detect size_t overflow
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000186}
187
188void WavWriter::WriteSamples(const float* samples, size_t num_samples) {
189 static const size_t kChunksize = 4096 / sizeof(uint16_t);
190 for (size_t i = 0; i < num_samples; i += kChunksize) {
191 int16_t isamples[kChunksize];
192 const size_t chunk = std::min(kChunksize, num_samples - i);
193 FloatS16ToS16(samples + i, chunk, isamples);
194 WriteSamples(isamples, chunk);
195 }
196}
197
198void WavWriter::Close() {
Niels Möller19a1d502019-06-13 14:08:24 +0200199 RTC_CHECK(file_.Rewind());
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000200 uint8_t header[kWavHeaderSize];
andrew@webrtc.orgf866b2d2014-11-03 18:20:06 +0000201 WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat,
202 kBytesPerSample, num_samples_);
Niels Möller19a1d502019-06-13 14:08:24 +0200203 RTC_CHECK(file_.Write(header, kWavHeaderSize));
204 RTC_CHECK(file_.Close());
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000205}
206
207} // namespace webrtc