s&box Package Code Search

Search C# source code, UI razor templates, shaders, and configs across s&box packages.

Showing code results for query: * (7 total matches found)
carsonk.tiktoktts / Code/TikTokTTS.cs
Game library
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Sandbox;

namespace TikTokTTS;

/// <summary>
/// A static class that allows you to use the TikTokTTS API.
/// </summary>
public static class TikTokTTS
{
    /// <summary>
    /// The API endpoint that the library uses.
    /// </summary>
    public static string ENDPOINT = "https://tiktok-tts.weilnet.workers.dev";

    static int currentId = 0;

    /// <summary>
    /// Use TikTok's TTS to speak via a MusicPlayer. This MusicPlayer will automatically dispose itself when it finishes playing.
    /// </summary>
    /// <param name="text">The text to be said by the TTS</param>
    /// <param name="voice">The name of the voice</param>
    /// <returns></returns>
    public static async Task<MusicPlayer> Say( string text, string voice = "en_us_007" )
    {
        string fileName = $"tts-{currentId}.mp3";
        currentId++;

        if ( !FileSystem.Data.FileExists( fileName ) )
        {
            await Download( text, voice, fileName, FileSystem.Data );
        }

        var musicPlayer = MusicPlayer.Play( FileSystem.Data, fileName );
        musicPlayer.ListenLocal = true;

        return musicPlayer;
    }

    /// <summary>
    /// Use TikTok's TTS to speak and download/save the mp3 file.
    /// </summary>
    /// <param name="text">The text to be said by the TTS</param>
    /// <param name="voice">The name of the voice</param>
    /// <param name="fileName">The file name/path it should download to (defaults to "tts-##########.mp3"</param>
    /// <param name="fileSystem">The filesystem to use (defaults to FileSystem.Data)</param>
    /// <returns>The file path</returns>
    public static async Task<string> Download( string text, string voice = "en_us_007", string fileName = null, BaseFileSystem fileSystem = null )
    {
        if ( fileSystem == null ) fileSystem = FileSystem.Data;
        if ( string.IsNullOrEmpty( fileName ) ) fileName = $"tts-{new Guid()}.mp3";

        var content = new Dictionary<string, string> {
            { "text", text },
            { "voice", voice }
        };
        var headers = new Dictionary<string, string> {
            { "Content-Type", "application/json" }
        };

        text = text.RemoveBadCharacters();
        text = text.Substring( 0, Math.Min( text.Length, 300 ) );
        if ( !text.Any( x => char.IsLetterOrDigit( x ) ) ) return null;
        if ( string.IsNullOrEmpty( text ) ) return null;

        // Make sure folders exist
        var folders = fileName.Split( '/' );
        if ( folders.Length == 1 ) folders = fileName.Split( '\\' );
        if ( folders.Length > 1 )
        {
            string path = "";
            for ( int i = 0; i < folders.Length - 1; i++ )
            {
                path += folders[i] + "/";
                if ( !fileSystem.DirectoryExists( path ) )
                    fileSystem.CreateDirectory( path );
            }
        }

        try
        {
            if ( fileSystem.FileExists( fileName ) )
                fileSystem.DeleteFile( fileName );
            var response = await Http.RequestJsonAsync<TikTokTTSResponse>( "https://tiktok-tts.weilnet.workers.dev/api/generation", "POST", Http.CreateJsonContent( content ), headers );
            string base64 = response.data;
            byte[] mp3 = Convert.FromBase64String( base64 );
            var stream = fileSystem.OpenWrite( fileName );
            stream.Write( mp3, 0, mp3.Length );
            stream.Close();
            return fileName;
        }
        catch ( Exception e )
        {
            Log.Error( e.Message );
            return null;
        }
    }

    class TikTokTTSResponse
    {
        public bool success { get; set; }
        public string data { get; set; }
        public string error { get; set; }
    }
}
carsonk.tiktoktts / UnitTests/LibraryTest.cs
UnitTest library
using Sandbox;

public partial class LibraryTests
{
	[TestMethod]
	public void SceneTest()
	{
		var scene = new Scene();
		using ( scene.Push() )
		{
			var go = new GameObject();

			Assert.AreEqual( 1, scene.Directory.GameObjectCount );
		}
	}

}
carsonk.tiktoktts / Editor/TikTokSpeechControlWidget.cs
Editor library
using Editor;
using Sandbox;
using System.Linq;

namespace TikTokTTS.Editor;

[CustomEditor( typeof( TikTokSpeech ) )]
public class TikTokSpeechComponentEditor : ComponentEditorWidget
{

	SerializedObject Target;

	public TikTokSpeechComponentEditor( SerializedObject obj ) : base( obj )
	{
		Target = obj;
		Layout = Layout.Column();
		var sheet = new PropertyControlSheet();
		sheet.AddObject( obj );
		Layout.Add( sheet );
		Layout.Spacing = 2;
		MinimumHeight = 70;

		var buttonPanel = Layout.Column();
		buttonPanel.Margin = new Sandbox.UI.Margin( 12, 0 );
		var button = new Button( "Speak", "play_arrow" )
		{
			Width = 200,
			Clicked = () =>
			{
				var component = Target.Targets.First() as TikTokSpeech;
				component?.Speak();
			}
		};
		buttonPanel.Add( button );
		Layout.Add( buttonPanel );
	}

	protected override void OnPaint()
	{
	}

}
carsonk.tiktoktts / Code/TikTokVoices.cs
Game library
using Sandbox;

namespace TikTokTTS;

