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_OBU_UTIL_H_
13 #define COMMON_UTILS_OBU_UTIL_H_
14
15 #include <cmath>
16 #include <cstdint>
17
18 #include "absl/functional/any_invocable.h"
19 #include "absl/log/check.h"
20 #include "absl/log/log.h"
21 #include "absl/status/status.h"
22 #include "absl/status/statusor.h"
23 #include "absl/strings/str_cat.h"
24 #include "absl/strings/string_view.h"
25 #include "iamf/common/utils/numeric_utils.h"
26 #include "iamf/obu/types.h"
27
28 namespace iamf_tools {
29
30 /*!\brief Gets the duration of a parameter subblock.
31 *
32 * The Spec defines a complex logic of getting the final subblock duration from
33 * multiple potential sources, including:
34 * - The constant subblock duration recorded in the parameter block.
35 * - The duration recorded in the parameter block's subblock at index i.
36 * - The constant subblock duration recorded in the parameter definition.
37 * - The subblock duration at index i recorded in the parameter definition.
38 *
39 * \param subblock_index Index of the subblock to get the duration of.
40 * \param num_subblocks Number of subblocks.
41 * \param constant_subblock_duration Constant subblock duration.
42 * \param subblock_duration_getter_from_parameter_block Getter function
43 * that returns the subblock duration recorded inside a parameter block,
44 * indexed at `subblock_index`.
45 * \param subblock_duration_getter_from_parameter_definition Getter function
46 * that returns the subblock duration recorded inside a parameter
47 * definition, indexed at `subblock_index`.
48 * \return Duration of the subblock or `absl::InvalidArgumentError()` on
49 * failure.
50 */
51 template <typename T>
GetParameterSubblockDuration(int subblock_index,T num_subblocks,T constant_subblock_duration,T total_duration,uint8_t param_definition_mode,absl::AnyInvocable<absl::StatusOr<T> (int)> subblock_duration_getter_from_parameter_block,absl::AnyInvocable<absl::StatusOr<T> (int)> subblock_duration_getter_from_parameter_definition)52 absl::StatusOr<T> GetParameterSubblockDuration(
53 int subblock_index, T num_subblocks, T constant_subblock_duration,
54 T total_duration, uint8_t param_definition_mode,
55 absl::AnyInvocable<absl::StatusOr<T>(int)>
56 subblock_duration_getter_from_parameter_block,
57 absl::AnyInvocable<absl::StatusOr<T>(int)>
58 subblock_duration_getter_from_parameter_definition) {
59 if (subblock_index > num_subblocks) {
60 return absl::InvalidArgumentError("subblock_index > num_subblocks");
61 }
62
63 if (constant_subblock_duration == 0) {
64 if (param_definition_mode == 1) {
65 // The durations are explicitly specified in the parameter block.
66 return subblock_duration_getter_from_parameter_block(subblock_index);
67 } else {
68 // The durations are explicitly specified in the parameter definition.
69 return subblock_duration_getter_from_parameter_definition(subblock_index);
70 }
71 }
72
73 // Otherwise the duration is implicit.
74 if (subblock_index == num_subblocks - 1 &&
75 num_subblocks * constant_subblock_duration > total_duration) {
76 // Sometimes the last subblock duration is shorter. The spec describes how
77 // to calculate the special case: "If NS x CSD > D, the actual duration of
78 // the last subblock SHALL be D - (NS - 1) x CSD."
79 return (total_duration - (num_subblocks - 1) * constant_subblock_duration);
80 } else {
81 // Otherwise the duration is based on `constant_subblock_duration`.
82 return constant_subblock_duration;
83 }
84 }
85
86 /*!\brief Interpolates a mix gain value in dB.
87 *
88 * The logic is used to partition parameter block protocol buffers as well as
89 * to query the gain value at a specific timestamp during mixing.
90 *
91 * \param animation_type Type of animation applied to the mix gain values.
92 * \param step_enum Enum value representing a step animation.
93 * \param linear_enum Enum value representing a linear animation.
94 * \param bezier_enum Enum value representing a Bezier animation.
95 * \param step_start_point_getter Getter function of the start point value
96 * of a step animation.
97 * \param linear_start_point_getter Getter function of the start point value
98 * of a linear animation.
99 * \param linear_end_point_getter Getter function of the end point value
100 * of a linear animation.
101 * \param bezier_start_point_getter Getter function of the start point value
102 * of a Bezier animation.
103 * \param bezier_end_point_getter Getter function of the end point value
104 * of a Bezier animation.
105 * \param bezier_control_point_getter Getter function of the middle control
106 * point value of a Bezier animation.
107 * \param bezier_control_point_relative_time_getter Getter function of the
108 * time of the middle control point of a Bezier animation.
109 * \param start_time Start time of the `MixGainParameterData`.
110 * \param end_time End time of the `MixGainParameterData`.
111 * \param target_time Target time to the get interpolated value of.
112 * \param target_mix_gain_db Output inteprolated mix gain value in dB.
113 * \return `absl::OkStatus()` on success. A specific status on failure.
114 */
115 template <typename AnimationEnumType>
InterpolateMixGainValue(AnimationEnumType animation_type,AnimationEnumType step_enum,AnimationEnumType linear_enum,AnimationEnumType bezier_enum,absl::AnyInvocable<int16_t ()> step_start_point_getter,absl::AnyInvocable<int16_t ()> linear_start_point_getter,absl::AnyInvocable<int16_t ()> linear_end_point_getter,absl::AnyInvocable<int16_t ()> bezier_start_point_getter,absl::AnyInvocable<int16_t ()> bezier_end_point_getter,absl::AnyInvocable<int16_t ()> bezier_control_point_getter,absl::AnyInvocable<int16_t ()> bezier_control_point_relative_time_getter,InternalTimestamp start_time,InternalTimestamp end_time,InternalTimestamp target_time,float & target_mix_gain_db)116 absl::Status InterpolateMixGainValue(
117 AnimationEnumType animation_type, AnimationEnumType step_enum,
118 AnimationEnumType linear_enum, AnimationEnumType bezier_enum,
119 absl::AnyInvocable<int16_t()> step_start_point_getter,
120 absl::AnyInvocable<int16_t()> linear_start_point_getter,
121 absl::AnyInvocable<int16_t()> linear_end_point_getter,
122 absl::AnyInvocable<int16_t()> bezier_start_point_getter,
123 absl::AnyInvocable<int16_t()> bezier_end_point_getter,
124 absl::AnyInvocable<int16_t()> bezier_control_point_getter,
125 absl::AnyInvocable<int16_t()> bezier_control_point_relative_time_getter,
126 InternalTimestamp start_time, InternalTimestamp end_time,
127 InternalTimestamp target_time, float& target_mix_gain_db) {
128 if (target_time < start_time || target_time > end_time ||
129 start_time > end_time) {
130 return absl::InvalidArgumentError(absl::StrCat(
131 "Cannot interpolate mix gain value with start time = ", start_time,
132 ", target_time = ", target_time, " and end_time = ", end_time));
133 }
134
135 // Shift times so start_time=0 to simplify calculations.
136 end_time -= start_time;
137 target_time -= start_time;
138 start_time = 0;
139
140 // TODO(b/283281856): Support resampling parameter blocks.
141 const int sample_rate_ratio = 1;
142 const int n_0 = start_time * sample_rate_ratio;
143 const int n = target_time * sample_rate_ratio;
144 const int n_2 = end_time * sample_rate_ratio;
145
146 if (animation_type == step_enum) {
147 // No interpolation is needed for step.
148 target_mix_gain_db = Q7_8ToFloat(step_start_point_getter());
149 } else if (animation_type == linear_enum) {
150 // Interpolate using the exact formula from the spec.
151 const float a = (float)n / (float)n_2;
152 const float p_0 = Q7_8ToFloat(linear_start_point_getter());
153 const float p_2 = Q7_8ToFloat(linear_end_point_getter());
154 target_mix_gain_db = (1 - a) * p_0 + a * p_2;
155 } else if (animation_type == bezier_enum) {
156 const float control_point_float =
157 Q0_8ToFloat(bezier_control_point_relative_time_getter());
158 // Using the definition of `round` in the IAMF spec.
159 const int n_1 = std::floor((end_time * control_point_float) + 0.5);
160
161 const float p_0 = Q7_8ToFloat(bezier_start_point_getter());
162 const float p_1 = Q7_8ToFloat(bezier_control_point_getter());
163 const float p_2 = Q7_8ToFloat(bezier_end_point_getter());
164
165 const float alpha = n_0 - 2 * n_1 + n_2;
166 const float beta = 2 * (n_1 - n_0);
167 const float gamma = n_0 - n;
168 const float a = alpha == 0
169 ? -gamma / beta
170 : (-beta + std::sqrt(beta * beta - 4 * alpha * gamma)) /
171 (2 * alpha);
172 target_mix_gain_db =
173 (1 - a) * (1 - a) * p_0 + 2 * (1 - a) * a * p_1 + a * a * p_2;
174 } else {
175 return absl::InvalidArgumentError(
176 absl::StrCat("Unknown animation_type = ", animation_type));
177 }
178
179 return absl::OkStatus();
180 }
181
182 } // namespace iamf_tools
183
184 #endif // COMMON_UTILS_OBU_UTIL_H_
185