1 /*
2 * Copyright (c) 2022, 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 #ifndef COMMON_UTILS_SAMPLE_PROCESSING_UTILS_H_
13 #define COMMON_UTILS_SAMPLE_PROCESSING_UTILS_H_
14
15 #include <algorithm>
16 #include <cstddef>
17 #include <cstdint>
18 #include <vector>
19
20 #include "absl/functional/any_invocable.h"
21 #include "absl/log/check.h"
22 #include "absl/log/log.h"
23 #include "absl/status/status.h"
24 #include "absl/strings/str_cat.h"
25 #include "absl/strings/string_view.h"
26 #include "absl/types/span.h"
27
28 namespace iamf_tools {
29
30 /*!\brief Writes the input PCM sample to a buffer.
31 *
32 * Writes the most significant `sample_size` bits of `sample` starting at
33 * `buffer[write_position]`. It is up to the user to ensure the buffer is valid.
34 *
35 * \param sample Sample to write the upper `sample_size` bits of.
36 * \param sample_size Sample size in bits. MUST be one of {8, 16, 24, 32}.
37 * \param big_endian `true` to write the sample as big endian. `false` to write
38 * it as little endian.
39 * \param buffer Start of the buffer to write to.
40 * \param write_position Offset of the buffer to write to. Incremented to one
41 * after the last byte written on success.
42 * \return `absl::OkStatus()` on success. `absl::InvalidArgumentError()` if
43 * `sample_size` is invalid.
44 */
45 absl::Status WritePcmSample(uint32_t sample, uint8_t sample_size,
46 bool big_endian, uint8_t* buffer,
47 size_t& write_position);
48
49 /*!\brief Arranges the input samples by time and channel.
50 *
51 * \param samples Interleaved samples to arrange.
52 * \param num_channels Number of channels.
53 * \param transform_samples Function to transform each sample to the output
54 * type.
55 * \param output Output vector to write the samples to. The size is not
56 * modified in this function even if the number of input samples do
57 * not fill the entire output vector. In that case, only the first
58 * `num_ticks` are filled.
59 * \param num_ticks Number of ticks (time samples) of the output vector that
60 * are filled in this function.
61 * \return `absl::OkStatus()` on success. `absl::InvalidArgumentError()` if the
62 * number of samples is not a multiple of the number of channels. An
63 * error propagated from `transform_samples` if it fails.
64 */
65 template <typename InputType, typename OutputType>
ConvertInterleavedToTimeChannel(absl::Span<const InputType> samples,size_t num_channels,const absl::AnyInvocable<absl::Status (InputType,OutputType &)const> & transform_samples,std::vector<std::vector<OutputType>> & output,size_t & num_ticks)66 absl::Status ConvertInterleavedToTimeChannel(
67 absl::Span<const InputType> samples, size_t num_channels,
68 const absl::AnyInvocable<absl::Status(InputType, OutputType&) const>&
69 transform_samples,
70 std::vector<std::vector<OutputType>>& output, size_t& num_ticks) {
71 if (samples.size() % num_channels != 0) [[unlikely]] {
72 return absl::InvalidArgumentError(absl::StrCat(
73 "Number of samples must be a multiple of the number of "
74 "channels. Found ",
75 samples.size(), " samples and ", num_channels, " channels."));
76 }
77
78 num_ticks = samples.size() / num_channels;
79 if (num_ticks > output.size()) [[unlikely]] {
80 return absl::InvalidArgumentError(absl::StrCat(
81 "Number of ticks does not fit into the output vector: (num_ticks= ",
82 num_ticks, " > output.size()= ", output.size(), ")"));
83 }
84
85 for (int t = 0; t < num_ticks; ++t) {
86 if (output[t].size() != num_channels) [[unlikely]] {
87 return absl::InvalidArgumentError(absl::StrCat(
88 "Number of channels is not equal to the output vector at tick ", t,
89 ": (", num_channels, " != ", output[t].size(), ")"));
90 }
91 for (int c = 0; c < num_channels; ++c) {
92 const auto status =
93 transform_samples(samples[t * num_channels + c], output[t][c]);
94 if (!status.ok()) [[unlikely]] {
95 return status;
96 }
97 }
98 }
99 return absl::OkStatus();
100 }
101
102 /*!\brief Interleaves the input samples.
103 *
104 * \param samples Samples in (time, channel) axes to arrange.
105 * \param transform_samples Function to transform each sample to the output
106 * type.
107 * \param output Output vector to write the interleaved samples to.
108 * \return `absl::OkStatus()` on success. `absl::InvalidArgumentError()` if the
109 * input has an inconsistent number of channels. An error propagated
110 * from `transform_samples` if it fails.
111 */
112 template <typename InputType, typename OutputType>
ConvertTimeChannelToInterleaved(absl::Span<const std::vector<InputType>> input,const absl::AnyInvocable<absl::Status (InputType,OutputType &)const> & transform_samples,std::vector<OutputType> & output)113 absl::Status ConvertTimeChannelToInterleaved(
114 absl::Span<const std::vector<InputType>> input,
115 const absl::AnyInvocable<absl::Status(InputType, OutputType&) const>&
116 transform_samples,
117 std::vector<OutputType>& output) {
118 const size_t num_channels = input.empty() ? 0 : input[0].size();
119 if (!std::all_of(input.begin(), input.end(), [&](const auto& tick) {
120 return tick.size() == num_channels;
121 })) {
122 return absl::InvalidArgumentError(
123 "All ticks must have the same number of channels.");
124 }
125
126 // TODO(b/382197581): avoid resizing inside this function.
127 output.clear();
128 output.reserve(input.size() * num_channels);
129 for (const auto& tick : input) {
130 for (const auto& sample : tick) {
131 OutputType transformed_sample;
132 auto status = transform_samples(sample, transformed_sample);
133 if (!status.ok()) {
134 return status;
135 }
136 output.emplace_back(transformed_sample);
137 }
138 }
139 return absl::OkStatus();
140 }
141
142 } // namespace iamf_tools
143
144 #endif // COMMON_UTILS_SAMPLE_PROCESSING_UTILS_H_
145