public enum TikTokVoice
{
    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Female - Jessie (en_us_002)" )] en_us_002,
    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Female - Empathetic (en_female_samc)" )] en_female_samc,
    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Female - Beauty Guru (en_female_makeup)" )] en_female_makeup,
    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Female - Bestie (en_female_richgirl)" )] en_female_richgirl,

    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Male - Joey (en_us_006)" )] en_us_006,
    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Male - Professor (en_us_007)" )] en_us_007,
    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Male - Scientist (en_us_009)" )] en_us_009,
    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Male - Confidence (en_us_010)" )] en_us_010,
    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Male - Serious (en_male_cody)" )] en_male_cody,
    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Male - Wacky (en_male_funny)" )] en_male_funny,
    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Male - Story Teller (en_male_narration)" )] en_male_narration,
    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Male - Game On (en_male_jomboy)" )] en_male_jomboy,
    [Icon( "๐Ÿ‡บ๐Ÿ‡ธ" ), Title( "US Male - Alfred (en_male_jarvis)" )] en_male_jarvis,

    [Icon( "๐Ÿ‡ฌ๐Ÿ‡ง" ), Title( "UK Male - Narrator (en_uk_001)" )] en_uk_001,
    [Icon( "๐Ÿ‡ฌ๐Ÿ‡ง" ), Title( "UK Male - English (en_uk_003)" )] en_uk_003,
    [Icon( "๐Ÿ‡ฌ๐Ÿ‡ง" ), Title( "UK Male - Olantekkers (en_male_olantekkers)" )] en_male_olantekkers,

    [Icon( "๐Ÿ‡ฆ๐Ÿ‡บ" ), Title( "AU Female - Metro (en_au_001)" )] en_au_001,
    [Icon( "๐Ÿ‡ฆ๐Ÿ‡บ" ), Title( "AU Female - Peaceful (en_female_emotional)" )] en_female_emotional,
    [Icon( "๐Ÿ‡ฆ๐Ÿ‡บ" ), Title( "AU Male - Smooth (en_au_002)" )] en_au_002,
    [Icon( "๐Ÿ‡ฆ๐Ÿ‡บ" ), Title( "AU Male - Ashmagic (en_male_ashmagic)" )] en_male_ashmagic,

    [Icon( "๐Ÿ’˜" ), Title( "Festive - Cupid (en_male_cupid)" )] en_male_cupid,
    [Icon( "๐ŸŽ…" ), Title( "Festive - Narration (en_male_santa_narration)" )] en_male_santa_narration,
    [Icon( "๐ŸŽ…" ), Title( "Festive - Santa (en_male_santa_effect)" )] en_male_santa_effect,

    [Icon( "๐Ÿ˜ฑ" ), Title( "Disney - Scream (en_us_ghostface)" )] en_us_ghostface,
    [Icon( "๐Ÿถ" ), Title( "Disney - Chewbacca (en_us_chewbacca)" )] en_us_chewbacca,
    [Icon( "๐Ÿค–" ), Title( "Disney - C3PO (en_us_c3po)" )] en_us_c3po,
    [Icon( "๐Ÿฅถ" ), Title( "Disney - Stitch (en_us_stitch)" )] en_us_stitch,
    [Icon( "๐Ÿช–" ), Title( "Disney - Stormtrooper (en_us_stormtrooper)" )] en_us_stormtrooper,
    [Icon( "๐Ÿš€" ), Title( "Disney - Rocket (en_us_rocket)" )] en_us_rocket,
    [Icon( "๐Ÿ‘ฉโ€๐Ÿฆณ" ), Title( "Disney - Madame Leota (en_female_madam_leota)" )] en_female_madam_leota,
    [Icon( "๐Ÿ‘ป" ), Title( "Disney - Ghost Host (en_male_ghosthost)" )] en_male_ghosthost,
    [Icon( "โ˜ ๏ธ" ), Title( "Disney - Pirate (en_male_pirate)" )] en_male_pirate,

    [Icon( "๐Ÿงฝ" ), Title( "Silly - Trickster/Spongebob (en_male_grinch)" )] en_male_grinch,
    [Icon( "๐Ÿง”" ), Title( "Silly - Mr. GoodGuy (en_male_deadpool)" )] en_male_deadpool,
    [Icon( "๐Ÿ™" ), Title( "Silly - Lord Cringe (en_male_ukneighbor)" )] en_male_ukneighbor,
    [Icon( "๐Ÿคต" ), Title( "Silly - Mr. Meticulous (en_male_ukbutler)" )] en_male_ukbutler,
    [Icon( "๐Ÿฅ‚" ), Title( "Silly - Debutante (en_female_shenna)" )] en_female_shenna,
    [Icon( "๐Ÿคช" ), Title( "Silly - Varsity (en_female_pansino)" )] en_female_pansino,
    [Icon( "๐Ÿ‘จโ€๐Ÿ”ฌ" ), Title( "Silly - Marty (en_male_trevor)" )] en_male_trevor,
    [Icon( "๐Ÿ™…" ), Title( "Silly - Bae (en_female_betty)" )] en_female_betty,
    [Icon( "๐Ÿ‘ต" ), Title( "Silly - Granny (en_female_grandma)" )] en_female_grandma,
    [Icon( "๐Ÿช„" ), Title( "Silly - Magician (en_male_wizard)" )] en_male_wizard,

    [Icon( "๐ŸŽถ" ), Title( "Music - Alto/Cottagecore (en_female_f08_salut_damour)" )] en_female_f08_salut_damour,
    [Icon( "๐ŸŽถ" ), Title( "Music - Tenor/Jingle (en_male_m03_lobby)" )] en_male_m03_lobby,
    [Icon( "๐ŸŽถ" ), Title( "Music - Sunshine Soon/Toon Beat (en_male_m03_sunshine_soon)" )] en_male_m03_sunshine_soon,
    [Icon( "๐ŸŽถ" ), Title( "Music - Open Mic (en_female_f08_warmy_breeze)" )] en_female_f08_warmy_breeze,
    [Icon( "๐ŸŽถ" ), Title( "Music - Euphoric (en_female_ht_f08_glorious)" )] en_female_ht_f08_glorious,
    [Icon( "๐ŸŽถ" ), Title( "Music - It Goes Up/Hypetrain (en_male_sing_funny_it_goes_up)" )] en_male_sing_funny_it_goes_up,
    [Icon( "๐ŸŽถ" ), Title( "Music - Chipmunk/Quirky Time (en_male_m2_xhxs_m03_silly)" )] en_male_m2_xhxs_m03_silly,
    [Icon( "๐ŸŽถ" ), Title( "Music - Melodrama (en_female_ht_f08_wonderful_world)" )] en_female_ht_f08_wonderful_world,

