From 7d3571289a1cbc518049b1a25d87924da4860d5b Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 17 Jan 2025 19:14:01 +0330 Subject: [PATCH 01/14] integrated caching feature into last commit --- .../sherpa/onnx/tts/engine/MainActivity.kt | 55 +++- .../onnx/tts/engine/PreferencesHelper.kt | 13 +- .../k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt | 10 + sherpa-onnx/csrc/CMakeLists.txt | 1 + .../csrc/offline-tts-cache-mechanism.cc | 267 ++++++++++++++++++ .../csrc/offline-tts-cache-mechanism.h | 82 ++++++ sherpa-onnx/csrc/offline-tts.cc | 77 ++++- sherpa-onnx/csrc/offline-tts.h | 18 ++ sherpa-onnx/jni/offline-tts.cc | 47 +++ sherpa-onnx/kotlin-api/Tts.kt | 22 ++ 10 files changed, 587 insertions(+), 5 deletions(-) create mode 100644 sherpa-onnx/csrc/offline-tts-cache-mechanism.cc create mode 100644 sherpa-onnx/csrc/offline-tts-cache-mechanism.h diff --git a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt index f7e34c5dd9..c5fb8d9c6b 100644 --- a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt +++ b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt @@ -28,6 +28,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 @@ -37,6 +38,8 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import com.k2fsa.sherpa.onnx.tts.engine.ui.theme.SherpaOnnxTtsEngineTheme import java.io.File +import kotlin.math.roundToInt +import kotlinx.coroutines.delay const val TAG = "sherpa-onnx-tts-engine" @@ -49,6 +52,9 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) TtsEngine.createTts(this) val preferenceHelper = PreferenceHelper(this) + + TtsEngine.cacheSize = preferenceHelper.getCacheSizeInMB() + setContent { SherpaOnnxTtsEngineTheme { // A surface container using the 'background' color from the theme @@ -61,6 +67,17 @@ class MainActivity : ComponentActivity() { }) { Box(modifier = Modifier.padding(it)) { Column(modifier = Modifier.padding(16.dp)) { + // Track used cache size in a mutable state + var usedCacheSizeMB by remember { mutableStateOf(0) } + + // LaunchedEffect to periodically update the used cache size + LaunchedEffect(Unit) { + while (true) { + usedCacheSizeMB = TtsEngine.tts?.getTotalUsedCacheSizeInMB() ?: 0 + delay(5000) // Update every 5 seconds + } + } + Column { Text("Speed " + String.format("%.1f", TtsEngine.speed)) Slider( @@ -72,6 +89,20 @@ class MainActivity : ComponentActivity() { valueRange = 0.2F..3.0F, modifier = Modifier.fillMaxWidth() ) + + Text("Cache Size: ${TtsEngine.cacheSize}MB (${usedCacheSizeMB}MB used)") + Slider( + value = TtsEngine.cacheSizeState.value.toFloat(), + onValueChange = { newValue -> + // Round the value to the nearest multiple of 10 + val roundedValue = (newValue / 5).roundToInt() * 5 + TtsEngine.cacheSize = roundedValue + preferenceHelper.setCacheSizeInMB(roundedValue) + TtsEngine.tts?.setCacheSizeInMB(roundedValue) + }, + valueRange = 0f..100f, + modifier = Modifier.fillMaxWidth() + ) } val testTextContent = getSampleText(TtsEngine.lang ?: "") @@ -166,6 +197,21 @@ class MainActivity : ComponentActivity() { }) { Text("Reset") } + + Button( + modifier = Modifier.padding(20.dp), + onClick = { + TtsEngine.tts?.clearCache() // Call the clearCache method + usedCacheSizeMB = 0 // Reset used cache size + Toast.makeText( + applicationContext, + "Cache cleared!", + Toast.LENGTH_SHORT + ).show() + } + ) { + Text("Clear") + } } } } @@ -175,6 +221,13 @@ class MainActivity : ComponentActivity() { } } + override fun onResume() { + super.onResume() + // Update used cache size when the app is resumed + val usedCacheSizeMB = TtsEngine.tts?.getTotalUsedCacheSizeInMB() ?: 0 + Log.i(TAG, "App resumed. Used cache size: ${usedCacheSizeMB}MB") + } + override fun onDestroy() { stopMediaPlayer() super.onDestroy() @@ -185,4 +238,4 @@ class MainActivity : ComponentActivity() { mediaPlayer?.release() mediaPlayer = null } -} +} \ No newline at end of file diff --git a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/PreferencesHelper.kt b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/PreferencesHelper.kt index 57a6e324ca..314ffa87de 100644 --- a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/PreferencesHelper.kt +++ b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/PreferencesHelper.kt @@ -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) @@ -29,4 +30,14 @@ class PreferenceHelper(context: Context) { fun getSid(): Int { return sharedPreferences.getInt(SID_KEY, 0) } -} \ No newline at end of file + + fun setCacheSizeInMB(value: Int) { + val editor = sharedPreferences.edit() + editor.putInt(CACHE_SIZE_KEY, value) + editor.apply() + } + + fun getCacheSizeInMB(): Int { + return sharedPreferences.getInt(CACHE_SIZE_KEY, 20) // Default cache size is 20MB + } +} diff --git a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt index cec07ffd52..65eeea04b2 100644 --- a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt +++ b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt @@ -25,6 +25,7 @@ object TtsEngine { val speedState: MutableState = mutableFloatStateOf(1.0F) + val cacheSizeState: MutableState = mutableIntStateOf(0) val speakerIdState: MutableState = mutableIntStateOf(0) var speed: Float @@ -33,6 +34,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) { @@ -176,8 +183,11 @@ object TtsEngine { speed = PreferenceHelper(context).getSpeed() speakerId = PreferenceHelper(context).getSid() + cacheSize = PreferenceHelper(context).getCacheSizeInMB() tts = OfflineTts(assetManager = assets, config = config) + + tts?.setCacheSizeInMB(cacheSize) } diff --git a/sherpa-onnx/csrc/CMakeLists.txt b/sherpa-onnx/csrc/CMakeLists.txt index d5303b7572..9dde417dcf 100644 --- a/sherpa-onnx/csrc/CMakeLists.txt +++ b/sherpa-onnx/csrc/CMakeLists.txt @@ -167,6 +167,7 @@ if(SHERPA_ONNX_ENABLE_TTS) offline-tts-vits-model.cc offline-tts.cc piper-phonemize-lexicon.cc + offline-tts-cache-mechanism.cc ) endif() diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc new file mode 100644 index 0000000000..db29a6df4e --- /dev/null +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc @@ -0,0 +1,267 @@ +// sherpa-onnx/csrc/offline-tts-cache-mechanism.cc +// +// @mah92 From Iranian people to the comunity with love + +#include "sherpa-onnx/csrc/offline-tts-cache-mechanism.h" + +#include +#include +#include +#include +#include + +#include "sherpa-onnx/csrc/file-utils.h" +#include "sherpa-onnx/csrc/macros.h" +#include "sherpa-onnx/csrc/wave-reader.h" +#include "sherpa-onnx/csrc/wave-writer.h" + +namespace sherpa_onnx { + +CacheMechanism::CacheMechanism(const std::string &cache_dir, int32_t cache_size) + : cache_dir_(cache_dir), cache_size_bytes_(cache_size), used_cache_size_bytes_(0) { + // Create the cache directory if it doesn't exist + if (!std::filesystem::exists(cache_dir_)) { + std::filesystem::create_directory(cache_dir_); + // SHERPA_ONNX_LOGE("Created cache directory: %s", cache_dir_.c_str()); + } + + // Load the repeat counts + LoadRepeatCounts(); + + // Update the cache vector and calculate the total cache size + UpdateCacheVector(); + + // Initialize the last save time + last_save_time_ = std::chrono::steady_clock::now(); +} + +CacheMechanism::~CacheMechanism() { + // Save the repeat counts on destruction + SaveRepeatCounts(); +} + +void CacheMechanism::AddWavFile(const std::string &text_hash, const std::vector &samples, int32_t sample_rate) { + std::lock_guard lock(mutex_); + + std::string file_path = cache_dir_ + "/" + text_hash + ".wav"; + + // Check if the file physically exists in the cache directory + bool file_exists = std::filesystem::exists(file_path); + + if (!file_exists) { // If the file does not exist, add it to the cache + // Ensure the cache does not exceed its size limit + EnsureCacheLimit(); + + // Write the audio samples to a WAV file + bool success = WriteWave(file_path, sample_rate, samples.data(), samples.size()); + if (success) { + // Calculate the size of the new WAV file and add it to the total cache size + std::ifstream file(file_path, std::ios::binary | std::ios::ate); + if (file.is_open()) { + used_cache_size_bytes_ += file.tellg(); + file.close(); + } + // SHERPA_ONNX_LOGE("Added new wav file to cache: %s, samples:%d", file_path.c_str(), samples.size()); + } else { + SHERPA_ONNX_LOGE("Failed to write wav file: %s", file_path.c_str()); + } + } + + // Ensure the text_hash exists in the map before incrementing the count + if (repeat_counts_.find(text_hash) == repeat_counts_.end()) { + repeat_counts_[text_hash] = 0; // Initialize if it doesn't exist + cache_vector_.push_back(text_hash); // Add the text_hash to the cache vector + } + repeat_counts_[text_hash]++; // Increment the repeat count + + // Save the repeat counts every 10 minutes + auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast(now - last_save_time_).count() >= 10 * 60) { + SaveRepeatCounts(); + last_save_time_ = now; + } +} + +std::vector CacheMechanism::GetWavFile(const std::string &text_hash, int32_t &sample_rate) { + std::lock_guard lock(mutex_); + + std::vector samples; + std::string file_path = cache_dir_ + "/" + text_hash + ".wav"; + + if (std::filesystem::exists(file_path)) { + bool is_ok = false; + samples = ReadWave(file_path, &sample_rate, &is_ok); + + if (is_ok) { + // SHERPA_ONNX_LOGE("Retrieved cached audio for text hash: %s, sample count: %d, freq:%dHz", text_hash.c_str(), samples.size(), sample_rate); + } else { + SHERPA_ONNX_LOGE("Failed to read cached file: %s", file_path.c_str()); + } + } else { + // SHERPA_ONNX_LOGE("Cached audio not found for text hash: %s", text_hash.c_str()); + } + + // Ensure the text_hash exists in the map before incrementing the count + if (repeat_counts_.find(text_hash) == repeat_counts_.end()) { + repeat_counts_[text_hash] = 0; // Initialize if it doesn't exist + } + repeat_counts_[text_hash]++; // Increment the repeat count + + return samples; +} + +int32_t CacheMechanism::GetCacheSize() const { + return cache_size_bytes_; +} + +void CacheMechanism::SetCacheSize(int32_t cache_size) { + std::lock_guard lock(mutex_); + cache_size_bytes_ = cache_size; + + EnsureCacheLimit(); +} + +void CacheMechanism::ClearCache() { + std::lock_guard lock(mutex_); + + // Remove all WAV files in the cache directory + for (const auto &entry : std::filesystem::directory_iterator(cache_dir_)) { + if (entry.path().extension() == ".wav") { + std::filesystem::remove(entry.path()); + // SHERPA_ONNX_LOGE("Removed wav file: %s", entry.path().c_str()); + } + } + + // SHERPA_ONNX_LOGE("Removed all wav files!"); + + // Reset the total cache size to 0 + used_cache_size_bytes_ = 0; + + // SHERPA_ONNX_LOGE("Cache cleared successfully."); +} + +int64_t CacheMechanism::GetTotalUsedCacheSize() { + std::lock_guard lock(mutex_); + return used_cache_size_bytes_; +} + +void CacheMechanism::LoadRepeatCounts() { + std::string repeat_count_file = cache_dir_ + "/repeat_counts.txt"; + + // Check if the file exists + if (!std::filesystem::exists(repeat_count_file)) { + // SHERPA_ONNX_LOGE("Repeat count file does not exist. Starting with an empty cache."); + return; // Skip loading if the file doesn't exist + } + + // Open the file for reading + std::ifstream ifs(repeat_count_file); + if (!ifs.is_open()) { + SHERPA_ONNX_LOGE("Failed to open repeat count file: %s", repeat_count_file.c_str()); + return; // Skip loading if the file cannot be opened + } + + // Read the file line by line + std::string line; + while (std::getline(ifs, line)) { + size_t pos = line.find(' '); + if (pos != std::string::npos) { + std::string text_hash = line.substr(0, pos); + int32_t count = std::stoi(line.substr(pos + 1)); + repeat_counts_[text_hash] = count; + // SHERPA_ONNX_LOGE("Loaded repeat count for text hash: %s, count: %d", text_hash.c_str(), count); + } + } +} + +void CacheMechanism::SaveRepeatCounts() { + std::string repeat_count_file = cache_dir_ + "/repeat_counts.txt"; + + // Open the file for writing + std::ofstream ofs(repeat_count_file); + if (!ofs.is_open()) { + SHERPA_ONNX_LOGE("Failed to open repeat count file for writing: %s", repeat_count_file.c_str()); + return; // Skip saving if the file cannot be opened + } + + // Write the repeat counts to the file + for (const auto &entry : repeat_counts_) { + ofs << entry.first << " " << entry.second; + if (!ofs) { + SHERPA_ONNX_LOGE("Failed to write repeat count for text hash: %s", entry.first.c_str()); + return; // Stop writing if an error occurs + } + ofs << std::endl; + } +} + +void CacheMechanism::RemoveWavFile(const std::string &text_hash) { + std::string file_path = cache_dir_ + "/" + text_hash + ".wav"; + if (std::filesystem::exists(file_path)) { + // Subtract the size of the removed WAV file from the total cache size + std::ifstream file(file_path, std::ios::binary | std::ios::ate); + if (file.is_open()) { + used_cache_size_bytes_ -= file.tellg(); + file.close(); + } + std::filesystem::remove(file_path); + // SHERPA_ONNX_LOGE("Removed wav file: %s", file_path.c_str()); + } + + // Remove the entry from the repeat counts and cache vector + if (repeat_counts_.find(text_hash) != repeat_counts_.end()) { + repeat_counts_.erase(text_hash); + cache_vector_.erase(std::remove(cache_vector_.begin(), cache_vector_.end(), text_hash), cache_vector_.end()); + } +} + +void CacheMechanism::UpdateCacheVector() { + used_cache_size_bytes_ = 0; // Reset the total cache size before recalculating + + for (const auto &entry : std::filesystem::directory_iterator(cache_dir_)) { + if (entry.path().extension() == ".wav") { + std::string text_hash = entry.path().stem().string(); + if (repeat_counts_.find(text_hash) == repeat_counts_.end()) { + // Remove the file if it's not in the repeat count file + std::filesystem::remove(entry.path()); + // SHERPA_ONNX_LOGE("Removed orphaned wav file: %s", entry.path().c_str()); + } else { + // Add the size of the WAV file to the total cache size + std::ifstream file(entry.path(), std::ios::binary | std::ios::ate); + if (file.is_open()) { + used_cache_size_bytes_ += file.tellg(); + file.close(); + } + cache_vector_.push_back(text_hash); + } + } + } +} + +void CacheMechanism::EnsureCacheLimit() { + if(used_cache_size_bytes_ > cache_size_bytes_) { + auto target_cache_size = std::max((int)(cache_size_bytes_*0.95), 0); //Remove more to prevent deleting every step + while (used_cache_size_bytes_> 0 && used_cache_size_bytes_ > target_cache_size) { + // Cache is full, remove the least repeated file + std::string least_repeated_file = GetLeastRepeatedFile(); + RemoveWavFile(least_repeated_file); + // SHERPA_ONNX_LOGE("Cache full, removed least repeated file: %s", least_repeated_file.c_str()); + } + } +} + +std::string CacheMechanism::GetLeastRepeatedFile() { + std::string least_repeated_file; + int32_t min_count = std::numeric_limits::max(); + + for (const auto &entry : repeat_counts_) { + if (entry.second < min_count) { + min_count = entry.second; + least_repeated_file = entry.first; + } + } + + return least_repeated_file; +} + +} // namespace sherpa_onnx \ No newline at end of file diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h new file mode 100644 index 0000000000..e5bf82929f --- /dev/null +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h @@ -0,0 +1,82 @@ +// sherpa-onnx/csrc/offline-tts-cache-mechanism.cc +// +// @mah92 From Iranian people to the comunity with love + +#ifndef SHERPA_ONNX_CSRC_OFFLINE_TTS_CACHE_MECHANISM_H_ +#define SHERPA_ONNX_CSRC_OFFLINE_TTS_CACHE_MECHANISM_H_ + +#include +#include +#include +#include +#include + +namespace sherpa_onnx { + +class CacheMechanism { + public: + CacheMechanism(const std::string &cache_dir, int32_t cache_size); + ~CacheMechanism(); + + // Add a new wav file to the cache + void AddWavFile(const std::string &text_hash, const std::vector &samples, int32_t sample_rate); + + // Get the cached wav file if it exists + std::vector GetWavFile(const std::string &text_hash, int32_t &sample_rate); + + // Get the current cache size in bytes + int32_t GetCacheSize() const; + + // Set the cache size in bytes + void SetCacheSize(int32_t cache_size); + + // Remove all the wav files in the cache + void ClearCache(); + + // To get total used cache size(for wav files) in bytes + int64_t GetTotalUsedCacheSize(); + + private: + // Load the repeat count file + void LoadRepeatCounts(); + + // Save the repeat count file + void SaveRepeatCounts(); + + // Remove a wav file from the cache + void RemoveWavFile(const std::string &text_hash); + + // Update the cache vector with the actual files in the cache folder + void UpdateCacheVector(); + + // Reduce used cache size if needed + void EnsureCacheLimit(); + + // Get the least repeated file in the cache + std::string GetLeastRepeatedFile(); + + // Data directory where the cache folder is located + std::string cache_dir_; + + // Maximum number of files in the cache + int32_t cache_size_bytes_; + + // Total used cache size for wav files in bytes + int64_t used_cache_size_bytes_; + + // Map of text hash to repeat count + std::unordered_map repeat_counts_; + + // Vector of cached file names + std::vector cache_vector_; + + // Mutex for thread safety (recursive to avoid deadlocks) + mutable std::recursive_mutex mutex_; + + // Time of last save + std::chrono::steady_clock::time_point last_save_time_; +}; + +} // namespace sherpa_onnx + +#endif // SHERPA_ONNX_CSRC_OFFLINE_TTS_CACHE_MECHANISM_H_ \ No newline at end of file diff --git a/sherpa-onnx/csrc/offline-tts.cc b/sherpa-onnx/csrc/offline-tts.cc index ec2c69523e..1894c6d56a 100644 --- a/sherpa-onnx/csrc/offline-tts.cc +++ b/sherpa-onnx/csrc/offline-tts.cc @@ -20,6 +20,7 @@ #include "sherpa-onnx/csrc/macros.h" #include "sherpa-onnx/csrc/offline-tts-impl.h" #include "sherpa-onnx/csrc/text-utils.h" +#include "sherpa-onnx/csrc/offline-tts-cache-mechanism.h" namespace sherpa_onnx { @@ -85,22 +86,92 @@ std::string OfflineTtsConfig::ToString() const { } OfflineTts::OfflineTts(const OfflineTtsConfig &config) - : impl_(OfflineTtsImpl::Create(config)) {} + : config_(config), impl_(OfflineTtsImpl::Create(config)), cache_mechanism_(nullptr) {} template OfflineTts::OfflineTts(Manager *mgr, const OfflineTtsConfig &config) - : impl_(OfflineTtsImpl::Create(mgr, config)) {} + : config_(config), impl_(OfflineTtsImpl::Create(mgr, config)), cache_mechanism_(nullptr) {} OfflineTts::~OfflineTts() = default; GeneratedAudio OfflineTts::Generate( const std::string &text, int64_t sid /*=0*/, float speed /*= 1.0*/, GeneratedAudioCallback callback /*= nullptr*/) const { - return impl_->Generate(text, sid, speed, std::move(callback)); + // Generate a hash for the text + std::hash hasher; + std::string text_hash = std::to_string(hasher(text)); + // SHERPA_ONNX_LOGE("Generated text hash: %s", text_hash.c_str()); + + // Check if the cache mechanism is active and if the audio is already cached + if (cache_mechanism_) { + int32_t sample_rate; + std::vector samples = cache_mechanism_->GetWavFile(text_hash, sample_rate); + + if (!samples.empty()) { + SHERPA_ONNX_LOGE("Returning cached audio for hash:%s", text_hash.c_str()); + + // If a callback is provided, call it with the cached audio + if (callback) { + int32_t result = callback(samples.data(), samples.size(), 1.0f /* progress */); + if (result == 0) { + // If the callback returns 0, stop further processing + SHERPA_ONNX_LOGE("Callback requested to stop processing."); + return {samples, sample_rate}; + } + } + + // Return the cached audio + return {samples, sample_rate}; + } + } + + // Generate the audio if not cached + GeneratedAudio audio = impl_->Generate(text, sid, speed, std::move(callback)); + // SHERPA_ONNX_LOGE("Generated audio: sample rate: %d, sample count: %d", audio.sample_rate, audio.samples.size()); + + // Cache the generated audio if the cache mechanism is active + if (cache_mechanism_) { + cache_mechanism_->AddWavFile(text_hash, audio.samples, audio.sample_rate); + // SHERPA_ONNX_LOGE("Cached audio for text hash: %s", text_hash.c_str()); + } + + return audio; } int32_t OfflineTts::SampleRate() const { return impl_->SampleRate(); } +int32_t OfflineTts::CacheSize() const { + return cache_mechanism_ ? cache_mechanism_->GetCacheSize() : 0; +} + +void OfflineTts::SetCacheSize(const int32_t cache_size) { + if (cache_size > 0) { + if (!cache_mechanism_) { + // Initialize the cache mechanism if it hasn't been initialized yet + cache_mechanism_ = std::make_unique(config_.cache_dir, cache_size); + } else { + // Update the cache size if the cache mechanism is already initialized + cache_mechanism_->SetCacheSize(cache_size); + } + } else if (cache_mechanism_) { + // If cache size is set to 0 or negative, destroy the cache mechanism + cache_mechanism_.reset(); + } +} + +void OfflineTts::ClearCache() { + if (cache_mechanism_) { + cache_mechanism_->ClearCache(); + } +} + +int64_t OfflineTts::GetTotalUsedCacheSize() { + if (cache_mechanism_) { + return cache_mechanism_->GetTotalUsedCacheSize(); + } + return -1; +} + int32_t OfflineTts::NumSpeakers() const { return impl_->NumSpeakers(); } #if __ANDROID_API__ >= 9 diff --git a/sherpa-onnx/csrc/offline-tts.h b/sherpa-onnx/csrc/offline-tts.h index 884173e7b6..2391daa209 100644 --- a/sherpa-onnx/csrc/offline-tts.h +++ b/sherpa-onnx/csrc/offline-tts.h @@ -12,6 +12,7 @@ #include "sherpa-onnx/csrc/offline-tts-model-config.h" #include "sherpa-onnx/csrc/parse-options.h" +#include "sherpa-onnx/csrc/offline-tts-cache-mechanism.h" namespace sherpa_onnx { @@ -32,6 +33,9 @@ struct OfflineTtsConfig { // If you set it to -1, then we process all sentences in a single batch. int32_t max_num_sentences = 1; + // Path to cache_directory + std::string cache_dir; + OfflineTtsConfig() = default; OfflineTtsConfig(const OfflineTtsModelConfig &model, const std::string &rule_fsts, const std::string &rule_fars, @@ -87,12 +91,26 @@ class OfflineTts { // Return the sample rate of the generated audio int32_t SampleRate() const; + // Return the maximum number of cached audio files size + int32_t CacheSize() const; + + // Set the maximum number of cached audio files size + void SetCacheSize(const int32_t cache_size); + + // Remove all cache data + void ClearCache(); + + // To get total used cache size(for wav files) in bytes + int64_t GetTotalUsedCacheSize(); + // Number of supported speakers. // If it supports only a single speaker, then it return 0 or 1. int32_t NumSpeakers() const; private: + OfflineTtsConfig config_; std::unique_ptr impl_; + std::unique_ptr cache_mechanism_; }; } // namespace sherpa_onnx diff --git a/sherpa-onnx/jni/offline-tts.cc b/sherpa-onnx/jni/offline-tts.cc index 985d581ef2..f57a766d2a 100644 --- a/sherpa-onnx/jni/offline-tts.cc +++ b/sherpa-onnx/jni/offline-tts.cc @@ -142,6 +142,24 @@ static OfflineTtsConfig GetOfflineTtsConfig(JNIEnv *env, jobject config) { fid = env->GetFieldID(cls, "maxNumSentences", "I"); ans.max_num_sentences = env->GetIntField(config, fid); + // Get data directory from config + jfieldID model_fid = env->GetFieldID(cls, "model", "Lcom/k2fsa/sherpa/onnx/OfflineTtsModelConfig;"); + jobject model_config = env->GetObjectField(config, model_fid); + jclass model_cls = env->GetObjectClass(model_config); + + jfieldID vits_fid = env->GetFieldID(model_cls, "vits", "Lcom/k2fsa/sherpa/onnx/OfflineTtsVitsModelConfig;"); + jobject vits_config = env->GetObjectField(model_config, vits_fid); + + fid = env->GetFieldID(vits_cls, "dataDir", "Ljava/lang/String;"); + jstring data_dir = (jstring)env->GetObjectField(vits_config, fid); + const char *p_data_dir = env->GetStringUTFChars(data_dir, nullptr); + + // Convert data directory to cache directory + std::string cache_dir = std::string(p_data_dir) + "/../cache"; + ans.cache_dir = cache_dir; + + env->ReleaseStringUTFChars(data_dir, p_data_dir); + return ans; } @@ -190,6 +208,18 @@ JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_delete( delete reinterpret_cast(ptr); } +SHERPA_ONNX_EXTERN_C +JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_setCacheSizeImpl( + JNIEnv * /*env*/, jobject /*obj*/, jlong ptr, jint cacheSize) { + reinterpret_cast(ptr)->SetCacheSize(static_cast(cacheSize)); +} + +SHERPA_ONNX_EXTERN_C +JNIEXPORT jint JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getCacheSizeImpl( + JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { + return reinterpret_cast(ptr)->CacheSize(); +} + SHERPA_ONNX_EXTERN_C JNIEXPORT jint JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getSampleRate( JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { @@ -202,6 +232,23 @@ JNIEXPORT jint JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getNumSpeakers( return reinterpret_cast(ptr)->NumSpeakers(); } +SHERPA_ONNX_EXTERN_C +JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getTotalUsedCacheSizeImpl( + JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { + return reinterpret_cast(ptr)->GetTotalUsedCacheSize(); +} + +SHERPA_ONNX_EXTERN_C +JNIEXPORT void JNICALL +Java_com_k2fsa_sherpa_onnx_OfflineTts_clearCacheImpl( + JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { + auto tts = reinterpret_cast(ptr); + if (tts) { + tts->ClearCache(); + SHERPA_ONNX_LOGE("Cache cleared from JNI."); + } +} + SHERPA_ONNX_EXTERN_C JNIEXPORT jobjectArray JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_generateImpl(JNIEnv *env, jobject /*obj*/, diff --git a/sherpa-onnx/kotlin-api/Tts.kt b/sherpa-onnx/kotlin-api/Tts.kt index 98efe66448..96f7f8080b 100644 --- a/sherpa-onnx/kotlin-api/Tts.kt +++ b/sherpa-onnx/kotlin-api/Tts.kt @@ -142,6 +142,22 @@ class OfflineTts( private external fun getSampleRate(ptr: Long): Int private external fun getNumSpeakers(ptr: Long): Int + fun getCacheSizeInMB(): Int { + return (getCacheSizeImpl(ptr) / (1024 * 1024)).toInt() // Convert bytes to MB + } + private external fun getCacheSizeImpl(ptr: Long): Long + + fun setCacheSizeInMB(cacheSize: Int) { + setCacheSizeImpl(ptr, cacheSize * (1024 * 1024)) + } + private external fun setCacheSizeImpl(ptr: Long, cacheSize: Int) + + fun getTotalUsedCacheSizeInMB(): Int { + return (getTotalUsedCacheSizeImpl(ptr) / (1024 * 1024)).toInt() // Convert bytes to MB + } + + private external fun getTotalUsedCacheSizeImpl(ptr: Long): Long + // The returned array has two entries: // - the first entry is an 1-D float array containing audio samples. // Each sample is normalized to the range [-1, 1] @@ -161,6 +177,12 @@ class OfflineTts( callback: (samples: FloatArray) -> Int ): Array + fun clearCache() { + clearCacheImpl(ptr) + } + + private external fun clearCacheImpl(ptr: Long) + companion object { init { System.loadLibrary("sherpa-onnx-jni") From 43e026224da390a90eb57f2e0e10b798d9285a85 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 17 Jan 2025 21:56:38 +0330 Subject: [PATCH 02/14] Speed up least repeated txt search --- sherpa-onnx/csrc/offline-tts-cache-mechanism.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc index db29a6df4e..21dcfa8067 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc @@ -255,6 +255,11 @@ std::string CacheMechanism::GetLeastRepeatedFile() { int32_t min_count = std::numeric_limits::max(); for (const auto &entry : repeat_counts_) { + if (entry.second == 1) { + least_repeated_file = entry.first; + return least_repeated_file; + } + if (entry.second < min_count) { min_count = entry.second; least_repeated_file = entry.first; From b7c91b471a4dcabdbd5e0bb2df8ad30e7bba342f Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 23 Jan 2025 11:48:41 +0330 Subject: [PATCH 03/14] Done some of the reviewer's request --- sherpa-onnx/csrc/CMakeLists.txt | 2 +- .../csrc/offline-tts-cache-mechanism.cc | 90 +++++++++++-------- .../csrc/offline-tts-cache-mechanism.h | 11 ++- sherpa-onnx/csrc/offline-tts.cc | 2 +- sherpa-onnx/csrc/offline-tts.h | 4 +- sherpa-onnx/jni/offline-tts.cc | 2 +- sherpa-onnx/kotlin-api/Tts.kt | 4 +- 7 files changed, 65 insertions(+), 50 deletions(-) diff --git a/sherpa-onnx/csrc/CMakeLists.txt b/sherpa-onnx/csrc/CMakeLists.txt index 9dde417dcf..c7ab27046a 100644 --- a/sherpa-onnx/csrc/CMakeLists.txt +++ b/sherpa-onnx/csrc/CMakeLists.txt @@ -155,6 +155,7 @@ if(SHERPA_ONNX_ENABLE_TTS) jieba-lexicon.cc lexicon.cc melo-tts-lexicon.cc + offline-tts-cache-mechanism.cc offline-tts-character-frontend.cc offline-tts-frontend.cc offline-tts-impl.cc @@ -167,7 +168,6 @@ if(SHERPA_ONNX_ENABLE_TTS) offline-tts-vits-model.cc offline-tts.cc piper-phonemize-lexicon.cc - offline-tts-cache-mechanism.cc ) endif() diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc index 21dcfa8067..f8e653e625 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc @@ -19,10 +19,16 @@ namespace sherpa_onnx { CacheMechanism::CacheMechanism(const std::string &cache_dir, int32_t cache_size) : cache_dir_(cache_dir), cache_size_bytes_(cache_size), used_cache_size_bytes_(0) { + // Create the cache directory if it doesn't exist if (!std::filesystem::exists(cache_dir_)) { - std::filesystem::create_directory(cache_dir_); - // SHERPA_ONNX_LOGE("Created cache directory: %s", cache_dir_.c_str()); + bool dir_created = std::filesystem::create_directory(cache_dir_); + if (!dir_created) { + SHERPA_ONNX_LOGE("Unable to create cache directory: %s", cache_dir_.c_str()); + SHERPA_ONNX_LOGE("Cache mechanism disabled!"); + cache_mechanism_inited_ = false; + return; + } } // Load the repeat counts @@ -33,9 +39,14 @@ CacheMechanism::CacheMechanism(const std::string &cache_dir, int32_t cache_size) // Initialize the last save time last_save_time_ = std::chrono::steady_clock::now(); + + // Indicate that initialization has been successful + cache_mechanism_inited_ = true; } CacheMechanism::~CacheMechanism() { + if(cache_mechanism_inited_ == false) return; + // Save the repeat counts on destruction SaveRepeatCounts(); } @@ -43,6 +54,8 @@ CacheMechanism::~CacheMechanism() { void CacheMechanism::AddWavFile(const std::string &text_hash, const std::vector &samples, int32_t sample_rate) { std::lock_guard lock(mutex_); + if(cache_mechanism_inited_ == false) return; + std::string file_path = cache_dir_ + "/" + text_hash + ".wav"; // Check if the file physically exists in the cache directory @@ -59,63 +72,59 @@ void CacheMechanism::AddWavFile(const std::string &text_hash, const std::vector< std::ifstream file(file_path, std::ios::binary | std::ios::ate); if (file.is_open()) { used_cache_size_bytes_ += file.tellg(); - file.close(); } - // SHERPA_ONNX_LOGE("Added new wav file to cache: %s, samples:%d", file_path.c_str(), samples.size()); } else { SHERPA_ONNX_LOGE("Failed to write wav file: %s", file_path.c_str()); } } - - // Ensure the text_hash exists in the map before incrementing the count - if (repeat_counts_.find(text_hash) == repeat_counts_.end()) { - repeat_counts_[text_hash] = 0; // Initialize if it doesn't exist - cache_vector_.push_back(text_hash); // Add the text_hash to the cache vector - } - repeat_counts_[text_hash]++; // Increment the repeat count - - // Save the repeat counts every 10 minutes - auto now = std::chrono::steady_clock::now(); - if (std::chrono::duration_cast(now - last_save_time_).count() >= 10 * 60) { - SaveRepeatCounts(); - last_save_time_ = now; - } } std::vector CacheMechanism::GetWavFile(const std::string &text_hash, int32_t &sample_rate) { std::lock_guard lock(mutex_); std::vector samples; + + if(cache_mechanism_inited_ == false) return samples; + std::string file_path = cache_dir_ + "/" + text_hash + ".wav"; if (std::filesystem::exists(file_path)) { bool is_ok = false; samples = ReadWave(file_path, &sample_rate, &is_ok); - if (is_ok) { - // SHERPA_ONNX_LOGE("Retrieved cached audio for text hash: %s, sample count: %d, freq:%dHz", text_hash.c_str(), samples.size(), sample_rate); - } else { + if (is_ok == false) { SHERPA_ONNX_LOGE("Failed to read cached file: %s", file_path.c_str()); } - } else { - // SHERPA_ONNX_LOGE("Cached audio not found for text hash: %s", text_hash.c_str()); } // Ensure the text_hash exists in the map before incrementing the count if (repeat_counts_.find(text_hash) == repeat_counts_.end()) { - repeat_counts_[text_hash] = 0; // Initialize if it doesn't exist + repeat_counts_[text_hash] = 1; // Initialize if it doesn't exist + } else { + repeat_counts_[text_hash]++; // Increment the repeat count + } + + // Save the repeat counts every 10 minutes + auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast(now - last_save_time_).count() >= 10 * 60) { + SaveRepeatCounts(); + last_save_time_ = now; } - repeat_counts_[text_hash]++; // Increment the repeat count return samples; } int32_t CacheMechanism::GetCacheSize() const { + if(cache_mechanism_inited_ == false) return 0; + return cache_size_bytes_; } void CacheMechanism::SetCacheSize(int32_t cache_size) { std::lock_guard lock(mutex_); + + if(cache_mechanism_inited_ == false) return; + cache_size_bytes_ = cache_size; EnsureCacheLimit(); @@ -124,33 +133,41 @@ void CacheMechanism::SetCacheSize(int32_t cache_size) { void CacheMechanism::ClearCache() { std::lock_guard lock(mutex_); + if(cache_mechanism_inited_ == false) return; + // Remove all WAV files in the cache directory for (const auto &entry : std::filesystem::directory_iterator(cache_dir_)) { if (entry.path().extension() == ".wav") { std::filesystem::remove(entry.path()); - // SHERPA_ONNX_LOGE("Removed wav file: %s", entry.path().c_str()); } } - // SHERPA_ONNX_LOGE("Removed all wav files!"); - // Reset the total cache size to 0 used_cache_size_bytes_ = 0; - // SHERPA_ONNX_LOGE("Cache cleared successfully."); + // Clear the repeat counts and cache vector + repeat_counts_.clear(); + cache_vector_.clear(); + + // Remove repeat counts also in the repeat_counts.txt + SaveRepeatCounts(); } -int64_t CacheMechanism::GetTotalUsedCacheSize() { +int32_t CacheMechanism::GetTotalUsedCacheSize() const { std::lock_guard lock(mutex_); + + if(cache_mechanism_inited_ == false) return 0; + return used_cache_size_bytes_; } +// Private functions /////////////////////////////////////////////////// + void CacheMechanism::LoadRepeatCounts() { std::string repeat_count_file = cache_dir_ + "/repeat_counts.txt"; // Check if the file exists if (!std::filesystem::exists(repeat_count_file)) { - // SHERPA_ONNX_LOGE("Repeat count file does not exist. Starting with an empty cache."); return; // Skip loading if the file doesn't exist } @@ -169,7 +186,6 @@ void CacheMechanism::LoadRepeatCounts() { std::string text_hash = line.substr(0, pos); int32_t count = std::stoi(line.substr(pos + 1)); repeat_counts_[text_hash] = count; - // SHERPA_ONNX_LOGE("Loaded repeat count for text hash: %s, count: %d", text_hash.c_str(), count); } } } @@ -205,7 +221,6 @@ void CacheMechanism::RemoveWavFile(const std::string &text_hash) { file.close(); } std::filesystem::remove(file_path); - // SHERPA_ONNX_LOGE("Removed wav file: %s", file_path.c_str()); } // Remove the entry from the repeat counts and cache vector @@ -222,15 +237,13 @@ void CacheMechanism::UpdateCacheVector() { if (entry.path().extension() == ".wav") { std::string text_hash = entry.path().stem().string(); if (repeat_counts_.find(text_hash) == repeat_counts_.end()) { - // Remove the file if it's not in the repeat count file + // Remove the file if it's not in the repeat count file (orphaned file) std::filesystem::remove(entry.path()); - // SHERPA_ONNX_LOGE("Removed orphaned wav file: %s", entry.path().c_str()); } else { // Add the size of the WAV file to the total cache size std::ifstream file(entry.path(), std::ios::binary | std::ios::ate); if (file.is_open()) { used_cache_size_bytes_ += file.tellg(); - file.close(); } cache_vector_.push_back(text_hash); } @@ -240,12 +253,11 @@ void CacheMechanism::UpdateCacheVector() { void CacheMechanism::EnsureCacheLimit() { if(used_cache_size_bytes_ > cache_size_bytes_) { - auto target_cache_size = std::max((int)(cache_size_bytes_*0.95), 0); //Remove more to prevent deleting every step + auto target_cache_size = std::max(static_cast (cache_size_bytes_*0.95), 0); //Remove more to prevent deleting every step while (used_cache_size_bytes_> 0 && used_cache_size_bytes_ > target_cache_size) { // Cache is full, remove the least repeated file std::string least_repeated_file = GetLeastRepeatedFile(); RemoveWavFile(least_repeated_file); - // SHERPA_ONNX_LOGE("Cache full, removed least repeated file: %s", least_repeated_file.c_str()); } } } @@ -255,7 +267,7 @@ std::string CacheMechanism::GetLeastRepeatedFile() { int32_t min_count = std::numeric_limits::max(); for (const auto &entry : repeat_counts_) { - if (entry.second == 1) { + if (entry.second <= 1) { least_repeated_file = entry.first; return least_repeated_file; } diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h index e5bf82929f..8d1dcf1836 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h @@ -1,4 +1,4 @@ -// sherpa-onnx/csrc/offline-tts-cache-mechanism.cc +// sherpa-onnx/csrc/offline-tts-cache-mechanism.h // // @mah92 From Iranian people to the comunity with love @@ -34,7 +34,7 @@ class CacheMechanism { void ClearCache(); // To get total used cache size(for wav files) in bytes - int64_t GetTotalUsedCacheSize(); + int32_t GetTotalUsedCacheSize() const; private: // Load the repeat count file @@ -58,11 +58,11 @@ class CacheMechanism { // Data directory where the cache folder is located std::string cache_dir_; - // Maximum number of files in the cache + // Maximum number of bytes in the cache int32_t cache_size_bytes_; // Total used cache size for wav files in bytes - int64_t used_cache_size_bytes_; + int32_t used_cache_size_bytes_; // Map of text hash to repeat count std::unordered_map repeat_counts_; @@ -75,6 +75,9 @@ class CacheMechanism { // Time of last save std::chrono::steady_clock::time_point last_save_time_; + + // if cache mechanism is inited successfully + bool cache_mechanism_inited_; }; } // namespace sherpa_onnx diff --git a/sherpa-onnx/csrc/offline-tts.cc b/sherpa-onnx/csrc/offline-tts.cc index 1894c6d56a..c0b4a4094c 100644 --- a/sherpa-onnx/csrc/offline-tts.cc +++ b/sherpa-onnx/csrc/offline-tts.cc @@ -165,7 +165,7 @@ void OfflineTts::ClearCache() { } } -int64_t OfflineTts::GetTotalUsedCacheSize() { +int32_t OfflineTts::GetTotalUsedCacheSize() { if (cache_mechanism_) { return cache_mechanism_->GetTotalUsedCacheSize(); } diff --git a/sherpa-onnx/csrc/offline-tts.h b/sherpa-onnx/csrc/offline-tts.h index 2391daa209..deaeb2e5c1 100644 --- a/sherpa-onnx/csrc/offline-tts.h +++ b/sherpa-onnx/csrc/offline-tts.h @@ -10,9 +10,9 @@ #include #include +#include "sherpa-onnx/csrc/offline-tts-cache-mechanism.h" #include "sherpa-onnx/csrc/offline-tts-model-config.h" #include "sherpa-onnx/csrc/parse-options.h" -#include "sherpa-onnx/csrc/offline-tts-cache-mechanism.h" namespace sherpa_onnx { @@ -101,7 +101,7 @@ class OfflineTts { void ClearCache(); // To get total used cache size(for wav files) in bytes - int64_t GetTotalUsedCacheSize(); + int32_t GetTotalUsedCacheSize(); // Number of supported speakers. // If it supports only a single speaker, then it return 0 or 1. diff --git a/sherpa-onnx/jni/offline-tts.cc b/sherpa-onnx/jni/offline-tts.cc index 23a2c7ae79..693dccaa42 100644 --- a/sherpa-onnx/jni/offline-tts.cc +++ b/sherpa-onnx/jni/offline-tts.cc @@ -266,7 +266,7 @@ JNIEXPORT jint JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getNumSpeakers( } SHERPA_ONNX_EXTERN_C -JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getTotalUsedCacheSizeImpl( +JNIEXPORT jint JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getTotalUsedCacheSizeImpl( JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { return reinterpret_cast(ptr)->GetTotalUsedCacheSize(); } diff --git a/sherpa-onnx/kotlin-api/Tts.kt b/sherpa-onnx/kotlin-api/Tts.kt index 9461b1f372..f53f56462b 100644 --- a/sherpa-onnx/kotlin-api/Tts.kt +++ b/sherpa-onnx/kotlin-api/Tts.kt @@ -154,7 +154,7 @@ class OfflineTts( fun getCacheSizeInMB(): Int { return (getCacheSizeImpl(ptr) / (1024 * 1024)).toInt() // Convert bytes to MB } - private external fun getCacheSizeImpl(ptr: Long): Long + private external fun getCacheSizeImpl(ptr: Long): Int fun setCacheSizeInMB(cacheSize: Int) { setCacheSizeImpl(ptr, cacheSize * (1024 * 1024)) @@ -165,7 +165,7 @@ class OfflineTts( return (getTotalUsedCacheSizeImpl(ptr) / (1024 * 1024)).toInt() // Convert bytes to MB } - private external fun getTotalUsedCacheSizeImpl(ptr: Long): Long + private external fun getTotalUsedCacheSizeImpl(ptr: Long): Int // The returned array has two entries: // - the first entry is an 1-D float array containing audio samples. From 42d6d24a2c964a47e710732a480914f395324ff4 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 23 Jan 2025 16:50:40 +0330 Subject: [PATCH 04/14] cpplint passed --- .../csrc/offline-tts-cache-mechanism.cc | 109 ++++++++++++------ .../csrc/offline-tts-cache-mechanism.h | 22 ++-- sherpa-onnx/csrc/offline-tts.cc | 18 ++- sherpa-onnx/csrc/offline-tts.h | 2 +- 4 files changed, 101 insertions(+), 50 deletions(-) diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc index f8e653e625..88c68d652e 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc @@ -1,30 +1,58 @@ // sherpa-onnx/csrc/offline-tts-cache-mechanism.cc // -// @mah92 From Iranian people to the comunity with love +// Copyright (c) 2025 @mah92 From Iranian people to the community with love #include "sherpa-onnx/csrc/offline-tts-cache-mechanism.h" +#include #include -#include #include -#include -#include +#include +#include #include "sherpa-onnx/csrc/file-utils.h" #include "sherpa-onnx/csrc/macros.h" #include "sherpa-onnx/csrc/wave-reader.h" #include "sherpa-onnx/csrc/wave-writer.h" +// Platform-specific time functions +#if defined(_WIN32) +#include +#else +#include +#include +#endif + namespace sherpa_onnx { -CacheMechanism::CacheMechanism(const std::string &cache_dir, int32_t cache_size) - : cache_dir_(cache_dir), cache_size_bytes_(cache_size), used_cache_size_bytes_(0) { +// Helper function to get the current time in seconds +static int64_t GetCurrentTimeInSeconds() { +#if defined(_WIN32) + // Windows implementation + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + uint64_t time = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime; + return static_cast(time / 10000000ULL - 11644473600ULL); +#else + // Unix implementation + struct timeval tv; + gettimeofday(&tv, nullptr); + return static_cast(tv.tv_sec); +#endif +} + +CacheMechanism::CacheMechanism(const std::string &cache_dir, + int32_t cache_size) + : cache_dir_(cache_dir), + cache_size_bytes_(cache_size), + used_cache_size_bytes_(0) { // Create the cache directory if it doesn't exist if (!std::filesystem::exists(cache_dir_)) { bool dir_created = std::filesystem::create_directory(cache_dir_); if (!dir_created) { - SHERPA_ONNX_LOGE("Unable to create cache directory: %s", cache_dir_.c_str()); + SHERPA_ONNX_LOGE("Unable to create cache directory: %s", + cache_dir_.c_str()); SHERPA_ONNX_LOGE("Cache mechanism disabled!"); cache_mechanism_inited_ = false; return; @@ -38,37 +66,41 @@ CacheMechanism::CacheMechanism(const std::string &cache_dir, int32_t cache_size) UpdateCacheVector(); // Initialize the last save time - last_save_time_ = std::chrono::steady_clock::now(); + last_save_time_ = GetCurrentTimeInSeconds(); // Indicate that initialization has been successful cache_mechanism_inited_ = true; } CacheMechanism::~CacheMechanism() { - if(cache_mechanism_inited_ == false) return; + if (cache_mechanism_inited_ == false) return; // Save the repeat counts on destruction SaveRepeatCounts(); } -void CacheMechanism::AddWavFile(const std::string &text_hash, const std::vector &samples, int32_t sample_rate) { +void CacheMechanism::AddWavFile( + const std::string &text_hash, + const std::vector &samples, + const int32_t sample_rate) { std::lock_guard lock(mutex_); - if(cache_mechanism_inited_ == false) return; + if (cache_mechanism_inited_ == false) return; std::string file_path = cache_dir_ + "/" + text_hash + ".wav"; // Check if the file physically exists in the cache directory bool file_exists = std::filesystem::exists(file_path); - if (!file_exists) { // If the file does not exist, add it to the cache + if (!file_exists) { // If the file does not exist, add it to the cache // Ensure the cache does not exceed its size limit EnsureCacheLimit(); // Write the audio samples to a WAV file - bool success = WriteWave(file_path, sample_rate, samples.data(), samples.size()); + bool success = WriteWave(file_path, + sample_rate, samples.data(), samples.size()); if (success) { - // Calculate the size of the new WAV file and add it to the total cache size + // Calculate size of the new WAV file and add it to the total cache size std::ifstream file(file_path, std::ios::binary | std::ios::ate); if (file.is_open()) { used_cache_size_bytes_ += file.tellg(); @@ -79,18 +111,20 @@ void CacheMechanism::AddWavFile(const std::string &text_hash, const std::vector< } } -std::vector CacheMechanism::GetWavFile(const std::string &text_hash, int32_t &sample_rate) { +std::vector CacheMechanism::GetWavFile( + const std::string &text_hash, + int32_t *sample_rate) { std::lock_guard lock(mutex_); std::vector samples; - if(cache_mechanism_inited_ == false) return samples; + if (cache_mechanism_inited_ == false) return samples; std::string file_path = cache_dir_ + "/" + text_hash + ".wav"; if (std::filesystem::exists(file_path)) { bool is_ok = false; - samples = ReadWave(file_path, &sample_rate, &is_ok); + samples = ReadWave(file_path, sample_rate, &is_ok); if (is_ok == false) { SHERPA_ONNX_LOGE("Failed to read cached file: %s", file_path.c_str()); @@ -99,14 +133,14 @@ std::vector CacheMechanism::GetWavFile(const std::string &text_hash, int3 // Ensure the text_hash exists in the map before incrementing the count if (repeat_counts_.find(text_hash) == repeat_counts_.end()) { - repeat_counts_[text_hash] = 1; // Initialize if it doesn't exist + repeat_counts_[text_hash] = 1; // Initialize if it doesn't exist } else { - repeat_counts_[text_hash]++; // Increment the repeat count + repeat_counts_[text_hash]++; // Increment the repeat count } // Save the repeat counts every 10 minutes - auto now = std::chrono::steady_clock::now(); - if (std::chrono::duration_cast(now - last_save_time_).count() >= 10 * 60) { + int64_t now = GetCurrentTimeInSeconds(); + if (now - last_save_time_ >= 10 * 60) { SaveRepeatCounts(); last_save_time_ = now; } @@ -115,7 +149,7 @@ std::vector CacheMechanism::GetWavFile(const std::string &text_hash, int3 } int32_t CacheMechanism::GetCacheSize() const { - if(cache_mechanism_inited_ == false) return 0; + if (cache_mechanism_inited_ == false) return 0; return cache_size_bytes_; } @@ -123,7 +157,7 @@ int32_t CacheMechanism::GetCacheSize() const { void CacheMechanism::SetCacheSize(int32_t cache_size) { std::lock_guard lock(mutex_); - if(cache_mechanism_inited_ == false) return; + if (cache_mechanism_inited_ == false) return; cache_size_bytes_ = cache_size; @@ -133,7 +167,7 @@ void CacheMechanism::SetCacheSize(int32_t cache_size) { void CacheMechanism::ClearCache() { std::lock_guard lock(mutex_); - if(cache_mechanism_inited_ == false) return; + if (cache_mechanism_inited_ == false) return; // Remove all WAV files in the cache directory for (const auto &entry : std::filesystem::directory_iterator(cache_dir_)) { @@ -156,7 +190,7 @@ void CacheMechanism::ClearCache() { int32_t CacheMechanism::GetTotalUsedCacheSize() const { std::lock_guard lock(mutex_); - if(cache_mechanism_inited_ == false) return 0; + if (cache_mechanism_inited_ == false) return 0; return used_cache_size_bytes_; } @@ -174,7 +208,8 @@ void CacheMechanism::LoadRepeatCounts() { // Open the file for reading std::ifstream ifs(repeat_count_file); if (!ifs.is_open()) { - SHERPA_ONNX_LOGE("Failed to open repeat count file: %s", repeat_count_file.c_str()); + SHERPA_ONNX_LOGE("Failed to open repeat count file: %s", + repeat_count_file.c_str()); return; // Skip loading if the file cannot be opened } @@ -196,7 +231,8 @@ void CacheMechanism::SaveRepeatCounts() { // Open the file for writing std::ofstream ofs(repeat_count_file); if (!ofs.is_open()) { - SHERPA_ONNX_LOGE("Failed to open repeat count file for writing: %s", repeat_count_file.c_str()); + SHERPA_ONNX_LOGE("Failed to open repeat count file for writing: %s", + repeat_count_file.c_str()); return; // Skip saving if the file cannot be opened } @@ -204,7 +240,8 @@ void CacheMechanism::SaveRepeatCounts() { for (const auto &entry : repeat_counts_) { ofs << entry.first << " " << entry.second; if (!ofs) { - SHERPA_ONNX_LOGE("Failed to write repeat count for text hash: %s", entry.first.c_str()); + SHERPA_ONNX_LOGE("Failed to write repeat count for text hash: %s", + entry.first.c_str()); return; // Stop writing if an error occurs } ofs << std::endl; @@ -226,12 +263,14 @@ void CacheMechanism::RemoveWavFile(const std::string &text_hash) { // Remove the entry from the repeat counts and cache vector if (repeat_counts_.find(text_hash) != repeat_counts_.end()) { repeat_counts_.erase(text_hash); - cache_vector_.erase(std::remove(cache_vector_.begin(), cache_vector_.end(), text_hash), cache_vector_.end()); + cache_vector_.erase( + std::remove(cache_vector_.begin(), cache_vector_.end(), text_hash), + cache_vector_.end()); } } void CacheMechanism::UpdateCacheVector() { - used_cache_size_bytes_ = 0; // Reset the total cache size before recalculating + used_cache_size_bytes_ = 0; // Reset total cache size before recalculating for (const auto &entry : std::filesystem::directory_iterator(cache_dir_)) { if (entry.path().extension() == ".wav") { @@ -252,9 +291,11 @@ void CacheMechanism::UpdateCacheVector() { } void CacheMechanism::EnsureCacheLimit() { - if(used_cache_size_bytes_ > cache_size_bytes_) { - auto target_cache_size = std::max(static_cast (cache_size_bytes_*0.95), 0); //Remove more to prevent deleting every step - while (used_cache_size_bytes_> 0 && used_cache_size_bytes_ > target_cache_size) { + if (used_cache_size_bytes_ > cache_size_bytes_) { + auto target_cache_size + = std::max(static_cast (cache_size_bytes_*0.95), 0); + while (used_cache_size_bytes_> 0 + && used_cache_size_bytes_ > target_cache_size) { // Cache is full, remove the least repeated file std::string least_repeated_file = GetLeastRepeatedFile(); RemoveWavFile(least_repeated_file); @@ -281,4 +322,4 @@ std::string CacheMechanism::GetLeastRepeatedFile() { return least_repeated_file; } -} // namespace sherpa_onnx \ No newline at end of file +} // namespace sherpa_onnx diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h index 8d1dcf1836..4fe3110906 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h @@ -1,6 +1,6 @@ // sherpa-onnx/csrc/offline-tts-cache-mechanism.h // -// @mah92 From Iranian people to the comunity with love +// Copyright (c) 2025 @mah92 From Iranian people to the community with love #ifndef SHERPA_ONNX_CSRC_OFFLINE_TTS_CACHE_MECHANISM_H_ #define SHERPA_ONNX_CSRC_OFFLINE_TTS_CACHE_MECHANISM_H_ @@ -8,8 +8,7 @@ #include #include #include -#include -#include +#include // NOLINT namespace sherpa_onnx { @@ -19,10 +18,15 @@ class CacheMechanism { ~CacheMechanism(); // Add a new wav file to the cache - void AddWavFile(const std::string &text_hash, const std::vector &samples, int32_t sample_rate); + void AddWavFile( + const std::string &text_hash, + const std::vector &samples, + const int32_t sample_rate); // Get the cached wav file if it exists - std::vector GetWavFile(const std::string &text_hash, int32_t &sample_rate); + std::vector GetWavFile( + const std::string &text_hash, + int32_t *sample_rate); // Get the current cache size in bytes int32_t GetCacheSize() const; @@ -30,7 +34,7 @@ class CacheMechanism { // Set the cache size in bytes void SetCacheSize(int32_t cache_size); - // Remove all the wav files in the cache + // Remove all the wav files in the cache void ClearCache(); // To get total used cache size(for wav files) in bytes @@ -73,8 +77,8 @@ class CacheMechanism { // Mutex for thread safety (recursive to avoid deadlocks) mutable std::recursive_mutex mutex_; - // Time of last save - std::chrono::steady_clock::time_point last_save_time_; + // Time of last save (in seconds since epoch) + int64_t last_save_time_; // if cache mechanism is inited successfully bool cache_mechanism_inited_; @@ -82,4 +86,4 @@ class CacheMechanism { } // namespace sherpa_onnx -#endif // SHERPA_ONNX_CSRC_OFFLINE_TTS_CACHE_MECHANISM_H_ \ No newline at end of file +#endif // SHERPA_ONNX_CSRC_OFFLINE_TTS_CACHE_MECHANISM_H_ diff --git a/sherpa-onnx/csrc/offline-tts.cc b/sherpa-onnx/csrc/offline-tts.cc index c0b4a4094c..5513d0284f 100644 --- a/sherpa-onnx/csrc/offline-tts.cc +++ b/sherpa-onnx/csrc/offline-tts.cc @@ -86,11 +86,15 @@ std::string OfflineTtsConfig::ToString() const { } OfflineTts::OfflineTts(const OfflineTtsConfig &config) - : config_(config), impl_(OfflineTtsImpl::Create(config)), cache_mechanism_(nullptr) {} + : config_(config), + impl_(OfflineTtsImpl::Create(config)), + cache_mechanism_(nullptr) {} template OfflineTts::OfflineTts(Manager *mgr, const OfflineTtsConfig &config) - : config_(config), impl_(OfflineTtsImpl::Create(mgr, config)), cache_mechanism_(nullptr) {} + : config_(config), + impl_(OfflineTtsImpl::Create(mgr, config)), + cache_mechanism_(nullptr) {} OfflineTts::~OfflineTts() = default; @@ -105,14 +109,16 @@ GeneratedAudio OfflineTts::Generate( // Check if the cache mechanism is active and if the audio is already cached if (cache_mechanism_) { int32_t sample_rate; - std::vector samples = cache_mechanism_->GetWavFile(text_hash, sample_rate); + std::vector samples + = cache_mechanism_->GetWavFile(text_hash, &sample_rate); if (!samples.empty()) { SHERPA_ONNX_LOGE("Returning cached audio for hash:%s", text_hash.c_str()); // If a callback is provided, call it with the cached audio if (callback) { - int32_t result = callback(samples.data(), samples.size(), 1.0f /* progress */); + int32_t result + = callback(samples.data(), samples.size(), 1.0f /* progress */); if (result == 0) { // If the callback returns 0, stop further processing SHERPA_ONNX_LOGE("Callback requested to stop processing."); @@ -127,7 +133,6 @@ GeneratedAudio OfflineTts::Generate( // Generate the audio if not cached GeneratedAudio audio = impl_->Generate(text, sid, speed, std::move(callback)); - // SHERPA_ONNX_LOGE("Generated audio: sample rate: %d, sample count: %d", audio.sample_rate, audio.samples.size()); // Cache the generated audio if the cache mechanism is active if (cache_mechanism_) { @@ -148,7 +153,8 @@ void OfflineTts::SetCacheSize(const int32_t cache_size) { if (cache_size > 0) { if (!cache_mechanism_) { // Initialize the cache mechanism if it hasn't been initialized yet - cache_mechanism_ = std::make_unique(config_.cache_dir, cache_size); + cache_mechanism_ + = std::make_unique(config_.cache_dir, cache_size); } else { // Update the cache size if the cache mechanism is already initialized cache_mechanism_->SetCacheSize(cache_size); diff --git a/sherpa-onnx/csrc/offline-tts.h b/sherpa-onnx/csrc/offline-tts.h index deaeb2e5c1..57dbb8e341 100644 --- a/sherpa-onnx/csrc/offline-tts.h +++ b/sherpa-onnx/csrc/offline-tts.h @@ -102,7 +102,7 @@ class OfflineTts { // To get total used cache size(for wav files) in bytes int32_t GetTotalUsedCacheSize(); - + // Number of supported speakers. // If it supports only a single speaker, then it return 0 or 1. int32_t NumSpeakers() const; From b1f9bcc2cd424ada8595e2ec0cdd0924261ba77a Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 24 Jan 2025 07:15:54 +0330 Subject: [PATCH 05/14] Removed Clear Button, remove cache on speed change --- .../sherpa/onnx/tts/engine/MainActivity.kt | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt index 7d60454772..6b1371450a 100644 --- a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt +++ b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt @@ -112,6 +112,8 @@ class MainActivity : ComponentActivity() { onValueChange = { TtsEngine.speed = it preferenceHelper.setSpeed(it) + TtsEngine.tts?.clearCache() // Call the clearCache method + usedCacheSizeMB = 0 // Reset used cache size }, valueRange = 0.2F..3.0F, modifier = Modifier.fillMaxWidth() @@ -294,21 +296,6 @@ class MainActivity : ComponentActivity() { Row { Text(rtfText) } - - Button( - modifier = Modifier.padding(20.dp), - onClick = { - TtsEngine.tts?.clearCache() // Call the clearCache method - usedCacheSizeMB = 0 // Reset used cache size - Toast.makeText( - applicationContext, - "Cache cleared!", - Toast.LENGTH_SHORT - ).show() - } - ) { - Text("Clear") - } } } } From a46e2f79b0d5064f9f0ccaa957fb74277dce4923 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 24 Jan 2025 07:19:52 +0330 Subject: [PATCH 06/14] Renamed CacheMechanism to OfflineTtsCacheMechanism --- .../csrc/offline-tts-cache-mechanism.cc | 28 +++++++++---------- .../csrc/offline-tts-cache-mechanism.h | 6 ++-- sherpa-onnx/csrc/offline-tts.cc | 4 +-- sherpa-onnx/csrc/offline-tts.h | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc index 88c68d652e..e57b4226cf 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc @@ -41,7 +41,7 @@ static int64_t GetCurrentTimeInSeconds() { #endif } -CacheMechanism::CacheMechanism(const std::string &cache_dir, +OfflineTtsCacheMechanism::OfflineTtsCacheMechanism(const std::string &cache_dir, int32_t cache_size) : cache_dir_(cache_dir), cache_size_bytes_(cache_size), @@ -72,14 +72,14 @@ CacheMechanism::CacheMechanism(const std::string &cache_dir, cache_mechanism_inited_ = true; } -CacheMechanism::~CacheMechanism() { +OfflineTtsCacheMechanism::~OfflineTtsCacheMechanism() { if (cache_mechanism_inited_ == false) return; // Save the repeat counts on destruction SaveRepeatCounts(); } -void CacheMechanism::AddWavFile( +void OfflineTtsCacheMechanism::AddWavFile( const std::string &text_hash, const std::vector &samples, const int32_t sample_rate) { @@ -111,7 +111,7 @@ void CacheMechanism::AddWavFile( } } -std::vector CacheMechanism::GetWavFile( +std::vector OfflineTtsCacheMechanism::GetWavFile( const std::string &text_hash, int32_t *sample_rate) { std::lock_guard lock(mutex_); @@ -148,13 +148,13 @@ std::vector CacheMechanism::GetWavFile( return samples; } -int32_t CacheMechanism::GetCacheSize() const { +int32_t OfflineTtsCacheMechanism::GetCacheSize() const { if (cache_mechanism_inited_ == false) return 0; return cache_size_bytes_; } -void CacheMechanism::SetCacheSize(int32_t cache_size) { +void OfflineTtsCacheMechanism::SetCacheSize(int32_t cache_size) { std::lock_guard lock(mutex_); if (cache_mechanism_inited_ == false) return; @@ -164,7 +164,7 @@ void CacheMechanism::SetCacheSize(int32_t cache_size) { EnsureCacheLimit(); } -void CacheMechanism::ClearCache() { +void OfflineTtsCacheMechanism::ClearCache() { std::lock_guard lock(mutex_); if (cache_mechanism_inited_ == false) return; @@ -187,7 +187,7 @@ void CacheMechanism::ClearCache() { SaveRepeatCounts(); } -int32_t CacheMechanism::GetTotalUsedCacheSize() const { +int32_t OfflineTtsCacheMechanism::GetTotalUsedCacheSize() const { std::lock_guard lock(mutex_); if (cache_mechanism_inited_ == false) return 0; @@ -197,7 +197,7 @@ int32_t CacheMechanism::GetTotalUsedCacheSize() const { // Private functions /////////////////////////////////////////////////// -void CacheMechanism::LoadRepeatCounts() { +void OfflineTtsCacheMechanism::LoadRepeatCounts() { std::string repeat_count_file = cache_dir_ + "/repeat_counts.txt"; // Check if the file exists @@ -225,7 +225,7 @@ void CacheMechanism::LoadRepeatCounts() { } } -void CacheMechanism::SaveRepeatCounts() { +void OfflineTtsCacheMechanism::SaveRepeatCounts() { std::string repeat_count_file = cache_dir_ + "/repeat_counts.txt"; // Open the file for writing @@ -248,7 +248,7 @@ void CacheMechanism::SaveRepeatCounts() { } } -void CacheMechanism::RemoveWavFile(const std::string &text_hash) { +void OfflineTtsCacheMechanism::RemoveWavFile(const std::string &text_hash) { std::string file_path = cache_dir_ + "/" + text_hash + ".wav"; if (std::filesystem::exists(file_path)) { // Subtract the size of the removed WAV file from the total cache size @@ -269,7 +269,7 @@ void CacheMechanism::RemoveWavFile(const std::string &text_hash) { } } -void CacheMechanism::UpdateCacheVector() { +void OfflineTtsCacheMechanism::UpdateCacheVector() { used_cache_size_bytes_ = 0; // Reset total cache size before recalculating for (const auto &entry : std::filesystem::directory_iterator(cache_dir_)) { @@ -290,7 +290,7 @@ void CacheMechanism::UpdateCacheVector() { } } -void CacheMechanism::EnsureCacheLimit() { +void OfflineTtsCacheMechanism::EnsureCacheLimit() { if (used_cache_size_bytes_ > cache_size_bytes_) { auto target_cache_size = std::max(static_cast (cache_size_bytes_*0.95), 0); @@ -303,7 +303,7 @@ void CacheMechanism::EnsureCacheLimit() { } } -std::string CacheMechanism::GetLeastRepeatedFile() { +std::string OfflineTtsCacheMechanism::GetLeastRepeatedFile() { std::string least_repeated_file; int32_t min_count = std::numeric_limits::max(); diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h index 4fe3110906..ff005fe19b 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h @@ -12,10 +12,10 @@ namespace sherpa_onnx { -class CacheMechanism { +class OfflineTtsCacheMechanism { public: - CacheMechanism(const std::string &cache_dir, int32_t cache_size); - ~CacheMechanism(); + OfflineTtsCacheMechanism(const std::string &cache_dir, int32_t cache_size); + ~OfflineTtsCacheMechanism(); // Add a new wav file to the cache void AddWavFile( diff --git a/sherpa-onnx/csrc/offline-tts.cc b/sherpa-onnx/csrc/offline-tts.cc index 5513d0284f..5d0f4cf2ab 100644 --- a/sherpa-onnx/csrc/offline-tts.cc +++ b/sherpa-onnx/csrc/offline-tts.cc @@ -153,8 +153,8 @@ void OfflineTts::SetCacheSize(const int32_t cache_size) { if (cache_size > 0) { if (!cache_mechanism_) { // Initialize the cache mechanism if it hasn't been initialized yet - cache_mechanism_ - = std::make_unique(config_.cache_dir, cache_size); + cache_mechanism_ = std::make_unique( + config_.cache_dir, cache_size); } else { // Update the cache size if the cache mechanism is already initialized cache_mechanism_->SetCacheSize(cache_size); diff --git a/sherpa-onnx/csrc/offline-tts.h b/sherpa-onnx/csrc/offline-tts.h index 57dbb8e341..060751beed 100644 --- a/sherpa-onnx/csrc/offline-tts.h +++ b/sherpa-onnx/csrc/offline-tts.h @@ -110,7 +110,7 @@ class OfflineTts { private: OfflineTtsConfig config_; std::unique_ptr impl_; - std::unique_ptr cache_mechanism_; + std::unique_ptr cache_mechanism_; }; } // namespace sherpa_onnx From f0c52428e48fee4969699f908e1a2d8209f6b1da Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 24 Jan 2025 07:32:06 +0330 Subject: [PATCH 07/14] Switched back to chrono as used in other files --- .../csrc/offline-tts-cache-mechanism.cc | 24 ++++--------------- .../csrc/offline-tts-cache-mechanism.h | 4 ++-- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc index e57b4226cf..166430bc70 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc @@ -5,6 +5,7 @@ #include "sherpa-onnx/csrc/offline-tts-cache-mechanism.h" #include +#include // NOLINT #include #include #include @@ -25,22 +26,6 @@ namespace sherpa_onnx { -// Helper function to get the current time in seconds -static int64_t GetCurrentTimeInSeconds() { -#if defined(_WIN32) - // Windows implementation - FILETIME ft; - GetSystemTimeAsFileTime(&ft); - uint64_t time = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime; - return static_cast(time / 10000000ULL - 11644473600ULL); -#else - // Unix implementation - struct timeval tv; - gettimeofday(&tv, nullptr); - return static_cast(tv.tv_sec); -#endif -} - OfflineTtsCacheMechanism::OfflineTtsCacheMechanism(const std::string &cache_dir, int32_t cache_size) : cache_dir_(cache_dir), @@ -66,7 +51,7 @@ OfflineTtsCacheMechanism::OfflineTtsCacheMechanism(const std::string &cache_dir, UpdateCacheVector(); // Initialize the last save time - last_save_time_ = GetCurrentTimeInSeconds(); + last_save_time_ = std::chrono::steady_clock::now(); // Indicate that initialization has been successful cache_mechanism_inited_ = true; @@ -139,8 +124,9 @@ std::vector OfflineTtsCacheMechanism::GetWavFile( } // Save the repeat counts every 10 minutes - int64_t now = GetCurrentTimeInSeconds(); - if (now - last_save_time_ >= 10 * 60) { + auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast( + now - last_save_time_).count() >= 10 * 60) { SaveRepeatCounts(); last_save_time_ = now; } diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h index ff005fe19b..c457622522 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h @@ -77,8 +77,8 @@ class OfflineTtsCacheMechanism { // Mutex for thread safety (recursive to avoid deadlocks) mutable std::recursive_mutex mutex_; - // Time of last save (in seconds since epoch) - int64_t last_save_time_; + // Time of last save + std::chrono::steady_clock::time_point last_save_time_; // if cache mechanism is inited successfully bool cache_mechanism_inited_; From f6e11ed50c5ffaa80b7782b98ceafb6a6017d8c6 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 24 Jan 2025 08:45:39 +0330 Subject: [PATCH 08/14] Added offline-tts-cache-mechanism-config.cc,.h --- sherpa-onnx/csrc/CMakeLists.txt | 1 + .../offline-tts-cache-mechanism-config.cc | 35 +++++++++++++++++++ .../csrc/offline-tts-cache-mechanism-config.h | 35 +++++++++++++++++++ .../csrc/offline-tts-cache-mechanism.h | 2 ++ sherpa-onnx/csrc/offline-tts.cc | 2 +- 5 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 sherpa-onnx/csrc/offline-tts-cache-mechanism-config.cc create mode 100644 sherpa-onnx/csrc/offline-tts-cache-mechanism-config.h diff --git a/sherpa-onnx/csrc/CMakeLists.txt b/sherpa-onnx/csrc/CMakeLists.txt index c7ab27046a..5b0bfef35f 100644 --- a/sherpa-onnx/csrc/CMakeLists.txt +++ b/sherpa-onnx/csrc/CMakeLists.txt @@ -155,6 +155,7 @@ if(SHERPA_ONNX_ENABLE_TTS) jieba-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 diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism-config.cc b/sherpa-onnx/csrc/offline-tts-cache-mechanism-config.cc new file mode 100644 index 0000000000..bd06794fe8 --- /dev/null +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism-config.cc @@ -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 + +#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 diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism-config.h b/sherpa-onnx/csrc/offline-tts-cache-mechanism-config.h new file mode 100644 index 0000000000..2f5d2baba4 --- /dev/null +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism-config.h @@ -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 + +#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_ diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h index c457622522..48f94c5b55 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h @@ -10,6 +10,8 @@ #include #include // NOLINT +#include "sherpa-onnx/csrc/offline-tts-cache-mechanism-config.h" + namespace sherpa_onnx { class OfflineTtsCacheMechanism { diff --git a/sherpa-onnx/csrc/offline-tts.cc b/sherpa-onnx/csrc/offline-tts.cc index 5d0f4cf2ab..b1aff38944 100644 --- a/sherpa-onnx/csrc/offline-tts.cc +++ b/sherpa-onnx/csrc/offline-tts.cc @@ -18,9 +18,9 @@ #include "sherpa-onnx/csrc/file-utils.h" #include "sherpa-onnx/csrc/macros.h" +#include "sherpa-onnx/csrc/offline-tts-cache-mechanism.h" #include "sherpa-onnx/csrc/offline-tts-impl.h" #include "sherpa-onnx/csrc/text-utils.h" -#include "sherpa-onnx/csrc/offline-tts-cache-mechanism.h" namespace sherpa_onnx { From 521e9007e7ffd7ce4f9719061039934cd1485cf6 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 25 Jan 2025 05:35:01 +0330 Subject: [PATCH 09/14] Using offline-tts-cahce-mechanism-config in both SherpaOnnxTts and SherpaOnnxEngine Removing cache_dir from OfflineTts Removing function implementations from OfflineTts Passing config pointer to OfflineTts Added cache_mechanism_ to OfflineTts (as public, might not be so neat) Changing all cache units in kotlin to Bytes from MB to reduce confusion. Showing is still in MBs --- .../com/k2fsa/sherpa/onnx/MainActivity.kt | 7 +- .../sherpa/onnx/tts/engine/MainActivity.kt | 24 +++--- .../onnx/tts/engine/PreferencesHelper.kt | 6 +- .../k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt | 11 ++- .../csrc/offline-tts-cache-mechanism.cc | 29 ++++--- .../csrc/offline-tts-cache-mechanism.h | 3 +- sherpa-onnx/csrc/offline-tts.cc | 60 +++++---------- sherpa-onnx/csrc/offline-tts.h | 26 +++---- sherpa-onnx/jni/offline-tts.cc | 77 +++++++++++++++---- sherpa-onnx/kotlin-api/Tts.kt | 39 +++++++--- 10 files changed, 167 insertions(+), 115 deletions(-) diff --git a/android/SherpaOnnxTts/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt b/android/SherpaOnnxTts/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt index 5aa5b9ad8c..bafeb57530 100644 --- a/android/SherpaOnnxTts/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt +++ b/android/SherpaOnnxTts/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt @@ -305,7 +305,12 @@ class MainActivity : AppCompatActivity() { ruleFars = ruleFars ?: "", )!! - tts = OfflineTts(assetManager = assets, config = config) + val cacheConfig = getOfflineTtsCacheMechanismConfig( + dataDir = dataDir ?: "", + cacheSize = 20*1024*1024, // Fixed to 20 MBs + )!! + + tts = OfflineTts(assetManager = assets, config = config, cacheConfig = cacheConfig) } diff --git a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt index 6b1371450a..f65b864e7c 100644 --- a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt +++ b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt @@ -80,7 +80,7 @@ class MainActivity : ComponentActivity() { val preferenceHelper = PreferenceHelper(this) - TtsEngine.cacheSize = preferenceHelper.getCacheSizeInMB() + TtsEngine.cacheSize = preferenceHelper.getTtsMechanismCacheSize() setContent { SherpaOnnxTtsEngineTheme { @@ -95,12 +95,12 @@ class MainActivity : ComponentActivity() { Box(modifier = Modifier.padding(it)) { Column(modifier = Modifier.padding(16.dp)) { // Track used cache size in a mutable state - var usedCacheSizeMB by remember { mutableStateOf(0) } + var usedCacheSize by remember { mutableStateOf(0) } // LaunchedEffect to periodically update the used cache size LaunchedEffect(Unit) { while (true) { - usedCacheSizeMB = TtsEngine.tts?.getTotalUsedCacheSizeInMB() ?: 0 + usedCacheSize = TtsEngine.tts?.getTotalUsedCacheSize() ?: 0 delay(5000) // Update every 5 seconds } } @@ -113,23 +113,23 @@ class MainActivity : ComponentActivity() { TtsEngine.speed = it preferenceHelper.setSpeed(it) TtsEngine.tts?.clearCache() // Call the clearCache method - usedCacheSizeMB = 0 // Reset used cache size + usedCacheSize = 0 // Reset used cache size }, valueRange = 0.2F..3.0F, modifier = Modifier.fillMaxWidth() ) - Text("Cache Size: ${TtsEngine.cacheSize}MB (${usedCacheSizeMB}MB used)") + 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 10 - val roundedValue = (newValue / 5).roundToInt() * 5 + // Round the value to the nearest multiple of 5MB + val roundedValue = (newValue / (5 * 1024 * 1024)).roundToInt() * (5 * 1024 * 1024) TtsEngine.cacheSize = roundedValue - preferenceHelper.setCacheSizeInMB(roundedValue) - TtsEngine.tts?.setCacheSizeInMB(roundedValue) + preferenceHelper.setCacheSize(roundedValue) + TtsEngine.tts?.setCacheSize(roundedValue) }, - valueRange = 0f..100f, + valueRange = 0f..209715200f, // 200MB modifier = Modifier.fillMaxWidth() ) } @@ -308,8 +308,8 @@ class MainActivity : ComponentActivity() { override fun onResume() { super.onResume() // Update used cache size when the app is resumed - val usedCacheSizeMB = TtsEngine.tts?.getTotalUsedCacheSizeInMB() ?: 0 - Log.i(TAG, "App resumed. Used cache size: ${usedCacheSizeMB}MB") + val usedCacheSize = (TtsEngine.tts?.getTotalUsedCacheSize() ?: 0) + Log.i(TAG, "App resumed. Used cache size: ${usedCacheSize}B") } override fun onDestroy() { diff --git a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/PreferencesHelper.kt b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/PreferencesHelper.kt index 314ffa87de..b856914be8 100644 --- a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/PreferencesHelper.kt +++ b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/PreferencesHelper.kt @@ -31,13 +31,13 @@ class PreferenceHelper(context: Context) { return sharedPreferences.getInt(SID_KEY, 0) } - fun setCacheSizeInMB(value: Int) { + fun setCacheSize(value: Int) { val editor = sharedPreferences.edit() editor.putInt(CACHE_SIZE_KEY, value) editor.apply() } - fun getCacheSizeInMB(): Int { - return sharedPreferences.getInt(CACHE_SIZE_KEY, 20) // Default cache size is 20MB + fun getTtsMechanismCacheSize(): Int { + return sharedPreferences.getInt(CACHE_SIZE_KEY, 20*(1024*1024)) // Default cache size is 20MB } } diff --git a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt index 91f6b2d127..6a72da597f 100644 --- a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt +++ b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt @@ -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 @@ -195,13 +196,17 @@ object TtsEngine { ruleFars = ruleFars ?: "" ) + cacheSize = PreferenceHelper(context).getTtsMechanismCacheSize() + val cacheConfig = getOfflineTtsCacheMechanismConfig( + dataDir = dataDir ?: "", + cacheSize = cacheSize, + ) + speed = PreferenceHelper(context).getSpeed() speakerId = PreferenceHelper(context).getSid() cacheSize = PreferenceHelper(context).getCacheSizeInMB() - tts = OfflineTts(assetManager = assets, config = config) - - tts?.setCacheSizeInMB(cacheSize) + tts = OfflineTts(assetManager = assets, config = config, cacheConfig = cacheConfig) } diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc index 166430bc70..049e0d90b6 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc @@ -16,22 +16,14 @@ #include "sherpa-onnx/csrc/wave-reader.h" #include "sherpa-onnx/csrc/wave-writer.h" -// Platform-specific time functions -#if defined(_WIN32) -#include -#else -#include -#include -#endif - namespace sherpa_onnx { -OfflineTtsCacheMechanism::OfflineTtsCacheMechanism(const std::string &cache_dir, - int32_t cache_size) - : cache_dir_(cache_dir), - cache_size_bytes_(cache_size), - used_cache_size_bytes_(0) { - +OfflineTtsCacheMechanism::OfflineTtsCacheMechanism( + const OfflineTtsCacheMechanismConfig &config) + : cache_dir_(config.cache_dir), + cache_size_bytes_(config.cache_size), + used_cache_size_bytes_(0) +{ // Create the cache directory if it doesn't exist if (!std::filesystem::exists(cache_dir_)) { bool dir_created = std::filesystem::create_directory(cache_dir_); @@ -44,6 +36,9 @@ OfflineTtsCacheMechanism::OfflineTtsCacheMechanism(const std::string &cache_dir, } } + if(cache_size_bytes_ == -1) + cache_size_bytes_ = INT32_MAX; // Unlimited cache size + // Load the repeat counts LoadRepeatCounts(); @@ -147,7 +142,11 @@ void OfflineTtsCacheMechanism::SetCacheSize(int32_t cache_size) { cache_size_bytes_ = cache_size; - EnsureCacheLimit(); + if(cache_size == 0) { + ClearCache(); + } else { + EnsureCacheLimit(); + } } void OfflineTtsCacheMechanism::ClearCache() { diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h index 48f94c5b55..0c672e1c29 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h @@ -16,7 +16,8 @@ namespace sherpa_onnx { class OfflineTtsCacheMechanism { public: - OfflineTtsCacheMechanism(const std::string &cache_dir, int32_t cache_size); + + explicit OfflineTtsCacheMechanism(const OfflineTtsCacheMechanismConfig &config); ~OfflineTtsCacheMechanism(); // Add a new wav file to the cache diff --git a/sherpa-onnx/csrc/offline-tts.cc b/sherpa-onnx/csrc/offline-tts.cc index b1aff38944..decb5a5266 100644 --- a/sherpa-onnx/csrc/offline-tts.cc +++ b/sherpa-onnx/csrc/offline-tts.cc @@ -86,15 +86,24 @@ std::string OfflineTtsConfig::ToString() const { } OfflineTts::OfflineTts(const OfflineTtsConfig &config) - : config_(config), - impl_(OfflineTtsImpl::Create(config)), - cache_mechanism_(nullptr) {} + : impl_(OfflineTtsImpl::Create(config)) {} + +OfflineTts::OfflineTts(const OfflineTtsConfig &config, + const OfflineTtsCacheMechanismConfig &cache_config) + : impl_(OfflineTtsImpl::Create(config)) { + cache_mechanism_ = std::make_unique(cache_config); +} template OfflineTts::OfflineTts(Manager *mgr, const OfflineTtsConfig &config) - : config_(config), - impl_(OfflineTtsImpl::Create(mgr, config)), - cache_mechanism_(nullptr) {} + : impl_(OfflineTtsImpl::Create(mgr, config)) {} + +template +OfflineTts::OfflineTts(Manager *mgr, const OfflineTtsConfig &config, + const OfflineTtsCacheMechanismConfig &cache_config) + : impl_(OfflineTtsImpl::Create(mgr, config)) { + cache_mechanism_ = std::make_unique(cache_config); +} OfflineTts::~OfflineTts() = default; @@ -145,49 +154,22 @@ GeneratedAudio OfflineTts::Generate( int32_t OfflineTts::SampleRate() const { return impl_->SampleRate(); } -int32_t OfflineTts::CacheSize() const { - return cache_mechanism_ ? cache_mechanism_->GetCacheSize() : 0; -} - -void OfflineTts::SetCacheSize(const int32_t cache_size) { - if (cache_size > 0) { - if (!cache_mechanism_) { - // Initialize the cache mechanism if it hasn't been initialized yet - cache_mechanism_ = std::make_unique( - config_.cache_dir, cache_size); - } else { - // Update the cache size if the cache mechanism is already initialized - cache_mechanism_->SetCacheSize(cache_size); - } - } else if (cache_mechanism_) { - // If cache size is set to 0 or negative, destroy the cache mechanism - cache_mechanism_.reset(); - } -} - -void OfflineTts::ClearCache() { - if (cache_mechanism_) { - cache_mechanism_->ClearCache(); - } -} - -int32_t OfflineTts::GetTotalUsedCacheSize() { - if (cache_mechanism_) { - return cache_mechanism_->GetTotalUsedCacheSize(); - } - return -1; -} - int32_t OfflineTts::NumSpeakers() const { return impl_->NumSpeakers(); } #if __ANDROID_API__ >= 9 template OfflineTts::OfflineTts(AAssetManager *mgr, const OfflineTtsConfig &config); +template OfflineTts::OfflineTts(AAssetManager *mgr, + const OfflineTtsConfig &config, + const OfflineTtsCacheMechanismConfig &cache_config); #endif #if __OHOS__ template OfflineTts::OfflineTts(NativeResourceManager *mgr, const OfflineTtsConfig &config); +template OfflineTts::OfflineTts(NativeResourceManager *mgr, + const OfflineTtsConfig &config, + const OfflineTtsCacheMechanismConfig &cache_config); #endif } // namespace sherpa_onnx diff --git a/sherpa-onnx/csrc/offline-tts.h b/sherpa-onnx/csrc/offline-tts.h index 060751beed..40d92dd070 100644 --- a/sherpa-onnx/csrc/offline-tts.h +++ b/sherpa-onnx/csrc/offline-tts.h @@ -18,6 +18,7 @@ namespace sherpa_onnx { struct OfflineTtsConfig { OfflineTtsModelConfig model; + // If not empty, it contains a list of rule FST filenames. // Filenames are separated by a comma. // Example value: rule1.fst,rule2,fst,rule3.fst @@ -33,9 +34,6 @@ struct OfflineTtsConfig { // If you set it to -1, then we process all sentences in a single batch. int32_t max_num_sentences = 1; - // Path to cache_directory - std::string cache_dir; - OfflineTtsConfig() = default; OfflineTtsConfig(const OfflineTtsModelConfig &model, const std::string &rule_fsts, const std::string &rule_fars, @@ -67,10 +65,16 @@ class OfflineTts { public: ~OfflineTts(); explicit OfflineTts(const OfflineTtsConfig &config); + explicit OfflineTts(const OfflineTtsConfig &config, + const OfflineTtsCacheMechanismConfig &cache_config); template OfflineTts(Manager *mgr, const OfflineTtsConfig &config); + template + OfflineTts(Manager *mgr, const OfflineTtsConfig &config, + const OfflineTtsCacheMechanismConfig &cache_config); + // @param text A string containing words separated by spaces // @param sid Speaker ID. Used only for multi-speaker models, e.g., models // trained using the VCTK dataset. It is not used for @@ -91,26 +95,14 @@ class OfflineTts { // Return the sample rate of the generated audio int32_t SampleRate() const; - // Return the maximum number of cached audio files size - int32_t CacheSize() const; - - // Set the maximum number of cached audio files size - void SetCacheSize(const int32_t cache_size); - - // Remove all cache data - void ClearCache(); - - // To get total used cache size(for wav files) in bytes - int32_t GetTotalUsedCacheSize(); - // Number of supported speakers. // If it supports only a single speaker, then it return 0 or 1. int32_t NumSpeakers() const; + std::unique_ptr cache_mechanism_; // not owned here + private: - OfflineTtsConfig config_; std::unique_ptr impl_; - std::unique_ptr cache_mechanism_; }; } // namespace sherpa_onnx diff --git a/sherpa-onnx/jni/offline-tts.cc b/sherpa-onnx/jni/offline-tts.cc index 693dccaa42..1191d47e8a 100644 --- a/sherpa-onnx/jni/offline-tts.cc +++ b/sherpa-onnx/jni/offline-tts.cc @@ -175,6 +175,27 @@ static OfflineTtsConfig GetOfflineTtsConfig(JNIEnv *env, jobject config) { fid = env->GetFieldID(cls, "maxNumSentences", "I"); ans.max_num_sentences = env->GetIntField(config, fid); + return ans; +} + +static OfflineTtsCacheMechanismConfig GetOfflineTtsCacheConfig(JNIEnv *env, jobject config) { + OfflineTtsCacheMechanismConfig ans; + + jclass cls = env->GetObjectClass(config); + jfieldID fid; + + fid = env->GetFieldID(cls, "cacheDir", "Ljava/lang/String;"); + jstring s = (jstring)env->GetObjectField(config, fid); + const char *p = env->GetStringUTFChars(s, nullptr); + ans.cache_dir = p; + env->ReleaseStringUTFChars(s, p); + + fid = env->GetFieldID(cls, "cacheSize", "I"); + ans.cache_size = env->GetIntField(config, fid); + + return ans; +} + // Get data directory from config jfieldID model_fid = env->GetFieldID(cls, "model", "Lcom/k2fsa/sherpa/onnx/OfflineTtsModelConfig;"); jobject model_config = env->GetObjectField(config, model_fid); @@ -193,14 +214,11 @@ static OfflineTtsConfig GetOfflineTtsConfig(JNIEnv *env, jobject config) { env->ReleaseStringUTFChars(data_dir, p_data_dir); - return ans; -} - } // namespace sherpa_onnx SHERPA_ONNX_EXTERN_C JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_newFromAsset( - JNIEnv *env, jobject /*obj*/, jobject asset_manager, jobject _config) { + JNIEnv *env, jobject /*obj*/, jobject asset_manager, jobject _config, jobject _cache_config) { #if __ANDROID_API__ >= 9 AAssetManager *mgr = AAssetManager_fromJava(env, asset_manager); if (!mgr) { @@ -208,29 +226,37 @@ JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_newFromAsset( return 0; } #endif + auto config = sherpa_onnx::GetOfflineTtsConfig(env, _config); SHERPA_ONNX_LOGE("config:\n%s", config.ToString().c_str()); + auto cache_config = sherpa_onnx::GetOfflineTtsCacheConfig(env, _cache_config); + SHERPA_ONNX_LOGE("cache config:\n%s", cache_config.ToString().c_str()); + auto tts = new sherpa_onnx::OfflineTts( #if __ANDROID_API__ >= 9 mgr, #endif - config); + config, + cache_config); return (jlong)tts; } SHERPA_ONNX_EXTERN_C JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_newFromFile( - JNIEnv *env, jobject /*obj*/, jobject _config) { + JNIEnv *env, jobject /*obj*/, jobject _config, jobject _cache_config) { auto config = sherpa_onnx::GetOfflineTtsConfig(env, _config); SHERPA_ONNX_LOGE("config:\n%s", config.ToString().c_str()); - if (!config.Validate()) { - SHERPA_ONNX_LOGE("Errors found in config!"); + auto cache_config = sherpa_onnx::GetOfflineTtsCacheConfig(env, _cache_config); + SHERPA_ONNX_LOGE("cache config:\n%s", cache_config.ToString().c_str()); + + if (!cache_config.Validate()) { + SHERPA_ONNX_LOGE("Errors found in cache_config!"); } - auto tts = new sherpa_onnx::OfflineTts(config); + auto tts = new sherpa_onnx::OfflineTts(config, cache_config); return (jlong)tts; } @@ -244,13 +270,25 @@ JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_delete( SHERPA_ONNX_EXTERN_C JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_setCacheSizeImpl( JNIEnv * /*env*/, jobject /*obj*/, jlong ptr, jint cacheSize) { - reinterpret_cast(ptr)->SetCacheSize(static_cast(cacheSize)); + auto tts = reinterpret_cast(ptr); + if (tts) { + if(tts->cache_mechanism_) { + tts->cache_mechanism_->SetCacheSize(static_cast(cacheSize)); + } + } } SHERPA_ONNX_EXTERN_C JNIEXPORT jint JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getCacheSizeImpl( JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { - return reinterpret_cast(ptr)->CacheSize(); + auto tts = reinterpret_cast(ptr); + if (tts) { + if(tts->cache_mechanism_) { + return tts->cache_mechanism_->GetCacheSize(); + } + } + + return 0; } SHERPA_ONNX_EXTERN_C @@ -268,7 +306,14 @@ JNIEXPORT jint JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getNumSpeakers( SHERPA_ONNX_EXTERN_C JNIEXPORT jint JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getTotalUsedCacheSizeImpl( JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { - return reinterpret_cast(ptr)->GetTotalUsedCacheSize(); + auto tts = reinterpret_cast(ptr); + if (tts) { + if(tts->cache_mechanism_) { + return tts->cache_mechanism_->GetTotalUsedCacheSize(); + } + } + + return 0; } SHERPA_ONNX_EXTERN_C @@ -277,8 +322,12 @@ Java_com_k2fsa_sherpa_onnx_OfflineTts_clearCacheImpl( JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { auto tts = reinterpret_cast(ptr); if (tts) { - tts->ClearCache(); - SHERPA_ONNX_LOGE("Cache cleared from JNI."); + if(tts->cache_mechanism_) { + tts->cache_mechanism_->ClearCache(); + static int times = 0; + times++; + SHERPA_ONNX_LOGE("Cache cleared from JNI for %ith time\n", times); + } } } diff --git a/sherpa-onnx/kotlin-api/Tts.kt b/sherpa-onnx/kotlin-api/Tts.kt index f53f56462b..c3cfc92bc7 100644 --- a/sherpa-onnx/kotlin-api/Tts.kt +++ b/sherpa-onnx/kotlin-api/Tts.kt @@ -49,6 +49,11 @@ data class OfflineTtsConfig( var maxNumSentences: Int = 1, ) +data class OfflineTtsCacheMechanismConfig( + var cacheDir: String = "", + var cacheSize: Int = -1, // Unlimited +) + class GeneratedAudio( val samples: FloatArray, val sampleRate: Int, @@ -66,14 +71,15 @@ class GeneratedAudio( class OfflineTts( assetManager: AssetManager? = null, var config: OfflineTtsConfig, + var cacheConfig: OfflineTtsCacheMechanismConfig, ) { private var ptr: Long init { ptr = if (assetManager != null) { - newFromAsset(assetManager, config) + newFromAsset(assetManager, config, cacheConfig) } else { - newFromFile(config) + newFromFile(config, cacheConfig) } } @@ -115,9 +121,9 @@ class OfflineTts( fun allocate(assetManager: AssetManager? = null) { if (ptr == 0L) { ptr = if (assetManager != null) { - newFromAsset(assetManager, config) + newFromAsset(assetManager, config, cacheConfig) } else { - newFromFile(config) + newFromFile(config, cacheConfig) } } } @@ -141,28 +147,30 @@ class OfflineTts( private external fun newFromAsset( assetManager: AssetManager, config: OfflineTtsConfig, + cacheConfig: OfflineTtsCacheMechanismConfig, ): Long private external fun newFromFile( config: OfflineTtsConfig, + cacheConfig: OfflineTtsCacheMechanismConfig, ): Long private external fun delete(ptr: Long) private external fun getSampleRate(ptr: Long): Int private external fun getNumSpeakers(ptr: Long): Int - fun getCacheSizeInMB(): Int { - return (getCacheSizeImpl(ptr) / (1024 * 1024)).toInt() // Convert bytes to MB + fun getTtsMechanismCacheSize(): Int { + return (getCacheSizeImpl(ptr)).toInt() } private external fun getCacheSizeImpl(ptr: Long): Int - fun setCacheSizeInMB(cacheSize: Int) { - setCacheSizeImpl(ptr, cacheSize * (1024 * 1024)) + fun setCacheSize(cacheSize: Int) { + setCacheSizeImpl(ptr, cacheSize) } private external fun setCacheSizeImpl(ptr: Long, cacheSize: Int) - fun getTotalUsedCacheSizeInMB(): Int { - return (getTotalUsedCacheSizeImpl(ptr) / (1024 * 1024)).toInt() // Convert bytes to MB + fun getTotalUsedCacheSize(): Int { + return (getTotalUsedCacheSizeImpl(ptr)).toInt() } private external fun getTotalUsedCacheSizeImpl(ptr: Long): Int @@ -294,3 +302,14 @@ fun getOfflineTtsConfig( ruleFars = ruleFars, ) } + +fun getOfflineTtsCacheMechanismConfig( + dataDir: String, + cacheSize: Int +): OfflineTtsCacheMechanismConfig { + + return OfflineTtsCacheMechanismConfig( + cacheDir = "$dataDir/../cache", + cacheSize = cacheSize, + ) +} From 1291ed22d57d1a5c289368b57684256b33a59a5b Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 25 Jan 2025 06:36:26 +0330 Subject: [PATCH 10/14] Changed all text_hash from string to size_t for more performance. Also solved bug by accidentally pasted extra code in previous commit --- .../csrc/offline-tts-cache-mechanism.cc | 82 ++++++++++--------- .../csrc/offline-tts-cache-mechanism.h | 14 ++-- sherpa-onnx/csrc/offline-tts.cc | 6 +- sherpa-onnx/jni/offline-tts.cc | 18 ---- 4 files changed, 52 insertions(+), 68 deletions(-) diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc index 049e0d90b6..ecd19f4c65 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc @@ -10,6 +10,7 @@ #include #include #include +#include // for std::size_t #include "sherpa-onnx/csrc/file-utils.h" #include "sherpa-onnx/csrc/macros.h" @@ -60,14 +61,14 @@ OfflineTtsCacheMechanism::~OfflineTtsCacheMechanism() { } void OfflineTtsCacheMechanism::AddWavFile( - const std::string &text_hash, + const std::size_t &text_hash, const std::vector &samples, const int32_t sample_rate) { std::lock_guard lock(mutex_); if (cache_mechanism_inited_ == false) return; - std::string file_path = cache_dir_ + "/" + text_hash + ".wav"; + std::string file_path = cache_dir_ + "/" + std::to_string(text_hash) + ".wav"; // Check if the file physically exists in the cache directory bool file_exists = std::filesystem::exists(file_path); @@ -92,7 +93,7 @@ void OfflineTtsCacheMechanism::AddWavFile( } std::vector OfflineTtsCacheMechanism::GetWavFile( - const std::string &text_hash, + const std::size_t &text_hash, int32_t *sample_rate) { std::lock_guard lock(mutex_); @@ -100,7 +101,7 @@ std::vector OfflineTtsCacheMechanism::GetWavFile( if (cache_mechanism_inited_ == false) return samples; - std::string file_path = cache_dir_ + "/" + text_hash + ".wav"; + std::string file_path = cache_dir_ + "/" + std::to_string(text_hash) + ".wav"; if (std::filesystem::exists(file_path)) { bool is_ok = false; @@ -119,12 +120,12 @@ std::vector OfflineTtsCacheMechanism::GetWavFile( } // Save the repeat counts every 10 minutes - auto now = std::chrono::steady_clock::now(); - if (std::chrono::duration_cast( - now - last_save_time_).count() >= 10 * 60) { + //auto now = std::chrono::steady_clock::now(); + //if (std::chrono::duration_cast( + //now - last_save_time_).count() >= 10 * 60) { SaveRepeatCounts(); - last_save_time_ = now; - } + //last_save_time_ = now; + //} return samples; } @@ -168,7 +169,7 @@ void OfflineTtsCacheMechanism::ClearCache() { repeat_counts_.clear(); cache_vector_.clear(); - // Remove repeat counts also in the repeat_counts.txt + // Remove repeat counts also in the repeat_counts file SaveRepeatCounts(); } @@ -183,58 +184,60 @@ int32_t OfflineTtsCacheMechanism::GetTotalUsedCacheSize() const { // Private functions /////////////////////////////////////////////////// void OfflineTtsCacheMechanism::LoadRepeatCounts() { - std::string repeat_count_file = cache_dir_ + "/repeat_counts.txt"; + std::string repeat_count_file = cache_dir_ + "/repeat_counts.bin"; // Check if the file exists if (!std::filesystem::exists(repeat_count_file)) { return; // Skip loading if the file doesn't exist } - // Open the file for reading - std::ifstream ifs(repeat_count_file); + // Open the file for reading in binary mode + std::ifstream ifs(repeat_count_file, std::ios::binary); if (!ifs.is_open()) { SHERPA_ONNX_LOGE("Failed to open repeat count file: %s", repeat_count_file.c_str()); return; // Skip loading if the file cannot be opened } - // Read the file line by line - std::string line; - while (std::getline(ifs, line)) { - size_t pos = line.find(' '); - if (pos != std::string::npos) { - std::string text_hash = line.substr(0, pos); - int32_t count = std::stoi(line.substr(pos + 1)); - repeat_counts_[text_hash] = count; - } + // Read the number of entries + size_t num_entries; + ifs.read(reinterpret_cast(&num_entries), sizeof(num_entries)); + + // Read each entry + for (size_t i = 0; i < num_entries; ++i) { + std::size_t text_hash; + int32_t count; + ifs.read(reinterpret_cast(&text_hash), sizeof(text_hash)); + ifs.read(reinterpret_cast(&count), sizeof(count)); + repeat_counts_[text_hash] = count; } } void OfflineTtsCacheMechanism::SaveRepeatCounts() { - std::string repeat_count_file = cache_dir_ + "/repeat_counts.txt"; + std::string repeat_count_file = cache_dir_ + "/repeat_counts.bin"; - // Open the file for writing - std::ofstream ofs(repeat_count_file); + // Open the file for writing in binary mode + std::ofstream ofs(repeat_count_file, std::ios::binary); if (!ofs.is_open()) { SHERPA_ONNX_LOGE("Failed to open repeat count file for writing: %s", repeat_count_file.c_str()); return; // Skip saving if the file cannot be opened } - // Write the repeat counts to the file + // Write the number of entries + size_t num_entries = repeat_counts_.size(); + ofs.write(reinterpret_cast(&num_entries), sizeof(num_entries)); + + // Write each entry for (const auto &entry : repeat_counts_) { - ofs << entry.first << " " << entry.second; - if (!ofs) { - SHERPA_ONNX_LOGE("Failed to write repeat count for text hash: %s", - entry.first.c_str()); - return; // Stop writing if an error occurs - } - ofs << std::endl; + ofs.write(reinterpret_cast(&entry.first), sizeof(entry.first)); + ofs.write(reinterpret_cast(&entry.second), sizeof(entry.second)); } } -void OfflineTtsCacheMechanism::RemoveWavFile(const std::string &text_hash) { - std::string file_path = cache_dir_ + "/" + text_hash + ".wav"; +void OfflineTtsCacheMechanism::RemoveWavFile(const std::size_t &text_hash) { + std::string file_path = cache_dir_ + "/" + + std::to_string(text_hash) + ".wav"; if (std::filesystem::exists(file_path)) { // Subtract the size of the removed WAV file from the total cache size std::ifstream file(file_path, std::ios::binary | std::ios::ate); @@ -259,7 +262,8 @@ void OfflineTtsCacheMechanism::UpdateCacheVector() { for (const auto &entry : std::filesystem::directory_iterator(cache_dir_)) { if (entry.path().extension() == ".wav") { - std::string text_hash = entry.path().stem().string(); + std::string text_hash_str = entry.path().stem().string(); + std::size_t text_hash = std::stoull(text_hash_str); if (repeat_counts_.find(text_hash) == repeat_counts_.end()) { // Remove the file if it's not in the repeat count file (orphaned file) std::filesystem::remove(entry.path()); @@ -282,14 +286,14 @@ void OfflineTtsCacheMechanism::EnsureCacheLimit() { while (used_cache_size_bytes_> 0 && used_cache_size_bytes_ > target_cache_size) { // Cache is full, remove the least repeated file - std::string least_repeated_file = GetLeastRepeatedFile(); + std::size_t least_repeated_file = GetLeastRepeatedFile(); RemoveWavFile(least_repeated_file); } } } -std::string OfflineTtsCacheMechanism::GetLeastRepeatedFile() { - std::string least_repeated_file; +std::size_t OfflineTtsCacheMechanism::GetLeastRepeatedFile() { + std::size_t least_repeated_file = 0; int32_t min_count = std::numeric_limits::max(); for (const auto &entry : repeat_counts_) { diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h index 0c672e1c29..b1e5dd0dac 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h @@ -9,6 +9,7 @@ #include #include #include // NOLINT +#include // for std::size_t #include "sherpa-onnx/csrc/offline-tts-cache-mechanism-config.h" @@ -16,19 +17,18 @@ namespace sherpa_onnx { class OfflineTtsCacheMechanism { public: - explicit OfflineTtsCacheMechanism(const OfflineTtsCacheMechanismConfig &config); ~OfflineTtsCacheMechanism(); // Add a new wav file to the cache void AddWavFile( - const std::string &text_hash, + const std::size_t &text_hash, const std::vector &samples, const int32_t sample_rate); // Get the cached wav file if it exists std::vector GetWavFile( - const std::string &text_hash, + const std::size_t &text_hash, int32_t *sample_rate); // Get the current cache size in bytes @@ -51,7 +51,7 @@ class OfflineTtsCacheMechanism { void SaveRepeatCounts(); // Remove a wav file from the cache - void RemoveWavFile(const std::string &text_hash); + void RemoveWavFile(const std::size_t &text_hash); // Update the cache vector with the actual files in the cache folder void UpdateCacheVector(); @@ -60,7 +60,7 @@ class OfflineTtsCacheMechanism { void EnsureCacheLimit(); // Get the least repeated file in the cache - std::string GetLeastRepeatedFile(); + std::size_t GetLeastRepeatedFile(); // Data directory where the cache folder is located std::string cache_dir_; @@ -72,10 +72,10 @@ class OfflineTtsCacheMechanism { int32_t used_cache_size_bytes_; // Map of text hash to repeat count - std::unordered_map repeat_counts_; + std::unordered_map repeat_counts_; // Vector of cached file names - std::vector cache_vector_; + std::vector cache_vector_; // Mutex for thread safety (recursive to avoid deadlocks) mutable std::recursive_mutex mutex_; diff --git a/sherpa-onnx/csrc/offline-tts.cc b/sherpa-onnx/csrc/offline-tts.cc index decb5a5266..c7a52a88b7 100644 --- a/sherpa-onnx/csrc/offline-tts.cc +++ b/sherpa-onnx/csrc/offline-tts.cc @@ -112,8 +112,7 @@ GeneratedAudio OfflineTts::Generate( GeneratedAudioCallback callback /*= nullptr*/) const { // Generate a hash for the text std::hash hasher; - std::string text_hash = std::to_string(hasher(text)); - // SHERPA_ONNX_LOGE("Generated text hash: %s", text_hash.c_str()); + std::size_t text_hash = hasher(text); // Check if the cache mechanism is active and if the audio is already cached if (cache_mechanism_) { @@ -122,7 +121,7 @@ GeneratedAudio OfflineTts::Generate( = cache_mechanism_->GetWavFile(text_hash, &sample_rate); if (!samples.empty()) { - SHERPA_ONNX_LOGE("Returning cached audio for hash:%s", text_hash.c_str()); + SHERPA_ONNX_LOGE("Returning cached audio for hash: %zu", text_hash); // If a callback is provided, call it with the cached audio if (callback) { @@ -146,7 +145,6 @@ GeneratedAudio OfflineTts::Generate( // Cache the generated audio if the cache mechanism is active if (cache_mechanism_) { cache_mechanism_->AddWavFile(text_hash, audio.samples, audio.sample_rate); - // SHERPA_ONNX_LOGE("Cached audio for text hash: %s", text_hash.c_str()); } return audio; diff --git a/sherpa-onnx/jni/offline-tts.cc b/sherpa-onnx/jni/offline-tts.cc index 1191d47e8a..8629e109ea 100644 --- a/sherpa-onnx/jni/offline-tts.cc +++ b/sherpa-onnx/jni/offline-tts.cc @@ -196,24 +196,6 @@ static OfflineTtsCacheMechanismConfig GetOfflineTtsCacheConfig(JNIEnv *env, jobj return ans; } - // Get data directory from config - jfieldID model_fid = env->GetFieldID(cls, "model", "Lcom/k2fsa/sherpa/onnx/OfflineTtsModelConfig;"); - jobject model_config = env->GetObjectField(config, model_fid); - jclass model_cls = env->GetObjectClass(model_config); - - jfieldID vits_fid = env->GetFieldID(model_cls, "vits", "Lcom/k2fsa/sherpa/onnx/OfflineTtsVitsModelConfig;"); - jobject vits_config = env->GetObjectField(model_config, vits_fid); - - fid = env->GetFieldID(vits_cls, "dataDir", "Ljava/lang/String;"); - jstring data_dir = (jstring)env->GetObjectField(vits_config, fid); - const char *p_data_dir = env->GetStringUTFChars(data_dir, nullptr); - - // Convert data directory to cache directory - std::string cache_dir = std::string(p_data_dir) + "/../cache"; - ans.cache_dir = cache_dir; - - env->ReleaseStringUTFChars(data_dir, p_data_dir); - } // namespace sherpa_onnx SHERPA_ONNX_EXTERN_C From 9b724eb5f29d3899016d1d5b279e14ce05773594 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 25 Jan 2025 07:41:23 +0330 Subject: [PATCH 11/14] Use binary repeat_counts file for better performance, Changed repeat_counts from uint32_t to size_t for better alignment in the file, Changed repeat_counts save time to every 1minute as destructor not working --- .../csrc/offline-tts-cache-mechanism.cc | 26 +++++++++++++------ .../csrc/offline-tts-cache-mechanism.h | 2 +- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc index ecd19f4c65..0d769728c6 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc @@ -119,13 +119,13 @@ std::vector OfflineTtsCacheMechanism::GetWavFile( repeat_counts_[text_hash]++; // Increment the repeat count } - // Save the repeat counts every 10 minutes - //auto now = std::chrono::steady_clock::now(); - //if (std::chrono::duration_cast( - //now - last_save_time_).count() >= 10 * 60) { + // Save the repeat counts every minute + auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast( + now - last_save_time_).count() >= 1 * 60) { SaveRepeatCounts(); - //last_save_time_ = now; - //} + last_save_time_ = now; + } return samples; } @@ -206,7 +206,7 @@ void OfflineTtsCacheMechanism::LoadRepeatCounts() { // Read each entry for (size_t i = 0; i < num_entries; ++i) { std::size_t text_hash; - int32_t count; + std::size_t count; ifs.read(reinterpret_cast(&text_hash), sizeof(text_hash)); ifs.read(reinterpret_cast(&count), sizeof(count)); repeat_counts_[text_hash] = count; @@ -214,6 +214,9 @@ void OfflineTtsCacheMechanism::LoadRepeatCounts() { } void OfflineTtsCacheMechanism::SaveRepeatCounts() { + // Start timing + auto start_time = std::chrono::steady_clock::now(); + std::string repeat_count_file = cache_dir_ + "/repeat_counts.bin"; // Open the file for writing in binary mode @@ -233,6 +236,13 @@ void OfflineTtsCacheMechanism::SaveRepeatCounts() { ofs.write(reinterpret_cast(&entry.first), sizeof(entry.first)); ofs.write(reinterpret_cast(&entry.second), sizeof(entry.second)); } + + // End timing + auto end_time = std::chrono::steady_clock::now(); + auto elapsed_time = std::chrono::duration_cast(end_time - start_time).count(); + + // Print the time taken + SHERPA_ONNX_LOGE("SaveRepeatCounts took %lld milliseconds", elapsed_time); } void OfflineTtsCacheMechanism::RemoveWavFile(const std::size_t &text_hash) { @@ -294,7 +304,7 @@ void OfflineTtsCacheMechanism::EnsureCacheLimit() { std::size_t OfflineTtsCacheMechanism::GetLeastRepeatedFile() { std::size_t least_repeated_file = 0; - int32_t min_count = std::numeric_limits::max(); + std::size_t min_count = std::numeric_limits::max(); for (const auto &entry : repeat_counts_) { if (entry.second <= 1) { diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h index b1e5dd0dac..9945b1f3cd 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.h +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.h @@ -72,7 +72,7 @@ class OfflineTtsCacheMechanism { int32_t used_cache_size_bytes_; // Map of text hash to repeat count - std::unordered_map repeat_counts_; + std::unordered_map repeat_counts_; // Vector of cached file names std::vector cache_vector_; From 1be61a3ac65b3100655a5db4c8b3e5f2b2305dc2 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 13 Feb 2025 17:40:13 +0330 Subject: [PATCH 12/14] bug fix --- .../src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt index 6a72da597f..e9b191d8bd 100644 --- a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt +++ b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt @@ -204,7 +204,6 @@ object TtsEngine { speed = PreferenceHelper(context).getSpeed() speakerId = PreferenceHelper(context).getSid() - cacheSize = PreferenceHelper(context).getCacheSizeInMB() tts = OfflineTts(assetManager = assets, config = config, cacheConfig = cacheConfig) } From 5d69dabe3cb385dc6e7113b1c70639fb61f40987 Mon Sep 17 00:00:00 2001 From: mah92 Date: Mon, 17 Feb 2025 00:28:23 +0330 Subject: [PATCH 13/14] corrections --- .../sherpa/onnx/tts/engine/MainActivity.kt | 2 +- .../k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt | 7 +- .../csrc/offline-tts-cache-mechanism.cc | 23 ++++-- sherpa-onnx/csrc/offline-tts-impl.cc | 58 +++++++++++++-- sherpa-onnx/csrc/offline-tts-impl.h | 13 +++- sherpa-onnx/csrc/offline-tts.cc | 72 +++---------------- sherpa-onnx/csrc/offline-tts.h | 10 +-- sherpa-onnx/jni/offline-tts.cc | 54 +++++--------- sherpa-onnx/kotlin-api/Tts.kt | 6 +- 9 files changed, 118 insertions(+), 127 deletions(-) diff --git a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt index 1baf851bc4..f1f12079dd 100644 --- a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt +++ b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/MainActivity.kt @@ -246,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 diff --git a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt index 92f9d0fe7a..de66c0e84b 100644 --- a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt +++ b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt @@ -174,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) { @@ -221,7 +223,10 @@ object TtsEngine { speed = PreferenceHelper(context).getSpeed() speakerId = PreferenceHelper(context).getSid() - tts = OfflineTts(assetManager = assets, config = config, cacheConfig = cacheConfig) + OfflineTtsCacheMechanismConfig config + auto cache = new OfflineTtsCacheMechanism(config) + + tts = OfflineTts(assetManager = assets, config = config, cache = cache) } diff --git a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc index 0d769728c6..a335158253 100644 --- a/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc +++ b/sherpa-onnx/csrc/offline-tts-cache-mechanism.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include // for std::size_t #include "sherpa-onnx/csrc/file-utils.h" @@ -74,8 +75,6 @@ void OfflineTtsCacheMechanism::AddWavFile( bool file_exists = std::filesystem::exists(file_path); if (!file_exists) { // If the file does not exist, add it to the cache - // Ensure the cache does not exceed its size limit - EnsureCacheLimit(); // Write the audio samples to a WAV file bool success = WriteWave(file_path, @@ -86,6 +85,10 @@ void OfflineTtsCacheMechanism::AddWavFile( if (file.is_open()) { used_cache_size_bytes_ += file.tellg(); } + + // Ensure the cache does not exceed its size limit, non-blocking + EnsureCacheLimit(); + } else { SHERPA_ONNX_LOGE("Failed to write wav file: %s", file_path.c_str()); } @@ -290,15 +293,21 @@ void OfflineTtsCacheMechanism::UpdateCacheVector() { } void OfflineTtsCacheMechanism::EnsureCacheLimit() { + std::lock_guard lock(mutex_); // Lock the mutex for the entire function + if (used_cache_size_bytes_ > cache_size_bytes_) { - auto target_cache_size - = std::max(static_cast (cache_size_bytes_*0.95), 0); - while (used_cache_size_bytes_> 0 - && used_cache_size_bytes_ > target_cache_size) { + // Launch a new thread to handle cache cleanup in a non-blocking way + std::thread([this]() { + std::lock_guard lock(mutex_); // Lock the mutex for the cleanup process + + auto target_cache_size = std::max(static_cast(cache_size_bytes_ * 0.95), 0); + while (used_cache_size_bytes_ > 0 + && used_cache_size_bytes_ > target_cache_size) { // Cache is full, remove the least repeated file std::size_t least_repeated_file = GetLeastRepeatedFile(); RemoveWavFile(least_repeated_file); - } + } + }).detach(); // Detach the thread to run independently } } diff --git a/sherpa-onnx/csrc/offline-tts-impl.cc b/sherpa-onnx/csrc/offline-tts-impl.cc index 199b0f7926..31e73fb343 100644 --- a/sherpa-onnx/csrc/offline-tts-impl.cc +++ b/sherpa-onnx/csrc/offline-tts-impl.cc @@ -35,7 +35,8 @@ std::vector OfflineTtsImpl::AddBlank(const std::vector &x, } std::unique_ptr OfflineTtsImpl::Create( - const OfflineTtsConfig &config) { + const OfflineTtsConfig &config, OfflineTtsCacheMechanism* cache) { + cache_ = cache; if (!config.model.vits.model.empty()) { return std::make_unique(config); } else if (!config.model.matcha.acoustic_model.empty()) { @@ -47,7 +48,8 @@ std::unique_ptr OfflineTtsImpl::Create( template std::unique_ptr OfflineTtsImpl::Create( - Manager *mgr, const OfflineTtsConfig &config) { + Manager *mgr, const OfflineTtsConfig &config, OfflineTtsCacheMechanism* cache) { + cache_ = cache; if (!config.model.vits.model.empty()) { return std::make_unique(mgr, config); } else if (!config.model.matcha.acoustic_model.empty()) { @@ -59,12 +61,58 @@ std::unique_ptr OfflineTtsImpl::Create( #if __ANDROID_API__ >= 9 template std::unique_ptr OfflineTtsImpl::Create( - AAssetManager *mgr, const OfflineTtsConfig &config); + AAssetManager *mgr, const OfflineTtsConfig &config, OfflineTtsCacheMechanism* cache); #endif - + #if __OHOS__ template std::unique_ptr OfflineTtsImpl::Create( - NativeResourceManager *mgr, const OfflineTtsConfig &config); + NativeResourceManager *mgr, const OfflineTtsConfig &config, OfflineTtsCacheMechanism* cache); #endif + +GeneratedAudio OfflineTtsImpl::GenerateWitchCache( + const std::string &text, int64_t sid, float speed, + GeneratedAudioCallback callback) const +{ + // Generate a hash for the text + std::hash hasher; + std::size_t text_hash = hasher(text); + + //In phones, long texts come from messages, websites and book which are usually not repeated. Repeated text comes from menus and settings which are usually short + bool text_is_long = text.length() > 50? true: false; + + // Check if the cache mechanism is active and if the audio is already cached + if (cache_ && !text_is_long) { + int32_t sample_rate; + std::vector samples + = cache_->GetWavFile(text_hash, &sample_rate); + + if (!samples.empty()) { + SHERPA_ONNX_LOGE("Returning cached audio for hash: %zu", text_hash); + + // If a callback is provided, call it with the cached audio + if (callback) { + int32_t result + = callback(samples.data(), samples.size(), 1.0f /* progress */); + if (result == 0) { + // If the callback returns 0, stop further processing + SHERPA_ONNX_LOGE("Callback requested to stop processing."); + return {samples, sample_rate}; + } + } + + // Return the cached audio + return {samples, sample_rate}; + } + } + + auto audio = Generate(text, sid, speed, callback); + // Cache the generated audio if the cache mechanism is active + if (cache_ && !text_is_long) { + cache_->AddWavFile(text_hash, audio.samples, audio.sample_rate); + } + + return audio; +} + } // namespace sherpa_onnx diff --git a/sherpa-onnx/csrc/offline-tts-impl.h b/sherpa-onnx/csrc/offline-tts-impl.h index 061acc747c..f7d4847340 100644 --- a/sherpa-onnx/csrc/offline-tts-impl.h +++ b/sherpa-onnx/csrc/offline-tts-impl.h @@ -17,16 +17,23 @@ class OfflineTtsImpl { public: virtual ~OfflineTtsImpl() = default; - static std::unique_ptr Create(const OfflineTtsConfig &config); + static std::unique_ptr Create( + const OfflineTtsConfig &config, + OfflineTtsCacheMechanism* cache = nullptr); template static std::unique_ptr Create(Manager *mgr, - const OfflineTtsConfig &config); + const OfflineTtsConfig &config, + OfflineTtsCacheMechanism* cache = nullptr); virtual GeneratedAudio Generate( const std::string &text, int64_t sid = 0, float speed = 1.0, GeneratedAudioCallback callback = nullptr) const = 0; + GeneratedAudio GenerateWitchCache( + const std::string &text, int64_t sid = 0, float speed = 1.0, + GeneratedAudioCallback callback = nullptr) const; + // Return the sample rate of the generated audio virtual int32_t SampleRate() const = 0; @@ -36,6 +43,8 @@ class OfflineTtsImpl { std::vector AddBlank(const std::vector &x, int32_t blank_id = 0) const; + private: + static OfflineTtsCacheMechanism *cache_; // not owned here }; } // namespace sherpa_onnx diff --git a/sherpa-onnx/csrc/offline-tts.cc b/sherpa-onnx/csrc/offline-tts.cc index c4e745820a..ea4e6ea646 100644 --- a/sherpa-onnx/csrc/offline-tts.cc +++ b/sherpa-onnx/csrc/offline-tts.cc @@ -162,25 +162,14 @@ std::string OfflineTtsConfig::ToString() const { return os.str(); } -OfflineTts::OfflineTts(const OfflineTtsConfig &config) - : impl_(OfflineTtsImpl::Create(config)) {} - OfflineTts::OfflineTts(const OfflineTtsConfig &config, - const OfflineTtsCacheMechanismConfig &cache_config) - : impl_(OfflineTtsImpl::Create(config)) { - cache_mechanism_ = std::make_unique(cache_config); -} - -template -OfflineTts::OfflineTts(Manager *mgr, const OfflineTtsConfig &config) - : impl_(OfflineTtsImpl::Create(mgr, config)) {} + OfflineTtsCacheMechanism *cache) + : impl_(OfflineTtsImpl::Create(config, cache)) {} template OfflineTts::OfflineTts(Manager *mgr, const OfflineTtsConfig &config, - const OfflineTtsCacheMechanismConfig &cache_config) - : impl_(OfflineTtsImpl::Create(mgr, config)) { - cache_mechanism_ = std::make_unique(cache_config); -} + OfflineTtsCacheMechanism *cache) + : impl_(OfflineTtsImpl::Create(mgr, config, cache)) {} OfflineTts::~OfflineTts() = default; @@ -188,42 +177,12 @@ GeneratedAudio OfflineTts::Generate( const std::string &text, int64_t sid /*=0*/, float speed /*= 1.0*/, GeneratedAudioCallback callback /*= nullptr*/) const { - // Generate a hash for the text - std::hash hasher; - std::size_t text_hash = hasher(text); - GeneratedAudio audio; - - // Check if the cache mechanism is active and if the audio is already cached - if (cache_mechanism_) { - int32_t sample_rate; - std::vector samples - = cache_mechanism_->GetWavFile(text_hash, &sample_rate); - - if (!samples.empty()) { - SHERPA_ONNX_LOGE("Returning cached audio for hash: %zu", text_hash); - - // If a callback is provided, call it with the cached audio - if (callback) { - int32_t result - = callback(samples.data(), samples.size(), 1.0f /* progress */); - if (result == 0) { - // If the callback returns 0, stop further processing - SHERPA_ONNX_LOGE("Callback requested to stop processing."); - return {samples, sample_rate}; - } - } - - // Return the cached audio - return {samples, sample_rate}; - } - } - // Generate the audio if not cached #if !defined(_WIN32) - audio = impl_->Generate(text, sid, speed, std::move(callback)); + return impl_->GenerateWitchCache(text, sid, speed, std::move(callback)); #else if (IsUtf8(text)) { - audio = impl_->Generate(text, sid, speed, std::move(callback)); + return impl_->GenerateWitchCache(text, sid, speed, std::move(callback)); } else if (IsGB2312(text)) { auto utf8_text = Gb2312ToUtf8(text); static bool printed = false; @@ -232,21 +191,14 @@ GeneratedAudio OfflineTts::Generate( "Detected GB2312 encoded string! Converting it to UTF8."); printed = true; } - audio = impl_->Generate(utf8_text, sid, speed, std::move(callback)); + return impl_->GenerateWitchCache(utf8_text, sid, speed, std::move(callback)); } else { SHERPA_ONNX_LOGE( "Non UTF8 encoded string is received. You would not get expected " "results!"); - audio = impl_->Generate(text, sid, speed, std::move(callback)); + return impl_->GenerateWitchCache(text, sid, speed, std::move(callback)); } #endif - - // Cache the generated audio if the cache mechanism is active - if (cache_mechanism_) { - cache_mechanism_->AddWavFile(text_hash, audio.samples, audio.sample_rate); - } - - return audio; } int32_t OfflineTts::SampleRate() const { return impl_->SampleRate(); } @@ -254,19 +206,15 @@ int32_t OfflineTts::SampleRate() const { return impl_->SampleRate(); } int32_t OfflineTts::NumSpeakers() const { return impl_->NumSpeakers(); } #if __ANDROID_API__ >= 9 -template OfflineTts::OfflineTts(AAssetManager *mgr, - const OfflineTtsConfig &config); template OfflineTts::OfflineTts(AAssetManager *mgr, const OfflineTtsConfig &config, - const OfflineTtsCacheMechanismConfig &cache_config); + OfflineTtsCacheMechanism *cache = nullptr); #endif #if __OHOS__ -template OfflineTts::OfflineTts(NativeResourceManager *mgr, - const OfflineTtsConfig &config); template OfflineTts::OfflineTts(NativeResourceManager *mgr, const OfflineTtsConfig &config, - const OfflineTtsCacheMechanismConfig &cache_config); + OfflineTtsCacheMechanism *cache = nullptr); #endif } // namespace sherpa_onnx diff --git a/sherpa-onnx/csrc/offline-tts.h b/sherpa-onnx/csrc/offline-tts.h index 99b08957d5..83f9b16be2 100644 --- a/sherpa-onnx/csrc/offline-tts.h +++ b/sherpa-onnx/csrc/offline-tts.h @@ -75,16 +75,12 @@ using GeneratedAudioCallback = std::function - OfflineTts(Manager *mgr, const OfflineTtsConfig &config); + OfflineTtsCacheMechanism *cache = nullptr); template OfflineTts(Manager *mgr, const OfflineTtsConfig &config, - const OfflineTtsCacheMechanismConfig &cache_config); + OfflineTtsCacheMechanism *cache = nullptr); // @param text A string containing words separated by spaces // @param sid Speaker ID. Used only for multi-speaker models, e.g., models @@ -110,8 +106,6 @@ class OfflineTts { // If it supports only a single speaker, then it return 0 or 1. int32_t NumSpeakers() const; - std::unique_ptr cache_mechanism_; // not owned here - private: std::unique_ptr impl_; }; diff --git a/sherpa-onnx/jni/offline-tts.cc b/sherpa-onnx/jni/offline-tts.cc index 2fe0c716ad..1d6a29a8ac 100644 --- a/sherpa-onnx/jni/offline-tts.cc +++ b/sherpa-onnx/jni/offline-tts.cc @@ -227,15 +227,12 @@ JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_newFromAsset( auto config = sherpa_onnx::GetOfflineTtsConfig(env, _config); SHERPA_ONNX_LOGE("config:\n%s", config.ToString().c_str()); - auto cache_config = sherpa_onnx::GetOfflineTtsCacheConfig(env, _cache_config); - SHERPA_ONNX_LOGE("cache config:\n%s", cache_config.ToString().c_str()); - auto tts = new sherpa_onnx::OfflineTts( #if __ANDROID_API__ >= 9 mgr, #endif config, - cache_config); + cache); return (jlong)tts; } @@ -246,14 +243,7 @@ JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_newFromFile( auto config = sherpa_onnx::GetOfflineTtsConfig(env, _config); SHERPA_ONNX_LOGE("config:\n%s", config.ToString().c_str()); - auto cache_config = sherpa_onnx::GetOfflineTtsCacheConfig(env, _cache_config); - SHERPA_ONNX_LOGE("cache config:\n%s", cache_config.ToString().c_str()); - - if (!cache_config.Validate()) { - SHERPA_ONNX_LOGE("Errors found in cache_config!"); - } - - auto tts = new sherpa_onnx::OfflineTts(config, cache_config); + auto tts = new sherpa_onnx::OfflineTts(config, cache); return (jlong)tts; } @@ -266,23 +256,17 @@ JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_delete( SHERPA_ONNX_EXTERN_C JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_setCacheSizeImpl( - JNIEnv * /*env*/, jobject /*obj*/, jlong ptr, jint cacheSize) { - auto tts = reinterpret_cast(ptr); - if (tts) { - if(tts->cache_mechanism_) { - tts->cache_mechanism_->SetCacheSize(static_cast(cacheSize)); - } + JNIEnv * /*env*/, jobject /*obj*/, jlong ptr, jint cacheSize) { + if(cache_) { + cache_->SetCacheSize(static_cast(cacheSize)); } } SHERPA_ONNX_EXTERN_C JNIEXPORT jint JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getCacheSizeImpl( - JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { - auto tts = reinterpret_cast(ptr); - if (tts) { - if(tts->cache_mechanism_) { - return tts->cache_mechanism_->GetCacheSize(); - } + JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { + if(cache_) { + return cache_->GetCacheSize(); } return 0; @@ -303,11 +287,8 @@ JNIEXPORT jint JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getNumSpeakers( SHERPA_ONNX_EXTERN_C JNIEXPORT jint JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getTotalUsedCacheSizeImpl( JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { - auto tts = reinterpret_cast(ptr); - if (tts) { - if(tts->cache_mechanism_) { - return tts->cache_mechanism_->GetTotalUsedCacheSize(); - } + if(cache_) { + return cache_->GetTotalUsedCacheSize(); } return 0; @@ -316,15 +297,12 @@ JNIEXPORT jint JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_getTotalUsedCacheSi SHERPA_ONNX_EXTERN_C JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_clearCacheImpl( - JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { - auto tts = reinterpret_cast(ptr); - if (tts) { - if(tts->cache_mechanism_) { - tts->cache_mechanism_->ClearCache(); - static int times = 0; - times++; - SHERPA_ONNX_LOGE("Cache cleared from JNI for %ith time\n", times); - } + JNIEnv * /*env*/, jobject /*obj*/, jlong ptr) { + if(cache_) { + cache_->ClearCache(); + static int times = 0; + times++; + SHERPA_ONNX_LOGE("Cache cleared from JNI for %ith time\n", times); } } diff --git a/sherpa-onnx/kotlin-api/Tts.kt b/sherpa-onnx/kotlin-api/Tts.kt index 27ad95dddb..db3381903d 100644 --- a/sherpa-onnx/kotlin-api/Tts.kt +++ b/sherpa-onnx/kotlin-api/Tts.kt @@ -74,15 +74,15 @@ class GeneratedAudio( class OfflineTts( assetManager: AssetManager? = null, var config: OfflineTtsConfig, - var cacheConfig: OfflineTtsCacheMechanismConfig, + var cache: OfflineTtsCacheMechanism, ) { private var ptr: Long init { ptr = if (assetManager != null) { - newFromAsset(assetManager, config, cacheConfig) + newFromAsset(assetManager, config, cache) } else { - newFromFile(config, cacheConfig) + newFromFile(config, cache) } } From c76b5c192c8769791d1200651cbdfeec497abc5d Mon Sep 17 00:00:00 2001 From: mah92 Date: Mon, 17 Feb 2025 07:27:23 +0330 Subject: [PATCH 14/14] corrections2 - still not compiles --- .../app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt | 6 ++++-- .../main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/android/SherpaOnnxTts/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt b/android/SherpaOnnxTts/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt index 2e0baec308..94973a15de 100644 --- a/android/SherpaOnnxTts/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt +++ b/android/SherpaOnnxTts/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt @@ -319,10 +319,12 @@ class MainActivity : AppCompatActivity() { val cacheConfig = getOfflineTtsCacheMechanismConfig( dataDir = dataDir ?: "", - cacheSize = 20*1024*1024, // Fixed to 20 MBs + cacheSize = 20*1024*1024, // Default is 20 MBs )!! - tts = OfflineTts(assetManager = assets, config = config, cacheConfig = cacheConfig) + val cache = new OfflineTtsCacheMechanism(cacheConfig) + + tts = OfflineTts(assetManager = assets, config = config, cache = cache) } diff --git a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt index de66c0e84b..6c5419703b 100644 --- a/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt +++ b/android/SherpaOnnxTtsEngine/app/src/main/java/com/k2fsa/sherpa/onnx/tts/engine/TtsEngine.kt @@ -223,8 +223,7 @@ object TtsEngine { speed = PreferenceHelper(context).getSpeed() speakerId = PreferenceHelper(context).getSid() - OfflineTtsCacheMechanismConfig config - auto cache = new OfflineTtsCacheMechanism(config) + val cache = new OfflineTtsCacheMechanism(cacheConfig) tts = OfflineTts(assetManager = assets, config = config, cache = cache) }