idk what's going on but my code works fine on windows but when I run it on android it doesn't

using System;
using System.IO;
using UnityEngine;
using UnityEngine.EventSystems;
using System.Text;

#if UNITY_ANDROID
using UnityEngine.Android;
#endif

public class AudioRecorderWithButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
    [Tooltip("Output file folder - if empty, defaults to persistentDataPath/AudioOut")]
    public string audioFileOutPath;

    private AudioClip recordingClip;
    private bool isRecording = false;
    private string microphoneDevice;
    private int sampleRate = 16000;

    public OllamaInfer OIF;
    public FastAPIAudioTranscriber transcriber;

    private void Start()
    {
        // Default output folder if not set
        if (string.IsNullOrEmpty(audioFileOutPath))
        {
            audioFileOutPath = Path.Combine(Application.persistentDataPath, "AudioOut");
        }

#if UNITY_ANDROID
        if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
        {
            Permission.RequestUserPermission(Permission.Microphone);
        }
#endif
    }

    // Called when pointer (mouse or touch) presses down on this UI element (button)
    public void OnPointerDown(PointerEventData eventData)
    {
        if (transcriber != null)
            transcriber.AgentTalkMode = true;

        Debug.Log("Button pressed - starting recording...");
        StartRecording();
    }

    // Called when pointer (mouse or touch) releases on this UI element (button)
    public void OnPointerUp(PointerEventData eventData)
    {
        Debug.Log("Button released - stopping recording...");
        StopAndSaveRecording();
    }

    public void StartRecording()
    {
#if UNITY_ANDROID
        if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
        {
            Debug.LogWarning("Microphone permission not granted.");
            return;
        }
#endif

        microphoneDevice = Microphone.devices.Length > 0 ? Microphone.devices[0] : null;
        if (microphoneDevice == null)
        {
            Debug.LogError("No microphone devices found.");
            return;
        }

        Debug.Log("Starting recording using device: " + microphoneDevice);

        // Start a max 300 seconds recording; looping false
        recordingClip = Microphone.Start(microphoneDevice, false, 300, sampleRate);

        if (recordingClip == null)
        {
            Debug.LogError("Failed to start recording.");
            return;
        }

        isRecording = true;
    }

    public void StopAndSaveRecording()
    {
        if (!isRecording)
        {
            Debug.LogWarning("StopAndSaveRecording called but not currently recording.");
            return;
        }

        // Get actual recorded length BEFORE ending the recording!
        int position = Microphone.GetPosition(microphoneDevice);
        if (position <= 0)
        {
            Debug.LogWarning("Recording too short or failed.");
            isRecording = false;
            recordingClip = null;
            return;
        }

        if (Microphone.IsRecording(microphoneDevice))
        {
            Microphone.End(microphoneDevice);
            Debug.Log("Microphone recording stopped.");
        }

        if (recordingClip == null)
        {
            Debug.LogWarning("No recording clip available to save.");
            isRecording = false;
            return;
        }

        float[] samples = new float[recordingClip.samples * recordingClip.channels];
        recordingClip.GetData(samples, 0);

        // Trim samples array to actual recorded data length
        float[] trimmedSamples = new float[position * recordingClip.channels];
        Array.Copy(samples, 0, trimmedSamples, 0, trimmedSamples.Length);

        // Create a new trimmed AudioClip
        AudioClip trimmedClip = AudioClip.Create("trimmed", position, recordingClip.channels, recordingClip.frequency, false);
        trimmedClip.SetData(trimmedSamples, 0);

        // Replace original clip with trimmed clip for saving
        recordingClip = trimmedClip;

        try
        {
            if (!Directory.Exists(audioFileOutPath))
            {
                Directory.CreateDirectory(audioFileOutPath);
            }
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to create directory: " + e);
            isRecording = false;
            return;
        }

        string filepath = Path.Combine(audioFileOutPath, "Recording.wav");

        if (File.Exists(filepath))
        {
            try
            {
                File.Delete(filepath);
            }
            catch (Exception e)
            {
                Debug.LogError("Failed to delete existing recording file: " + e);
                isRecording = false;
                return;
            }
        }

        bool success = SaveWavFile(recordingClip, filepath);
        if (success)
        {
            FileInfo fi = new FileInfo(filepath);
            Debug.Log($"Saved recording to: {filepath}, file size: {fi.Length} bytes");

            if (transcriber != null)
            {
                transcriber.AgentTalkMode = true;
                transcriber.audioFilePath = filepath;
                transcriber.StartTranscriptionAsync();
            }
            else
            {
                Debug.LogWarning("Transcriber reference not set.");
            }
        }
        else
        {
            Debug.LogError("Failed to save WAV file.");
        }

        isRecording = false;
        recordingClip = null;
    }

    /// <summary>
    /// Saves an AudioClip to a WAV file.
    /// Note: AudioClip must be PCM, frequency should be the same as recorded.
    /// </summary>
    /// <param name="clip">AudioClip to save</param>
    /// <param name="filepath">Full path to save WAV</param>
    /// <returns>True if success</returns>
    public bool SaveWavFile(AudioClip clip, string filepath)
    {
        if (clip == null)
        {
            Debug.LogError("SaveWavFile called with null AudioClip");
            return false;
        }

        try
        {
            using (FileStream fileStream = CreateEmptyWav(filepath))
            {
                ConvertAndWrite(fileStream, clip);
                WriteHeader(fileStream, clip);
            }
            return true;
        }
        catch (Exception e)
        {
            Debug.LogError($"Exception during WAV save: {e}");
            return false;
        }
    }

    private FileStream CreateEmptyWav(string filepath)
    {
        var fileStream = new FileStream(filepath, FileMode.Create);
        // Write a placeholder 44 byte WAV header (will rewrite later)
        byte[] emptyHeader = new byte[44];
        fileStream.Write(emptyHeader, 0, emptyHeader.Length);
        return fileStream;
    }

    private void ConvertAndWrite(FileStream fileStream, AudioClip clip)
    {
        float[] samples = new float[clip.samples * clip.channels];
        clip.GetData(samples, 0);

        short[] intData = new short[samples.Length];
        byte[] bytesData = new byte[samples.Length * 2];

        // Convert float samples to Int16 PCM
        const float rescaleFactor = 32767f;

        for (int i = 0; i < samples.Length; i++)
        {
            float sample = Mathf.Clamp(samples[i], -1f, 1f);
            intData[i] = (short)(sample * rescaleFactor);
            byte[] byteArr = BitConverter.GetBytes(intData[i]);
            byteArr.CopyTo(bytesData, i * 2);
        }

        fileStream.Write(bytesData, 0, bytesData.Length);
    }

    private void WriteHeader(FileStream fileStream, AudioClip clip)
    {
        int hz = clip.frequency;
        int channels = clip.channels;
        int samples = clip.samples;

        fileStream.Seek(0, SeekOrigin.Begin);

        byte[] riff = Encoding.ASCII.GetBytes("RIFF");
        fileStream.Write(riff, 0, 4);

        byte[] chunkSize = BitConverter.GetBytes((int)(fileStream.Length - 8));
        fileStream.Write(chunkSize, 0, 4);

        byte[] wave = Encoding.ASCII.GetBytes("WAVE");
        fileStream.Write(wave, 0, 4);

        byte[] fmt = Encoding.ASCII.GetBytes("fmt ");
        fileStream.Write(fmt, 0, 4);

        byte[] subChunk1 = BitConverter.GetBytes(16);
        fileStream.Write(subChunk1, 0, 4);

        ushort audioFormat = 1; // PCM
        byte[] audioFormatBytes = BitConverter.GetBytes(audioFormat);
        fileStream.Write(audioFormatBytes, 0, 2);

        byte[] numChannels = BitConverter.GetBytes((ushort)channels);
        fileStream.Write(numChannels, 0, 2);

        byte[] sampleRate = BitConverter.GetBytes(hz);
        fileStream.Write(sampleRate, 0, 4);

        byte[] byteRate = BitConverter.GetBytes(hz * channels * 2);
        fileStream.Write(byteRate, 0, 4);

        ushort blockAlign = (ushort)(channels * 2);
        byte[] blockAlignBytes = BitConverter.GetBytes(blockAlign);
        fileStream.Write(blockAlignBytes, 0, 2);

        ushort bitsPerSample = 16;
        byte[] bitsPerSampleBytes = BitConverter.GetBytes(bitsPerSample);
        fileStream.Write(bitsPerSampleBytes, 0, 2);

        byte[] dataString = Encoding.ASCII.GetBytes("data");
        fileStream.Write(dataString, 0, 4);

        int subChunk2 = samples * channels * 2;
        byte[] subChunk2Bytes = BitConverter.GetBytes(subChunk2);
        fileStream.Write(subChunk2Bytes, 0, 4);
    }

    // Optional helper for sanitizing file/folder names if you want to organize by agent
    private string SanitizeFileName(string filename)
    {
        if (string.IsNullOrEmpty(filename))
            return "UnknownAgent";

        foreach (char c in Path.GetInvalidFileNameChars())
            filename = filename.Replace(c.ToString(), "");

        return filename;
    }
}

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;

