Coverage Summary for Class: BpmMeter (com.vsevolodganin.clicktrack.metronome)

Class Class, % Method, % Branch, % Line, % Instruction, %
BpmMeter 0% (0/1) 0% (0/4) 0% (0/14) 0% (0/19) 0% (0/117)


 package com.vsevolodganin.clicktrack.metronome
 
 import com.vsevolodganin.clicktrack.model.BeatsPerMinute
 import me.tatarka.inject.annotations.Inject
 import kotlin.time.Clock
 import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.ExperimentalTime
 
 @Inject
 class BpmMeter {
     private val samples: Array<Long?> = Array(HISTORY_SIZE) { null }
     private var index: Int = 0
 
     fun addTap() {
         val now = now()
         samples[index] = now
         index = (index + 1) % HISTORY_SIZE
     }
 
     fun calculateBpm(): BeatsPerMinute? {
         val now = now()
         val applicableSamples = samples
             .asSequence()
             .filterNotNull()
             .filter { now - it < TIME_WINDOW_SIZE_MILLIS }
             .toList()
             .takeIf { it.size >= MIN_COUNT_TO_DERIVE_BPM }
             ?: return null
         val minTime = applicableSamples.minOrNull() ?: return null
         val maxTime = applicableSamples.maxOrNull() ?: return null
         return BeatsPerMinute(
             beatCount = applicableSamples.size - 1,
             timelapse = (maxTime - minTime).milliseconds,
         )
     }
 
     @OptIn(ExperimentalTime::class)
     private fun now() = Clock.System.now().toEpochMilliseconds()
 }
 
 private const val TIME_WINDOW_SIZE_MILLIS = 3000L
 private const val HISTORY_SIZE = 10
 private const val MIN_COUNT_TO_DERIVE_BPM = 3