You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
5.2 KiB
155 lines
5.2 KiB
package com.whispercppdemo.whisper
|
|
|
|
import android.content.res.AssetManager
|
|
import android.os.Build
|
|
import android.util.Log
|
|
import kotlinx.coroutines.*
|
|
import java.io.File
|
|
import java.io.InputStream
|
|
import java.util.concurrent.Executors
|
|
|
|
private const val LOG_TAG = "LibWhisper"
|
|
|
|
class WhisperContext private constructor(private var ptr: Long) {
|
|
// Meet Whisper C++ constraint: Don't access from more than one thread at a time.
|
|
private val scope: CoroutineScope = CoroutineScope(
|
|
Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
|
)
|
|
|
|
suspend fun transcribeData(data: FloatArray): String = withContext(scope.coroutineContext) {
|
|
require(ptr != 0L)
|
|
WhisperLib.fullTranscribe(ptr, data)
|
|
val textCount = WhisperLib.getTextSegmentCount(ptr)
|
|
return@withContext buildString {
|
|
for (i in 0 until textCount) {
|
|
append(WhisperLib.getTextSegment(ptr, i))
|
|
}
|
|
}
|
|
}
|
|
|
|
suspend fun benchMemory(nthreads: Int): String = withContext(scope.coroutineContext) {
|
|
return@withContext WhisperLib.benchMemcpy(nthreads)
|
|
}
|
|
|
|
suspend fun benchGgmlMulMat(nthreads: Int): String = withContext(scope.coroutineContext) {
|
|
return@withContext WhisperLib.benchGgmlMulMat(nthreads)
|
|
}
|
|
|
|
suspend fun release() = withContext(scope.coroutineContext) {
|
|
if (ptr != 0L) {
|
|
WhisperLib.freeContext(ptr)
|
|
ptr = 0
|
|
}
|
|
}
|
|
|
|
protected fun finalize() {
|
|
runBlocking {
|
|
release()
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
fun createContextFromFile(filePath: String): WhisperContext {
|
|
val ptr = WhisperLib.initContext(filePath)
|
|
if (ptr == 0L) {
|
|
throw java.lang.RuntimeException("Couldn't create context with path $filePath")
|
|
}
|
|
return WhisperContext(ptr)
|
|
}
|
|
|
|
fun createContextFromInputStream(stream: InputStream): WhisperContext {
|
|
val ptr = WhisperLib.initContextFromInputStream(stream)
|
|
|
|
if (ptr == 0L) {
|
|
throw java.lang.RuntimeException("Couldn't create context from input stream")
|
|
}
|
|
return WhisperContext(ptr)
|
|
}
|
|
|
|
fun createContextFromAsset(assetManager: AssetManager, assetPath: String): WhisperContext {
|
|
val ptr = WhisperLib.initContextFromAsset(assetManager, assetPath)
|
|
|
|
if (ptr == 0L) {
|
|
throw java.lang.RuntimeException("Couldn't create context from asset $assetPath")
|
|
}
|
|
return WhisperContext(ptr)
|
|
}
|
|
|
|
fun getSystemInfo(): String {
|
|
return WhisperLib.getSystemInfo()
|
|
}
|
|
}
|
|
}
|
|
|
|
private class WhisperLib {
|
|
companion object {
|
|
init {
|
|
Log.d(LOG_TAG, "Primary ABI: ${Build.SUPPORTED_ABIS[0]}")
|
|
var loadVfpv4 = false
|
|
var loadV8fp16 = false
|
|
if (isArmEabiV7a()) {
|
|
// armeabi-v7a needs runtime detection support
|
|
val cpuInfo = cpuInfo()
|
|
cpuInfo?.let {
|
|
Log.d(LOG_TAG, "CPU info: $cpuInfo")
|
|
if (cpuInfo.contains("vfpv4")) {
|
|
Log.d(LOG_TAG, "CPU supports vfpv4")
|
|
loadVfpv4 = true
|
|
}
|
|
}
|
|
} else if (isArmEabiV8a()) {
|
|
// ARMv8.2a needs runtime detection support
|
|
val cpuInfo = cpuInfo()
|
|
cpuInfo?.let {
|
|
Log.d(LOG_TAG, "CPU info: $cpuInfo")
|
|
if (cpuInfo.contains("fphp")) {
|
|
Log.d(LOG_TAG, "CPU supports fp16 arithmetic")
|
|
loadV8fp16 = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if (loadVfpv4) {
|
|
Log.d(LOG_TAG, "Loading libwhisper_vfpv4.so")
|
|
System.loadLibrary("whisper_vfpv4")
|
|
} else if (loadV8fp16) {
|
|
Log.d(LOG_TAG, "Loading libwhisper_v8fp16_va.so")
|
|
System.loadLibrary("whisper_v8fp16_va")
|
|
} else {
|
|
Log.d(LOG_TAG, "Loading libwhisper.so")
|
|
System.loadLibrary("whisper")
|
|
}
|
|
}
|
|
|
|
// JNI methods
|
|
external fun initContextFromInputStream(inputStream: InputStream): Long
|
|
external fun initContextFromAsset(assetManager: AssetManager, assetPath: String): Long
|
|
external fun initContext(modelPath: String): Long
|
|
external fun freeContext(contextPtr: Long)
|
|
external fun fullTranscribe(contextPtr: Long, audioData: FloatArray)
|
|
external fun getTextSegmentCount(contextPtr: Long): Int
|
|
external fun getTextSegment(contextPtr: Long, index: Int): String
|
|
external fun getSystemInfo(): String
|
|
external fun benchMemcpy(nthread: Int): String
|
|
external fun benchGgmlMulMat(nthread: Int): String
|
|
}
|
|
}
|
|
|
|
private fun isArmEabiV7a(): Boolean {
|
|
return Build.SUPPORTED_ABIS[0].equals("armeabi-v7a")
|
|
}
|
|
|
|
private fun isArmEabiV8a(): Boolean {
|
|
return Build.SUPPORTED_ABIS[0].equals("arm64-v8a")
|
|
}
|
|
|
|
private fun cpuInfo(): String? {
|
|
return try {
|
|
File("/proc/cpuinfo").inputStream().bufferedReader().use {
|
|
it.readText()
|
|
}
|
|
} catch (e: Exception) {
|
|
Log.w(LOG_TAG, "Couldn't read /proc/cpuinfo", e)
|
|
null
|
|
}
|
|
} |