public class TTSClientRunner : MonoBehaviour
{
    [Header("TTS Server Settings")]
    [Tooltip("Your TTS server URL")]
    public string url = "YOUR_URL";

    [Tooltip("API key for authorization")]
    public string apiKey = "YOUR_API_KEY";

    [Header("Text Settings")]
    [TextArea]
    public string text = "Hello, this is a test!";

    [Header("Reference Data (optional)")]
    [Tooltip("Reference texts for voice adaptation")]
    public List<string> referenceTexts = new List<string>() { "Hello" };

    [Tooltip("File paths for reference audio files (relative or absolute)")]
    public List<string> referenceAudioFilePaths = new List<string>();

    [Header("TTS Parameters")]
    [Tooltip("Audio output format (e.g., wav, mp3)")]
    public string format = "wav";

    [Tooltip("Chunk length for streaming (0 disables chunking)")]
    public int chunkLength = 0;

    [Tooltip("Top P parameter")]
    public float topP = 0.9f;

    [Tooltip("Temperature parameter")]
    public float temperature = 0.9f;

    [Tooltip("Random seed (nullable, 0 or null disables)")]
    public int? seed = 0;

    [Tooltip("Maximum new tokens")]
    public int maxNewTokens = 0;

    [Tooltip("Repetition penalty")]
    public float repetitionPenalty = 1.1f;

