Giter Site home page Giter Site logo

compose-audiowaveform's Introduction

Compose AudioWaveform

GitHub release (latest by date) GitHub

GitHub followers GitHub stars GitHub forks

Description

AudioWaveform is a lightweight Jetpack Compose library which draws waveform of audio.
Library uses compose Canvas API under the hood. It helps us to create customizable and flexible waveforms. This library was inspired by the WaveformSeekBar library (xml implementation).
AudioWaveform is fully compatible with Amplituda library.

Waveforms created with AudioWaveform

Download

allprojects {
  repositories {
    maven { url 'https://jitpack.io' }
  }
}
dependencies {
  implementation 'com.github.lincollincol:compose-audiowaveform:x.y.z'
}

Usage

Common way

var waveformProgress by remember { mutableStateOf(0F) }
AudioWaveform(
  amplitudes = amplitudes,
  progress = waveformProgress,
  onProgressChange = { waveformProgress = it }
)

Custom color

  • SolidColor() - single color Brush.
  • Brush.horizontalGradient(), Brush.verticalGradient() (and more) - default Brush static gradient implementations.
  • Brush.infinite*Gradient() (where * is one of linear, horizontal or vertical) - infinite animated gradient. This is AudioWaveform library extension functions created with this article.
var waveformProgress by remember { mutableStateOf(0F) }

val colorBrush = SolidColor(Color.Magenta)

val staticGradientBrush = Brush.linearGradient(colors = listOf(Color(0xff22c1c3), Color(0xfffdbb2d)))

val animatedGradientBrush = Brush.infiniteLinearGradient(
  colors = listOf(Color(0xff22c1c3), Color(0xfffdbb2d)),
  animation = tween(durationMillis = 6000, easing = LinearEasing),
  width = 128F
)

AudioWaveform(
  progress = waveformProgress,
  progressBrush = brush,
  amplitudes = amplitudes,
  onProgressChange = { waveformProgress = it }
)

All parameters

var waveformProgress by remember { mutableStateOf(0F) }
AudioWaveform(
  modifier = Modifier.fillMaxWidth(),
  // Spike DrawStyle: Fill or Stroke 
  style = Fill,
  waveformAlignment = WaveformAlignment.Center, 
  amplitudeType = AmplitudeType.Avg,
  // Colors could be updated with Brush API
  progressBrush = SolidColor(Color.Magenta),
  waveformBrush = SolidColor(Color.LightGray),
  spikeWidth = 4.dp,
  spikePadding = 2.dp,
  spikeRadius = 4.dp,
  progress = waveformProgress,
  amplitudes = amplitudes,
  onProgressChange = { waveformProgress = it },
  onProgressChangeFinished = {}
)

Sample app

You could also try sample app, which demonstrates all AudioWaveform library features. Waveform from sample app is also synchronized with Media3.
Download sample app apk here.

Amplituda compatibility

AudioWaveform requires amplitudes to draw waveform. This parameter is a list of integers, which represents audio data. You can process the audio file by yourself or use already existing library Amplituda.
Amplituda is a fast audio processing library, which provides you data for drawing waveforms. Library has caching and compressing processed data features out of the box.
Here is Amplituda library usage in a sample app.

License

   Copyright 2022-present lincollincol

   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.

compose-audiowaveform's People

Contributors

lincollincol avatar thedroiddiv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

compose-audiowaveform's Issues

It seems amplitudes > 128 are clipped

See screenhot (these are 256 samples going from 0 to 255).
It's completely fine with me if 128 is the max value of an amplitude. It's just that I couldn't find it anywhere in the docs.

Screenshot 2023-10-13 at 11 55 15

How can we play from the Url?

Currently, the sample app contains the example which uses the ContentResolver to play the local files.
We need some help to directly play media from the Url. how do we set up the MediaItem to play from the URL? From the Example, we can see it is using MediController to play the local file is there any example where we can play using URL?

Unable to set custom width for the waveform.

As the title suggested whenever I try to customize waveform width with the following line, the waveform take the full width of the screen

          AudioWaveform( modifier = modifier.fillMaxWidth(0.75f) ) 

This is maybe due to behavior of Internally AudioWaveform Composable the Canvas function uses fillMaxWidth()

Screenshot 2023-04-25 at 17 37 45

Any resolution for this?

