using System.Linq;
using UnityEngine;
namespace Crosstales.RTVoice
{
/// Global cache for wrappers.
[ExecuteInEditMode]
[DisallowMultipleComponent]
[HelpURL("https://crosstales.com/media/data/assets/rtvoice/api/class_crosstales_1_1_r_t_voice_1_1_global_cache.html")]
public class GlobalCache : Crosstales.Common.Util.Singleton
{
#region Variables
[UnityEngine.Serialization.FormerlySerializedAsAttribute("ClipCacheSize")] [Header("Cache Settings"), Tooltip("Size of the clip cache in MB (default: 256)"), Range(16, 1024), SerializeField]
private int clipCacheSize = Crosstales.RTVoice.Util.Constants.DEFAULT_CACHE_SIZE_CLIPS;
[Tooltip("Automatically loads and saves the cache (default: false)"), SerializeField] private bool persistCache;
///Dictionary with all cached clips.
public readonly System.Collections.Generic.Dictionary Clips = new System.Collections.Generic.Dictionary();
private readonly System.Collections.Generic.List clipKeys = new System.Collections.Generic.List();
private Transform tf;
private static string dataStorePath;
#endregion
#region Properties
///Size of the clip cache in Bytes.
public int ClipCacheSize
{
get => clipCacheSize * Crosstales.RTVoice.Util.Constants.FACTOR_MB;
set => clipCacheSize = Mathf.Clamp(value / Crosstales.RTVoice.Util.Constants.FACTOR_MB, 1, Crosstales.RTVoice.Util.Constants.DEFAULT_MAX_CACHE_SIZE_CLIPS);
}
/// Current size of the clip cache in Bytes.
public int CurrentClipCacheSize => Clips.Sum(pair => pair.Value.samples * 2 * 4);
/// Automatically loads and saves the cache.
public bool PersistCache
{
get => persistCache;
set => persistCache = value;
}
#endregion
#region MonoBehaviour methods
private void Start()
{
dataStorePath = $"{Application.persistentDataPath}/rtvoice_datastore.xml";
if (persistCache)
LoadCache();
}
private void OnValidate()
{
if (clipCacheSize <= 16)
{
clipCacheSize = 16;
}
else if (clipCacheSize > Crosstales.RTVoice.Util.Constants.DEFAULT_MAX_CACHE_SIZE_CLIPS)
{
clipCacheSize = Crosstales.RTVoice.Util.Constants.DEFAULT_MAX_CACHE_SIZE_CLIPS;
}
}
protected override void OnApplicationQuit()
{
if (persistCache)
SaveCache();
ClearCache();
base.OnApplicationQuit();
}
#endregion
#region Public methods
/// Resets this object.
//[RuntimeInitializeOnLoadMethod]
public static void ResetObject()
{
DeleteInstance();
}
/// Returns the AudioClip for a given key.
/// Key for the AudioClip.
/// AudioClip for the given key.
public AudioClip GetClip(Crosstales.RTVoice.Model.Wrapper key)
{
if (key != null)
{
Clips.TryGetValue(key, out AudioClip data);
//Debug.LogWarning("DATA: " + data);
return data;
}
return null;
}
/// Removes an AudioClip for a given key.
/// Key for the AudioClip.
public void RemoveClip(Crosstales.RTVoice.Model.Wrapper key)
{
if (key != null && Clips.ContainsKey(key))
{
Destroy(Clips[key]);
Clips.Remove(key);
clipKeys.Remove(key);
}
}
/// Adds an AudioClip for a given key.
/// Key for the AudioClip.
/// AudioClip for the key.
public void AddClip(Crosstales.RTVoice.Model.Wrapper key, AudioClip data)
{
if (key != null && data != null && !Clips.ContainsKey(key))
{
while (CurrentClipCacheSize >= ClipCacheSize)
{
RemoveClip(clipKeys[0]);
}
Clips.Add(key, data);
clipKeys.Add(key);
}
}
/// Clears the clips cache.
public void ClearClipCache()
{
Crosstales.RTVoice.Util.Context.NumberOfCachedSpeeches = 0;
Crosstales.RTVoice.Util.Context.NumberOfNonCachedSpeeches = 0;
foreach (System.Collections.Generic.KeyValuePair kvp in Clips)
{
if (Crosstales.RTVoice.Util.Helper.isEditorMode)
{
DestroyImmediate(kvp.Value);
}
else
{
Destroy(kvp.Value);
}
}
Clips.Clear();
clipKeys.Clear();
}
/// Clears the complete cache.
public void ClearCache()
{
ClearClipCache();
}
/// Clears and deletes the complete cache.
public void ClearAndDeleteCache()
{
ClearCache();
if (System.IO.File.Exists(dataStorePath))
System.IO.File.Delete(dataStorePath);
}
/// Saves the complete cache.
public void SaveCache()
{
System.Collections.Generic.List data = Clips.Select(kvp => new DataStore(kvp.Key, Crosstales.Common.Audio.WavMaster.FromAudioClip(kvp.Value))).ToList();
Crosstales.Common.Util.XmlHelper.SerializeToFile(data, dataStorePath);
//Debug.Log("SaveCache: " + data.Count);
}
/// Loads the complete cache.
public void LoadCache()
{
if (System.IO.File.Exists(dataStorePath))
{
System.Collections.Generic.List data = Crosstales.Common.Util.XmlHelper.DeserializeFromFile>(dataStorePath);
if (data != null)
{
foreach (DataStore ds in data)
{
AddClip(ds.wrapper, Crosstales.Common.Audio.WavMaster.ToAudioClip(ds.Data));
}
}
}
//Debug.Log("LoadCache: " + Clips.Count);
}
#endregion
}
/// Model for a voice.
[System.Serializable]
public class DataStore
{
public Crosstales.RTVoice.Model.Wrapper wrapper;
public byte[] Data;
/// Default.
public DataStore()
{
}
/// Instantiate the class.
/// Wrapper of the speech.
/// Data of the speech.
public DataStore(Crosstales.RTVoice.Model.Wrapper wrapper, byte[] data)
{
this.wrapper = wrapper;
Data = data;
}
}
}
// © 2020-2023 crosstales LLC (https://www.crosstales.com)