    [Tooltip("Optional explicit output file path. Leave empty to use persistentDataPath.")]
    public string outputFilePath = "";

    // Reference to audio source for playback
    private AudioSource audioSource;

    private void Awake()
    {
        // Try to get AudioSource or add one if missing
        audioSource = GetComponent<AudioSource>();
        if (audioSource == null)
            audioSource = gameObject.AddComponent<AudioSource>();
    }

    public void RunTTSx()
    {
        StartCoroutine(RunTTS());
    }

    private IEnumerator RunTTS()
    {
        Debug.Log("Starting TTS request...");

        // Load reference audio files into List<byte[]>
        List<byte[]> referenceAudios = new List<byte[]>();

        foreach (var filePath in referenceAudioFilePaths)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                referenceAudios.Add(new byte[0]);
                continue;
            }

            string fullPath = filePath;

            // If path is relative, try combining with Application.persistentDataPath or StreamingAssetsPath
            if (!Path.IsPathRooted(filePath))
            {
                // Try persistentDataPath first
                fullPath = Path.Combine(Application.persistentDataPath, filePath);

                // If file doesn't exist there, try StreamingAssets (on Android StreamingAssets is not simple folder)
                if (!File.Exists(fullPath))
                {
#if UNITY_ANDROID && !UNITY_EDITOR
                    // Android StreamingAssets must be accessed via UnityWebRequest
                    yield return LoadFileFromAndroidStreamingAssets(filePath, audioBytes =>
                    {
                        if (audioBytes != null)
                            referenceAudios.Add(audioBytes);
                        else
                            referenceAudios.Add(new byte[0]);
                    });
                    continue;
#else
                    fullPath = Path.Combine(Application.streamingAssetsPath, filePath);
#endif
                }
            }

