using System.Diagnostics; using Toak.Core; using Toak.Core.Interfaces; namespace Toak.Audio; public class AudioRecorder(IRecordingStateTracker stateTracker, INotifications notifications) : IAudioRecorder { private readonly string _wavPath = Constants.Paths.RecordingWavFile; private readonly IRecordingStateTracker _stateTracker = stateTracker; private readonly INotifications _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(); } } } }