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/111)
|
PrimitiveAudioMonoRenderer$AudioData |
0%
(0/1)
|
|
0%
(0/3)
|
0%
(0/12)
|
PrimitiveAudioMonoRenderer$renderToMonoSamples$1$1 |
0%
(0/1)
|
0%
(0/4)
|
0%
(0/9)
|
0%
(0/146)
|
Total |
0%
(0/6)
|
0%
(0/12)
|
0%
(0/42)
|
0%
(0/269)
|
package com.vsevolodganin.clicktrack.primitiveaudio
import com.vsevolodganin.clicktrack.model.ClickSoundSource
import com.vsevolodganin.clicktrack.player.PlayerEvent
import com.vsevolodganin.clicktrack.soundlibrary.SoundSourceProvider
import me.tatarka.inject.annotations.Assisted
import me.tatarka.inject.annotations.Inject
@Inject
class PrimitiveAudioMonoRenderer(
@Assisted private val targetSampleRate: Int,
private val primitiveAudioProvider: PrimitiveAudioProvider,
) {
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 = Resampler(
channelCount = audioData.channelCount,
inputRate = audioData.sampleRate,
outputRate = targetSampleRate,
quality = Resampler.Quality.Medium,
).resample(audioData.samples),
channelCount = audioData.channelCount,
)
}
}
private class AudioData(
val samples: FloatArray,
val channelCount: Int,
)
}