hi I need you to please add the ability to show a play button and stop button whenever the generation is complete so that I can play the generation on the editor window, please provide full script tysm.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine.Networking;
using Unity.EditorCoroutines.Editor;

public class AceStepEditorWindow : EditorWindow
{
    private Texture2D logoTexture;

    // The input object
    private ACEStepInput input = new ACEStepInput()
    {
        checkpoint_path = "",
        bf16 = true,
        torch_compile = false,
        device_id = 0,
        output_path = "",
        audio_duration = 10f,
        prompt = "",
        lyrics = "",
        infer_step = 50,
        guidance_scale = 7.5f,
        scheduler_type = "euler",
        cfg_type = "apg",
        omega_scale = 0f,
        actual_seeds = new List<int>() { 42 },
        guidance_interval = 1f,
        guidance_interval_decay = 0f,
        min_guidance_scale = 0f,
        use_erg_tag = false,
        use_erg_lyric = false,
        use_erg_diffusion = false,
        oss_steps = new List<int>() { 1, 2, 3 },
        guidance_scale_text = 0f,
        guidance_scale_lyric = 0f,
    };

    // Server URL (base URL - no trailing slash)
    private string server_url = "http://localhost:8000";

    // Generated audio variables
    private string generatedAudioPath = "";
    private AudioClip generatedClip;
    private bool isPlaying = false;
    private float playPosition = 0f;

    private Vector2 scrollPos;

    private bool isGenerating = false;
    private string generateStatus = "";

    private ACEStepClientGenerationRunner clientRunner;

    private Rect dragDropAreaRect;

    [MenuItem("Tools/AceStep")]
    public static void ShowWindow()
    {
        var window = GetWindow<AceStepEditorWindow>();
        window.titleContent = new GUIContent("AceStep");
        window.minSize = new Vector2(600, 700);
        window.Show();
    }

    private void OnEnable()
    {
        logoTexture = Resources.Load<Texture2D>("AceStepLogo");
        clientRunner = new ACEStepClientGenerationRunner(this);
        EditorApplication.update += UpdateAudioPlayback;
    }

    private void OnDisable()
    {
        EditorApplication.update -= UpdateAudioPlayback;
        StopAudio();
        if (generatedClip != null)
            DestroyImmediate(generatedClip, true);
    }

    void OnGUI()
    {
        DrawLogo();

        scrollPos = EditorGUILayout.BeginScrollView(scrollPos);

        GUILayout.Space(10);
        DrawFolderDragDropArea();

        GUILayout.Space(10);

        EditorGUILayout.LabelField("Server URL (no trailing slash)", EditorStyles.boldLabel);
        server_url = EditorGUILayout.TextField(server_url);

        GUILayout.Space(10);

        EditorGUILayout.LabelField("ACEStep Parameters", EditorStyles.boldLabel);

        DrawInputFields();

        GUILayout.Space(20);

        EditorGUI.BeginDisabledGroup(isGenerating);
        if (GUILayout.Button("GENERATE SONG", GUILayout.Height(40)))
        {
            if (string.IsNullOrEmpty(input.output_path))
            {
                EditorUtility.DisplayDialog("Error", "Please drag & drop an output folder before generating.", "OK");
            }
            else
            {
                generateStatus = "Starting generation...";
                isGenerating = true;
                generatedAudioPath = "";
                generatedClip = null;
                clientRunner.GenerateAudio(input, OnGenerationSuccess, OnGenerationError);
            }
        }
        EditorGUI.EndDisabledGroup();

        GUILayout.Space(10);

        if (isGenerating)
        {
            EditorGUILayout.HelpBox(generateStatus, MessageType.Info);
        }
        else if (!string.IsNullOrEmpty(generatedAudioPath) && generatedClip != null)
        {
            DrawAudioPlaybackControls();
        }
        else if (!string.IsNullOrEmpty(generateStatus))
        {
            EditorGUILayout.HelpBox(generateStatus, MessageType.Info);
        }

        EditorGUILayout.EndScrollView();
    }

