diff --git a/app/src/main/java/com/linc/audiowaveform/sample/ui/screen/waveform/AudioWaveformScreen.kt b/app/src/main/java/com/linc/audiowaveform/sample/ui/screen/waveform/AudioWaveformScreen.kt index 88ee646..c6ecf17 100644 --- a/app/src/main/java/com/linc/audiowaveform/sample/ui/screen/waveform/AudioWaveformScreen.kt +++ b/app/src/main/java/com/linc/audiowaveform/sample/ui/screen/waveform/AudioWaveformScreen.kt @@ -103,28 +103,6 @@ fun AudioWaveformScreen( scrollEnabled = true } ) - - AudioWaveform( - modifier = Modifier.fillMaxWidth(), - style = Fill, - waveformAlignment = WaveformAlignment.Center, - amplitudeType = AmplitudeType.Avg, - progressBrush = SolidColor(Color.Magenta), - waveformBrush = SolidColor(Color.LightGray), - spikeWidth = 4.dp, - spikePadding = 2.dp, - spikeRadius = 4.dp, - progress = uiState.progress, - amplitudes = uiState.amplitudes, - onProgressChange = { - scrollEnabled = false - onProgressChange(it) - }, - onProgressChangeFinished = { - scrollEnabled = true - } - ) - Divider(modifier = Modifier.padding(vertical = 8.dp)) LabelSlider( text = stringResource(id = R.string.spike_width), diff --git a/audiowaveform/src/main/java/com/linc/audiowaveform/AudioWaveform.kt b/audiowaveform/src/main/java/com/linc/audiowaveform/AudioWaveform.kt index aced680..e23f6d1 100644 --- a/audiowaveform/src/main/java/com/linc/audiowaveform/AudioWaveform.kt +++ b/audiowaveform/src/main/java/com/linc/audiowaveform/AudioWaveform.kt @@ -35,6 +35,7 @@ private const val MaxProgress: Float = 1F private const val MinSpikeHeight: Float = 1F private const val DefaultGraphicsLayerAlpha: Float = 0.99F +private const val AmplitudeMultiplier: Float = 2F @OptIn(ExperimentalComposeUiApi::class) @Composable @@ -61,8 +62,8 @@ fun AudioWaveform( val _spikeTotalWidth = remember(spikeWidth, spikePadding) { _spikeWidth + _spikePadding } var canvasSize by remember { mutableStateOf(Size(0f, 0f)) } var spikes by remember { mutableStateOf(0F) } - val pixelAmplitudes = remember(amplitudes, spikes, amplitudeType) { - getPixelAmplitudes( + val spikesAmplitudes = remember(amplitudes, spikes, amplitudeType) { + getSpikesAmplitudes( amplitudeType = amplitudeType, amplitudes = amplitudes, spikes = spikes.toInt(), @@ -95,7 +96,7 @@ fun AudioWaveform( ) { canvasSize = size spikes = size.width / _spikeTotalWidth.toPx() - pixelAmplitudes.forEachIndexed { index, amplitude -> + spikesAmplitudes.forEachIndexed { index, amplitude -> drawRoundRect( brush = waveformBrush, topLeft = Offset( @@ -125,23 +126,25 @@ fun AudioWaveform( } } -private fun getPixelAmplitudes( +private fun getSpikesAmplitudes( amplitudeType: AmplitudeType, amplitudes: List, spikes: Int, minHeight: Float, maxHeight: Float ): List { - return when { - amplitudes.isEmpty() || spikes == 0 -> List(spikes) { minHeight } - amplitudes.count() < spikes -> amplitudes.map(Int::toFloat) - else -> amplitudes.chunked(amplitudes.count() / spikes) - .map { - when(amplitudeType) { - AmplitudeType.Avg -> it.average() - AmplitudeType.Max -> it.max() - AmplitudeType.Min -> it.min() - }.toFloat().times(2).coerceIn(minHeight, maxHeight) - } + if(amplitudes.isEmpty() || spikes == 0) { + return List(spikes) { minHeight } + } + if(amplitudes.count() < spikes) { + return amplitudes.map(Int::toFloat) } + return amplitudes.map(Int::toFloat) + .chunkedToSize(spikes) { + when(amplitudeType) { + AmplitudeType.Avg -> it.average() + AmplitudeType.Max -> it.max() + AmplitudeType.Min -> it.min() + }.toFloat().times(AmplitudeMultiplier).coerceIn(minHeight, maxHeight) + } } \ No newline at end of file diff --git a/audiowaveform/src/main/java/com/linc/audiowaveform/Utils.kt b/audiowaveform/src/main/java/com/linc/audiowaveform/Utils.kt new file mode 100644 index 0000000..a140276 --- /dev/null +++ b/audiowaveform/src/main/java/com/linc/audiowaveform/Utils.kt @@ -0,0 +1,21 @@ +package com.linc.audiowaveform + +import kotlin.math.ceil +import kotlin.math.roundToInt + +internal fun Iterable.chunkedToSize(size: Int, transform: (List) -> T): List { + val chunkSize = count() / size + val remainder = count() % size + val remainderIndex = ceil(count().safeDiv(remainder)).roundToInt() + val chunkIteration = filterIndexed { index, _ -> + remainderIndex == 0 || index % remainderIndex != 0 + }.chunked(chunkSize, transform) + return when (size) { + chunkIteration.count() -> chunkIteration + else -> chunkIteration.chunkedToSize(size, transform) + } +} + +internal fun Int.safeDiv(value: Int): Float { + return if(value == 0) return 0F else this / value.toFloat() +} \ No newline at end of file