diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 9cd188cd9f..b77838951f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -964,7 +964,7 @@ class Account( mimeType: String?, hash: String, duration: Int, - waveform: List, + waveform: List, ) { signAndComputeBroadcast(VoiceEvent.build(url, mimeType, hash, duration, waveform)) } @@ -974,7 +974,7 @@ class Account( mimeType: String?, hash: String, duration: Int, - waveform: List, + waveform: List, replyTo: EventHintBundle, ) { signAndComputeBroadcast(VoiceReplyEvent.build(url, mimeType, hash, duration, waveform, replyTo)) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/VideoView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/VideoView.kt index 8b31f98922..ea0b3dd99d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/VideoView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/playback/composable/VideoView.kt @@ -47,7 +47,7 @@ import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag @Immutable class WaveformData( - val wave: List, + val wave: List, ) @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceMessageRecorder.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceMessageRecorder.kt index ba98a19c45..750251cbe8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceMessageRecorder.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/VoiceMessageRecorder.kt @@ -35,7 +35,7 @@ import java.io.File class RecordingResult( val file: File, val mimeType: String, - val amplitudes: List, + val amplitudes: List, val duration: Int, ) @@ -44,7 +44,7 @@ class VoiceMessageRecorder { private var outputFile: File? = null private var startTime: Long = 0 private var job: Job? = null - private var amplitudes: MutableList = mutableListOf() + private var amplitudes: MutableList = mutableListOf() private fun createRecorder(context: Context): MediaRecorder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { @@ -80,7 +80,7 @@ class VoiceMessageRecorder { job = scope.launch { while (recorder != null) { - amplitudes.add(((recorder?.maxAmplitude ?: 0) / 100).toInt()) + amplitudes.add(recorder?.maxAmplitude?.toFloat() ?: 0f) delay(1000) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/AudioWaveformReadOnly.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/AudioWaveformReadOnly.kt index 330dd51b20..b3f2c667e6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/AudioWaveformReadOnly.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/AudioWaveformReadOnly.kt @@ -20,6 +20,8 @@ */ package com.vitorpamplona.amethyst.ui.components +import android.R.attr.maxHeight +import android.R.attr.minHeight import androidx.compose.animation.core.AnimationSpec import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween @@ -48,6 +50,7 @@ import androidx.compose.ui.unit.coerceIn import androidx.compose.ui.unit.dp import com.linc.audiowaveform.model.AmplitudeType import com.linc.audiowaveform.model.WaveformAlignment +import com.vitorpamplona.amethyst.ui.components.toDrawableAmplitudes import kotlin.math.ceil import kotlin.math.roundToInt @@ -77,7 +80,7 @@ fun AudioWaveformReadOnly( spikeRadius: Dp = 2.dp, spikePadding: Dp = 2.dp, progress: Float = 0F, - amplitudes: List, + amplitudes: List, onProgressChange: (Float) -> Unit, ) { val backgroundColor = MaterialTheme.colorScheme.background @@ -101,6 +104,7 @@ fun AudioWaveformReadOnly( maxHeight = canvasSize.height.coerceAtLeast(MIN_SPIKE_HEIGHT), ) }.map { animateFloatAsState(it, spikeAnimationSpec).value } + Canvas( modifier = Modifier @@ -145,14 +149,13 @@ fun AudioWaveformReadOnly( } } -private fun List.toDrawableAmplitudes( +private fun List.toDrawableAmplitudes( amplitudeType: AmplitudeType, spikes: Int, minHeight: Float, maxHeight: Float, ): List { - val amplitudes = map(Int::toFloat) - if (amplitudes.isEmpty() || spikes == 0) { + if (this.isEmpty() || spikes == 0) { return List(spikes) { minHeight } } val transform = { data: List -> @@ -161,11 +164,10 @@ private fun List.toDrawableAmplitudes( AmplitudeType.Max -> data.max() AmplitudeType.Min -> data.min() }.toFloat() - .coerceIn(minHeight, maxHeight) } return when { - spikes > amplitudes.count() -> amplitudes.fillToSize(spikes, transform) - else -> amplitudes.chunkToSize(spikes, transform) + spikes > this.count() -> this.fillToSize(spikes, transform) + else -> this.chunkToSize(spikes, transform) }.normalize(minHeight, maxHeight) } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/audio/header/AudioHeaderEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/audio/header/AudioHeaderEvent.kt index 6173346971..dde8b8b3f7 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/audio/header/AudioHeaderEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/audio/header/AudioHeaderEvent.kt @@ -54,7 +54,7 @@ class AudioHeaderEvent( description: String, downloadUrl: String, streamUrl: String? = null, - wavefront: List? = null, + wavefront: List? = null, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, description, createdAt) { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/audio/header/TagArrayBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/audio/header/TagArrayBuilderExt.kt index 5dbaaf522b..d41bdf1723 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/audio/header/TagArrayBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/audio/header/TagArrayBuilderExt.kt @@ -29,4 +29,4 @@ fun TagArrayBuilder.downloadUrl(downloadUrlTag: String) = addU fun TagArrayBuilder.streamUrl(streamUrl: String) = addUnique(StreamUrlTag.assemble(streamUrl)) -fun TagArrayBuilder.wavefront(wave: List) = addUnique(WaveformTag.assemble(wave)) +fun TagArrayBuilder.wavefront(wave: List) = addUnique(WaveformTag.assemble(wave)) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/audio/header/tags/WaveformTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/audio/header/tags/WaveformTag.kt index 7813f78b92..fc383cdc75 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/audio/header/tags/WaveformTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/audio/header/tags/WaveformTag.kt @@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.jackson.JsonMapper import com.vitorpamplona.quartz.utils.ensure class WaveformTag( - val wave: List, + val wave: List, ) { fun toTagArray() = assemble(wave) @@ -38,12 +38,12 @@ class WaveformTag( ensure(tag.has(1)) { return null } ensure(tag[0] == TAG_NAME) { return null } ensure(tag[1].isNotEmpty()) { return null } - val wave = runCatching { JsonMapper.mapper.readValue>(tag[1]) }.getOrNull() + val wave = runCatching { JsonMapper.mapper.readValue>(tag[1]) }.getOrNull() if (wave.isNullOrEmpty()) return null return WaveformTag(wave) } @JvmStatic - fun assemble(wave: List) = arrayOf(TAG_NAME, JsonMapper.mapper.writeValueAsString(wave)) + fun assemble(wave: List) = arrayOf(TAG_NAME, JsonMapper.mapper.writeValueAsString(wave)) } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/AudioMeta.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/AudioMeta.kt index 8d035e7206..d74160cccf 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/AudioMeta.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/AudioMeta.kt @@ -29,7 +29,7 @@ data class AudioMeta( val mimeType: String? = null, val hash: String? = null, val duration: Int? = null, - val waveform: List? = null, + val waveform: List? = null, ) { fun toIMetaArray(): Array = IMetaTagBuilder(url) @@ -37,7 +37,7 @@ data class AudioMeta( mimeType?.let { mimeType(it) } hash?.let { hash(it) } duration?.let { duration(it) } - waveform?.let { waveform(it) } + waveform?.let { waveformFloat(it) } }.build() .toTagArray() diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/IMetaTagBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/IMetaTagBuilderExt.kt index 4562d52754..25da88f798 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/IMetaTagBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/IMetaTagBuilderExt.kt @@ -34,6 +34,8 @@ fun IMetaTagBuilder.hash(hash: HexKey) = add(HashSha256Tag.TAG_NAME, hash) fun IMetaTagBuilder.duration(size: Int) = add(DurationTag.TAG_NAME, size.toString()) -fun IMetaTagBuilder.waveform(wave: List) = add(WaveformTag.TAG_NAME, WaveformTag.assembleWave(wave)) +fun IMetaTagBuilder.waveformInt(wave: List) = add(WaveformTag.TAG_NAME, WaveformTag.assembleWaveInt(wave)) + +fun IMetaTagBuilder.waveformFloat(wave: List) = add(WaveformTag.TAG_NAME, WaveformTag.assembleWaveFloat(wave)) fun IMetaTagBuilder.mimeType(mime: String) = add(MimeTypeTag.TAG_NAME, mime) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/TagArrayBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/TagArrayBuilderExt.kt index f482eb2019..42352f12d9 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/TagArrayBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/TagArrayBuilderExt.kt @@ -33,7 +33,7 @@ fun TagArrayBuilder.audioIMeta( mimeType: String? = null, hash: String? = null, duration: Int? = null, - waveform: List? = null, + waveform: List? = null, ) = audioIMeta(AudioMeta(url, mimeType, hash, duration, waveform)) fun TagArrayBuilder.audioIMeta(imeta: AudioMeta): TagArrayBuilder { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/VoiceEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/VoiceEvent.kt index 33f30d596f..60d94c44c8 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/VoiceEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/VoiceEvent.kt @@ -43,7 +43,7 @@ class VoiceEvent( mimeType: String?, hash: String, duration: Int, - waveform: List, + waveform: List, ) = build(AudioMeta(url, mimeType, hash, duration, waveform)) fun build( diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/VoiceReplyEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/VoiceReplyEvent.kt index eb7444881d..2b064deb31 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/VoiceReplyEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/VoiceReplyEvent.kt @@ -64,7 +64,7 @@ class VoiceReplyEvent( mimeType: String?, hash: String, duration: Int, - waveform: List, + waveform: List, replyingTo: EventHintBundle, ) = build(AudioMeta(url, mimeType, hash, duration, waveform), replyingTo) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/tags/WaveformTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/tags/WaveformTag.kt index d4366fcba5..a05d9a5a2c 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/tags/WaveformTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nipA0VoiceMessages/tags/WaveformTag.kt @@ -22,11 +22,12 @@ package com.vitorpamplona.quartz.nipA0VoiceMessages.tags import com.vitorpamplona.quartz.nip01Core.core.has import com.vitorpamplona.quartz.utils.ensure +import kotlin.collections.joinToString class WaveformTag( - val wave: List, + val wave: List, ) { - fun toTagArray() = assemble(wave) + fun toTagArray() = assembleFloat(wave) companion object { const val TAG_NAME = "waveform" @@ -35,26 +36,32 @@ class WaveformTag( fun parse(tag: Array): WaveformTag? = parseWave(tag)?.let { WaveformTag(it) } @JvmStatic - fun parseWave(tag: Array): List? { + fun parseWave(tag: Array): List? { ensure(tag.has(1)) { return null } ensure(tag[0] == TAG_NAME) { return null } ensure(tag[1].isNotEmpty()) { return null } - val wave = tag[1].split(" ").mapNotNull { it.toIntOrNull() } + val wave = tag[1].split(" ").mapNotNull { it.toFloatOrNull() } if (wave.isEmpty()) return null return wave } - fun parseWave(wave: String): List? { - val wave = wave.split(" ").mapNotNull { it.toIntOrNull() } + fun parseWave(wave: String): List? { + val wave = wave.split(" ").mapNotNull { it.toFloatOrNull() } if (wave.isEmpty()) return null return wave } @JvmStatic - fun assembleWave(wave: List) = wave.joinToString(" ") + fun assembleWaveInt(wave: List) = wave.joinToString(" ") @JvmStatic - fun assemble(wave: List) = arrayOf(TAG_NAME, assembleWave(wave)) + fun assembleInt(wave: List) = arrayOf(TAG_NAME, assembleWaveInt(wave)) + + @JvmStatic + fun assembleWaveFloat(wave: List) = wave.joinToString(" ") + + @JvmStatic + fun assembleFloat(wave: List) = arrayOf(TAG_NAME, assembleWaveFloat(wave)) } }