libvisual  0.5.0
lv_fourier.cpp
1 /* Libvisual - The audio visualisation framework.
2  *
3  * Copyright (C) 2012 Libvisual team
4  * 2004-2006 Dennis Smit
5  *
6  * Authors: Chong Kai Xiong <kaixiong@codeleft.sg>
7  * Dennis Smit <ds@nerds-incorporated.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as
11  * published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  */
23 
24 #include "config.h"
25 #include "lv_fourier.h"
26 #include "lv_common.h"
27 #include "lv_math.h"
28 #include <cmath>
29 #include <vector>
30 #include <unordered_map>
31 
32 // Log scale settings
33 #define AMP_LOG_SCALE_THRESHOLD0 0.001f
34 #define AMP_LOG_SCALE_DIVISOR 6.908f // divisor = -log threshold
35 
36 namespace LV {
37 
38  namespace {
39 
40  enum DFTMethod
41  {
42  DFT_METHOD_BRUTE_FORCE,
43  DFT_METHOD_FFT
44  };
45 
46  class DFTCache
47  {
48  public:
49 
50  class Entry
51  {
52  public:
53 
54  std::vector<float> bitrevtable;
55  std::vector<float> sintable;
56  std::vector<float> costable;
57 
58  // FIXME: Eliminate this constructor
59  Entry () {}
60 
61  Entry (DFTMethod method, unsigned int sample_count);
62 
63  private:
64 
65  void fft_bitrev_table_init (unsigned int sample_count);
66  void fft_cossin_table_init (unsigned int sample_count);
67  void dft_cossin_table_init (unsigned int sample_count);
68  };
69 
70  Entry const& get_entry (DFTMethod method, unsigned int sample_count);
71 
72  private:
73 
74  typedef std::unordered_map<unsigned int, Entry> Table;
75 
76  Table m_cache;
77  };
78 
79  DFTCache dft_cache;
80 
81  } // anonymous namespace
82 
83 
84  class DFT::Impl
85  {
86  public:
87  unsigned int sample_count;
88  unsigned int spectrum_size;
89  unsigned int samples_out;
90  DFTMethod method;
91  std::vector<float> real;
92  std::vector<float> imag;
93 
94  Impl (unsigned int samples_out, unsigned int samples_in);
95 
96  void perform_brute_force (float const* input);
97  void perform_fft_radix2_dit (float const* input);
98 
99  DFTMethod best_method (unsigned int sample_count);
100  };
101 
102 
103  namespace {
104 
105  DFTCache::Entry const& DFTCache::get_entry (DFTMethod method, unsigned int sample_count)
106  {
107  auto entry = m_cache.find (sample_count);
108  if (entry != m_cache.end ())
109  return entry->second;
110 
111  // FIXME: Use emplace() when libstdc++ supports it
112  m_cache[sample_count] = Entry (method, sample_count);
113 
114  return m_cache[sample_count];
115  }
116 
117  DFTCache::Entry::Entry (DFTMethod method, unsigned int sample_count)
118  {
119  switch (method) {
120  case DFT_METHOD_BRUTE_FORCE:
121  dft_cossin_table_init (sample_count);
122  break;
123 
124  case DFT_METHOD_FFT:
125  fft_bitrev_table_init (sample_count);
126  fft_cossin_table_init (sample_count);
127  break;
128  }
129  }
130 
131  void DFTCache::Entry::fft_bitrev_table_init (unsigned int sample_count)
132  {
133  bitrevtable.clear ();
134  bitrevtable.reserve (sample_count);
135 
136  for (unsigned int i = 0; i < sample_count; i++)
137  bitrevtable.push_back (i);
138 
139  unsigned int j = 0;
140 
141  for (unsigned int i = 0; i < sample_count; i++) {
142  if (j > i) {
143  std::swap (bitrevtable[i], bitrevtable[j]);
144  }
145 
146  unsigned int m = sample_count >> 1;
147 
148  while (m >= 1 && j >= m) {
149  j -= m;
150  m >>= 1;
151  }
152 
153  j += m;
154  }
155  }
156 
157  void DFTCache::Entry::fft_cossin_table_init (unsigned int sample_count)
158  {
159  unsigned int dft_size = 2;
160  unsigned int tab_size = 0;
161 
162  while (dft_size <= sample_count) {
163  tab_size++;
164  dft_size <<= 1;
165  }
166 
167  sintable.clear ();
168  sintable.reserve (tab_size);
169 
170  costable.clear ();
171  costable.reserve (tab_size);
172 
173  dft_size = 2;
174 
175  while (dft_size <= sample_count) {
176  float theta = -2.0f * VISUAL_MATH_PI / dft_size;
177 
178  costable.push_back (std::cos (theta));
179  sintable.push_back (std::sin (theta));
180 
181  dft_size <<= 1;
182  }
183  }
184 
185  void DFTCache::Entry::dft_cossin_table_init (unsigned int sample_count)
186  {
187  sintable.clear ();
188  sintable.reserve (sample_count);
189 
190  costable.clear ();
191  costable.reserve (sample_count);
192 
193  for (unsigned int i = 0; i < sample_count; i++) {
194  float theta = (-2.0f * VISUAL_MATH_PI * i) / sample_count;
195 
196  costable.push_back (std::cos (theta));
197  sintable.push_back (std::sin (theta));
198  }
199  }
200 
201  } // anonymous namespace
202 
203  DFT::DFT (unsigned int samples_out, unsigned int samples_in)
204  : m_impl (new Impl (samples_out, samples_in))
205  {
206  // empty
207  }
208 
209  DFT::DFT (DFT&& rhs)
210  : m_impl {std::move (rhs.m_impl)}
211  {
212  // nothing
213  }
214 
216  {
217  // empty
218  }
219 
220  DFT& DFT::operator= (DFT&& rhs)
221  {
222  m_impl.swap (rhs.m_impl);
223  return *this;
224  }
225 
226  void DFT::perform (float *output, float const* input)
227  {
228  visual_return_if_fail (output != nullptr);
229  visual_return_if_fail (input != nullptr);
230 
231  switch (m_impl->method) {
232  case DFT_METHOD_BRUTE_FORCE:
233  m_impl->perform_brute_force (input);
234  break;
235 
236  case DFT_METHOD_FFT:
237  m_impl->perform_fft_radix2_dit (input);
238  break;
239  }
240 
241  visual_math_simd_complex_scaled_norm (output, m_impl->real.data (), m_impl->imag.data (),
242  1.0 / m_impl->sample_count, m_impl->samples_out);
243  }
244 
245  void DFT::log_scale (float *output, float const* input, unsigned int size)
246  {
247  visual_return_if_fail (output != nullptr);
248  visual_return_if_fail (input != nullptr);
249 
250  return log_scale_standard (output, input, size);
251  }
252 
253  void DFT::log_scale_standard (float *output, float const* input, unsigned int size)
254  {
255  visual_return_if_fail (output != nullptr);
256  visual_return_if_fail (input != nullptr);
257 
258  return log_scale_custom (output, input, size, AMP_LOG_SCALE_DIVISOR);
259  }
260 
261  void DFT::log_scale_custom (float* output, float const* input, unsigned int size, float log_scale_divisor)
262  {
263  visual_return_if_fail (output != nullptr);
264  visual_return_if_fail (input != nullptr);
265 
266  for (unsigned int i = 0; i < size; i++) {
267  if (input[i] > AMP_LOG_SCALE_THRESHOLD0)
268  output[i] = 1.0f + log (input[i]) / log_scale_divisor;
269  else
270  output[i] = 0.0f;
271  }
272  }
273 
274  DFT::Impl::Impl (unsigned int samples_out_, unsigned int samples_in_)
275  : sample_count (samples_in_),
276  spectrum_size (sample_count/2 + 1),
277  samples_out (std::min (samples_out_, spectrum_size)),
278  method (best_method (sample_count)),
279  real (sample_count),
280  imag (sample_count)
281  {
282  // empty
283  }
284 
285  DFTMethod DFT::Impl::best_method (unsigned int sample_count)
286  {
287  if (visual_math_is_power_of_2 (sample_count))
288  return DFT_METHOD_FFT;
289  else
290  return DFT_METHOD_BRUTE_FORCE;
291  }
292 
293  void DFT::Impl::perform_brute_force (float const* input)
294  {
295  DFTCache::Entry const& fcache = dft_cache.get_entry (method, sample_count);
296 
297  for (unsigned int i = 0; i < spectrum_size; i++) {
298  float xr = 0.0f;
299  float xi = 0.0f;
300 
301  float wr = 1.0f;
302  float wi = 0.0f;
303 
304  for (unsigned int j = 0; j < sample_count; j++) {
305  xr += input[j] * wr;
306  xi += input[j] * wi;
307 
308  float wtemp = wr;
309 
310  wr = wr * fcache.costable[i] - wi * fcache.sintable[i];
311  wi = wtemp * fcache.sintable[i] + wi * fcache.costable[i];
312  }
313 
314  real[i] = xr;
315  imag[i] = xi;
316  }
317  }
318 
319  void DFT::Impl::perform_fft_radix2_dit (float const* input)
320  {
321  DFTCache::Entry const& fcache = dft_cache.get_entry (method, sample_count);
322 
323  for (unsigned int i = 0; i < sample_count; i++) {
324  unsigned int idx = fcache.bitrevtable[i];
325 
326  if (idx < sample_count)
327  real[i] = input[idx];
328  else
329  real[i] = 0;
330  }
331 
332  unsigned int dft_size = 2;
333  unsigned int t = 0;
334 
335  while (dft_size <= sample_count) {
336  float wpr = fcache.costable[t];
337  float wpi = fcache.sintable[t];
338 
339  float wr = 1.0f;
340  float wi = 0.0f;
341 
342  unsigned int half_dft_size = dft_size >> 1;
343 
344  for (unsigned int m = 0; m < half_dft_size; m++) {
345  for (unsigned int i = m; i < sample_count; i += dft_size) {
346  unsigned int j = i + half_dft_size;
347 
348  float tempr = wr * real[j] - wi * imag[j];
349  float tempi = wr * imag[j] + wi * real[j];
350 
351  real[j] = real[i] - tempr;
352  imag[j] = imag[i] - tempi;
353 
354  real[i] += tempr;
355  imag[i] += tempi;
356  }
357 
358  float wtemp;
359 
360  wr = (wtemp = wr) * wpr - wi * wpi;
361  wi = wi * wpr + wtemp * wpi;
362  }
363 
364  dft_size <<= 1;
365  t++;
366  }
367  }
368 
369 } // LV namespace