this repo has no description
0
fork

Configure Feed

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

fix, bindings

alice e34bc5f2 06837484

+259 -42
+16 -7
CLAUDE.md
··· 112 112 113 113 #### API Functions 114 114 ```lua 115 - value = fft(bin) -- Get raw FFT magnitude for bin (0-1023) 116 - value = ffts(bin) -- Get smoothed FFT magnitude for bin (0-1023) 115 + value = fft(bin) -- Get peak-normalized FFT magnitude for bin (0-1023) 116 + value = ffts(bin) -- Get smoothed peak-normalized FFT magnitude for bin (0-1023) 117 117 ``` 118 + 119 + **Note:** Both FFT functions return peak-normalized values (auto-gain controlled), not raw magnitudes. 118 120 119 121 ### VQT Implementation 120 122 ··· 147 149 148 150 #### API Functions 149 151 ```lua 150 - value = vqt(bin) -- Get raw VQT magnitude for bin (0-119) 152 + value = vqt(bin) -- Get peak-normalized VQT magnitude for bin (0-119) 151 153 -- Note mapping: Bin = octave * 12 + note 152 154 -- Note: C=0, C#=1, D=2, D#=3, E=4, F=5, F#=6, G=7, G#=8, A=9, A#=10, B=11 153 155 ``` 156 + 157 + **Note:** VQT also returns peak-normalized values (auto-gain controlled), not raw magnitudes. 154 158 155 159 ### FFT vs VQT Comparison 156 160 ··· 206 210 ### Completed Features 207 211 - **FFT**: 1024 bins with exact original behavior preserved 208 212 - **VQT**: 120 bins with Variable-Q implementation 209 - - **Spectral Whitening**: Per-bin normalization for VQT 213 + - **Spectral Whitening**: Per-bin normalization for VQT (removed due to spreading issues) 210 214 - **Shared Audio Buffer**: Automatic sizing for both FFT and VQT 211 - - **Lua API**: `fft()`, `ffts()`, `vqt()` functions implemented 215 + - **API Functions**: `fft()`, `ffts()`, `vqt()`, `vqts()` implemented for all supported languages 216 + - **Peak Normalization**: Both FFT and VQT use auto-gain control 212 217 213 218 ### Configuration Options 214 219 - `VQT_FFT_SIZE`: Default 8192 (configurable in vqtdata.h) ··· 226 231 ## Future Enhancements 227 232 228 233 ### Additional API Functions 229 - - `vqts(bin)`: Smoothed VQT data 230 - - `vqto(octave, note)`: Raw VQT by musical note 234 + - `vqts(bin)`: Smoothed VQT data (already implemented) 235 + - `vqto(octave, note)`: VQT by musical note 231 236 - `vqtos(octave, note)`: Smoothed VQT by musical note 237 + - `fftr(bin)`: Raw (non-normalized) FFT magnitude 238 + - `fftrs(bin)`: Raw smoothed FFT magnitude 239 + - `vqtr(bin)`: Raw (non-normalized) VQT magnitude 240 + - `vqtrs(bin)`: Raw smoothed VQT magnitude 232 241 233 242 ### Signal Processing Enhancements 234 243 - **Adaptive Thresholding**: Dynamic noise floor removal
+31 -1
src/api.h
··· 822 822 1, \ 823 823 0, \ 824 824 double, \ 825 - tic_mem*, s32 startFreq, s32 endFreq) 825 + tic_mem*, s32 startFreq, s32 endFreq) \ 826 + \ 827 + \ 828 + macro(vqt, \ 829 + "vqt(bin)", \ 830 + \ 831 + "Get Variable-Q Transform magnitude for a specific frequency bin.\n" \ 832 + "VQT provides 120 bins (0-119) with logarithmic frequency spacing for musical analysis.\n" \ 833 + "Each bin corresponds to a musical note: bin = octave * 12 + note\n" \ 834 + "where octave is 0-9 and note is 0-11 (C=0, C#=1, D=2, ..., B=11).\n" \ 835 + "Returns a value roughly 0..1 based on the intensity at that frequency.", \ 836 + 1, \ 837 + 1, \ 838 + 0, \ 839 + double, \ 840 + tic_mem*, s32 bin) \ 841 + \ 842 + \ 843 + macro(vqts, \ 844 + "vqts(bin)", \ 845 + \ 846 + "Get smoothed Variable-Q Transform magnitude for a specific frequency bin.\n" \ 847 + "VQT provides 120 bins (0-119) with logarithmic frequency spacing for musical analysis.\n" \ 848 + "Each bin corresponds to a musical note: bin = octave * 12 + note\n" \ 849 + "where octave is 0-9 and note is 0-11 (C=0, C#=1, D=2, ..., B=11).\n" \ 850 + "Returns a smoothed value roughly 0..1 based on the intensity at that frequency.", \ 851 + 1, \ 852 + 1, \ 853 + 0, \ 854 + double, \ 855 + tic_mem*, s32 bin) 826 856 827 857 #define TIC_API_DEF(name, _, __, ___, ____, _____, ret, ...) ret tic_api_##name(__VA_ARGS__); 828 858 TIC_API_LIST(TIC_API_DEF)
+25 -1
src/api/janet.c
··· 81 81 static Janet janet_fset(int32_t argc, Janet* argv); 82 82 static Janet janet_fft(int32_t argc, Janet* argv); 83 83 static Janet janet_ffts(int32_t argc, Janet* argv); 84 + static Janet janet_vqt(int32_t argc, Janet* argv); 85 + static Janet janet_vqts(int32_t argc, Janet* argv); 84 86 85 87 static void closeJanet(tic_mem* tic); 86 88 static bool initJanet(tic_mem* tic, const char* code); ··· 146 148 {"fset", janet_fset, NULL}, 147 149 {"fft", janet_fft, NULL}, 148 150 {"ffts", janet_ffts, NULL}, 151 + {"vqt", janet_vqt, NULL}, 152 + {"vqts", janet_vqts, NULL}, 149 153 {NULL, NULL, NULL} 150 154 }; 151 155 ··· 1081 1085 if (argc >= 2) end_freq = janet_getinteger(argv, 1); 1082 1086 1083 1087 tic_core* core = getJanetMachine(); tic_mem* tic = (tic_mem*)core; 1084 - return janet_wrap_number(core->api.fft(tic, start_freq, end_freq)); 1088 + return janet_wrap_number(core->api.ffts(tic, start_freq, end_freq)); 1089 + } 1090 + 1091 + static Janet janet_vqt(int32_t argc, Janet* argv) 1092 + { 1093 + janet_fixarity(argc, 1); 1094 + 1095 + s32 bin = janet_getinteger(argv, 0); 1096 + 1097 + tic_core* core = getJanetMachine(); tic_mem* tic = (tic_mem*)core; 1098 + return janet_wrap_number(core->api.vqt(tic, bin)); 1099 + } 1100 + 1101 + static Janet janet_vqts(int32_t argc, Janet* argv) 1102 + { 1103 + janet_fixarity(argc, 1); 1104 + 1105 + s32 bin = janet_getinteger(argv, 0); 1106 + 1107 + tic_core* core = getJanetMachine(); tic_mem* tic = (tic_mem*)core; 1108 + return janet_wrap_number(core->api.vqts(tic, bin)); 1085 1109 } 1086 1110 1087 1111 /* ***************** */
+16
src/api/js.c
··· 1032 1032 return JS_NewFloat64(ctx, core->api.ffts(tic, start_freq, end_freq)); 1033 1033 } 1034 1034 1035 + static JSValue js_vqt(JSContext *ctx, JSValueConst this_val, s32 argc, JSValueConst *argv) 1036 + { 1037 + tic_core* core = getCore(ctx); tic_mem* tic = (tic_mem*)core; 1038 + s32 bin = getInteger(ctx, argv[0]); 1039 + 1040 + return JS_NewFloat64(ctx, core->api.vqt(tic, bin)); 1041 + } 1042 + 1043 + static JSValue js_vqts(JSContext *ctx, JSValueConst this_val, s32 argc, JSValueConst *argv) 1044 + { 1045 + tic_core* core = getCore(ctx); tic_mem* tic = (tic_mem*)core; 1046 + s32 bin = getInteger(ctx, argv[0]); 1047 + 1048 + return JS_NewFloat64(ctx, core->api.vqts(tic, bin)); 1049 + } 1050 + 1035 1051 static bool initJavascript(tic_mem* tic, const char* code) 1036 1052 { 1037 1053 closeJavascript(tic);
+7 -32
src/api/luaapi.c
··· 21 21 // SOFTWARE. 22 22 23 23 #include "core/core.h" 24 - #include "vqtdata.h" 24 + #include "ext/vqt.h" 25 25 26 26 #include <stdlib.h> 27 27 #include <lua.h> ··· 1598 1598 1599 1599 static s32 lua_vqt(lua_State* lua) 1600 1600 { 1601 + tic_core* core = getLuaCore(lua); 1602 + tic_mem* tic = (tic_mem*)core; 1601 1603 s32 top = lua_gettop(lua); 1602 1604 1603 1605 if (top >= 1) 1604 1606 { 1605 1607 s32 bin = getLuaNumber(lua, 1); 1606 1608 1607 - // Validate bin range 1608 - if (bin < 0 || bin >= VQT_BINS) 1609 - { 1610 - luaL_error(lua, "vqt bin out of range (0-%d)\n", VQT_BINS - 1); 1611 - return 0; 1612 - } 1613 - 1614 - #ifdef TIC80_FFT_UNSUPPORTED 1615 - lua_pushnumber(lua, 0.0); 1616 - #else 1617 - // Return raw VQT data (normalized but unsmoothed) 1618 - lua_pushnumber(lua, vqtData[bin] / vqtPeakSmoothValue); 1619 - #endif 1609 + lua_pushnumber(lua, tic_api_vqt(tic, bin)); 1620 1610 return 1; 1621 1611 } 1622 1612 ··· 1626 1616 1627 1617 static s32 lua_vqts(lua_State* lua) 1628 1618 { 1619 + tic_core* core = getLuaCore(lua); 1620 + tic_mem* tic = (tic_mem*)core; 1629 1621 s32 top = lua_gettop(lua); 1630 1622 1631 1623 if (top >= 1) 1632 1624 { 1633 1625 s32 bin = getLuaNumber(lua, 1); 1634 1626 1635 - // Validate bin range 1636 - if (bin < 0 || bin >= VQT_BINS) 1637 - { 1638 - luaL_error(lua, "vqts bin out of range (0-%d)\n", VQT_BINS - 1); 1639 - return 0; 1640 - } 1641 - 1642 - #ifdef TIC80_FFT_UNSUPPORTED 1643 - lua_pushnumber(lua, 0.0); 1644 - #else 1645 - // Return smoothed VQT data (smoothed + normalized) 1646 - // This gives more stable values 1647 - lua_pushnumber(lua, vqtNormalizedData[bin]); 1648 - #endif 1627 + lua_pushnumber(lua, tic_api_vqts(tic, bin)); 1649 1628 return 1; 1650 1629 } 1651 1630 ··· 1707 1686 1708 1687 registerLuaFunction(core, lua_dofile, "dofile"); 1709 1688 registerLuaFunction(core, lua_loadfile, "loadfile"); 1710 - 1711 - // Register VQT functions 1712 - registerLuaFunction(core, lua_vqt, "vqt"); 1713 - registerLuaFunction(core, lua_vqts, "vqts"); 1714 1689 } 1715 1690 1716 1691 void luaapi_close(tic_mem* tic)
+38
src/api/mruby.c
··· 568 568 } 569 569 } 570 570 571 + static mrb_value mrb_vqt(mrb_state* mrb, mrb_value self) 572 + { 573 + mrb_int bin; 574 + mrb_int argc = mrb_get_args(mrb, "i", &bin); 575 + 576 + tic_core* core = getMRubyMachine(mrb); 577 + tic_mem* tic = (tic_mem*)core; 578 + 579 + if (argc == 0) 580 + { 581 + mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid params, vqt(bin)\n"); 582 + return mrb_nil_value(); 583 + } 584 + else 585 + { 586 + return mrb_float_value(mrb, core->api.vqt(tic, bin)); 587 + } 588 + } 589 + 590 + static mrb_value mrb_vqts(mrb_state* mrb, mrb_value self) 591 + { 592 + mrb_int bin; 593 + mrb_int argc = mrb_get_args(mrb, "i", &bin); 594 + 595 + tic_core* core = getMRubyMachine(mrb); 596 + tic_mem* tic = (tic_mem*)core; 597 + 598 + if (argc == 0) 599 + { 600 + mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid params, vqts(bin)\n"); 601 + return mrb_nil_value(); 602 + } 603 + else 604 + { 605 + return mrb_float_value(mrb, core->api.vqts(tic, bin)); 606 + } 607 + } 608 + 571 609 typedef struct 572 610 { 573 611 mrb_state* mrb;
+22
src/api/scheme.c
··· 772 772 return s7_make_real(sc, core->api.ffts(tic, start_freq, end_freq)); 773 773 } 774 774 775 + s7_pointer scheme_vqt(s7_scheme* sc, s7_pointer args) 776 + { 777 + // vqt(int bin) -> float_value 778 + tic_core* core = getSchemeCore(sc); 779 + tic_mem* tic = (tic_mem*)core; 780 + const int argn = s7_list_length(sc, args); 781 + const s32 bin = argn > 0 ? s7_integer(s7_car(args)) : 0; 782 + 783 + return s7_make_real(sc, core->api.vqt(tic, bin)); 784 + } 785 + 786 + s7_pointer scheme_vqts(s7_scheme* sc, s7_pointer args) 787 + { 788 + // vqts(int bin) -> float_value 789 + tic_core* core = getSchemeCore(sc); 790 + tic_mem* tic = (tic_mem*)core; 791 + const int argn = s7_list_length(sc, args); 792 + const s32 bin = argn > 0 ? s7_integer(s7_car(args)) : 0; 793 + 794 + return s7_make_real(sc, core->api.vqts(tic, bin)); 795 + } 796 + 775 797 static void initAPI(tic_core* core) 776 798 { 777 799 s7_scheme* sc = core->currentVM;
+40
src/api/squirrel.c
··· 1581 1581 return 0; 1582 1582 } 1583 1583 1584 + static SQInteger squirrel_vqt(HSQUIRRELVM vm) 1585 + { 1586 + tic_core* core = getSquirrelCore(vm); 1587 + tic_mem* tic = (tic_mem*)core; 1588 + 1589 + SQInteger top = sq_gettop(vm); 1590 + 1591 + if (top >= 2) 1592 + { 1593 + double bin = getSquirrelNumber(vm, 2); 1594 + 1595 + sq_pushfloat(vm, (SQFloat)(core->api.vqt(tic, bin))); 1596 + return 1; 1597 + } 1598 + 1599 + sq_throwerror(vm, "invalid params, vqt(bin)\n"); 1600 + 1601 + return 0; 1602 + } 1603 + 1604 + static SQInteger squirrel_vqts(HSQUIRRELVM vm) 1605 + { 1606 + tic_core* core = getSquirrelCore(vm); 1607 + tic_mem* tic = (tic_mem*)core; 1608 + 1609 + SQInteger top = sq_gettop(vm); 1610 + 1611 + if (top >= 2) 1612 + { 1613 + double bin = getSquirrelNumber(vm, 2); 1614 + 1615 + sq_pushfloat(vm, (SQFloat)(core->api.vqts(tic, bin))); 1616 + return 1; 1617 + } 1618 + 1619 + sq_throwerror(vm, "invalid params, vqts(bin)\n"); 1620 + 1621 + return 0; 1622 + } 1623 + 1584 1624 static SQInteger squirrel_dofile(HSQUIRRELVM vm) 1585 1625 { 1586 1626 return sq_throwerror(vm, "unknown method: \"dofile\"\n");
+36
src/api/wren.c
··· 1507 1507 wrenError(vm, "invalid params, ffts(start_freq, end_freq)\n"); 1508 1508 } 1509 1509 1510 + static void wren_vqt(WrenVM* vm) 1511 + { 1512 + tic_core* core = getWrenCore(vm); 1513 + tic_mem* tic = (tic_mem*)core; 1514 + s32 top = wrenGetSlotCount(vm); 1515 + 1516 + if (top > 1) 1517 + { 1518 + double bin = getWrenNumber(vm, 1); 1519 + 1520 + wrenSetSlotDouble(vm, 0, core->api.vqt(tic, bin)); 1521 + return; 1522 + } 1523 + 1524 + wrenError(vm, "invalid params, vqt(bin)\n"); 1525 + } 1526 + 1527 + static void wren_vqts(WrenVM* vm) 1528 + { 1529 + tic_core* core = getWrenCore(vm); 1530 + tic_mem* tic = (tic_mem*)core; 1531 + s32 top = wrenGetSlotCount(vm); 1532 + 1533 + if (top > 1) 1534 + { 1535 + double bin = getWrenNumber(vm, 1); 1536 + 1537 + wrenSetSlotDouble(vm, 0, core->api.vqts(tic, bin)); 1538 + return; 1539 + } 1540 + 1541 + wrenError(vm, "invalid params, vqts(bin)\n"); 1542 + } 1543 + 1510 1544 static WrenForeignMethodFn foreignTicMethods(const char* signature) 1511 1545 { 1512 1546 if (strcmp(signature, "static TIC.btn()" ) == 0) return wren_btn; ··· 1620 1654 1621 1655 if (strcmp(signature, "static TIC.fft(_,_)" ) == 0) return wren_fft; 1622 1656 if (strcmp(signature, "static TIC.ffts(_,_)" ) == 0) return wren_ffts; 1657 + if (strcmp(signature, "static TIC.vqt(_)" ) == 0) return wren_vqt; 1658 + if (strcmp(signature, "static TIC.vqts(_)" ) == 0) return wren_vqts; 1623 1659 1624 1660 // internal functions 1625 1661 if (strcmp(signature, "static TIC.map_width__" ) == 0) return wren_map_width;
+26
src/ext/vqt.c
··· 1 + #include "api.h" 1 2 #include "vqt.h" 2 3 #include "vqt_kernel.h" 3 4 #include "../vqtdata.h" ··· 418 419 VQT_Cleanup(); 419 420 } 420 421 422 + // API functions for VQT 423 + double tic_api_vqt(tic_mem* memory, s32 bin) 424 + { 425 + // Validate bin range 426 + if (bin < 0 || bin >= VQT_BINS) 427 + return 0.0; 428 + 429 + // Return raw VQT data (normalized but unsmoothed) 430 + return vqtData[bin] / vqtPeakSmoothValue; 431 + } 432 + 433 + double tic_api_vqts(tic_mem* memory, s32 bin) 434 + { 435 + // Validate bin range 436 + if (bin < 0 || bin >= VQT_BINS) 437 + return 0.0; 438 + 439 + // Return smoothed VQT data (smoothed + normalized) 440 + return vqtNormalizedData[bin]; 441 + } 442 + 421 443 #else // TIC80_FFT_UNSUPPORTED 422 444 423 445 // Stub implementations when FFT is unsupported ··· 425 447 void VQT_ProcessAudio(void) {} 426 448 void VQT_Close(void) {} 427 449 void VQT_ApplyKernels(const float* fftReal, const float* fftImag) {} 450 + 451 + // API stubs when FFT is unsupported 452 + double tic_api_vqt(tic_mem* memory, s32 bin) { return 0.0; } 453 + double tic_api_vqts(tic_mem* memory, s32 bin) { return 0.0; } 428 454 429 455 #endif // TIC80_FFT_UNSUPPORTED
+2 -1
src/ext/vqt.h
··· 1 1 #pragma once 2 2 3 3 #include <stdbool.h> 4 + #include "tic80_types.h" 4 5 5 6 // Initialize VQT processing 6 7 // Returns true on success, false on failure ··· 13 14 void VQT_Close(void); 14 15 15 16 // Apply VQT kernels to FFT output 16 - void VQT_ApplyKernels(const float* fftReal, const float* fftImag); 17 + void VQT_ApplyKernels(const float* fftReal, const float* fftImag);