App Crash - java.lang.NoSuchMethodError: No static method drawRoundRect-

    java.lang.NoSuchMethodError: No static method drawRoundRect-ZuiqVtQ$default(Landroidx/compose/ui/graphics/drawscope/DrawScope;Landroidx/compose/ui/graphics/Brush;JJJFLandroidx/compose/ui/graphics/drawscope/DrawStyle;Landroidx/compose/ui/graphics/ColorFilter;IILjava/lang/Object;)V in class Landroidx/compose/ui/graphics/drawscope/DrawScope; or its super classes (declaration of 'androidx.compose.ui.graphics.drawscope.DrawScope' appears in /data/app/~~wme9RWI_TEWRMheotXYODg==/jp.tripmate.slo.dev-dqTZEZknICnb5A4bkff_CQ==/base.apk)
        at com.linc.audiowaveform.AudioWaveformKt$AudioWaveform$2.invoke(AudioWaveform.kt:100)
        at com.linc.audiowaveform.AudioWaveformKt$AudioWaveform$2.invoke(AudioWaveform.kt:74)

Hi, I'm facing above crash. Is this a bug?

Empty wave form issue

How do i create A basic wave form with a simple list like this?
I see nothing

 AudioWaveform(
            modifier = Modifier.fillMaxSize(),
            amplitudes = listOf(1000,3000,400),
            amplitudeType = AmplitudeType.Avg,
            progressBrush = animatedGradientBrush,
            onProgressChange = { waveformProgress = it }
        )
   

Implementation Error

Hi. I want to use Audio Wave.

There is a error like:

Task failed with an exception.

  • What went wrong:
    Execution failed for task ':app:mergeDebugNativeLibs'.

Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
Could not find com.github.lincollincol:compose-audiowaveform:v1.1.1.
Searched in the following locations:
- https://dl.google.com/dl/android/maven2/com/github/lincollincol/compose-audiowaveform/v1.1.1/compose-audiowaveform-v1.1.1.pom
- https://repo.maven.apache.org/maven2/com/github/lincollincol/compose-audiowaveform/v1.1.1/compose-audiowaveform-v1.1.1.pom
Required by:
project :app

  • Try:

