diff --git a/HearingProtector.csproj b/HearingProtector.csproj index 5de8ecb..33b21d0 100644 --- a/HearingProtector.csproj +++ b/HearingProtector.csproj @@ -2,11 +2,17 @@ Exe - net10.0 + net10.0-windows enable enable true true + HearingProtector.Program + + + + + diff --git a/Program.cs b/Program.cs index 45b56bd..0298498 100644 --- a/Program.cs +++ b/Program.cs @@ -1,10 +1,79 @@ -namespace HearingProtector +using NAudio.CoreAudioApi; + +namespace HearingProtector; + +internal static class Program { - internal 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() { - static void Main(string[] args) + 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) { - Console.WriteLine("Hello, World!"); + 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); } } -} +} \ No newline at end of file