1 /*
2 * Copyright (c) 2023, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 3-Clause Clear License
5 * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6 * License was not distributed with this source code in the LICENSE file, you
7 * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8 * Alliance for Open Media Patent License 1.0 was not distributed with this
9 * source code in the PATENTS file, you can obtain it at
10 * www.aomedia.org/license/patent.
11 */
12
13 #include "iamf/cli/wav_reader.h"
14
15 #include <cerrno>
16 #include <cstddef>
17 #include <cstdint>
18 #include <cstdio>
19 #include <cstring>
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 #include "absl/log/log.h"
25 #include "absl/status/status.h"
26 #include "absl/status/statusor.h"
27 #include "absl/strings/str_cat.h"
28 #include "src/dsp/read_wav_file.h"
29 #include "src/dsp/read_wav_info.h"
30
31 namespace iamf_tools {
32
33 namespace {
34 const int kAudioToTactileFailure = 0;
35 }
36
CreateFromFile(const std::string & wav_filename,const size_t num_samples_per_frame)37 absl::StatusOr<WavReader> WavReader::CreateFromFile(
38 const std::string& wav_filename, const size_t num_samples_per_frame) {
39 if (num_samples_per_frame == 0) {
40 return absl::InvalidArgumentError("num_samples_per_frame must be > 0");
41 }
42 LOG(INFO) << "Reading \"" << wav_filename << "\"";
43 FILE* file = std::fopen(wav_filename.c_str(), "rb");
44 if (file == nullptr) {
45 return absl::FailedPreconditionError(
46 absl::StrCat("Failed to open file: \"", wav_filename,
47 "\" with error: ", std::strerror(errno), "."));
48 }
49
50 ReadWavInfo info;
51 if (ReadWavHeader(file, &info) == kAudioToTactileFailure) {
52 return absl::FailedPreconditionError(
53 absl::StrCat("Failed to read header of file: \"", wav_filename,
54 "\". Maybe it is not a valid RIFF WAV."));
55 }
56
57 // Overwrite `info_.destination_alignment_bytes` to 4 to always store results
58 // in 4 bytes (32 bits), so we can handle 16-, 24-, and 32-bit PCMs.
59 info.destination_alignment_bytes = 4;
60
61 // Log the header info.
62 LOG(INFO) << "WAV header info:";
63 LOG(INFO) << " num_channels= " << info.num_channels;
64 LOG(INFO) << " sample_rate_hz= " << info.sample_rate_hz;
65 LOG(INFO) << " remaining_samples= " << info.remaining_samples;
66 LOG(INFO) << " bit_depth= " << info.bit_depth;
67 LOG(INFO) << " destination_alignment_bytes= "
68 << info.destination_alignment_bytes;
69 LOG(INFO) << " encoding= " << info.encoding;
70 LOG(INFO) << " sample_format= " << info.sample_format;
71
72 return WavReader(num_samples_per_frame, file, info);
73 }
74
WavReader(const size_t num_samples_per_frame,FILE * file,const ReadWavInfo & info)75 WavReader::WavReader(const size_t num_samples_per_frame, FILE* file,
76 const ReadWavInfo& info)
77 : buffers_(num_samples_per_frame,
78 std::vector<int32_t>(info.num_channels, 0)),
79 num_samples_per_frame_(num_samples_per_frame),
80 file_(file),
81 info_(info) {}
82
WavReader(WavReader && original)83 WavReader::WavReader(WavReader&& original)
84 : buffers_(std::move(original.buffers_)),
85 num_samples_per_frame_(original.num_samples_per_frame_),
86 file_(original.file_),
87 info_(original.info_) {
88 // Invalidate the file pointer on the original copy to prevent it from being
89 // closed on destruction.
90 original.file_ = nullptr;
91 }
92
~WavReader()93 WavReader::~WavReader() {
94 if (file_ != nullptr) {
95 std::fclose(file_);
96 }
97 }
98
ReadFrame()99 size_t WavReader::ReadFrame() {
100 size_t samples_read = 0;
101 for (int i = 0; i < buffers_.size(); i++) {
102 samples_read +=
103 ReadWavSamples(file_, &info_, buffers_[i].data(), buffers_[i].size());
104 }
105 return samples_read;
106 }
107
108 } // namespace iamf_tools
109