Coverage Summary for Class: PrimitiveAudioMonoRenderer (com.vsevolodganin.clicktrack.primitiveaudio)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| PrimitiveAudioMonoRenderer |
0%
(0/4)
|
0%
(0/8)
|
0%
(0/30)
|
0%
(0/114)
|
| PrimitiveAudioMonoRenderer$AudioData |
0%
(0/1)
|
|
0%
(0/3)
|
0%
(0/12)
|
| PrimitiveAudioMonoRenderer$Factory |
|
| PrimitiveAudioMonoRenderer$Factory$Impl |
|
| PrimitiveAudioMonoRenderer$Factory$Impl$Companion |
|
| PrimitiveAudioMonoRenderer$MetroFactory |
0%
(0/1)
|
|
0%
(0/1)
|
0%
(0/12)
|
| PrimitiveAudioMonoRenderer$MetroFactory$Companion |
0%
(0/1)
|
|
0%
(0/1)
|
0%
(0/12)
|
| PrimitiveAudioMonoRenderer$renderToMonoSamples$1$1 |
0%
(0/1)
|
0%
(0/4)
|
0%
(0/9)
|
0%
(0/146)
|
| Total |
0%
(0/8)
|
0%
(0/12)
|
0%
(0/44)
|
0%
(0/296)
|
package com.vsevolodganin.clicktrack.primitiveaudio
import com.vsevolodganin.clicktrack.model.ClickSoundSource
import com.vsevolodganin.clicktrack.player.PlayerEvent
import com.vsevolodganin.clicktrack.soundlibrary.SoundSourceProvider
import com.vsevolodganin.clicktrack.utils.resampler.MultiChannelResampler
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
@AssistedInject
class PrimitiveAudioMonoRenderer(
@Assisted private val targetSampleRate: Int,
private val primitiveAudioProvider: PrimitiveAudioProvider,
) {
@AssistedFactory
fun interface Factory {
fun create(targetSampleRate: Int): PrimitiveAudioMonoRenderer
}
private val preparedSamples = mutableMapOf<ClickSoundSource, AudioData?>()
fun renderToMonoSamples(events: Sequence<PlayerEvent>, soundSourceProvider: SoundSourceProvider): Sequence<Float> {
return events
.flatMap { playerEvent ->
val audioData = playerEvent.soundType
?.let(soundSourceProvider::provide)
?.let(::getAudioData)
?: AudioData(samples = FloatArray(0), channelCount = 1)
val audioFrames = audioData.samples
.asSequence()
.chunked(audioData.channelCount)
.toList()
val totalFramesNumber = convertDurationToFramesNumber(
duration = playerEvent.duration,
sampleRate = targetSampleRate,
channelCount = audioData.channelCount,
)
sequence {
audioFrames
.take(totalFramesNumber)
.forEach { frame ->
yield(frame.sumOf { sample -> sample.toDouble() / frame.size }.toFloat())
}
val restSilenceFramesNumber = maxOf(0, totalFramesNumber - audioFrames.size)
repeat(restSilenceFramesNumber) {
yield(0f)
}
}
}
}
private fun getAudioData(soundSource: ClickSoundSource): AudioData? {
return preparedSamples.getOrPut(soundSource) {
val audioData = primitiveAudioProvider.get(soundSource) ?: return@getOrPut null
AudioData(
samples = MultiChannelResampler(
channelCount = audioData.channelCount,
inputRate = audioData.sampleRate,
outputRate = targetSampleRate,
quality = MultiChannelResampler.Quality.Medium,
).resample(audioData.samples),
channelCount = audioData.channelCount,
)
}
}
private class AudioData(
val samples: FloatArray,
val channelCount: Int,
)
}