using NAudio.CoreAudioApi; namespace HearingProtector; internal static class Program { // ─── Настройки — только то, что интересно пользователю ─────────────── private const double MinVolumePercent = 1.0; // до какого % убавляем private const double FullDecayMinutes = 30.0; // за сколько минут 100% → MinVolume private const int PollMs = 1000; // шаг цикла private const float UserChangeEps = 0.005f;// порог «ручное изменение» private const int LogEveryTicks = 30; // лог раз в 30 сек // ─── Производные величины — считаются автоматически ────────────────── private static readonly double MinVolume = MinVolumePercent / 100.0; private static readonly double TotalTicks = FullDecayMinutes * 60_000.0 / PollMs; private static readonly double DecayPerTick = Math.Pow(MinVolume, 1.0 / TotalTicks); private static readonly double DbPerTick = 20.0 * Math.Log10(DecayPerTick); private static async Task Main() { using var enumerator = new MMDeviceEnumerator(); var device = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); var volume = device.AudioEndpointVolume; // ── Показываем пользователю, что мы из его настроек вывели ── Console.WriteLine($"Диапазон: 100% → {MinVolumePercent:F1}%"); Console.WriteLine($"Заданное время: {FullDecayMinutes:F1} мин " + $"({TotalTicks:F0} тиков по {PollMs} мс)"); Console.WriteLine($"Множитель/тик: ×{DecayPerTick:F6} " + $"= {DbPerTick:F4} дБ"); Console.WriteLine(); float current = volume.MasterVolumeLevelScalar; float lastSet = current; Console.WriteLine($"Стартуем с {current * 100:F1}%. Ctrl+C — выход."); Console.WriteLine(); int tick = 0; while (true) { float actual = volume.MasterVolumeLevelScalar; // 1) Реакция на ручное изменение громкости. if (Math.Abs(actual - lastSet) > UserChangeEps) { var dir = actual > lastSet ? "повышение" : "понижение"; Console.WriteLine( $"[{DateTime.Now:HH:mm:ss}] Ручное изменение ({dir}): " + $"{lastSet * 100:F1}% → {actual * 100:F1}%"); current = actual; } // 2) Один экспоненциальный шаг вниз. current = (float)Math.Max(MinVolume, current * DecayPerTick); // 3) Применяем, если значение реально отличается. if (Math.Abs(volume.MasterVolumeLevelScalar - current) > 0.0005f) volume.MasterVolumeLevelScalar = current; lastSet = current; // 4) Редкий лог с ETA. if (++tick % LogEveryTicks == 0) { double etaSec = Math.Log(MinVolume / current) / Math.Log(DecayPerTick) * PollMs / 1000.0; var eta = TimeSpan.FromSeconds(Math.Max(0, etaSec)); Console.WriteLine( $"[{DateTime.Now:HH:mm:ss}] " + $"Громкость: {current * 100:F1}% " + $"(до минимума ~{eta:hh\\:mm\\:ss})"); } await Task.Delay(PollMs); } } }