Run with --info or --debug option to get more log output.
Run with --scan to get full insights.

  • Exception is:
    org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:mergeDebugNativeLibs'.
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:38)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:42)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:338)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:325)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:318)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:304)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:463)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:380)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:49)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
    at java.base/java.lang.Thread.run(Thread.java:1589)
    Caused by: org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration$ArtifactResolveException: Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
    at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.mapFailure(DefaultConfiguration.java:1595)
    at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.access$3900(DefaultConfiguration.java:166)
    at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$DefaultResolutionHost.mapFailure(DefaultConfiguration.java:2278)
    at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$DefaultResolutionHost.rethrowFailure(DefaultConfiguration.java:2283)
    at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationFileCollection.visitContents(DefaultConfiguration.java:1563)
    at org.gradle.api.internal.file.AbstractFileCollection.getFiles(AbstractFileCollection.java:130)
    at org.gradle.api.internal.file.AbstractFileCollection.iterator(AbstractFileCollection.java:171)
    at org.gradle.api.internal.file.FilteredFileCollection.iterator(FilteredFileCollection.java:79)
    at com.google.common.collect.ImmutableSet.copyOf(ImmutableSet.java:262)
    at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection$1.visitCollection(DefaultConfigurableFileCollection.java:244)
    at org.gradle.api.internal.file.AbstractFileCollection.visitContents(AbstractFileCollection.java:371)
    at org.gradle.api.internal.file.AbstractFileCollection.visitStructure(AbstractFileCollection.java:366)
    at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection.lambda$calculateFinalizedValue$0(DefaultConfigurableFileCollection.java:241)
    at org.gradle.api.internal.file.collections.UnpackingVisitor.add(UnpackingVisitor.java:67)
    at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection$UnresolvedItemsCollector.visitContents(DefaultConfigurableFileCollection.java:370)
    at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection.calculateFinalizedValue(DefaultConfigurableFileCollection.java:241)
    at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection.visitChildren(DefaultConfigurableFileCollection.java:272)
    at org.gradle.api.internal.file.CompositeFileCollection.visitContents(CompositeFileCollection.java:124)
    at org.gradle.api.internal.file.AbstractFileCollection.visitStructure(AbstractFileCollection.java:366)
    at org.gradle.api.internal.file.CompositeFileCollection.lambda$visitContents$0(CompositeFileCollection.java:124)
    at org.gradle.api.internal.file.collections.UnpackingVisitor.add(UnpackingVisitor.java:67)
    at org.gradle.api.internal.file.collections.UnpackingVisitor.add(UnpackingVisitor.java:92)
    at org.gradle.api.internal.file.DefaultFileCollectionFactory$ResolvingFileCollection.visitChildren(DefaultFileCollectionFactory.java:254)
    at org.gradle.api.internal.file.CompositeFileCollection.visitContents(CompositeFileCollection.java:124)
    at org.gradle.api.internal.file.AbstractFileCollection.visitStructure(AbstractFileCollection.java:366)
    at org.gradle.api.internal.file.CompositeFileCollection.lambda$visitContents$0(CompositeFileCollection.java:124)
    at org.gradle.api.internal.tasks.PropertyFileCollection.visitChildren(PropertyFileCollection.java:48)
    at org.gradle.api.internal.file.CompositeFileCollection.visitContents(CompositeFileCollection.java:124)
    at org.gradle.api.internal.file.AbstractFileCollection.visitStructure(AbstractFileCollection.java:366)
    at org.gradle.internal.fingerprint.impl.DefaultFileCollectionSnapshotter.snapshot(DefaultFileCollectionSnapshotter.java:47)
    at org.gradle.internal.execution.impl.DefaultInputFingerprinter$InputCollectingVisitor.visitInputFileProperty(DefaultInputFingerprinter.java:133)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep$1.visitInputFileProperty(SkipEmptyWorkStep.java:131)
    at org.gradle.api.internal.tasks.execution.TaskExecution.visitRegularInputs(TaskExecution.java:322)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.lambda$fingerprintPrimaryInputs$2(SkipEmptyWorkStep.java:127)
    at org.gradle.internal.execution.impl.DefaultInputFingerprinter.fingerprintInputProperties(DefaultInputFingerprinter.java:63)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.fingerprintPrimaryInputs(SkipEmptyWorkStep.java:118)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:77)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:53)
    at org.gradle.internal.execution.steps.RemoveUntrackedExecutionStateStep.execute(RemoveUntrackedExecutionStateStep.java:32)
    at org.gradle.internal.execution.steps.RemoveUntrackedExecutionStateStep.execute(RemoveUntrackedExecutionStateStep.java:21)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
    at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:36)
    at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:23)
    at org.gradle.internal.execution.steps.CleanupStaleOutputsStep.execute(CleanupStaleOutputsStep.java:75)
    at org.gradle.internal.execution.steps.CleanupStaleOutputsStep.execute(CleanupStaleOutputsStep.java:41)
    at org.gradle.internal.execution.steps.AssignWorkspaceStep.lambda$execute$0(AssignWorkspaceStep.java:32)
    at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:287)
    at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:30)
    at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:21)
    at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:37)
    at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:27)
    at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:42)
    at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:31)
    at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:64)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:146)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:135)
    at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:42)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:338)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:325)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:318)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:304)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:463)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:380)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:49)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
    at java.base/java.lang.Thread.run(Thread.java:1589)
    Caused by: org.gradle.internal.resolve.ModuleVersionNotFoundException: Could not find com.github.lincollincol:compose-audiowaveform:v1.1.1.
    Searched in the following locations:

==============================================================================

BUILD FAILED in 732ms
21 actionable tasks: 8 executed, 13 up-to-date

java.lang.NoSuchMethodError: No static method graphicsLayer

java.lang.NoSuchMethodError: No static method graphicsLayer-Ap8cVGQ$default(Landroidx/compose/ui/Modifier;FFFFFFFFFFJLandroidx/compose/ui/graphics/Shape;ZLandroidx/compose/ui/graphics/RenderEffect;JJIILjava/lang/Object;)Landroidx/compose/ui/Modifier; in class Landroidx/compose/ui/graphics/GraphicsLayerModifierKt; or its super classes (declaration of 'androidx.compose.ui.graphics.GraphicsLayerModifierKt' appears in /data/app/com.pico.learningcanvas-1/base.apk) at com.linc.audiowaveform.AudioWaveformKt.AudioWaveform-x4UjrtE(AudioWaveform.kt:76)
It's the same issue in #9 and I don't know why the issue is closed without providing a solution for it :')

  • Here is my project gradle.build file:

