package com.suno.android.common_data.media.usecase import android.content.Context import android.net.Uri import com.linc.amplituda.Amplituda import com.linc.amplituda.AmplitudaResult.DurationUnit import com.linc.amplituda.Compress import com.suno.android.common_core_utils.SunoLogger import com.suno.android.common_data.media.model.Waveform import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.InputStream import javax.inject.Inject import kotlin.coroutines.Continuation import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlin.time.Duration.Companion.milliseconds class GetAudioWaveformUseCase @Inject constructor( loggerFactory: SunoLogger.Factory, @ApplicationContext private val context: Context, ) { private val logger = loggerFactory.create(this@GetAudioWaveformUseCase) suspend operator fun invoke( uri: Uri, targetSampleRate: Int = 10, ): Result = withContext(Dispatchers.IO) { runCatching { val amplituda = Amplituda(context) val inputStream = checkNotNull(context.contentResolver.openInputStream(uri)) { "Failed to open input stream for URI: $uri" } inputStream.use { stream -> suspendCoroutine { continuation -> processAudio( amplituda = amplituda, stream = stream, targetSampleRate = targetSampleRate, continuation = continuation, ) } } }.onFailure { e -> logger.e(e) { "Error generating audio waveform" } }.getOrElse { e -> when (e) { is UnsatisfiedLinkError -> Result.failure( IllegalStateException("Unable to generate waveform: Native audio library failed to load", e), ) else -> Result.failure(e) } } } private fun processAudio( amplituda: Amplituda, stream: InputStream, targetSampleRate: Int, continuation: Continuation>, ) { amplituda.processAudio(stream, Compress.withParams(Compress.PEAK, targetSampleRate)) .get( { result -> val waveformResult = runCatching { val amplitudes = result.amplitudesAsList() require(amplitudes.isNotEmpty()) { "Audio file contains no amplitude data" } val max = amplitudes.maxOrNull() ?: 0 require(max > 0) { "Audio file is silent" } Waveform( samples = FloatArray(amplitudes.size) { i -> amplitudes[i].toFloat() / max }, duration = result.getAudioDuration(DurationUnit.MILLIS).milliseconds, ) }.onFailure { e -> logger.e(e) { "Error processing waveform data" } } continuation.resume(waveformResult) }, { error -> logger.e(error) { "Error returned from Amplituda processAudio" } }, ) } }