-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAudioKeepAwake.cs
More file actions
196 lines (167 loc) · 7.39 KB
/
Copy pathAudioKeepAwake.cs
File metadata and controls
196 lines (167 loc) · 7.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using System.Drawing;
namespace AudioKeepAwake
{
class Program
{
// Windows API flags to prevent sleep
[FlagsAttribute]
public enum EXECUTION_STATE : uint
{
ES_CONTINUOUS = 0x80000000,
ES_SYSTEM_REQUIRED = 0x00000001,
ES_DISPLAY_REQUIRED = 0x00000002
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
static NotifyIcon trayIcon;
static bool wasPlaying = false;
static Icon activeIcon;
static Icon inactiveIcon;
[STAThread]
static void Main(string[] args)
{
// Use a mutex to prevent multiple instances from running concurrently
bool createdNew;
using (Mutex mutex = new Mutex(false, "Global\\AudioKeepAwakeMutex", out createdNew))
{
if (!createdNew)
{
return; // Another instance is already running
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Pre-generate icon states
activeIcon = CreateZzzIcon(Color.LimeGreen, "Zzz");
inactiveIcon = CreateZzzIcon(Color.Orange, "Zzz");
// Setup Tray Icon
trayIcon = new NotifyIcon();
trayIcon.Icon = inactiveIcon;
trayIcon.Text = "AudioKeepAwake: Allowing Sleep";
trayIcon.Visible = true;
// Setup Context Menu for right click -> Exit
ContextMenu trayMenu = new ContextMenu();
trayMenu.MenuItems.Add("Exit", OnExit);
trayIcon.ContextMenu = trayMenu;
// Setup a timer to check audio
System.Windows.Forms.Timer checkTimer = new System.Windows.Forms.Timer();
checkTimer.Interval = 60000; // 60 seconds
checkTimer.Tick += new EventHandler(CheckAudioTick);
checkTimer.Start();
// Check right away
CheckAudioTick(null, null);
// Start message loop so icon appears
Application.Run();
}
}
static void CheckAudioTick(object sender, EventArgs e)
{
bool isPlaying = IsAudioPlaying();
if (isPlaying && !wasPlaying)
{
// Prevent sleep and display turn-off
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS | EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED);
wasPlaying = true;
trayIcon.Icon = activeIcon;
trayIcon.Text = "AudioKeepAwake: Preventing Sleep";
}
else if (!isPlaying && wasPlaying)
{
// Allow sleep
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
wasPlaying = false;
trayIcon.Icon = inactiveIcon;
trayIcon.Text = "AudioKeepAwake: Allowing Sleep";
}
}
static void OnExit(object sender, EventArgs e)
{
// Allow sleep before exiting, just in case
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
trayIcon.Visible = false;
Application.Exit();
}
static Icon CreateZzzIcon(Color mainColor, string text)
{
Bitmap bmp = new Bitmap(16, 16);
using (Graphics g = Graphics.FromImage(bmp))
{
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
g.Clear(Color.Transparent);
using (Brush brush = new SolidBrush(mainColor))
{
// Draw text in a small font fitting 16x16
using (Font font = new Font("Tahoma", 7, FontStyle.Bold))
{
SizeF textSize = g.MeasureString(text, font);
float x = (16 - textSize.Width) / 2;
float y = (16 - textSize.Height) / 2;
g.DrawString(text, font, brush, x, y);
}
}
}
return Icon.FromHandle(bmp.GetHicon());
}
static bool IsAudioPlaying()
{
try
{
// Instantiate MMDeviceEnumerator
Type deviceEnumeratorType = Type.GetTypeFromCLSID(new Guid("BCDE0395-E52F-467C-8E3D-C4579291692E"));
if (deviceEnumeratorType == null) return false;
IMMDeviceEnumerator enumerator = (IMMDeviceEnumerator)Activator.CreateInstance(deviceEnumeratorType);
// Get the default audio endpoint for playing sounds (eRender = 0, eConsole = 0)
IMMDevice device;
enumerator.GetDefaultAudioEndpoint(0, 0, out device);
if (device != null)
{
// Activate AudioMeterInformation interface
Guid IID_IAudioMeterInformation = new Guid("C02216F6-8C67-4B5B-9D00-D008E73E0064");
object meterObject;
device.Activate(ref IID_IAudioMeterInformation, 1, IntPtr.Zero, out meterObject);
IAudioMeterInformation meter = (IAudioMeterInformation)meterObject;
float peakValue;
meter.GetPeakValue(out peakValue);
// Clean up COM objects
Marshal.ReleaseComObject(meter);
Marshal.ReleaseComObject(device);
Marshal.ReleaseComObject(enumerator);
// 0.0001f avoids tiny background noise triggering it
return peakValue > 0.0001f;
}
}
catch
{
// In case of exceptions (e.g. no audio devices), assume not playing
}
return false;
}
}
// -- Minimum COM Interop definitions --
[ComImport]
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDeviceEnumerator
{
// Pad methods to match vtable
[PreserveSig] int EnumAudioEndpoints(int dataFlow, int dwStateMask, out IntPtr ppDevices);
[PreserveSig] int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice ppEndpoint);
}
[ComImport]
[Guid("D666063F-1587-4E43-81F1-B948E807363F")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDevice
{
[PreserveSig] int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
}
[ComImport]
[Guid("C02216F6-8C67-4B5B-9D00-D008E73E0064")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAudioMeterInformation
{
[PreserveSig] int GetPeakValue(out float pfPeak);
}
}