buildscript { ext { compose_version = '1.2.0' } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { id 'com.android.application' version '7.4.1' apply false id 'com.android.library' version '7.4.1' apply false id 'org.jetbrains.kotlin.android' version '1.7.0' apply false }

  • App level gradle.build file:

` plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}

android {
namespace 'com.pico.learningcanvas'
compileSdk 33

  defaultConfig {
      applicationId "com.pico.learningcanvas"
      minSdk 23
      targetSdk 33
      versionCode 1
      versionName "1.0"

      testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
      vectorDrawables {
          useSupportLibrary true
      }
  }

  buildTypes {
      release {
          minifyEnabled false
          proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
      }
  }
  compileOptions {
      sourceCompatibility JavaVersion.VERSION_1_8
      targetCompatibility JavaVersion.VERSION_1_8
  }
  kotlinOptions {
      jvmTarget = '1.8'
  }
  buildFeatures {
      compose true
  }
  composeOptions {
      kotlinCompilerExtensionVersion '1.2.0'
  }
  packagingOptions {
      resources {
          excludes += '/META-INF/{AL2.0,LGPL2.1}'
      }
  }

}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.activity:activity-compose:1.3.1'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation 'androidx.compose.material3:material3:1.0.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"

  implementation 'com.github.lincollincol:amplituda:2.2.2'
  implementation 'com.github.lincollincol:compose-audiowaveform:1.1.1'
  implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'

}
`

  • I tried to copy the source code as a file in my project files, and it works totally fine
  • Here is the source code I copied:

`

import android.view.MotionEvent
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.runtime.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.drawscope.DrawStyle
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.unit.Dp
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 kotlin.math.ceil
import kotlin.math.roundToInt

private val MinSpikeWidthDp: Dp = 1.dp
private val MaxSpikeWidthDp: Dp = 24.dp
private val MinSpikePaddingDp: Dp = 0.dp
private val MaxSpikePaddingDp: Dp = 12.dp
private val MinSpikeRadiusDp: Dp = 0.dp
private val MaxSpikeRadiusDp: Dp = 12.dp

private const val MinProgress: Float = 0F
private const val MaxProgress: Float = 1F

private const val MinSpikeHeight: Float = 1F
private const val DefaultGraphicsLayerAlpha: Float = 0.99F

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun AudioWaveform(
    modifier: Modifier = Modifier,
    style: DrawStyle = Fill,
    waveformBrush: Brush = SolidColor(Color.White),
    progressBrush: Brush = SolidColor(Color.Blue),
    waveformAlignment: WaveformAlignment = WaveformAlignment.Center,
    amplitudeType: AmplitudeType = AmplitudeType.Avg,
    onProgressChangeFinished: (() -> Unit)? = null,
    spikeAnimationSpec: AnimationSpec<Float> = tween(500),
    spikeWidth: Dp = 4.dp,
    spikeRadius: Dp = 2.dp,
    spikePadding: Dp = 1.dp,
    progress: Float = 0F,
    amplitudes: List<Int>,
    onProgressChange: (Float) -> Unit
) {
    val _progress = remember(progress) { progress.coerceIn(MinProgress, MaxProgress) }
    val _spikeWidth = remember(spikeWidth) { spikeWidth.coerceIn(MinSpikeWidthDp, MaxSpikeWidthDp) }
    val _spikePadding = remember(spikePadding) { spikePadding.coerceIn(MinSpikePaddingDp, MaxSpikePaddingDp) }
    val _spikeRadius = remember(spikeRadius) { spikeRadius.coerceIn(MinSpikeRadiusDp, MaxSpikeRadiusDp) }
    val _spikeTotalWidth = remember(spikeWidth, spikePadding) { _spikeWidth + _spikePadding }
    var canvasSize by remember { mutableStateOf(Size(0f, 0f)) }
    var spikes by remember { mutableStateOf(0F) }
    val spikesAmplitudes = remember(amplitudes, spikes, amplitudeType) {
        amplitudes.toDrawableAmplitudes(
            amplitudeType = amplitudeType,
            spikes = spikes.toInt(),
            minHeight = MinSpikeHeight,
            maxHeight = canvasSize.height.coerceAtLeast(MinSpikeHeight)
        )
    }.map { animateFloatAsState(it, spikeAnimationSpec).value }
    Canvas(
        modifier = Modifier
            .fillMaxWidth()
            .requiredHeight(48.dp)
            .graphicsLayer(alpha = DefaultGraphicsLayerAlpha)
            .pointerInteropFilter {
                return@pointerInteropFilter when (it.action) {
                    MotionEvent.ACTION_DOWN,
                    MotionEvent.ACTION_MOVE -> {
                        if (it.x in 0F..canvasSize.width) {
                            onProgressChange(it.x / canvasSize.width)
                            true
                        } else false
                    }
                    MotionEvent.ACTION_UP -> {
                        onProgressChangeFinished?.invoke()
                        true
                    }
                    else -> false
                }
            }
            .then(modifier)
    ) {
        canvasSize = size
        spikes = size.width / _spikeTotalWidth.toPx()
        spikesAmplitudes.forEachIndexed { index, amplitude ->
            drawRoundRect(
                brush = waveformBrush,
                topLeft = Offset(
                    x = index * _spikeTotalWidth.toPx(),
                    y = when(waveformAlignment) {
                        WaveformAlignment.Top -> 0F
                        WaveformAlignment.Bottom -> size.height - amplitude
                        WaveformAlignment.Center -> size.height / 2F - amplitude / 2F
                    }
                ),
                size = Size(
                    width = _spikeWidth.toPx(),
                    height = amplitude
                ),
                cornerRadius = CornerRadius(_spikeRadius.toPx(), _spikeRadius.toPx()),
                style = style
            )
            drawRect(
                brush = progressBrush,
                size = Size(
                    width = _progress * size.width,
                    height = size.height
                ),
                blendMode = BlendMode.SrcAtop
            )
        }
    }
}

private fun List<Int>.toDrawableAmplitudes(
    amplitudeType: AmplitudeType,
    spikes: Int,
    minHeight: Float,
    maxHeight: Float
): List<Float> {
    val amplitudes = map(Int::toFloat)
    if(amplitudes.isEmpty() || spikes == 0) {
        return List(spikes) { minHeight }
    }
    val transform = { data: List<Float> ->
        when(amplitudeType) {
            AmplitudeType.Avg -> data.average()
            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)
    }.normalize(minHeight, maxHeight)
}


internal fun <T> Iterable<T>.fillToSize(size: Int, transform: (List<T>) -> T): List<T> {
    val capacity = ceil(size.safeDiv(count())).roundToInt()
    return map { data -> List(capacity) { data } }.flatten().chunkToSize(size, transform)
}

internal fun <T> Iterable<T>.chunkToSize(size: Int, transform: (List<T>) -> T): List<T> {
    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.chunkToSize(size, transform)
    }
}

internal fun Iterable<Float>.normalize(min: Float, max: Float): List<Float> {
    return map { (max-min) * ((it - min()) / (max() - min())) + min }
}

private fun Int.safeDiv(value: Int): Float {
    return if(value == 0) return 0F else this / value.toFloat()
}

`
I really don't know if it's ok to use it like that

App crashing

Hey there, a very weird thing is happening to me. If i run my app it works fine, the moment I generate an APK (debug & prod) the app starts crashing, not sure if there is a version conflict of compose or something

Fatal Exception: java.lang.NoSuchMethodError: No static method drawRoundRect-ZuiqVtQ$default(Landroidx/compose/ui/graphics/drawscope/DrawScope;Landroidx/compose/ui/graphics/Brush;JJJFLandroidx/compose/ui/graphics/drawscope/DrawStyle;Landroidx/compose/ui/graphics/ColorFilter;IILjava/lang/Object;)V in class Landroidx/compose/ui/graphics/drawscope/DrawScope; or its super classes (declaration of 'androidx.compose.ui.graphics.drawscope.DrawScope' appears in /data/app/~~T3hw3VsgkcCWIL9FcsmQJQ==/com.zero.android-64B_I5Q69FNpyFB7i7CAtA==/base.apk)
       at com.linc.audiowaveform.AudioWaveformKt$AudioWaveform$2.invoke(AudioWaveform.kt:100)
       at com.linc.audiowaveform.AudioWaveformKt$AudioWaveform$2.invoke(AudioWaveform.kt:74)
       at androidx.compose.ui.draw.DrawBackgroundModifier.draw(DrawModifier.kt:104)
       at androidx.compose.ui.node.BackwardsCompatNode.draw(BackwardsCompatNode.kt:376)
       at androidx.compose.ui.node.LayoutNodeDrawScope.draw-x_KDEd0$ui_release(LayoutNodeDrawScope.kt:92)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:371)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:236)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.access$drawContainedDrawModifiers(NodeCoordinator.kt:58)
       at androidx.compose.ui.node.NodeCoordinator$invoke$1.invoke(NodeCoordinator.kt:397)
       at androidx.compose.ui.node.NodeCoordinator$invoke$1.invoke(NodeCoordinator.kt:396)
       at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2139)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver$observeReads$1$1.invoke(SnapshotStateObserver.kt:130)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver$observeReads$1$1.invoke(SnapshotStateObserver.kt:126)
       at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341)
       at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(:1)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:126)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:120)
       at androidx.compose.ui.node.NodeCoordinator.invoke(NodeCoordinator.kt:396)
       at androidx.compose.ui.node.NodeCoordinator.invoke(NodeCoordinator.kt:58)
       at androidx.compose.ui.platform.RenderNodeApi29.record(RenderNodeApi29.android.kt:180)
       at androidx.compose.ui.platform.RenderNodeLayer.updateDisplayList(RenderNodeLayer.android.kt:298)
       at androidx.compose.ui.platform.RenderNodeLayer.drawLayer(RenderNodeLayer.android.kt:239)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:355)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:236)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:236)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:236)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:840)
       at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:151)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:236)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:840)
       at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:151)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:236)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:840)
       at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:151)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:236)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:840)
       at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:151)
       at androidx.compose.ui.node.LayoutNodeDrawScope.drawContent(LayoutNodeDrawScope.kt:64)
       at androidx.compose.foundation.Background.draw(Background.kt:107)
       at androidx.compose.ui.node.BackwardsCompatNode.draw(BackwardsCompatNode.kt:376)
       at androidx.compose.ui.node.LayoutNodeDrawScope.draw-x_KDEd0$ui_release(LayoutNodeDrawScope.kt:92)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:371)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:840)
       at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:151)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:840)
       at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:151)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:236)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:236)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:840)
       at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:151)
       at androidx.compose.ui.node.LayoutNodeDrawScope.drawContent(LayoutNodeDrawScope.kt:64)
       at androidx.compose.material.ripple.AndroidRippleIndicationInstance.drawIndication(Ripple.android.kt:184)
       at androidx.compose.foundation.IndicationModifier.draw(Indication.kt:183)
       at androidx.compose.ui.node.BackwardsCompatNode.draw(BackwardsCompatNode.kt:376)
       at androidx.compose.ui.node.LayoutNodeDrawScope.draw-x_KDEd0$ui_release(LayoutNodeDrawScope.kt:92)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:371)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:236)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:840)
       at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:151)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:360)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:236)
       at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:368)
       at androidx.compose.ui.node.NodeCoordinator.access$drawContainedDrawModifiers(NodeCoordinator.kt:58)
       at androidx.compose.ui.node.NodeCoordinator$invoke$1.invoke(NodeCoordinator.kt:397)
       at androidx.compose.ui.node.NodeCoordinator$invoke$1.invoke(NodeCoordinator.kt:396)
       at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2139)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver$observeReads$1$1.invoke(SnapshotStateObserver.kt:130)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver$observeReads$1$1.invoke(SnapshotStateObserver.kt:126)
       at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341)
       at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(:1)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:126)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:120)
       at androidx.compose.ui.node.NodeCoordinator.invoke(NodeCoordinator.kt:396)
       at androidx.compose.ui.node.NodeCoordinator.invoke(NodeCoordinator.kt:58)
       at androidx.compose.ui.platform.RenderNodeApi29.record(RenderNodeApi29.android.kt:180)
       at androidx.compose.ui.platform.RenderNodeLayer.updateDisplayList(RenderNodeLayer.android.kt:298)
       at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:1010)
       at android.view.View.draw(View.java:22818)
       at android.view.View.updateDisplayListIfDirty(View.java:21669)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4588)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4561)
       at android.view.View.updateDisplayListIfDirty(View.java:21626)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4588)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4561)
       at android.view.View.updateDisplayListIfDirty(View.java:21626)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4588)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4561)
       at android.view.View.updateDisplayListIfDirty(View.java:21626)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4588)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4561)
       at android.view.View.updateDisplayListIfDirty(View.java:21626)
       at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:534)
       at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:540)
       at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:620)
       at android.view.ViewRootImpl.draw(ViewRootImpl.java:4763)
       at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4467)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3549)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2322)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9155)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1232)
       at android.view.Choreographer.doCallbacks(Choreographer.java:1029)
       at android.view.ChoreographerExtImpl.checkScrollOptSceneEnable(ChoreographerExtImpl.java:387)
       at android.view.Choreographer.doFrame(Choreographer.java:913)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1217)
       at android.os.Handler.handleCallback(Handler.java:938)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:238)
       at android.os.Looper.loop(Looper.java:349)
       at android.app.ActivityThread.main(ActivityThread.java:8241)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:584)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1034)

