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 "webrtc/common_audio/wav_file.h" 12 13 #include <algorithm> 14 #include <cstdio> 15 #include <limits> 16 #include <sstream> 17 18 #include "webrtc/base/checks.h" 19 #include "webrtc/base/safe_conversions.h" 20 #include "webrtc/common_audio/include/audio_util.h" 21 #include "webrtc/common_audio/wav_header.h" 22 23 namespace webrtc { 24 25 // We write 16-bit PCM WAV files. 26 static const WavFormat kWavFormat = kWavFormatPcm; 27 static const size_t kBytesPerSample = 2; 28 29 // Doesn't take ownership of the file handle and won't close it. 30 class ReadableWavFile : public ReadableWav { 31 public: 32 explicit ReadableWavFile(FILE* file) : file_(file) {} 33 virtual size_t Read(void* buf, size_t num_bytes) { 34 return fread(buf, 1, num_bytes, file_); 35 } 36 37 private: 38 FILE* file_; 39 }; 40 41 std::string WavFile::FormatAsString() const { 42 std::ostringstream s; 43 s << "Sample rate: " << sample_rate() << " Hz, Channels: " << num_channels() 44 << ", Duration: " 45 << (1.f * num_samples()) / (num_channels() * sample_rate()) << " s"; 46 return s.str(); 47 } 48 49 WavReader::WavReader(const std::string& filename) 50 : file_handle_(fopen(filename.c_str(), "rb")) { 51 RTC_CHECK(file_handle_) << "Could not open wav file for reading."; 52 53 ReadableWavFile readable(file_handle_); 54 WavFormat format; 55 size_t bytes_per_sample; 56 RTC_CHECK(ReadWavHeader(&readable, &num_channels_, &sample_rate_, &format, 57 &bytes_per_sample, &num_samples_)); 58 num_samples_remaining_ = num_samples_; 59 RTC_CHECK_EQ(kWavFormat, format); 60 RTC_CHECK_EQ(kBytesPerSample, bytes_per_sample); 61 } 62 63 WavReader::~WavReader() { 64 Close(); 65 } 66 67 size_t WavReader::ReadSamples(size_t num_samples, int16_t* samples) { 68 #ifndef WEBRTC_ARCH_LITTLE_ENDIAN 69 #error "Need to convert samples to big-endian when reading from WAV file" 70 #endif 71 // There could be metadata after the audio; ensure we don't read it. 72 num_samples = std::min(num_samples, num_samples_remaining_); 73 const size_t read = 74 fread(samples, sizeof(*samples), num_samples, file_handle_); 75 // If we didn't read what was requested, ensure we've reached the EOF. 76 RTC_CHECK(read == num_samples || feof(file_handle_)); 77 RTC_CHECK_LE(read, num_samples_remaining_); 78 num_samples_remaining_ -= read; 79 return read; 80 } 81 82 size_t WavReader::ReadSamples(size_t num_samples, float* samples) { 83 static const size_t kChunksize = 4096 / sizeof(uint16_t); 84 size_t read = 0; 85 for (size_t i = 0; i < num_samples; i += kChunksize) { 86 int16_t isamples[kChunksize]; 87 size_t chunk = std::min(kChunksize, num_samples - i); 88 chunk = ReadSamples(chunk, isamples); 89 for (size_t j = 0; j < chunk; ++j) 90 samples[i + j] = isamples[j]; 91 read += chunk; 92 } 93 return read; 94 } 95 96 void WavReader::Close() { 97 RTC_CHECK_EQ(0, fclose(file_handle_)); 98 file_handle_ = NULL; 99 } 100 101 WavWriter::WavWriter(const std::string& filename, int sample_rate, 102 size_t num_channels) 103 : sample_rate_(sample_rate), 104 num_channels_(num_channels), 105 num_samples_(0), 106 file_handle_(fopen(filename.c_str(), "wb")) { 107 RTC_CHECK(file_handle_) << "Could not open wav file for writing."; 108 RTC_CHECK(CheckWavParameters(num_channels_, sample_rate_, kWavFormat, 109 kBytesPerSample, num_samples_)); 110 111 // Write a blank placeholder header, since we need to know the total number 112 // of samples before we can fill in the real data. 113 static const uint8_t blank_header[kWavHeaderSize] = {0}; 114 RTC_CHECK_EQ(1u, fwrite(blank_header, kWavHeaderSize, 1, file_handle_)); 115 } 116 117 WavWriter::~WavWriter() { 118 Close(); 119 } 120 121 void WavWriter::WriteSamples(const int16_t* samples, size_t num_samples) { 122 #ifndef WEBRTC_ARCH_LITTLE_ENDIAN 123 #error "Need to convert samples to little-endian when writing to WAV file" 124 #endif 125 const size_t written = 126 fwrite(samples, sizeof(*samples), num_samples, file_handle_); 127 RTC_CHECK_EQ(num_samples, written); 128 num_samples_ += written; 129 RTC_CHECK(num_samples_ >= written); // detect size_t overflow 130 } 131 132 void WavWriter::WriteSamples(const float* samples, size_t num_samples) { 133 static const size_t kChunksize = 4096 / sizeof(uint16_t); 134 for (size_t i = 0; i < num_samples; i += kChunksize) { 135 int16_t isamples[kChunksize]; 136 const size_t chunk = std::min(kChunksize, num_samples - i); 137 FloatS16ToS16(samples + i, chunk, isamples); 138 WriteSamples(isamples, chunk); 139 } 140 } 141 142 void WavWriter::Close() { 143 RTC_CHECK_EQ(0, fseek(file_handle_, 0, SEEK_SET)); 144 uint8_t header[kWavHeaderSize]; 145 WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat, 146 kBytesPerSample, num_samples_); 147 RTC_CHECK_EQ(1u, fwrite(header, kWavHeaderSize, 1, file_handle_)); 148 RTC_CHECK_EQ(0, fclose(file_handle_)); 149 file_handle_ = NULL; 150 } 151 152 } // namespace webrtc 153 154 rtc_WavWriter* rtc_WavOpen(const char* filename, 155 int sample_rate, 156 size_t num_channels) { 157 return reinterpret_cast<rtc_WavWriter*>( 158 new webrtc::WavWriter(filename, sample_rate, num_channels)); 159 } 160 161 void rtc_WavClose(rtc_WavWriter* wf) { 162 delete reinterpret_cast<webrtc::WavWriter*>(wf); 163 } 164 165 void rtc_WavWriteSamples(rtc_WavWriter* wf, 166 const float* samples, 167 size_t num_samples) { 168 reinterpret_cast<webrtc::WavWriter*>(wf)->WriteSamples(samples, num_samples); 169 } 170 171 int rtc_WavSampleRate(const rtc_WavWriter* wf) { 172 return reinterpret_cast<const webrtc::WavWriter*>(wf)->sample_rate(); 173 } 174 175 size_t rtc_WavNumChannels(const rtc_WavWriter* wf) { 176 return reinterpret_cast<const webrtc::WavWriter*>(wf)->num_channels(); 177 } 178 179 size_t rtc_WavNumSamples(const rtc_WavWriter* wf) { 180 return reinterpret_cast<const webrtc::WavWriter*>(wf)->num_samples(); 181 } 182