Wayverb
pressure.h
1 #pragma once
2 
3 #include <array>
4 #include <complex>
5 
6 // pressure - N / m^2
7 // force per unit area
8 //
9 // intensity - mean energy flow
10 // energy transported per second through a unit area
11 //
12 // If several pressure signals are present, their total level is the sum of the
13 // individual pressures.
14 //
15 // For cohrent, in-phase signals the pressure-time functions are added,
16 // doubling the pressure (a level change of 6dB)
17 //
18 // If they're in antiphase, the signals cancel (minus infinity level)
19 //
20 // If they're incorherent, you can sum intensities directly.
21 
22 namespace raytracer {
23 
24 // vorlander2007 eq 3.18
25 // pressure contribution from a source (or image source) depends on
26 // distance from image source, r
27 // angular wavenumber, k
28 // the product of reflection functions at incident angles along the path
29 // ...
30 //
31 // kuttruff2009 eq 9.23
32 // each surface has a reflection factor which is a complex function of
33 // frequency
34 // angle of incidence
35 // i.e. we can produce a complex spectrum for each surface that varies with
36 // angle of incidence
37 // *I think* we convolve the spectra together, adjust the phase depending on
38 // the total distance travelled from the image source, and adjust the level
39 // also depending on the distance
40 //
42 //
43 // OK here's what we'll do
44 // -----------------------
45 //
46 // * each surface will define multiband *absorption* coefficients
47 // * we can find multiband reflection factor magnitudes easily from these
48 // * then for each set of magnitudes, smooth to full spectra and
49 // accompanying compute minimum phase spectra
50 // * multiply together the reflection factors for multi-reflection paths
51 //
52 // alternatively
53 // -------------
54 //
55 // * each surface has a set of impedances (perhaps derived from absorptions)
56 // * trace in bands, keeping track of phase changes due to incident angles
57 // * extract real signal somehow????
58 
62 float cosine_interpolation(float y0, float y1, float x) {
63  const auto phase = (1 - std::cos(x * M_PI)) * 0.5;
64  return y0 * (1 - phase) + y1 * phase;
65 }
66 
67 // TODO add an abstraction point for image-source finding
68 // i.e. when a valid path is found, delegate the actual action to a
69 // callback
70 // TODO analytic signal / hilbert function
71 
72 template <size_t num_bands>
73 struct img_src_calculation final {
74  using real = float;
75  using cplx = std::complex<real>;
76 
77  template <typename T>
78  using bins = std::array<T, num_bands>;
79 
80  template <typename T, typename Func>
81  static constexpr auto map_bins(const bins<T>& x, Func func) {
82  using ret_t = decltype(func(x.front()));
83  bins<ret_t> ret;
84  using std::begin;
85  using std::end;
86  std::transform(
87  begin(x), end(x), begin(ret), [&](auto i) { return func(i); });
88  return ret;
89  }
90 
91  static auto absorption_to_reflection_factor(real x) {
92  return std::sqrt(1 - x);
93  }
94 
95  static auto absorption_to_reflection_factor(const bins<real>& x) {
96  return map_bins(
97  x, [](auto i) { return absorption_to_reflection_factor(i); });
98  }
99 
100  static real compute_phase(real frequency, real time) {
101  return 2 * M_PI * frequency * time;
102  }
103 
104  static auto compute_phase(const bins<real>& per_band_frequencies,
105  real time) {
106  return map_bins(per_band_frequencies,
107  [&](auto i) { return compute_phase(i, time); });
108  }
109 
110  static auto compute_unit_phase_spectrum(
111  const bins<real>& per_band_frequencies, real time) {
112  const auto phases = compute_phase(per_band_frequencies, time);
113  return map_bins(phases, [](auto i) { return std::exp(cplx{0, -i}); });
114  }
115 
116  template <size_t output_bins>
117  static std::array<real, output_bins> cosine_smoothing(
118  const bins<real>& per_band_amplitudes,
119  const bins<real>& per_band_frequencies,
120  real sample_rate) {
121  const auto extend = [](auto min, auto arr, auto max) {
122  std::array<real, num_bands + 2> extended;
123  extended.front() = min;
124  extended.back() = max;
125  std::copy(arr.begin(), arr.end(), extended.begin() + 1);
126  return extended;
127  };
128 
129  const auto extended_amplitudes = extend(0, per_band_amplitudes, 0);
130  const auto extended_frequencies =
131  extend(0, per_band_frequencies, sample_rate / 2);
132 
133  std::array<real, output_bins> ret;
134  auto this_band = 1u;
135  for (auto i = 0u; i != ret.size(); ++i) {
136  const auto bin_frequency = (i * sample_rate) / (2 * ret.size());
137  while (extended_frequencies[this_band] < bin_frequency) {
138  this_band += 1;
139  }
140 
141  const auto lower = extended_frequencies[this_band - 1];
142  const auto upper = extended_frequencies[this_band];
143  const auto x = (bin_frequency - lower) / (upper - lower);
144  ret[i] = cosine_interpolation(extended_amplitudes[this_band - 1],
145  extended_amplitudes[this_band],
146  x);
147  }
148 
149  return ret;
150  }
151 
152  template <typename It>
154  It begin,
155  It end,
156  real distance,
157  real speed_of_sound,
158  real amplitude_adjustment, // ambient density * source strength /
159  // 4 pi
160  const bins<real>& per_band_frequencies) {
161  // Find the phase spectrum at this distance.
162  const auto time = distance / speed_of_sound;
163 
164  auto spectrum = compute_unit_phase_spectrum(per_band_frequencies, time);
165 
166  // Correct spectrum for pressure loss over distance travelled.
167  const auto amplitude = amplitude_adjustment / distance;
168  for (auto& i : spectrum) {
169  i *= amplitude;
170  }
171 
172  // Now, for each reflection, compute its spectrum and convolve it with
173  // the current spectrum
174  for (; begin != end; ++begin) {
175  }
176 
177  return spectrum;
178  }
179 };
180 
181 } // namespace raytracer
Definition: pressure.h:22
static auto compute_pressure_spectrum(It begin, It end, real distance, real speed_of_sound, real amplitude_adjustment, const bins< real > &per_band_frequencies)
iterator over absorption coefficients
Definition: pressure.h:153
Definition: pressure.h:73