| 144 | } |
| 145 | |
| 146 | void Percussion::computeFeatures(const fft::Bins& fft) { |
| 147 | auto bins = fft.raw(); |
| 148 | int n = static_cast<int>(bins.size()); |
| 149 | if (n < 16) { |
| 150 | mBassToTotal = 0.0f; |
| 151 | mTrebleToTotal = 0.0f; |
| 152 | mClickRatio = 0.0f; |
| 153 | mTrebleFlatness = 0.0f; |
| 154 | mMidToTreble = 0.0f; |
| 155 | mSubBassProxy = 0.0f; |
| 156 | return; |
| 157 | } |
| 158 | |
| 159 | // Frequency-based band boundaries (range-agnostic). |
| 160 | // Bass: fmin–300 Hz (kick body, sub-bass) |
| 161 | // Mid: 300–2000 Hz (snare body, tom) |
| 162 | // Treble: 2000+ Hz (click transients, hi-hat, cymbals) |
| 163 | // Click: 1500–4500 Hz (kick beater attack, snare crack) |
| 164 | int bassCutBin = fl::max(1, fft.freqToBin(300.0f)); |
| 165 | int trebleCutBin = fl::max(bassCutBin + 1, fft.freqToBin(2000.0f)); |
| 166 | int clickLoBin = fl::max(bassCutBin, fft.freqToBin(1500.0f)); |
| 167 | int clickHiBin = fl::min(n - 1, fft.freqToBin(4500.0f)); |
| 168 | |
| 169 | float bassSum = 0.0f; |
| 170 | for (int i = 0; i <= bassCutBin; ++i) bassSum += bins[i]; |
| 171 | |
| 172 | float midSum = 0.0f; |
| 173 | for (int i = bassCutBin + 1; i < trebleCutBin; ++i) midSum += bins[i]; |
| 174 | |
| 175 | float trebleSum = 0.0f; |
| 176 | for (int i = trebleCutBin; i < n; ++i) trebleSum += bins[i]; |
| 177 | |
| 178 | // Click band: 1500–4500 Hz (kick click, snare crack) |
| 179 | float clickBandSum = 0.0f; |
| 180 | for (int i = clickLoBin; i <= clickHiBin; ++i) clickBandSum += bins[i]; |
| 181 | |
| 182 | // Upper-mid for click ratio denominator |
| 183 | float upperMidSum = midSum; |
| 184 | |
| 185 | float total = bassSum + midSum + trebleSum; |
| 186 | |
| 187 | // Bass/Total ratio |
| 188 | mBassToTotal = (total > 1e-6f) ? bassSum / total : 0.0f; |
| 189 | |
| 190 | // Treble/Total ratio |
| 191 | mTrebleToTotal = (total > 1e-6f) ? trebleSum / total : 0.0f; |
| 192 | |
| 193 | // Click ratio: treble click band vs upper-mid notch region |
| 194 | mClickRatio = (upperMidSum > 1e-6f) ? clickBandSum / upperMidSum : 0.0f; |
| 195 | |
| 196 | // Mid/Treble ratio |
| 197 | mMidToTreble = (trebleSum > 1e-6f) ? midSum / trebleSum : 0.0f; |
| 198 | |
| 199 | // Sub-bass proxy: lowest bin / first mid bin |
| 200 | int firstMidBin = bassCutBin + 1; |
| 201 | mSubBassProxy = (firstMidBin < n && bins[firstMidBin] > 1e-6f) |
| 202 | ? bins[0] / bins[firstMidBin] |
| 203 | : 0.0f; |