Skip to content

Commit a769dbc

Browse files
committed
allow getting native libs dir from android app, add tflite logs on screen
1 parent e843749 commit a769dbc

File tree

10 files changed

+93
-123
lines changed

10 files changed

+93
-123
lines changed

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,13 @@ make env
7575

7676
2. Inside the Docker environment, build the `whisperkit-cli` CLI using (for Android and Linux):
7777
```
78-
make build [linux | qnn | gpu]
78+
make build [linux | qnn | gpu | jni]
7979
```
8080

8181
The QNN option builds WhisperKit with Qualcomm AI NPU support and the QNN TFLite delegate.
8282
The 'gpu' option is the generic GPU backend for all Android devices from TFLite GPU delegate.
8383
Linux builds are currently CPU-only.
84+
The 'jni' option builds the .so file with JNI library to use on android (using QNN support).
8485

8586
3. Back on the host machine (outside Docker shell), push dependencies to the Android device:
8687
```
@@ -111,7 +112,16 @@ For Linux:
111112
make build linux
112113
```
113114

114-
2. Manually run `whisperkit-cli`:
115+
2. Run on Android with `run_on_android.sh` script:
116+
117+
Log in via adb shell:
118+
```
119+
adb shell
120+
cd /sdcard/argmax/tflite
121+
sh run_on_android.sh
122+
```
123+
124+
3. Manually run `whisperkit-cli`:
115125

116126
Usage:
117127

@@ -130,7 +140,7 @@ export LD_LIBRARY_PATH=/data/local/tmp/lib
130140
whisperkit-cli transcribe --model-path /path/to/openai_whisper-base --audio-path /path/to/inputs/jfk_441khz.m4a
131141
```
132142

133-
3. Sample execution output:
143+
4. Sample execution output:
134144
```
135145
root@cf40510e9b93:/src/AXIE# ./build/linux/whisperkit-cli transcribe --model-path /src/AXIE/models/openai_whisper-small --audio-path /src/AXIE/test/jfk_441khz.m4a
136146
SoC: generic CPU (x86, arm64, etc)

