diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index efcaf35..41a6038 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -11,8 +11,8 @@ android {
applicationId = "net.mmanningau.speechtokeyboard"
minSdk = 28
targetSdk = 36
- versionCode = 9
- versionName = "1.0"
+ versionCode = 10
+ versionName = "1.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..c63de09
Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/java/net/mmanningau/speechtokeyboard/MainActivity.kt b/app/src/main/java/net/mmanningau/speechtokeyboard/MainActivity.kt
index ed3a182..6d7b73d 100644
--- a/app/src/main/java/net/mmanningau/speechtokeyboard/MainActivity.kt
+++ b/app/src/main/java/net/mmanningau/speechtokeyboard/MainActivity.kt
@@ -89,7 +89,7 @@ class MainActivity : AppCompatActivity() {
var entry = zipInputStream.nextEntry
var foundEncoder = false
var foundDecoder = false
- var foundJoiner = false
+ // var foundJoiner = false - removed for true Whisper model use
var foundTokens = false
while (entry != null) {
@@ -100,7 +100,7 @@ class MainActivity : AppCompatActivity() {
val targetFileName = when {
name.contains("encoder") && name.endsWith(".onnx") -> "encoder.onnx"
name.contains("decoder") && name.endsWith(".onnx") -> "decoder.onnx"
- name.contains("joiner") && name.endsWith(".onnx") -> "joiner.onnx"
+ // name.contains("joiner") && name.endsWith(".onnx") -> "joiner.onnx" - removed for true Whisper model use
name.contains("tokens.txt") -> "tokens.txt"
else -> null
}
@@ -115,7 +115,7 @@ class MainActivity : AppCompatActivity() {
when (targetFileName) {
"encoder.onnx" -> foundEncoder = true
"decoder.onnx" -> foundDecoder = true
- "joiner.onnx" -> foundJoiner = true
+ // "joiner.onnx" -> foundJoiner = true = re,moved for true Whisper model use
"tokens.txt" -> foundTokens = true
}
}
@@ -124,7 +124,8 @@ class MainActivity : AppCompatActivity() {
}
runOnUiThread {
- if (foundEncoder && foundDecoder && foundJoiner && foundTokens) {
+ // if (foundEncoder && foundDecoder && foundJoiner && foundTokens) { - removed for true Whisper model use
+ if (foundEncoder && foundDecoder && foundTokens) {
statusText.text = "Model Installed Successfully!"
Toast.makeText(this, "Ready to use!", Toast.LENGTH_SHORT).show()
} else {
diff --git a/app/src/main/java/net/mmanningau/speechtokeyboard/TestModelActivity.kt b/app/src/main/java/net/mmanningau/speechtokeyboard/TestModelActivity.kt
index a7ef7a3..1e35d4e 100644
--- a/app/src/main/java/net/mmanningau/speechtokeyboard/TestModelActivity.kt
+++ b/app/src/main/java/net/mmanningau/speechtokeyboard/TestModelActivity.kt
@@ -20,11 +20,23 @@ import com.hoho.android.usbserial.driver.UsbSerialProber
import com.hoho.android.usbserial.util.SerialInputOutputManager
import com.k2fsa.sherpa.onnx.EndpointConfig
import com.k2fsa.sherpa.onnx.EndpointRule
+/*
import com.k2fsa.sherpa.onnx.FeatureConfig
+import com.k2fsa.sherpa.onnx.OnlineModelConfig
import com.k2fsa.sherpa.onnx.OnlineRecognizer
import com.k2fsa.sherpa.onnx.OnlineRecognizerConfig
import com.k2fsa.sherpa.onnx.OnlineTransducerModelConfig
import com.k2fsa.sherpa.onnx.OnlineStream
+
+ */
+// Below for the "offline" libraries and the true Whisper integration
+import com.k2fsa.sherpa.onnx.OfflineRecognizer
+import com.k2fsa.sherpa.onnx.OfflineStream
+import com.k2fsa.sherpa.onnx.OfflineRecognizerConfig
+import com.k2fsa.sherpa.onnx.OfflineModelConfig
+import com.k2fsa.sherpa.onnx.OfflineWhisperModelConfig
+import com.k2fsa.sherpa.onnx.FeatureConfig
+
import java.io.File
class TestModelActivity : AppCompatActivity() {
@@ -34,8 +46,10 @@ class TestModelActivity : AppCompatActivity() {
private lateinit var micButton: ImageButton
// Sherpa (Whisper) Components
- private var recognizer: OnlineRecognizer? = null
- private var stream: OnlineStream? = null
+ // private var recognizer: OnlineRecognizer? = null // - Removed for true Whisper model usa
+ // private var stream: OnlineStream? = null // - Removed for true Whisper model usa
+ private var recognizer: OfflineRecognizer? = null // Was OnlineRecognizer
+ private var stream: OfflineStream? = null // Was OnlineStream
private var isRecording = false
private var recordingThread: Thread? = null
@@ -76,46 +90,37 @@ class TestModelActivity : AppCompatActivity() {
return
}
+ // 1. Point to your files
+ val encoderPath = File(modelDir, "encoder.onnx").absolutePath
+ val decoderPath = File(modelDir, "decoder.onnx").absolutePath
+ val tokensPath = File(modelDir, "tokens.txt").absolutePath
+
try {
- // 1. Define Model Paths
- val transducerConfig = OnlineTransducerModelConfig(
- encoder = File(modelDir, "encoder.onnx").absolutePath,
- decoder = File(modelDir, "decoder.onnx").absolutePath,
- joiner = File(modelDir, "joiner.onnx").absolutePath
- )
-
- // 2. Define General Config
- val onlineModelConfig = com.k2fsa.sherpa.onnx.OnlineModelConfig(
- transducer = transducerConfig,
- tokens = File(modelDir, "tokens.txt").absolutePath,
- numThreads = 1,
- debug = false,
- modelType = "zipformer"
- )
-
- // 3. Define Endpoint Rule (The fix for your error)
- // rule1 = detected silence after speech. We set this to 2.4 seconds.
- val silenceRule = EndpointRule(
- mustContainNonSilence = false,
- minTrailingSilence = 2.4f,
- minUtteranceLength = 0.0f
- )
-
- // 4. Create Recognizer Config
- val config = OnlineRecognizerConfig(
+ // CONFIGURATION FOR WHISPER (OFFLINE)
+ val config = OfflineRecognizerConfig(
featConfig = FeatureConfig(sampleRate = 16000, featureDim = 80),
- modelConfig = onlineModelConfig,
- endpointConfig = EndpointConfig(rule1 = silenceRule), // Pass the rule object here
- enableEndpoint = true,
+ modelConfig = OfflineModelConfig(
+ // This parameter 'whisper' exists here!
+ whisper = OfflineWhisperModelConfig(
+ encoder = encoderPath,
+ decoder = decoderPath,
+ // tokenizer is not strictly needed in config here if passed in tokens param below
+ // but usually standard offline config uses just these two:
+ ),
+ tokens = tokensPath,
+ modelType = "whisper",
+ debug = false,
+ numThreads = 1
+ ),
decodingMethod = "greedy_search",
maxActivePaths = 4
)
- // recognizer = OnlineRecognizer(assetManager = assets, config = config)
- recognizer = OnlineRecognizer(config = config)
+ // Initialize OFFLINE Engine
+ recognizer = OfflineRecognizer(config = config)
stream = recognizer?.createStream()
- outputText.text = "Engine Loaded. Ready to Stream."
+ outputText.text = "Whisper Engine Ready."
} catch (e: Exception) {
Log.e("Sherpa", "Init Error", e)
@@ -156,21 +161,46 @@ class TestModelActivity : AppCompatActivity() {
private fun stopRecording() {
isRecording = false
- recordingThread?.join()
+ try {
+ recordingThread?.join() // Wait for loop to finish
+ } catch (e: InterruptedException) {
+ // Handle interruption if necessary
+ }
+
micButton.clearColorFilter()
- // Just show what we have, don't overwrite with "[Stopped]"
- // to prevent visual jarring.
- outputText.append("\n[Stopped]")
+ // FIX: Safely unwrap 'stream' before passing it to getResult
+ // This reads: "If stream is NOT null, call getResult. Otherwise return empty string."
+ val finalCurrentText = stream?.let { activeStream ->
+ recognizer?.getResult(activeStream)?.text
+ } ?: ""
+
+ val cleanFinal = finalCurrentText.lowercase()
+
+ if (cleanFinal.isNotEmpty()) {
+ // 1. Commit to history
+ committedText += "$cleanFinal "
+
+ // 2. Send to Pico
+ sendToPico("$cleanFinal ")
+
+ // 3. Update UI
+ outputText.text = "$committedText \n[Stopped]"
+
+ // 4. Reset for next time
+ // We release the old stream and create a fresh one for the next sentence
+ stream?.release()
+ stream = recognizer?.createStream()
+ } else {
+ outputText.append("\n[Stopped - No Text]")
+ }
}
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.
+ // 1. GUARD CLAUSE (Safely unwrap nullables)
val localRec = recognizer ?: return
val localStream = stream ?: return
@@ -188,28 +218,24 @@ class TestModelActivity : AppCompatActivity() {
if (ret > 0) {
val samples = FloatArray(ret) { buffer[it] / 32768.0f }
- // 2. Use the LOCAL (non-null) variables
+ // 2. Feed Audio
localStream.acceptWaveform(samples, sampleRate)
- while (localRec.isReady(localStream)) {
- localRec.decode(localStream)
- }
+ // 3. Decode (No isReady check needed for Offline)
+ localRec.decode(localStream)
+ // 4. Get Current Text
+ // Whisper updates this string constantly as it hears more
val text = localRec.getResult(localStream).text
- val isEndpoint = localRec.isEndpoint(localStream)
if (text.isNotEmpty()) {
val cleanText = text.lowercase()
runOnUiThread {
- if (isEndpoint) {
- committedText += "$cleanText "
- outputText.text = committedText
- sendToPico("$cleanText ")
- localRec.reset(localStream)
- } else {
- outputText.text = "$committedText $cleanText"
- }
+ // Update the screen so user sees what is happening
+ // We do NOT send to USB yet, because Whisper might change this text
+ // as you keep speaking.
+ outputText.text = "$committedText $cleanText"
}
}
}
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..036d09b
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..036d09b
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml
deleted file mode 100644
index 6f3b755..0000000
--- a/app/src/main/res/mipmap-anydpi/ic_launcher.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
deleted file mode 100644
index 6f3b755..0000000
--- a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
index c209e78..bc80388 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..0020c64
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
index b2dfe3d..6f29998 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
index 4f0f1d6..db7b700 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..7e26258
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
index 62b611d..73ab348 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
index 948a307..36f91c4 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..2531c92
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
index 1b9a695..cdf0cab 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
index 28d4b77..6b7c6e6 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..c5d2d7e
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
index 9287f50..13024dd 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
index aa7d642..a53a10c 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..cb1a443
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
index 9126ae3..98c334b 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 0000000..8be8333
--- /dev/null
+++ b/app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #0878F5
+
\ No newline at end of file