            if (File.Exists(fullPath))
            {
                byte[] bytes = null;
                try
                {
                    bytes = File.ReadAllBytes(fullPath);
                }
                catch (System.Exception ex)
                {
                    Debug.LogWarning($"Failed to read reference audio file '{fullPath}': {ex.Message}");
                }

                referenceAudios.Add(bytes ?? new byte[0]);
            }
            else
            {
                Debug.LogWarning($"Reference audio file not found: {fullPath}");
                referenceAudios.Add(new byte[0]);
            }
        }

        // Determine final output file path
        string finalOutputFilePath;

        if (string.IsNullOrEmpty(outputFilePath))
        {
            // Define safe output folder inside the app folder
            string outputFolder = Path.Combine(Application.persistentDataPath, "Audio");
            if (!Directory.Exists(outputFolder))
            {
                Directory.CreateDirectory(outputFolder);
            }
            // Define final filename - you can change the name if you want
            string fileName = "tts_output." + format;
            finalOutputFilePath = Path.Combine(outputFolder, fileName);
        }
        else
        {
            finalOutputFilePath = outputFilePath;
        }

        var ttsClient = new TTSClient(
            url,
            text,
            apiKey,
            referenceTexts,
            referenceAudios,
            format,
            chunkLength,
            topP,
            temperature,
            seed,
            maxNewTokens,
            repetitionPenalty,
            finalOutputFilePath);

        yield return ttsClient.SendRequestAndSaveAudioCoroutine(result =>
        {
            if (result.success)
            {
                Debug.Log($"TTS success! Audio saved to: {result.message}");
                // Start playback coroutine immediately with the saved file path
                StartCoroutine(PlayAudioFromFile(finalOutputFilePath));
            }
            else
            {
                Debug.LogError($"TTS failed: {result.message}");
            }
        });
    }

    private IEnumerator PlayAudioFromFile(string path)
    {
        if (!File.Exists(path))
        {
            Debug.LogError($"Playback failed. Audio file does not exist: {path}");
            yield break;
        }

        // Compose file URI (must use file://)
        string url = "file://" + path;

        // Determine AudioType from format
        AudioType audioType = AudioType.UNKNOWN;
        switch (format.ToLower())
        {
            case "wav":
                audioType = AudioType.WAV;
                break;
            case "mp3":
                audioType = AudioType.MPEG;
                break;
            case "ogg":
                audioType = AudioType.OGGVORBIS;
                break;
                // add others if needed
        }

        using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(url, audioType))
        {
            yield return www.SendWebRequest();

#if UNITY_2020_1_OR_NEWER
            if (www.result != UnityWebRequest.Result.Success)
#else
            if (www.isNetworkError || www.isHttpError)
#endif
            {
                Debug.LogError($"Failed to load audio clip for playback: {www.error}");
                yield break;
            }
            else
            {
                AudioClip clip = DownloadHandlerAudioClip.GetContent(www);
                if (clip == null)
                {
                    Debug.LogError("Failed to decode audio clip.");
                    yield break;
                }

                // Play the clip
                audioSource.clip = clip;
                audioSource.Play();
                Debug.Log("Playing TTS audio...");
            }
        }
    }

#if UNITY_ANDROID && !UNITY_EDITOR
    // Special helper to load StreamingAssets on Android
    private IEnumerator LoadFileFromAndroidStreamingAssets(string relativePath, System.Action<byte[]> onComplete)
    {
        string streamingAssetsPath = Path.Combine(Application.streamingAssetsPath, relativePath);
        using UnityEngine.Networking.UnityWebRequest request = UnityEngine.Networking.UnityWebRequest.Get(streamingAssetsPath);
        yield return request.SendWebRequest();

#if UNITY_2020_1_OR_NEWER
        if (request.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
#else
        if (!request.isNetworkError && !request.isHttpError)
#endif
        {
            onComplete?.Invoke(request.downloadHandler.data);
        }
        else
        {
            Debug.LogWarning($"Failed to load {streamingAssetsPath}: {request.error}");
            onComplete?.Invoke(null);
        }
    }
#endif
}

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;