Wrong behavior when using an alpha color

Hi, thank you for this compose library

I've got an issue when using a colour with an alpha in waveformBrush, the alpha is also carried over to the progressBrush.

A small example to reproduce:

var waveformProgress by remember { mutableStateOf(0F) }

AudioWaveform(
    amplitudes = amplitude,
    waveformBrush = SolidColor(Color.White.copy(alpha = .5f)),
    progressBrush = SolidColor(Color.Red),
    amplitudeType = AmplitudeType.Max,
    progress = waveformProgress,
    onProgressChange = { waveformProgress = it }
)

And here is the actual rendering, the red does not match, it also has the alpha channel

image

App Crash v1.1.1 - normalize() function returns NaN

App crashes when using silent audio source
ex) amplitudes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

java.lang.IllegalStateException: AnimationVector cannot contain a NaN. AnimationVector1D: value = NaN. Animation: TargetBasedAnimation: 1.0 -> NaN,initial velocity: AnimationVector1D: value = 0.0, duration: 500 ms,animationSpec: androidx.compose.animation.core.VectorizedTweenSpec@64fbf4d, playTimeNanos: 0
    at androidx.compose.animation.core.TargetBasedAnimation.getValueFromNanos(Animation.kt:242)
    at androidx.compose.animation.core.SuspendAnimationKt.animate(SuspendAnimation.kt:233)
    at androidx.compose.animation.core.Animatable$runAnimation$2.invokeSuspend(Animatable.kt:305)
    at androidx.compose.animation.core.Animatable$runAnimation$2.invoke(Unknown Source:8)
    at androidx.compose.animation.core.Animatable$runAnimation$2.invoke(Unknown Source:2)
    at androidx.compose.animation.core.MutatorMutex$mutate$2.invokeSuspend(InternalMutatorMutex.kt:119)
    at androidx.compose.animation.core.MutatorMutex$mutate$2.invoke(Unknown Source:8)
    at androidx.compose.animation.core.MutatorMutex$mutate$2.invoke(Unknown Source:4)
    at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:89)
    at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:264)
    at androidx.compose.animation.core.MutatorMutex.mutate(InternalMutatorMutex.kt:112)
    at androidx.compose.animation.core.MutatorMutex.mutate$default(InternalMutatorMutex.kt:109)
    at androidx.compose.animation.core.Animatable.runAnimation(Animatable.kt:295)
    at androidx.compose.animation.core.Animatable.animateTo(Animatable.kt:238)
    at androidx.compose.animation.core.Animatable.animateTo$default(Animatable.kt:225)
    at androidx.compose.animation.core.AnimateAsStateKt$animateValueAsState$3$1.invokeSuspend(AnimateAsState.kt:428)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch(AndroidUiDispatcher.android.kt:81)
    at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch(AndroidUiDispatcher.android.kt:41)
    at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run(AndroidUiDispatcher.android.kt:57)
    at android.os.Handler.handleCallback(Handler.java:942)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:201)
    at android.os.Looper.loop(Looper.java:288)
    at android.app.ActivityThread.main(ActivityThread.java:7872)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

Chunked amplitudes remainder

  • I get 100 samples from the amplituda call

  • I can have only 80 max, to fit my width
    Chunked doesn't work here because chunked takes an Int so 100 / 80, which is 1.25, will turn out as 1. So I get a 1 to 1 mapping and end up with 100 samples instead of 80.

lincollincol/Amplituda#15

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.