    [Icon( "๐ŸŽถ" ), Title( "Parody - Pop Lullaby (en_female_f08_twinkle)" )] en_female_f08_twinkle,
    [Icon( "๐ŸŽถ" ), Title( "Parody - Classic Electric (en_male_m03_classical)" )] en_male_m03_classical,
    [Icon( "๐ŸŽถ" ), Title( "Parody - Cozy (en_male_m2_xhxs_m03_christmas)" )] en_male_m2_xhxs_m03_christmas,
    [Icon( "๐ŸŽถ" ), Title( "Parody - Caroler (en_male_sing_deep_jingle)" )] en_male_sing_deep_jingle,
    [Icon( "๐ŸŽถ" ), Title( "Parody - NYE 2023 (en_female_ht_f08_newyear)" )] en_female_ht_f08_newyear,
    [Icon( "๐ŸŽถ" ), Title( "Parody - Opera (en_female_ht_f08_halloween)" )] en_female_ht_f08_halloween,
    [Icon( "๐ŸŽถ" ), Title( "Parody - Thanksgiving (en_male_sing_funny_thanksgiving)" )] en_male_sing_funny_thanksgiving,

    [Icon( "๐Ÿ‡ซ๐Ÿ‡ท" ), Title( "French - Male 1 (fr_001)" )] fr_001,
    [Icon( "๐Ÿ‡ซ๐Ÿ‡ท" ), Title( "French - Male 2 (fr_002)" )] fr_002,

    [Icon( "๐Ÿ‡ช๐Ÿ‡ธ" ), Title( "Spanish - Spain Male (es_002)" )] es_002,

    [Icon( "๐Ÿ‡ฒ๐Ÿ‡ฝ" ), Title( "Spanish - MX Male (es_mx_002)" )] es_mx_002,

    [Icon( "๐Ÿ‡ง๐Ÿ‡ท" ), Title( "Portuguese BR - Female 1 (br_001)" )] br_001,
    [Icon( "๐Ÿ‡ง๐Ÿ‡ท" ), Title( "Portuguese BR - Female 2 (br_003)" )] br_003,
    [Icon( "๐Ÿ‡ง๐Ÿ‡ท" ), Title( "Portuguese BR - Female 3 (br_004)" )] br_004,
    [Icon( "๐Ÿ‡ง๐Ÿ‡ท" ), Title( "Portuguese BR - Male (br_005)" )] br_005,

    [Icon( "๐Ÿ‡ฉ๐Ÿ‡ช" ), Title( "German - Female (de_001)" )] de_001,
    [Icon( "๐Ÿ‡ฉ๐Ÿ‡ช" ), Title( "German - Male (de_002)" )] de_002,

    [Icon( "๐Ÿ‡ฎ๐Ÿ‡ฉ" ), Title( "Indonesian - Female (id_001)" )] id_001,

    [Icon( "๐Ÿ‡ฏ๐Ÿ‡ต" ), Title( "Japanese - Female 1 (jp_001)" )] jp_001,
    [Icon( "๐Ÿ‡ฏ๐Ÿ‡ต" ), Title( "Japanese - Female 2 (jp_003)" )] jp_003,
    [Icon( "๐Ÿ‡ฏ๐Ÿ‡ต" ), Title( "Japanese - Female 3 (jp_005)" )] jp_005,
    [Icon( "๐Ÿ‡ฏ๐Ÿ‡ต" ), Title( "Japanese - Male (jp_006)" )] jp_006,

    [Icon( "๐Ÿ‡ฐ๐Ÿ‡ท" ), Title( "Korean - Female 1 (kr_003)" )] kr_003,
    [Icon( "๐Ÿ‡ฐ๐Ÿ‡ท" ), Title( "Korean - Male 1 (kr_002)" )] kr_002,
    [Icon( "๐Ÿ‡ฐ๐Ÿ‡ท" ), Title( "Korean - Male 2 (kr_004)" )] kr_004,

    [Icon( "๐Ÿ‡ป๐Ÿ‡ณ" ), Title( "Vietnamese - Female (BV074_streaming)" )] BV074_streaming,
    [Icon( "๐Ÿ‡ป๐Ÿ‡ณ" ), Title( "Vietnamese - Male (BV075_streaming)" )] BV075_streaming,



}
carsonk.tiktoktts / Editor/PropertyControlSheet.cs
Editor library
using System;
using Editor;
using Sandbox;

namespace TikTokTTS.Editor;

public class PropertyControlSheet : GridLayout
{
	public SerializedObject TargetObject { get; set; }

	int rows = 0;

	public PropertyControlSheet() : base()
	{
		Margin = new Sandbox.UI.Margin( 16, 8, 16, 8 );
		HorizontalSpacing = 10;
		VerticalSpacing = 2;
		SetColumnStretch( 1, 2 );
		SetMinimumColumnWidth( 0, 120 );
	}

	public void AddObject( SerializedObject obj )
	{
		foreach ( var entry in obj )
		{
			if ( !entry.HasAttribute<PropertyAttribute>() )
			{
				continue;
			}
			if ( entry.PropertyType.Name.StartsWith( "Action" ) )
			{
				continue;
			}

			AddRow( entry );
		}
	}

