this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

phase 1

alice 70f9f595 b23b81cb

+608 -6
+31 -6
CLAUDE.md
··· 245 245 5. **Hook into tic_core_tick** after FFT processing 246 246 6. **Test with simple Lua visualization** 247 247 248 - #### Phase 2: Full API (Test in 3-4 hours) 249 - 1. Add smoothing to CQT processing 250 - 2. Implement remaining API functions: `cqts()`, `cqto()`, `cqtos()` 251 - 3. Add normalization and auto-gain 252 - 4. Create demo comparing FFT vs CQT 248 + #### Phase 1 Status: COMPLETE ✓ 249 + - [x] cqtdata.h - Basic structure and constants 250 + - [x] cqtdata.c - Global data instance 251 + - [x] cqt_kernel.h - Kernel generation header 252 + - [x] cqt_kernel.c - Adapt ESP32 kernel generation 253 + - [x] cqt.h - Main CQT header 254 + - [x] cqt.c - Basic CQT processing (with placeholder FFT mapping) 255 + - [x] luaapi.c - Add cqt() function 256 + - [x] tic80.c - Hook into core tick 257 + - [x] CMakeLists.txt - Add new files to build 258 + 259 + **Test Results**: Basic API working, visualization shows 10 octaves with test data mapping 260 + 261 + #### Phase 2: Real CQT Processing (Priority Tasks) 262 + 1. **Access raw audio buffer** from FFT capture system 263 + 2. **Implement 4096-point FFT** for CQT (separate from main FFT) 264 + 3. **Apply CQT kernels** to FFT output (currently stubbed) 265 + 4. **Fix kernel initialization** - kernels not being generated on startup 266 + 5. **Add remaining API functions**: `cqts()`, `cqto()`, `cqtos()` 267 + 6. **Create comparison demo** showing FFT vs CQT side-by-side 253 268 254 269 #### Phase 3: Optimization (If needed) 255 270 1. Profile and identify bottlenecks ··· 266 281 local val = cqt(i) * 100 -- scale up for visibility 267 282 rect(i*2, 136-val, 2, val, 12) 268 283 end 284 + 285 + -- Draw octave markers 286 + for oct=0,9 do 287 + local x = oct * 12 * 2 288 + line(x, 0, x, 136, 8) 289 + print(oct, x+2, 2, 12, false, 1, true) 290 + end 291 + 292 + -- Show info 293 + print("CQT TEST - 10 octaves x 12 notes", 2, 120, 12, false, 1, true) 269 294 end 270 295 ``` 271 296 ··· 282 307 - Platform-specific code should be isolated in src/system/ 283 308 - The studio uses immediate mode GUI principles 284 309 - Cartridge format is documented in wiki and src/studio/project.c 285 - - PRO version enables additional features like extra memory banks 310 + - PRO version enables additional features like extra memory banks
+3
cmake/core.cmake
··· 16 16 set(TIC80CORE_DIR ${CMAKE_SOURCE_DIR}/src) 17 17 set(TIC80CORE_SRC 18 18 ${TIC80CORE_DIR}/fftdata.c 19 + ${TIC80CORE_DIR}/cqtdata.c 19 20 ${TIC80CORE_DIR}/core/core.c 20 21 ${TIC80CORE_DIR}/core/draw.c 21 22 ${TIC80CORE_DIR}/core/io.c ··· 27 28 ${TIC80CORE_DIR}/tilesheet.c 28 29 ${TIC80CORE_DIR}/script.c 29 30 ${TIC80CORE_DIR}/ext/fft.c 31 + ${TIC80CORE_DIR}/ext/cqt.c 32 + ${TIC80CORE_DIR}/ext/cqt_kernel.c 30 33 ${TIC80CORE_DIR}/ext/kiss_fft.c 31 34 ${TIC80CORE_DIR}/ext/kiss_fftr.c 32 35 )
+28
src/api/luaapi.c
··· 21 21 // SOFTWARE. 22 22 23 23 #include "core/core.h" 24 + #include "cqtdata.h" 24 25 25 26 #include <stdlib.h> 26 27 #include <lua.h> ··· 1595 1596 return 0; 1596 1597 } 1597 1598 1599 + static s32 lua_cqt(lua_State* lua) 1600 + { 1601 + s32 top = lua_gettop(lua); 1602 + 1603 + if (top >= 1) 1604 + { 1605 + s32 bin = getLuaNumber(lua, 1); 1606 + 1607 + // Validate bin range 1608 + if (bin < 0 || bin >= CQT_BINS) 1609 + { 1610 + luaL_error(lua, "cqt bin out of range (0-%d)\n", CQT_BINS - 1); 1611 + return 0; 1612 + } 1613 + 1614 + // Return normalized CQT data 1615 + lua_pushnumber(lua, cqtNormalizedData[bin]); 1616 + return 1; 1617 + } 1618 + 1619 + luaL_error(lua, "invalid params, cqt(bin)\n"); 1620 + return 0; 1621 + } 1622 + 1598 1623 static int lua_dofile(lua_State *lua) 1599 1624 { 1600 1625 luaL_error(lua, "unknown method: \"dofile\"\n"); ··· 1648 1673 1649 1674 registerLuaFunction(core, lua_dofile, "dofile"); 1650 1675 registerLuaFunction(core, lua_loadfile, "loadfile"); 1676 + 1677 + // Register CQT function 1678 + registerLuaFunction(core, lua_cqt, "cqt"); 1651 1679 } 1652 1680 1653 1681 void luaapi_close(tic_mem* tic)
+34
src/core/core.c
··· 22 22 23 23 #include "fftdata.h" 24 24 #include "../ext/fft.h" 25 + #include "cqtdata.h" 26 + #include "../ext/cqt.h" 25 27 26 28 #include "api.h" 27 29 #include "core.h" ··· 442 444 if (fftEnabled) 443 445 { 444 446 FFT_GetFFT(fftData); 447 + 448 + // Process CQT using existing FFT data 449 + // For now, tie CQT to FFT enable flag 450 + cqtEnabled = fftEnabled; 451 + if (cqtEnabled) 452 + { 453 + // TODO: In a full implementation, we would: 454 + // 1. Get raw audio samples 455 + // 2. Perform our own 4096-point FFT 456 + // 3. Apply CQT kernels 457 + // For now, we'll use a simplified approach with existing FFT data 458 + 459 + // TEMPORARY: Generate test data for Phase 1 testing 460 + // This creates a simple frequency sweep pattern 461 + for (int i = 0; i < CQT_BINS; i++) 462 + { 463 + // Use existing FFT data to create some movement 464 + int fftIndex = (i * FFT_SIZE) / CQT_BINS; 465 + if (fftIndex < FFT_SIZE) 466 + { 467 + // Map FFT data to CQT bins with some scaling 468 + cqtData[i] = fftData[fftIndex] * 0.5f; 469 + 470 + // Apply smoothing 471 + cqtSmoothingData[i] = cqtSmoothingData[i] * 0.8f + cqtData[i] * 0.2f; 472 + 473 + // Normalize 474 + cqtNormalizedData[i] = cqtSmoothingData[i]; 475 + if (cqtNormalizedData[i] > 1.0f) cqtNormalizedData[i] = 1.0f; 476 + } 477 + } 478 + } 445 479 } 446 480 if (!core->state.initialized) 447 481 {
+57
src/cqtdata.c
··· 1 + #include "cqtdata.h" 2 + #include <string.h> 3 + #include <stdlib.h> 4 + 5 + // Global CQT data arrays 6 + float cqtData[CQT_BINS]; 7 + float cqtSmoothingData[CQT_BINS]; 8 + float cqtNormalizedData[CQT_BINS]; 9 + 10 + // Peak tracking for auto-gain 11 + float cqtPeakValue = 1.0f; 12 + float cqtPeakSmoothValue = 1.0f; 13 + 14 + // Enable flag (tied to fftEnabled initially) 15 + bool cqtEnabled = false; 16 + 17 + // Array of kernels, one per CQT bin 18 + CqtKernel cqtKernels[CQT_BINS]; 19 + 20 + void CQT_Init(void) 21 + { 22 + // Zero all data arrays 23 + memset(cqtData, 0, sizeof(cqtData)); 24 + memset(cqtSmoothingData, 0, sizeof(cqtSmoothingData)); 25 + memset(cqtNormalizedData, 0, sizeof(cqtNormalizedData)); 26 + 27 + // Initialize peak values 28 + cqtPeakValue = 1.0f; 29 + cqtPeakSmoothValue = 1.0f; 30 + 31 + // Zero kernel pointers 32 + memset(cqtKernels, 0, sizeof(cqtKernels)); 33 + } 34 + 35 + void CQT_Cleanup(void) 36 + { 37 + // Free all allocated kernel memory 38 + for (int i = 0; i < CQT_BINS; i++) 39 + { 40 + if (cqtKernels[i].real) 41 + { 42 + free(cqtKernels[i].real); 43 + cqtKernels[i].real = NULL; 44 + } 45 + if (cqtKernels[i].imag) 46 + { 47 + free(cqtKernels[i].imag); 48 + cqtKernels[i].imag = NULL; 49 + } 50 + if (cqtKernels[i].indices) 51 + { 52 + free(cqtKernels[i].indices); 53 + cqtKernels[i].indices = NULL; 54 + } 55 + cqtKernels[i].length = 0; 56 + } 57 + }
+48
src/cqtdata.h
··· 1 + #pragma once 2 + #include <stdbool.h> 3 + 4 + #define CQT_BINS 120 5 + #define CQT_OCTAVES 10 6 + #define CQT_BINS_PER_OCTAVE 12 7 + #define CQT_FFT_SIZE 4096 // Larger FFT for better sub-bass resolution 8 + 9 + // CQT frequency range 10 + #define CQT_MIN_FREQ 20.0f // Below piano's lowest A 11 + #define CQT_MAX_FREQ 20480.0f // Nearest note to 20kHz 12 + 13 + // Smoothing parameters 14 + #define CQT_SMOOTHING_FACTOR 0.7f 15 + #define CQT_SPARSITY_THRESHOLD 0.01f 16 + 17 + // Raw CQT magnitude data 18 + extern float cqtData[CQT_BINS]; 19 + 20 + // Smoothed CQT data for visual stability 21 + extern float cqtSmoothingData[CQT_BINS]; 22 + 23 + // Normalized CQT data (0-1 range) 24 + extern float cqtNormalizedData[CQT_BINS]; 25 + 26 + // Peak tracking for auto-gain 27 + extern float cqtPeakValue; 28 + extern float cqtPeakSmoothValue; 29 + 30 + // Enable flag (tied to fftEnabled initially) 31 + extern bool cqtEnabled; 32 + 33 + // Sparse kernel storage structures 34 + typedef struct { 35 + float* real; // Real parts of kernel (sparse) 36 + float* imag; // Imaginary parts of kernel (sparse) 37 + int* indices; // Non-zero FFT bin indices 38 + int length; // Number of non-zero elements 39 + } CqtKernel; 40 + 41 + // Array of kernels, one per CQT bin 42 + extern CqtKernel cqtKernels[CQT_BINS]; 43 + 44 + // Initialize CQT data structures 45 + void CQT_Init(void); 46 + 47 + // Free CQT kernel memory 48 + void CQT_Cleanup(void);
+152
src/ext/cqt.c
··· 1 + #include "cqt.h" 2 + #include "cqt_kernel.h" 3 + #include "../cqtdata.h" 4 + #include "../fftdata.h" 5 + #include "kiss_fftr.h" 6 + #include <math.h> 7 + #include <string.h> 8 + 9 + // FFT configuration for CQT 10 + static kiss_fftr_cfg cqtFftCfg = NULL; 11 + static float* cqtAudioBuffer = NULL; 12 + static kiss_fft_cpx* cqtFftOutput = NULL; 13 + 14 + // Initialize CQT processing 15 + bool CQT_Open(void) 16 + { 17 + // Initialize data structures 18 + CQT_Init(); 19 + 20 + // Allocate FFT buffers 21 + cqtAudioBuffer = (float*)malloc(CQT_FFT_SIZE * sizeof(float)); 22 + cqtFftOutput = (kiss_fft_cpx*)malloc((CQT_FFT_SIZE/2 + 1) * sizeof(kiss_fft_cpx)); 23 + 24 + if (!cqtAudioBuffer || !cqtFftOutput) 25 + { 26 + CQT_Close(); 27 + return false; 28 + } 29 + 30 + // Create FFT configuration 31 + cqtFftCfg = kiss_fftr_alloc(CQT_FFT_SIZE, 0, NULL, NULL); 32 + if (!cqtFftCfg) 33 + { 34 + CQT_Close(); 35 + return false; 36 + } 37 + 38 + // Configure CQT kernels 39 + CqtKernelConfig config = { 40 + .fftSize = CQT_FFT_SIZE, 41 + .numBins = CQT_BINS, 42 + .minFreq = CQT_MIN_FREQ, 43 + .maxFreq = CQT_MAX_FREQ, 44 + .sampleRate = 44100.0f, // Standard TIC-80 sample rate 45 + .windowType = CQT_WINDOW_HAMMING, 46 + .sparsityThreshold = CQT_SPARSITY_THRESHOLD 47 + }; 48 + 49 + // Generate kernels 50 + if (!CQT_GenerateKernels(cqtKernels, &config)) 51 + { 52 + CQT_Close(); 53 + return false; 54 + } 55 + 56 + return true; 57 + } 58 + 59 + // Apply CQT kernels to FFT output 60 + void CQT_ApplyKernels(const float* fftReal, const float* fftImag) 61 + { 62 + // Clear CQT output 63 + memset(cqtData, 0, sizeof(cqtData)); 64 + 65 + // Apply each kernel to compute CQT bins 66 + for (int bin = 0; bin < CQT_BINS; bin++) 67 + { 68 + CqtKernel* kernel = &cqtKernels[bin]; 69 + float real = 0.0f; 70 + float imag = 0.0f; 71 + 72 + // Sparse matrix multiplication 73 + for (int k = 0; k < kernel->length; k++) 74 + { 75 + int idx = kernel->indices[k]; 76 + // Complex multiplication: (a + bi) * (c + di) = (ac - bd) + (ad + bc)i 77 + real += fftReal[idx] * kernel->real[k] - fftImag[idx] * kernel->imag[k]; 78 + imag += fftReal[idx] * kernel->imag[k] + fftImag[idx] * kernel->real[k]; 79 + } 80 + 81 + // Calculate magnitude 82 + cqtData[bin] = sqrt(real * real + imag * imag); 83 + } 84 + } 85 + 86 + // Process CQT from audio data (using existing FFT data from fftdata.h) 87 + void CQT_Process(const float* fftReal, const float* fftImag) 88 + { 89 + if (!cqtFftCfg || !cqtEnabled) return; 90 + 91 + // Note: In the full implementation, we would: 92 + // 1. Get audio data from the shared buffer 93 + // 2. Perform our own 4096-point FFT 94 + // For now, we'll process using provided FFT data 95 + 96 + // Apply CQT kernels 97 + CQT_ApplyKernels(fftReal, fftImag); 98 + 99 + // Apply smoothing 100 + for (int i = 0; i < CQT_BINS; i++) 101 + { 102 + cqtSmoothingData[i] = cqtSmoothingData[i] * CQT_SMOOTHING_FACTOR + 103 + cqtData[i] * (1.0f - CQT_SMOOTHING_FACTOR); 104 + } 105 + 106 + // Find peak for normalization 107 + float currentPeak = 0.0f; 108 + for (int i = 0; i < CQT_BINS; i++) 109 + { 110 + if (cqtSmoothingData[i] > currentPeak) 111 + currentPeak = cqtSmoothingData[i]; 112 + } 113 + 114 + // Smooth peak value 115 + if (currentPeak > cqtPeakSmoothValue) 116 + cqtPeakSmoothValue = currentPeak; 117 + else 118 + cqtPeakSmoothValue = cqtPeakSmoothValue * 0.99f + currentPeak * 0.01f; 119 + 120 + // Normalize data 121 + float normalizer = (cqtPeakSmoothValue > 0.0001f) ? (1.0f / cqtPeakSmoothValue) : 1.0f; 122 + for (int i = 0; i < CQT_BINS; i++) 123 + { 124 + cqtNormalizedData[i] = cqtSmoothingData[i] * normalizer; 125 + if (cqtNormalizedData[i] > 1.0f) cqtNormalizedData[i] = 1.0f; 126 + } 127 + } 128 + 129 + // Close CQT processing and free resources 130 + void CQT_Close(void) 131 + { 132 + if (cqtFftCfg) 133 + { 134 + free(cqtFftCfg); 135 + cqtFftCfg = NULL; 136 + } 137 + 138 + if (cqtAudioBuffer) 139 + { 140 + free(cqtAudioBuffer); 141 + cqtAudioBuffer = NULL; 142 + } 143 + 144 + if (cqtFftOutput) 145 + { 146 + free(cqtFftOutput); 147 + cqtFftOutput = NULL; 148 + } 149 + 150 + // Clean up kernels 151 + CQT_Cleanup(); 152 + }
+17
src/ext/cqt.h
··· 1 + #pragma once 2 + 3 + #include <stdbool.h> 4 + 5 + // Initialize CQT processing 6 + // Returns true on success, false on failure 7 + bool CQT_Open(void); 8 + 9 + // Process CQT from FFT data 10 + // fftData: Complex FFT output (size should be CQT_FFT_SIZE/2 + 1) 11 + void CQT_Process(const float* fftReal, const float* fftImag); 12 + 13 + // Close CQT processing and free resources 14 + void CQT_Close(void); 15 + 16 + // Apply CQT kernels to FFT output 17 + void CQT_ApplyKernels(const float* fftReal, const float* fftImag);
+203
src/ext/cqt_kernel.c
··· 1 + #include "cqt_kernel.h" 2 + #include <math.h> 3 + #include <stdlib.h> 4 + #include <string.h> 5 + #include "../ext/kiss_fftr.h" 6 + 7 + #ifndef M_PI 8 + #define M_PI 3.14159265358979323846 9 + #endif 10 + 11 + // Generate logarithmically spaced center frequencies 12 + void CQT_GenerateCenterFrequencies(float* frequencies, int numBins, float minFreq, float maxFreq) 13 + { 14 + float logRatio = log(maxFreq / minFreq); 15 + for (int i = 0; i < numBins; i++) 16 + { 17 + frequencies[i] = minFreq * exp(logRatio * i / (numBins - 1)); 18 + } 19 + } 20 + 21 + // Calculate Q factor for constant-Q transform 22 + float CQT_CalculateQ(int binsPerOctave) 23 + { 24 + // Q = 1 / (2^(1/binsPerOctave) - 1) 25 + // For 12 bins/octave, Q ≈ 17.0 26 + return 1.0f / (pow(2.0, 1.0 / binsPerOctave) - 1.0f); 27 + } 28 + 29 + // Generate Hamming window 30 + static void generateHammingWindow(float* window, int length) 31 + { 32 + const float a0 = 0.54f; 33 + const float a1 = 0.46f; 34 + 35 + for (int i = 0; i < length; i++) 36 + { 37 + window[i] = a0 - a1 * cos(2.0 * M_PI * i / (length - 1)); 38 + } 39 + } 40 + 41 + // Generate Gaussian window 42 + static void generateGaussianWindow(float* window, int length) 43 + { 44 + const float sigma = 0.5f; // Standard deviation factor 45 + float center = (length - 1) / 2.0f; 46 + float normFactor = sigma * length / 2.0f; 47 + 48 + for (int i = 0; i < length; i++) 49 + { 50 + float x = (i - center) / normFactor; 51 + window[i] = exp(-0.5f * x * x); 52 + } 53 + } 54 + 55 + // Generate a single CQT kernel 56 + static bool generateSingleKernel( 57 + CqtKernel* kernel, 58 + kiss_fftr_cfg fftCfg, 59 + int fftSize, 60 + float centerFreq, 61 + float minFreq, 62 + float sampleRate, 63 + CqtWindowType windowType, 64 + float sparsityThreshold) 65 + { 66 + // Calculate window length based on frequency (inverse relationship) 67 + float factor = centerFreq / minFreq; 68 + int windowLength = (int)(fftSize / factor); 69 + if (windowLength < 1) windowLength = 1; 70 + if (windowLength > fftSize) windowLength = fftSize; 71 + 72 + // Allocate temporary arrays 73 + float* timeKernel = (float*)calloc(fftSize, sizeof(float)); 74 + kiss_fft_cpx* freqKernel = (kiss_fft_cpx*)malloc((fftSize/2 + 1) * sizeof(kiss_fft_cpx)); 75 + 76 + if (!timeKernel || !freqKernel) 77 + { 78 + free(timeKernel); 79 + free(freqKernel); 80 + return false; 81 + } 82 + 83 + // Generate window in the center of the kernel 84 + int windowStart = (fftSize - windowLength) / 2; 85 + float* window = &timeKernel[windowStart]; 86 + 87 + switch (windowType) 88 + { 89 + case CQT_WINDOW_HAMMING: 90 + generateHammingWindow(window, windowLength); 91 + break; 92 + case CQT_WINDOW_GAUSSIAN: 93 + generateGaussianWindow(window, windowLength); 94 + break; 95 + } 96 + 97 + // Modulate window with complex exponential 98 + for (int i = 0; i < fftSize; i++) 99 + { 100 + float phase = 2.0f * M_PI * centerFreq / sampleRate * (i - fftSize / 2); 101 + timeKernel[i] *= cos(phase); 102 + } 103 + 104 + // Normalize by window length 105 + for (int i = 0; i < fftSize; i++) 106 + { 107 + timeKernel[i] /= windowLength; 108 + } 109 + 110 + // Perform FFT 111 + kiss_fftr(fftCfg, timeKernel, freqKernel); 112 + 113 + // Count non-zero elements (above sparsity threshold) 114 + int nonZeroCount = 0; 115 + for (int i = 0; i <= fftSize/2; i++) 116 + { 117 + float magnitude = sqrt(freqKernel[i].r * freqKernel[i].r + 118 + freqKernel[i].i * freqKernel[i].i); 119 + if (magnitude > sparsityThreshold) 120 + { 121 + nonZeroCount++; 122 + } 123 + } 124 + 125 + // Allocate sparse storage 126 + kernel->real = (float*)malloc(nonZeroCount * sizeof(float)); 127 + kernel->imag = (float*)malloc(nonZeroCount * sizeof(float)); 128 + kernel->indices = (int*)malloc(nonZeroCount * sizeof(int)); 129 + kernel->length = nonZeroCount; 130 + 131 + if (!kernel->real || !kernel->imag || !kernel->indices) 132 + { 133 + free(kernel->real); 134 + free(kernel->imag); 135 + free(kernel->indices); 136 + free(timeKernel); 137 + free(freqKernel); 138 + return false; 139 + } 140 + 141 + // Store non-zero elements 142 + int sparseIndex = 0; 143 + for (int i = 0; i <= fftSize/2; i++) 144 + { 145 + float magnitude = sqrt(freqKernel[i].r * freqKernel[i].r + 146 + freqKernel[i].i * freqKernel[i].i); 147 + if (magnitude > sparsityThreshold) 148 + { 149 + kernel->real[sparseIndex] = freqKernel[i].r; 150 + kernel->imag[sparseIndex] = freqKernel[i].i; 151 + kernel->indices[sparseIndex] = i; 152 + sparseIndex++; 153 + } 154 + } 155 + 156 + free(timeKernel); 157 + free(freqKernel); 158 + return true; 159 + } 160 + 161 + // Generate CQT kernels for all bins 162 + bool CQT_GenerateKernels(CqtKernel* kernels, const CqtKernelConfig* config) 163 + { 164 + // Generate center frequencies 165 + float* centerFreqs = (float*)malloc(config->numBins * sizeof(float)); 166 + if (!centerFreqs) return false; 167 + 168 + CQT_GenerateCenterFrequencies(centerFreqs, config->numBins, 169 + config->minFreq, config->maxFreq); 170 + 171 + // Create FFT configuration 172 + kiss_fftr_cfg fftCfg = kiss_fftr_alloc(config->fftSize, 0, NULL, NULL); 173 + if (!fftCfg) 174 + { 175 + free(centerFreqs); 176 + return false; 177 + } 178 + 179 + // Generate kernel for each CQT bin 180 + bool success = true; 181 + for (int i = 0; i < config->numBins; i++) 182 + { 183 + if (!generateSingleKernel(&kernels[i], fftCfg, config->fftSize, 184 + centerFreqs[i], config->minFreq, 185 + config->sampleRate, config->windowType, 186 + config->sparsityThreshold)) 187 + { 188 + // Clean up on failure 189 + for (int j = 0; j < i; j++) 190 + { 191 + free(kernels[j].real); 192 + free(kernels[j].imag); 193 + free(kernels[j].indices); 194 + } 195 + success = false; 196 + break; 197 + } 198 + } 199 + 200 + free(fftCfg); 201 + free(centerFreqs); 202 + return success; 203 + }
+31
src/ext/cqt_kernel.h
··· 1 + #pragma once 2 + 3 + #include <stdbool.h> 4 + #include "../cqtdata.h" 5 + 6 + // Window types for CQT kernels 7 + typedef enum { 8 + CQT_WINDOW_HAMMING = 0, 9 + CQT_WINDOW_GAUSSIAN 10 + } CqtWindowType; 11 + 12 + // CQT kernel configuration 13 + typedef struct { 14 + int fftSize; // FFT size (4096) 15 + int numBins; // Number of CQT bins (120) 16 + float minFreq; // Minimum frequency (20 Hz) 17 + float maxFreq; // Maximum frequency (20480 Hz) 18 + float sampleRate; // Sample rate (44100 Hz) 19 + CqtWindowType windowType; // Window function type 20 + float sparsityThreshold; // Threshold for sparse matrix (0.01) 21 + } CqtKernelConfig; 22 + 23 + // Generate CQT kernels for all bins 24 + // Returns true on success, false on failure 25 + bool CQT_GenerateKernels(CqtKernel* kernels, const CqtKernelConfig* config); 26 + 27 + // Calculate Q factor for given parameters 28 + float CQT_CalculateQ(int binsPerOctave); 29 + 30 + // Generate center frequencies for CQT bins 31 + void CQT_GenerateCenterFrequencies(float* frequencies, int numBins, float minFreq, float maxFreq);
+4
src/system/sdl/main.c
··· 24 24 #include "tools.h" 25 25 26 26 #include "ext/fft.h" 27 + #include "ext/cqt.h" 27 28 #include <stdlib.h> 28 29 #include <stdio.h> 29 30 #include <string.h> ··· 323 324 if (studio_config(platform.studio)->fft) 324 325 { 325 326 FFT_Open(studio_config(platform.studio)->fftcaptureplaybackdevices, studio_config(platform.studio)->fftdevice); 327 + // Initialize CQT when FFT is enabled 328 + CQT_Open(); 326 329 } 327 330 328 331 platform.audio.device = SDL_OpenAudioDevice(NULL, 0, &want, &platform.audio.spec, 0); ··· 2003 2006 if (studio_config(platform.studio)->fft) 2004 2007 { 2005 2008 FFT_Close(); 2009 + CQT_Close(); 2006 2010 } 2007 2011 } 2008 2012