public class TTSClient
{
    private readonly string _url;
    private readonly string _text;
    private readonly string _apiKey;
    private readonly string _format;
    private readonly int _chunkLength;
    private readonly float _topP;
    private readonly float _temperature;
    private readonly int? _seed;
    private readonly int _maxNewTokens;
    private readonly float _repetitionPenalty;
    private readonly string _outputFilePath;

    private readonly List<string> _referenceTexts;
    private readonly List<byte[]> _referenceAudios;

    public TTSClient(
        string url,
        string text,
        string apiKey,
        List<string> referenceTexts,
        List<byte[]> referenceAudios,
        string format = "wav",
        int chunkLength = 0,
        float topP = 0.9f,
        float temperature = 0.9f,
        int? seed = 0,
        int maxNewTokens = 0,
        float repetitionPenalty = 1.1f,
        string outputFilePath = null
    )
    {
        _url = url ?? throw new ArgumentNullException(nameof(url));
        _text = text ?? throw new ArgumentNullException(nameof(text));
        _apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
        _referenceTexts = referenceTexts ?? throw new ArgumentNullException(nameof(referenceTexts));
        _referenceAudios = referenceAudios ?? throw new ArgumentNullException(nameof(referenceAudios));
        _format = format;
        _chunkLength = chunkLength;
        _topP = topP;
        _temperature = temperature;
        _seed = seed;
        _maxNewTokens = maxNewTokens;
        _repetitionPenalty = repetitionPenalty;

        if (string.IsNullOrEmpty(outputFilePath))
        {
            _outputFilePath = Path.Combine(Application.persistentDataPath, $"output.{_format}");
        }
        else
        {
            _outputFilePath = outputFilePath;
        }
    }

    [Serializable]
    private class ReferenceObject
    {
        public string text;
        public string audio;
        public ReferenceObject(string text, string audio)
        {
            this.text = text;
            this.audio = audio;
        }
    }

    [Serializable]
    private class RequestData
    {
        public string text;
        public ReferenceObject[] references;
        public bool normalize;
        public string format;
        public int max_new_tokens;
        public int chunk_length;
        public float top_p;
        public float repetition_penalty;
        public float temperature;
        public bool streaming;
        public string use_memory_cache;
        public int? seed;
    }

    private ReferenceObject[] CreateReferenceObjects()
    {
        int count = Math.Min(_referenceTexts.Count, _referenceAudios.Count);
        ReferenceObject[] refs = new ReferenceObject[count];

        for (int i = 0; i < count; i++)
        {
            string base64Audio = (_referenceAudios[i] != null && _referenceAudios[i].Length > 0)
                ? Convert.ToBase64String(_referenceAudios[i])
                : null;
            refs[i] = new ReferenceObject(_referenceTexts[i], base64Audio);
        }
        return refs;
    }

    /// <summary>
    /// Coroutine that sends TTS request and saves audio file.
    /// </summary>
    /// <param name="resultCallback">Callback invoked on completion: (bool success, string message)</param>
    public IEnumerator SendRequestAndSaveAudioCoroutine(Action<(bool success, string message)> resultCallback)
    {
        var requestData = new RequestData()
        {
            text = _text,
            references = CreateReferenceObjects(),
            normalize = false,
            format = _format,
            max_new_tokens = _maxNewTokens,
            chunk_length = _chunkLength,
            top_p = _topP,
            repetition_penalty = _repetitionPenalty,
            temperature = _temperature,
            streaming = false,
            use_memory_cache = "on",
            seed = _seed
        };

        string json = JsonUtility.ToJson(requestData);

        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(json);

        using var webRequest = new UnityWebRequest(_url, UnityWebRequest.kHttpVerbPOST);
        webRequest.uploadHandler = new UploadHandlerRaw(bodyRaw);
        webRequest.downloadHandler = new DownloadHandlerBuffer();
        webRequest.SetRequestHeader("Content-Type", "application/json");
        webRequest.SetRequestHeader("Authorization", "Bearer " + _apiKey);

        yield return webRequest.SendWebRequest();

#if UNITY_2020_1_OR_NEWER
        if (webRequest.result != UnityWebRequest.Result.Success)
#else
        if (webRequest.isNetworkError || webRequest.isHttpError)
#endif
        {
            string errorMsg = !string.IsNullOrEmpty(webRequest.error) ? webRequest.error : "Unknown error";
            resultCallback?.Invoke((false, $"Request error: {errorMsg}"));
            yield break;
        }

        // Save audio bytes to file
        try
        {
            byte[] audioBytes = webRequest.downloadHandler.data;
            if (audioBytes == null || audioBytes.Length == 0)
            {
                resultCallback?.Invoke((false, "Received empty audio data"));
                yield break;
            }

            File.WriteAllBytes(_outputFilePath, audioBytes);
            resultCallback?.Invoke((true, _outputFilePath));
        }
        catch (Exception e)
        {
            resultCallback?.Invoke((false, $"Failed to save audio: {e.Message}"));
        }
    }
}

