/* * ATS Cloud Platform - Unity SDK * AI Services for AR/VR Applications * * INTEGRATION GUIDE: * 1. Add this script to a GameObject in your scene * 2. Set your API key (get from portal.atscloudplatform.com) * 3. Call methods: SpeechToText(), TextToSpeech(), Chat() * * URL Configuration: * - Development: http://192.168.1.167:8000 * - Production: https://atscloudplatform.com */ using System; using System.Collections; using System.Text; using UnityEngine; using UnityEngine.Networking; public class ATSCloudAPI : MonoBehaviour { [Header("API Configuration")] [Tooltip("Change to https://atscloudplatform.com for production")] public string baseUrl = "http://192.168.1.167:8000"; [Tooltip("Your API key from ATS Cloud Portal")] public string apiKey = "YOUR_API_KEY_HERE"; [Header("Audio Settings")] public int recordingDuration = 5; public int sampleRate = 16000; // Events for callbacks public event Action OnTranscriptionComplete; public event Action OnChatResponseReceived; public event Action OnTTSComplete; public event Action OnSpeechToSpeechComplete; // Audio, User Text, AI Text public event Action OnFeedbackReceived; public event Action OnError; private AudioClip recordedClip; public string currentSessionId; // Stores the current S2S session ID // ============== Response Classes ============== [Serializable] public class STTResponse { public string text; public float confidence; public float processing_time_ms; } [Serializable] public class ChatResponse { public string response; public float processing_time_ms; } [Serializable] public class ChatRequest { public string message; } [Serializable] public class TTSRequest { public string text; public string voice; } [Serializable] public class FeedbackResponse { public string feedback; public int questions_asked; public float session_duration_minutes; } [Serializable] public class FeedbackRequest { public string session_id; } // ============== Public Methods ============== /// /// Record audio and convert to text using ATS Cloud STT /// public void StartSpeechToText() { StartCoroutine(RecordAndTranscribe()); } /// /// Send text message and get AI response /// public void SendChat(string message) { StartCoroutine(ChatCoroutine(message)); } /// /// Convert text to speech audio /// public void TextToSpeech(string text, string voice = "en-US-GuyNeural") { StartCoroutine(TTSCoroutine(text, voice)); } /// /// Check API health status /// public void CheckHealth() { StartCoroutine(HealthCheckCoroutine()); } /// /// Record audio and get spoken response from A1 (All-in-one) /// public void StartSpeechToSpeech(string voice = "en-US-GuyNeural", string serviceType = "chat") { StartCoroutine(RecordAndSpeechToSpeech(voice, serviceType)); } /// /// Get feedback for normal Mock Interview session /// public void GetMockInterviewFeedback(string sessionId) { StartCoroutine(GetFeedbackCoroutine("/v1/mock-interview/feedback", sessionId, true)); } /// /// Get feedback for S2S Mock Interview session /// public void GetS2SFeedback(string sessionId) { StartCoroutine(GetFeedbackCoroutine("/v1/speech-to-speech/feedback", sessionId, false)); } // ============== STT Implementation ============== private IEnumerator RecordAndTranscribe() { Debug.Log("[ATS Cloud] Starting recording..."); // Record audio recordedClip = Microphone.Start(null, false, recordingDuration, sampleRate); yield return new WaitForSeconds(recordingDuration); Microphone.End(null); Debug.Log("[ATS Cloud] Recording complete, sending to API..."); // Convert to WAV bytes byte[] audioData = WavUtility.FromAudioClip(recordedClip); // Create multipart form WWWForm form = new WWWForm(); form.AddBinaryData("audio", audioData, "recording.wav", "audio/wav"); string url = $"{baseUrl}/v1/stt"; using (UnityWebRequest request = UnityWebRequest.Post(url, form)) { request.SetRequestHeader("X-API-Key", apiKey); yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) { string json = request.downloadHandler.text; Debug.Log("[ATS Cloud] STT Response: " + json); STTResponse response = JsonUtility.FromJson(json); OnTranscriptionComplete?.Invoke(response.text); } else { string error = $"STT Error: {request.error}"; Debug.LogError("[ATS Cloud] " + error); OnError?.Invoke(error); } } } // ============== Chat Implementation ============== private IEnumerator ChatCoroutine(string message) { Debug.Log("[ATS Cloud] Sending chat message..."); string url = $"{baseUrl}/v1/chat/simple"; ChatRequest chatRequest = new ChatRequest { message = message }; string jsonBody = JsonUtility.ToJson(chatRequest); using (UnityWebRequest request = new UnityWebRequest(url, "POST")) { byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonBody); request.uploadHandler = new UploadHandlerRaw(bodyRaw); request.downloadHandler = new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); request.SetRequestHeader("X-API-Key", apiKey); yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) { string json = request.downloadHandler.text; Debug.Log("[ATS Cloud] Chat Response: " + json); ChatResponse response = JsonUtility.FromJson(json); OnChatResponseReceived?.Invoke(response.response); } else { string error = $"Chat Error: {request.error}"; Debug.LogError("[ATS Cloud] " + error); OnError?.Invoke(error); } } } // ============== TTS Implementation ============== private IEnumerator TTSCoroutine(string text, string voice) { Debug.Log("[ATS Cloud] Requesting TTS..."); string url = $"{baseUrl}/v1/tts"; TTSRequest ttsRequest = new TTSRequest { text = text, voice = voice }; string jsonBody = JsonUtility.ToJson(ttsRequest); using (UnityWebRequest request = new UnityWebRequest(url, "POST")) { byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonBody); request.uploadHandler = new UploadHandlerRaw(bodyRaw); request.downloadHandler = new DownloadHandlerAudioClip(url, AudioType.MPEG); request.SetRequestHeader("Content-Type", "application/json"); request.SetRequestHeader("X-API-Key", apiKey); yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) { AudioClip audioClip = DownloadHandlerAudioClip.GetContent(request); Debug.Log("[ATS Cloud] TTS audio received"); OnTTSComplete?.Invoke(audioClip); } else { string error = $"TTS Error: {request.error}"; Debug.LogError("[ATS Cloud] " + error); OnError?.Invoke(error); } } } // ============== Health Check ============== private IEnumerator HealthCheckCoroutine() { string url = $"{baseUrl}/health"; using (UnityWebRequest request = UnityWebRequest.Get(url)) { yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) { Debug.Log("[ATS Cloud] Health: " + request.downloadHandler.text); } else { Debug.LogError("[ATS Cloud] Health check failed: " + request.error); } } } // ============== Speech-to-Speech Implementation ============== private IEnumerator RecordAndSpeechToSpeech(string voice, string serviceType) { Debug.Log($"[ATS Cloud] S2S ({serviceType}): Starting recording..."); // Record audio recordedClip = Microphone.Start(null, false, recordingDuration, sampleRate); yield return new WaitForSeconds(recordingDuration); Microphone.End(null); Debug.Log("[ATS Cloud] S2S: Recording complete, sending to API..."); // Convert to WAV bytes byte[] audioData = WavUtility.FromAudioClip(recordedClip); // Create multipart form WWWForm form = new WWWForm(); form.AddBinaryData("audio", audioData, "recording.wav", "audio/wav"); form.AddField("voice", voice); form.AddField("service_type", serviceType); // Add session ID if available to maintain context if (!string.IsNullOrEmpty(currentSessionId)) { form.AddField("session_id", currentSessionId); } string url = $"{baseUrl}/v1/speech-to-speech"; using (UnityWebRequest request = UnityWebRequest.Post(url, form)) { request.downloadHandler = new DownloadHandlerAudioClip(url, AudioType.MPEG); request.SetRequestHeader("X-API-Key", apiKey); yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) { AudioClip audioClip = DownloadHandlerAudioClip.GetContent(request); string transcription = request.GetResponseHeader("X-Transcription"); string responseText = request.GetResponseHeader("X-Response-Text"); // Update session ID if returned string newSessionId = request.GetResponseHeader("X-Session-Id"); if (!string.IsNullOrEmpty(newSessionId)) { currentSessionId = newSessionId; Debug.Log($"[ATS Cloud] Session ID updated: {currentSessionId}"); } Debug.Log($"[ATS Cloud] S2S Success. User: {transcription}, AI: {responseText}"); OnSpeechToSpeechComplete?.Invoke(audioClip, transcription, responseText); } else { string error = $"S2S Error: {request.error}"; Debug.LogError("[ATS Cloud] " + error); OnError?.Invoke(error); } } } // ============== Feedback Implementation ============== private IEnumerator GetFeedbackCoroutine(string endpoint, string sessionId, bool isJson) { string url = $"{baseUrl}{endpoint}"; Debug.Log($"[ATS Cloud] Requesting feedback from {url}..."); UnityWebRequest request; if (isJson) { // JSON Body (Mock Interview) FeedbackRequest body = new FeedbackRequest { session_id = sessionId }; string jsonBody = JsonUtility.ToJson(body); request = new UnityWebRequest(url, "POST"); byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonBody); request.uploadHandler = new UploadHandlerRaw(bodyRaw); request.downloadHandler = new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); } else { // Form Data (S2S) WWWForm form = new WWWForm(); form.AddField("session_id", sessionId); request = UnityWebRequest.Post(url, form); } using (request) { request.SetRequestHeader("X-API-Key", apiKey); yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) { string json = request.downloadHandler.text; Debug.Log("[ATS Cloud] Feedback Received: " + json); FeedbackResponse response = JsonUtility.FromJson(json); OnFeedbackReceived?.Invoke(response.feedback); } else { string error = $"Feedback Error: {request.error} - {request.downloadHandler.text}"; Debug.LogError("[ATS Cloud] " + error); OnError?.Invoke(error); } } } } /* * WavUtility - Helper class to convert AudioClip to WAV bytes * Include this in your project or use an existing implementation */ public static class WavUtility { public static byte[] FromAudioClip(AudioClip clip) { float[] samples = new float[clip.samples * clip.channels]; clip.GetData(samples, 0); Int16[] intData = new Int16[samples.Length]; Byte[] bytesData = new Byte[samples.Length * 2]; int rescaleFactor = 32767; for (int i = 0; i < samples.Length; i++) { intData[i] = (Int16)(samples[i] * rescaleFactor); Byte[] byteArr = BitConverter.GetBytes(intData[i]); byteArr.CopyTo(bytesData, i * 2); } // Create WAV header using (var memoryStream = new System.IO.MemoryStream()) using (var writer = new System.IO.BinaryWriter(memoryStream)) { // RIFF header writer.Write(new char[4] { 'R', 'I', 'F', 'F' }); writer.Write(36 + bytesData.Length); writer.Write(new char[4] { 'W', 'A', 'V', 'E' }); // fmt chunk writer.Write(new char[4] { 'f', 'm', 't', ' ' }); writer.Write(16); // Subchunk1Size writer.Write((Int16)1); // AudioFormat (PCM) writer.Write((Int16)clip.channels); writer.Write(clip.frequency); writer.Write(clip.frequency * clip.channels * 2); writer.Write((Int16)(clip.channels * 2)); writer.Write((Int16)16); // Bits per sample // data chunk writer.Write(new char[4] { 'd', 'a', 't', 'a' }); writer.Write(bytesData.Length); writer.Write(bytesData); return memoryStream.ToArray(); } } }