summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/resonance-audio/resonance_audio/utils/wav_reader.cc
blob: ca1078467657cb6accc5e23232f4a14c34529a01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/*
Copyright 2018 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS-IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "utils/wav_reader.h"

#include <algorithm>
#include <string>

#include "base/integral_types.h"
#include "base/logging.h"

namespace vraudio {

namespace {

struct ChunkHeader {
  char id[4];
  uint32 size;
};

struct WavFormat {
  ChunkHeader header;
  uint16 format_tag;                // Integer identifier of the format
  uint16 num_channels;              // Number of audio channels
  uint32 samples_rate;              // Audio sample rate
  uint32 average_bytes_per_second;  // Bytes per second (possibly approximate)
  uint16 block_align;      // Size in bytes of a sample block (all channels)
  uint16 bits_per_sample;  // Size in bits of a single per-channel sample
};
static_assert(sizeof(WavFormat) == 24, "Padding in WavFormat struct detected");

struct WavHeader {
  struct {
    ChunkHeader header;
    char format[4];
  } riff;
  WavFormat format;
  struct {
    ChunkHeader header;
  } data;
};

// Size of WAV header.
const size_t kWavHeaderSize = 44;

static_assert(sizeof(WavHeader) == kWavHeaderSize,
              "Padding in WavHeader struct detected");

// Supported WAV encoding formats.
static const uint16 kExtensibleWavFormat = 0xfffe;
static const uint16 kPcmFormat = 0x1;
}  // namespace

WavReader::WavReader(std::istream* binary_stream)
    : binary_stream_(CHECK_NOTNULL(binary_stream)),
      num_channels_(0),
      sample_rate_hz_(-1),
      num_total_samples_(0),
      num_remaining_samples_(0),
      pcm_offset_bytes_(0) {
  init_ = ParseHeader();
}

size_t WavReader::ReadBinaryDataFromStream(void* target_ptr, size_t size) {
  if (!binary_stream_->good()) {
    return 0;
  }
  binary_stream_->read(static_cast<char*>(target_ptr), size);
  return static_cast<size_t>(binary_stream_->gcount());
}

bool WavReader::ParseHeader() {
  WavHeader header;
  // Exclude data field to be able to optionally parse the two-byte extension
  // field.
  if (ReadBinaryDataFromStream(&header, kWavHeaderSize - sizeof(header.data)) !=
      kWavHeaderSize - sizeof(header.data))
    return false;
  const uint32 format_size = header.format.header.size;
  // Size of |WavFormat| without |ChunkHeader|.
  static const uint32 kFormatSubChunkHeader =
      sizeof(WavFormat) - sizeof(ChunkHeader);
  if (format_size < kFormatSubChunkHeader) {
    return false;
  }
  if (format_size != kFormatSubChunkHeader) {
    // Parse optional extension fields.
    int16 extension_size;
    if (ReadBinaryDataFromStream(&extension_size, sizeof(extension_size)) !=
        sizeof(extension_size))
      return false;
    int8 extension_data;
    for (size_t i = 0; i < static_cast<size_t>(extension_size); ++i) {
      if (ReadBinaryDataFromStream(&extension_data, sizeof(extension_data)) !=
          sizeof(extension_data))
        return false;
    }
  }
  if (header.format.format_tag == kExtensibleWavFormat) {
    // Skip extensible wav format "fact" header.
    ChunkHeader fact_header;
    if (ReadBinaryDataFromStream(&fact_header, sizeof(fact_header)) !=
        sizeof(fact_header)) {
      return false;
    }
    if (std::string(fact_header.id, 4) != "fact") {
      return false;
    }
    int8 fact_data;
    for (size_t i = 0; i < static_cast<size_t>(fact_header.size); ++i) {
      if (ReadBinaryDataFromStream(&fact_data, sizeof(fact_data)) !=
          sizeof(fact_data))
        return false;
    }
  }

  // Read the "data" header.
  if (ReadBinaryDataFromStream(&header.data, sizeof(header.data)) !=
      sizeof(header.data)) {
    return false;
  }

  num_channels_ = header.format.num_channels;
  sample_rate_hz_ = header.format.samples_rate;

  bytes_per_sample_ = header.format.bits_per_sample / 8;
  if (bytes_per_sample_ == 0 || bytes_per_sample_ != sizeof(int16)) {
    return false;
  }
  const size_t bytes_in_payload = header.data.header.size;
  num_total_samples_ = bytes_in_payload / bytes_per_sample_;
  num_remaining_samples_ = num_total_samples_;

  if (header.format.num_channels == 0 || num_total_samples_ == 0 ||
      bytes_in_payload % bytes_per_sample_ != 0 ||
      (header.format.format_tag != kPcmFormat &&
       header.format.format_tag != kExtensibleWavFormat) ||
      (std::string(header.riff.header.id, 4) != "RIFF") ||
      (std::string(header.riff.format, 4) != "WAVE") ||
      (std::string(header.format.header.id, 4) != "fmt ") ||
      (std::string(header.data.header.id, 4) != "data")) {
    return false;
  }

  int64 current_position = binary_stream_->tellg();
  if (current_position < 0) {
    return false;
  }
  pcm_offset_bytes_ = static_cast<uint64>(current_position);
  return true;
}

size_t WavReader::ReadSamples(size_t num_samples, int16* target_buffer) {
  const size_t num_samples_to_read =
      std::min(num_remaining_samples_, num_samples);
  if (num_samples_to_read == 0) {
    return 0;
  }
  const size_t num_bytes_read =
      ReadBinaryDataFromStream(target_buffer, num_samples * sizeof(int16));
  const size_t num_samples_read = num_bytes_read / bytes_per_sample_;

  num_remaining_samples_ -= num_samples_read;
  return num_samples_read;
}

int64 WavReader::SeekToFrame(const uint64 frame_position) {
  DCHECK_GT(num_channels_, 0U);
  if (frame_position <= (num_total_samples_ / num_channels_)) {
    const uint64 seek_position_byte =
        pcm_offset_bytes_ + frame_position * num_channels_ * bytes_per_sample_;
    binary_stream_->seekg(seek_position_byte, binary_stream_->beg);
  }

  int64 binary_stream_position_byte =
      static_cast<int64>(binary_stream_->tellg());
  if (binary_stream_position_byte < 0) {
    // Return an error status if the actual bitstream position could not be
    // queried.
    return binary_stream_position_byte;
  }

  if (static_cast<uint64>(binary_stream_position_byte) > pcm_offset_bytes_) {
    DCHECK_GT(bytes_per_sample_, 0U);
    return (static_cast<uint64>(binary_stream_position_byte) -
            pcm_offset_bytes_) /
           (bytes_per_sample_ * num_channels_);
  }

  return 0;
}

size_t WavReader::GetNumTotalSamples() const { return num_total_samples_; }

size_t WavReader::GetNumChannels() const { return num_channels_; }

int WavReader::GetSampleRateHz() const { return sample_rate_hz_; }

bool WavReader::IsHeaderValid() const { return init_; }

}  // namespace vraudio