Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ class MainActivity : ComponentActivity() {
private lateinit var viewModel: ModelSettingsViewModel
private lateinit var downloadViewModel: ModelDownloadViewModel

// Cached ParakeetModule: loaded once and reused across transcription calls.
private var parakeetModule: ParakeetModule? = null
private var loadedModelPath: String = ""
private var loadedTokenizerPath: String = ""
private var loadedDataPath: String? = null

enum class Screen {
DOWNLOAD,
MAIN,
Expand Down Expand Up @@ -211,6 +217,35 @@ class MainActivity : ComponentActivity() {
}.start()
}

/**
* Returns the cached ParakeetModule, creating or recreating it only if
* the model settings have changed since the last load.
*/
private fun getOrCreateModule(settings: ModelSettings): ParakeetModule {
val dataPath = settings.dataPath.ifBlank { null }
if (parakeetModule != null &&
loadedModelPath == settings.modelPath &&
loadedTokenizerPath == settings.tokenizerPath &&
loadedDataPath == dataPath
) {
return parakeetModule!!
Comment on lines +224 to +231
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getOrCreateModule() reads/writes parakeetModule and the loaded*Path fields without any synchronization. Since runParakeetOnWavFile() can be invoked from different threads (UI thread via runParakeet() and a background Thread via runParakeetFromFile()), this can race with module creation/close and/or concurrent transcribe() calls. Consider guarding module access with a lock/single-thread executor, and ensure only one transcription can run at a time across all entry points.

Copilot uses AI. Check for mistakes.
}

// Settings changed or first load — close the old module and create a new one.
parakeetModule?.close()
Log.v(TAG, "Loading model: ${settings.modelPath}")
val module = ParakeetModule(
modelPath = settings.modelPath,
tokenizerPath = settings.tokenizerPath,
dataPath = dataPath
)
parakeetModule = module
loadedModelPath = settings.modelPath
loadedTokenizerPath = settings.tokenizerPath
loadedDataPath = dataPath
return module
}

/**
* Common method to run Parakeet on a WAV file path.
*/
Expand All @@ -232,24 +267,18 @@ class MainActivity : ComponentActivity() {
buttonEnabled = false
}

val parakeetModule = ParakeetModule(
modelPath = settings.modelPath,
tokenizerPath = settings.tokenizerPath,
dataPath = settings.dataPath.ifBlank { null }
)
val module = getOrCreateModule(settings)

Log.v(TAG, "Starting transcribe for: $wavFilePath")
runOnUiThread {
statusText = "Transcribing..."
}
val startTime = System.currentTimeMillis()
val result = parakeetModule.transcribe(wavFilePath)
val result = module.transcribe(wavFilePath)
val elapsedTime = System.currentTimeMillis() - startTime
val elapsedSeconds = elapsedTime / 1000.0
Log.v(TAG, "Finished transcribe in ${elapsedSeconds}s")

parakeetModule.close()

runOnUiThread {
transcriptionOutput = result
statusText = "Transcription complete (%.2fs)".format(elapsedSeconds)
Expand Down Expand Up @@ -468,6 +497,8 @@ class MainActivity : ComponentActivity() {
if (isRecording) {
stopRecording()
}
parakeetModule?.close()
parakeetModule = null
}

private val requestPermissionLauncher = registerForActivityResult(
Expand Down
Loading