Browse Source

Always treat waveform as List<Float> (#1663)

[MSC3246](https://github.com/matrix-org/matrix-spec-proposals/pull/3246) specifies the waveform as a list of ints because:

> Because floating point numbers are not allowed in Matrix events

Though DSP on audio data is almost always done using their floating point representation.
This PR brings the float<->int rescaling in the `matrix` module so that the application code can always work with float waveform samples.
pull/1664/head
Marco Romano 11 months ago committed by GitHub
parent
commit
0e4141863b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt
  2. 28
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/WaveformUtils.kt
  3. 3
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt
  4. 2
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioDetails.kt
  5. 2
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
  6. 20
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioDetails.kt
  7. 5
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
  8. 2
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
  9. 2
      libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt
  10. 2
      libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt

3
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt

@ -29,7 +29,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt @@ -29,7 +29,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractor
import io.element.android.features.messages.impl.timeline.util.toHtmlDocument
import io.element.android.features.messages.impl.voicemessages.fromMSC3246range
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.featureflag.api.FeatureFlagService
@ -118,7 +117,7 @@ class TimelineItemContentMessageFactory @Inject constructor( @@ -118,7 +117,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
mediaSource = messageType.source,
duration = messageType.info?.duration ?: Duration.ZERO,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
waveform = messageType.details?.waveform?.fromMSC3246range()?.toImmutableList() ?: persistentListOf(),
waveform = messageType.details?.waveform?.toImmutableList() ?: persistentListOf(),
)
else -> TimelineItemAudioContent(
body = messageType.body,

28
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/WaveformUtils.kt

@ -1,28 +0,0 @@ @@ -1,28 +0,0 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.messages.impl.voicemessages
/**
* Resizes the given [0;1024] int list as per unstable MSC3246 spec
* to a [0;1] range float list to be used for waveform rendering.
*/
internal fun List<Int>.fromMSC3246range(): List<Float> = map { it / 1024f }
/**
* Resizes the given [0;1] float list to [0;1024] int list as per unstable MSC3246 spec.
*/
internal fun List<Float>.toMSC3246range(): List<Int> = map { (it * 1024).toInt() }

3
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt

@ -28,7 +28,6 @@ import androidx.compose.runtime.setValue @@ -28,7 +28,6 @@ import androidx.compose.runtime.setValue
import androidx.core.net.toUri
import androidx.lifecycle.Lifecycle
import io.element.android.features.messages.impl.voicemessages.VoiceMessageException
import io.element.android.features.messages.impl.voicemessages.toMSC3246range
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.SingleIn
@ -219,7 +218,7 @@ class VoiceMessageComposerPresenter @Inject constructor( @@ -219,7 +218,7 @@ class VoiceMessageComposerPresenter @Inject constructor(
val result = mediaSender.sendVoiceMessage(
uri = file.toUri(),
mimeType = mimeType,
waveForm = waveform.toMSC3246range(),
waveForm = waveform,
)
if (result.isFailure) {

2
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/AudioDetails.kt

@ -20,5 +20,5 @@ import java.time.Duration @@ -20,5 +20,5 @@ import java.time.Duration
data class AudioDetails(
val duration: Duration,
val waveform: List<Int>,
val waveform: List<Float>,
)

2
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt

@ -195,7 +195,7 @@ interface MatrixRoom : Closeable { @@ -195,7 +195,7 @@ interface MatrixRoom : Closeable {
suspend fun sendVoiceMessage(
file: File,
audioInfo: AudioInfo,
waveform: List<Int>,
waveform: List<Float>,
progressCallback: ProgressCallback?
): Result<MediaUploadHandler>

20
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/AudioDetails.kt

@ -21,10 +21,26 @@ import org.matrix.rustcomponents.sdk.UnstableAudioDetailsContent as RustAudioDet @@ -21,10 +21,26 @@ import org.matrix.rustcomponents.sdk.UnstableAudioDetailsContent as RustAudioDet
fun RustAudioDetails.map(): AudioDetails = AudioDetails(
duration = duration,
waveform = waveform.map { it.toInt() },
waveform = waveform.fromMSC3246range(),
)
fun AudioDetails.map(): RustAudioDetails = RustAudioDetails(
duration = duration,
waveform = waveform.map { it.toUShort() }
waveform = waveform.toMSC3246range()
)
/**
* Resizes the given [0;1024] int list as per unstable MSC3246 spec
* to a [0;1] float list to be used for waveform rendering.
*
* https://github.com/matrix-org/matrix-spec-proposals/blob/travis/msc/audio-waveform/proposals/3246-audio-waveform.md
*/
internal fun List<UShort>.fromMSC3246range(): List<Float> = map { it.toInt() / 1024f }
/**
* Resizes the given [0;1] float list as per unstable MSC3246 spec
* to a [0;1024] int list to be used for waveform rendering.
*
* https://github.com/matrix-org/matrix-spec-proposals/blob/travis/msc/audio-waveform/proposals/3246-audio-waveform.md
*/
internal fun List<Float>.toMSC3246range(): List<UShort> = map { (it * 1024).toInt().toUShort() }

5
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt

@ -46,6 +46,7 @@ import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings @@ -46,6 +46,7 @@ import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
import io.element.android.libraries.matrix.impl.core.toProgressWatcher
import io.element.android.libraries.matrix.impl.media.MediaUploadHandlerImpl
import io.element.android.libraries.matrix.impl.media.map
import io.element.android.libraries.matrix.impl.media.toMSC3246range
import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService
import io.element.android.libraries.matrix.impl.poll.toInner
import io.element.android.libraries.matrix.impl.room.location.toInner
@ -499,13 +500,13 @@ class RustMatrixRoom( @@ -499,13 +500,13 @@ class RustMatrixRoom(
override suspend fun sendVoiceMessage(
file: File,
audioInfo: AudioInfo,
waveform: List<Int>,
waveform: List<Float>,
progressCallback: ProgressCallback?,
): Result<MediaUploadHandler> = sendAttachment(listOf(file)) {
innerRoom.sendVoiceMessage(
url = file.path,
audioInfo = audioInfo.map(),
waveform = waveform.map { it.toUShort() },
waveform = waveform.toMSC3246range(),
progressWatcher = progressCallback?.toProgressWatcher(),
)
}

2
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt

@ -387,7 +387,7 @@ class FakeMatrixRoom( @@ -387,7 +387,7 @@ class FakeMatrixRoom(
override suspend fun sendVoiceMessage(
file: File,
audioInfo: AudioInfo,
waveform: List<Int>,
waveform: List<Float>,
progressCallback: ProgressCallback?
): Result<MediaUploadHandler> = fakeSendMedia(progressCallback)

2
libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt

@ -55,7 +55,7 @@ class MediaSender @Inject constructor( @@ -55,7 +55,7 @@ class MediaSender @Inject constructor(
suspend fun sendVoiceMessage(
uri: Uri,
mimeType: String,
waveForm: List<Int>,
waveForm: List<Float>,
progressCallback: ProgressCallback? = null
): Result<Unit> {
return preProcessor

2
libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaUploadInfo.kt

@ -29,6 +29,6 @@ sealed interface MediaUploadInfo { @@ -29,6 +29,6 @@ sealed interface MediaUploadInfo {
data class Image(override val file: File, val imageInfo: ImageInfo, val thumbnailFile: File) : MediaUploadInfo
data class Video(override val file: File, val videoInfo: VideoInfo, val thumbnailFile: File) : MediaUploadInfo
data class Audio(override val file: File, val audioInfo: AudioInfo) : MediaUploadInfo
data class VoiceMessage(override val file: File, val audioInfo: AudioInfo, val waveform: List<Int>) : MediaUploadInfo
data class VoiceMessage(override val file: File, val audioInfo: AudioInfo, val waveform: List<Float>) : MediaUploadInfo
data class AnyFile(override val file: File, val fileInfo: FileInfo) : MediaUploadInfo
}

Loading…
Cancel
Save