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;
}
}
}