| 740 | // decimation filter removes high-frequency content before bass analysis. |
| 741 | |
| 742 | void initHybrid(int samples, int bands, float fmin, float fmax, int sr) { |
| 743 | float logRatio = logf(fmax / fmin); |
| 744 | |
| 745 | // Log-spaced center frequencies for all bins |
| 746 | FASTLED_STACK_ARRAY(float, centerFreqs, bands); |
| 747 | for (int i = 0; i < bands; i++) { |
| 748 | centerFreqs[i] = |
| 749 | fmin * expf(logRatio * static_cast<float>(i) / |
| 750 | static_cast<float>(bands - 1)); |
| 751 | } |
| 752 | |
| 753 | // 3-tier split at octave boundaries: |
| 754 | // bass/mid at fmin*4 (~698 Hz, 2 octaves above fmin) |
| 755 | // mid/upper at fmin*8 (~1397 Hz, 3 octaves above fmin) |
| 756 | float bassMidFreq = fmin * 4.0f; |
| 757 | float midUpperFreq = fmin * 8.0f; |
| 758 | |
| 759 | // Clamp splits to valid range |
| 760 | if (midUpperFreq >= fmax) midUpperFreq = fmax * 0.5f; |
| 761 | if (bassMidFreq >= midUpperFreq) bassMidFreq = midUpperFreq * 0.5f; |
| 762 | |
| 763 | // Find split bin indices |
| 764 | mHybridSplitBin = 0; |
| 765 | for (int i = 0; i < bands; i++) { |
| 766 | if (centerFreqs[i] < bassMidFreq) |
| 767 | mHybridSplitBin = i + 1; |
| 768 | } |
| 769 | mHybridMidSplitBin = mHybridSplitBin; |
| 770 | for (int i = mHybridSplitBin; i < bands; i++) { |
| 771 | if (centerFreqs[i] < midUpperFreq) |
| 772 | mHybridMidSplitBin = i + 1; |
| 773 | } |
| 774 | |
| 775 | // Ensure each tier has at least 1 bin |
| 776 | if (mHybridSplitBin < 1) mHybridSplitBin = 1; |
| 777 | if (mHybridMidSplitBin <= mHybridSplitBin) |
| 778 | mHybridMidSplitBin = mHybridSplitBin + 1; |
| 779 | if (mHybridMidSplitBin >= bands) |
| 780 | mHybridMidSplitBin = bands - 1; |
| 781 | |
| 782 | // LOG_REBIN bin edges (shared by all three tiers) |
| 783 | mLogBinEdges.resize(bands + 1); |
| 784 | if (bands <= 1) { |
| 785 | mLogBinEdges[0] = fmin; |
| 786 | mLogBinEdges[1] = fmax; |
| 787 | } else { |
| 788 | float denom = 2.0f * static_cast<float>(bands - 1); |
| 789 | mLogBinEdges[0] = fmin * expf(-logRatio / denom); |
| 790 | for (int i = 1; i < bands; i++) { |
| 791 | mLogBinEdges[i] = |
| 792 | fmin * expf(logRatio * |
| 793 | (2.0f * static_cast<float>(i) - 1.0f) / denom); |
| 794 | } |
| 795 | mLogBinEdges[bands] = fmax * expf(logRatio / denom); |
| 796 | } |
| 797 | |
| 798 | // Clamp top bin edge to Nyquist |
| 799 | float nyquist = static_cast<float>(sr) / 2.0f; |