using System; using System.Collections; using Convai.Scripts.Runtime.Addons; using Convai.Scripts.Runtime.LoggerSystem; using UnityEngine; namespace Convai.Scripts.Runtime.UI { /// /// This class is used to control the microphone test. /// It requires a UIMicrophoneSettings, AudioSource, and MicrophoneInputChecker components to work. /// [DefaultExecutionOrder(-100)] [RequireComponent(typeof(UIMicrophoneSettings), typeof(AudioSource), typeof(MicrophoneInputChecker))] public class MicrophoneTestController : MonoBehaviour { /// /// Constants for frequency and maximum recording time. /// private const int FREQUENCY = 44100; private const int MAX_RECORD_TIME = 10; /// /// The AudioSource component attached to this GameObject. /// private AudioSource _audioSource; /// /// The Audio Playing Status. /// private bool _isAudioPlaying; /// /// The Recording Status. /// private bool _isRecording; /// /// The MicrophoneInputChecker component attached to this GameObject. /// private MicrophoneInputChecker _microphoneInputChecker; /// /// Coroutine reference for stopping the recording. /// private Coroutine _recordTimeCounterCoroutine; /// /// The currently selected microphone device. /// private string _selectedDevice; /// /// The UIMicrophoneSettings component attached to this GameObject. /// private UIMicrophoneSettings _uiMicrophoneSettings; /// /// Singleton instance of the MicrophoneTestController. /// public static MicrophoneTestController Instance { get; private set; } /// /// Get the components on Awake. /// private void Awake() { // Ensure there's only one instance of MicrophoneTestController if (Instance != null) { ConvaiLogger.DebugLog(" There's More Than One MicrophoneTestController " + transform + " - " + Instance, ConvaiLogger.LogCategory.UI); Destroy(gameObject); return; } Instance = this; // Get the components _uiMicrophoneSettings = GetComponent(); _audioSource = GetComponent(); _microphoneInputChecker = GetComponent(); } /// /// Add click event listeners for record and stop buttons on Start. /// private void Start() { _uiMicrophoneSettings.GetRecordControllerButton().onClick.AddListener(RecordController); _selectedDevice = MicrophoneManager.Instance.SelectedMicrophoneName; } /// /// Events for different states of recording. /// public event Action OnRecordStarted; public event Action OnRecordCompleted; public event Action OnAudioClipCompleted; /// /// Check if the selected microphone device is working. /// public void CheckMicrophoneDeviceWorkingStatus(AudioClip audioClip) { _microphoneInputChecker.IsMicrophoneWorking(audioClip); } /// /// Handle the Record/Stop button click. /// private void RecordController() { if (_isRecording) StopMicrophoneTestRecording(); else if (!_isAudioPlaying) StartMicrophoneTestRecording(); } /// /// Start recording from the selected microphone device. /// private void StartMicrophoneTestRecording() { if (_uiMicrophoneSettings.GetMicrophoneSelectDropdown().options.Count > 0) { _selectedDevice = MicrophoneManager.Instance.SelectedMicrophoneName; AudioClip recordedClip = Microphone.Start(_selectedDevice, false, MAX_RECORD_TIME, FREQUENCY); _audioSource.clip = recordedClip; CheckMicrophoneDeviceWorkingStatus(recordedClip); OnRecordStarted?.Invoke(); _isRecording = true; _recordTimeCounterCoroutine = StartCoroutine(RecordTimeCounter()); } else { ConvaiLogger.Error("No microphone devices found.", ConvaiLogger.LogCategory.UI); } } /// /// Stop recording from the selected microphone device. /// private void StopMicrophoneTestRecording() { if (Microphone.IsRecording(_selectedDevice)) { StopCoroutine(_recordTimeCounterCoroutine); int position = Microphone.GetPosition(_selectedDevice); Microphone.End(_selectedDevice); TrimAudio(position); _audioSource.Play(); _isAudioPlaying = true; OnRecordCompleted?.Invoke(); _isRecording = false; StartCoroutine(AudioClipTimeCounter(_audioSource.clip.length)); } } /// /// Coroutine to automatically stop recording after MAX_RECORD_TIME. /// private IEnumerator RecordTimeCounter() { yield return new WaitForSeconds(MAX_RECORD_TIME); StopMicrophoneTestRecording(); } /// /// Coroutine to invoke OnAudioClipCompleted after the audio clip duration. /// private IEnumerator AudioClipTimeCounter(float length) { yield return new WaitForSeconds(length); OnAudioClipCompleted?.Invoke(); _isAudioPlaying = false; } /// /// Trim the audio based on the last recorded position. /// private void TrimAudio(int micRecordLastPosition) { if (_audioSource.clip == null) { ConvaiLogger.Error("AudioSource clip is null.", ConvaiLogger.LogCategory.UI); return; } if (micRecordLastPosition <= 0) { ConvaiLogger.Error("Microphone position is zero or negative. Cannot trim audio.", ConvaiLogger.LogCategory.UI); return; } AudioClip tempAudioClip = _audioSource.clip; int channels = tempAudioClip.channels; int position = micRecordLastPosition; float[] samplesArray = new float[position * channels]; tempAudioClip.GetData(samplesArray, 0); AudioClip newClip = AudioClip.Create("RecordedSound", position * channels, channels, FREQUENCY, false); newClip.SetData(samplesArray, 0); _audioSource.clip = newClip; } } }