s&box Package Code Search

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

Showing code results for query: * (4 total matches found)
attackhelicopter.aitools / Editor/SpectogramWidget.cs
Editor library
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using Editor;
using Sandbox;
using System.Text;

class SpectogramWidget : Widget 
{
    private short[] samples;
    private int sampleRate;
    private List<int> splitPoints = new List<int>();
    private int? dragPoint = null;
    private Label loadingLabel;
    private Label dropLabel;
    private bool isLoading = true;
    public SoundFile CurrentSound { get; private set; }

    public SpectogramWidget(SoundFile soundFile) : base(null)
    {
        MinimumSize = 100;
        MouseTracking = true;
        AcceptDrops = true;

        loadingLabel = new Label(this);
        loadingLabel.Text = "Loading audio data...";
        loadingLabel.Visible = false;
        
        dropLabel = new Label(this);
        dropLabel.Text = "Drop a sound file here";
        dropLabel.SetStyles("font-size: 18px; color: #aaa; text-align: center;");
        
        if (soundFile != null)
        {
            LoadSound(soundFile);
        }
    }

    public async void LoadSound(SoundFile soundFile)
    {
        CurrentSound = soundFile;
        isLoading = true;
        samples = null;
        splitPoints.Clear();
        
        loadingLabel.Visible = true;
        dropLabel.Visible = false;

        await LoadAudioDataAsync(soundFile);
    }

    private async Task LoadAudioDataAsync(SoundFile soundFile)
    {
        try 
        {
            await soundFile.LoadAsync();
            samples = await soundFile.GetSamplesAsync();
            
            if (samples == null)
            {
                loadingLabel.Text = "Failed to load audio data";
                return;
            }

            sampleRate = soundFile.Rate;
            splitPoints.Add(0);
            splitPoints.Add(samples.Length - 1);

            loadingLabel.Visible = false;
            isLoading = false;

            Update();
        }
        catch (Exception ex)
        {
            loadingLabel.Text = $"Error loading audio: {ex.Message}";
        }
    }

    protected override void DoLayout()
    {
        base.DoLayout();

        if (loadingLabel != null)
        {
            loadingLabel.Position = new Vector2(10, Height / 2 - 10);
            loadingLabel.Size = new Vector2(Width - 20, 20);
        }

        if (dropLabel != null)
        {
            dropLabel.Position = new Vector2(10, Height / 2 - 10);
            dropLabel.Size = new Vector2(Width - 20, 20);
        }
    }

    public override void OnDragDrop(DragEvent e)
    {
        base.OnDragDrop(e);

        if (!e.Data.HasFileOrFolder) return;

        var asset = AssetSystem.FindByPath(e.Data.FileOrFolder);
        if (asset?.AssetType != AssetType.SoundFile) return;

        var soundFile = SoundFile.Load(asset.Path);
        if (soundFile != null)
        {
            LoadSound(soundFile);
        }
    }

    public override void OnDragHover(DragEvent e)
    {
        base.OnDragHover(e);

        if (!e.Data.HasFileOrFolder) return;

        var asset = AssetSystem.FindByPath(e.Data.FileOrFolder);
        if (asset?.AssetType != AssetType.SoundFile) return;

        e.Action = DropAction.Link;
    }

    protected override void OnMouseClick(MouseEvent e)
    {
        if (isLoading) return;
        
        base.OnMouseClick(e);

        if (e.Button == MouseButtons.Left)
        {
            var samplePos = (int)(e.LocalPosition.x / Width * samples.Length);
            var nearPoint = splitPoints.FirstOrDefault(p => Math.Abs(p - samplePos) < (samples.Length / Width * 5));
            
            if (nearPoint != default)
            {
                dragPoint = splitPoints.IndexOf(nearPoint);
            }
            else
            {
                splitPoints.Add(samplePos);
                splitPoints.Sort();
                Update();
            }
        }
    }

    protected override void OnMouseMove(MouseEvent e)
    {
        if (isLoading) return;
        
        base.OnMouseMove(e);

        if (dragPoint.HasValue)
        {
            var samplePos = (int)(e.LocalPosition.x / Width * samples.Length);
            splitPoints[dragPoint.Value] = samplePos;
            splitPoints.Sort();
            Update();
        }
    }

    protected override void OnMouseReleased(MouseEvent e)
    {
        if (isLoading) return;
        
        base.OnMouseReleased(e);
        dragPoint = null;
    }

    protected override void OnPaint()
    {
        base.OnPaint();

        if (isLoading || samples == null)
        {
            return;
        }

        Paint.ClearPen();
        Paint.SetBrush(Theme.Grey.WithAlpha(0.1f));
        Paint.DrawRect(LocalRect);

        Paint.SetPen(Theme.Blue);
        var samplesPerPixel = samples.Length / Width;
        for (int x = 0; x < Width; x++)
        {
            var startSample = (int)(x * samplesPerPixel);
            var endSample = Math.Min(startSample + samplesPerPixel, samples.Length);
            
            var max = short.MinValue;
            var min = short.MaxValue;
            
            for (int i = startSample; i < endSample; i++)
            {
                max = Math.Max(max, samples[i]);
                min = Math.Min(min, samples[i]);
            }

            var y1 = Height / 2 + (min / (float)short.MaxValue * Height / 2);
            var y2 = Height / 2 + (max / (float)short.MaxValue * Height / 2);
            
            Paint.DrawLine(new Vector2(x, y1), new Vector2(x, y2));
        }

        Paint.SetPen(Theme.Red);
        foreach (var point in splitPoints)
        {
            var x = point / (float)samples.Length * Width;
            Paint.DrawLine(new Vector2(x, 0), new Vector2(x, Height));
        }
    }

    public List<int> GetSplitPoints()
    {
        return new List<int>(splitPoints);
    }

