17#ifndef RESONANCE_AUDIO_AMBISONICS_AMBISONIC_CODEC_IMPL_H_
18#define RESONANCE_AUDIO_AMBISONICS_AMBISONIC_CODEC_IMPL_H_
24#include "ambisonics/ambisonic_codec.h"
25#include "ambisonics/associated_legendre_polynomials_generator.h"
26#include "ambisonics/utils.h"
27#include "base/audio_buffer.h"
28#include "base/constants_and_types.h"
29#include "base/logging.h"
30#include "base/spherical_angle.h"
31#include "utils/pseudoinverse.h"
60 const std::vector<SphericalAngle>& angles);
65 int ambisonic_order()
const override;
66 size_t num_angles()
const override;
67 size_t num_spherical_harmonics()
const override;
68 const std::vector<SphericalAngle>& angles()
const override;
69 void set_angles(
const std::vector<SphericalAngle>& angles)
override;
88 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
93 float UnnormalizedSphericalHarmonic(
int degree,
int order,
98 const int ambisonic_order_;
102 std::vector<SphericalAngle> angles_;
107 bool encoder_matrix_invalid_;
112 bool decoder_matrix_invalid_;
116 std::vector<float> associated_legendre_polynomials_temp_;
119template <
int NumAngles,
int NumSphericalHarmonics>
121 int ambisonic_order,
const std::vector<SphericalAngle>& angles)
122 : ambisonic_order_(ambisonic_order),
123 alp_generator_(ambisonic_order, false, false) {
124 DCHECK_GE(ambisonic_order_, 0);
128template <
int NumAngles,
int NumSphericalHarmonics>
129void AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::Encode(
132 encoded_vector.noalias() = GetEncoderMatrix() * decoded_vector;
135template <
int NumAngles,
int NumSphericalHarmonics>
136void AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::Decode(
139 decoded_vector.noalias() = GetDecoderMatrix() * encoded_vector;
142template <
int NumAngles,
int NumSphericalHarmonics>
143void AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::EncodeBuffer(
144 const AudioBuffer& input, AudioBuffer* output) {
146 CHECK_EQ(input.num_channels(), num_angles());
147 CHECK_EQ(output->num_channels(), num_spherical_harmonics());
148 CHECK_EQ(input.num_frames(), output->num_frames());
153 unencoded_buffer(&input[0][0], num_angles(), output->GetChannelStride());
158 encoded_buffer(&(*output)[0][0], num_spherical_harmonics(),
159 input.GetChannelStride());
161 encoded_buffer.noalias() = GetEncoderMatrix() * unencoded_buffer;
164template <
int NumAngles,
int NumSphericalHarmonics>
165void AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::DecodeBuffer(
166 const AudioBuffer& input, AudioBuffer* output) {
168 CHECK_EQ(input.num_channels(), num_spherical_harmonics());
169 CHECK_EQ(output->num_channels(), num_angles());
170 CHECK_EQ(input.num_frames(), output->num_frames());
175 encoded_buffer(&input[0][0], num_spherical_harmonics(),
176 input.GetChannelStride());
181 decoded_buffer(&(*output)[0][0], num_angles(),
182 output->GetChannelStride());
184 decoded_buffer.noalias() = GetDecoderMatrix() * encoded_buffer;
187template <
int NumAngles,
int NumSphericalHarmonics>
189AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::GetEncoderMatrix() {
190 if (encoder_matrix_invalid_) {
191 encoder_matrix_ = EncoderMatrix(
192 GetNumPeriphonicComponents(ambisonic_order_), angles_.size());
193 for (
int col = 0; col < encoder_matrix_.cols(); col++) {
194 const SphericalAngle&
angle = angles_[col];
195 associated_legendre_polynomials_temp_ =
196 alp_generator_.Generate(std::sin(
angle.elevation()));
198 for (
int degree = 0; degree <= ambisonic_order_; degree++) {
199 for (
int order = -degree; order <= degree; order++) {
200 const int row = AcnSequence(degree, order);
205 encoder_matrix_(row, col) =
206 Sn3dNormalization(degree, order) *
207 UnnormalizedSphericalHarmonic(degree, order, angle);
211 encoder_matrix_invalid_ =
false;
213 return encoder_matrix_;
216template <
int NumAngles,
int NumSphericalHarmonics>
218AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::GetDecoderMatrix() {
219 if (decoder_matrix_invalid_) {
220 decoder_matrix_ = Pseudoinverse<Eigen::MatrixXf>(GetEncoderMatrix());
223 const float condition_number =
224 static_cast<float>(GetEncoderMatrix().norm() * decoder_matrix_.norm());
225 const float num_rows =
static_cast<float>(GetEncoderMatrix().rows());
226 const float num_cols =
static_cast<float>(GetEncoderMatrix().cols());
227 if (condition_number >
228 1.0f / (std::max(num_rows, num_cols) * kEpsilonFloat)) {
229 LOG(WARNING) <<
"Ambisonic decoding matrix is ill-conditioned. Results "
230 <<
"may be inaccurate.";
232 decoder_matrix_invalid_ =
false;
234 return decoder_matrix_;
237template <
int NumAngles,
int NumSphericalHarmonics>
238int AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::ambisonic_order()
240 return ambisonic_order_;
243template <
int NumAngles,
int NumSphericalHarmonics>
244size_t AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::num_angles()
246 return angles_.size();
249template <
int NumAngles,
int NumSphericalHarmonics>
250size_t AmbisonicCodecImpl<
251 NumAngles, NumSphericalHarmonics>::num_spherical_harmonics()
const {
254 return GetNumPeriphonicComponents(ambisonic_order_);
257template <
int NumAngles,
int NumSphericalHarmonics>
258const std::vector<SphericalAngle>&
259AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::angles()
const {
263template <
int NumAngles,
int NumSphericalHarmonics>
264void AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::set_angles(
265 const std::vector<SphericalAngle>& angles) {
266 CHECK_GT(angles.size(), 0);
269 encoder_matrix_invalid_ = decoder_matrix_invalid_ =
true;
272template <
int NumAngles,
int NumSphericalHarmonics>
273float AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::
274 UnnormalizedSphericalHarmonic(
int degree,
int order,
275 const SphericalAngle& angle)
const {
276 const float last_term =
277 (order >= 0) ? std::cos(
static_cast<float>(order) *
angle.azimuth())
278 : std::
sin(static_cast<float>(-order) *
angle.azimuth());
279 return associated_legendre_polynomials_temp_[alp_generator_.GetIndex(
280 degree, std::abs(order))] *
285template <
int NumSphericalHarmonics = Eigen::Dynamic>
286using MonoAmbisonicCodec = AmbisonicCodecImpl<1, NumSphericalHarmonics>;
289template <
int NumAngles>
290using FirstOrderPeriphonicAmbisonicCodec =
291 AmbisonicCodecImpl<NumAngles, GetNumPeriphonicComponentsStatic<1>::value>;
A matrix or vector expression mapping an existing array of data.
Definition Map.h:96
The matrix class, also used for vectors and row-vectors.
Definition Matrix.h:180
A matrix or vector expression mapping an existing expression.
Definition Ref.h:283
Definition ambisonic_codec_impl.h:44
Definition ambisonic_codec.h:28
Definition associated_legendre_polynomials_generator.h:26
Definition audio_buffer.h:78
Definition spherical_angle.h:25
GLM_FUNC_QUALIFIER vec< L, T, Q > sin(vec< L, T, Q > const &v)
Definition func_trigonometric.inl:41
@ Aligned
Definition Constants.h:240
@ RowMajor
Definition Constants.h:321
GLM_FUNC_DECL T angle(qua< T, Q > const &x)
Definition quaternion_trigonometric.inl:6
GLM_FUNC_DECL genType::row_type row(genType const &m, length_t index)
Definition matrix_access.inl:23
const int Dynamic
Definition Constants.h:22