using UnityEngine;
using Michsky.UI.Reach;
using System.IO;
using System;
using UnityEngine.UI;

public class InjectReqs : MonoBehaviour
{
    public TTSClientRunner ttsClientRunner;
    public Michsky.UI.Reach.Dropdown AgentSelection;
    string BasePath = string.Empty;

    public void InjectReq()
    {
        string selectedText = AgentSelection.items[AgentSelection.selectedItemIndex].itemName;

        BasePath = Path.Combine(Application.persistentDataPath, @"Agent Presets\" + selectedText + @"\");

        string ReferenceVoicePath = BasePath + "Agent_Voice.wav";
        string ReferenceTextPath = BasePath + "Agent_Transcription.txt";

        // Clear stuff first.
        ttsClientRunner.referenceTexts.Clear();
        ttsClientRunner.referenceAudioFilePaths.Clear();

        // Now continue.
        ttsClientRunner.referenceAudioFilePaths.Add(ReferenceVoicePath);
        ttsClientRunner.referenceTexts.Add(ReferenceTextPath);
    }
}

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using System.IO;
using TMPro;
using Michsky.UI.Reach;
using System;
using System.Threading.Tasks;

// Response data model matching JSON output: { "text": "some transcription" }
[Serializable]
public class TranscriptionResponse
{
    public string text;
}

public class FastAPIAudioTranscriber : MonoBehaviour
{
    public TMP_InputField inputField;
    public string XOut;
    public bool AgentTalkMode = false;
    public Michsky.UI.Reach.Dropdown SelectedAgent;
    public OllamaInfer OLF;
    // URL of the FastAPI transcription endpoint
    [Tooltip("Set this to your FastAPI server's /transcribe endpoint")]
    public string apiUrl = "http://yourserver.com/transcribe";

    // Path to the audio file on the device
    [Tooltip("Local path to the audio file to transcribe")]
    public string audioFilePath;

    /// <summary>
    /// Async helper method to send a UnityWebRequest and await completion.
    /// Throws on error with details.
    /// </summary>
    private async Task<UnityWebRequest> SendWebRequestAsync(UnityWebRequest request)
    {
        var operation = request.SendWebRequest();

        while (!operation.isDone)
            await Task.Yield();

#if UNITY_2020_1_OR_NEWER
        if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
#else
        if (request.isNetworkError || request.isHttpError)
#endif
        {
            throw new Exception($"Request error: {request.error}, Response: {request.downloadHandler.text}");
        }

        return request;
    }

    /// <summary>
    /// Transcribes audio from a file asynchronously and returns the transcribed text or null on failure.
    /// Does not modify internal state or UI components.
    /// </summary>
    /// <param name="path">Full local path to the audio file</param>
    /// <returns>Transcription text or null if failed</returns>
    public async Task<string> TranscribeAudioFileAsync(string path)
    {
        if (string.IsNullOrEmpty(path))
        {
            Debug.LogError("Provided audio file path is empty.");
            return null;
        }

        if (!File.Exists(path))
        {
            Debug.LogError($"Audio file does not exist at path: {path}");
            return null;
        }

        byte[] audioData;
        try
        {
            audioData = File.ReadAllBytes(path);
        }
        catch (Exception e)
        {
            Debug.LogError($"Failed to read audio data from file. Exception: {e.Message}");
            return null;
        }

        if (audioData == null || audioData.Length == 0)
        {
            Debug.LogError("Audio data is empty.");
            return null;
        }

        List<IMultipartFormSection> formData = new List<IMultipartFormSection>
        {
            new MultipartFormFileSection("audio_file", audioData, Path.GetFileName(path), "audio/wav"),
            new MultipartFormDataSection("task", "transcribe")
        };

        using (UnityWebRequest www = UnityWebRequest.Post(apiUrl, formData))
        {
            www.timeout = 60;

            try
            {
                await SendWebRequestAsync(www);

                TranscriptionResponse response = JsonUtility.FromJson<TranscriptionResponse>(www.downloadHandler.text);
                if (response != null)
                {
                    return response.text;
                }
                else
                {
                    Debug.LogWarning("Failed to parse transcription response JSON.");
                    return null;
                }
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error during transcription request: {ex.Message}");
                return null;
            }
        }
    }

