mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-11-10 22:26:56 +01:00
Enabling the display for amplitudes in an array of floats for YakBak
This commit is contained in:
@@ -964,7 +964,7 @@ class Account(
|
|||||||
mimeType: String?,
|
mimeType: String?,
|
||||||
hash: String,
|
hash: String,
|
||||||
duration: Int,
|
duration: Int,
|
||||||
waveform: List<Int>,
|
waveform: List<Float>,
|
||||||
) {
|
) {
|
||||||
signAndComputeBroadcast(VoiceEvent.build(url, mimeType, hash, duration, waveform))
|
signAndComputeBroadcast(VoiceEvent.build(url, mimeType, hash, duration, waveform))
|
||||||
}
|
}
|
||||||
@@ -974,7 +974,7 @@ class Account(
|
|||||||
mimeType: String?,
|
mimeType: String?,
|
||||||
hash: String,
|
hash: String,
|
||||||
duration: Int,
|
duration: Int,
|
||||||
waveform: List<Int>,
|
waveform: List<Float>,
|
||||||
replyTo: EventHintBundle<VoiceEvent>,
|
replyTo: EventHintBundle<VoiceEvent>,
|
||||||
) {
|
) {
|
||||||
signAndComputeBroadcast(VoiceReplyEvent.build(url, mimeType, hash, duration, waveform, replyTo))
|
signAndComputeBroadcast(VoiceReplyEvent.build(url, mimeType, hash, duration, waveform, replyTo))
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
|
|||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
class WaveformData(
|
class WaveformData(
|
||||||
val wave: List<Int>,
|
val wave: List<Float>,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import java.io.File
|
|||||||
class RecordingResult(
|
class RecordingResult(
|
||||||
val file: File,
|
val file: File,
|
||||||
val mimeType: String,
|
val mimeType: String,
|
||||||
val amplitudes: List<Int>,
|
val amplitudes: List<Float>,
|
||||||
val duration: Int,
|
val duration: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ class VoiceMessageRecorder {
|
|||||||
private var outputFile: File? = null
|
private var outputFile: File? = null
|
||||||
private var startTime: Long = 0
|
private var startTime: Long = 0
|
||||||
private var job: Job? = null
|
private var job: Job? = null
|
||||||
private var amplitudes: MutableList<Int> = mutableListOf()
|
private var amplitudes: MutableList<Float> = mutableListOf()
|
||||||
|
|
||||||
private fun createRecorder(context: Context): MediaRecorder =
|
private fun createRecorder(context: Context): MediaRecorder =
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
@@ -80,7 +80,7 @@ class VoiceMessageRecorder {
|
|||||||
job =
|
job =
|
||||||
scope.launch {
|
scope.launch {
|
||||||
while (recorder != null) {
|
while (recorder != null) {
|
||||||
amplitudes.add(((recorder?.maxAmplitude ?: 0) / 100).toInt())
|
amplitudes.add(recorder?.maxAmplitude?.toFloat() ?: 0f)
|
||||||
delay(1000)
|
delay(1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.components
|
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.AnimationSpec
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
@@ -48,6 +50,7 @@ import androidx.compose.ui.unit.coerceIn
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.linc.audiowaveform.model.AmplitudeType
|
import com.linc.audiowaveform.model.AmplitudeType
|
||||||
import com.linc.audiowaveform.model.WaveformAlignment
|
import com.linc.audiowaveform.model.WaveformAlignment
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.toDrawableAmplitudes
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@@ -77,7 +80,7 @@ fun AudioWaveformReadOnly(
|
|||||||
spikeRadius: Dp = 2.dp,
|
spikeRadius: Dp = 2.dp,
|
||||||
spikePadding: Dp = 2.dp,
|
spikePadding: Dp = 2.dp,
|
||||||
progress: Float = 0F,
|
progress: Float = 0F,
|
||||||
amplitudes: List<Int>,
|
amplitudes: List<Float>,
|
||||||
onProgressChange: (Float) -> Unit,
|
onProgressChange: (Float) -> Unit,
|
||||||
) {
|
) {
|
||||||
val backgroundColor = MaterialTheme.colorScheme.background
|
val backgroundColor = MaterialTheme.colorScheme.background
|
||||||
@@ -101,6 +104,7 @@ fun AudioWaveformReadOnly(
|
|||||||
maxHeight = canvasSize.height.coerceAtLeast(MIN_SPIKE_HEIGHT),
|
maxHeight = canvasSize.height.coerceAtLeast(MIN_SPIKE_HEIGHT),
|
||||||
)
|
)
|
||||||
}.map { animateFloatAsState(it, spikeAnimationSpec).value }
|
}.map { animateFloatAsState(it, spikeAnimationSpec).value }
|
||||||
|
|
||||||
Canvas(
|
Canvas(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
@@ -145,14 +149,13 @@ fun AudioWaveformReadOnly(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<Int>.toDrawableAmplitudes(
|
private fun List<Float>.toDrawableAmplitudes(
|
||||||
amplitudeType: AmplitudeType,
|
amplitudeType: AmplitudeType,
|
||||||
spikes: Int,
|
spikes: Int,
|
||||||
minHeight: Float,
|
minHeight: Float,
|
||||||
maxHeight: Float,
|
maxHeight: Float,
|
||||||
): List<Float> {
|
): List<Float> {
|
||||||
val amplitudes = map(Int::toFloat)
|
if (this.isEmpty() || spikes == 0) {
|
||||||
if (amplitudes.isEmpty() || spikes == 0) {
|
|
||||||
return List(spikes) { minHeight }
|
return List(spikes) { minHeight }
|
||||||
}
|
}
|
||||||
val transform = { data: List<Float> ->
|
val transform = { data: List<Float> ->
|
||||||
@@ -161,11 +164,10 @@ private fun List<Int>.toDrawableAmplitudes(
|
|||||||
AmplitudeType.Max -> data.max()
|
AmplitudeType.Max -> data.max()
|
||||||
AmplitudeType.Min -> data.min()
|
AmplitudeType.Min -> data.min()
|
||||||
}.toFloat()
|
}.toFloat()
|
||||||
.coerceIn(minHeight, maxHeight)
|
|
||||||
}
|
}
|
||||||
return when {
|
return when {
|
||||||
spikes > amplitudes.count() -> amplitudes.fillToSize(spikes, transform)
|
spikes > this.count() -> this.fillToSize(spikes, transform)
|
||||||
else -> amplitudes.chunkToSize(spikes, transform)
|
else -> this.chunkToSize(spikes, transform)
|
||||||
}.normalize(minHeight, maxHeight)
|
}.normalize(minHeight, maxHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class AudioHeaderEvent(
|
|||||||
description: String,
|
description: String,
|
||||||
downloadUrl: String,
|
downloadUrl: String,
|
||||||
streamUrl: String? = null,
|
streamUrl: String? = null,
|
||||||
wavefront: List<Int>? = null,
|
wavefront: List<Float>? = null,
|
||||||
createdAt: Long = TimeUtils.now(),
|
createdAt: Long = TimeUtils.now(),
|
||||||
initializer: TagArrayBuilder<AudioHeaderEvent>.() -> Unit = {},
|
initializer: TagArrayBuilder<AudioHeaderEvent>.() -> Unit = {},
|
||||||
) = eventTemplate(KIND, description, createdAt) {
|
) = eventTemplate(KIND, description, createdAt) {
|
||||||
|
|||||||
@@ -29,4 +29,4 @@ fun TagArrayBuilder<AudioHeaderEvent>.downloadUrl(downloadUrlTag: String) = addU
|
|||||||
|
|
||||||
fun TagArrayBuilder<AudioHeaderEvent>.streamUrl(streamUrl: String) = addUnique(StreamUrlTag.assemble(streamUrl))
|
fun TagArrayBuilder<AudioHeaderEvent>.streamUrl(streamUrl: String) = addUnique(StreamUrlTag.assemble(streamUrl))
|
||||||
|
|
||||||
fun TagArrayBuilder<AudioHeaderEvent>.wavefront(wave: List<Int>) = addUnique(WaveformTag.assemble(wave))
|
fun TagArrayBuilder<AudioHeaderEvent>.wavefront(wave: List<Float>) = addUnique(WaveformTag.assemble(wave))
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.jackson.JsonMapper
|
|||||||
import com.vitorpamplona.quartz.utils.ensure
|
import com.vitorpamplona.quartz.utils.ensure
|
||||||
|
|
||||||
class WaveformTag(
|
class WaveformTag(
|
||||||
val wave: List<Int>,
|
val wave: List<Float>,
|
||||||
) {
|
) {
|
||||||
fun toTagArray() = assemble(wave)
|
fun toTagArray() = assemble(wave)
|
||||||
|
|
||||||
@@ -38,12 +38,12 @@ class WaveformTag(
|
|||||||
ensure(tag.has(1)) { return null }
|
ensure(tag.has(1)) { return null }
|
||||||
ensure(tag[0] == TAG_NAME) { return null }
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
ensure(tag[1].isNotEmpty()) { return null }
|
ensure(tag[1].isNotEmpty()) { return null }
|
||||||
val wave = runCatching { JsonMapper.mapper.readValue<List<Int>>(tag[1]) }.getOrNull()
|
val wave = runCatching { JsonMapper.mapper.readValue<List<Float>>(tag[1]) }.getOrNull()
|
||||||
if (wave.isNullOrEmpty()) return null
|
if (wave.isNullOrEmpty()) return null
|
||||||
return WaveformTag(wave)
|
return WaveformTag(wave)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun assemble(wave: List<Int>) = arrayOf(TAG_NAME, JsonMapper.mapper.writeValueAsString(wave))
|
fun assemble(wave: List<Float>) = arrayOf(TAG_NAME, JsonMapper.mapper.writeValueAsString(wave))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ data class AudioMeta(
|
|||||||
val mimeType: String? = null,
|
val mimeType: String? = null,
|
||||||
val hash: String? = null,
|
val hash: String? = null,
|
||||||
val duration: Int? = null,
|
val duration: Int? = null,
|
||||||
val waveform: List<Int>? = null,
|
val waveform: List<Float>? = null,
|
||||||
) {
|
) {
|
||||||
fun toIMetaArray(): Array<String> =
|
fun toIMetaArray(): Array<String> =
|
||||||
IMetaTagBuilder(url)
|
IMetaTagBuilder(url)
|
||||||
@@ -37,7 +37,7 @@ data class AudioMeta(
|
|||||||
mimeType?.let { mimeType(it) }
|
mimeType?.let { mimeType(it) }
|
||||||
hash?.let { hash(it) }
|
hash?.let { hash(it) }
|
||||||
duration?.let { duration(it) }
|
duration?.let { duration(it) }
|
||||||
waveform?.let { waveform(it) }
|
waveform?.let { waveformFloat(it) }
|
||||||
}.build()
|
}.build()
|
||||||
.toTagArray()
|
.toTagArray()
|
||||||
|
|
||||||
|
|||||||
@@ -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.duration(size: Int) = add(DurationTag.TAG_NAME, size.toString())
|
||||||
|
|
||||||
fun IMetaTagBuilder.waveform(wave: List<Int>) = add(WaveformTag.TAG_NAME, WaveformTag.assembleWave(wave))
|
fun IMetaTagBuilder.waveformInt(wave: List<Int>) = add(WaveformTag.TAG_NAME, WaveformTag.assembleWaveInt(wave))
|
||||||
|
|
||||||
|
fun IMetaTagBuilder.waveformFloat(wave: List<Float>) = add(WaveformTag.TAG_NAME, WaveformTag.assembleWaveFloat(wave))
|
||||||
|
|
||||||
fun IMetaTagBuilder.mimeType(mime: String) = add(MimeTypeTag.TAG_NAME, mime)
|
fun IMetaTagBuilder.mimeType(mime: String) = add(MimeTypeTag.TAG_NAME, mime)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ fun <T : BaseVoiceEvent> TagArrayBuilder<T>.audioIMeta(
|
|||||||
mimeType: String? = null,
|
mimeType: String? = null,
|
||||||
hash: String? = null,
|
hash: String? = null,
|
||||||
duration: Int? = null,
|
duration: Int? = null,
|
||||||
waveform: List<Int>? = null,
|
waveform: List<Float>? = null,
|
||||||
) = audioIMeta(AudioMeta(url, mimeType, hash, duration, waveform))
|
) = audioIMeta(AudioMeta(url, mimeType, hash, duration, waveform))
|
||||||
|
|
||||||
fun <T : BaseVoiceEvent> TagArrayBuilder<T>.audioIMeta(imeta: AudioMeta): TagArrayBuilder<T> {
|
fun <T : BaseVoiceEvent> TagArrayBuilder<T>.audioIMeta(imeta: AudioMeta): TagArrayBuilder<T> {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class VoiceEvent(
|
|||||||
mimeType: String?,
|
mimeType: String?,
|
||||||
hash: String,
|
hash: String,
|
||||||
duration: Int,
|
duration: Int,
|
||||||
waveform: List<Int>,
|
waveform: List<Float>,
|
||||||
) = build(AudioMeta(url, mimeType, hash, duration, waveform))
|
) = build(AudioMeta(url, mimeType, hash, duration, waveform))
|
||||||
|
|
||||||
fun build(
|
fun build(
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class VoiceReplyEvent(
|
|||||||
mimeType: String?,
|
mimeType: String?,
|
||||||
hash: String,
|
hash: String,
|
||||||
duration: Int,
|
duration: Int,
|
||||||
waveform: List<Int>,
|
waveform: List<Float>,
|
||||||
replyingTo: EventHintBundle<VoiceEvent>,
|
replyingTo: EventHintBundle<VoiceEvent>,
|
||||||
) = build(AudioMeta(url, mimeType, hash, duration, waveform), replyingTo)
|
) = build(AudioMeta(url, mimeType, hash, duration, waveform), replyingTo)
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,12 @@ package com.vitorpamplona.quartz.nipA0VoiceMessages.tags
|
|||||||
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||||
import com.vitorpamplona.quartz.utils.ensure
|
import com.vitorpamplona.quartz.utils.ensure
|
||||||
|
import kotlin.collections.joinToString
|
||||||
|
|
||||||
class WaveformTag(
|
class WaveformTag(
|
||||||
val wave: List<Int>,
|
val wave: List<Float>,
|
||||||
) {
|
) {
|
||||||
fun toTagArray() = assemble(wave)
|
fun toTagArray() = assembleFloat(wave)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG_NAME = "waveform"
|
const val TAG_NAME = "waveform"
|
||||||
@@ -35,26 +36,32 @@ class WaveformTag(
|
|||||||
fun parse(tag: Array<String>): WaveformTag? = parseWave(tag)?.let { WaveformTag(it) }
|
fun parse(tag: Array<String>): WaveformTag? = parseWave(tag)?.let { WaveformTag(it) }
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parseWave(tag: Array<String>): List<Int>? {
|
fun parseWave(tag: Array<String>): List<Float>? {
|
||||||
ensure(tag.has(1)) { return null }
|
ensure(tag.has(1)) { return null }
|
||||||
ensure(tag[0] == TAG_NAME) { return null }
|
ensure(tag[0] == TAG_NAME) { return null }
|
||||||
ensure(tag[1].isNotEmpty()) { 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
|
if (wave.isEmpty()) return null
|
||||||
return wave
|
return wave
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parseWave(wave: String): List<Int>? {
|
fun parseWave(wave: String): List<Float>? {
|
||||||
val wave = wave.split(" ").mapNotNull { it.toIntOrNull() }
|
val wave = wave.split(" ").mapNotNull { it.toFloatOrNull() }
|
||||||
if (wave.isEmpty()) return null
|
if (wave.isEmpty()) return null
|
||||||
return wave
|
return wave
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun assembleWave(wave: List<Int>) = wave.joinToString(" ")
|
fun assembleWaveInt(wave: List<Int>) = wave.joinToString(" ")
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun assemble(wave: List<Int>) = arrayOf(TAG_NAME, assembleWave(wave))
|
fun assembleInt(wave: List<Int>) = arrayOf(TAG_NAME, assembleWaveInt(wave))
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun assembleWaveFloat(wave: List<Float>) = wave.joinToString(" ")
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun assembleFloat(wave: List<Float>) = arrayOf(TAG_NAME, assembleWaveFloat(wave))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user