Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App crashed when notifydatasetChanged is called() #55

Open
Usama99786 opened this issue Nov 4, 2022 · 3 comments
Open

App crashed when notifydatasetChanged is called() #55

Usama99786 opened this issue Nov 4, 2022 · 3 comments
Assignees
Labels
help wanted Extra attention is needed

Comments

@Usama99786
Copy link

I am building a chat module. So whenever I send an audio in the chat and the recyclerview adapter is refreshed, It crashes on

mBinding?.waveform?.setSampleFrom(file_path)

in the adapter. It also crashes randomly sometimes when there are a lot of audio messages as well. It gives the following exception
W/System.err: linc.com.amplituda.exceptions.processing.PacketSubmittingException: Error submitting a packet for decoding!
I would really appreciate some help here.
I am using

implementation 'com.github.lincollincol:amplituda:2.2.2'
implementation 'com.github.massoudss:waveformSeekBar:5.0.2'

@lincollincol
Copy link
Owner

@Usama99786 Hello! Could you please provide an audio file or code example?
You can also try to catch errors with AmplitudaErrorListener (more here)

@lincollincol lincollincol self-assigned this Nov 8, 2022
@lincollincol lincollincol added the help wanted Extra attention is needed label Nov 8, 2022
@Usama99786
Copy link
Author

@lincollincol Thanks for replying. But as I said that it does not crash on a specific audio file. It crashes when there are a lot of audio files. And I am not using any code related to amplituda. I am just generating a waveform using
mBinding?.waveform?.setSampleFrom(file_path)
Is there a way to use wavformSeekbar with amplituda?

@lincollincol
Copy link
Owner

@Usama99786
I think in your case you should use Amplituda separately from WaveformSeekBar library. For example:

  • Process audio using Amplituda at your data layer
  • Show processed data at your UI layer (setSampleFrom())

Here is my example code:
Data layer

class LocalAudioDataSource(
    private val amplituda: Amplituda,
    private val context: Context
) {

    private val contentResolver: ContentResolver get() = context.contentResolver
    private val audioProjection: Array<String> get() = arrayOf(
        MediaStore.Audio.AudioColumns._ID,
        MediaStore.Audio.AudioColumns.DATE_ADDED,
        MediaStore.Audio.AudioColumns.DISPLAY_NAME,
        MediaStore.Audio.AudioColumns.DURATION,
        MediaStore.Audio.Media.DATA,
        MediaStore.Audio.AudioColumns.SIZE
    )

    suspend fun loadAudioFiles() : List<LocalAudio> = withContext(Dispatchers.IO) {
        val contentUri = when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ->
                MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
            else -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
        }
        val content = mutableListOf<LocalAudio?>()
        val cursor = contentResolver.query(
            contentUri,
            audioProjection,
            null,
            null,
            "${MediaStore.MediaColumns.DATE_ADDED} DESC LIMIT 100"
        )
        cursor?.use {
            while (it.moveToNext()) {
                content.add(it.toLocalAudio())
            }
        }
        return@withContext content.filterNotNull()
    }

    private fun Cursor.toLocalAudio(): LocalAudio? {
        try {
            val data = getColumnIndex(MediaStore.Audio.Media.DATA).let(::getString)
            if(!File(data).exists()) {
                return null
            }
            val amplitudes = amplituda.processAudio(data, Cache.withParams(Cache.REUSE))
                .get(AmplitudaErrorListener { it.printStackTrace() })
                .amplitudesAsList()
            return LocalAudio(
                path = data,
                name = getColumnIndex(MediaStore.Audio.AudioColumns.DISPLAY_NAME).let(::getString),
                amplitudes = amplitudes
            )
        } catch (e: FileNotFoundException) {
            return null
        }
    }
}

data class LocalAudio(
    val path: String,
    val name: String,
    val amplitudes: List<Int>
)

Ui layer
RecyclerView adapter

class WaveformsAdapter : RecyclerView.Adapter<WaveformsAdapter.WaveformViewHolder>() {

    var items: MutableList<LocalAudio> = mutableListOf()
        set(value) {
            field.clear()
            field.addAll(value)
            notifyDataSetChanged()
        }

    inner class WaveformViewHolder(
        view: View
    ) : RecyclerView.ViewHolder(view) {
        fun bind(audio: LocalAudio) {
            itemView.findViewById<WaveformSeekBar>(R.id.waveformView)
                .setSampleFrom(audio.amplitudes.toIntArray())
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WaveformViewHolder {
        return LayoutInflater.from(parent.context)
            .inflate(R.layout.item_waveform, parent, false)
            .let(::WaveformViewHolder)
    }

    override fun onBindViewHolder(holder: WaveformViewHolder, position: Int) {
        holder.bind(items[position])
    }

    override fun getItemCount(): Int = items.count()

}

Activity/Fragment

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val waveformsAdapter = WaveformsAdapter()
        findViewById<RecyclerView>(R.id.audioRecyclerView).apply {
            layoutManager = LinearLayoutManager(this@MainActivity)
            adapter = waveformsAdapter
        }
        
        /* Note!
         It would be better to move code below to ViewModel
        */
        val localAudioSource = LocalAudioDataSource(
            Amplituda(applicationContext),
            applicationContext
        )
        coroutineScope.launch {
            try {
                val files = localAudioSource.loadAudioFiles()
                files.forEach {
                    println(it.toString())
                }
                withContext(Dispatchers.Main) {
                    waveformsAdapter.items = files.toMutableList()
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants