| 206 | } |
| 207 | |
| 208 | MultibandAccent Backbeat::calculateMultibandAccent(const fft::Bins& fft) { |
| 209 | MultibandAccent accent; |
| 210 | |
| 211 | // Define band ranges for 16-bin CQ log-spaced fft::FFT |
| 212 | // Bass: bins 0-3 (~175-400 Hz) - kick drum fundamentals |
| 213 | // Mid: bins 4-10 (~460-2100 Hz) - snare fundamental and harmonics |
| 214 | // High: bins 11-15 (~2450-4698 Hz) - hi-hats, cymbals |
| 215 | |
| 216 | size numBins = fft.raw().size(); |
| 217 | if (numBins == 0) { |
| 218 | accent = {0.0f, 0.0f, 0.0f, 0.0f}; |
| 219 | return accent; |
| 220 | } |
| 221 | |
| 222 | // Calculate current energy for each band |
| 223 | float bassEnergy = 0.0f; |
| 224 | float midEnergy = 0.0f; |
| 225 | float highEnergy = 0.0f; |
| 226 | |
| 227 | // Adjust band ranges based on actual bin count |
| 228 | size bassEnd = fl::min(static_cast<size>(4), numBins); |
| 229 | size midStart = bassEnd; |
| 230 | size midEnd = fl::min(static_cast<size>(11), numBins); |
| 231 | size highStart = midEnd; |
| 232 | size highEnd = numBins; |
| 233 | |
| 234 | // Sum energy in each band |
| 235 | for (size i = 0; i < bassEnd; i++) { |
| 236 | bassEnergy += fft.raw()[i]; |
| 237 | } |
| 238 | for (size i = midStart; i < midEnd; i++) { |
| 239 | midEnergy += fft.raw()[i]; |
| 240 | } |
| 241 | for (size i = highStart; i < highEnd; i++) { |
| 242 | highEnergy += fft.raw()[i]; |
| 243 | } |
| 244 | |
| 245 | // Normalize by bin count |
| 246 | bassEnergy /= static_cast<float>(bassEnd); |
| 247 | if (midEnd > midStart) { |
| 248 | midEnergy /= static_cast<float>(midEnd - midStart); |
| 249 | } |
| 250 | if (highEnd > highStart) { |
| 251 | highEnergy /= static_cast<float>(highEnd - highStart); |
| 252 | } |
| 253 | |
| 254 | // Calculate accent strength for each band (current vs previous) |
| 255 | // Using logarithmic ratio with normalization |
| 256 | const float epsilon = 1e-6f; |
| 257 | const float maxRatio = 10.0f; // Maximum expected energy increase |
| 258 | |
| 259 | float bassRatio = (mPreviousAccent.bass > epsilon) |
| 260 | ? (bassEnergy / mPreviousAccent.bass) |
| 261 | : 1.0f; |
| 262 | float midRatio = (mPreviousAccent.mid > epsilon) |
| 263 | ? (midEnergy / mPreviousAccent.mid) |
| 264 | : 1.0f; |
| 265 | float highRatio = (mPreviousAccent.high > epsilon) |