FastLED 3.9.15
Loading...
Searching...
No Matches
cq_kernel.cpp
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 <math.h>
16#include <string.h>
17#include "cq_kernel.h"
18
19#ifndef M_PI
20#define M_PI 3.1415926535897932384626433832795
21#endif
22
23
24void _generate_guassian(kiss_fft_scalar window[], int N);
25
26void _generate_center_freqs(float freq[], int bands, float fmin, float fmax){
27 float m = log(fmax/fmin);
28 for(int i = 0; i < bands; i++) freq[i] = fmin*exp(m*i/(bands-1));
29}
30
31void _generate_hamming(kiss_fft_scalar window[], int N){
32 float a0 = 0.54;
33 for(int i = 0; i < N; i++){
34 #ifdef FIXED_POINT // If fixed_point, represent hamming window with integers
35 window[i] = SAMP_MAX*(a0-(1-a0)*cos(2*M_PI*i/(N-1)));
36 #else // Else if floating point, represent hamming window as-is
37 window[i] = a0-(1-a0)*cos(2*M_PI*i/(N-1));
38 #endif
39 }
40}
41
42void _generate_guassian(kiss_fft_scalar window[], int N){
43 float sigma = 0.5; // makes a window accurate to -30dB from peak, but smaller sigma is more accurate
44 for(int i = 0; i < N; i++){
45 #ifdef FIXED_POINT // If fixed_point, represent window with integers
46 window[i] = SAMP_MAX*exp(-0.5*pow((i-N/2.0)/(sigma*N/2.0), 2));
47 #else // Else if floating point, represent window as-is
48 window[i] = exp(-0.5*pow((i-N/2.0)/(sigma*N/2.0), 2));
49 #endif
50 }
51}
52
53void _generate_kernel(kiss_fft_cpx kernel[], kiss_fftr_cfg cfg, enum window_type window_type, float f, float fmin, float fs, int N){
54 // Generates window in the center and zero everywhere else
55 float factor = f/fmin;
56 int N_window = N/factor; // Scales inversely with frequency (see CQT paper)
57 kiss_fft_scalar *time_K = (kiss_fft_scalar*)calloc(N, sizeof(kiss_fft_scalar));
58
59 switch(window_type){
60 case HAMMING:
61 _generate_hamming(&time_K[(N-N_window)/2], N_window);
62 break;
63 case GAUSSIAN:
64 _generate_guassian(&time_K[(N-N_window)/2], N_window);
65 break;
66 }
67
68 // Fills window with f Hz wave sampled at fs Hz
69 for(int i = 0; i < N; i++) time_K[i] *= cos(2*M_PI*(f/fs)*(i-N/2));
70
71 #ifdef FIXED_POINT // If using fixed point, just scale inversely to N after FFT (don't normalize)
72 kiss_fftr(cfg, time_K, kernel); // Outputs garbage for Q31
73 for(int i = 0; i < N; i++){
74 kernel[i].r *= factor;
75 kernel[i].i *= factor;
76 }
77 #else // Else if floating point, follow CQT paper more exactly (normalize with N before FFT)
78 for(int i = 0; i < N; i++) time_K[i] /= N_window;
79 kiss_fftr(cfg, time_K, kernel);
80 #endif
81
82 free(time_K);
83}
84
86 return sqrt(x.r*x.r+x.i*x.i);
87}
88
90 float *freq = (float*)malloc(cfg.bands * sizeof(float));
91 _generate_center_freqs(freq, cfg.bands, cfg.fmin, cfg.fmax);
92
93 kiss_fftr_cfg fft_cfg = kiss_fftr_alloc(cfg.samples, 0, NULL, NULL);
94 struct sparse_arr* kernels = (struct sparse_arr*)malloc(cfg.bands*sizeof(struct sparse_arr));
95 kiss_fft_cpx *temp_kernel = (kiss_fft_cpx*)malloc(cfg.samples*sizeof(kiss_fft_cpx));
96
97 for(int i = 0; i < cfg.bands; i++){
98 // Clears temp_kernel before calling _generate_kernel on it
99 for(int i = 0; i < cfg.samples; i++) temp_kernel[i].r = temp_kernel[i].i = 0;
100
101 _generate_kernel(temp_kernel, fft_cfg, cfg.window_type, freq[i], cfg.fmin, cfg.fs, cfg.samples);
102
103 // Counts number of elements with a complex magnitude above cfg.min_val in temp_kernel
104 int n_elems = 0;
105 for(int j = 0; j < cfg.samples; j++) if(_mag(temp_kernel[j]) > cfg.min_val) n_elems++;
106
107 // Generates sparse_arr holding n_elems sparse_arr_elem's
108 kernels[i].n_elems = n_elems;
109 kernels[i].elems = (struct sparse_arr_elem*)malloc(n_elems*sizeof(struct sparse_arr_elem));
110
111 // Generates sparse_arr_elem's from complex values counted before
112 int k = 0;
113 for(int j = 0; j < cfg.samples; j++){
114 if(_mag(temp_kernel[j]) > cfg.min_val){
115 kernels[i].elems[k].val = temp_kernel[j];
116 kernels[i].elems[k].n = j;
117 k++;
118 }
119 }
120 }
121
122 free(fft_cfg);
123 free(temp_kernel);
124 free(freq);
125
126 return kernels;
127}
128
129struct sparse_arr* reallocate_kernels(struct sparse_arr *old_ptr, struct cq_kernel_cfg cfg){
130 struct sparse_arr *new_ptr = (struct sparse_arr*)malloc(cfg.bands*sizeof(struct sparse_arr));
131 for(int i = 0; i < cfg.bands; i++){
132 new_ptr[i].n_elems = old_ptr[i].n_elems;
133 new_ptr[i].elems = (struct sparse_arr_elem*)malloc(old_ptr[i].n_elems*sizeof(struct sparse_arr_elem));
134 memcpy(new_ptr[i].elems, old_ptr[i].elems, old_ptr[i].n_elems*sizeof(struct sparse_arr_elem));
135 free(old_ptr[i].elems);
136 }
137 free(old_ptr);
138 return new_ptr;
139}
140
141void apply_kernels(kiss_fft_cpx fft[], kiss_fft_cpx cq[], struct sparse_arr kernels[], struct cq_kernel_cfg cfg){
142 for(int i = 0; i < cfg.bands; i++){
143 for(int j = 0; j < kernels[i].n_elems; j++){
144 kiss_fft_cpx weighted_val;
145 C_MUL(weighted_val, fft[kernels[i].elems[j].n], kernels[i].elems[j].val);
146 C_ADDTO(cq[i], weighted_val);
147 }
148 }
149}
150
151void free_kernels(struct sparse_arr *kernels, struct cq_kernel_cfg cfg){
152 for(int i = 0; i < cfg.bands; i++) free(kernels[i].elems);
153 free(kernels);
154}
#define C_ADDTO(res, a)
#define SAMP_MAX
#define C_MUL(m, a, b)
int x
Definition simple.h:92
AudioAnalyzeFFT1024 fft
void _generate_guassian(kiss_fft_scalar window[], int N)
Definition cq_kernel.cpp:42
void _generate_hamming(kiss_fft_scalar window[], int N)
Definition cq_kernel.cpp:31
kiss_fft_scalar _mag(kiss_fft_cpx x)
Definition cq_kernel.cpp:85
void free_kernels(struct sparse_arr *kernels, struct cq_kernel_cfg cfg)
void _generate_kernel(kiss_fft_cpx kernel[], kiss_fftr_cfg cfg, enum window_type window_type, float f, float fmin, float fs, int N)
Definition cq_kernel.cpp:53
void apply_kernels(kiss_fft_cpx fft[], kiss_fft_cpx cq[], struct sparse_arr kernels[], struct cq_kernel_cfg cfg)
struct sparse_arr * generate_kernels(struct cq_kernel_cfg cfg)
Definition cq_kernel.cpp:89
void _generate_center_freqs(float freq[], int bands, float fmin, float fmax)
Definition cq_kernel.cpp:26
struct sparse_arr * reallocate_kernels(struct sparse_arr *old_ptr, struct cq_kernel_cfg cfg)
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
enum window_type window_type
Definition cq_kernel.h:42
kiss_fft_scalar min_val
Definition cq_kernel.h:43
#define kiss_fft_scalar
Definition kiss_fft.h:63
kiss_fft_scalar r
Definition kiss_fft.h:73
kiss_fft_scalar i
Definition kiss_fft.h:74
void kiss_fftr(kiss_fftr_cfg st, const kiss_fft_scalar *timedata, kiss_fft_cpx *freqdata)
Definition kiss_fftr.cpp:67
kiss_fftr_cfg kiss_fftr_alloc(int nfft, int inverse_fft, void *mem, size_t *lenmem)
Definition kiss_fftr.cpp:21
struct kiss_fftr_state * kiss_fftr_cfg
Definition kiss_fftr.h:26
#define M_PI
Definition math_macros.h:93