sequencer
fft.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <sequencer/assert.hpp>
6 
7 #include <array>
8 #include <cassert>
9 #include <cmath>
10 #include <complex>
11 #include <cstring>
12 #include <vector>
13 
14 namespace sequencer::audio
15 {
16  template < class T >
17  constexpr std::complex< T > imag = std::complex< T >{0, 1};
18 
19  template < class T >
20  auto create_table()
21  {
22  std::array< std::vector< std::complex< T > >, 16 > table;
23 
24  auto size = 2u;
25  for ( decltype( table.size() ) size_exp = 0; size_exp < table.size(); ++size_exp )
26  {
27  table[ size_exp ].resize( size );
28  for ( decltype( table[ size_exp ].size() ) k = 0; k < table[ size_exp ].size(); ++k )
29  {
30  table[ size_exp ][ k ] =
31  std::exp( minus_two_pi< T > * imag< T > * ( T( k ) / size ) );
32  }
33  size *= 2;
34  }
35 
36  return table;
37  }
38 
39  template < class T >
40  auto& gauss_table()
41  {
42  static auto table = create_table< T >();
43  return table;
44  }
45 
46  template < class T, class Size >
47  std::complex< T > compute_gauss( Size k, Size size )
48  {
49  switch ( size )
50  {
51  case 2:
52  return gauss_table< T >()[ 0 ][ k ];
53  case 4:
54  return gauss_table< T >()[ 1 ][ k ];
55  case 8:
56  return gauss_table< T >()[ 2 ][ k ];
57  case 16:
58  return gauss_table< T >()[ 3 ][ k ];
59  case 32:
60  return gauss_table< T >()[ 4 ][ k ];
61  case 64:
62  return gauss_table< T >()[ 5 ][ k ];
63  case 128:
64  return gauss_table< T >()[ 6 ][ k ];
65  case 256:
66  return gauss_table< T >()[ 7 ][ k ];
67  case 512:
68  return gauss_table< T >()[ 8 ][ k ];
69  case 1024:
70  return gauss_table< T >()[ 9 ][ k ];
71  case 2048:
72  return gauss_table< T >()[ 10 ][ k ];
73  case 4096:
74  return gauss_table< T >()[ 11 ][ k ];
75  case 8192:
76  return gauss_table< T >()[ 12 ][ k ];
77  case 16384:
78  return gauss_table< T >()[ 13 ][ k ];
79  case 32768:
80  return gauss_table< T >()[ 14 ][ k ];
81  case 65536:
82  return gauss_table< T >()[ 15 ][ k ];
83  }
84 
85  return std::exp( minus_two_pi< T > * imag< T > * ( T( k ) / size ) );
86  }
87 
88  template < class Container >
89  auto radix2( const Container& x )
90  {
91  if ( x.size() == 1 )
92  {
93  return std::vector{x[ 0 ]};
94  }
95 
96  auto y = radix2( const_vector_view{&x, 0, 2} );
97  const auto z = radix2( const_vector_view{&x, 1, 2} );
98 
99  y.resize( x.size() );
100  for ( typename Container::size_type k = 0; k < x.size() / 2; ++k )
101  {
102  using value_type = typename Container::value_type::value_type;
103  const auto dy = compute_gauss< value_type >( k, x.size() ) * z[ k ];
104  y[ x.size() / 2 + k ] = y[ k ] - dy;
105  y[ k ] += dy;
106  }
107 
108  return y;
109  }
110 
111  template < class Container, class Result >
112  void radix2( const Container& x, Result& result )
113  {
114  if ( x.size() == 1 )
115  {
116  result[ 0 ] = x[ 0 ];
117  return;
118  }
119 
120  auto even_result_view = vector_view{&result, 0};
121  auto odd_result_view = vector_view{&result, result.size() / 2};
122  radix2( const_vector_view{&x, 0, 2}, even_result_view );
123  radix2( const_vector_view{&x, 1, 2}, odd_result_view );
124 
125  for ( typename Container::size_type k = 0; k < x.size() / 2; ++k )
126  {
127  using value_type = typename Container::value_type::value_type;
128  const auto dy =
129  compute_gauss< value_type >( k, x.size() ) * result[ result.size() / 2 + k ];
130  result[ x.size() / 2 + k ] = result[ k ] - dy;
131  result[ k ] += dy;
132  }
133  }
134 
135  template < class Container >
136  auto inverse_radix2( Container x )
137  {
138  if ( x.size() == 1 )
139  {
140  return std::vector{x[ 0 ]};
141  }
142 
143  for ( auto& v : x )
144  {
145  v = conj( v );
146  }
147 
148  auto y = radix2( const_vector_view{&x, 0, 2} );
149  const auto z = radix2( const_vector_view{&x, 1, 2} );
150 
151  y.resize( x.size() );
152  using value_type = typename Container::value_type::value_type;
153  const auto scale = value_type( 1.0 ) / y.size();
154  for ( typename Container::size_type k = 0; k < x.size() / 2; ++k )
155  {
156  using value_type = typename Container::value_type::value_type;
157  const auto dy = compute_gauss< value_type >( k, x.size() ) * z[ k ];
158  y[ x.size() / 2 + k ] = scale * conj( y[ k ] - dy );
159  y[ k ] += dy;
160  y[ k ] = scale * conj( y[ k ] );
161  }
162 
163  return y;
164  }
165 
166  template < class T, std::enable_if_t< !is_complex_v< T > >* = nullptr >
167  std::vector< std::complex< T > > fft( const std::vector< T >& x )
168  {
169  using size_type = typename std::vector< std::complex< T > >::size_type;
170 
171  SEQUENCER_ASSERT( x.size() % 2 == 0 )
172  const auto half_result =
173  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
174  radix2( reinterpret_cast< const std::vector< std::complex< T > >& >( x ) );
175 
176  std::vector< std::complex< T > > result( half_result.size() );
177  for ( size_type k = 0; k < result.size(); ++k )
178  {
179  const auto s_k = ( k == 0 ) ? 0 : result.size() - k;
180  const auto z = conj( half_result[ s_k ] );
181  result[ k ] =
182  T( 0.5 ) * ( ( half_result[ k ] + z ) - imag< T > * ( half_result[ k ] - z ) *
183  compute_gauss< T >( k, x.size() ) );
184  }
185 
186  return result;
187  }
188 
189  template < class T, std::enable_if_t< !is_complex_v< T > >* = nullptr >
190  std::pair< std::vector< std::complex< T > >, std::vector< std::complex< T > > >
191  fft( const std::vector< T >& x, const std::vector< T >& y )
192  {
193  using size_type = typename std::vector< std::complex< T > >::size_type;
194 
195  SEQUENCER_ASSERT( x.size() % 2 == 0 )
196  SEQUENCER_ASSERT( x.size() == y.size() )
197  std::vector< std::complex< T > > z( x.size() );
198  for ( size_type i = 0; i < x.size(); ++i )
199  {
200  z[ i ] = std::complex{x[ i ], y[ i ]};
201  }
202  const auto result = radix2( z );
203  std::vector< std::complex< T > > x_result( result.size() / 2 );
204  std::vector< std::complex< T > > y_result( result.size() / 2 );
205  for ( size_type k = 0; k < x_result.size(); ++k )
206  {
207  auto z = conj( result[ x.size() - k ] );
208  x_result[ k ] = ( result[ k ] + z ) / 2.f;
209  y_result[ k ] = -imag< T > * ( result[ k ] - z ) / 2.f;
210  }
211 
212  return std::make_pair( x_result, y_result );
213  }
214 
215  template < class T, std::enable_if_t< !is_complex_v< T > >* = nullptr >
216  void fft( const std::vector< T >& x, std::vector< std::complex< T > >& result )
217  {
218  using size_type = typename std::vector< std::complex< T > >::size_type;
219 
220  SEQUENCER_ASSERT( x.size() % 2 == 0 )
221  std::vector< std::complex< T > > half_result( x.size() / 2 );
222  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
223  radix2( reinterpret_cast< const std::vector< std::complex< T > >& >( x ), half_result );
224 
225  for ( size_type k = 0; k < result.size(); ++k )
226  {
227  const auto s_k = ( k == 0 ) ? 0 : result.size() - k;
228  const auto z = conj( half_result[ s_k ] );
229  result[ k ] =
230  T( 0.5 ) * ( ( half_result[ k ] + z ) - imag< T > * ( half_result[ k ] - z ) *
231  compute_gauss< T >( k, x.size() ) );
232  }
233  }
234 
235  template < class T, std::enable_if_t< !is_complex_v< T > >* = nullptr >
236  std::vector< T > fft_abs_scaled( const std::vector< T >& x )
237  {
238  using size_type = typename std::vector< std::complex< T > >::size_type;
239 
240  SEQUENCER_ASSERT( x.size() % 2 == 0 )
241  const auto half_result =
242  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
243  radix2( reinterpret_cast< const std::vector< std::complex< T > >& >( x ) );
244 
245  std::vector< T > result( half_result.size() );
246  const auto scale = T( 0.5 ) / result.size();
247  for ( size_type k = 0; k < result.size(); ++k )
248  {
249  const auto s_k = ( k == 0 ) ? 0 : result.size() - k;
250  const auto z = conj( half_result[ s_k ] );
251  result[ k ] =
252  scale * abs( ( half_result[ k ] + z ) - imag< T > * ( half_result[ k ] - z ) *
253  compute_gauss< T >( k, x.size() ) );
254  }
255 
256  return result;
257  }
258 
259  template < class T, std::enable_if_t< !is_complex_v< T > >* = nullptr >
260  std::vector< T > inverse_fft( const std::vector< std::complex< T > >& x )
261  {
262  using size_type = typename std::vector< std::complex< T > >::size_type;
263 
264  SEQUENCER_ASSERT( x.size() % 2 == 0 )
265  std::vector< std::complex< T > > y( x.size() );
266  for ( size_type k = 0; k < y.size(); ++k )
267  {
268  const auto s_k = ( k == 0 ) ? 0 : y.size() - k;
269  const auto z = conj( x[ s_k ] );
270  y[ s_k ] =
271  T( 0.5 ) * conj( ( x[ k ] + z ) - imag< T > * ( x[ k ] - z ) /
272  compute_gauss< T >( k, 2 * x.size() ) );
273  }
274 
275  const auto complex_result = inverse_radix2( y );
276  std::vector< T > result( 2 * complex_result.size() );
277  std::memcpy( result.data(), complex_result.data(), result.size() * sizeof( T ) );
278  return result;
279  }
280 } // namespace sequencer::audio
std::vector< std::complex< T > > fft(const std::vector< T > &x)
Definition: fft.hpp:167
size_type size() const noexcept
Definition: vector_view.hpp:32
#define SEQUENCER_ASSERT(cond)
Definition: assert.hpp:8
Definition: vector_view.hpp:85
std::vector< T > fft_abs_scaled(const std::vector< T > &x)
Definition: fft.hpp:236
auto create_table()
Definition: fft.hpp:20
Definition: delay.hpp:12
std::complex< T > compute_gauss(Size k, Size size)
Definition: fft.hpp:47
constexpr std::complex< T > imag
Definition: fft.hpp:17
auto inverse_radix2(Container x)
Definition: fft.hpp:136
auto & gauss_table()
Definition: fft.hpp:40
Definition: vector_view.hpp:62
std::vector< T > inverse_fft(const std::vector< std::complex< T > > &x)
Definition: fft.hpp:260
auto radix2(const Container &x)
Definition: fft.hpp:89