	/// <summary>
	/// Add a serialized property row. This will create an editor for the row and a label.
	/// </summary>
	public void AddRow( SerializedProperty property, float labelIndent = 0.0f )
	{
		var editor = ControlWidget.Create( property );
		if ( editor is null )
			return;

		if ( editor.IsWideMode )
		{
			AddCell( 0, rows++, new Label( property.DisplayName ) { MinimumHeight = Theme.RowHeight, Alignment = TextFlag.LeftCenter }, 2, 1, TextFlag.LeftTop );
			var lo = AddCell( 0, rows, Layout.Column(), 2, 1, TextFlag.LeftTop );

			lo.Margin = new Sandbox.UI.Margin( 16, 0, 0, 0 );
			lo.Add( editor );
		}
		else
		{
			int cell = 0;

			var label = AddCell( cell++, rows, new Label( property.DisplayName ), 1, 1, TextFlag.LeftTop );
			label.MinimumHeight = Theme.RowHeight;
			label.Alignment = TextFlag.LeftCenter;
			label.SetStyles( "color: #aaa;" );

			label.ToolTip = property.Description ?? property.DisplayName;

			if ( labelIndent > 0 )
			{
				label.ContentMargins = new Sandbox.UI.Margin( labelIndent, 0, 0, 0 );
			}

			AddCell( cell++, rows, editor, 1, 1, TextFlag.LeftTop );
		}

		rows++;
	}

	/// <summary>
	/// Add a layout to a double wide cell
	/// </summary>
	public void AddLayout( Layout layout )
	{
		AddCell( 0, rows++, layout, 2, 1, TextFlag.LeftTop );
	}

}
carsonk.tiktoktts / Code/TikTokSpeech.cs
Game library
using System;
using Sandbox;

namespace TikTokTTS;

public class TikTokSpeech : Component
{

    /// <summary>
    /// The text that will be spoken by the TTS.
    /// </summary>
    [Property] public string Text { get; set; } = "Hello World!";

    /// <summary>
    /// The voice that will be used to speak the text.
    /// </summary>
    [Property] public TikTokVoice Voice { get; set; } = TikTokVoice.en_us_002;

    /// <summary>
    /// Whether or not the audio should be positional (based on the position of the GameObject that this component is attached to)
    /// </summary>
    [Property] public bool PositionalAudio { get; set; } = false;

    /// <summary>
    /// Whether or not to cache the files that are generated by the TTS. When enabled, the files will be stored in the data directory and referenced if the same call is made again.
    /// </summary>
    [Property] public bool CacheFiles { get; set; } = true;

    /// <summary>
    /// An event that is fired when the TTS has started speaking.
    /// </summary>
    [Property] public Action<string, TikTokVoice> OnSpeak { get; set; }

    private string VoiceName => Enum.GetName( typeof( TikTokVoice ), Voice );
    private string FileName => $"tts/tts-{Text.ToLowerInvariant().UrlEncode()}-{VoiceName}.mp3";
    private MusicPlayer MusicPlayer;

    /// <summary>
    /// Speak the current text using the TTS.
    /// </summary>
    public async void Speak()
    {
        MusicPlayer?.Dispose();

        if ( CacheFiles )
        {
            if ( !FileSystem.Data.FileExists( FileName ) )
            {
                await TikTokTTS.Download( Text, VoiceName, FileName, FileSystem.Data );
            }

            MusicPlayer = MusicPlayer.Play( FileSystem.Data, FileName );
        }
        else
        {
            MusicPlayer = await TikTokTTS.Say( Text, VoiceName );
            if ( MusicPlayer is null )
            {
                Log.Error( "Failed to speak" );
                return;
            }
        }

        OnSpeak?.Invoke( Text, Voice );

        MusicPlayer.ListenLocal = !PositionalAudio;
    }

