using System.Diagnostics; using Toak.Core; using Toak.IO; using Toak.Core.Interfaces; namespace Toak.Audio; public class AudioRecorder : IAudioRecorder { private readonly string WavPath = Constants.Paths.RecordingWavFile; private readonly IRecordingStateTracker _stateTracker; private readonly INotifications _notifications; public AudioRecorder(IRecordingStateTracker stateTracker, INotifications notifications) { _stateTracker = stateTracker; _notifications = notifications; } public string GetWavPath() => WavPath; public void StartRecording() { if (File.Exists(WavPath)) { Logger.LogDebug($"Deleting old audio file: {WavPath}"); File.Delete(WavPath); } Logger.LogDebug("Starting pw-record to record audio..."); var pInfo = new ProcessStartInfo { FileName = Constants.Commands.AudioRecord, Arguments = $"--rate=16000 --channels=1 --format=s16 \"{WavPath}\"", UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }; var process = Process.Start(pInfo); if (process != null) { _stateTracker.SetRecording(process.Id); _notifications.Notify("Recording Started"); } } public void StopRecording() { var pid = _stateTracker.GetRecordingPid(); if (pid.HasValue) { Logger.LogDebug($"Found active recording process with PID {pid.Value}. Attempting to stop..."); try { var process = Process.GetProcessById(pid.Value); if (!process.HasExited) { // Gracefully stop pw-record using SIGINT to ensure WAV headers are finalizing cleanly Process.Start(new ProcessStartInfo { FileName = Constants.Commands.ProcessKill, Arguments = $"-INT {pid.Value}", CreateNoWindow = true, UseShellExecute = false })?.WaitForExit(); process.WaitForExit(2000); // give it a moment to flush } } catch (Exception ex) { // Process might already be dead Console.WriteLine($"[AudioRecorder] Error stopping pw-record: {ex.Message}"); } finally { _stateTracker.ClearRecording(); } } } }