| 1 | /* | 
| 2 | Copyright 2018 Google Inc. All Rights Reserved. | 
| 3 |  | 
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); | 
| 5 | you may not use this file except in compliance with the License. | 
| 6 | You may obtain a copy of the License at | 
| 7 |  | 
| 8 |     http://www.apache.org/licenses/LICENSE-2.0 | 
| 9 |  | 
| 10 | Unless required by applicable law or agreed to in writing, software | 
| 11 | distributed under the License is distributed on an "AS-IS" BASIS, | 
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
| 13 | See the License for the specific language governing permissions and | 
| 14 | limitations under the License. | 
| 15 | */ | 
| 16 |  | 
| 17 | #include "utils/wav_reader.h" | 
| 18 |  | 
| 19 | #include <algorithm> | 
| 20 | #include <string> | 
| 21 |  | 
| 22 | #include "base/integral_types.h" | 
| 23 | #include "base/logging.h" | 
| 24 |  | 
| 25 | namespace vraudio { | 
| 26 |  | 
| 27 | namespace { | 
| 28 |  | 
| 29 | struct  { | 
| 30 |   char [4]; | 
| 31 |   uint32 ; | 
| 32 | }; | 
| 33 |  | 
| 34 | struct WavFormat { | 
| 35 |   ChunkHeader ; | 
| 36 |   uint16 format_tag;                // Integer identifier of the format | 
| 37 |   uint16 num_channels;              // Number of audio channels | 
| 38 |   uint32 samples_rate;              // Audio sample rate | 
| 39 |   uint32 average_bytes_per_second;  // Bytes per second (possibly approximate) | 
| 40 |   uint16 block_align;      // Size in bytes of a sample block (all channels) | 
| 41 |   uint16 bits_per_sample;  // Size in bits of a single per-channel sample | 
| 42 | }; | 
| 43 | static_assert(sizeof(WavFormat) == 24, "Padding in WavFormat struct detected" ); | 
| 44 |  | 
| 45 | struct  { | 
| 46 |   struct { | 
| 47 |     ChunkHeader ; | 
| 48 |     char [4]; | 
| 49 |   } ; | 
| 50 |   WavFormat ; | 
| 51 |   struct { | 
| 52 |     ChunkHeader ; | 
| 53 |   } ; | 
| 54 | }; | 
| 55 |  | 
| 56 | // Size of WAV header. | 
| 57 | const size_t  = 44; | 
| 58 |  | 
| 59 | static_assert(sizeof(WavHeader) == kWavHeaderSize, | 
| 60 |               "Padding in WavHeader struct detected" ); | 
| 61 |  | 
| 62 | // Supported WAV encoding formats. | 
| 63 | static const uint16 kExtensibleWavFormat = 0xfffe; | 
| 64 | static const uint16 kPcmFormat = 0x1; | 
| 65 | }  // namespace | 
| 66 |  | 
| 67 | WavReader::WavReader(std::istream* binary_stream) | 
| 68 |     : binary_stream_(CHECK_NOTNULL(binary_stream)), | 
| 69 |       num_channels_(0), | 
| 70 |       sample_rate_hz_(-1), | 
| 71 |       num_total_samples_(0), | 
| 72 |       num_remaining_samples_(0), | 
| 73 |       pcm_offset_bytes_(0) { | 
| 74 |   init_ = ParseHeader(); | 
| 75 | } | 
| 76 |  | 
| 77 | size_t WavReader::ReadBinaryDataFromStream(void* target_ptr, size_t size) { | 
| 78 |   if (!binary_stream_->good()) { | 
| 79 |     return 0; | 
| 80 |   } | 
| 81 |   binary_stream_->read(s: static_cast<char*>(target_ptr), n: size); | 
| 82 |   return static_cast<size_t>(binary_stream_->gcount()); | 
| 83 | } | 
| 84 |  | 
| 85 | bool WavReader::() { | 
| 86 |   WavHeader ; | 
| 87 |   // Exclude data field to be able to optionally parse the two-byte extension | 
| 88 |   // field. | 
| 89 |   if (ReadBinaryDataFromStream(target_ptr: &header, size: kWavHeaderSize - sizeof(header.data)) != | 
| 90 |       kWavHeaderSize - sizeof(header.data)) | 
| 91 |     return false; | 
| 92 |   const uint32 format_size = header.format.header.size; | 
| 93 |   // Size of |WavFormat| without |ChunkHeader|. | 
| 94 |   static const uint32  = | 
| 95 |       sizeof(WavFormat) - sizeof(ChunkHeader); | 
| 96 |   if (format_size < kFormatSubChunkHeader) { | 
| 97 |     return false; | 
| 98 |   } | 
| 99 |   if (format_size != kFormatSubChunkHeader) { | 
| 100 |     // Parse optional extension fields. | 
| 101 |     int16 extension_size; | 
| 102 |     if (ReadBinaryDataFromStream(target_ptr: &extension_size, size: sizeof(extension_size)) != | 
| 103 |         sizeof(extension_size)) | 
| 104 |       return false; | 
| 105 |     int8 extension_data; | 
| 106 |     for (size_t i = 0; i < static_cast<size_t>(extension_size); ++i) { | 
| 107 |       if (ReadBinaryDataFromStream(target_ptr: &extension_data, size: sizeof(extension_data)) != | 
| 108 |           sizeof(extension_data)) | 
| 109 |         return false; | 
| 110 |     } | 
| 111 |   } | 
| 112 |   if (header.format.format_tag == kExtensibleWavFormat) { | 
| 113 |     // Skip extensible wav format "fact" header. | 
| 114 |     ChunkHeader ; | 
| 115 |     if (ReadBinaryDataFromStream(target_ptr: &fact_header, size: sizeof(fact_header)) != | 
| 116 |         sizeof(fact_header)) { | 
| 117 |       return false; | 
| 118 |     } | 
| 119 |     if (std::string(fact_header.id, 4) != "fact" ) { | 
| 120 |       return false; | 
| 121 |     } | 
| 122 |     int8 fact_data; | 
| 123 |     for (size_t i = 0; i < static_cast<size_t>(fact_header.size); ++i) { | 
| 124 |       if (ReadBinaryDataFromStream(target_ptr: &fact_data, size: sizeof(fact_data)) != | 
| 125 |           sizeof(fact_data)) | 
| 126 |         return false; | 
| 127 |     } | 
| 128 |   } | 
| 129 |  | 
| 130 |   // Read the "data" header. | 
| 131 |   if (ReadBinaryDataFromStream(target_ptr: &header.data, size: sizeof(header.data)) != | 
| 132 |       sizeof(header.data)) { | 
| 133 |     return false; | 
| 134 |   } | 
| 135 |  | 
| 136 |   num_channels_ = header.format.num_channels; | 
| 137 |   sample_rate_hz_ = header.format.samples_rate; | 
| 138 |  | 
| 139 |   bytes_per_sample_ = header.format.bits_per_sample / 8; | 
| 140 |   if (bytes_per_sample_ == 0 || bytes_per_sample_ != sizeof(int16)) { | 
| 141 |     return false; | 
| 142 |   } | 
| 143 |   const size_t bytes_in_payload = header.data.header.size; | 
| 144 |   num_total_samples_ = bytes_in_payload / bytes_per_sample_; | 
| 145 |   num_remaining_samples_ = num_total_samples_; | 
| 146 |  | 
| 147 |   if (header.format.num_channels == 0 || num_total_samples_ == 0 || | 
| 148 |       bytes_in_payload % bytes_per_sample_ != 0 || | 
| 149 |       (header.format.format_tag != kPcmFormat && | 
| 150 |        header.format.format_tag != kExtensibleWavFormat) || | 
| 151 |       (std::string(header.riff.header.id, 4) != "RIFF" ) || | 
| 152 |       (std::string(header.riff.format, 4) != "WAVE" ) || | 
| 153 |       (std::string(header.format.header.id, 4) != "fmt " ) || | 
| 154 |       (std::string(header.data.header.id, 4) != "data" )) { | 
| 155 |     return false; | 
| 156 |   } | 
| 157 |  | 
| 158 |   int64 current_position = binary_stream_->tellg(); | 
| 159 |   if (current_position < 0) { | 
| 160 |     return false; | 
| 161 |   } | 
| 162 |   pcm_offset_bytes_ = static_cast<uint64>(current_position); | 
| 163 |   return true; | 
| 164 | } | 
| 165 |  | 
| 166 | size_t WavReader::ReadSamples(size_t num_samples, int16* target_buffer) { | 
| 167 |   const size_t num_samples_to_read = | 
| 168 |       std::min(a: num_remaining_samples_, b: num_samples); | 
| 169 |   if (num_samples_to_read == 0) { | 
| 170 |     return 0; | 
| 171 |   } | 
| 172 |   const size_t num_bytes_read = | 
| 173 |       ReadBinaryDataFromStream(target_ptr: target_buffer, size: num_samples * sizeof(int16)); | 
| 174 |   const size_t num_samples_read = num_bytes_read / bytes_per_sample_; | 
| 175 |  | 
| 176 |   num_remaining_samples_ -= num_samples_read; | 
| 177 |   return num_samples_read; | 
| 178 | } | 
| 179 |  | 
| 180 | int64 WavReader::SeekToFrame(const uint64 frame_position) { | 
| 181 |   DCHECK_GT(num_channels_, 0U); | 
| 182 |   if (frame_position <= (num_total_samples_ / num_channels_)) { | 
| 183 |     const uint64 seek_position_byte = | 
| 184 |         pcm_offset_bytes_ + frame_position * num_channels_ * bytes_per_sample_; | 
| 185 |     binary_stream_->seekg(seek_position_byte, binary_stream_->beg); | 
| 186 |   } | 
| 187 |  | 
| 188 |   int64 binary_stream_position_byte = | 
| 189 |       static_cast<int64>(binary_stream_->tellg()); | 
| 190 |   if (binary_stream_position_byte < 0) { | 
| 191 |     // Return an error status if the actual bitstream position could not be | 
| 192 |     // queried. | 
| 193 |     return binary_stream_position_byte; | 
| 194 |   } | 
| 195 |  | 
| 196 |   if (static_cast<uint64>(binary_stream_position_byte) > pcm_offset_bytes_) { | 
| 197 |     DCHECK_GT(bytes_per_sample_, 0U); | 
| 198 |     return (static_cast<uint64>(binary_stream_position_byte) - | 
| 199 |             pcm_offset_bytes_) / | 
| 200 |            (bytes_per_sample_ * num_channels_); | 
| 201 |   } | 
| 202 |  | 
| 203 |   return 0; | 
| 204 | } | 
| 205 |  | 
| 206 | size_t WavReader::GetNumTotalSamples() const { return num_total_samples_; } | 
| 207 |  | 
| 208 | size_t WavReader::GetNumChannels() const { return num_channels_; } | 
| 209 |  | 
| 210 | int WavReader::GetSampleRateHz() const { return sample_rate_hz_; } | 
| 211 |  | 
| 212 | bool WavReader::() const { return init_; } | 
| 213 |  | 
| 214 | }  // namespace vraudio | 
| 215 |  |