    /// <summary>
    /// Public method to start transcription asynchronously.
    /// Updates input field or XOut depending on AgentTalkMode.
    /// </summary>
    public async void StartTranscriptionAsync()
    {
        if (string.IsNullOrEmpty(audioFilePath))
        {
            Debug.LogError("Audio file path is empty.");
            return;
        }

        if (!File.Exists(audioFilePath))
        {
            Debug.LogError($"Audio file does not exist at path: {audioFilePath}");
            return;
        }

        try
        {
            string transcription = await TranscribeAudioFileAsync(audioFilePath);

            if (string.IsNullOrEmpty(transcription))
            {
                Debug.LogWarning("Transcription returned null or empty text.");
                return;
            }

            if (AgentTalkMode)
            {
                if (SelectedAgent == null || SelectedAgent.items == null || SelectedAgent.items.Count == 0)
                {
                    Debug.LogWarning("SelectedAgent is not properly initialized or empty.");
                    return;
                }

                string selectedItemName = SelectedAgent.items[SelectedAgent.selectedItemIndex].itemName;
                XOut = "You are a Female by the name of: " + selectedItemName + " You are cute, silly, funny, smart: User Cody Says [" + transcription + "] Reply to him. Get right into acting don't say alright let's do this! or any weird intro.";
                OLF.ChatBot(XOut);
                AgentTalkMode = false; // Reset flag immediately to prevent repeated appending
            }
            else
            {
                if (inputField != null)
                {
                    inputField.text = transcription;
                }
                else
                {
                    Debug.LogWarning("InputField is null - cannot update transcription text.");
                }
            }
        }
        catch (Exception ex)
        {
            Debug.LogError($"Unexpected error in StartTranscriptionAsync: {ex.Message}");
        }
    }
}

using System.Collections.Generic;
using UnityEngine;

public class OllamaInfer : MonoBehaviour
{
    [Tooltip("Stores the plain text response from Ollama server.")]
    public string response;
    public TTSClientRunner runner;

    public void TestOllama()
    {
        var ollamaClient = gameObject.GetComponent<OllamaClient>();
        if (ollamaClient == null)
        {
            Debug.LogError("OllamaClient component not found on this GameObject.");
            return;
        }

        var messages = new List<OllamaClient.Message>
        {
            new OllamaClient.Message("user", "test you seem broken rn?")
        };

        ollamaClient.Chat(
            messages,
            responseText =>
            {
                // Remove surrounding quotes if they exist
                if (!string.IsNullOrEmpty(responseText)
                    && responseText.Length >= 2
                    && responseText.StartsWith("\"")
                    && responseText.EndsWith("\""))
                {
                    response = responseText.Substring(1, responseText.Length - 2);
                    runner.text = response;
                    runner.RunTTSx();
                }
                else
                {
                    response = responseText;
                }

                Debug.Log("Response: " + response);
            },
            error => Debug.LogError(error));
    }