    private void DrawLogo()
    {
        if (logoTexture != null)
        {
            float maxWidth = position.width * 0.8f;
            float ratio = (float)logoTexture.height / logoTexture.width;
            float width = Mathf.Min(maxWidth, logoTexture.width);
            float height = width * ratio;

            GUILayout.Space(10);
            Rect rect = GUILayoutUtility.GetRect(width, height, GUILayout.ExpandWidth(false));
            rect.x = (position.width - width) / 2;
            EditorGUI.DrawPreviewTexture(rect, logoTexture, null, ScaleMode.ScaleToFit);
            GUILayout.Space(10);
        }
        else
        {
            EditorGUILayout.HelpBox("Logo 'AceStepLogo.png' not found in Resources folder.", MessageType.Warning);
        }
    }

    private void DrawFolderDragDropArea()
    {
        GUILayout.Label("Output Folder (drag & drop your Unity folder here)", EditorStyles.boldLabel);

        dragDropAreaRect = GUILayoutUtility.GetRect(0, 50, GUILayout.ExpandWidth(true));
        GUI.Box(dragDropAreaRect, string.IsNullOrEmpty(input.output_path) ?
            "Drag & Drop Output Folder Here" : $"Output Folder:\n{input.output_path}", EditorStyles.helpBox);

        Event evt = Event.current;

        if ((evt.type == EventType.DragUpdated || evt.type == EventType.DragPerform) && dragDropAreaRect.Contains(evt.mousePosition))
        {
            DragAndDrop.visualMode = DragAndDropVisualMode.Copy;

            if (evt.type == EventType.DragPerform)
            {
                DragAndDrop.AcceptDrag();

                foreach (var draggedObject in DragAndDrop.objectReferences)
                {
                    string path = AssetDatabase.GetAssetPath(draggedObject);
                    if (!string.IsNullOrEmpty(path) && AssetDatabase.IsValidFolder(path))
                    {
                        input.output_path = path;
                        GUI.FocusControl(null);
                        break;
                    }
                }
                evt.Use();
            }
        }
    }

    private void DrawInputFields()
    {
        input.checkpoint_path = EditorGUILayout.TextField("Checkpoint Path", input.checkpoint_path);

        input.bf16 = EditorGUILayout.Toggle("bf16", input.bf16);
        input.torch_compile = EditorGUILayout.Toggle("torch_compile", input.torch_compile);
        input.device_id = EditorGUILayout.IntField("device_id", input.device_id);

        input.audio_duration = EditorGUILayout.FloatField("audio_duration", input.audio_duration);

        EditorGUILayout.LabelField("Prompt");
        input.prompt = EditorGUILayout.TextArea(input.prompt, GUILayout.Height(60));

        EditorGUILayout.LabelField("Lyrics");
        input.lyrics = EditorGUILayout.TextArea(input.lyrics, GUILayout.Height(60));

        input.infer_step = EditorGUILayout.IntField("infer_step", input.infer_step);
        input.guidance_scale = EditorGUILayout.FloatField("guidance_scale", input.guidance_scale);

        string[] schedulerOptions = new string[] { "euler", "heun" };
        int schedulerSelectedIndex = Array.IndexOf(schedulerOptions, input.scheduler_type);
        if (schedulerSelectedIndex < 0) schedulerSelectedIndex = 0;

        schedulerSelectedIndex = EditorGUILayout.Popup("scheduler_type", schedulerSelectedIndex, schedulerOptions);
        input.scheduler_type = schedulerOptions[schedulerSelectedIndex];

        string[] cfgOptions = new string[] { "cfg", "apg", "cfg_star" };
        int cfgSelectedIndex = Array.IndexOf(cfgOptions, input.cfg_type);
        if (cfgSelectedIndex < 0) cfgSelectedIndex = 1;

        cfgSelectedIndex = EditorGUILayout.Popup("cfg_type", cfgSelectedIndex, cfgOptions);
        input.cfg_type = cfgOptions[cfgSelectedIndex];

        input.omega_scale = EditorGUILayout.FloatField("omega_scale", input.omega_scale);

        DrawIntListField("actual_seeds", input.actual_seeds);

        input.guidance_interval = EditorGUILayout.FloatField("guidance_interval", input.guidance_interval);
        input.guidance_interval_decay = EditorGUILayout.FloatField("guidance_interval_decay", input.guidance_interval_decay);
        input.min_guidance_scale = EditorGUILayout.FloatField("min_guidance_scale", input.min_guidance_scale);

        input.use_erg_tag = EditorGUILayout.Toggle("use_erg_tag", input.use_erg_tag);
        input.use_erg_lyric = EditorGUILayout.Toggle("use_erg_lyric", input.use_erg_lyric);
        input.use_erg_diffusion = EditorGUILayout.Toggle("use_erg_diffusion", input.use_erg_diffusion);

        DrawIntListField("oss_steps", input.oss_steps);

        input.guidance_scale_text = EditorGUILayout.FloatField("guidance_scale_text", input.guidance_scale_text);
        input.guidance_scale_lyric = EditorGUILayout.FloatField("guidance_scale_lyric", input.guidance_scale_lyric);
    }