    public void SplitCurrentSound(Action<SoundFile> onSoundCreated)
    {
        if (CurrentSound == null || samples == null) return;

        var splitPoints = GetSplitPoints();
        if (splitPoints.Count < 2) return;

        try
        {
            var baseFileName = Path.GetFileNameWithoutExtension(CurrentSound.ResourcePath);
            var outputDir = Path.Combine(
                Project.Current.GetAssetsPath(),
                "generated",
                $"{baseFileName}_splits"
            );
            Directory.CreateDirectory(outputDir);

            for (int i = 0; i < splitPoints.Count - 1; i++)
            {
                var start = splitPoints[i];
                var end = splitPoints[i + 1];
                var length = end - start;

                var segmentSamples = new short[length];
                Array.Copy(samples, start, segmentSamples, 0, length);

                var wavPath = Path.Combine(outputDir, $"{baseFileName}_part_{i + 1}.wav");

                using (var writer = new BinaryWriter(File.Create(wavPath)))
                {
                    writer.Write(Encoding.ASCII.GetBytes("RIFF"));
                    writer.Write(36 + (segmentSamples.Length * 2));
                    writer.Write(Encoding.ASCII.GetBytes("WAVE"));

                    writer.Write(Encoding.ASCII.GetBytes("fmt "));
                    writer.Write(16);
                    writer.Write((short)1);
                    writer.Write((short)CurrentSound.Channels);
                    writer.Write(CurrentSound.Rate);
                    writer.Write(CurrentSound.Rate * CurrentSound.Channels * 2);
                    writer.Write((short)(CurrentSound.Channels * 2));
                    writer.Write((short)16);

                    writer.Write(Encoding.ASCII.GetBytes("data"));
                    writer.Write(segmentSamples.Length * 2);

                    foreach (var sample in segmentSamples)
                    {
                        writer.Write(sample);
                    }
                }

                var asset = AssetSystem.RegisterFile(wavPath);
                if (asset != null)
                {
                    var soundFile = SoundFile.Load(asset.RelativePath);
                    if (soundFile != null)
                    {
                        onSoundCreated?.Invoke(soundFile);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Log.Error($"Error splitting sound: {ex.Message}");
        }
    }
}
attackhelicopter.aitools / Editor/SoundAddons.cs
Editor library
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Reflection;
using Sandbox;
using System.Collections.Generic;
using System.Linq;

namespace Editor;

public static class SoundAddons
{
	private static string previousSoundGenerationPromp;
	private static string previousSoundGenerationDuration;

	// [ContextMenuFor( typeof( SoundFile ), "Generate sound", "create" )]
	public static void Generate( SoundEvent soundEvent, Action<SoundFile> finished )
	{
		string defaultInput = !string.IsNullOrEmpty( previousSoundGenerationPromp )
			? previousSoundGenerationPromp
			: "A dog barking";

		Dialog.AskString( ( description ) =>
		{
			previousSoundGenerationPromp = description;

			Dialog.AskString( async ( durationStr ) =>
			{
				previousSoundGenerationDuration = durationStr;
				if ( !float.TryParse( durationStr, out float duration ) || duration < 0.5f || duration > 22f )
				{
					Log.Error( "Duration must be between 0.5 and 22 seconds" );
					return;
				}

				try
				{
					string apiKey = SoundSettings.Settings.ElevenLabsApiKey;
					var client = new HttpClient();
					client.DefaultRequestHeaders.Add( "xi-api-key", apiKey );

					var content = new StringContent( JsonSerializer.Serialize( new
					{
						text = description,
						duration_seconds = duration,
						prompt_influence = 0.3
					} ), Encoding.UTF8, "application/json" );

					var response = await client.PostAsync(
						"https://api.elevenlabs.io/v1/sound-generation",
						content
					);

					if ( response.IsSuccessStatusCode )
					{
						var bytes = await response.Content.ReadAsByteArrayAsync();

						string safeFileName = description.Replace( " ", "_" ).Replace( "/", "_" ).Replace( "\\", "_" );
						var mp3Path = Path.Combine(
							SoundSettings.Settings.GenerationPath,
							$"{safeFileName}_{DateTime.Now:yyyyMMddHHmmss}.mp3"
						);

						var fullMp3Path = Path.Combine( Project.Current.GetAssetsPath(), mp3Path );
						Directory.CreateDirectory( Path.GetDirectoryName( fullMp3Path ) );

						await File.WriteAllBytesAsync( fullMp3Path, bytes );
						Log.Info( "File saved to: " + fullMp3Path );

						var mp3Asset = AssetSystem.RegisterFile( fullMp3Path );
						if ( mp3Asset != null )
						{
							var soundFile = SoundFile.Load( mp3Asset.RelativePath );
							if ( soundFile != null )
							{
								finished(soundFile);
								Log.Info( $"Successfully generated and linked sound: {mp3Asset.RelativePath}" );
							}
							else
							{
								Log.Error( "Failed to load generated sound file" );
							}
						}
						else
						{
							Log.Error( "Failed to register generated mp3 file as asset" );
						}
					}
					else
					{
						var errorContent = await response.Content.ReadAsStringAsync();
						Log.Error( $"Failed to generate sound: {response.StatusCode}. Error: {errorContent}" );
					}
				}
				catch ( Exception ex )
				{
					Log.Error( $"Error generating sound: {ex.Message}" );
				}
			},
			"Enter duration in seconds (0.5 to 22):",
			"Generate",
			"Cancel",
			previousSoundGenerationDuration,
			"Set Sound Duration" );
		},
		"Describe the sound effect to generate:",
		"Next",
		"Cancel",
		defaultInput,
		"Generate Sound Effect" );
	}

	// [ContextMenuFor( typeof( SoundFile ), "Split sound", "content_cut" )]
	public static async void SplitSound( Widget parent, SerializedProperty property )
	{
		var soundResource = property.GetValue<SoundFile>();
		if ( soundResource == null || !soundResource.IsValid )
		{
			Log.Error( "No valid sound file selected" );
			return;
		}

		var dialog = new Dialog();
		dialog.Window.Title = "Split Sound";
		dialog.Window.Size = new Vector2( 800, 400 );

		dialog.Layout = Layout.Column();
		var mainLayout = dialog.Layout.AddColumn();
		mainLayout.Margin = 16f;

		var loadingLabel = new Label( "Loading audio data..." );
		mainLayout.Add( loadingLabel );

		dialog.Show();

		var samples = await soundResource.GetSamplesAsync();
		if ( samples == null )
		{
			loadingLabel.Text = "Failed to load audio samples";
			return;
		}

		loadingLabel.Destroy();

		var spectogramWidget = new SpectogramWidget( soundResource );
		mainLayout.Add( spectogramWidget, 1 );

		var controlsLayout = mainLayout.AddRow();

		var splitButton = new Button.Primary( "Split", "content_cut" );
		controlsLayout.Add( splitButton );

		splitButton.Clicked = () =>
		{
			var splitPoints = spectogramWidget.GetSplitPoints();
			if ( splitPoints.Count < 1 ) return;

			var listControlWidget = parent.GetAncestor<ListControlWidget>();
			if ( listControlWidget == null )
			{
				Log.Error( "Could not find ListControlWidget parent" );
				return;
			}

			try
			{
				var baseFileName = Path.GetFileNameWithoutExtension( soundResource.ResourcePath );
				var outputDir = Path.Combine(
					Project.Current.GetAssetsPath(),
					"generated",
					$"{baseFileName}_splits"
				);
				Directory.CreateDirectory( outputDir );

				var newSoundFiles = new List<SoundFile>();

				for ( int i = 0; i < splitPoints.Count - 1; i++ )
				{
					var start = splitPoints[i];
					var end = splitPoints[i + 1];
					var length = end - start;

					var segmentSamples = new short[length];
					Array.Copy( samples, start, segmentSamples, 0, length );

					var wavPath = Path.Combine( outputDir, $"{baseFileName}_part_{i + 1}.wav" );

					using ( var writer = new BinaryWriter( File.Create( wavPath ) ) )
					{
						writer.Write( Encoding.ASCII.GetBytes( "RIFF" ) );
						writer.Write( 36 + (segmentSamples.Length * 2) );
						writer.Write( Encoding.ASCII.GetBytes( "WAVE" ) );

						writer.Write( Encoding.ASCII.GetBytes( "fmt " ) );
						writer.Write( 16 );
						writer.Write( (short)1 );
						writer.Write( (short)soundResource.Channels );
						writer.Write( soundResource.Rate );
						writer.Write( soundResource.Rate * soundResource.Channels * 2 );
						writer.Write( (short)(soundResource.Channels * 2) );
						writer.Write( (short)16 );

						writer.Write( Encoding.ASCII.GetBytes( "data" ) );
						writer.Write( segmentSamples.Length * 2 );

						foreach ( var sample in segmentSamples )
						{
							writer.Write( sample );
						}
					}

					var asset = AssetSystem.RegisterFile( wavPath );
					if ( asset != null )
					{
						var soundFile = SoundFile.Load( asset.RelativePath );
						if ( soundFile != null )
						{
							newSoundFiles.Add( soundFile );
						}
					}
				}

				if ( newSoundFiles.Count > 0 )
				{
					try
					{
						var collectionField = listControlWidget.GetType()
							.GetFields( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public )
							.FirstOrDefault( f => typeof( SerializedCollection ).IsAssignableFrom( f.FieldType ) );

						if ( collectionField == null )
						{
							return;
						}

						var collection = collectionField.GetValue( listControlWidget ) as SerializedCollection;
						if ( collection == null )
						{
							return;
						}

						if ( property.TryGetAsObject( out var original ) )
						{
							foreach ( var soundFile in newSoundFiles )
							{
								collection.Add( soundFile );
							}

							collection.Remove( property );
						}
					}
					catch ( Exception ex )
					{
						Log.Error( $"Error manipulating collection: {ex.Message}" );
						Log.Error( $"Stack trace: {ex.StackTrace}" );
					}
				}

				dialog.Close();
			}
			catch ( Exception ex )
			{
				Log.Error( $"Error splitting sound: {ex.Message}" );
			}
		};
	}
}
attackhelicopter.aitools / Editor/SoundSettings.cs
Editor library
using System;
using System.IO;
using System.Text.Json;
using Editor;
using Sandbox;

public class SoundSettings : Widget
{
    public class Config : ConfigData
    {
        public string ElevenLabsApiKey { get; set; } = "";
        public string GenerationPath { get; set; } = "generated";
    }

    private LineEdit apiKeyInput;
    private LineEdit pathInput;
    private static Config _settings;
    private Button saveButton;

    public static Config Settings 
    {
        get
        {
            if (_settings == null)
                _settings = EditorUtility.LoadProjectSettings<Config>("settings.json");
            return _settings;
        }
    }

    public SoundSettings() : base(null)
    {
        Layout = Layout.Column();
        var mainLayout = Layout.Add(Layout.Column());
        mainLayout.Margin = 16;

        var apiKeyGroup = mainLayout.AddRow();
        apiKeyGroup.Add(new Label("ElevenLabs API Key:"));
        
        apiKeyInput = new LineEdit(this);
        apiKeyInput.MinimumWidth = 300;
        apiKeyInput.PlaceholderText = "Enter your ElevenLabs API key";
        apiKeyInput.Text = Settings.ElevenLabsApiKey;
        apiKeyGroup.Add(apiKeyInput);

        mainLayout.AddSpacingCell(8);

        var pathGroup = mainLayout.AddRow();
        pathGroup.Add(new Label("Generation Path:"));
        
        pathInput = new LineEdit(this);
        pathInput.MinimumWidth = 300;
        pathInput.PlaceholderText = "Enter generation path (relative to assets)";
        pathInput.Text = Settings.GenerationPath;
        pathGroup.Add(pathInput);

        mainLayout.AddStretchCell();

        var buttonLayout = mainLayout.AddRow();
        buttonLayout.AddStretchCell();

        saveButton = new Button.Primary("Save", "save");
        saveButton.Clicked = SaveSettings;
        buttonLayout.Add(saveButton);
    }

    private void SaveSettings()
    {
        Settings.ElevenLabsApiKey = apiKeyInput.Text;
        Settings.GenerationPath = pathInput.Text;
        EditorUtility.SaveProjectSettings(Settings, "settings.json");
        Dialog.AskConfirm(() => {}, "Settings saved successfully.", "Success", "Okay");
    }

    [Menu("Editor", "AI Tools/Settings")]
    public static void OpenSettings()
    {
        var dialog = new Dialog();
        dialog.Window.Title = "Sound Generation Settings";
        dialog.Window.Size = new Vector2(500, 200);
        dialog.Layout = Layout.Column();
        dialog.Layout.Add(new SoundSettings());
        dialog.Show();
    }

    public static string GetGenerationPath()
    {
        return Path.Combine(Project.Current.GetAssetsPath(), Settings.GenerationPath);
    }
}
attackhelicopter.aitools / Editor/AIToolsMenu.cs
Editor library
using System.Linq;
using Editor;
using Sandbox;

public static class AIToolsMenu
{
	[Menu( "Editor", "AI Tools/Generate sound" )]
    public static void Generate()
    {
		if (SoundSettings.Settings.ElevenLabsApiKey.Length < 1)
        {
            Dialog.AskConfirm(() => {}, "Set your ElevenLabs API key first in settings.", "Error", "Okay");
            return;
        }

        var inspectorObject = EditorUtility.InspectorObject as Asset;
        if (inspectorObject == null)
        {
            Dialog.AskConfirm(() => {}, "Open SoundEvent in Inspector first.", "Error", "Okay");
            return;
        }

        var resource = inspectorObject.LoadResource();
        if (resource == null)
        {
            Dialog.AskConfirm(() => {}, "Failed to load resource.", "Error", "Okay");
            return;
        }

        if (!(resource is SoundEvent))
        {
            Dialog.AskConfirm(() => {}, "Selected asset must be a SoundEvent.", "Error", "Okay");
            return;
        }

        var soundEvent = resource as SoundEvent;
        SoundAddons.Generate(soundEvent, (soundFile) => {
            soundEvent.Sounds.Add(soundFile);
			EditorUtility.InspectorObject = null;
            EditorUtility.InspectorObject = inspectorObject;
        });
    }

[Menu("Editor", "AI Tools/Split sound")]
    public static void Split()
    {
        var asset = EditorUtility.InspectorObject as Asset;
        var soundEvent = asset.LoadResource() as SoundEvent;

        var dialog = new Dialog();
        dialog.Window.Title = "Split Sound";
        dialog.Window.Size = new Vector2(800, 400);

        dialog.Layout = Layout.Column();
        var mainLayout = dialog.Layout.AddColumn();
        mainLayout.Margin = 16f;

        var spectogramWidget = new SpectogramWidget(null);
        mainLayout.Add(spectogramWidget, 1);

        var controlsLayout = mainLayout.AddRow();
        var splitButton = new Button.Primary("Split", "content_cut");
        controlsLayout.Add(splitButton);

        splitButton.Clicked = () =>
        {
            if (spectogramWidget.CurrentSound == null)
            {
                Log.Error("No sound loaded to split");
                return;
            }

            spectogramWidget.SplitCurrentSound((soundFile) => {
                soundEvent.Sounds.Add(soundFile);
				EditorUtility.InspectorObject = null;
				EditorUtility.InspectorObject = asset;
            });
        };

        dialog.Show();
    }
}
Debug: View Raw JSON Response
{
    "TotalCount": 4,
    "Files": [
        {
            "Ident": "attackhelicopter.aitools",
            "Path": "Editor/SpectogramWidget.cs",
            "FileName": "SpectogramWidget.cs",
            "PackageType": "library",
            "CodeKind": "Editor",
            "AssetVersionId": 76432,
            "IsPrivate": false,
            "Code": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing System.IO;\r\nusing Editor;\r\nusing Sandbox;\r\nusing System.Text;\r\n\r\nclass SpectogramWidget : Widget \r\n{\r\n    private short[] samples;\r\n    private int sampleRate;\r\n    private List<int> splitPoints = new List<int>();\r\n    private int? dragPoint = null;\r\n    private Label loadingLabel;\r\n    private Label dropLabel;\r\n    private bool isLoading = true;\r\n    public SoundFile CurrentSound { get; private set; }\r\n\r\n    public SpectogramWidget(SoundFile soundFile) : base(null)\r\n    {\r\n        MinimumSize = 100;\r\n        MouseTracking = true;\r\n        AcceptDrops = true;\r\n\r\n        loadingLabel = new Label(this);\r\n        loadingLabel.Text = \"Loading audio data...\";\r\n        loadingLabel.Visible = false;\r\n        \r\n        dropLabel = new Label(this);\r\n        dropLabel.Text = \"Drop a sound file here\";\r\n        dropLabel.SetStyles(\"font-size: 18px; color: #aaa; text-align: center;\");\r\n        \r\n        if (soundFile != null)\r\n        {\r\n            LoadSound(soundFile);\r\n        }\r\n    }\r\n\r\n    public async void LoadSound(SoundFile soundFile)\r\n    {\r\n        CurrentSound = soundFile;\r\n        isLoading = true;\r\n        samples = null;\r\n        splitPoints.Clear();\r\n        \r\n        loadingLabel.Visible = true;\r\n        dropLabel.Visible = false;\r\n\r\n        await LoadAudioDataAsync(soundFile);\r\n    }\r\n\r\n    private async Task LoadAudioDataAsync(SoundFile soundFile)\r\n    {\r\n        try \r\n        {\r\n            await soundFile.LoadAsync();\r\n            samples = await soundFile.GetSamplesAsync();\r\n            \r\n            if (samples == null)\r\n            {\r\n                loadingLabel.Text = \"Failed to load audio data\";\r\n                return;\r\n            }\r\n\r\n            sampleRate = soundFile.Rate;\r\n            splitPoints.Add(0);\r\n            splitPoints.Add(samples.Length - 1);\r\n\r\n            loadingLabel.Visible = false;\r\n            isLoading = false;\r\n\r\n            Update();\r\n        }\r\n        catch (Exception ex)\r\n        {\r\n            loadingLabel.Text = $\"Error loading audio: {ex.Message}\";\r\n        }\r\n    }\r\n\r\n    protected override void DoLayout()\r\n    {\r\n        base.DoLayout();\r\n\r\n        if (loadingLabel != null)\r\n        {\r\n            loadingLabel.Position = new Vector2(10, Height / 2 - 10);\r\n            loadingLabel.Size = new Vector2(Width - 20, 20);\r\n        }\r\n\r\n        if (dropLabel != null)\r\n        {\r\n            dropLabel.Position = new Vector2(10, Height / 2 - 10);\r\n            dropLabel.Size = new Vector2(Width - 20, 20);\r\n        }\r\n    }\r\n\r\n    public override void OnDragDrop(DragEvent e)\r\n    {\r\n        base.OnDragDrop(e);\r\n\r\n        if (!e.Data.HasFileOrFolder) return;\r\n\r\n        var asset = AssetSystem.FindByPath(e.Data.FileOrFolder);\r\n        if (asset?.AssetType != AssetType.SoundFile) return;\r\n\r\n        var soundFile = SoundFile.Load(asset.Path);\r\n        if (soundFile != null)\r\n        {\r\n            LoadSound(soundFile);\r\n        }\r\n    }\r\n\r\n    public override void OnDragHover(DragEvent e)\r\n    {\r\n        base.OnDragHover(e);\r\n\r\n        if (!e.Data.HasFileOrFolder) return;\r\n\r\n        var asset = AssetSystem.FindByPath(e.Data.FileOrFolder);\r\n        if (asset?.AssetType != AssetType.SoundFile) return;\r\n\r\n        e.Action = DropAction.Link;\r\n    }\r\n\r\n    protected override void OnMouseClick(MouseEvent e)\r\n    {\r\n        if (isLoading) return;\r\n        \r\n        base.OnMouseClick(e);\r\n\r\n        if (e.Button == MouseButtons.Left)\r\n        {\r\n            var samplePos = (int)(e.LocalPosition.x / Width * samples.Length);\r\n            var nearPoint = splitPoints.FirstOrDefault(p => Math.Abs(p - samplePos) < (samples.Length / Width * 5));\r\n            \r\n            if (nearPoint != default)\r\n            {\r\n                dragPoint = splitPoints.IndexOf(nearPoint);\r\n            }\r\n            else\r\n            {\r\n                splitPoints.Add(samplePos);\r\n                splitPoints.Sort();\r\n                Update();\r\n            }\r\n        }\r\n    }\r\n\r\n    protected override void OnMouseMove(MouseEvent e)\r\n    {\r\n        if (isLoading) return;\r\n        \r\n        base.OnMouseMove(e);\r\n\r\n        if (dragPoint.HasValue)\r\n        {\r\n            var samplePos = (int)(e.LocalPosition.x / Width * samples.Length);\r\n            splitPoints[dragPoint.Value] = samplePos;\r\n            splitPoints.Sort();\r\n            Update();\r\n        }\r\n    }\r\n\r\n    protected override void OnMouseReleased(MouseEvent e)\r\n    {\r\n        if (isLoading) return;\r\n        \r\n        base.OnMouseReleased(e);\r\n        dragPoint = null;\r\n    }\r\n\r\n    protected override void OnPaint()\r\n    {\r\n        base.OnPaint();\r\n\r\n        if (isLoading || samples == null)\r\n        {\r\n            return;\r\n        }\r\n\r\n        Paint.ClearPen();\r\n        Paint.SetBrush(Theme.Grey.WithAlpha(0.1f));\r\n        Paint.DrawRect(LocalRect);\r\n\r\n        Paint.SetPen(Theme.Blue);\r\n        var samplesPerPixel = samples.Length / Width;\r\n        for (int x = 0; x < Width; x++)\r\n        {\r\n            var startSample = (int)(x * samplesPerPixel);\r\n            var endSample = Math.Min(startSample + samplesPerPixel, samples.Length);\r\n            \r\n            var max = short.MinValue;\r\n            var min = short.MaxValue;\r\n            \r\n            for (int i = startSample; i < endSample; i++)\r\n            {\r\n                max = Math.Max(max, samples[i]);\r\n                min = Math.Min(min, samples[i]);\r\n            }\r\n\r\n            var y1 = Height / 2 + (min / (float)short.MaxValue * Height / 2);\r\n            var y2 = Height / 2 + (max / (float)short.MaxValue * Height / 2);\r\n            \r\n            Paint.DrawLine(new Vector2(x, y1), new Vector2(x, y2));\r\n        }\r\n\r\n        Paint.SetPen(Theme.Red);\r\n        foreach (var point in splitPoints)\r\n        {\r\n            var x = point / (float)samples.Length * Width;\r\n            Paint.DrawLine(new Vector2(x, 0), new Vector2(x, Height));\r\n        }\r\n    }\r\n\r\n    public List<int> GetSplitPoints()\r\n    {\r\n        return new List<int>(splitPoints);\r\n    }\r\n\r\n    public void SplitCurrentSound(Action<SoundFile> onSoundCreated)\r\n    {\r\n        if (CurrentSound == null || samples == null) return;\r\n\r\n        var splitPoints = GetSplitPoints();\r\n        if (splitPoints.Count < 2) return;\r\n\r\n        try\r\n        {\r\n            var baseFileName = Path.GetFileNameWithoutExtension(CurrentSound.ResourcePath);\r\n            var outputDir = Path.Combine(\r\n                Project.Current.GetAssetsPath(),\r\n                \"generated\",\r\n                $\"{baseFileName}_splits\"\r\n            );\r\n            Directory.CreateDirectory(outputDir);\r\n\r\n            for (int i = 0; i < splitPoints.Count - 1; i++)\r\n            {\r\n                var start = splitPoints[i];\r\n                var end = splitPoints[i + 1];\r\n                var length = end - start;\r\n\r\n                var segmentSamples = new short[length];\r\n                Array.Copy(samples, start, segmentSamples, 0, length);\r\n\r\n                var wavPath = Path.Combine(outputDir, $\"{baseFileName}_part_{i + 1}.wav\");\r\n\r\n                using (var writer = new BinaryWriter(File.Create(wavPath)))\r\n                {\r\n                    writer.Write(Encoding.ASCII.GetBytes(\"RIFF\"));\r\n                    writer.Write(36 + (segmentSamples.Length * 2));\r\n                    writer.Write(Encoding.ASCII.GetBytes(\"WAVE\"));\r\n\r\n                    writer.Write(Encoding.ASCII.GetBytes(\"fmt \"));\r\n                    writer.Write(16);\r\n                    writer.Write((short)1);\r\n                    writer.Write((short)CurrentSound.Channels);\r\n                    writer.Write(CurrentSound.Rate);\r\n                    writer.Write(CurrentSound.Rate * CurrentSound.Channels * 2);\r\n                    writer.Write((short)(CurrentSound.Channels * 2));\r\n                    writer.Write((short)16);\r\n\r\n                    writer.Write(Encoding.ASCII.GetBytes(\"data\"));\r\n                    writer.Write(segmentSamples.Length * 2);\r\n\r\n                    foreach (var sample in segmentSamples)\r\n                    {\r\n                        writer.Write(sample);\r\n                    }\r\n                }\r\n\r\n                var asset = AssetSystem.RegisterFile(wavPath);\r\n                if (asset != null)\r\n                {\r\n                    var soundFile = SoundFile.Load(asset.RelativePath);\r\n                    if (soundFile != null)\r\n                    {\r\n                        onSoundCreated?.Invoke(soundFile);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        catch (Exception ex)\r\n        {\r\n            Log.Error($\"Error splitting sound: {ex.Message}\");\r\n        }\r\n    }\r\n}"
        },
        {
            "Ident": "attackhelicopter.aitools",
            "Path": "Editor/SoundAddons.cs",
            "FileName": "SoundAddons.cs",
            "PackageType": "library",
            "CodeKind": "Editor",
            "AssetVersionId": 76432,
            "IsPrivate": false,
            "Code": "using System;\r\nusing System.IO;\r\nusing System.Net.Http;\r\nusing System.Text;\r\nusing System.Text.Json;\r\nusing System.Reflection;\r\nusing Sandbox;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\n\r\nnamespace Editor;\r\n\r\npublic static class SoundAddons\r\n{\r\n\tprivate static string previousSoundGenerationPromp;\r\n\tprivate static string previousSoundGenerationDuration;\r\n\r\n\t// [ContextMenuFor( typeof( SoundFile ), \"Generate sound\", \"create\" )]\r\n\tpublic static void Generate( SoundEvent soundEvent, Action<SoundFile> finished )\r\n\t{\r\n\t\tstring defaultInput = !string.IsNullOrEmpty( previousSoundGenerationPromp )\r\n\t\t\t? previousSoundGenerationPromp\r\n\t\t\t: \"A dog barking\";\r\n\r\n\t\tDialog.AskString( ( description ) =>\r\n\t\t{\r\n\t\t\tpreviousSoundGenerationPromp = description;\r\n\r\n\t\t\tDialog.AskString( async ( durationStr ) =>\r\n\t\t\t{\r\n\t\t\t\tpreviousSoundGenerationDuration = durationStr;\r\n\t\t\t\tif ( !float.TryParse( durationStr, out float duration ) || duration < 0.5f || duration > 22f )\r\n\t\t\t\t{\r\n\t\t\t\t\tLog.Error( \"Duration must be between 0.5 and 22 seconds\" );\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\r\n\t\t\t\ttry\r\n\t\t\t\t{\r\n\t\t\t\t\tstring apiKey = SoundSettings.Settings.ElevenLabsApiKey;\r\n\t\t\t\t\tvar client = new HttpClient();\r\n\t\t\t\t\tclient.DefaultRequestHeaders.Add( \"xi-api-key\", apiKey );\r\n\r\n\t\t\t\t\tvar content = new StringContent( JsonSerializer.Serialize( new\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttext = description,\r\n\t\t\t\t\t\tduration_seconds = duration,\r\n\t\t\t\t\t\tprompt_influence = 0.3\r\n\t\t\t\t\t} ), Encoding.UTF8, \"application/json\" );\r\n\r\n\t\t\t\t\tvar response = await client.PostAsync(\r\n\t\t\t\t\t\t\"https://api.elevenlabs.io/v1/sound-generation\",\r\n\t\t\t\t\t\tcontent\r\n\t\t\t\t\t);\r\n\r\n\t\t\t\t\tif ( response.IsSuccessStatusCode )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar bytes = await response.Content.ReadAsByteArrayAsync();\r\n\r\n\t\t\t\t\t\tstring safeFileName = description.Replace( \" \", \"_\" ).Replace( \"/\", \"_\" ).Replace( \"\\\\\", \"_\" );\r\n\t\t\t\t\t\tvar mp3Path = Path.Combine(\r\n\t\t\t\t\t\t\tSoundSettings.Settings.GenerationPath,\r\n\t\t\t\t\t\t\t$\"{safeFileName}_{DateTime.Now:yyyyMMddHHmmss}.mp3\"\r\n\t\t\t\t\t\t);\r\n\r\n\t\t\t\t\t\tvar fullMp3Path = Path.Combine( Project.Current.GetAssetsPath(), mp3Path );\r\n\t\t\t\t\t\tDirectory.CreateDirectory( Path.GetDirectoryName( fullMp3Path ) );\r\n\r\n\t\t\t\t\t\tawait File.WriteAllBytesAsync( fullMp3Path, bytes );\r\n\t\t\t\t\t\tLog.Info( \"File saved to: \" + fullMp3Path );\r\n\r\n\t\t\t\t\t\tvar mp3Asset = AssetSystem.RegisterFile( fullMp3Path );\r\n\t\t\t\t\t\tif ( mp3Asset != null )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tvar soundFile = SoundFile.Load( mp3Asset.RelativePath );\r\n\t\t\t\t\t\t\tif ( soundFile != null )\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tfinished(soundFile);\r\n\t\t\t\t\t\t\t\tLog.Info( $\"Successfully generated and linked sound: {mp3Asset.RelativePath}\" );\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tLog.Error( \"Failed to load generated sound file\" );\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tLog.Error( \"Failed to register generated mp3 file as asset\" );\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar errorContent = await response.Content.ReadAsStringAsync();\r\n\t\t\t\t\t\tLog.Error( $\"Failed to generate sound: {response.StatusCode}. Error: {errorContent}\" );\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tcatch ( Exception ex )\r\n\t\t\t\t{\r\n\t\t\t\t\tLog.Error( $\"Error generating sound: {ex.Message}\" );\r\n\t\t\t\t}\r\n\t\t\t},\r\n\t\t\t\"Enter duration in seconds (0.5 to 22):\",\r\n\t\t\t\"Generate\",\r\n\t\t\t\"Cancel\",\r\n\t\t\tpreviousSoundGenerationDuration,\r\n\t\t\t\"Set Sound Duration\" );\r\n\t\t},\r\n\t\t\"Describe the sound effect to generate:\",\r\n\t\t\"Next\",\r\n\t\t\"Cancel\",\r\n\t\tdefaultInput,\r\n\t\t\"Generate Sound Effect\" );\r\n\t}\r\n\r\n\t// [ContextMenuFor( typeof( SoundFile ), \"Split sound\", \"content_cut\" )]\r\n\tpublic static async void SplitSound( Widget parent, SerializedProperty property )\r\n\t{\r\n\t\tvar soundResource = property.GetValue<SoundFile>();\r\n\t\tif ( soundResource == null || !soundResource.IsValid )\r\n\t\t{\r\n\t\t\tLog.Error( \"No valid sound file selected\" );\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tvar dialog = new Dialog();\r\n\t\tdialog.Window.Title = \"Split Sound\";\r\n\t\tdialog.Window.Size = new Vector2( 800, 400 );\r\n\r\n\t\tdialog.Layout = Layout.Column();\r\n\t\tvar mainLayout = dialog.Layout.AddColumn();\r\n\t\tmainLayout.Margin = 16f;\r\n\r\n\t\tvar loadingLabel = new Label( \"Loading audio data...\" );\r\n\t\tmainLayout.Add( loadingLabel );\r\n\r\n\t\tdialog.Show();\r\n\r\n\t\tvar samples = await soundResource.GetSamplesAsync();\r\n\t\tif ( samples == null )\r\n\t\t{\r\n\t\t\tloadingLabel.Text = \"Failed to load audio samples\";\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tloadingLabel.Destroy();\r\n\r\n\t\tvar spectogramWidget = new SpectogramWidget( soundResource );\r\n\t\tmainLayout.Add( spectogramWidget, 1 );\r\n\r\n\t\tvar controlsLayout = mainLayout.AddRow();\r\n\r\n\t\tvar splitButton = new Button.Primary( \"Split\", \"content_cut\" );\r\n\t\tcontrolsLayout.Add( splitButton );\r\n\r\n\t\tsplitButton.Clicked = () =>\r\n\t\t{\r\n\t\t\tvar splitPoints = spectogramWidget.GetSplitPoints();\r\n\t\t\tif ( splitPoints.Count < 1 ) return;\r\n\r\n\t\t\tvar listControlWidget = parent.GetAncestor<ListControlWidget>();\r\n\t\t\tif ( listControlWidget == null )\r\n\t\t\t{\r\n\t\t\t\tLog.Error( \"Could not find ListControlWidget parent\" );\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tvar baseFileName = Path.GetFileNameWithoutExtension( soundResource.ResourcePath );\r\n\t\t\t\tvar outputDir = Path.Combine(\r\n\t\t\t\t\tProject.Current.GetAssetsPath(),\r\n\t\t\t\t\t\"generated\",\r\n\t\t\t\t\t$\"{baseFileName}_splits\"\r\n\t\t\t\t);\r\n\t\t\t\tDirectory.CreateDirectory( outputDir );\r\n\r\n\t\t\t\tvar newSoundFiles = new List<SoundFile>();\r\n\r\n\t\t\t\tfor ( int i = 0; i < splitPoints.Count - 1; i++ )\r\n\t\t\t\t{\r\n\t\t\t\t\tvar start = splitPoints[i];\r\n\t\t\t\t\tvar end = splitPoints[i + 1];\r\n\t\t\t\t\tvar length = end - start;\r\n\r\n\t\t\t\t\tvar segmentSamples = new short[length];\r\n\t\t\t\t\tArray.Copy( samples, start, segmentSamples, 0, length );\r\n\r\n\t\t\t\t\tvar wavPath = Path.Combine( outputDir, $\"{baseFileName}_part_{i + 1}.wav\" );\r\n\r\n\t\t\t\t\tusing ( var writer = new BinaryWriter( File.Create( wavPath ) ) )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\twriter.Write( Encoding.ASCII.GetBytes( \"RIFF\" ) );\r\n\t\t\t\t\t\twriter.Write( 36 + (segmentSamples.Length * 2) );\r\n\t\t\t\t\t\twriter.Write( Encoding.ASCII.GetBytes( \"WAVE\" ) );\r\n\r\n\t\t\t\t\t\twriter.Write( Encoding.ASCII.GetBytes( \"fmt \" ) );\r\n\t\t\t\t\t\twriter.Write( 16 );\r\n\t\t\t\t\t\twriter.Write( (short)1 );\r\n\t\t\t\t\t\twriter.Write( (short)soundResource.Channels );\r\n\t\t\t\t\t\twriter.Write( soundResource.Rate );\r\n\t\t\t\t\t\twriter.Write( soundResource.Rate * soundResource.Channels * 2 );\r\n\t\t\t\t\t\twriter.Write( (short)(soundResource.Channels * 2) );\r\n\t\t\t\t\t\twriter.Write( (short)16 );\r\n\r\n\t\t\t\t\t\twriter.Write( Encoding.ASCII.GetBytes( \"data\" ) );\r\n\t\t\t\t\t\twriter.Write( segmentSamples.Length * 2 );\r\n\r\n\t\t\t\t\t\tforeach ( var sample in segmentSamples )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\twriter.Write( sample );\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tvar asset = AssetSystem.RegisterFile( wavPath );\r\n\t\t\t\t\tif ( asset != null )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar soundFile = SoundFile.Load( asset.RelativePath );\r\n\t\t\t\t\t\tif ( soundFile != null )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tnewSoundFiles.Add( soundFile );\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif ( newSoundFiles.Count > 0 )\r\n\t\t\t\t{\r\n\t\t\t\t\ttry\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar collectionField = listControlWidget.GetType()\r\n\t\t\t\t\t\t\t.GetFields( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public )\r\n\t\t\t\t\t\t\t.FirstOrDefault( f => typeof( SerializedCollection ).IsAssignableFrom( f.FieldType ) );\r\n\r\n\t\t\t\t\t\tif ( collectionField == null )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\treturn;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tvar collection = collectionField.GetValue( listControlWidget ) as SerializedCollection;\r\n\t\t\t\t\t\tif ( collection == null )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\treturn;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tif ( property.TryGetAsObject( out var original ) )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tforeach ( var soundFile in newSoundFiles )\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tcollection.Add( soundFile );\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tcollection.Remove( property );\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcatch ( Exception ex )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tLog.Error( $\"Error manipulating collection: {ex.Message}\" );\r\n\t\t\t\t\t\tLog.Error( $\"Stack trace: {ex.StackTrace}\" );\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tdialog.Close();\r\n\t\t\t}\r\n\t\t\tcatch ( Exception ex )\r\n\t\t\t{\r\n\t\t\t\tLog.Error( $\"Error splitting sound: {ex.Message}\" );\r\n\t\t\t}\r\n\t\t};\r\n\t}\r\n}"
        },
        {
            "Ident": "attackhelicopter.aitools",
            "Path": "Editor/SoundSettings.cs",
            "FileName": "SoundSettings.cs",
            "PackageType": "library",
            "CodeKind": "Editor",
            "AssetVersionId": 76432,
            "IsPrivate": false,
            "Code": "using System;\r\nusing System.IO;\r\nusing System.Text.Json;\r\nusing Editor;\r\nusing Sandbox;\r\n\r\npublic class SoundSettings : Widget\r\n{\r\n    public class Config : ConfigData\r\n    {\r\n        public string ElevenLabsApiKey { get; set; } = \"\";\r\n        public string GenerationPath { get; set; } = \"generated\";\r\n    }\r\n\r\n    private LineEdit apiKeyInput;\r\n    private LineEdit pathInput;\r\n    private static Config _settings;\r\n    private Button saveButton;\r\n\r\n    public static Config Settings \r\n    {\r\n        get\r\n        {\r\n            if (_settings == null)\r\n                _settings = EditorUtility.LoadProjectSettings<Config>(\"settings.json\");\r\n            return _settings;\r\n        }\r\n    }\r\n\r\n    public SoundSettings() : base(null)\r\n    {\r\n        Layout = Layout.Column();\r\n        var mainLayout = Layout.Add(Layout.Column());\r\n        mainLayout.Margin = 16;\r\n\r\n        var apiKeyGroup = mainLayout.AddRow();\r\n        apiKeyGroup.Add(new Label(\"ElevenLabs API Key:\"));\r\n        \r\n        apiKeyInput = new LineEdit(this);\r\n        apiKeyInput.MinimumWidth = 300;\r\n        apiKeyInput.PlaceholderText = \"Enter your ElevenLabs API key\";\r\n        apiKeyInput.Text = Settings.ElevenLabsApiKey;\r\n        apiKeyGroup.Add(apiKeyInput);\r\n\r\n        mainLayout.AddSpacingCell(8);\r\n\r\n        var pathGroup = mainLayout.AddRow();\r\n        pathGroup.Add(new Label(\"Generation Path:\"));\r\n        \r\n        pathInput = new LineEdit(this);\r\n        pathInput.MinimumWidth = 300;\r\n        pathInput.PlaceholderText = \"Enter generation path (relative to assets)\";\r\n        pathInput.Text = Settings.GenerationPath;\r\n        pathGroup.Add(pathInput);\r\n\r\n        mainLayout.AddStretchCell();\r\n\r\n        var buttonLayout = mainLayout.AddRow();\r\n        buttonLayout.AddStretchCell();\r\n\r\n        saveButton = new Button.Primary(\"Save\", \"save\");\r\n        saveButton.Clicked = SaveSettings;\r\n        buttonLayout.Add(saveButton);\r\n    }\r\n\r\n    private void SaveSettings()\r\n    {\r\n        Settings.ElevenLabsApiKey = apiKeyInput.Text;\r\n        Settings.GenerationPath = pathInput.Text;\r\n        EditorUtility.SaveProjectSettings(Settings, \"settings.json\");\r\n        Dialog.AskConfirm(() => {}, \"Settings saved successfully.\", \"Success\", \"Okay\");\r\n    }\r\n\r\n    [Menu(\"Editor\", \"AI Tools/Settings\")]\r\n    public static void OpenSettings()\r\n    {\r\n        var dialog = new Dialog();\r\n        dialog.Window.Title = \"Sound Generation Settings\";\r\n        dialog.Window.Size = new Vector2(500, 200);\r\n        dialog.Layout = Layout.Column();\r\n        dialog.Layout.Add(new SoundSettings());\r\n        dialog.Show();\r\n    }\r\n\r\n    public static string GetGenerationPath()\r\n    {\r\n        return Path.Combine(Project.Current.GetAssetsPath(), Settings.GenerationPath);\r\n    }\r\n}"
        },
        {
            "Ident": "attackhelicopter.aitools",
            "Path": "Editor/AIToolsMenu.cs",
            "FileName": "AIToolsMenu.cs",
            "PackageType": "library",
            "CodeKind": "Editor",
            "AssetVersionId": 76432,
            "IsPrivate": false,
            "Code": "using System.Linq;\r\nusing Editor;\r\nusing Sandbox;\r\n\r\npublic static class AIToolsMenu\r\n{\r\n\t[Menu( \"Editor\", \"AI Tools/Generate sound\" )]\r\n    public static void Generate()\r\n    {\r\n\t\tif (SoundSettings.Settings.ElevenLabsApiKey.Length < 1)\r\n        {\r\n            Dialog.AskConfirm(() => {}, \"Set your ElevenLabs API key first in settings.\", \"Error\", \"Okay\");\r\n            return;\r\n        }\r\n\r\n        var inspectorObject = EditorUtility.InspectorObject as Asset;\r\n        if (inspectorObject == null)\r\n        {\r\n            Dialog.AskConfirm(() => {}, \"Open SoundEvent in Inspector first.\", \"Error\", \"Okay\");\r\n            return;\r\n        }\r\n\r\n        var resource = inspectorObject.LoadResource();\r\n        if (resource == null)\r\n        {\r\n            Dialog.AskConfirm(() => {}, \"Failed to load resource.\", \"Error\", \"Okay\");\r\n            return;\r\n        }\r\n\r\n        if (!(resource is SoundEvent))\r\n        {\r\n            Dialog.AskConfirm(() => {}, \"Selected asset must be a SoundEvent.\", \"Error\", \"Okay\");\r\n            return;\r\n        }\r\n\r\n        var soundEvent = resource as SoundEvent;\r\n        SoundAddons.Generate(soundEvent, (soundFile) => {\r\n            soundEvent.Sounds.Add(soundFile);\r\n\t\t\tEditorUtility.InspectorObject = null;\r\n            EditorUtility.InspectorObject = inspectorObject;\r\n        });\r\n    }\r\n\r\n[Menu(\"Editor\", \"AI Tools/Split sound\")]\r\n    public static void Split()\r\n    {\r\n        var asset = EditorUtility.InspectorObject as Asset;\r\n        var soundEvent = asset.LoadResource() as SoundEvent;\r\n\r\n        var dialog = new Dialog();\r\n        dialog.Window.Title = \"Split Sound\";\r\n        dialog.Window.Size = new Vector2(800, 400);\r\n\r\n        dialog.Layout = Layout.Column();\r\n        var mainLayout = dialog.Layout.AddColumn();\r\n        mainLayout.Margin = 16f;\r\n\r\n        var spectogramWidget = new SpectogramWidget(null);\r\n        mainLayout.Add(spectogramWidget, 1);\r\n\r\n        var controlsLayout = mainLayout.AddRow();\r\n        var splitButton = new Button.Primary(\"Split\", \"content_cut\");\r\n        controlsLayout.Add(splitButton);\r\n\r\n        splitButton.Clicked = () =>\r\n        {\r\n            if (spectogramWidget.CurrentSound == null)\r\n            {\r\n                Log.Error(\"No sound loaded to split\");\r\n                return;\r\n            }\r\n\r\n            spectogramWidget.SplitCurrentSound((soundFile) => {\r\n                soundEvent.Sounds.Add(soundFile);\r\n\t\t\t\tEditorUtility.InspectorObject = null;\r\n\t\t\t\tEditorUtility.InspectorObject = asset;\r\n            });\r\n        };\r\n\r\n        dialog.Show();\r\n    }\r\n}\r\n"
        }
    ]
}