Helper: Run detector on a beat sequence and collect metrics enableLogging: Print detailed confidence and detection info for analysis warmupMeasures: Number of measures to run before collecting metrics (default: 1)
| 132 | // enableLogging: Print detailed confidence and detection info for analysis |
| 133 | // warmupMeasures: Number of measures to run before collecting metrics (default: 1) |
| 134 | DetectionMetrics runDetectorTest(audio::detector::Downbeat& detector, |
| 135 | const vector<GroundTruthMarker>& groundTruth, |
| 136 | float accentMultiplier = 1.0f, |
| 137 | int timingJitterMs = 0, |
| 138 | bool enableLogging = false, |
| 139 | int warmupMeasures = 1) { |
| 140 | DetectionMetrics metrics; |
| 141 | u32 timestamp = 0; |
| 142 | u32 frameInterval = 23; // ~43 fps |
| 143 | |
| 144 | // Track confidence values for analysis |
| 145 | vector<float> confidenceValues; |
| 146 | |
| 147 | // Calculate beat interval from ground truth (if available) |
| 148 | u32 beatInterval = 500; // Default: 120 BPM |
| 149 | if (groundTruth.size() >= 2) { |
| 150 | beatInterval = groundTruth[1].timestamp - groundTruth[0].timestamp; |
| 151 | if (enableLogging) { |
| 152 | FL_MESSAGE("Detected beat interval from ground truth: " << beatInterval << "ms (" << (60000.0f / beatInterval) << " BPM)"); |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | // Phase 1: Warm-up period to let detector establish rhythm |
| 157 | // This allows audio::detector::Beat and audio::detector::Downbeat to build up history |
| 158 | if (warmupMeasures > 0 && !groundTruth.empty()) { |
| 159 | int beatsPerMeasure = detector.getBeatsPerMeasure(); |
| 160 | int warmupBeats = warmupMeasures * beatsPerMeasure; |
| 161 | |
| 162 | if (enableLogging) { |
| 163 | FL_MESSAGE("Warm-up phase: " << warmupBeats << " beats (" << warmupMeasures << " measures)"); |
| 164 | } |
| 165 | |
| 166 | for (int beat = 0; beat < warmupBeats; beat++) { |
| 167 | bool isDownbeat = (beat % beatsPerMeasure == 0); |
| 168 | u32 beatStartTime = timestamp; |
| 169 | u32 beatEndTime = timestamp + beatInterval; |
| 170 | |
| 171 | // Generate frames to fill the beat interval |
| 172 | // Use fewer frames per beat: 1 quiet + 1 onset (sufficient for spectral flux) |
| 173 | u32 beatMidpoint = beatStartTime + beatInterval / 2; |
| 174 | |
| 175 | // Single quiet frame |
| 176 | auto quietContext = createMockAudioContext(beatMidpoint, false, true, accentMultiplier); |
| 177 | detector.update(quietContext); |
| 178 | |
| 179 | // Single beat onset frame |
| 180 | auto beatContext = createMockAudioContext(beatEndTime - frameInterval, isDownbeat, false, accentMultiplier); |
| 181 | detector.update(beatContext); |
| 182 | |
| 183 | // Ensure we're exactly at the next beat boundary |
| 184 | timestamp = beatEndTime; |
| 185 | } |
| 186 | |
| 187 | if (enableLogging) { |
| 188 | FL_MESSAGE("Warm-up complete at t=" << timestamp << ", starting metric collection"); |
| 189 | } |
| 190 | } |
| 191 |
no test coverage detected