    protected override void OnUpdate()
    {
        if ( MusicPlayer is not null )
        {
            MusicPlayer.Position = Transform.Position;
        }
    }

}
carsonk.tiktoktts / UnitTests/UnitTest.cs
UnitTest library
global using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class TestInit
{
	[AssemblyInitialize]
	public static void ClassInitialize( TestContext context )
	{
		Sandbox.Application.InitUnitTest();
	}
}
Debug: View Raw JSON Response
{
    "TotalCount": 7,
    "Files": [
        {
            "Ident": "carsonk.tiktoktts",
            "Path": "Code/TikTokTTS.cs",
            "FileName": "TikTokTTS.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 55749,
            "IsPrivate": false,
            "Code": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing Sandbox;\r\n\r\nnamespace TikTokTTS;\r\n\r\n/// <summary>\r\n/// A static class that allows you to use the TikTokTTS API.\r\n/// </summary>\r\npublic static class TikTokTTS\r\n{\r\n    /// <summary>\r\n    /// The API endpoint that the library uses.\r\n    /// </summary>\r\n    public static string ENDPOINT = \"https://tiktok-tts.weilnet.workers.dev\";\r\n\r\n    static int currentId = 0;\r\n\r\n    /// <summary>\r\n    /// Use TikTok's TTS to speak via a MusicPlayer. This MusicPlayer will automatically dispose itself when it finishes playing.\r\n    /// </summary>\r\n    /// <param name=\"text\">The text to be said by the TTS</param>\r\n    /// <param name=\"voice\">The name of the voice</param>\r\n    /// <returns></returns>\r\n    public static async Task<MusicPlayer> Say( string text, string voice = \"en_us_007\" )\r\n    {\r\n        string fileName = $\"tts-{currentId}.mp3\";\r\n        currentId++;\r\n\r\n        if ( !FileSystem.Data.FileExists( fileName ) )\r\n        {\r\n            await Download( text, voice, fileName, FileSystem.Data );\r\n        }\r\n\r\n        var musicPlayer = MusicPlayer.Play( FileSystem.Data, fileName );\r\n        musicPlayer.ListenLocal = true;\r\n\r\n        return musicPlayer;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Use TikTok's TTS to speak and download/save the mp3 file.\r\n    /// </summary>\r\n    /// <param name=\"text\">The text to be said by the TTS</param>\r\n    /// <param name=\"voice\">The name of the voice</param>\r\n    /// <param name=\"fileName\">The file name/path it should download to (defaults to \"tts-##########.mp3\"</param>\r\n    /// <param name=\"fileSystem\">The filesystem to use (defaults to FileSystem.Data)</param>\r\n    /// <returns>The file path</returns>\r\n    public static async Task<string> Download( string text, string voice = \"en_us_007\", string fileName = null, BaseFileSystem fileSystem = null )\r\n    {\r\n        if ( fileSystem == null ) fileSystem = FileSystem.Data;\r\n        if ( string.IsNullOrEmpty( fileName ) ) fileName = $\"tts-{new Guid()}.mp3\";\r\n\r\n        var content = new Dictionary<string, string> {\r\n            { \"text\", text },\r\n            { \"voice\", voice }\r\n        };\r\n        var headers = new Dictionary<string, string> {\r\n            { \"Content-Type\", \"application/json\" }\r\n        };\r\n\r\n        text = text.RemoveBadCharacters();\r\n        text = text.Substring( 0, Math.Min( text.Length, 300 ) );\r\n        if ( !text.Any( x => char.IsLetterOrDigit( x ) ) ) return null;\r\n        if ( string.IsNullOrEmpty( text ) ) return null;\r\n\r\n        // Make sure folders exist\r\n        var folders = fileName.Split( '/' );\r\n        if ( folders.Length == 1 ) folders = fileName.Split( '\\\\' );\r\n        if ( folders.Length > 1 )\r\n        {\r\n            string path = \"\";\r\n            for ( int i = 0; i < folders.Length - 1; i++ )\r\n            {\r\n                path += folders[i] + \"/\";\r\n                if ( !fileSystem.DirectoryExists( path ) )\r\n                    fileSystem.CreateDirectory( path );\r\n            }\r\n        }\r\n\r\n        try\r\n        {\r\n            if ( fileSystem.FileExists( fileName ) )\r\n                fileSystem.DeleteFile( fileName );\r\n            var response = await Http.RequestJsonAsync<TikTokTTSResponse>( \"https://tiktok-tts.weilnet.workers.dev/api/generation\", \"POST\", Http.CreateJsonContent( content ), headers );\r\n            string base64 = response.data;\r\n            byte[] mp3 = Convert.FromBase64String( base64 );\r\n            var stream = fileSystem.OpenWrite( fileName );\r\n            stream.Write( mp3, 0, mp3.Length );\r\n            stream.Close();\r\n            return fileName;\r\n        }\r\n        catch ( Exception e )\r\n        {\r\n            Log.Error( e.Message );\r\n            return null;\r\n        }\r\n    }\r\n\r\n    class TikTokTTSResponse\r\n    {\r\n        public bool success { get; set; }\r\n        public string data { get; set; }\r\n        public string error { get; set; }\r\n    }\r\n}"
        },
        {
            "Ident": "carsonk.tiktoktts",
            "Path": "UnitTests/LibraryTest.cs",
            "FileName": "LibraryTest.cs",
            "PackageType": "library",
            "CodeKind": "UnitTest",
            "AssetVersionId": 55749,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\n\r\npublic partial class LibraryTests\r\n{\r\n\t[TestMethod]\r\n\tpublic void SceneTest()\r\n\t{\r\n\t\tvar scene = new Scene();\r\n\t\tusing ( scene.Push() )\r\n\t\t{\r\n\t\t\tvar go = new GameObject();\r\n\r\n\t\t\tAssert.AreEqual( 1, scene.Directory.GameObjectCount );\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"
        },
        {
            "Ident": "carsonk.tiktoktts",
            "Path": "Editor/TikTokSpeechControlWidget.cs",
            "FileName": "TikTokSpeechControlWidget.cs",
            "PackageType": "library",
            "CodeKind": "Editor",
            "AssetVersionId": 55749,
            "IsPrivate": false,
            "Code": "using Editor;\r\nusing Sandbox;\r\nusing System.Linq;\r\n\r\nnamespace TikTokTTS.Editor;\r\n\r\n[CustomEditor( typeof( TikTokSpeech ) )]\r\npublic class TikTokSpeechComponentEditor : ComponentEditorWidget\r\n{\r\n\r\n\tSerializedObject Target;\r\n\r\n\tpublic TikTokSpeechComponentEditor( SerializedObject obj ) : base( obj )\r\n\t{\r\n\t\tTarget = obj;\r\n\t\tLayout = Layout.Column();\r\n\t\tvar sheet = new PropertyControlSheet();\r\n\t\tsheet.AddObject( obj );\r\n\t\tLayout.Add( sheet );\r\n\t\tLayout.Spacing = 2;\r\n\t\tMinimumHeight = 70;\r\n\r\n\t\tvar buttonPanel = Layout.Column();\r\n\t\tbuttonPanel.Margin = new Sandbox.UI.Margin( 12, 0 );\r\n\t\tvar button = new Button( \"Speak\", \"play_arrow\" )\r\n\t\t{\r\n\t\t\tWidth = 200,\r\n\t\t\tClicked = () =>\r\n\t\t\t{\r\n\t\t\t\tvar component = Target.Targets.First() as TikTokSpeech;\r\n\t\t\t\tcomponent?.Speak();\r\n\t\t\t}\r\n\t\t};\r\n\t\tbuttonPanel.Add( button );\r\n\t\tLayout.Add( buttonPanel );\r\n\t}\r\n\r\n\tprotected override void OnPaint()\r\n\t{\r\n\t}\r\n\r\n}"
        },
        {
            "Ident": "carsonk.tiktoktts",
            "Path": "Code/TikTokVoices.cs",
            "FileName": "TikTokVoices.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 55749,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\n\r\nnamespace TikTokTTS;\r\n\r\npublic enum TikTokVoice\r\n{\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Female - Jessie (en_us_002)\" )] en_us_002,\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Female - Empathetic (en_female_samc)\" )] en_female_samc,\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Female - Beauty Guru (en_female_makeup)\" )] en_female_makeup,\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Female - Bestie (en_female_richgirl)\" )] en_female_richgirl,\r\n\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Male - Joey (en_us_006)\" )] en_us_006,\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Male - Professor (en_us_007)\" )] en_us_007,\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Male - Scientist (en_us_009)\" )] en_us_009,\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Male - Confidence (en_us_010)\" )] en_us_010,\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Male - Serious (en_male_cody)\" )] en_male_cody,\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Male - Wacky (en_male_funny)\" )] en_male_funny,\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Male - Story Teller (en_male_narration)\" )] en_male_narration,\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Male - Game On (en_male_jomboy)\" )] en_male_jomboy,\r\n    [Icon( \"\ud83c\uddfa\ud83c\uddf8\" ), Title( \"US Male - Alfred (en_male_jarvis)\" )] en_male_jarvis,\r\n\r\n    [Icon( \"\ud83c\uddec\ud83c\udde7\" ), Title( \"UK Male - Narrator (en_uk_001)\" )] en_uk_001,\r\n    [Icon( \"\ud83c\uddec\ud83c\udde7\" ), Title( \"UK Male - English (en_uk_003)\" )] en_uk_003,\r\n    [Icon( \"\ud83c\uddec\ud83c\udde7\" ), Title( \"UK Male - Olantekkers (en_male_olantekkers)\" )] en_male_olantekkers,\r\n\r\n    [Icon( \"\ud83c\udde6\ud83c\uddfa\" ), Title( \"AU Female - Metro (en_au_001)\" )] en_au_001,\r\n    [Icon( \"\ud83c\udde6\ud83c\uddfa\" ), Title( \"AU Female - Peaceful (en_female_emotional)\" )] en_female_emotional,\r\n    [Icon( \"\ud83c\udde6\ud83c\uddfa\" ), Title( \"AU Male - Smooth (en_au_002)\" )] en_au_002,\r\n    [Icon( \"\ud83c\udde6\ud83c\uddfa\" ), Title( \"AU Male - Ashmagic (en_male_ashmagic)\" )] en_male_ashmagic,\r\n\r\n    [Icon( \"\ud83d\udc98\" ), Title( \"Festive - Cupid (en_male_cupid)\" )] en_male_cupid,\r\n    [Icon( \"\ud83c\udf85\" ), Title( \"Festive - Narration (en_male_santa_narration)\" )] en_male_santa_narration,\r\n    [Icon( \"\ud83c\udf85\" ), Title( \"Festive - Santa (en_male_santa_effect)\" )] en_male_santa_effect,\r\n\r\n    [Icon( \"\ud83d\ude31\" ), Title( \"Disney - Scream (en_us_ghostface)\" )] en_us_ghostface,\r\n    [Icon( \"\ud83d\udc36\" ), Title( \"Disney - Chewbacca (en_us_chewbacca)\" )] en_us_chewbacca,\r\n    [Icon( \"\ud83e\udd16\" ), Title( \"Disney - C3PO (en_us_c3po)\" )] en_us_c3po,\r\n    [Icon( \"\ud83e\udd76\" ), Title( \"Disney - Stitch (en_us_stitch)\" )] en_us_stitch,\r\n    [Icon( \"\ud83e\ude96\" ), Title( \"Disney - Stormtrooper (en_us_stormtrooper)\" )] en_us_stormtrooper,\r\n    [Icon( \"\ud83d\ude80\" ), Title( \"Disney - Rocket (en_us_rocket)\" )] en_us_rocket,\r\n    [Icon( \"\ud83d\udc69\u200d\ud83e\uddb3\" ), Title( \"Disney - Madame Leota (en_female_madam_leota)\" )] en_female_madam_leota,\r\n    [Icon( \"\ud83d\udc7b\" ), Title( \"Disney - Ghost Host (en_male_ghosthost)\" )] en_male_ghosthost,\r\n    [Icon( \"\u2620\ufe0f\" ), Title( \"Disney - Pirate (en_male_pirate)\" )] en_male_pirate,\r\n\r\n    [Icon( \"\ud83e\uddfd\" ), Title( \"Silly - Trickster/Spongebob (en_male_grinch)\" )] en_male_grinch,\r\n    [Icon( \"\ud83e\uddd4\" ), Title( \"Silly - Mr. GoodGuy (en_male_deadpool)\" )] en_male_deadpool,\r\n    [Icon( \"\ud83d\ude4d\" ), Title( \"Silly - Lord Cringe (en_male_ukneighbor)\" )] en_male_ukneighbor,\r\n    [Icon( \"\ud83e\udd35\" ), Title( \"Silly - Mr. Meticulous (en_male_ukbutler)\" )] en_male_ukbutler,\r\n    [Icon( \"\ud83e\udd42\" ), Title( \"Silly - Debutante (en_female_shenna)\" )] en_female_shenna,\r\n    [Icon( \"\ud83e\udd2a\" ), Title( \"Silly - Varsity (en_female_pansino)\" )] en_female_pansino,\r\n    [Icon( \"\ud83d\udc68\u200d\ud83d\udd2c\" ), Title( \"Silly - Marty (en_male_trevor)\" )] en_male_trevor,\r\n    [Icon( \"\ud83d\ude45\" ), Title( \"Silly - Bae (en_female_betty)\" )] en_female_betty,\r\n    [Icon( \"\ud83d\udc75\" ), Title( \"Silly - Granny (en_female_grandma)\" )] en_female_grandma,\r\n    [Icon( \"\ud83e\ude84\" ), Title( \"Silly - Magician (en_male_wizard)\" )] en_male_wizard,\r\n\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Music - Alto/Cottagecore (en_female_f08_salut_damour)\" )] en_female_f08_salut_damour,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Music - Tenor/Jingle (en_male_m03_lobby)\" )] en_male_m03_lobby,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Music - Sunshine Soon/Toon Beat (en_male_m03_sunshine_soon)\" )] en_male_m03_sunshine_soon,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Music - Open Mic (en_female_f08_warmy_breeze)\" )] en_female_f08_warmy_breeze,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Music - Euphoric (en_female_ht_f08_glorious)\" )] en_female_ht_f08_glorious,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Music - It Goes Up/Hypetrain (en_male_sing_funny_it_goes_up)\" )] en_male_sing_funny_it_goes_up,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Music - Chipmunk/Quirky Time (en_male_m2_xhxs_m03_silly)\" )] en_male_m2_xhxs_m03_silly,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Music - Melodrama (en_female_ht_f08_wonderful_world)\" )] en_female_ht_f08_wonderful_world,\r\n\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Parody - Pop Lullaby (en_female_f08_twinkle)\" )] en_female_f08_twinkle,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Parody - Classic Electric (en_male_m03_classical)\" )] en_male_m03_classical,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Parody - Cozy (en_male_m2_xhxs_m03_christmas)\" )] en_male_m2_xhxs_m03_christmas,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Parody - Caroler (en_male_sing_deep_jingle)\" )] en_male_sing_deep_jingle,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Parody - NYE 2023 (en_female_ht_f08_newyear)\" )] en_female_ht_f08_newyear,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Parody - Opera (en_female_ht_f08_halloween)\" )] en_female_ht_f08_halloween,\r\n    [Icon( \"\ud83c\udfb6\" ), Title( \"Parody - Thanksgiving (en_male_sing_funny_thanksgiving)\" )] en_male_sing_funny_thanksgiving,\r\n\r\n    [Icon( \"\ud83c\uddeb\ud83c\uddf7\" ), Title( \"French - Male 1 (fr_001)\" )] fr_001,\r\n    [Icon( \"\ud83c\uddeb\ud83c\uddf7\" ), Title( \"French - Male 2 (fr_002)\" )] fr_002,\r\n\r\n    [Icon( \"\ud83c\uddea\ud83c\uddf8\" ), Title( \"Spanish - Spain Male (es_002)\" )] es_002,\r\n\r\n    [Icon( \"\ud83c\uddf2\ud83c\uddfd\" ), Title( \"Spanish - MX Male (es_mx_002)\" )] es_mx_002,\r\n\r\n    [Icon( \"\ud83c\udde7\ud83c\uddf7\" ), Title( \"Portuguese BR - Female 1 (br_001)\" )] br_001,\r\n    [Icon( \"\ud83c\udde7\ud83c\uddf7\" ), Title( \"Portuguese BR - Female 2 (br_003)\" )] br_003,\r\n    [Icon( \"\ud83c\udde7\ud83c\uddf7\" ), Title( \"Portuguese BR - Female 3 (br_004)\" )] br_004,\r\n    [Icon( \"\ud83c\udde7\ud83c\uddf7\" ), Title( \"Portuguese BR - Male (br_005)\" )] br_005,\r\n\r\n    [Icon( \"\ud83c\udde9\ud83c\uddea\" ), Title( \"German - Female (de_001)\" )] de_001,\r\n    [Icon( \"\ud83c\udde9\ud83c\uddea\" ), Title( \"German - Male (de_002)\" )] de_002,\r\n\r\n    [Icon( \"\ud83c\uddee\ud83c\udde9\" ), Title( \"Indonesian - Female (id_001)\" )] id_001,\r\n\r\n    [Icon( \"\ud83c\uddef\ud83c\uddf5\" ), Title( \"Japanese - Female 1 (jp_001)\" )] jp_001,\r\n    [Icon( \"\ud83c\uddef\ud83c\uddf5\" ), Title( \"Japanese - Female 2 (jp_003)\" )] jp_003,\r\n    [Icon( \"\ud83c\uddef\ud83c\uddf5\" ), Title( \"Japanese - Female 3 (jp_005)\" )] jp_005,\r\n    [Icon( \"\ud83c\uddef\ud83c\uddf5\" ), Title( \"Japanese - Male (jp_006)\" )] jp_006,\r\n\r\n    [Icon( \"\ud83c\uddf0\ud83c\uddf7\" ), Title( \"Korean - Female 1 (kr_003)\" )] kr_003,\r\n    [Icon( \"\ud83c\uddf0\ud83c\uddf7\" ), Title( \"Korean - Male 1 (kr_002)\" )] kr_002,\r\n    [Icon( \"\ud83c\uddf0\ud83c\uddf7\" ), Title( \"Korean - Male 2 (kr_004)\" )] kr_004,\r\n\r\n    [Icon( \"\ud83c\uddfb\ud83c\uddf3\" ), Title( \"Vietnamese - Female (BV074_streaming)\" )] BV074_streaming,\r\n    [Icon( \"\ud83c\uddfb\ud83c\uddf3\" ), Title( \"Vietnamese - Male (BV075_streaming)\" )] BV075_streaming,\r\n\r\n\r\n\r\n}"
        },
        {
            "Ident": "carsonk.tiktoktts",
            "Path": "Editor/PropertyControlSheet.cs",
            "FileName": "PropertyControlSheet.cs",
            "PackageType": "library",
            "CodeKind": "Editor",
            "AssetVersionId": 55749,
            "IsPrivate": false,
            "Code": "using System;\r\nusing Editor;\r\nusing Sandbox;\r\n\r\nnamespace TikTokTTS.Editor;\r\n\r\npublic class PropertyControlSheet : GridLayout\r\n{\r\n\tpublic SerializedObject TargetObject { get; set; }\r\n\r\n\tint rows = 0;\r\n\r\n\tpublic PropertyControlSheet() : base()\r\n\t{\r\n\t\tMargin = new Sandbox.UI.Margin( 16, 8, 16, 8 );\r\n\t\tHorizontalSpacing = 10;\r\n\t\tVerticalSpacing = 2;\r\n\t\tSetColumnStretch( 1, 2 );\r\n\t\tSetMinimumColumnWidth( 0, 120 );\r\n\t}\r\n\r\n\tpublic void AddObject( SerializedObject obj )\r\n\t{\r\n\t\tforeach ( var entry in obj )\r\n\t\t{\r\n\t\t\tif ( !entry.HasAttribute<PropertyAttribute>() )\r\n\t\t\t{\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif ( entry.PropertyType.Name.StartsWith( \"Action\" ) )\r\n\t\t\t{\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tAddRow( entry );\r\n\t\t}\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Add a serialized property row. This will create an editor for the row and a label.\r\n\t/// </summary>\r\n\tpublic void AddRow( SerializedProperty property, float labelIndent = 0.0f )\r\n\t{\r\n\t\tvar editor = ControlWidget.Create( property );\r\n\t\tif ( editor is null )\r\n\t\t\treturn;\r\n\r\n\t\tif ( editor.IsWideMode )\r\n\t\t{\r\n\t\t\tAddCell( 0, rows++, new Label( property.DisplayName ) { MinimumHeight = Theme.RowHeight, Alignment = TextFlag.LeftCenter }, 2, 1, TextFlag.LeftTop );\r\n\t\t\tvar lo = AddCell( 0, rows, Layout.Column(), 2, 1, TextFlag.LeftTop );\r\n\r\n\t\t\tlo.Margin = new Sandbox.UI.Margin( 16, 0, 0, 0 );\r\n\t\t\tlo.Add( editor );\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tint cell = 0;\r\n\r\n\t\t\tvar label = AddCell( cell++, rows, new Label( property.DisplayName ), 1, 1, TextFlag.LeftTop );\r\n\t\t\tlabel.MinimumHeight = Theme.RowHeight;\r\n\t\t\tlabel.Alignment = TextFlag.LeftCenter;\r\n\t\t\tlabel.SetStyles( \"color: #aaa;\" );\r\n\r\n\t\t\tlabel.ToolTip = property.Description ?? property.DisplayName;\r\n\r\n\t\t\tif ( labelIndent > 0 )\r\n\t\t\t{\r\n\t\t\t\tlabel.ContentMargins = new Sandbox.UI.Margin( labelIndent, 0, 0, 0 );\r\n\t\t\t}\r\n\r\n\t\t\tAddCell( cell++, rows, editor, 1, 1, TextFlag.LeftTop );\r\n\t\t}\r\n\r\n\t\trows++;\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Add a layout to a double wide cell\r\n\t/// </summary>\r\n\tpublic void AddLayout( Layout layout )\r\n\t{\r\n\t\tAddCell( 0, rows++, layout, 2, 1, TextFlag.LeftTop );\r\n\t}\r\n\r\n}"
        },
        {
            "Ident": "carsonk.tiktoktts",
            "Path": "Code/TikTokSpeech.cs",
            "FileName": "TikTokSpeech.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 55749,
            "IsPrivate": false,
            "Code": "using System;\r\nusing Sandbox;\r\n\r\nnamespace TikTokTTS;\r\n\r\npublic class TikTokSpeech : Component\r\n{\r\n\r\n    /// <summary>\r\n    /// The text that will be spoken by the TTS.\r\n    /// </summary>\r\n    [Property] public string Text { get; set; } = \"Hello World!\";\r\n\r\n    /// <summary>\r\n    /// The voice that will be used to speak the text.\r\n    /// </summary>\r\n    [Property] public TikTokVoice Voice { get; set; } = TikTokVoice.en_us_002;\r\n\r\n    /// <summary>\r\n    /// Whether or not the audio should be positional (based on the position of the GameObject that this component is attached to)\r\n    /// </summary>\r\n    [Property] public bool PositionalAudio { get; set; } = false;\r\n\r\n    /// <summary>\r\n    /// Whether or not to cache the files that are generated by the TTS. When enabled, the files will be stored in the data directory and referenced if the same call is made again.\r\n    /// </summary>\r\n    [Property] public bool CacheFiles { get; set; } = true;\r\n\r\n    /// <summary>\r\n    /// An event that is fired when the TTS has started speaking.\r\n    /// </summary>\r\n    [Property] public Action<string, TikTokVoice> OnSpeak { get; set; }\r\n\r\n    private string VoiceName => Enum.GetName( typeof( TikTokVoice ), Voice );\r\n    private string FileName => $\"tts/tts-{Text.ToLowerInvariant().UrlEncode()}-{VoiceName}.mp3\";\r\n    private MusicPlayer MusicPlayer;\r\n\r\n    /// <summary>\r\n    /// Speak the current text using the TTS.\r\n    /// </summary>\r\n    public async void Speak()\r\n    {\r\n        MusicPlayer?.Dispose();\r\n\r\n        if ( CacheFiles )\r\n        {\r\n            if ( !FileSystem.Data.FileExists( FileName ) )\r\n            {\r\n                await TikTokTTS.Download( Text, VoiceName, FileName, FileSystem.Data );\r\n            }\r\n\r\n            MusicPlayer = MusicPlayer.Play( FileSystem.Data, FileName );\r\n        }\r\n        else\r\n        {\r\n            MusicPlayer = await TikTokTTS.Say( Text, VoiceName );\r\n            if ( MusicPlayer is null )\r\n            {\r\n                Log.Error( \"Failed to speak\" );\r\n                return;\r\n            }\r\n        }\r\n\r\n        OnSpeak?.Invoke( Text, Voice );\r\n\r\n        MusicPlayer.ListenLocal = !PositionalAudio;\r\n    }\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        if ( MusicPlayer is not null )\r\n        {\r\n            MusicPlayer.Position = Transform.Position;\r\n        }\r\n    }\r\n\r\n}"
        },
        {
            "Ident": "carsonk.tiktoktts",
            "Path": "UnitTests/UnitTest.cs",
            "FileName": "UnitTest.cs",
            "PackageType": "library",
            "CodeKind": "UnitTest",
            "AssetVersionId": 55749,
            "IsPrivate": false,
            "Code": "global using Microsoft.VisualStudio.TestTools.UnitTesting;\r\n\r\n[TestClass]\r\npublic class TestInit\r\n{\r\n\t[AssemblyInitialize]\r\n\tpublic static void ClassInitialize( TestContext context )\r\n\t{\r\n\t\tSandbox.Application.InitUnitTest();\r\n\t}\r\n}\r\n"
        }
    ]
}