From 7d3571289a1cbc518049b1a25d87924da4860d5b Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 17 Jan 2025 19:14:01 +0330 Subject: [PATCH 1/8] 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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 8/8] 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 {