diff --git a/app/build.gradle.kts b/app/build.gradle.kts index aedd00a..efcaf35 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,7 +11,7 @@ android { applicationId = "net.mmanningau.speechtokeyboard" minSdk = 28 targetSdk = 36 - versionCode = 8 + versionCode = 9 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/net/mmanningau/speechtokeyboard/TestModelActivity.kt b/app/src/main/java/net/mmanningau/speechtokeyboard/TestModelActivity.kt index bb1f78f..a7ef7a3 100644 --- a/app/src/main/java/net/mmanningau/speechtokeyboard/TestModelActivity.kt +++ b/app/src/main/java/net/mmanningau/speechtokeyboard/TestModelActivity.kt @@ -156,26 +156,28 @@ class TestModelActivity : AppCompatActivity() { private fun stopRecording() { isRecording = false - recordingThread?.join() // Wait for loop to finish + recordingThread?.join() micButton.clearColorFilter() - outputText.text = "$committedText [Stopped]" + + // Just show what we have, don't overwrite with "[Stopped]" + // to prevent visual jarring. + outputText.append("\n[Stopped]") } private fun processAudioLoop() { val sampleRate = 16000 val bufferSize = AudioRecord.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT) + // 1. GUARD CLAUSE: Unpack nullables safely + // If recognizer or stream are null, we stop immediately. + // This creates 'localRec' and 'localStream' which are GUARANTEED non-null. + val localRec = recognizer ?: return + val localStream = stream ?: return + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { return } - // --- FIX START --- - // Capture global variables into local non-null variables. - // If either is null, we just exit the loop safely. - val activeStream = stream ?: return - val activeRecognizer = recognizer ?: return - // --- FIX END --- - val record = AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize) record.startRecording() @@ -186,28 +188,27 @@ class TestModelActivity : AppCompatActivity() { if (ret > 0) { val samples = FloatArray(ret) { buffer[it] / 32768.0f } - // Use 'activeStream' and 'activeRecognizer' (No ? needed anymore) - activeStream.acceptWaveform(samples, sampleRate) + // 2. Use the LOCAL (non-null) variables + localStream.acceptWaveform(samples, sampleRate) - while (activeRecognizer.isReady(activeStream)) { - activeRecognizer.decode(activeStream) + while (localRec.isReady(localStream)) { + localRec.decode(localStream) } - val text = activeRecognizer.getResult(activeStream).text + val text = localRec.getResult(localStream).text + val isEndpoint = localRec.isEndpoint(localStream) if (text.isNotEmpty()) { val cleanText = text.lowercase() + runOnUiThread { - outputText.text = "$committedText $cleanText" - } - - if (activeRecognizer.isEndpoint(activeStream)) { - if (cleanText.isNotBlank()) { + if (isEndpoint) { committedText += "$cleanText " + outputText.text = committedText sendToPico("$cleanText ") - - // Reset the stream - activeRecognizer.reset(activeStream) + localRec.reset(localStream) + } else { + outputText.text = "$committedText $cleanText" } } }