Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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 @@ -317,7 +317,14 @@ class MainActivity : AppCompatActivity() {
ruleFars = ruleFars ?: "",
)!!

tts = OfflineTts(assetManager = assets, config = config)
val cacheConfig = getOfflineTtsCacheMechanismConfig(
dataDir = dataDir ?: "",
cacheSize = 20*1024*1024, // Default is 20 MBs
)!!

val cache = new OfflineTtsCacheMechanism(cacheConfig)

tts = OfflineTts(assetManager = assets, config = config, cache = cache)
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import androidx.compose.material3.Slider
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand All @@ -48,6 +49,8 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import kotlin.math.roundToInt
import kotlinx.coroutines.delay
import kotlin.time.TimeSource

const val TAG = "sherpa-onnx-tts-engine"
Expand Down Expand Up @@ -78,6 +81,9 @@ class MainActivity : ComponentActivity() {
Log.i(TAG, "Finish initializing AudioTrack")

val preferenceHelper = PreferenceHelper(this)

TtsEngine.cacheSize = preferenceHelper.getTtsMechanismCacheSize()

setContent {
SherpaOnnxTtsEngineTheme {
// A surface container using the 'background' color from the theme
Expand All @@ -90,17 +96,44 @@ class MainActivity : ComponentActivity() {
}) {
Box(modifier = Modifier.padding(it)) {
Column(modifier = Modifier.padding(16.dp)) {
// Track used cache size in a mutable state
var usedCacheSize by remember { mutableStateOf(0) }

// LaunchedEffect to periodically update the used cache size
LaunchedEffect(Unit) {
while (true) {
usedCacheSize = TtsEngine.tts?.getTotalUsedCacheSize() ?: 0
delay(5000) // Update every 5 seconds
}
}

Column {
Text("Speed " + String.format("%.1f", TtsEngine.speed))
Slider(
value = TtsEngine.speedState.value,
onValueChange = {
TtsEngine.speed = it
preferenceHelper.setSpeed(it)
TtsEngine.tts?.clearCache() // Call the clearCache method
usedCacheSize = 0 // Reset used cache size
},
valueRange = 0.2F..3.0F,
modifier = Modifier.fillMaxWidth()
)

Text("Cache Size: ${TtsEngine.cacheSize / (1024 * 1024)}MB (${usedCacheSize / (1024 * 1024)}MB used)")
Slider(
value = TtsEngine.cacheSizeState.value.toFloat(),
onValueChange = { newValue ->
// Round the value to the nearest multiple of 5MB
val roundedValue = (newValue / (5 * 1024 * 1024)).roundToInt() * (5 * 1024 * 1024)
TtsEngine.cacheSize = roundedValue
preferenceHelper.setCacheSize(roundedValue)
TtsEngine.tts?.setCacheSize(roundedValue)
},
valueRange = 0f..209715200f, // 200MB
modifier = Modifier.fillMaxWidth()
)
}

val testTextContent = getSampleText(TtsEngine.lang ?: "")
Expand Down Expand Up @@ -213,8 +246,8 @@ class MainActivity : ComponentActivity() {
val RTF = String.format(
"Number of threads: %d\nElapsed: %.3f s\nAudio duration: %.3f s\nRTF: %.3f/%.3f = %.3f",
TtsEngine.tts!!.config.model.numThreads,
audioDuration,
elapsed,
audioDuration,
elapsed,
audioDuration,
elapsed / audioDuration
Expand Down Expand Up @@ -277,6 +310,13 @@ class MainActivity : ComponentActivity() {
}
}

override fun onResume() {
super.onResume()
// Update used cache size when the app is resumed
val usedCacheSize = (TtsEngine.tts?.getTotalUsedCacheSize() ?: 0)
Log.i(TAG, "App resumed. Used cache size: ${usedCacheSize}B")
}

override fun onDestroy() {
stopMediaPlayer()
super.onDestroy()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class PreferenceHelper(context: Context) {
private val PREFS_NAME = "com.k2fsa.sherpa.onnx.tts.engine"
private val SPEED_KEY = "speed"
private val SID_KEY = "speaker_id"
private val CACHE_SIZE_KEY = "cache_size"

private val sharedPreferences: SharedPreferences =
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
Expand All @@ -29,4 +30,14 @@ class PreferenceHelper(context: Context) {
fun getSid(): Int {
return sharedPreferences.getInt(SID_KEY, 0)
}
}

fun setCacheSize(value: Int) {
val editor = sharedPreferences.edit()
editor.putInt(CACHE_SIZE_KEY, value)
editor.apply()
}

fun getTtsMechanismCacheSize(): Int {
return sharedPreferences.getInt(CACHE_SIZE_KEY, 20*(1024*1024)) // Default cache size is 20MB
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import com.k2fsa.sherpa.onnx.OfflineTts
import com.k2fsa.sherpa.onnx.getOfflineTtsConfig
import com.k2fsa.sherpa.onnx.getOfflineTtsCacheMechanismConfig
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
Expand All @@ -25,6 +26,7 @@ object TtsEngine {


val speedState: MutableState<Float> = mutableFloatStateOf(1.0F)
val cacheSizeState: MutableState<Int> = mutableIntStateOf(0)
val speakerIdState: MutableState<Int> = mutableIntStateOf(0)

var speed: Float
Expand All @@ -33,6 +35,12 @@ object TtsEngine {
speedState.value = value
}

var cacheSize: Int
get() = cacheSizeState.value
set(value) {
cacheSizeState.value = value
}

var speakerId: Int
get() = speakerIdState.value
set(value) {
Expand Down Expand Up @@ -166,6 +174,8 @@ object TtsEngine {
//
// This model supports many languages, e.g., English, Chinese, etc.
// We set lang to eng here.


}

fun createTts(context: Context) {
Expand Down Expand Up @@ -204,10 +214,18 @@ object TtsEngine {
ruleFars = ruleFars ?: ""
)

cacheSize = PreferenceHelper(context).getTtsMechanismCacheSize()
val cacheConfig = getOfflineTtsCacheMechanismConfig(
dataDir = dataDir ?: "",
cacheSize = cacheSize,
)

speed = PreferenceHelper(context).getSpeed()
speakerId = PreferenceHelper(context).getSid()

tts = OfflineTts(assetManager = assets, config = config)
val cache = new OfflineTtsCacheMechanism(cacheConfig)

tts = OfflineTts(assetManager = assets, config = config, cache = cache)
}


Expand Down
2 changes: 2 additions & 0 deletions sherpa-onnx/csrc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ if(SHERPA_ONNX_ENABLE_TTS)
kokoro-multi-lang-lexicon.cc
lexicon.cc
melo-tts-lexicon.cc
offline-tts-cache-mechanism-config.cc
offline-tts-cache-mechanism.cc
offline-tts-character-frontend.cc
offline-tts-frontend.cc
offline-tts-impl.cc
Expand Down
35 changes: 35 additions & 0 deletions sherpa-onnx/csrc/offline-tts-cache-mechanism-config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// sherpa-onnx/csrc/offline-tts-cache-mechanism-config.cc
//
// Copyright (c) 2025 @mah92 From Iranian people to the community with love

#include "sherpa-onnx/csrc/offline-tts-cache-mechanism-config.h"

#include <vector>

#include "sherpa-onnx/csrc/file-utils.h"
#include "sherpa-onnx/csrc/macros.h"

namespace sherpa_onnx {

void OfflineTtsCacheMechanismConfig::Register(ParseOptions *po) {
po->Register("tts-cache-dir", &cache_dir,
"Path to the directory containing dict for espeak-ng.");
po->Register("tts-cache-size", &cache_size,
"Cache size for wav files in bytes. After the cache size is filled, wav files are kept based on usage statstics.");
}

bool OfflineTtsCacheMechanismConfig::Validate() const {
return true;
}

std::string OfflineTtsCacheMechanismConfig::ToString() const {
std::ostringstream os;

os << "OfflineTtsCacheMechanismConfig(";
os << "cache_dir=\"" << cache_dir << "\", ";
os << "cache_size=" << cache_size << ")";

return os.str();
}

} // namespace sherpa_onnx
35 changes: 35 additions & 0 deletions sherpa-onnx/csrc/offline-tts-cache-mechanism-config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// sherpa-onnx/csrc/offline-tts-cache-mechanism-config.h
//
// Copyright (c) 2025 @mah92 From Iranian people to the community with love

#ifndef SHERPA_ONNX_CSRC_OFFLINE_TTS_CACHE_MECHANISM_CONFIG_H_
#define SHERPA_ONNX_CSRC_OFFLINE_TTS_CACHE_MECHANISM_CONFIG_H_

#include <string>

#include "sherpa-onnx/csrc/parse-options.h"

namespace sherpa_onnx {

struct OfflineTtsCacheMechanismConfig {

std::string cache_dir;

int32_t cache_size;

OfflineTtsCacheMechanismConfig() = default;

OfflineTtsCacheMechanismConfig(const std::string &cache_dir,
int32_t cache_size)
: cache_dir(cache_dir),
cache_size(cache_size) {}

void Register(ParseOptions *po);
bool Validate() const;

std::string ToString() const;
};

} // namespace sherpa_onnx

#endif // SHERPA_ONNX_CSRC_OFFLINE_TTS_CACHE_MECHANISM_CONFIG_H_
Loading
Loading