    public void ChatBot(string Message)
    {
        var ollamaClient = gameObject.GetComponent<OllamaClient>();
        if (ollamaClient == null)
        {
            Debug.LogError("OllamaClient component not found on this GameObject.");
            return;
        }

        var messages = new List<OllamaClient.Message>
        {
            new OllamaClient.Message("user", Message)
        };

        ollamaClient.Chat(
            messages,
            responseText =>
            {
                // Remove surrounding quotes if they exist
                if (!string.IsNullOrEmpty(responseText)
                    && responseText.Length >= 2
                    && responseText.StartsWith("\"")
                    && responseText.EndsWith("\""))
                {
                    response = responseText.Substring(1, responseText.Length - 2);
                    runner.text = response;
                    runner.RunTTSx();
                }
                else
                {
                    response = responseText;
                    runner.text = response;
                    runner.RunTTSx();
                }

                Debug.Log("Response: " + response);
            },
            error => Debug.LogError(error));
    }
}

'server error'
INFO:     192.168.240.1:64227 - "POST /v1/tts HTTP/1.1" 500 Internal Server Error
Traceback (most recent call last):
  File "/home/meow/anaconda3/envs/fish-speech/lib/python3.12/site-packages/kui/asgi/exceptions.py", line 27, in wrapper
    return await endpoint()
           ^^^^^^^^^^^^^^^^
  File "/home/meow/fish-speech/tools/api_server.py", line 40, in passthrough
    return await endpoint()
           ^^^^^^^^^^^^^^^^
  File "/home/meow/anaconda3/envs/fish-speech/lib/python3.12/site-packages/kui/asgi/views.py", line 29, in wrapper
    return await function()
           ^^^^^^^^^^^^^^^^
  File "/home/meow/anaconda3/envs/fish-speech/lib/python3.12/site-packages/kui/asgi/parameters.py", line 119, in callback_with_auto_bound_params
    result = await result
             ^^^^^^^^^^^^
  File "/home/meow/fish-speech/tools/server/views.py", line 127, in tts
    fake_audios = next(inference(req, engine))
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/meow/fish-speech/tools/server/inference.py", line 18, in inference_wrapper
    for result in engine.inference(req):
                  ^^^^^^^^^^^^^^^^^^^^^
  File "/home/meow/anaconda3/envs/fish-speech/lib/python3.12/site-packages/torch/utils/_contextlib.py", line 36, in generator_context
    response = gen.send(None)
               ^^^^^^^^^^^^^^
  File "/home/meow/fish-speech/fish_speech/inference_engine/__init__.py", line 55, in inference
    prompt_tokens, prompt_texts = self.load_by_hash(
                                  ^^^^^^^^^^^^^^^^^^
  File "/home/meow/fish-speech/fish_speech/inference_engine/reference_loader.py", line 92, in load_by_hash
    self.encode_reference(
  File "/home/meow/fish-speech/fish_speech/inference_engine/vq_manager.py", line 37, in encode_reference
    reference_audio_content = self.load_audio(reference_audio, sample_rate)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/meow/fish-speech/fish_speech/inference_engine/reference_loader.py", line 122, in load_audio
    waveform, original_sr = torchaudio.load(audio_buffer, backend=self.backend)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/meow/anaconda3/envs/fish-speech/lib/python3.12/site-packages/torchaudio/_backend/utils.py", line 205, in load
    return backend.load(uri, frame_offset, num_frames, normalize, channels_first, format, buffer_size)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/meow/anaconda3/envs/fish-speech/lib/python3.12/site-packages/torchaudio/_backend/soundfile.py", line 27, in load
    return soundfile_backend.load(uri, frame_offset, num_frames, normalize, channels_first, format)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/meow/anaconda3/envs/fish-speech/lib/python3.12/site-packages/torchaudio/_backend/soundfile_backend.py", line 221, in load
    with soundfile.SoundFile(filepath, "r") as file_:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/meow/anaconda3/envs/fish-speech/lib/python3.12/site-packages/soundfile.py", line 690, in __init__
    self._file = self._open(file, mode_int, closefd)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/meow/anaconda3/envs/fish-speech/lib/python3.12/site-packages/soundfile.py", line 1265, in _open
    raise LibsndfileError(err, prefix="Error opening {0!r}: ".format(self.name))
soundfile.LibsndfileError: Error opening <_io.BytesIO object at 0x7f6f2498a660>: Format not recognised.
INFO:     192.168.240.1:64227 - "POST /v1/tts HTTP/1.1" 500 Internal Server Error