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 <errno.h>
14 #include <algorithm>
15 #include <cstdio>
16 #include <type_traits>
17
18 #include "common_audio/include/audio_util.h"
19 #include "common_audio/wav_header.h"
20 #include "rtc_base/checks.h"
21 #include "rtc_base/logging.h"
22 #include "rtc_base/system/arch.h"
23
24 namespace webrtc {
25 namespace {
26
27 // We write 16-bit PCM WAV files.
28 constexpr WavFormat kWavFormat = kWavFormatPcm;
29 static_assert(std::is_trivially_destructible<WavFormat>::value, "");
30 constexpr size_t kBytesPerSample = 2;
31
32 // Doesn't take ownership of the file handle and won't close it.
33 class ReadableWavFile : public ReadableWav {
34 public:
ReadableWavFile(FILE * file)35 explicit ReadableWavFile(FILE* file) : file_(file) {}
36 ReadableWavFile(const ReadableWavFile&) = delete;
37 ReadableWavFile& operator=(const ReadableWavFile&) = delete;
Read(void * buf,size_t num_bytes)38 size_t Read(void* buf, size_t num_bytes) override {
39 return fread(buf, 1, num_bytes, file_);
40 }
Eof() const41 bool Eof() const override { return feof(file_) != 0; }
SeekForward(uint32_t num_bytes)42 bool SeekForward(uint32_t num_bytes) override {
43 return fseek(file_, num_bytes, SEEK_CUR) == 0;
44 }
45
46 private:
47 FILE* file_;
48 };
49
50 } // namespace
51
WavReader(const std::string & filename)52 WavReader::WavReader(const std::string& filename)
53 : WavReader(rtc::OpenPlatformFileReadOnly(filename)) {}
54
WavReader(rtc::PlatformFile file)55 WavReader::WavReader(rtc::PlatformFile file) {
56 RTC_CHECK_NE(file, rtc::kInvalidPlatformFileValue)
57 << "Invalid file. Could not create file handle for wav file.";
58 file_handle_ = rtc::FdopenPlatformFile(file, "rb");
59 if (!file_handle_) {
60 RTC_LOG(LS_ERROR) << "Could not open wav file for reading: " << errno;
61 // Even though we failed to open a FILE*, the file is still open
62 // and needs to be closed.
63 if (!rtc::ClosePlatformFile(file)) {
64 RTC_LOG(LS_ERROR) << "Can't close file.";
65 }
66 FATAL() << "Could not open wav file for reading.";
67 }
68
69 ReadableWavFile readable(file_handle_);
70 WavFormat format;
71 size_t bytes_per_sample;
72 RTC_CHECK(ReadWavHeader(&readable, &num_channels_, &sample_rate_, &format,
73 &bytes_per_sample, &num_samples_));
74 num_samples_remaining_ = num_samples_;
75 RTC_CHECK_EQ(kWavFormat, format);
76 RTC_CHECK_EQ(kBytesPerSample, bytes_per_sample);
77 }
78
~WavReader()79 WavReader::~WavReader() {
80 Close();
81 }
82
sample_rate() const83 int WavReader::sample_rate() const {
84 return sample_rate_;
85 }
86
num_channels() const87 size_t WavReader::num_channels() const {
88 return num_channels_;
89 }
90
num_samples() const91 size_t WavReader::num_samples() const {
92 return num_samples_;
93 }
94
ReadSamples(size_t num_samples,int16_t * samples)95 size_t WavReader::ReadSamples(size_t num_samples, int16_t* samples) {
96 // There could be metadata after the audio; ensure we don't read it.
97 num_samples = std::min(num_samples, num_samples_remaining_);
98 const size_t read =
99 fread(samples, sizeof(*samples), num_samples, file_handle_);
100 // If we didn't read what was requested, ensure we've reached the EOF.
101 RTC_CHECK(read == num_samples || feof(file_handle_));
102 RTC_CHECK_LE(read, num_samples_remaining_);
103 num_samples_remaining_ -= read;
104 #ifndef WEBRTC_ARCH_LITTLE_ENDIAN
105 //convert to big-endian
106 for(size_t idx = 0; idx < num_samples; idx++) {
107 samples[idx] = (samples[idx]<<8) | (samples[idx]>>8);
108 }
109 #endif
110 return read;
111 }
112
ReadSamples(size_t num_samples,float * samples)113 size_t WavReader::ReadSamples(size_t num_samples, float* samples) {
114 static const size_t kChunksize = 4096 / sizeof(uint16_t);
115 size_t read = 0;
116 for (size_t i = 0; i < num_samples; i += kChunksize) {
117 int16_t isamples[kChunksize];
118 size_t chunk = std::min(kChunksize, num_samples - i);
119 chunk = ReadSamples(chunk, isamples);
120 for (size_t j = 0; j < chunk; ++j)
121 samples[i + j] = isamples[j];
122 read += chunk;
123 }
124 return read;
125 }
126
Close()127 void WavReader::Close() {
128 RTC_CHECK_EQ(0, fclose(file_handle_));
129 file_handle_ = nullptr;
130 }
131
WavWriter(const std::string & filename,int sample_rate,size_t num_channels)132 WavWriter::WavWriter(const std::string& filename,
133 int sample_rate,
134 size_t num_channels)
135 // Unlike plain fopen, CreatePlatformFile takes care of filename utf8 ->
136 // wchar conversion on windows.
137 : WavWriter(rtc::CreatePlatformFile(filename), sample_rate, num_channels) {}
138
WavWriter(rtc::PlatformFile file,int sample_rate,size_t num_channels)139 WavWriter::WavWriter(rtc::PlatformFile file,
140 int sample_rate,
141 size_t num_channels)
142 : sample_rate_(sample_rate), num_channels_(num_channels), num_samples_(0) {
143 // Handle errors from the CreatePlatformFile call in above constructor.
144 RTC_CHECK_NE(file, rtc::kInvalidPlatformFileValue)
145 << "Invalid file. Could not create wav file.";
146 file_handle_ = rtc::FdopenPlatformFile(file, "wb");
147 if (!file_handle_) {
148 RTC_LOG(LS_ERROR) << "Could not open wav file for writing.";
149 // Even though we failed to open a FILE*, the file is still open
150 // and needs to be closed.
151 if (!rtc::ClosePlatformFile(file)) {
152 RTC_LOG(LS_ERROR) << "Can't close file.";
153 }
154 FATAL() << "Could not open wav file for writing.";
155 }
156
157 RTC_CHECK(CheckWavParameters(num_channels_, sample_rate_, kWavFormat,
158 kBytesPerSample, num_samples_));
159
160 // Write a blank placeholder header, since we need to know the total number
161 // of samples before we can fill in the real data.
162 static const uint8_t blank_header[kWavHeaderSize] = {0};
163 RTC_CHECK_EQ(1, fwrite(blank_header, kWavHeaderSize, 1, file_handle_));
164 }
165
~WavWriter()166 WavWriter::~WavWriter() {
167 Close();
168 }
169
sample_rate() const170 int WavWriter::sample_rate() const {
171 return sample_rate_;
172 }
173
num_channels() const174 size_t WavWriter::num_channels() const {
175 return num_channels_;
176 }
177
num_samples() const178 size_t WavWriter::num_samples() const {
179 return num_samples_;
180 }
181
WriteSamples(const int16_t * samples,size_t num_samples)182 void WavWriter::WriteSamples(const int16_t* samples, size_t num_samples) {
183 #ifndef WEBRTC_ARCH_LITTLE_ENDIAN
184 int16_t * le_samples = new int16_t[num_samples];
185 for(size_t idx = 0; idx < num_samples; idx++) {
186 le_samples[idx] = (samples[idx]<<8) | (samples[idx]>>8);
187 }
188 const size_t written =
189 fwrite(le_samples, sizeof(*le_samples), num_samples, file_handle_);
190 delete []le_samples;
191 #else
192 const size_t written =
193 fwrite(samples, sizeof(*samples), num_samples, file_handle_);
194 #endif
195 RTC_CHECK_EQ(num_samples, written);
196 num_samples_ += written;
197 RTC_CHECK(num_samples_ >= written); // detect size_t overflow
198 }
199
WriteSamples(const float * samples,size_t num_samples)200 void WavWriter::WriteSamples(const float* samples, size_t num_samples) {
201 static const size_t kChunksize = 4096 / sizeof(uint16_t);
202 for (size_t i = 0; i < num_samples; i += kChunksize) {
203 int16_t isamples[kChunksize];
204 const size_t chunk = std::min(kChunksize, num_samples - i);
205 FloatS16ToS16(samples + i, chunk, isamples);
206 WriteSamples(isamples, chunk);
207 }
208 }
209
Close()210 void WavWriter::Close() {
211 RTC_CHECK_EQ(0, fseek(file_handle_, 0, SEEK_SET));
212 uint8_t header[kWavHeaderSize];
213 WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat,
214 kBytesPerSample, num_samples_);
215 RTC_CHECK_EQ(1, fwrite(header, kWavHeaderSize, 1, file_handle_));
216 RTC_CHECK_EQ(0, fclose(file_handle_));
217 file_handle_ = nullptr;
218 }
219
220 } // namespace webrtc
221
rtc_WavOpen(const char * filename,int sample_rate,size_t num_channels)222 rtc_WavWriter* rtc_WavOpen(const char* filename,
223 int sample_rate,
224 size_t num_channels) {
225 return reinterpret_cast<rtc_WavWriter*>(
226 new webrtc::WavWriter(filename, sample_rate, num_channels));
227 }
228
rtc_WavClose(rtc_WavWriter * wf)229 void rtc_WavClose(rtc_WavWriter* wf) {
230 delete reinterpret_cast<webrtc::WavWriter*>(wf);
231 }
232
rtc_WavWriteSamples(rtc_WavWriter * wf,const float * samples,size_t num_samples)233 void rtc_WavWriteSamples(rtc_WavWriter* wf,
234 const float* samples,
235 size_t num_samples) {
236 reinterpret_cast<webrtc::WavWriter*>(wf)->WriteSamples(samples, num_samples);
237 }
238
rtc_WavSampleRate(const rtc_WavWriter * wf)239 int rtc_WavSampleRate(const rtc_WavWriter* wf) {
240 return reinterpret_cast<const webrtc::WavWriter*>(wf)->sample_rate();
241 }
242
rtc_WavNumChannels(const rtc_WavWriter * wf)243 size_t rtc_WavNumChannels(const rtc_WavWriter* wf) {
244 return reinterpret_cast<const webrtc::WavWriter*>(wf)->num_channels();
245 }
246
rtc_WavNumSamples(const rtc_WavWriter * wf)247 size_t rtc_WavNumSamples(const rtc_WavWriter* wf) {
248 return reinterpret_cast<const webrtc::WavWriter*>(wf)->num_samples();
249 }
250