WhisperKit/src/TranscribeTask.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ extern "C" {
3030
#define TRANSCRIBE_TASK_DEFAULT_LIB_DIR "/data/local/tmp/lib"
3131
#define TRANSCRIBE_TASK_DEFAULT_CACHE_DIR "/data/user/0/com.whispertflite/cache"
3232
#elif (QNN_DELEGATE || GPU_DELEGATE)
33-
// for Android QNN or GPU delegatea
33+
// for Android QNN or GPU delegate
3434
#define TRANSCRIBE_TASK_TFLITE_ROOT_PATH "/sdcard/argmax/tflite"
3535
#define TRANSCRIBE_TASK_DEFAULT_LIB_DIR "/data/local/tmp/lib"
3636
#define TRANSCRIBE_TASK_DEFAULT_CACHE_DIR "/data/local/tmp/cache"
@@ -199,7 +199,7 @@ std::unique_ptr<std::string> Runtime::cmdexec(const char* cmd) {
199199

200200
bool Runtime::check_qcom_soc() {
201201
vector<string> supported_socs{
202-
"SM8650", "SM8550", "SM8450","SM8350"
202+
"SM8650", "SM8550", "SM8450","SM8350", "SM7450"
203203
};
204204

205205
auto soc = *cmdexec("getprop ro.soc.model");

androidApp/app/src/main/java/com/whispertflite/MainActivity.kt

Lines changed: 56 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ import androidx.compose.foundation.background
2828
import androidx.compose.foundation.layout.Arrangement
2929
import androidx.compose.foundation.layout.Box
3030
import androidx.compose.foundation.layout.Column
31+
import androidx.compose.foundation.layout.PaddingValues
3132
import androidx.compose.foundation.layout.Row
33+
import androidx.compose.foundation.layout.fillMaxHeight
3234
import androidx.compose.foundation.layout.fillMaxSize
3335
import androidx.compose.foundation.layout.fillMaxWidth
3436
import androidx.compose.foundation.layout.padding
3537
import androidx.compose.foundation.layout.width
38+
import androidx.compose.foundation.lazy.LazyColumn
39+
import androidx.compose.foundation.lazy.items
3640
import androidx.compose.foundation.rememberScrollState
3741
import androidx.compose.foundation.shape.RoundedCornerShape
3842
import androidx.compose.foundation.verticalScroll
@@ -46,8 +50,10 @@ import androidx.compose.material3.Surface
4650
import androidx.compose.material3.Text
4751
import androidx.compose.material3.TextButton
4852
import androidx.compose.runtime.Composable
53+
import androidx.compose.runtime.LaunchedEffect
4954
import androidx.compose.runtime.MutableState
5055
import androidx.compose.runtime.getValue
56+
import androidx.compose.runtime.mutableStateListOf
5157
import androidx.compose.runtime.mutableStateOf
5258
import androidx.compose.runtime.remember
5359
import androidx.compose.runtime.setValue
@@ -58,6 +64,11 @@ import androidx.compose.ui.text.font.FontWeight
5864
import androidx.compose.ui.unit.dp
5965
import androidx.core.content.ContextCompat
6066
import androidx.lifecycle.ViewModelProvider
67+
import kotlinx.coroutines.Dispatchers
68+
import kotlinx.coroutines.isActive
69+
import kotlinx.coroutines.withContext
70+
import java.io.BufferedReader
71+
import java.io.InputStreamReader
6172

6273
/** Sample activity to test the TFLite C API. */
6374
class MainActivity : ComponentActivity() {
@@ -155,6 +166,8 @@ class MainActivity : ComponentActivity() {
155166
StatusSection()
156167
Divider(modifier = Modifier.padding(vertical = 16.dp))
157168
ResultsSection()
169+
Divider(modifier = Modifier.padding(vertical = 16.dp))
170+
LogcatScreen()
158171
}
159172
}
160173

@@ -279,8 +292,10 @@ class MainActivity : ComponentActivity() {
279292
val result by mainViewModel.resultState
280293
Column(
281294
modifier = Modifier
295+
.fillMaxHeight(0.5f)
282296
.fillMaxWidth()
283297
.verticalScroll(rememberScrollState())
298+
284299
) {
285300
Text(
286301
text = result,
@@ -378,114 +393,45 @@ class MainActivity : ComponentActivity() {
378393
}
379394
}
380395

381-
// @Composable
382-
// fun ModelSelectionSection() {
383-
// val modelList by mainViewModel.tfliteFileNamesState
384-
// Surface(
385-
// modifier = Modifier
386-
// .fillMaxWidth()
387-
// .padding(horizontal = 16.dp, vertical = 8.dp),
388-
// color = Color(0xFFC4C4C4),
389-
// shape = RoundedCornerShape(8.dp),
390-
// ) {
391-
// Row(
392-
// modifier = Modifier
393-
// .fillMaxWidth()
394-
// .padding(horizontal = 16.dp, vertical = 8.dp), // Content padding
395-
// verticalAlignment = Alignment.CenterVertically
396-
// ) {
397-
// Text(
398-
// text = "SELECT MODEL",
399-
// modifier = Modifier
400-
// .width(130.dp),
401-
// color = Color(0xFFFFFFFF),
402-
// style = MaterialTheme.typography.bodyMedium.copy(
403-
// fontWeight = FontWeight.Bold
404-
// )
405-
// )
406-
// DropdownMenuComponent(
407-
// items = modelList,
408-
// selectedItemState = mainViewModel.selectedTfLiteFilename,
409-
// modifier = Modifier.weight(5f)
410-
// )
411-
// }
412-
// }
413-
//
414-
// }
415-
416-
// @Composable
417-
// fun LogViewer() {
418-
// val items by mainViewModel.eventsFlow.collectAsStateWithLifecycle()
419-
// LazyColumn (
420-
// modifier = Modifier
421-
// .fillMaxSize()
422-
// .background(Color.LightGray)
423-
// ) {
424-
// item {
425-
// Text(
426-
// text="Hellooo",
427-
// modifier = Modifier.padding(vertical = 4.dp)
428-
// )
429-
// }
430-
// items(items) { item ->
431-
// Text(
432-
// text=item,
433-
// modifier = Modifier.padding(vertical = 4.dp)
434-
// )
435-
// }
436-
//
437-
// }
438-
// }
439-
440-
// private fun logEvent(message: String, logMessages: MutableList<String>, throwable: Throwable? = null) {
441-
// Log.e(TAG, message, throwable)
442-
// logMessages.add(message)
443-
// throwable?.let {
444-
// logMessages.add("${it.javaClass.canonicalName}: ${it.message}")
445-
// logMessages.addAll(it.stackTrace.map { stackElement -> stackElement.toString() })
446-
// }
447-
// }
448-
//
449-
// private fun getFileArrayAdapter(files: ArrayList<File>): ArrayAdapter<File> {
450-
// return object : ArrayAdapter<File>(this, android.R.layout.simple_spinner_item, files) {
451-
// override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
452-
// val view = super.getView(position, convertView, parent)
453-
// val textView: TextView = view.findViewById(android.R.id.text1)
454-
// textView.text = getItem(position)?.name
455-
// return view
456-
// }
457-
//
458-
// override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
459-
// val view = super.getDropDownView(position, convertView, parent)
460-
// val textView: TextView = view.findViewById(android.R.id.text1)
461-
// textView.text = getItem(position)?.name
462-
// return view
463-
// }
464-
// }.apply {
465-
// setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
466-
// }
467-
// }
468-
469-
// class SharedResource {
470-
// private val lock = Object()
471-
//
472-
// fun waitForSignalWithTimeout(timeoutMillis: Long): Boolean {
473-
// val startTime = System.currentTimeMillis()
474-
// synchronized(lock) {
475-
// try {
476-
// lock.wait(timeoutMillis) // Wait for the signal or timeout
477-
// } catch (e: InterruptedException) {
478-
// Thread.currentThread().interrupt() // Restore interrupt status
479-
// return false
480-
// }
481-
// return (System.currentTimeMillis() - startTime) < timeoutMillis
482-
// }
483-
// }
484-
//
485-
// fun sendSignal() {
486-
// synchronized(lock) {
487-
// lock.notify() // Notify the waiting thread
488-
// }
489-
// }
490-
// }
396+
@Composable
397+
fun LogcatScreen() {
398+
// State list to hold log lines
399+
val logs = remember { mutableStateListOf<String>() }
400+
401+
// Launch a coroutine to read from logcat
402+
LaunchedEffect(Unit) {
403+
withContext(Dispatchers.IO) {
404+
try {
405+
// Execute the logcat command
406+
val process = Runtime.getRuntime().exec("logcat tflite:V *:S")
407+
val reader = BufferedReader(InputStreamReader(process.inputStream))
408+
// Continuously read new lines
409+
while (isActive) {
410+
val line = reader.readLine() ?: break
411+
// Post the line to the UI thread
412+
withContext(Dispatchers.Main) {
413+
logs.add(line)
414+
// Optionally, limit the size of the logs list
415+
if (logs.size > 1000) { // for example, keep only the latest 1000 lines
416+
logs.removeAt(0)
417+
}
418+
}
419+
}
420+
} catch (e: Exception) {
421+
e.printStackTrace()
422+
}
423+
}
424+
}
425+
426+
// Display the logs in a scrollable list
427+
LazyColumn(
428+
modifier = Modifier.fillMaxWidth(),
429+
contentPadding = PaddingValues(16.dp)
430+
) {
431+
items(logs) { log ->
432+
Text(text = log)
433+
}
434+
}
435+
}
436+
491437
}

androidApp/app/src/main/java/com/whispertflite/MainViewModel.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ class MainViewModel : ViewModel() {
4141
private lateinit var whisperKit: WhisperKitNative
4242

4343
fun initialize(context: Context) {
44+
val cacheDir = context.cacheDir
45+
Log.d("Cache", cacheDir.absolutePath)
46+
val nativeLibsDir = context.applicationInfo.nativeLibraryDir
4447
copyDataToSdCardFolder(context)
4548
loadAudioFileNames(sdcardDataFolder!!.absolutePath)
4649

@@ -55,7 +58,7 @@ class MainViewModel : ViewModel() {
5558
waveFile = File(sdcardDataFolder!!.absolutePath + "/" + microphoneInputFileName)
5659

5760
viewModelScope.launch(Dispatchers.IO) {
58-
whisperKit = WhisperKitNative(modelDestFolder.absolutePath, audioPath, ".", 4)
61+
whisperKit = WhisperKitNative(modelDestFolder.absolutePath, audioPath, ".", nativeLibsDir, 4)
5962
statusState.value = "IDLE"
6063
}
6164
}

androidApp/app/src/main/java/com/whispertflite/WhisperKitNative.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
public class WhisperKitNative {
44
private long nativePtr;
55

6-
public WhisperKitNative(String modelPath, String audioPath, String reportPath, int concurrentWorkers) {
7-
nativePtr = init(modelPath, audioPath, reportPath, false, concurrentWorkers);
6+
public WhisperKitNative(String modelPath, String audioPath, String reportPath, String nativeLibsDir, int concurrentWorkers) {
7+
nativePtr = init(modelPath, audioPath, reportPath, nativeLibsDir, false, concurrentWorkers);
88
}
99

1010
public String transcribe(String audioPath) {
@@ -15,7 +15,7 @@ public void release() {
1515
release(nativePtr);
1616
}
1717

18-
private native long init(String modelPath, String audioPath, String reportPath, boolean enableReport, int concurrentWorkers);
18+
private native long init(String modelPath, String audioPath, String reportPath, String nativeLibsDir, boolean enableReport, int concurrentWorkers);
1919
private native String transcribe(long nativePtr, String audioPath);
2020
private native void release(long nativePtr);
2121
}
Binary file not shown.
Binary file not shown.

cli/whisperkit_cli.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
#include <string>
66
#include "whisperkit_cli.h"
77

8+
#define TRANSCRIBE_TASK_DEFAULT_LIB_DIR "/data/local/tmp/lib"
9+
10+
811
WhisperKitConfig::WhisperKitConfig() {
912
audioPath = "";
1013
modelPath = "";
@@ -32,7 +35,7 @@ void CHECK_WHISPERKIT_STATUS(whisperkit_status_t status) {
3235
}
3336
}
3437

35-
WhisperKitRunner::WhisperKitRunner(WhisperKitConfig& config) : config(config) {
38+
WhisperKitRunner::WhisperKitRunner(WhisperKitConfig& config, std::string libDir) : config(config) {
3639

3740
whisperkit_status_t status = WHISPERKIT_STATUS_SUCCESS;
3841
status = whisperkit_configuration_create(&configuration);
@@ -41,6 +44,9 @@ WhisperKitRunner::WhisperKitRunner(WhisperKitConfig& config) : config(config) {
4144
status = whisperkit_pipeline_create(&pipeline);
4245
CHECK_WHISPERKIT_STATUS(status);
4346

47+
status = whisperkit_configuration_set_lib_dir(configuration, libDir.c_str());
48+
CHECK_WHISPERKIT_STATUS(status);
49+
4450
}
4551

4652
void WhisperKitRunner::buildPipeline() {
@@ -146,7 +152,7 @@ int main(int argc, char* argv[]) {
146152
std::cerr << "Error parsing options: " << e.what() << std::endl;
147153
return 1;
148154
}
149-
WhisperKitRunner runner(config);
155+
WhisperKitRunner runner(config, std::string(TRANSCRIBE_TASK_DEFAULT_LIB_DIR));
150156

151157
try {
152158
runner.buildPipeline();

cli/whisperkit_cli.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ void CHECK_WHISPERKIT_STATUS(whisperkit_status_t status);
3434

3535
class WhisperKitRunner {
3636
public:
37-
explicit WhisperKitRunner(WhisperKitConfig& config);
37+
explicit WhisperKitRunner(WhisperKitConfig& config, std::string libDir);
3838
~WhisperKitRunner();
3939
void buildPipeline();
4040
void transcribe();

0 commit comments

Comments
 (0)