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