SAL
A C++ library for spatial audio.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
salutilities.h
Go to the documentation of this file.
1 /*
2  Spatial Audio Library (SAL)
3  Copyright (c) 2018, Enzo De Sena
4  All rights reserved.
5 
6  Authors: Enzo De Sena, enzodesena@gmail.com
7  */
8 
9 #ifndef SALUTILITIES_H
10 #define SALUTILITIES_H
11 
12 
13 #include <vector>
14 #include "saltypes.h"
15 #include "comparisonop.h"
16 #include "digitalfilter.h"
17 #include "iirfilter.h"
18 #include "vectorop.h"
19 #include "elementaryop.h"
20 #include "point.h"
21 #include <mutex>
22 #include <iostream>
23 
24 
25 namespace sal {
26 
27 enum class RotationDirection {
28  kClockwise,
29  kCounterclockwise
30 };
31 
32 template<class T>
33 std::vector<T> UniformAngles(const Int num_elements,
34  const T first_element_heading,
35  const RotationDirection direction = RotationDirection::kCounterclockwise) noexcept {
36  std::vector<T> angles(num_elements);
37  T separation = (T) 2.0*PI/((Angle) num_elements);
38  if (direction == RotationDirection::kClockwise) {
39  separation = -separation;
40  }
41 
42  for (Int i=0; i<num_elements; ++i) {
43  angles[i] = first_element_heading + separation * ((T) i);
44  }
45  return angles;
46 }
47 
48 
49 template<class T, class V>
50 std::vector<V> ConvertToType(std::vector<T> vector) {
51  std::vector<V> new_vector(vector.size());
52  for (mcl::Int i=0; i<(Int)vector.size(); ++i) {
53  new_vector[i] = (V) vector[i];
54  }
55  return new_vector;
56 }
57 
58 typedef mcl::Point Triplet;
59 
60 
63 public:
64  TripletHandler(const Triplet& initial_triplet) :
65  target_triplet_(initial_triplet),
66  current_triplet_(initial_triplet),
67  max_speed_(std::numeric_limits<Speed>::infinity()),
68  has_reached_target_(true) {
69  ASSERT(std::numeric_limits<Speed>::has_infinity);
70  ASSERT(std::numeric_limits<Speed>::infinity() ==
71  std::numeric_limits<Speed>::infinity());
72  }
73 
74  void SetMaxSpeed(const sal::Length& max_speed) {
75  max_speed_ = max_speed;
76  }
77 
79  void SetValue(const Triplet& triplet) noexcept {
80  target_triplet_ = triplet;
81  current_triplet_ = triplet;
82  has_reached_target_ = true;
83  }
84 
85  void SetTargetValue(const Triplet& target_triplet) noexcept {
86  target_triplet_ = target_triplet;
87  has_reached_target_ = false;
88  }
89 
90  Triplet target_value() const noexcept {
91  return target_triplet_;
92  }
93 
94  void Update(const Time time_elapsed_since_last_tick) {
95  if (max_speed_ == std::numeric_limits<Speed>::infinity()) {
96  current_triplet_ = target_triplet_;
97  has_reached_target_ = true;
98  } else {
99  // Detect if the point is moving faster than `max_speed`
100  sal::Length speed = Distance(target_triplet_, current_triplet_) /
101  time_elapsed_since_last_tick;
102 
103  if (speed <= max_speed_) {
104  current_triplet_ = target_triplet_;
105  has_reached_target_ = true;
106  } else {
107  current_triplet_ = PointOnLine(current_triplet_,
108  target_triplet_,
109  max_speed_*time_elapsed_since_last_tick);
110  }
111  }
112  }
113 
114  bool HasReachedTarget() const noexcept {
115  return has_reached_target_;
116  }
117 
118  mcl::Point value() const noexcept {
119  return current_triplet_;
120  }
121 
122  static bool Test();
123 
124 private:
126  Triplet target_triplet_;
127  Triplet current_triplet_;
128  sal::Length max_speed_;
129  bool has_reached_target_;
130 };
131 
132 
134 public:
137  RampSmoother(const Sample initial_value,
138  const Time sampling_frequency) noexcept :
139  current_value_(initial_value), target_value_(initial_value),
140  step_(0.0), countdown_(0), sampling_frequency_(sampling_frequency) {
141  ASSERT_WITH_MESSAGE(std::isgreaterequal(sampling_frequency, 0.0),
142  "Sampling frequency cannot be negative ");
143  }
144 
145  Sample GetNextValue() noexcept {
146  if (countdown_ <= 0) { return target_value_; }
147  --countdown_;
148  current_value_ += step_;
149  return current_value_;
150  }
151 
152  Sample GetNextValue(const Int num_jumps) noexcept {
153  // The +1 below is to make this identical to GetNextValue()
154  if ((countdown_-num_jumps+1) <= 0) {
155  countdown_ = 0;
156  return target_value_;
157  } else {
158  countdown_ -= num_jumps;
159  current_value_ += step_*((Sample)num_jumps);
160  return current_value_;
161  }
162  }
163 
171  void GetNextValuesMultiply(const Sample* input_data,
172  const Int num_samples,
173  Sample* output_data) noexcept {
174  if (IsUpdating()) {
175  for (Int i=0; i<num_samples; ++i) {
176  output_data[i] = input_data[i]*GetNextValue();
177  }
178  } else {
179  mcl::Multiply(input_data, num_samples, target_value_, output_data);
180  }
181  }
182 
190  void GetNextValuesMultiplyAdd(const Sample* input_data,
191  const Int num_samples,
192  Sample* input_output_data) noexcept {
193  if (IsUpdating()) {
194  for (Int i=0; i<num_samples; ++i) {
195  input_output_data[i] += input_data[i]*GetNextValue();
196  }
197  } else {
198  for (Int i=0; i<num_samples; ++i) {
199  input_output_data[i] += input_data[i]*target_value_;
200  }
201  }
202  }
203 
206  void PredictNextValuesAndMultiply(const Sample* input_data,
207  const Int num_samples,
208  Sample* output_data) const noexcept {
209  if (IsUpdating()) {
210  RampSmoother temp(*this); // Create a copy of itself that we will discard.
211  for (Int i=0; i<num_samples; ++i) {
212  output_data[i] = input_data[i]*temp.GetNextValue();
213  }
214  } else {
215  mcl::Multiply(input_data, num_samples, target_value_, output_data);
216  }
217  }
218 
219  Sample target_value() const noexcept { return target_value_; }
220 
221  void SetTargetValue(const Sample target_value, const Time ramp_time) noexcept {
222  ASSERT_WITH_MESSAGE(std::isgreaterequal(ramp_time, 0.0),
223  "Ramp time cannot be negative ");
224  if ((mcl::RoundToInt(ramp_time*sampling_frequency_)) == 0) {
225  target_value_ = target_value;
226  current_value_ = target_value;
227  countdown_ = 0;
228  return;
229  }
230 
231  if (std::islessgreater(target_value, target_value_)) {
232  const Int num_update_samples = mcl::RoundToInt(ramp_time*sampling_frequency_);
233  countdown_ = num_update_samples;
234  target_value_ = target_value;
235 
236  if (num_update_samples == 0) {
237  current_value_ = target_value;
238  } else {
239  step_ = (target_value_ - current_value_) /
240  ((Sample) num_update_samples);
241  }
242  }
243  }
244 
245  bool IsUpdating() const noexcept { return countdown_ > 0; }
246 
247 
248 private:
249  Sample current_value_;
250  Sample target_value_;
251  Sample step_;
252  Int countdown_;
253 
254  Time sampling_frequency_;
255 };
256 
257 
259 class LowPassSmoothingFilter : public mcl::DigitalFilter {
260 public:
264  LowPassSmoothingFilter(const mcl::Real ramp_samples) noexcept {
265  ASSERT_WITH_MESSAGE(std::isgreaterequal(ramp_samples, 0),
266  "Decay constant cannot be negative.");
267 
268  mcl::Real a1 = exp(-1.0/ramp_samples);
269  mcl::Real b0 = 1.0 - a1;
270  filter_ = mcl::IirFilter(mcl::BinaryVector<mcl::Real>(b0, 0.0),
271  mcl::BinaryVector<mcl::Real>(1.0, -a1));
272  }
273 
274  virtual mcl::Real Filter(const mcl::Real input) noexcept {
275  return filter_.Filter(input);
276  }
277 
278  using mcl::DigitalFilter::Filter;
279 
280  virtual void Reset() noexcept { filter_.Reset(); }
281 
282 private:
283  mcl::IirFilter filter_;
284 };
285 
286 
287 template <typename T>
288 std::string ToString(T input) {
289  std::ostringstream output;
290  output << input;
291  return output.str();
292 }
293 
294 } // namespace sal
295 
296 #endif /* salutilities_h */