    private void DrawIntListField(string label, List<int> list)
    {
        EditorGUILayout.LabelField(label);
        if (list == null) list = new List<int>();
        EditorGUI.indentLevel++;

        int removeIndex = -1;
        for (int i = 0; i < list.Count; i++)
        {
            EditorGUILayout.BeginHorizontal();
            list[i] = EditorGUILayout.IntField(list[i]);
            if (GUILayout.Button("-", GUILayout.Width(24)))
            {
                removeIndex = i;
            }
            EditorGUILayout.EndHorizontal();
        }
        if (removeIndex >= 0)
            list.RemoveAt(removeIndex);

        if (GUILayout.Button("+ Add " + label))
            list.Add(0);

        EditorGUI.indentLevel--;
    }

    private void OnGenerationSuccess(ACEStepOutput output)
    {
        generateStatus = "Generation succeeded! Downloading file...";
        EditorCoroutineUtility.StartCoroutineOwnerless(DownloadAndSaveAudio(output.output_path));
    }

    private IEnumerator DownloadAndSaveAudio(string filename)
    {
        if (string.IsNullOrEmpty(filename))
        {
            generateStatus = "Error: No filename received from server.";
            isGenerating = false;
            Repaint();
            yield break;
        }

        string downloadUrl = $"{server_url.TrimEnd('/')}/files/{filename}";

        UnityWebRequest www = UnityWebRequest.Get(downloadUrl);
        yield return www.SendWebRequest();

#if UNITY_2020_1_OR_NEWER
        bool error = www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError;
#else
        bool error = www.isNetworkError || www.isHttpError;
#endif

        if (error)
        {
            generateStatus = $"Failed to download generated audio: {www.error}";
            isGenerating = false;
            Repaint();
            yield break;
        }

        byte[] audioBytes = www.downloadHandler.data;

        string localFolder = input.output_path;
        if (string.IsNullOrEmpty(localFolder) || !Directory.Exists(localFolder))
        {
            generateStatus = $"Invalid output folder selected: {localFolder}";
            isGenerating = false;
            Repaint();
            yield break;
        }

        string localPath = Path.Combine(localFolder, filename);

        try
        {
            File.WriteAllBytes(localPath, audioBytes);
            AssetDatabase.Refresh();

            generatedAudioPath = localPath;
            generateStatus = $"Saved audio to: {localPath}";

            LoadAudioClipFromPath(localPath);

            EditorApplication.delayCall += () =>
            {
                SelectGeneratedAudioAsset();
                Repaint();
            };
        }
        catch (Exception e)
        {
            generateStatus = $"Error saving file: {e.Message}";
        }
        finally
        {
            isGenerating = false;
            Repaint();
        }
    }

    private void LoadAudioClipFromPath(string fullPath)
    {
        if (!fullPath.StartsWith(Application.dataPath))
        {
            Debug.LogWarning("Generated audio file is outside the Assets folder and cannot be loaded.");
            generatedClip = null;
            return;
        }

        string relativePath = "Assets" + fullPath.Substring(Application.dataPath.Length);
        generatedAudioPath = relativePath;

        generatedClip = AssetDatabase.LoadAssetAtPath<AudioClip>(relativePath);

        if (generatedClip == null)
            generateStatus = "Failed to load audio clip from saved file.";
        else
        {
            generateStatus = "Audio clip loaded. You can now play it.";
            isPlaying = false;
            playPosition = 0f;
        }
    }

