blob: 008891f9cfbfc4e639c586c386612cadc0243b1a [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
13#include <algorithm>
14#include <cstdio>
15#include <limits>
16
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "common_audio/include/audio_util.h"
18#include "common_audio/wav_header.h"
19#include "rtc_base/checks.h"
Artem Titove62f6002018-03-19 15:40:00 +010020#include "rtc_base/logging.h"
Karl Wiberge40468b2017-11-22 10:42:26 +010021#include "rtc_base/numerics/safe_conversions.h"
Niels Möllera12c42a2018-07-25 16:05:48 +020022#include "rtc_base/system/arch.h"
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000023
24namespace webrtc {
25
26// We write 16-bit PCM WAV files.
27static const WavFormat kWavFormat = kWavFormatPcm;
pkasting25702cb2016-01-08 13:50:27 -080028static const size_t kBytesPerSample = 2;
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000029
andrew@webrtc.org048c5022014-12-16 20:17:21 +000030// Doesn't take ownership of the file handle and won't close it.
31class ReadableWavFile : public ReadableWav {
32 public:
33 explicit ReadableWavFile(FILE* file) : file_(file) {}
Mirko Bonadei91df0912018-07-17 11:08:15 +020034 size_t Read(void* buf, size_t num_bytes) override {
andrew@webrtc.org048c5022014-12-16 20:17:21 +000035 return fread(buf, 1, num_bytes, file_);
36 }
37
38 private:
39 FILE* file_;
40};
41
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000042WavReader::WavReader(const std::string& filename)
Artem Titove62f6002018-03-19 15:40:00 +010043 : WavReader(rtc::OpenPlatformFileReadOnly(filename)) {}
44
45WavReader::WavReader(rtc::PlatformFile file) {
46 RTC_CHECK_NE(file, rtc::kInvalidPlatformFileValue)
47 << "Invalid file. Could not create file handle for wav file.";
48 file_handle_ = rtc::FdopenPlatformFile(file, "rb");
49 if (!file_handle_) {
50 RTC_LOG(LS_ERROR) << "Could not open wav file for reading: " << errno;
51 // Even though we failed to open a FILE*, the file is still open
52 // and needs to be closed.
53 if (!rtc::ClosePlatformFile(file)) {
54 RTC_LOG(LS_ERROR) << "Can't close file.";
55 }
56 FATAL() << "Could not open wav file for reading.";
57 }
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000058
andrew@webrtc.org048c5022014-12-16 20:17:21 +000059 ReadableWavFile readable(file_handle_);
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000060 WavFormat format;
pkasting25702cb2016-01-08 13:50:27 -080061 size_t bytes_per_sample;
henrikg91d6ede2015-09-17 00:24:34 -070062 RTC_CHECK(ReadWavHeader(&readable, &num_channels_, &sample_rate_, &format,
63 &bytes_per_sample, &num_samples_));
andrew@webrtc.org048c5022014-12-16 20:17:21 +000064 num_samples_remaining_ = num_samples_;
henrikg91d6ede2015-09-17 00:24:34 -070065 RTC_CHECK_EQ(kWavFormat, format);
66 RTC_CHECK_EQ(kBytesPerSample, bytes_per_sample);
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000067}
68
69WavReader::~WavReader() {
70 Close();
71}
72
peahd1f718b2016-02-22 02:13:28 -080073int WavReader::sample_rate() const {
74 return sample_rate_;
75}
76
77size_t WavReader::num_channels() const {
78 return num_channels_;
79}
80
81size_t WavReader::num_samples() const {
82 return num_samples_;
83}
84
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000085size_t WavReader::ReadSamples(size_t num_samples, int16_t* samples) {
86#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
87#error "Need to convert samples to big-endian when reading from WAV file"
88#endif
andrew@webrtc.org048c5022014-12-16 20:17:21 +000089 // There could be metadata after the audio; ensure we don't read it.
pkasting25702cb2016-01-08 13:50:27 -080090 num_samples = std::min(num_samples, num_samples_remaining_);
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000091 const size_t read =
92 fread(samples, sizeof(*samples), num_samples, file_handle_);
93 // If we didn't read what was requested, ensure we've reached the EOF.
henrikg91d6ede2015-09-17 00:24:34 -070094 RTC_CHECK(read == num_samples || feof(file_handle_));
95 RTC_CHECK_LE(read, num_samples_remaining_);
pkasting25702cb2016-01-08 13:50:27 -080096 num_samples_remaining_ -= read;
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000097 return read;
98}
99
100size_t WavReader::ReadSamples(size_t num_samples, float* samples) {
101 static const size_t kChunksize = 4096 / sizeof(uint16_t);
102 size_t read = 0;
103 for (size_t i = 0; i < num_samples; i += kChunksize) {
104 int16_t isamples[kChunksize];
105 size_t chunk = std::min(kChunksize, num_samples - i);
106 chunk = ReadSamples(chunk, isamples);
107 for (size_t j = 0; j < chunk; ++j)
108 samples[i + j] = isamples[j];
109 read += chunk;
110 }
111 return read;
112}
113
114void WavReader::Close() {
henrikg91d6ede2015-09-17 00:24:34 -0700115 RTC_CHECK_EQ(0, fclose(file_handle_));
deadbeef922246a2017-02-26 04:18:12 -0800116 file_handle_ = nullptr;
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000117}
118
Artem Titove62f6002018-03-19 15:40:00 +0100119WavWriter::WavWriter(const std::string& filename,
120 int sample_rate,
Peter Kasting69558702016-01-12 16:26:35 -0800121 size_t num_channels)
Artem Titove62f6002018-03-19 15:40:00 +0100122 // Unlike plain fopen, CreatePlatformFile takes care of filename utf8 ->
123 // wchar conversion on windows.
124 : WavWriter(rtc::CreatePlatformFile(filename), sample_rate, num_channels) {}
125
126WavWriter::WavWriter(rtc::PlatformFile file,
127 int sample_rate,
128 size_t num_channels)
129 : sample_rate_(sample_rate), num_channels_(num_channels), num_samples_(0) {
130 // Handle errors from the CreatePlatformFile call in above constructor.
131 RTC_CHECK_NE(file, rtc::kInvalidPlatformFileValue)
132 << "Invalid file. Could not create wav file.";
133 file_handle_ = rtc::FdopenPlatformFile(file, "wb");
134 if (!file_handle_) {
135 RTC_LOG(LS_ERROR) << "Could not open wav file for writing.";
136 // Even though we failed to open a FILE*, the file is still open
137 // and needs to be closed.
138 if (!rtc::ClosePlatformFile(file)) {
139 RTC_LOG(LS_ERROR) << "Can't close file.";
140 }
141 FATAL() << "Could not open wav file for writing.";
142 }
143
henrikg91d6ede2015-09-17 00:24:34 -0700144 RTC_CHECK(CheckWavParameters(num_channels_, sample_rate_, kWavFormat,
145 kBytesPerSample, num_samples_));
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000146
147 // Write a blank placeholder header, since we need to know the total number
148 // of samples before we can fill in the real data.
149 static const uint8_t blank_header[kWavHeaderSize] = {0};
kwibergaf476c72016-11-28 15:21:39 -0800150 RTC_CHECK_EQ(1, fwrite(blank_header, kWavHeaderSize, 1, file_handle_));
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000151}
152
153WavWriter::~WavWriter() {
154 Close();
155}
156
peahd1f718b2016-02-22 02:13:28 -0800157int WavWriter::sample_rate() const {
158 return sample_rate_;
159}
160
161size_t WavWriter::num_channels() const {
162 return num_channels_;
163}
164
165size_t WavWriter::num_samples() const {
166 return num_samples_;
167}
168
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000169void WavWriter::WriteSamples(const int16_t* samples, size_t num_samples) {
170#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
171#error "Need to convert samples to little-endian when writing to WAV file"
172#endif
173 const size_t written =
174 fwrite(samples, sizeof(*samples), num_samples, file_handle_);
henrikg91d6ede2015-09-17 00:24:34 -0700175 RTC_CHECK_EQ(num_samples, written);
pkasting25702cb2016-01-08 13:50:27 -0800176 num_samples_ += written;
177 RTC_CHECK(num_samples_ >= written); // detect size_t overflow
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000178}
179
180void WavWriter::WriteSamples(const float* samples, size_t num_samples) {
181 static const size_t kChunksize = 4096 / sizeof(uint16_t);
182 for (size_t i = 0; i < num_samples; i += kChunksize) {
183 int16_t isamples[kChunksize];
184 const size_t chunk = std::min(kChunksize, num_samples - i);
185 FloatS16ToS16(samples + i, chunk, isamples);
186 WriteSamples(isamples, chunk);
187 }
188}
189
190void WavWriter::Close() {
henrikg91d6ede2015-09-17 00:24:34 -0700191 RTC_CHECK_EQ(0, fseek(file_handle_, 0, SEEK_SET));
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000192 uint8_t header[kWavHeaderSize];
andrew@webrtc.orgf866b2d2014-11-03 18:20:06 +0000193 WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat,
194 kBytesPerSample, num_samples_);
kwibergaf476c72016-11-28 15:21:39 -0800195 RTC_CHECK_EQ(1, fwrite(header, kWavHeaderSize, 1, file_handle_));
henrikg91d6ede2015-09-17 00:24:34 -0700196 RTC_CHECK_EQ(0, fclose(file_handle_));
deadbeef922246a2017-02-26 04:18:12 -0800197 file_handle_ = nullptr;
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000198}
199
200} // namespace webrtc
201
202rtc_WavWriter* rtc_WavOpen(const char* filename,
203 int sample_rate,
Peter Kasting69558702016-01-12 16:26:35 -0800204 size_t num_channels) {
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000205 return reinterpret_cast<rtc_WavWriter*>(
206 new webrtc::WavWriter(filename, sample_rate, num_channels));
207}
208
209void rtc_WavClose(rtc_WavWriter* wf) {
210 delete reinterpret_cast<webrtc::WavWriter*>(wf);
211}
212
213void rtc_WavWriteSamples(rtc_WavWriter* wf,
214 const float* samples,
215 size_t num_samples) {
216 reinterpret_cast<webrtc::WavWriter*>(wf)->WriteSamples(samples, num_samples);
217}
218
219int rtc_WavSampleRate(const rtc_WavWriter* wf) {
220 return reinterpret_cast<const webrtc::WavWriter*>(wf)->sample_rate();
221}
222
Peter Kasting69558702016-01-12 16:26:35 -0800223size_t rtc_WavNumChannels(const rtc_WavWriter* wf) {
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000224 return reinterpret_cast<const webrtc::WavWriter*>(wf)->num_channels();
225}
226
pkasting25702cb2016-01-08 13:50:27 -0800227size_t rtc_WavNumSamples(const rtc_WavWriter* wf) {
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000228 return reinterpret_cast<const webrtc::WavWriter*>(wf)->num_samples();
229}