FastLED 3.9.15
Loading...
Searching...
No Matches
cq_kernel.cpp.hpp
Go to the documentation of this file.
1// Copyright 2020 Kenny Peng
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "fl/stl/cstddef.h"
16#include "fl/math/math.h"
17#include "fl/stl/string.h"
18#include "fl/stl/malloc.h"
19#include "fl/stl/cstring.h" // for fl::memset() and fl::memcpy()
20#include "fl/stl/noexcept.h"
21#include "cq_kernel.h"
22#include "fft_precision.h"
23
24#ifndef M_PI
25#define M_PI 3.1415926535897932384626433832795
26#endif
27
28
30
31void _generate_center_freqs(float freq[], int bands, float fmin, float fmax) FL_NOEXCEPT {
32 if (bands <= 1) {
33 if (bands == 1) freq[0] = fmin;
34 return;
35 }
36 fft_float_t m = FFT_LOG(fmax/fmin);
37 for(int i = 0; i < bands; i++) freq[i] = fmin*FFT_EXP(m*i/(bands-1));
38}
39
41 if (N <= 1) {
42 if (N == 1) window[0] = 1;
43 return;
44 }
45 fft_float_t a0 = 0.54;
46 for(int i = 0; i < N; i++){
47 fft_float_t val = a0-(1-a0)*FFT_COS(2*M_PI*i/(N-1));
48 #ifdef FIXED_POINT // If fixed_point, represent hamming window with integers
49 window[i] = (kiss_fft_scalar)(SAMP_MAX*val);
50 #else // Else if floating point, represent hamming window as-is
51 window[i] = val;
52 #endif
53 }
54}
55
57 fft_float_t sigma = 0.5; // makes a window accurate to -30dB from peak, but smaller sigma is more accurate
58 for(int i = 0; i < N; i++){
59 #ifdef FIXED_POINT // If fixed_point, represent window with integers
60 window[i] = SAMP_MAX*FFT_EXP(-0.5*FFT_POW((i-N/2.0)/(sigma*N/2.0), 2.0));
61 #else // Else if floating point, represent window as-is
62 window[i] = FFT_EXP(-0.5*FFT_POW((i-N/2.0)/(sigma*N/2.0), 2.0));
63 #endif
64 }
65}
66
67void _generate_kernel(kiss_fft_cpx kernel[], kiss_fftr_cfg cfg, enum window_type window_type, float f, float fmin, float fs, int N) FL_NOEXCEPT {
68 // Generates window in the center and zero everywhere else
69 float factor = f/fmin;
70 int N_window = N/factor; // Scales inversely with frequency (see CQT paper)
72
73 switch(window_type){
74 case HAMMING:
75 _generate_hamming(&time_K[(N-N_window)/2], N_window);
76 break;
77 case GAUSSIAN:
78 _generate_guassian(&time_K[(N-N_window)/2], N_window);
79 break;
80 }
81
82 // Fills window with f Hz wave sampled at fs Hz
83 for(int i = 0; i < N; i++) time_K[i] *= FFT_COS(2*M_PI*(f/fs)*(i-N/2));
84
85 #ifdef FIXED_POINT // If using fixed point, just scale inversely to N after FFT (don't normalize)
86 kiss_fftr(cfg, time_K, kernel); // Outputs garbage for Q31
87 for(int i = 0; i < N; i++){
88 kernel[i].r *= factor;
89 kernel[i].i *= factor;
90 }
91 #else // Else if floating point, follow CQT paper more exactly (normalize with N before FFT)
92 for(int i = 0; i < N; i++) time_K[i] /= N_window;
93 kiss_fftr(cfg, time_K, kernel);
94 #endif
95
96 fl::free(time_K);
97}
98
100 return FFT_SQRT(x.r*x.r+x.i*x.i);
101}
102
104 float *freq = (float*)fl::malloc(cfg.bands * sizeof(float));
105 _generate_center_freqs(freq, cfg.bands, cfg.fmin, cfg.fmax);
106
107 kiss_fftr_cfg fft_cfg = kiss_fftr_alloc(cfg.samples, 0, nullptr, nullptr);
108 struct sparse_arr* kernels = (struct sparse_arr*)fl::malloc(cfg.bands*sizeof(struct sparse_arr));
109 kiss_fft_cpx *temp_kernel = (kiss_fft_cpx*)fl::malloc(cfg.samples*sizeof(kiss_fft_cpx));
110
111 for(int i = 0; i < cfg.bands; i++){
112 // Clears temp_kernel before calling _generate_kernel on it
113 for(int i = 0; i < cfg.samples; i++) temp_kernel[i].r = temp_kernel[i].i = 0;
114
115 _generate_kernel(temp_kernel, fft_cfg, cfg.window_type, freq[i], cfg.fmin, cfg.fs, cfg.samples);
116
117 // Counts number of elements with a complex magnitude above cfg.min_val in temp_kernel
118 int n_elems = 0;
119 for(int j = 0; j < cfg.samples; j++) if(_mag(temp_kernel[j]) > cfg.min_val) n_elems++;
120
121 // Generates sparse_arr holding n_elems sparse_arr_elem's
122 kernels[i].n_elems = n_elems;
123 kernels[i].elems = (struct sparse_arr_elem*)fl::malloc(n_elems*sizeof(struct sparse_arr_elem));
124
125 // Generates sparse_arr_elem's from complex values counted before
126 int k = 0;
127 for(int j = 0; j < cfg.samples; j++){
128 if(_mag(temp_kernel[j]) > cfg.min_val){
129 kernels[i].elems[k].val = temp_kernel[j];
130 kernels[i].elems[k].n = j;
131 k++;
132 }
133 }
134 }
135
136 fl::free(fft_cfg);
137 fl::free(temp_kernel);
138 fl::free(freq);
139
140 return kernels;
141}
142
144 struct sparse_arr *new_ptr = (struct sparse_arr*)fl::malloc(cfg.bands*sizeof(struct sparse_arr));
145 for(int i = 0; i < cfg.bands; i++){
146 new_ptr[i].n_elems = old_ptr[i].n_elems;
147 new_ptr[i].elems = (struct sparse_arr_elem*)fl::malloc(old_ptr[i].n_elems*sizeof(struct sparse_arr_elem));
148 fl::memcpy(new_ptr[i].elems, old_ptr[i].elems, old_ptr[i].n_elems*sizeof(struct sparse_arr_elem));
149 fl::free(old_ptr[i].elems);
150 }
151 fl::free(old_ptr);
152 return new_ptr;
153}
154
155void apply_kernels(kiss_fft_cpx fft[], kiss_fft_cpx cq[], struct sparse_arr kernels[], struct cq_kernel_cfg cfg) FL_NOEXCEPT {
156 for(int i = 0; i < cfg.bands; i++){
157 for(int j = 0; j < kernels[i].n_elems; j++){
158 kiss_fft_cpx weighted_val;
159 C_MUL(weighted_val, fft[kernels[i].elems[j].n], kernels[i].elems[j].val);
160 C_ADDTO(cq[i], weighted_val);
161 }
162 }
163}
164
165void free_kernels(struct sparse_arr *kernels, struct cq_kernel_cfg cfg) FL_NOEXCEPT {
166 for(int i = 0; i < cfg.bands; i++) fl::free(kernels[i].elems);
167 fl::free(kernels);
168}
#define C_ADDTO(res, a)
#define SAMP_MAX
#define C_MUL(m, a, b)
int x
Definition simple.h:92
AudioAnalyzeFFT1024 fft
void _generate_center_freqs(float freq[], int bands, float fmin, float fmax) FL_NOEXCEPT
struct sparse_arr * reallocate_kernels(struct sparse_arr *old_ptr, struct cq_kernel_cfg cfg) FL_NOEXCEPT
void _generate_kernel(kiss_fft_cpx kernel[], kiss_fftr_cfg cfg, enum window_type window_type, float f, float fmin, float fs, int N) FL_NOEXCEPT
void apply_kernels(kiss_fft_cpx fft[], kiss_fft_cpx cq[], struct sparse_arr kernels[], struct cq_kernel_cfg cfg) FL_NOEXCEPT
struct sparse_arr * generate_kernels(struct cq_kernel_cfg cfg) FL_NOEXCEPT
void free_kernels(struct sparse_arr *kernels, struct cq_kernel_cfg cfg) FL_NOEXCEPT
void _generate_hamming(kiss_fft_scalar window[], int N) FL_NOEXCEPT
void _generate_guassian(kiss_fft_scalar window[], int N) FL_NOEXCEPT
#define M_PI
kiss_fft_scalar _mag(kiss_fft_cpx x) FL_NOEXCEPT
window_type
Definition cq_kernel.h:26
@ HAMMING
Definition cq_kernel.h:27
@ GAUSSIAN
Definition cq_kernel.h:28
struct sparse_arr_elem * elems
Definition cq_kernel.h:56
kiss_fft_cpx val
Definition cq_kernel.h:52
int n_elems
Definition cq_kernel.h:55
#define FFT_COS(x)
#define FFT_EXP(x)
#define FFT_POW(x, y)
float fft_float_t
#define FFT_SQRT(x)
#define FFT_LOG(x)
#define kiss_fft_scalar
Definition kiss_fft.h:69
void kiss_fftr(kiss_fftr_cfg st, const kiss_fft_scalar *timedata, kiss_fft_cpx *freqdata) FL_NOEXCEPT
kiss_fftr_cfg kiss_fftr_alloc(int nfft, int inverse_fft, void *mem, size_t *lenmem) FL_NOEXCEPT
struct kiss_fftr_state * kiss_fftr_cfg
Definition kiss_fftr.h:26
void * memcpy(void *dest, const void *src, size_t n) FL_NOEXCEPT
void * calloc(size_t nmemb, size_t size)
void * malloc(size_t size)
Definition malloc.cpp.hpp:9
void free(void *ptr)
#define FL_NOEXCEPT