    private void SelectGeneratedAudioAsset()
    {
        if (string.IsNullOrEmpty(generatedAudioPath))
            return;

        UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(generatedAudioPath);
        if (obj != null)
        {
            Selection.activeObject = obj;
            EditorGUIUtility.PingObject(obj);
        }
    }

    private void OnGenerationError(string errorMsg)
    {
        generateStatus = "Generation failed: " + errorMsg;
        isGenerating = false;
        Repaint();
    }

    private void DrawAudioPlaybackControls()
    {
        GUILayout.Label("Generated Audio Playback", EditorStyles.boldLabel);

        EditorGUILayout.BeginHorizontal();
        if (!isPlaying)
        {
            if (GUILayout.Button("Play"))
                PlayAudio();
        }
        else
        {
            if (GUILayout.Button("Stop"))
                StopAudio();
        }
        EditorGUILayout.EndHorizontal();

        if (generatedClip == null) return;

        float newPos = EditorGUILayout.Slider(playPosition, 0f, generatedClip.length);

        if (Math.Abs(newPos - playPosition) > 0.01f)
        {
            playPosition = newPos;
            if (isPlaying)
            {
                StopAudio();
                PlayAudio();
            }
        }

        if (GUILayout.Button("Locate Generated Audio in Project"))
        {
            UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(generatedAudioPath);
            if (obj != null)
            {
                EditorGUIUtility.PingObject(obj);
                Selection.activeObject = obj;
            }
            else
            {
                EditorUtility.DisplayDialog("Locate File", "Could not find generated audio clip in the project.", "OK");
            }
        }
    }

    private void PlayAudio()
    {
        if (generatedClip == null)
            return;

        isPlaying = true;
        EditorApplication.update += EditorUpdateAudioPosition;

        PlayClip(generatedClip);
    }

    private void EditorUpdateAudioPosition()
    {
        if (!isPlaying) return;

        playPosition += Time.deltaTime;

        if (playPosition >= generatedClip.length)
            StopAudio();

        Repaint();
    }

    private void StopAudio()
    {
        if (!isPlaying)
            return;

        isPlaying = false;
        EditorApplication.update -= EditorUpdateAudioPosition;

        StopClip(generatedClip);

        playPosition = 0f;
        Repaint();
    }

    private void UpdateAudioPlayback()
    {
        if (isPlaying)
            Repaint();
    }

    private static class AudioUtil
    {
        private static readonly Type audioUtilType;
        private static readonly System.Reflection.MethodInfo playClipMethod;
        private static readonly System.Reflection.MethodInfo stopClipMethod;

        static AudioUtil()
        {
            audioUtilType = typeof(AudioImporter).Assembly.GetType("UnityEditor.AudioUtil");
            playClipMethod = audioUtilType.GetMethod("PlayClip", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic, null, new Type[] { typeof(AudioClip) }, null);
            stopClipMethod = audioUtilType.GetMethod("StopClip", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic, null, new Type[] { typeof(AudioClip) }, null);
        }

        public static void PlayClip(AudioClip clip)
        {
            playClipMethod?.Invoke(null, new object[] { clip });
        }

        public static void StopClip(AudioClip clip)
        {
            stopClipMethod?.Invoke(null, new object[] { clip });
        }
    }

    private void PlayClip(AudioClip clip) => AudioUtil.PlayClip(clip);
    private void StopClip(AudioClip clip) => AudioUtil.StopClip(clip);

    class ACEStepClientGenerationRunner : MonoBehaviour
    {
        private static ACEStepClientGenerationRunner _instance;

        private ACEStepClient client;

        public ACEStepClientGenerationRunner(AceStepEditorWindow window)
        {
            if (_instance == null)
            {
                GameObject go = new GameObject("ACEStepClientGenerationRunner");
                go.hideFlags = HideFlags.HideAndDontSave;
                _instance = go.AddComponent<ACEStepClientGenerationRunner>();
            }
            client = _instance.gameObject.GetComponent<ACEStepClient>();
            if (client == null)
                client = _instance.gameObject.AddComponent<ACEStepClient>();
        }

        public void GenerateAudio(ACEStepInput input, Action<ACEStepOutput> onSuccess, Action<string> onError)
        {
            _instance.StartCoroutine(client.GenerateAudioRoutine(input, onSuccess, onError));
        }
    }
}