├── README.md ├── .gitattributes ├── FlowTimer ├── Resources │ ├── SDL2.dll │ ├── lock.png │ ├── pin.png │ ├── clack.wav │ ├── click1.wav │ ├── ping1.wav │ └── ping2.wav ├── Dependencies │ └── Newtonsoft.Json.dll ├── App.config ├── packages.config ├── TimerError.cs ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── Program.cs ├── JsonTimersFile.cs ├── HotkeySelection.cs ├── JsonIGTTracking.cs ├── SpriteSheet.cs ├── BaseTimer.cs ├── HotkeySelection.Designer.cs ├── FileSystem.cs ├── Win32.cs ├── MainForm.cs ├── Extensions.cs ├── SettingsForm.resx ├── HotkeySelection.resx ├── AudioContext.cs ├── Settings.cs ├── MainForm.resx ├── SettingsForm.Designer.cs ├── SDL.cs ├── SettingsForm.cs ├── FlowTimer.csproj ├── VariableOffsetTimer.cs ├── MMDeviceAPI.cs ├── FixedOffsetTimer.cs ├── FlowTimer.cs ├── IGTTracking.cs └── MainForm.Designer.cs ├── FlowTimer.sln └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | Direct Download: https://gunnermaniac.com/ft -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /FlowTimer/Resources/SDL2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringflow/FlowTimer/HEAD/FlowTimer/Resources/SDL2.dll -------------------------------------------------------------------------------- /FlowTimer/Resources/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringflow/FlowTimer/HEAD/FlowTimer/Resources/lock.png -------------------------------------------------------------------------------- /FlowTimer/Resources/pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringflow/FlowTimer/HEAD/FlowTimer/Resources/pin.png -------------------------------------------------------------------------------- /FlowTimer/Resources/clack.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringflow/FlowTimer/HEAD/FlowTimer/Resources/clack.wav -------------------------------------------------------------------------------- /FlowTimer/Resources/click1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringflow/FlowTimer/HEAD/FlowTimer/Resources/click1.wav -------------------------------------------------------------------------------- /FlowTimer/Resources/ping1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringflow/FlowTimer/HEAD/FlowTimer/Resources/ping1.wav -------------------------------------------------------------------------------- /FlowTimer/Resources/ping2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringflow/FlowTimer/HEAD/FlowTimer/Resources/ping2.wav -------------------------------------------------------------------------------- /FlowTimer/Dependencies/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stringflow/FlowTimer/HEAD/FlowTimer/Dependencies/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /FlowTimer/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /FlowTimer/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /FlowTimer/TimerError.cs: -------------------------------------------------------------------------------- 1 | namespace FlowTimer { 2 | 3 | public enum TimerError { 4 | 5 | NoError, 6 | InvalidOffset, 7 | InvalidInterval, 8 | InvalidNumBeeps, 9 | InvalidFrame, 10 | InvalidFPS, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /FlowTimer/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FlowTimer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace FlowTimer { 8 | static class Program { 9 | /// 10 | /// The main entry point for the application. 11 | /// 12 | [STAThread] 13 | static void Main() { 14 | FileSystem.Init(); 15 | FileSystem.UnpackAllFileExtensions("wav", FlowTimer.Beeps); 16 | FileSystem.Unpack("SDL2.dll", FlowTimer.Folder + "SDL2.dll"); 17 | Win32.SetDllDirectory(FlowTimer.Folder); 18 | 19 | Application.EnableVisualStyles(); 20 | Application.SetCompatibleTextRenderingDefault(false); 21 | Application.Run(new MainForm()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /FlowTimer/JsonTimersFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace FlowTimer { 4 | 5 | public class JsonTimersHeader { 6 | 7 | public int Version = FlowTimer.GetCurrentBuild(); 8 | // future use (maybe) 9 | } 10 | 11 | public class JsonTimer { 12 | 13 | public string Name; 14 | public string Offsets; 15 | public string Interval; 16 | public string NumBeeps; 17 | } 18 | 19 | public class JsonTimersFile { 20 | 21 | public JsonTimersHeader Header; 22 | public List Timers; 23 | 24 | // Json Constructor 25 | public JsonTimersFile() { } 26 | 27 | public JsonTimersFile(JsonTimersHeader header, List timers) => (Header, Timers) = (header, timers); 28 | 29 | public JsonTimer this[int i] { 30 | get { return Timers[i]; } 31 | set { Timers[i] = value; } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /FlowTimer/HotkeySelection.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Forms; 2 | using static FlowTimer.Win32; 3 | 4 | namespace FlowTimer { 5 | 6 | public partial class HotkeySelection : Form { 7 | 8 | public Keys Key; 9 | 10 | public HotkeySelection() { 11 | InitializeComponent(); 12 | TopMost = FlowTimer.Settings.Pinned; 13 | KeyDown += Form_KeyDown; 14 | } 15 | 16 | private void Form_KeyDown(object sender, KeyEventArgs e) { 17 | Key = (Keys) e.KeyValue; 18 | 19 | if(Key == Keys.Shift || Key == Keys.ShiftKey) { 20 | Key = GetAsyncKey(Keys.LShiftKey, Keys.RShiftKey); 21 | } else if(Key == Keys.Control || Key == Keys.ControlKey) { 22 | Key = GetAsyncKey(Keys.LControlKey, Keys.RControlKey); 23 | } else if(Key == Keys.Menu) { 24 | Key = GetAsyncKey(Keys.LMenu, Keys.RMenu); 25 | } 26 | 27 | DialogResult = DialogResult.OK; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /FlowTimer/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace FlowTimer.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.3.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /FlowTimer/JsonIGTTracking.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace FlowTimer { 4 | public class JsonIGTDelayer { 5 | public string Name; 6 | public double Delay; 7 | } 8 | 9 | public class JsonIGTDelayersGame { 10 | public string Game; 11 | public List Delayers; 12 | } 13 | 14 | public class JsonIGTDelayersSettings { 15 | public JsonTimersHeader Header; 16 | public List Games; 17 | } 18 | 19 | public class JsonIGTTimer { 20 | 21 | public string Name; 22 | public string Frame; 23 | public string Offsets; 24 | public string Interval; 25 | public string NumBeeps; 26 | } 27 | 28 | public class JsonIGTTimersFile { 29 | 30 | public JsonTimersHeader Header; 31 | public List Timers; 32 | 33 | public JsonIGTTimersFile() { } 34 | 35 | public JsonIGTTimersFile(JsonTimersHeader header, List timers) => (Header, Timers) = (header, timers); 36 | 37 | public JsonIGTTimer this[int i] { 38 | get { return Timers[i]; } 39 | set { Timers[i] = value; } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /FlowTimer/SpriteSheet.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace FlowTimer { 4 | 5 | public class SpriteSheet { 6 | 7 | private Bitmap[] SubBitmaps; 8 | 9 | public SpriteSheet(Bitmap bitmap, int spriteWidth, int spriteHeight) { 10 | int numSpritesPerColumn = bitmap.Width / spriteWidth; 11 | int numSpritesPerRow = bitmap.Height / spriteHeight; 12 | 13 | SubBitmaps = new Bitmap[numSpritesPerColumn * numSpritesPerRow]; 14 | 15 | for(int x = 0; x < numSpritesPerColumn; x++) { 16 | for(int y = 0; y < numSpritesPerRow; y++) { 17 | int index = x + y * numSpritesPerColumn; 18 | SubBitmaps[index] = new Bitmap(spriteWidth, spriteHeight); 19 | using(Graphics graphics = Graphics.FromImage(SubBitmaps[index])) { 20 | graphics.DrawImage(bitmap, new Rectangle(0, 0, spriteWidth, spriteHeight), x * spriteWidth, y * spriteHeight, spriteWidth, spriteHeight, GraphicsUnit.Pixel); 21 | } 22 | } 23 | } 24 | } 25 | 26 | public Bitmap this[int index] { 27 | get { return SubBitmaps[index]; } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /FlowTimer/BaseTimer.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Forms; 2 | 3 | namespace FlowTimer { 4 | 5 | public abstract class BaseTimer { 6 | 7 | public delegate double TimerUpdateCallback(double startTime); 8 | 9 | public TabPage Tab; 10 | public TimerUpdateCallback TimerCallback; 11 | public Control[] ControlsToCopy; 12 | 13 | public bool Selected { 14 | get { return FlowTimer.MainForm.TabControl.SelectedTab == Tab; } 15 | } 16 | 17 | public BaseTimer(TabPage tab, TimerUpdateCallback timerCallback, params Control[] controlsToCopy) { 18 | Tab = tab; 19 | Tab.SetDrawing(false); 20 | Tab.RemoveKeyControls(); 21 | TimerCallback = timerCallback; 22 | ControlsToCopy = controlsToCopy; 23 | } 24 | 25 | public virtual void OnLoad() { 26 | foreach(Control control in ControlsToCopy) { 27 | Tab.Controls.Add(control); 28 | } 29 | } 30 | 31 | public abstract void OnInit(); 32 | public abstract void OnTimerStart(); 33 | public abstract void OnVisualTimerStart(); 34 | public abstract void OnTimerStop(); 35 | public abstract void OnKeyEvent(Keys key); 36 | public abstract void OnBeepSoundChange(); 37 | public abstract void OnBeepVolumeChange(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /FlowTimer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("FlowTimer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("FlowTimer")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("fce97535-41fd-4334-94d4-cd37e440927f")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion( "48.0.0.0")] 36 | [assembly: AssemblyFileVersion("48.0.0.0")] 37 | -------------------------------------------------------------------------------- /FlowTimer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29418.71 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowTimer", "FlowTimer\FlowTimer.csproj", "{FCE97535-41FD-4334-94D4-CD37E440927F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {FCE97535-41FD-4334-94D4-CD37E440927F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {FCE97535-41FD-4334-94D4-CD37E440927F}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {FCE97535-41FD-4334-94D4-CD37E440927F}.Debug|x64.ActiveCfg = Debug|x64 21 | {FCE97535-41FD-4334-94D4-CD37E440927F}.Debug|x64.Build.0 = Debug|x64 22 | {FCE97535-41FD-4334-94D4-CD37E440927F}.Debug|x86.ActiveCfg = Debug|x86 23 | {FCE97535-41FD-4334-94D4-CD37E440927F}.Debug|x86.Build.0 = Debug|x86 24 | {FCE97535-41FD-4334-94D4-CD37E440927F}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {FCE97535-41FD-4334-94D4-CD37E440927F}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {FCE97535-41FD-4334-94D4-CD37E440927F}.Release|x64.ActiveCfg = Release|x64 27 | {FCE97535-41FD-4334-94D4-CD37E440927F}.Release|x64.Build.0 = Release|x64 28 | {FCE97535-41FD-4334-94D4-CD37E440927F}.Release|x86.ActiveCfg = Release|x86 29 | {FCE97535-41FD-4334-94D4-CD37E440927F}.Release|x86.Build.0 = Release|x86 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {7ADB28D9-CCA9-4591-B7CE-2353B894952C} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /FlowTimer/HotkeySelection.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace FlowTimer { 2 | partial class HotkeySelection { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if(disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | this.Label = new System.Windows.Forms.Label(); 27 | this.SuspendLayout(); 28 | // 29 | // Label 30 | // 31 | this.Label.AutoSize = true; 32 | this.Label.Location = new System.Drawing.Point(21, 22); 33 | this.Label.Name = "Label"; 34 | this.Label.Size = new System.Drawing.Size(95, 13); 35 | this.Label.TabIndex = 0; 36 | this.Label.Text = "Press any button..."; 37 | // 38 | // HotkeySelection 39 | // 40 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 41 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 42 | this.ClientSize = new System.Drawing.Size(137, 57); 43 | this.Controls.Add(this.Label); 44 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 45 | this.Name = "HotkeySelection"; 46 | this.ResumeLayout(false); 47 | this.PerformLayout(); 48 | 49 | } 50 | 51 | #endregion 52 | private System.Windows.Forms.Label Label; 53 | } 54 | } -------------------------------------------------------------------------------- /FlowTimer/FileSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.IO; 4 | using System.Reflection; 5 | using System.Security.Cryptography; 6 | 7 | namespace FlowTimer { 8 | 9 | public static class FileSystem { 10 | 11 | private static Assembly Asm; 12 | private static MD5 MD5; 13 | 14 | public static void Init() { 15 | Asm = Assembly.GetExecutingAssembly(); 16 | MD5 = MD5.Create(); 17 | 18 | AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => Assembly.Load(ReadPackedResource("FlowTimer.Dependencies.Newtonsoft.Json.dll")); 19 | } 20 | 21 | public static void UnpackAllFileExtensions(string extension, string destFolder) { 22 | foreach(string file in Asm.GetManifestResourceNames()) { 23 | if(file.StartsWith("FlowTimer.Resources")) { 24 | string[] splitarray = file.Split('.'); 25 | string filename = splitarray[2]; 26 | string fileextension = splitarray[3]; 27 | 28 | if(fileextension.ToLower() == extension.ToLower()) { 29 | Unpack(filename + "." + fileextension, destFolder + filename + "." + fileextension); 30 | } 31 | } 32 | } 33 | } 34 | 35 | public static void Unpack(string src, string dest) { 36 | string directory = Path.GetDirectoryName(dest); 37 | if(!Directory.Exists(directory)) { 38 | Directory.CreateDirectory(directory); 39 | } 40 | 41 | byte[] data = ReadPackedResource("FlowTimer.Resources." + src); 42 | if(ShouldUnpack(data, dest)) { 43 | Console.WriteLine("Unpacking " + src + " to " + dest); 44 | File.WriteAllBytes(dest, data); 45 | } 46 | } 47 | 48 | private static bool ShouldUnpack(byte[] data, string dest) { 49 | if(!File.Exists(dest)) { 50 | return true; 51 | } else { 52 | return !Enumerable.SequenceEqual(MD5.ComputeHash(data), MD5.ComputeHash(File.ReadAllBytes(dest))); 53 | } 54 | } 55 | 56 | public static Stream ReadPackedResourceStream(string src) { 57 | return Asm.GetManifestResourceStream(src); 58 | } 59 | 60 | public static byte[] ReadPackedResource(string src) { 61 | using(Stream stream = ReadPackedResourceStream(src)) { 62 | byte[] binary = new byte[stream.Length]; 63 | stream.Read(binary, 0, binary.Length); 64 | return binary; 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /FlowTimer/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace FlowTimer.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FlowTimer.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /FlowTimer/Win32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Diagnostics; 4 | using System.Windows.Forms; 5 | 6 | namespace FlowTimer { 7 | 8 | public static class Win32 { 9 | 10 | public const int WH_KEYBOARD_LL = 0x000D; 11 | 12 | public const int WM_SETREDRAW = 0x000B; 13 | public const int WM_KEYDOWN = 0x0100; 14 | public const int WM_KEYUP = 0x0101; 15 | public const int WM_SYSKEYDOWN = 0x0104; 16 | public const int WM_SYSKEYUP = 0x0105; 17 | 18 | private static double Frequency; 19 | 20 | public delegate IntPtr Proc(int nCode, int wParam, IntPtr lParam); 21 | 22 | public static IntPtr SetHook(int id, Proc proc) { 23 | using(Process curProcess = Process.GetCurrentProcess()) { 24 | using(ProcessModule curModule = curProcess.MainModule) { 25 | return SetWindowsHookEx(id, proc, GetModuleHandle(curModule.ModuleName), 0); 26 | } 27 | } 28 | } 29 | 30 | public static Keys GetAsyncKey(params Keys[] keys) { 31 | foreach(Keys key in keys) { 32 | if(GetAsyncKeyState(key) != 0) { 33 | return key; 34 | } 35 | } 36 | 37 | return Keys.None; 38 | } 39 | 40 | public static void InitTiming() { 41 | long freq; 42 | QueryPerformanceFrequency(out freq); 43 | Frequency = freq / 1000.0; 44 | } 45 | 46 | public static double GetTime() { 47 | long timeStamp; 48 | QueryPerformanceCounter(out timeStamp); 49 | return timeStamp / Frequency; 50 | } 51 | 52 | [DllImport("user32.dll")] 53 | public static extern int SendMessage(IntPtr hWnd, int wMsg, bool wParam, int lParam); 54 | 55 | [DllImport("user32.dll")] 56 | public static extern IntPtr SetWindowsHookEx(int idHook, Proc lpfn, IntPtr hMod, uint dwThreadId); 57 | 58 | [DllImport("user32.dll")] 59 | [return: MarshalAs(UnmanagedType.Bool)] 60 | public static extern bool UnhookWindowsHookEx(IntPtr hhk); 61 | 62 | [DllImport("user32.dll")] 63 | public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, int wParam, IntPtr lParam); 64 | 65 | [DllImport("user32.dll")] 66 | public static extern short GetKeyState(Keys vKey); 67 | 68 | [DllImport("user32.dll")] 69 | public static extern short GetAsyncKeyState(Keys vKey); 70 | 71 | [DllImport("kernel32.dll")] 72 | public static extern IntPtr GetModuleHandle(string lpModuleName); 73 | 74 | [DllImport("kernel32.dll")] 75 | public static extern bool SetDllDirectory(string lpPathName); 76 | 77 | [DllImport("kernel32.dll")] 78 | public static extern bool QueryPerformanceCounter(out long lpPerformanceCount); 79 | 80 | [DllImport("kernel32.dll")] 81 | public static extern bool QueryPerformanceFrequency(out long lpFrequency); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /FlowTimer/MainForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace FlowTimer { 5 | 6 | public unsafe partial class MainForm : Form { 7 | 8 | public MainForm() { 9 | InitializeComponent(); 10 | FlowTimer.SetMainForm(this); 11 | FlowTimer.RegisterTabs(TabPageFixedOffset, TabPageVariableOffset, TabPageIGTTracking); 12 | FlowTimer.Init(); 13 | } 14 | 15 | private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { 16 | FlowTimer.Destroy(); 17 | } 18 | 19 | private void ButtonAdd_Click(object sender, EventArgs e) { 20 | FlowTimer.FixedOffset.AddTimer(); 21 | } 22 | 23 | private void ButtonStart_Click(object sender, EventArgs e) { 24 | FlowTimer.StartTimer(); 25 | } 26 | 27 | private void ButtonStop_Click(object sender, EventArgs e) { 28 | FlowTimer.StopTimer(false); 29 | } 30 | 31 | private void ButtonSettings_Click(object sender, EventArgs e) { 32 | FlowTimer.OpenSettingsForm(); 33 | } 34 | 35 | private void ButtonLoadTimers_Click(object sender, EventArgs e) { 36 | FlowTimer.FixedOffset.OpenLoadTimersDialog(); 37 | } 38 | 39 | private void ButtonSaveTimers_Click(object sender, EventArgs e) { 40 | FlowTimer.FixedOffset.OpenSaveTimersDialog(); 41 | } 42 | 43 | private void PictureBoxPin_Click(object sender, EventArgs e) { 44 | FlowTimer.TogglePin(); 45 | } 46 | 47 | private void ButtonSubmit_Click(object sender, EventArgs e) { 48 | FlowTimer.VariableOffset.Submit(); 49 | } 50 | 51 | private void VariableTimer_DataChange(object sender, EventArgs e) { 52 | FlowTimer.VariableOffset.OnDataChange(); 53 | } 54 | 55 | private void ButtonPlus_Click(object sender, EventArgs e) { 56 | FlowTimer.VariableOffset.ChangeAudio(1); 57 | } 58 | 59 | private void ButtonMinus_Click(object sender, EventArgs e) { 60 | FlowTimer.VariableOffset.ChangeAudio(-1); 61 | } 62 | 63 | private void ButtonUndo_Click(object sender, EventArgs e) { 64 | FlowTimer.VariableOffset.Undo(); 65 | } 66 | 67 | private void ButtonPlay_Click(object sender, EventArgs e) { 68 | FlowTimer.IGTTracking.Play(); 69 | } 70 | 71 | private void ComboBoxFPS3_DataChange(object sender, EventArgs e) { 72 | FlowTimer.IGTTracking.OnDataChange(); 73 | } 74 | 75 | private void ButtonUndoPlay_Click(object sender, EventArgs e) { 76 | FlowTimer.IGTTracking.Undo(); 77 | } 78 | 79 | private void ButtonAdd3_Click(object sender, EventArgs e) { 80 | FlowTimer.IGTTracking.AddTimer(); 81 | } 82 | 83 | private void ButtonLoadTimers3_Click(object sender, EventArgs e) { 84 | FlowTimer.IGTTracking.OpenLoadTimersDialog(); 85 | } 86 | 87 | private void ButtonSaveTimers3_Click(object sender, EventArgs e) { 88 | FlowTimer.IGTTracking.OpenSaveTimersDialog(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /FlowTimer/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using System.Threading; 4 | using System.Globalization; 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace FlowTimer { 9 | 10 | public static class Extensions { 11 | 12 | public static void AbortIfAlive(this Thread thread) { 13 | if(thread.IsAlive) { 14 | thread.Abort(); 15 | thread.Join(); 16 | } 17 | } 18 | 19 | public static double ToDouble(this string val) { 20 | return double.Parse(val, CultureInfo.InvariantCulture); 21 | } 22 | 23 | public static string ToFormattedString(this double val) { 24 | return val.ToString("F3", CultureInfo.InvariantCulture); 25 | } 26 | 27 | public static string ToFormattedString(this Keys val) { 28 | return val == Keys.None ? "Unset" : val.ToString(); 29 | } 30 | 31 | public static string ToFormattedString(this KeyMethod val) { 32 | switch(val) { 33 | case KeyMethod.OnPress: return "On Press"; 34 | case KeyMethod.OnRelease: return "On Release"; 35 | } 36 | 37 | throw new Exception("Unknown KeyMethod: " + Enum.GetName(typeof(KeyMethod), val)); 38 | } 39 | 40 | public static bool IsActivatedByEvent(this KeyMethod val, int wParam) { 41 | switch(val) { 42 | case KeyMethod.OnPress: return wParam == Win32.WM_KEYDOWN || wParam == Win32.WM_SYSKEYDOWN; 43 | case KeyMethod.OnRelease: return wParam == Win32.WM_KEYUP || wParam == Win32.WM_SYSKEYUP; 44 | } 45 | 46 | throw new Exception("Unknown KeyMethod: " + Enum.GetName(typeof(KeyMethod), val)); 47 | } 48 | 49 | public static void SetDrawing(this Control control, bool enabled) { 50 | Win32.SendMessage(control.Handle, Win32.WM_SETREDRAW, enabled, 0); 51 | } 52 | 53 | // credit to https://stackoverflow.com/a/32859334/7281499 54 | private static readonly Action SetStyle = (Action) Delegate.CreateDelegate(typeof(Action), typeof(Control).GetMethod("SetStyle", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(ControlStyles), typeof(bool) }, null)); 55 | public static void DisableSelect(this Control control) { 56 | SetStyle(control, ControlStyles.Selectable, false); 57 | } 58 | 59 | public static T[] Subarray(this T[] source, int index, int length) { 60 | T[] subarray = new T[length]; 61 | Array.Copy(source, index, subarray, 0, length); 62 | return subarray; 63 | } 64 | 65 | public static T Consume(this byte[] array, ref int pointer) where T : unmanaged { 66 | T ret = ReadStruct(array, pointer); 67 | pointer += Marshal.SizeOf(); 68 | return ret; 69 | } 70 | 71 | public static T ReadStruct(this byte[] array, int pointer) where T : unmanaged { 72 | int structSize = Marshal.SizeOf(); 73 | IntPtr ptr = Marshal.AllocHGlobal(structSize); 74 | Marshal.Copy(array, pointer, ptr, structSize); 75 | T str = Marshal.PtrToStructure(ptr); 76 | Marshal.FreeHGlobal(ptr); 77 | return str; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /FlowTimer/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /FlowTimer/SettingsForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /FlowTimer/HotkeySelection.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /FlowTimer/AudioContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using static FlowTimer.SDL; 5 | 6 | namespace FlowTimer { 7 | 8 | public unsafe class AudioContext { 9 | 10 | public static int MinBufferSize; 11 | 12 | public SDL_AudioSpec AudioSpec; 13 | public uint DeviceId; 14 | 15 | public int SampleRate { 16 | get { return AudioSpec.freq; } 17 | } 18 | 19 | public int Format { 20 | get { return AudioSpec.format; } 21 | } 22 | 23 | public int NumChannels { 24 | get { return AudioSpec.channels; } 25 | } 26 | 27 | public int Samples { 28 | get { return AudioSpec.samples; } 29 | } 30 | 31 | public int BytesPerSample; 32 | 33 | public static void GlobalInit() { 34 | if(SDL_Init(SDL_INIT_AUDIO) < 0) { 35 | throw new Exception("Unable to load SDL!"); 36 | } 37 | 38 | MMDevice audioDevice = MMDeviceAPI.CreateMMDeviceEnumerator().GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); 39 | AudioClient audioClient = audioDevice.CreateAudioCilent(); 40 | audioClient.GetSharedModeEnginePeriod(out _, out _, out MinBufferSize, out _); 41 | } 42 | 43 | public static void GlobalDestroy() { 44 | SDL_Quit(); 45 | } 46 | 47 | public AudioContext(int freq, ushort format, byte channels) { 48 | AudioSpec = new SDL_AudioSpec() { 49 | freq = freq, 50 | format = format, 51 | channels = channels, 52 | samples = (ushort) MinBufferSize, 53 | }; 54 | 55 | switch(format) { 56 | case AUDIO_U8: { 57 | BytesPerSample = 1; 58 | } break; 59 | 60 | case AUDIO_S16LSB: { 61 | BytesPerSample = 2; 62 | } break; 63 | }; 64 | 65 | DeviceId = SDL_OpenAudioDevice(null, 0, ref AudioSpec, out AudioSpec, 0); 66 | if(DeviceId == 0) { 67 | throw new Exception(SDL_GetError()); 68 | } 69 | 70 | SDL_PauseAudioDevice(DeviceId, 0); 71 | } 72 | 73 | public void Destroy() { 74 | SDL_CloseAudioDevice(DeviceId); 75 | } 76 | 77 | public void QueueAudio(byte[] pcm) { 78 | fixed(byte* ptr = pcm) SDL_QueueAudio(DeviceId, ptr, pcm.Length); 79 | } 80 | 81 | public void ClearQueuedAudio() { 82 | SDL_ClearQueuedAudio(DeviceId); 83 | } 84 | } 85 | 86 | public static class Wave { 87 | 88 | private static readonly uint WaveId_RIFF = MakeRiff("RIFF"); 89 | private static readonly uint WaveId_WAVE = MakeRiff("WAVE"); 90 | private static readonly uint WaveId_data = MakeRiff("data"); 91 | private static readonly uint WaveId_fmt = MakeRiff("fmt "); 92 | 93 | private static uint MakeRiff(string str) { 94 | return (uint) ((str[3] << 24) | (str[2] << 16) | (str[1] << 8) | str[0]); 95 | } 96 | 97 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 98 | private struct WaveHeader { 99 | 100 | public uint RiffId; 101 | public uint Size; 102 | public uint WaveId; 103 | } 104 | 105 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 106 | private struct WaveChunkHeader { 107 | 108 | public uint Id; 109 | public uint Size; 110 | } 111 | 112 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 113 | private struct WaveFmt { 114 | 115 | public ushort FormatTag; 116 | public ushort Channels; 117 | public uint SampleRate; 118 | public uint AvgBytesPerSec; 119 | public ushort BlockAlign; 120 | public ushort BitsPerSample; 121 | } 122 | 123 | public static bool LoadWAV(string fileName, out byte[] pcm, out SDL_AudioSpec spec) { 124 | pcm = new byte[0]; 125 | spec = new SDL_AudioSpec(); 126 | 127 | byte[] bytes = File.ReadAllBytes(fileName); 128 | int pointer = 0; 129 | 130 | WaveHeader header = bytes.Consume(ref pointer); 131 | if(header.RiffId != WaveId_RIFF) return false; 132 | if(header.WaveId != WaveId_WAVE) return false; 133 | 134 | while(pointer < bytes.Length) { 135 | WaveChunkHeader chunkHeader = bytes.Consume(ref pointer); 136 | 137 | if(chunkHeader.Id == WaveId_fmt) { 138 | WaveFmt fmt = bytes.ReadStruct(pointer); 139 | spec = new SDL_AudioSpec() { 140 | freq = (int) fmt.SampleRate, 141 | channels = (byte) fmt.Channels, 142 | }; 143 | 144 | if(fmt.FormatTag != 1) return false; 145 | 146 | switch(fmt.BitsPerSample) { 147 | case 8: { 148 | spec.format = AUDIO_U8; 149 | } break; 150 | 151 | case 16: { 152 | spec.format = AUDIO_S16LSB; 153 | } break; 154 | }; 155 | } else if(chunkHeader.Id == WaveId_data) { 156 | pcm = bytes.Subarray(pointer, (int) chunkHeader.Size); 157 | } 158 | 159 | pointer += (int) chunkHeader.Size; 160 | } 161 | 162 | if(spec.format == AUDIO_U8) { 163 | byte[] newPCM = new byte[pcm.Length * 2]; 164 | for(int i = 0; i < pcm.Length; i++) { 165 | short sample16 = (short) ((pcm[i] - 0x80) << 8); 166 | newPCM[i * 2 + 0] = (byte) (sample16 & 0xFF); 167 | newPCM[i * 2 + 1] = (byte) (sample16 >> 8); 168 | } 169 | pcm = newPCM; 170 | spec.format = AUDIO_S16LSB; 171 | } 172 | 173 | return true; 174 | } 175 | } 176 | } -------------------------------------------------------------------------------- /FlowTimer/Settings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using System.Drawing; 4 | using Newtonsoft.Json; 5 | 6 | namespace FlowTimer { 7 | 8 | public class Settings { 9 | 10 | public Hotkey Start = new Hotkey(Keys.None, Keys.None, false); 11 | public Hotkey Stop = new Hotkey(Keys.None, Keys.None, false); 12 | public Hotkey Play = new Hotkey(Keys.None, Keys.None, false); 13 | public Hotkey Undo = new Hotkey(Keys.None, Keys.None, false); 14 | public Hotkey Up = new Hotkey(Keys.None, Keys.None, false); 15 | public Hotkey Down = new Hotkey(Keys.None, Keys.None, false); 16 | public Hotkey AddFrame = new Hotkey(Keys.None, Keys.None, false); 17 | public Hotkey SubFrame = new Hotkey(Keys.None, Keys.None, false); 18 | public Hotkey Add2 = new Hotkey(Keys.None, Keys.None, false); 19 | public Hotkey Sub2 = new Hotkey(Keys.None, Keys.None, false); 20 | public Hotkey Add3 = new Hotkey(Keys.None, Keys.None, false); 21 | public Hotkey Sub3 = new Hotkey(Keys.None, Keys.None, false); 22 | public Hotkey Add4 = new Hotkey(Keys.None, Keys.None, false); 23 | public Hotkey Sub4 = new Hotkey(Keys.None, Keys.None, false); 24 | public Hotkey Add5 = new Hotkey(Keys.None, Keys.None, false); 25 | public Hotkey Sub5 = new Hotkey(Keys.None, Keys.None, false); 26 | public Hotkey Add6 = new Hotkey(Keys.None, Keys.None, false); 27 | public Hotkey Sub6 = new Hotkey(Keys.None, Keys.None, false); 28 | public KeyMethod KeyMethod = KeyMethod.OnPress; 29 | public string Beep = "ping1"; 30 | public bool Pinned = false; 31 | public string LastLoadedTimers = null; 32 | public string LastLoadedIGTTimers = null; 33 | public bool AutoUpdate = false; 34 | public int Volume = 100; 35 | 36 | [JsonIgnore] 37 | private CheckBox _CheckBoxAutoUpdate; 38 | [JsonIgnore] 39 | public CheckBox CheckBoxAutoUpdate { 40 | get { return _CheckBoxAutoUpdate; } 41 | set { 42 | _CheckBoxAutoUpdate = value; 43 | _CheckBoxAutoUpdate.Checked = AutoUpdate; 44 | _CheckBoxAutoUpdate.Click += CheckBoxAutoUpdate_CheckChanged; 45 | } 46 | } 47 | 48 | private void CheckBoxAutoUpdate_CheckChanged(object sender, EventArgs args) { 49 | AutoUpdate = _CheckBoxAutoUpdate.Checked; 50 | } 51 | 52 | public string VariableFPS = "59.7275"; 53 | public string VariableOffset = "0"; 54 | public string VariableInterval = "500"; 55 | public string VariableNumBeeps = "5"; 56 | public string IGTFPS = "59.7275"; 57 | public string IGTGame = null; 58 | } 59 | 60 | public class Hotkey { 61 | 62 | public Input Primary; 63 | public Input Secondary; 64 | 65 | [JsonIgnore] 66 | private Button _ButtonClear; 67 | [JsonIgnore] 68 | public Button ButtonClear { 69 | get { return _ButtonClear; } 70 | set { 71 | _ButtonClear = value; 72 | _ButtonClear.Click += ButtonClear_Click; 73 | } 74 | } 75 | 76 | [JsonIgnore] 77 | private CheckBox _CheckBoxGlobal; 78 | [JsonIgnore] 79 | public CheckBox CheckBoxGlobal { 80 | get { return _CheckBoxGlobal; } 81 | set { 82 | _CheckBoxGlobal = value; 83 | _CheckBoxGlobal.Checked = Global; 84 | _CheckBoxGlobal.CheckedChanged += CheckBoxGlobal_CheckChanged; 85 | } 86 | } 87 | 88 | public bool Global; 89 | 90 | // Json Constructor 91 | public Hotkey() { } 92 | 93 | public Hotkey(Keys primary, Keys secondary, bool global) { 94 | Primary = new Input() { 95 | Key = primary, 96 | }; 97 | Secondary = new Input() { 98 | Key = secondary, 99 | }; 100 | Global = global; 101 | } 102 | 103 | public void SetControls(Button primaryButton, Button secondaryButton, Button clearButton, CheckBox globalCheckBox) { 104 | Primary.Button = primaryButton; 105 | Secondary.Button = secondaryButton; 106 | ButtonClear = clearButton; 107 | CheckBoxGlobal = globalCheckBox; 108 | } 109 | 110 | public bool IsPressed(Keys key) { 111 | return (Primary.Key == key || Secondary.Key == key) && (Form.ActiveForm == FlowTimer.MainForm || Global); 112 | } 113 | 114 | private void CheckBoxGlobal_CheckChanged(object sender, EventArgs args) { 115 | Global = _CheckBoxGlobal.Checked; 116 | } 117 | 118 | private void ButtonClear_Click(object sender, EventArgs args) { 119 | (Secondary.Key != Keys.None ? Secondary : Primary).Key = Keys.None; 120 | } 121 | } 122 | 123 | public class Input { 124 | 125 | [JsonIgnore] 126 | private Button _Button; 127 | [JsonIgnore] 128 | public Button Button { 129 | get { return _Button; } 130 | set { 131 | _Button = value; 132 | _Button.Click += Button_Click; 133 | UpdateButtonText(); 134 | } 135 | } 136 | 137 | [JsonIgnore] 138 | private Keys _Key; 139 | 140 | public Keys Key { 141 | get { return _Key; } 142 | set { 143 | _Key = value; 144 | UpdateButtonText(); 145 | } 146 | } 147 | 148 | private void UpdateButtonText() { 149 | if(_Button != null) { 150 | _Button.Text = _Key.ToFormattedString(); 151 | _Button.ForeColor = _Key == Keys.None ? Color.Gray : SystemColors.ControlText; 152 | } 153 | } 154 | 155 | private void Button_Click(object sender, EventArgs args) { 156 | HotkeySelection selection = new HotkeySelection(); 157 | if(selection.ShowDialog() == DialogResult.OK) { 158 | Key = selection.Key; 159 | } 160 | } 161 | } 162 | 163 | public enum KeyMethod { 164 | 165 | OnPress, 166 | OnRelease, 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /FlowTimer/MainForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | True 122 | 123 | -------------------------------------------------------------------------------- /FlowTimer/SettingsForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace FlowTimer { 2 | partial class SettingsForm { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if(disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | this.LabelBeep = new System.Windows.Forms.Label(); 27 | this.ComboBoxBeep = new System.Windows.Forms.ComboBox(); 28 | this.ButtonImportBeep = new System.Windows.Forms.Button(); 29 | this.ComboBoxKey = new System.Windows.Forms.ComboBox(); 30 | this.LabelKey = new System.Windows.Forms.Label(); 31 | this.TrackBarVolume = new System.Windows.Forms.TrackBar(); 32 | this.TextBoxVolume = new System.Windows.Forms.TextBox(); 33 | this.LabelVolume = new System.Windows.Forms.Label(); 34 | ((System.ComponentModel.ISupportInitialize)(this.TrackBarVolume)).BeginInit(); 35 | this.SuspendLayout(); 36 | // 37 | // LabelBeep 38 | // 39 | this.LabelBeep.AutoSize = true; 40 | this.LabelBeep.Location = new System.Drawing.Point(5, 40); 41 | this.LabelBeep.Name = "LabelBeep"; 42 | this.LabelBeep.Size = new System.Drawing.Size(35, 13); 43 | this.LabelBeep.TabIndex = 20; 44 | this.LabelBeep.Text = "Beep:"; 45 | // 46 | // ComboBoxBeep 47 | // 48 | this.ComboBoxBeep.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 49 | this.ComboBoxBeep.FormattingEnabled = true; 50 | this.ComboBoxBeep.Location = new System.Drawing.Point(52, 36); 51 | this.ComboBoxBeep.Name = "ComboBoxBeep"; 52 | this.ComboBoxBeep.Size = new System.Drawing.Size(112, 21); 53 | this.ComboBoxBeep.TabIndex = 22; 54 | // 55 | // ButtonImportBeep 56 | // 57 | this.ButtonImportBeep.Location = new System.Drawing.Point(168, 35); 58 | this.ButtonImportBeep.Name = "ButtonImportBeep"; 59 | this.ButtonImportBeep.Size = new System.Drawing.Size(114, 23); 60 | this.ButtonImportBeep.TabIndex = 23; 61 | this.ButtonImportBeep.Text = "Import Beep"; 62 | this.ButtonImportBeep.UseVisualStyleBackColor = true; 63 | this.ButtonImportBeep.Click += new System.EventHandler(this.ButtonImportBeep_Click); 64 | // 65 | // ComboBoxKey 66 | // 67 | this.ComboBoxKey.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 68 | this.ComboBoxKey.FormattingEnabled = true; 69 | this.ComboBoxKey.Location = new System.Drawing.Point(52, 88); 70 | this.ComboBoxKey.Name = "ComboBoxKey"; 71 | this.ComboBoxKey.Size = new System.Drawing.Size(112, 21); 72 | this.ComboBoxKey.TabIndex = 25; 73 | // 74 | // LabelKey 75 | // 76 | this.LabelKey.AutoSize = true; 77 | this.LabelKey.Location = new System.Drawing.Point(5, 91); 78 | this.LabelKey.Name = "LabelKey"; 79 | this.LabelKey.Size = new System.Drawing.Size(28, 13); 80 | this.LabelKey.TabIndex = 24; 81 | this.LabelKey.Text = "Key:"; 82 | // 83 | // TrackBarVolume 84 | // 85 | this.TrackBarVolume.AutoSize = false; 86 | this.TrackBarVolume.Location = new System.Drawing.Point(45, 63); 87 | this.TrackBarVolume.Maximum = 100; 88 | this.TrackBarVolume.Name = "TrackBarVolume"; 89 | this.TrackBarVolume.Size = new System.Drawing.Size(165, 21); 90 | this.TrackBarVolume.TabIndex = 28; 91 | this.TrackBarVolume.TickFrequency = 0; 92 | this.TrackBarVolume.TickStyle = System.Windows.Forms.TickStyle.None; 93 | this.TrackBarVolume.Value = 100; 94 | // 95 | // TextBoxVolume 96 | // 97 | this.TextBoxVolume.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.7F); 98 | this.TextBoxVolume.Location = new System.Drawing.Point(208, 62); 99 | this.TextBoxVolume.Name = "TextBoxVolume"; 100 | this.TextBoxVolume.Size = new System.Drawing.Size(73, 21); 101 | this.TextBoxVolume.TabIndex = 29; 102 | // 103 | // LabelVolume 104 | // 105 | this.LabelVolume.AutoSize = true; 106 | this.LabelVolume.Location = new System.Drawing.Point(6, 64); 107 | this.LabelVolume.Name = "LabelVolume"; 108 | this.LabelVolume.Size = new System.Drawing.Size(45, 13); 109 | this.LabelVolume.TabIndex = 30; 110 | this.LabelVolume.Text = "Volume:"; 111 | // 112 | // SettingsForm 113 | // 114 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 115 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 116 | this.ClientSize = new System.Drawing.Size(346, 115); 117 | this.Controls.Add(this.LabelVolume); 118 | this.Controls.Add(this.TextBoxVolume); 119 | this.Controls.Add(this.TrackBarVolume); 120 | this.Controls.Add(this.ComboBoxKey); 121 | this.Controls.Add(this.LabelKey); 122 | this.Controls.Add(this.ButtonImportBeep); 123 | this.Controls.Add(this.ComboBoxBeep); 124 | this.Controls.Add(this.LabelBeep); 125 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 126 | this.Name = "SettingsForm"; 127 | this.Text = "Settings"; 128 | ((System.ComponentModel.ISupportInitialize)(this.TrackBarVolume)).EndInit(); 129 | this.ResumeLayout(false); 130 | this.PerformLayout(); 131 | 132 | } 133 | 134 | #endregion 135 | public System.Windows.Forms.ComboBox ComboBoxBeep; 136 | public System.Windows.Forms.Label LabelBeep; 137 | public System.Windows.Forms.Button ButtonImportBeep; 138 | public System.Windows.Forms.ComboBox ComboBoxKey; 139 | public System.Windows.Forms.Label LabelKey; 140 | private System.Windows.Forms.TrackBar TrackBarVolume; 141 | private System.Windows.Forms.TextBox TextBoxVolume; 142 | public System.Windows.Forms.Label LabelVolume; 143 | } 144 | } -------------------------------------------------------------------------------- /FlowTimer/SDL.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace FlowTimer { 6 | 7 | public static unsafe class SDL { 8 | 9 | public const string DLL = "SDL2.dll"; 10 | 11 | public const uint SDL_INIT_TIMER = 0x00000001; 12 | public const uint SDL_INIT_AUDIO = 0x00000010; 13 | public const uint SDL_INIT_VIDEO = 0x00000020; 14 | public const uint SDL_INIT_JOYSTICK = 0x00000200; 15 | public const uint SDL_INIT_HAPTIC = 0x00001000; 16 | public const uint SDL_INIT_GAMECONTROLLER = 0x00002000; 17 | public const uint SDL_INIT_EVENTS = 0x00004000; 18 | public const uint SDL_INIT_NOPARACHUTE = 0x00100000; 19 | public const uint SDL_INIT_EVERYTHING = SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER; 20 | 21 | public const ushort AUDIO_U8 = 0x0008; 22 | public const ushort AUDIO_S8 = 0x8008; 23 | public const ushort AUDIO_U16LSB = 0x0010; 24 | public const ushort AUDIO_S16LSB = 0x8010; 25 | public const ushort AUDIO_U16MSB = 0x1010; 26 | public const ushort AUDIO_S16MSB = 0x9010; 27 | public const ushort AUDIO_U16 = AUDIO_U16LSB; 28 | public const ushort AUDIO_S16 = AUDIO_S16LSB; 29 | public const ushort AUDIO_S32LSB = 0x8020; 30 | public const ushort AUDIO_S32MSB = 0x9020; 31 | public const ushort AUDIO_S32 = AUDIO_S32LSB; 32 | public const ushort AUDIO_F32LSB = 0x8120; 33 | public const ushort AUDIO_F32MSB = 0x9120; 34 | public const ushort AUDIO_F32 = AUDIO_F32LSB; 35 | 36 | public static readonly ushort AUDIO_U16SYS = BitConverter.IsLittleEndian ? AUDIO_U16LSB : AUDIO_U16MSB; 37 | public static readonly ushort AUDIO_S16SYS = BitConverter.IsLittleEndian ? AUDIO_S16LSB : AUDIO_S16MSB; 38 | public static readonly ushort AUDIO_S32SYS = BitConverter.IsLittleEndian ? AUDIO_S32LSB : AUDIO_S32MSB; 39 | public static readonly ushort AUDIO_F32SYS = BitConverter.IsLittleEndian ? AUDIO_F32LSB : AUDIO_F32MSB; 40 | 41 | public const int SDL_AUDIO_ALLOW_FREQUENCY_CHANGE = 0x00000001; 42 | public const int SDL_AUDIO_ALLOW_FORMAT_CHANGE = 0x00000002; 43 | public const int SDL_AUDIO_ALLOW_CHANNELS_CHANGE = 0x00000004; 44 | public const int SDL_AUDIO_ALLOW_SAMPLES_CHANGE = 0x00000008; 45 | public const int SDL_AUDIO_ALLOW_ANY_CHANGE = SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_FORMAT_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE; 46 | 47 | public delegate void SDL_AudioCallback(IntPtr userdata, IntPtr stream, int len); 48 | 49 | public struct SDL_AudioSpec { 50 | 51 | public int freq; 52 | public ushort format; 53 | public byte channels; 54 | public byte silence; 55 | public ushort samples; 56 | public uint size; 57 | public SDL_AudioCallback callback; 58 | public IntPtr userdata; 59 | } 60 | 61 | [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] 62 | public static extern int SDL_Init(uint flags); 63 | 64 | [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] 65 | public static extern int SDL_Quit(); 66 | 67 | [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] 68 | public static extern void SDL_Delay(uint ms); 69 | 70 | [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] 71 | public static extern IntPtr SDL_malloc(IntPtr size); 72 | 73 | [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] 74 | public static extern void SDL_free(IntPtr ptr); 75 | 76 | [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] 77 | public static extern void SDL_memset(IntPtr dst, int c, uint len); 78 | 79 | [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] 80 | [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(SDLMarshaler), MarshalCookie = SDLMarshaler.LeaveAllocated)] 81 | public static extern string SDL_GetError(); 82 | 83 | [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] 84 | public static extern uint SDL_OpenAudioDevice([In()] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(SDLMarshaler))] string device, int iscapture, ref SDL_AudioSpec desired, out SDL_AudioSpec obtained, int allowed_change); 85 | 86 | [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] 87 | public static extern void SDL_PauseAudioDevice(uint dev, int pause_on); 88 | 89 | [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] 90 | public static extern int SDL_QueueAudio(uint dev, byte* data, int len); 91 | 92 | [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] 93 | public static extern int SDL_ClearQueuedAudio(uint dev); 94 | 95 | [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] 96 | public static extern void SDL_CloseAudioDevice(uint dev); 97 | } 98 | 99 | internal unsafe class SDLMarshaler : ICustomMarshaler { 100 | 101 | public const string LeaveAllocated = "LeaveAllocated"; 102 | 103 | private bool ShouldLeaveAllocated; 104 | 105 | private static ICustomMarshaler LeaveAllocatedInstance = new SDLMarshaler(true); 106 | private static ICustomMarshaler DefaultInstance = new SDLMarshaler(false); 107 | 108 | public static ICustomMarshaler GetInstance(string cookie) { 109 | switch(cookie) { 110 | case "LeaveAllocated": return LeaveAllocatedInstance; 111 | default: return DefaultInstance; 112 | } 113 | } 114 | 115 | public SDLMarshaler(bool leaveAllocated) { 116 | ShouldLeaveAllocated = leaveAllocated; 117 | } 118 | 119 | public object MarshalNativeToManaged(IntPtr pNativeData) { 120 | if(pNativeData == IntPtr.Zero) { 121 | return null; 122 | } 123 | 124 | var ptr = (byte*) pNativeData; 125 | while(*ptr != 0) { 126 | ptr++; 127 | } 128 | 129 | var bytes = new byte[ptr - (byte*) pNativeData]; 130 | Marshal.Copy(pNativeData, bytes, 0, bytes.Length); 131 | return Encoding.UTF8.GetString(bytes); 132 | } 133 | 134 | public IntPtr MarshalManagedToNative(object ManagedObj) { 135 | if(ManagedObj == null) { 136 | return IntPtr.Zero; 137 | } 138 | 139 | var str = ManagedObj as string; 140 | if(str == null) { 141 | throw new ArgumentException("ManagedObj must be a string.", "ManagedObj"); 142 | } 143 | 144 | var bytes = Encoding.UTF8.GetBytes(str); 145 | var mem = SDL.SDL_malloc((IntPtr) (bytes.Length + 1)); 146 | Marshal.Copy(bytes, 0, mem, bytes.Length); 147 | ((byte*) mem)[bytes.Length] = 0; 148 | return mem; 149 | } 150 | 151 | public void CleanUpManagedData(object ManagedObj) { 152 | } 153 | 154 | public void CleanUpNativeData(IntPtr pNativeData) { 155 | if(!ShouldLeaveAllocated) { 156 | SDL.SDL_free(pNativeData); 157 | } 158 | } 159 | 160 | public int GetNativeDataSize() { 161 | return -1; 162 | } 163 | } 164 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /FlowTimer/SettingsForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using System.IO; 4 | 5 | namespace FlowTimer { 6 | 7 | public partial class SettingsForm : Form { 8 | 9 | public static bool UpdateFound; 10 | private int SizeKeybinds = 0; 11 | 12 | public SettingsForm() { 13 | InitializeComponent(); 14 | 15 | int tab = FlowTimer.MainForm.TabControl.SelectedIndex + 1; 16 | 17 | AddKeybindSetting("Start", FlowTimer.Settings.Start); 18 | AddKeybindSetting("Stop", FlowTimer.Settings.Stop); 19 | if(tab == 3) AddKeybindSetting("Play", FlowTimer.Settings.Play); 20 | if(tab == 2 || tab == 3) AddKeybindSetting("Undo", FlowTimer.Settings.Undo); 21 | if(tab == 1 || tab == 3) AddKeybindSetting("Up", FlowTimer.Settings.Up); 22 | if(tab == 1 || tab == 3) AddKeybindSetting("Down", FlowTimer.Settings.Down); 23 | if(tab == 2) AddKeybindSetting("+Frame", FlowTimer.Settings.AddFrame); 24 | if(tab == 2) AddKeybindSetting("-Frame", FlowTimer.Settings.SubFrame); 25 | if(tab == 3) AddKeybindSetting("+ (1)", FlowTimer.Settings.AddFrame); 26 | if(tab == 3) AddKeybindSetting("- (1)", FlowTimer.Settings.SubFrame); 27 | if(tab == 3) AddKeybindSetting("+ (2)", FlowTimer.Settings.Add2); 28 | if(tab == 3) AddKeybindSetting("- (2)", FlowTimer.Settings.Sub2); 29 | if(tab == 3) AddKeybindSetting("+ (3)", FlowTimer.Settings.Add3); 30 | if(tab == 3) AddKeybindSetting("- (3)", FlowTimer.Settings.Sub3); 31 | if(tab == 3) AddKeybindSetting("+ (4)", FlowTimer.Settings.Add4); 32 | if(tab == 3) AddKeybindSetting("- (4)", FlowTimer.Settings.Sub4); 33 | if(tab == 3) AddKeybindSetting("+ (5)", FlowTimer.Settings.Add5); 34 | if(tab == 3) AddKeybindSetting("- (5)", FlowTimer.Settings.Sub5); 35 | if(tab == 3) AddKeybindSetting("+ (6)", FlowTimer.Settings.Add6); 36 | if(tab == 3) AddKeybindSetting("- (6)", FlowTimer.Settings.Sub6); 37 | 38 | LabelBeep.Top = SizeKeybinds + 14; 39 | ComboBoxBeep.Top = SizeKeybinds + 10; 40 | ButtonImportBeep.Top = SizeKeybinds + 9; 41 | LabelVolume.Top = SizeKeybinds + 38; 42 | TrackBarVolume.Top = SizeKeybinds + 37; 43 | TextBoxVolume.Top = SizeKeybinds + 36; 44 | LabelKey.Top = SizeKeybinds + 65; 45 | ComboBoxKey.Top = SizeKeybinds + 62; 46 | 47 | Size = new System.Drawing.Size { 48 | Width = Size.Width, 49 | Height = SizeKeybinds + 128 50 | }; 51 | 52 | foreach(string file in Directory.GetFiles(FlowTimer.Beeps, "*.wav")) { 53 | ComboBoxBeep.Items.Add(Path.GetFileNameWithoutExtension(file)); 54 | } 55 | ComboBoxBeep.SelectedItem = FlowTimer.Settings.Beep; 56 | 57 | foreach(KeyMethod keymethod in Enum.GetValues(typeof(KeyMethod))) { 58 | ComboBoxKey.Items.Add(keymethod.ToFormattedString()); 59 | } 60 | ComboBoxKey.SelectedIndex = (int) FlowTimer.Settings.KeyMethod; 61 | 62 | TrackBarVolume.Value = FlowTimer.Settings.Volume; 63 | TextBoxVolume.Text = FlowTimer.Settings.Volume.ToString(); 64 | 65 | ComboBoxBeep.SelectedIndexChanged += ComboBoxBeep_SelectedIndexChanged; 66 | TrackBarVolume.ValueChanged += TrackBarVolume_ValueChanged; 67 | TrackBarVolume.MouseUp += TrackBarVolume_MouseUp; 68 | TextBoxVolume.KeyPress += TextBoxVolume_KeyPress; 69 | TextBoxVolume.TextChanged += TextBoxVolume_TextChanged; 70 | ComboBoxKey.SelectedIndexChanged += ComboBoxKey_SelectedIndexChanged; 71 | } 72 | 73 | private void AddKeybindSetting(string name, Hotkey hotkey) { 74 | Label Label = new Label { 75 | AutoSize = true, 76 | Location = new System.Drawing.Point(5, SizeKeybinds + 12), 77 | Size = new System.Drawing.Size(32, 13), 78 | TabIndex = 0, 79 | Text = name + ":" 80 | }; 81 | Controls.Add(Label); 82 | 83 | Button ButtonPrimary = new Button { 84 | Location = new System.Drawing.Point(51, SizeKeybinds + 7), 85 | Size = new System.Drawing.Size(75, 23), 86 | TabIndex = 1, 87 | Text = "Unset", 88 | UseVisualStyleBackColor = true 89 | }; 90 | Controls.Add(ButtonPrimary); 91 | 92 | Button ButtonSecondary = new Button { 93 | Location = new System.Drawing.Point(129, SizeKeybinds + 7), 94 | Size = new System.Drawing.Size(75, 23), 95 | TabIndex = 2, 96 | Text = "Unset", 97 | UseVisualStyleBackColor = true 98 | }; 99 | Controls.Add(ButtonSecondary); 100 | 101 | Button ButtonClear = new Button { 102 | Location = new System.Drawing.Point(207, SizeKeybinds + 7), 103 | Size = new System.Drawing.Size(75, 23), 104 | TabIndex = 3, 105 | Text = "Clear", 106 | UseVisualStyleBackColor = true 107 | }; 108 | Controls.Add(ButtonClear); 109 | 110 | CheckBox CheckBoxGlobal = new CheckBox { 111 | AutoSize = true, 112 | Location = new System.Drawing.Point(289, SizeKeybinds + 11), 113 | Size = new System.Drawing.Size(56, 17), 114 | TabIndex = 4, 115 | Text = "Global", 116 | UseVisualStyleBackColor = true 117 | }; 118 | Controls.Add(CheckBoxGlobal); 119 | 120 | hotkey.SetControls(ButtonPrimary, ButtonSecondary, ButtonClear, CheckBoxGlobal); 121 | SizeKeybinds += 26; 122 | } 123 | 124 | private void ComboBoxBeep_SelectedIndexChanged(object sender, EventArgs e) { 125 | FlowTimer.ChangeBeepSound(ComboBoxBeep.SelectedItem as string); 126 | } 127 | 128 | private void ComboBoxKey_SelectedIndexChanged(object sender, EventArgs e) { 129 | FlowTimer.ChangeKeyMethod((KeyMethod) ComboBoxKey.SelectedIndex); 130 | } 131 | 132 | private void ButtonImportBeep_Click(object sender, EventArgs e) { 133 | FlowTimer.OpenImportBeepSoundDialog(); 134 | } 135 | 136 | private void TrackBarVolume_ValueChanged(object sender, EventArgs e) { 137 | if((Win32.GetKeyState(Keys.LButton) & 0x80) == 0) { 138 | if(TextBoxVolume.Text != "") TextBoxVolume.Text = TrackBarVolume.Value.ToString(); 139 | FlowTimer.AdjustBeepSoundVolume(TrackBarVolume.Value); 140 | FlowTimer.AudioContext.QueueAudio(FlowTimer.BeepSound); 141 | FlowTimer.Settings.Volume = TrackBarVolume.Value; 142 | } 143 | } 144 | 145 | private void TrackBarVolume_MouseUp(object sender, MouseEventArgs e) { 146 | TrackBarVolume_ValueChanged(sender, e); 147 | } 148 | 149 | private void TextBoxVolume_KeyPress(object sender, KeyPressEventArgs e) { 150 | if(!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar)) { 151 | e.Handled = true; 152 | } 153 | 154 | if(e.KeyChar != 0x8 && TextBoxVolume.Text.Length >= 3) { 155 | e.Handled = true; 156 | } 157 | } 158 | 159 | private void TextBoxVolume_TextChanged(object sender, EventArgs e) { 160 | int newValue; 161 | if(TextBoxVolume.Text == "") { 162 | newValue = 0; 163 | } else { 164 | newValue = Convert.ToInt32(TextBoxVolume.Text); 165 | newValue = Math.Min(newValue, 100); 166 | newValue = Math.Max(newValue, 0); 167 | int cursorPosition = TextBoxVolume.SelectionStart; 168 | TextBoxVolume.Text = newValue.ToString(); 169 | TextBoxVolume.SelectionStart = cursorPosition; 170 | } 171 | 172 | TrackBarVolume.Value = newValue; 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /FlowTimer/FlowTimer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {FCE97535-41FD-4334-94D4-CD37E440927F} 8 | WinExe 9 | FlowTimer 10 | FlowTimer 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | true 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | true 36 | 37 | 38 | true 39 | bin\x64\Debug\ 40 | DEBUG;TRACE 41 | true 42 | full 43 | x64 44 | 7.3 45 | prompt 46 | MinimumRecommendedRules.ruleset 47 | true 48 | 49 | 50 | bin\x64\Release\ 51 | TRACE 52 | true 53 | true 54 | pdbonly 55 | x64 56 | 7.3 57 | prompt 58 | MinimumRecommendedRules.ruleset 59 | true 60 | 61 | 62 | true 63 | bin\x86\Debug\ 64 | DEBUG;TRACE 65 | true 66 | full 67 | x86 68 | 7.3 69 | prompt 70 | MinimumRecommendedRules.ruleset 71 | true 72 | 73 | 74 | bin\x86\Release\ 75 | TRACE 76 | true 77 | true 78 | pdbonly 79 | x86 80 | 7.3 81 | prompt 82 | MinimumRecommendedRules.ruleset 83 | true 84 | 85 | 86 | 87 | ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll 88 | 89 | 90 | ..\packages\PresentationFramework.4.6.0\lib\PresentationFramework.dll 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 | Form 118 | 119 | 120 | MainForm.cs 121 | 122 | 123 | Form 124 | 125 | 126 | HotkeySelection.cs 127 | 128 | 129 | 130 | 131 | Form 132 | 133 | 134 | SettingsForm.cs 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | MainForm.cs 144 | 145 | 146 | HotkeySelection.cs 147 | 148 | 149 | ResXFileCodeGenerator 150 | Resources.Designer.cs 151 | Designer 152 | 153 | 154 | True 155 | Resources.resx 156 | True 157 | 158 | 159 | SettingsForm.cs 160 | 161 | 162 | 163 | SettingsSingleFileGenerator 164 | Settings.Designer.cs 165 | 166 | 167 | True 168 | Settings.settings 169 | True 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 | -------------------------------------------------------------------------------- /FlowTimer/VariableOffsetTimer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using System.Threading; 4 | 5 | namespace FlowTimer { 6 | 7 | public struct VariableInfo { 8 | 9 | public uint Frame; 10 | public double FPS; 11 | public int Offset; 12 | public uint Interval; 13 | public uint NumBeeps; 14 | } 15 | 16 | public class VariableOffsetTimer : BaseTimer { 17 | 18 | public TextBox TextBoxFrame; 19 | public ComboBox ComboBoxFPS; 20 | public TextBox TextBoxOffset; 21 | public TextBox TextBoxInterval; 22 | public TextBox TextBoxBeeps; 23 | public Button ButtonSubmit; 24 | 25 | public VariableInfo Info; 26 | public bool Submitted; 27 | public double CurrentOffset; 28 | 29 | public double Adjusted; 30 | 31 | public VariableOffsetTimer(TabPage tab, params Control[] copyControls) : base(tab, null, copyControls) { 32 | TextBoxFrame = FlowTimer.MainForm.TextBoxFrame; 33 | ComboBoxFPS = FlowTimer.MainForm.ComboBoxFPS; 34 | TextBoxOffset = FlowTimer.MainForm.TextBoxOffset; 35 | TextBoxInterval = FlowTimer.MainForm.TextBoxInterval; 36 | TextBoxBeeps = FlowTimer.MainForm.TextBoxBeeps; 37 | ButtonSubmit = FlowTimer.MainForm.ButtonSubmit; 38 | 39 | TimerCallback = TimerCallbackFn; // c# is silly!! 40 | TextBoxFrame.KeyDown += (sender, e) => { if(e.KeyCode == Keys.Enter && FlowTimer.MainForm.ButtonSubmit.Enabled) { Submit(); e.SuppressKeyPress = true; } }; 41 | ComboBoxFPS.SelectedIndexChanged += (sender, e) => FlowTimer.Settings.VariableFPS = FlowTimer.MainForm.ComboBoxFPS.SelectedItem as string; 42 | TextBoxOffset.TextChanged += (sender, e) => FlowTimer.Settings.VariableOffset = FlowTimer.MainForm.TextBoxOffset.Text; 43 | TextBoxInterval.TextChanged += (sender, e) => FlowTimer.Settings.VariableInterval = FlowTimer.MainForm.TextBoxInterval.Text; 44 | TextBoxBeeps.TextChanged += (sender, e) => FlowTimer.Settings.VariableNumBeeps = FlowTimer.MainForm.TextBoxBeeps.Text; 45 | } 46 | 47 | public override void OnInit() { 48 | ComboBoxFPS.SelectedItem = FlowTimer.Settings.VariableFPS; 49 | TextBoxOffset.Text = FlowTimer.Settings.VariableOffset; 50 | TextBoxInterval.Text = FlowTimer.Settings.VariableInterval; 51 | TextBoxBeeps.Text = FlowTimer.Settings.VariableNumBeeps; 52 | } 53 | 54 | public override void OnLoad() { 55 | base.OnLoad(); 56 | FlowTimer.ResizeForm(FlowTimer.MainForm.Width, 211); 57 | FlowTimer.MainForm.ButtonStart.Enabled = true; 58 | FlowTimer.MainForm.ButtonStop.Enabled = true; 59 | FlowTimer.MainForm.LabelTimer.Text = 0.0.ToFormattedString(); 60 | OnTimerStop(); 61 | } 62 | 63 | public override void OnTimerStart() { 64 | CurrentOffset = double.MaxValue; 65 | Submitted = false; 66 | TextBoxFrame.Enabled = true; 67 | TextBoxFrame.Focus(); 68 | Adjusted = 0; 69 | } 70 | 71 | public override void OnVisualTimerStart() { 72 | } 73 | 74 | public override void OnTimerStop() { 75 | Submitted = false; 76 | TextBoxFrame.Enabled = false; 77 | TextBoxFrame.Text = ""; 78 | EnableControls(true); 79 | FlowTimer.MainForm.LabelTimer.Text = 0.0.ToFormattedString(); 80 | FlowTimer.MainForm.LabelTimer.Focus(); 81 | } 82 | 83 | public override void OnKeyEvent(Keys key) { 84 | if(FlowTimer.Settings.AddFrame.IsPressed(key) && FlowTimer.MainForm.ButtonPlus.Enabled) { 85 | ChangeAudio(1); 86 | } else if(FlowTimer.Settings.SubFrame.IsPressed(key) && FlowTimer.MainForm.ButtonMinus.Enabled) { 87 | ChangeAudio(-1); 88 | } else if(FlowTimer.Settings.Undo.IsPressed(key) && FlowTimer.MainForm.ButtonUndo.Enabled) { 89 | Undo(); 90 | } 91 | } 92 | 93 | public override void OnBeepSoundChange() { 94 | } 95 | 96 | public override void OnBeepVolumeChange() { 97 | } 98 | 99 | public double TimerCallbackFn(double start) { 100 | OnDataChange(); 101 | double ret = Math.Min(Math.Max((Win32.GetTime() - start) / 1000.0, 0.001), CurrentOffset); 102 | if(ret == CurrentOffset) ret = 0.0; 103 | return ret; 104 | } 105 | 106 | public void OnDataChange() { 107 | TimerError error = GetVariableInfo(out Info); 108 | double currentTime = error == TimerError.NoError ? double.Parse(FlowTimer.MainForm.LabelTimer.Text) : 0; 109 | FlowTimer.MainForm.ButtonSubmit.Enabled = error == TimerError.NoError && !Submitted && FlowTimer.IsTimerRunning && Info.Frame / Info.FPS + Info.Offset / 1000.0f >= currentTime + (Info.Interval * (Info.NumBeeps - 1) / 1000.0f); 110 | FlowTimer.MainForm.ButtonUndo.Enabled = Submitted && FlowTimer.IsTimerRunning; 111 | 112 | bool canAdjust = Submitted && currentTime < CurrentOffset - Info.Interval * (Info.NumBeeps - 1) / 1000.0f - 0.05; 113 | FlowTimer.MainForm.ButtonPlus.Enabled = canAdjust; 114 | FlowTimer.MainForm.ButtonMinus.Enabled = canAdjust; 115 | } 116 | 117 | public void Submit() { 118 | GetVariableInfo(out Info); 119 | double now = Win32.GetTime(); 120 | double offset = (Info.Frame / Info.FPS * 1000.0f) - (now - FlowTimer.TimerStart) + Info.Offset + Adjusted; 121 | FlowTimer.UpdatePCM(new double[] { offset }, Info.Interval, Info.NumBeeps, false); 122 | FlowTimer.AudioContext.QueueAudio(FlowTimer.PCM); 123 | ButtonSubmit.Enabled = false; 124 | CurrentOffset = Info.Frame / Info.FPS + (Info.Offset + Adjusted) / 1000.0f; 125 | Submitted = true; 126 | EnableControls(false); 127 | FlowTimer.MainForm.TextBoxFrame.Enabled = false; 128 | } 129 | 130 | public void Undo() { 131 | Submitted = false; 132 | EnableControls(true); 133 | CurrentOffset = double.MaxValue; 134 | Adjusted = 0; 135 | FlowTimer.AudioContext.ClearQueuedAudio(); 136 | 137 | new Thread(() => { 138 | Thread.Sleep(50); 139 | MethodInvoker inv = delegate { 140 | FlowTimer.MainForm.TextBoxFrame.Text = ""; 141 | FlowTimer.MainForm.TextBoxFrame.Enabled = true; 142 | FlowTimer.MainForm.TextBoxFrame.Focus(); 143 | }; 144 | FlowTimer.MainForm.Invoke(inv); 145 | }).Start(); 146 | } 147 | 148 | public void ChangeAudio(int numFrames) { 149 | FlowTimer.AudioContext.ClearQueuedAudio(); 150 | double amount = numFrames * 1000.0 / Info.FPS; 151 | Adjusted += amount; 152 | Submit(); 153 | 154 | int numFramesAdjusted = (int) Math.Round(Adjusted / 1000.0 * Info.FPS); 155 | GetVariableInfo(out VariableInfo info); 156 | TextBoxFrame.Text = info.Frame.ToString(); 157 | if(numFramesAdjusted != 0) { 158 | TextBoxFrame.Text += numFramesAdjusted.ToString("+#;-#"); 159 | } 160 | } 161 | 162 | public void EnableControls(bool enabled) { 163 | ComboBoxFPS.Enabled = enabled; 164 | TextBoxOffset.Enabled = enabled; 165 | TextBoxInterval.Enabled = enabled; 166 | TextBoxBeeps.Enabled = enabled; 167 | } 168 | 169 | public TimerError GetVariableInfo(out VariableInfo info) { 170 | info = new VariableInfo(); 171 | 172 | string frameText = FlowTimer.MainForm.TextBoxFrame.Text; 173 | if(frameText.Contains("+") || frameText.Contains("-")) frameText = frameText.Substring(0, Math.Max(frameText.IndexOf("+"), frameText.IndexOf("-"))); 174 | 175 | if(!uint.TryParse(frameText, out info.Frame)) { 176 | return TimerError.InvalidFrame; 177 | } 178 | 179 | if(!double.TryParse(FlowTimer.MainForm.ComboBoxFPS.SelectedItem as string, out info.FPS)) { 180 | return TimerError.InvalidFPS; 181 | } 182 | 183 | if(!int.TryParse(FlowTimer.MainForm.TextBoxOffset.Text, out info.Offset)) { 184 | return TimerError.InvalidOffset; 185 | } 186 | 187 | if(!uint.TryParse(FlowTimer.MainForm.TextBoxInterval.Text, out info.Interval)) { 188 | return TimerError.InvalidInterval; 189 | } 190 | 191 | if(!uint.TryParse(FlowTimer.MainForm.TextBoxBeeps.Text, out info.NumBeeps)) { 192 | return TimerError.InvalidNumBeeps; 193 | } 194 | 195 | if(info.Interval >= ushort.MaxValue << 9) { 196 | return TimerError.InvalidInterval; 197 | } 198 | 199 | if(info.NumBeeps >= ushort.MaxValue << 9) { 200 | return TimerError.InvalidNumBeeps; 201 | } 202 | 203 | if(info.Frame >= ushort.MaxValue << 8) { 204 | return TimerError.InvalidFrame; 205 | } 206 | 207 | return TimerError.NoError; 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /FlowTimer/MMDeviceAPI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Collections; 5 | using static FlowTimer.MMDeviceAPI; 6 | 7 | namespace FlowTimer { 8 | 9 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 10 | public struct WAVEFORMATEX { 11 | 12 | public ushort wFormatTag; 13 | public short nChannels; 14 | public int nSamplesPerSec; 15 | public int nAvgBytesPerSec; 16 | public short nBlockAlign; 17 | public short wBitsPerSample; 18 | public short cbSize; 19 | } 20 | 21 | [StructLayout(LayoutKind.Explicit)] 22 | public struct WAVEFORMATEXTENSIBLE { 23 | 24 | [FieldOffset(0x00)] public WAVEFORMATEX Format; 25 | [FieldOffset(0x12)] public short wValidBitsPerSample; 26 | [FieldOffset(0x12)] public short wSamplesPerBlock; 27 | [FieldOffset(0x12)] public short wValidBitsPewReservedrSample; 28 | [FieldOffset(0x14)] public uint dwChannelMask; 29 | [FieldOffset(0x18)] public Guid SubFormat; 30 | } 31 | 32 | [Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 33 | public interface IMMDevice { 34 | 35 | int Activate(ref Guid iid, uint dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface); 36 | } 37 | 38 | [Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 39 | public interface IMMDeviceCollection { 40 | 41 | int GetCount(out int pcDevices); 42 | int Item(int nDevice, out IMMDevice ppDevice); 43 | } 44 | 45 | [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 46 | public interface IMMDeviceEnumerator { 47 | 48 | int EnumAudioEndpoints(DataFlow dataFlow, uint dwStateMask, out IMMDeviceCollection ppDevices); 49 | int GetDefaultAudioEndpoint(DataFlow dataFlow, Role role, out IMMDevice ppEndPoint); 50 | } 51 | 52 | [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42")] 53 | public interface IAudioClient3 { 54 | 55 | int Initialize(uint shareMode, uint streamFlags, long hnsBufferDuration, long hnsPeriodicity, WAVEFORMATEX pFormat, IntPtr audioSessionGuid); 56 | int GetBufferSize(out uint bufferSize); 57 | [return: MarshalAs(UnmanagedType.I8)] long GetStreamLatency(); 58 | int GetCurrentPadding(out int currentPadding); 59 | int IsFormatSupported(uint shareMode, WAVEFORMATEX pFormat, out IntPtr closestMatchFormat); 60 | int GetMixFormat(out IntPtr deviceFormatPointer); 61 | int GetDevicePeriod(out long defaultDevicePeriod, out long minimumDevicePeriod); 62 | int Start(); 63 | int Stop(); 64 | int Reset(); 65 | int SetEventHandle(IntPtr eventHandle); 66 | int GetService([MarshalAs(UnmanagedType.LPStruct)] Guid interfaceId, [MarshalAs(UnmanagedType.IUnknown)] out object interfacePointer); 67 | void IsOffloadCapable(int category, out bool pbOffloadCapable); 68 | void SetClientProperties(IntPtr pProperties); 69 | void GetBufferSizeLimits(IntPtr pFormat, bool bEventDriven, out long phnsMinBufferDuration, out long phnsMaxBufferDuration); 70 | int GetSharedModeEnginePeriod(IntPtr pFormat, out int pDefaultPeriodInFrames, out int pFundementalPeriodInFrames, out int pMinPeriodInFrames, out int pMaxPeriodInFrames); 71 | int GetCurrentSharedModeEnginePeriod(IntPtr ppFormat, out uint pCurrentPeriodInFrames); 72 | } 73 | 74 | public class MMDevice { 75 | 76 | private IMMDevice Interface; 77 | 78 | public MMDevice(IMMDevice interfaceIn) => Interface = interfaceIn; 79 | 80 | public AudioClient CreateAudioCilent() { 81 | Interface.Activate(ref IAudioClient3ID, CLSCTX_ALL, IntPtr.Zero, out object audioClientInterface); 82 | return new AudioClient(audioClientInterface as IAudioClient3); 83 | } 84 | } 85 | 86 | public class MMDeviceCollection : IEnumerable { 87 | 88 | private IMMDeviceCollection Interface; 89 | 90 | public MMDeviceCollection(IMMDeviceCollection interfaceIn) => Interface = interfaceIn; 91 | 92 | public int Count { 93 | get { 94 | Interface.GetCount(out int result); 95 | return result; 96 | } 97 | } 98 | 99 | public MMDevice this[int index] { 100 | get { 101 | Interface.Item(index, out IMMDevice result); 102 | return new MMDevice(result); 103 | } 104 | } 105 | 106 | public IEnumerator GetEnumerator() { 107 | for(int i = 0; i < Count; i++) { 108 | yield return this[i]; 109 | } 110 | } 111 | 112 | IEnumerator IEnumerable.GetEnumerator() { 113 | return GetEnumerator(); 114 | } 115 | } 116 | 117 | public class MMDeviceEnumerator { 118 | 119 | private IMMDeviceEnumerator Interface; 120 | 121 | public MMDeviceEnumerator(IMMDeviceEnumerator interfaceIn) => Interface = interfaceIn; 122 | 123 | public MMDeviceCollection EnumerateAudioEndPoints(DataFlow dataFlow, uint dwStateMask) { 124 | Interface.EnumAudioEndpoints(dataFlow, dwStateMask, out IMMDeviceCollection result); 125 | return new MMDeviceCollection(result); 126 | } 127 | 128 | public MMDevice GetDefaultAudioEndpoint(DataFlow dataFlow, Role role) { 129 | Interface.GetDefaultAudioEndpoint(dataFlow, role, out IMMDevice result); 130 | return new MMDevice(result); 131 | } 132 | } 133 | 134 | public class AudioClient { 135 | 136 | private IAudioClient3 Interface; 137 | 138 | public AudioClient(IAudioClient3 interfaceIn) => Interface = interfaceIn; 139 | 140 | public IntPtr MixFormat { 141 | get { 142 | Interface.GetMixFormat(out IntPtr result); 143 | return result; 144 | } 145 | } 146 | 147 | public void GetSharedModeEnginePeriod(out int defaultPeriod, out int fundementalPeriod, out int minPeriod, out int maxPeriod) { 148 | Interface.GetSharedModeEnginePeriod(MixFormat, out defaultPeriod, out fundementalPeriod, out minPeriod, out maxPeriod); 149 | } 150 | } 151 | 152 | public enum DataFlow : uint { 153 | 154 | Render, 155 | Capture, 156 | All, 157 | } 158 | 159 | public enum Role : uint { 160 | 161 | Console, 162 | Multimedia, 163 | Communications, 164 | } 165 | 166 | public static class MMDeviceAPI { 167 | 168 | public static Guid MMDeviceEnumeratorID = new Guid("BCDE0395-E52F-467C-8E3D-C4579291692E"); 169 | public static Guid IAudioClient3ID = new Guid("7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42"); 170 | 171 | public const uint DEVICE_STATE_ACTIVATE = 0x1; 172 | public const uint DEVICE_STATE_DISABLED = 0x2; 173 | public const uint DEVICE_STATE_NOTPRESENT = 0x4; 174 | public const uint DEVICE_STATE_UNPLUGGED = 0x8; 175 | 176 | public const uint CLSCTX_INPROC_SERVER = 0x1; 177 | public const uint CLSCTX_INPROC_HANDLER = 0x2; 178 | public const uint CLSCTX_LOCAL_SERVER = 0x4; 179 | public const uint CLSCTX_INPROC_SERVER16 = 0x8; 180 | public const uint CLSCTX_REMOTE_SERVER = 0x10; 181 | public const uint CLSCTX_INPROC_HANDLER16 = 0x20; 182 | public const uint CLSCTX_RESERVED1 = 0x40; 183 | public const uint CLSCTX_RESERVED2 = 0x80; 184 | public const uint CLSCTX_RESERVED3 = 0x100; 185 | public const uint CLSCTX_RESERVED4 = 0x200; 186 | public const uint CLSCTX_NO_CODE_DOWNLOAD = 0x400; 187 | public const uint CLSCTX_RESERVED5 = 0x800; 188 | public const uint CLSCTX_NO_CUSTOM_MARSHAL = 0x1000; 189 | public const uint CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000; 190 | public const uint CLSCTX_NO_FAILURE_LOG = 0x4000; 191 | public const uint CLSCTX_DISABLE_AAA = 0x8000; 192 | public const uint CLSCTX_ENABLE_AAA = 0x10000; 193 | public const uint CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000; 194 | public const uint CLSCTX_ACTIVATE_32_BIT_SERVER = 0x40000; 195 | public const uint CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000; 196 | public const uint CLSCTX_ENABLE_CLOAKING = 0x100000; 197 | public const uint CLSCTX_PS_DLL = 0x80000000; 198 | public const uint CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER; 199 | public const uint CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER; 200 | public const uint CLSCTX_ALL = CLSCTX_SERVER | CLSCTX_INPROC_HANDLER; 201 | 202 | public static MMDeviceEnumerator CreateMMDeviceEnumerator() { 203 | return new MMDeviceEnumerator(CreateInternal(MMDeviceEnumeratorID)); 204 | } 205 | 206 | private static T CreateInternal(Guid guid) { 207 | Type type = Type.GetTypeFromCLSID(guid); 208 | return (T) Activator.CreateInstance(type); 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /FlowTimer/FixedOffsetTimer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.IO; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Linq; 7 | using System.Windows.Forms; 8 | using System.Drawing; 9 | 10 | namespace FlowTimer { 11 | 12 | public class FixedOffsetTimer : BaseTimer { 13 | 14 | public Button ButtonAdd; 15 | public Button ButtonLoadTimers; 16 | public Button ButtonSaveTimers; 17 | 18 | public List Timers; 19 | public Timer SelectedTimer; 20 | 21 | public FixedOffsetTimer(TabPage tab, params Control[] copyControls) : base(tab, (start) => Math.Max((FlowTimer.MaxOffset - (Win32.GetTime() - start)) / 1000.0, 0.0), copyControls) { 22 | ButtonAdd = FlowTimer.MainForm.ButtonAdd; 23 | ButtonLoadTimers = FlowTimer.MainForm.ButtonLoadTimers; 24 | ButtonSaveTimers = FlowTimer.MainForm.ButtonSaveTimers; 25 | 26 | Timers = new List(); 27 | } 28 | 29 | public override void OnInit() { 30 | if(FlowTimer.Settings.LastLoadedTimers != null && File.Exists(FlowTimer.Settings.LastLoadedTimers)) { 31 | LoadTimers(FlowTimer.Settings.LastLoadedTimers, false); 32 | } else { 33 | AddTimer(); 34 | } 35 | } 36 | 37 | public override void OnLoad() { 38 | base.OnLoad(); 39 | RepositionAddButton(); 40 | SelectTimer(SelectedTimer); 41 | } 42 | 43 | public override void OnTimerStart() { 44 | FlowTimer.AudioContext.QueueAudio(FlowTimer.PCM); 45 | 46 | SelectedTimer.GetTimerInfo(out TimerInfo info); 47 | FlowTimer.MaxOffset = info.MaxOffset; 48 | } 49 | 50 | public override void OnVisualTimerStart() { 51 | EnableControls(false); 52 | } 53 | 54 | public override void OnTimerStop() { 55 | SelectTimer(SelectedTimer); 56 | EnableControls(true); 57 | } 58 | 59 | public override void OnKeyEvent(Keys key) { 60 | if(FlowTimer.Settings.Up.IsPressed(key)) { 61 | MoveSelectedTimerIndex(-1); 62 | } else if(FlowTimer.Settings.Down.IsPressed(key)) { 63 | MoveSelectedTimerIndex(+1); 64 | } 65 | } 66 | 67 | public override void OnBeepSoundChange() { 68 | SelectTimer(SelectedTimer); 69 | } 70 | 71 | public override void OnBeepVolumeChange() { 72 | SelectTimer(SelectedTimer); 73 | } 74 | 75 | public void RepositionAddButton() { 76 | ButtonAdd.SetBounds(Timer.X, Timer.Y + Timer.Size * Timers.Count - 2, ButtonAdd.Bounds.Width, ButtonAdd.Bounds.Height); 77 | FlowTimer.ResizeForm(FlowTimer.MainForm.Width, FlowTimer.MainFormBaseHeight + Math.Max(Timers.Count - 5, 0) * Timer.Size); 78 | } 79 | 80 | public void EnableControls(bool enabled) { 81 | foreach(Timer timer in Timers) { 82 | Control[] excluded = { timer.RadioButton, }; 83 | timer.Controls.Except(excluded).ToList().ForEach(control => control.Enabled = enabled); 84 | } 85 | 86 | ButtonAdd.Enabled = enabled; 87 | ButtonLoadTimers.Enabled = enabled; 88 | ButtonSaveTimers.Enabled = enabled; 89 | } 90 | 91 | public void AddTimer() { 92 | AddTimer(new Timer(Timers.Count)); 93 | } 94 | 95 | public void AddTimer(Timer timer) { 96 | Timers.Add(timer); 97 | 98 | foreach(Control control in timer.Controls) { 99 | Tab.Controls.Add(control); 100 | control.RemoveKeyControls(); 101 | } 102 | 103 | Timers[0].RemoveButton.Visible = Timers.Count > 1; 104 | RepositionAddButton(); 105 | SelectTimer(timer); 106 | } 107 | 108 | public void RemoveTimer(Timer timer) { 109 | int timerIndex = Timers.IndexOf(timer); 110 | timer.Controls.ForEach(Tab.Controls.Remove); 111 | Timers.Remove(timer); 112 | for(int i = timerIndex; i < Timers.Count; i++) { 113 | Timers[i].Index = Timers[i].Index - 1; 114 | } 115 | 116 | Timers[0].RemoveButton.Visible = Timers.Count > 1; 117 | RepositionAddButton(); 118 | if(SelectedTimer == timer) SelectTimer(Timers[0]); 119 | } 120 | 121 | public void ClearAllTimers() { 122 | for(int i = 0; i < Timers.Count; i++) { 123 | Timers[i].Controls.ForEach(Tab.Controls.Remove); 124 | } 125 | Timers.Clear(); 126 | RepositionAddButton(); 127 | } 128 | 129 | public void SelectTimer(Timer timer) { 130 | if(!Selected || timer == null) return; 131 | 132 | SelectedTimer = timer; 133 | timer.RadioButton.Checked = true; 134 | 135 | TimerInfo timerInfo; 136 | TimerError error = SelectedTimer.GetTimerInfo(out timerInfo); 137 | 138 | List controls = new List() { FlowTimer.MainForm.ButtonStart, FlowTimer.MainForm.ButtonStop, }; 139 | 140 | if(error == TimerError.NoError) { 141 | FlowTimer.UpdatePCM(Array.ConvertAll(timerInfo.Offsets, x => (double) x), timerInfo.Interval, timerInfo.NumBeeps); 142 | if(!FlowTimer.IsTimerRunning) FlowTimer.MainForm.LabelTimer.Text = (timerInfo.MaxOffset / 1000.0).ToFormattedString(); 143 | controls.ForEach(control => control.Enabled = true); 144 | } else { 145 | FlowTimer.MainForm.LabelTimer.Text = "Error"; 146 | controls.ForEach(control => control.Enabled = false); 147 | } 148 | } 149 | 150 | public void MoveSelectedTimerIndex(int amount) { 151 | if(SelectedTimer == null || Timers.Count == 0 || !Selected) { 152 | return; 153 | } 154 | 155 | int selectedTimerIndex = Timers.IndexOf(SelectedTimer); 156 | selectedTimerIndex = (((selectedTimerIndex + amount) % Timers.Count) + Timers.Count) % Timers.Count; 157 | SelectTimer(Timers[selectedTimerIndex]); 158 | } 159 | 160 | public void OpenLoadTimersDialog() { 161 | OpenFileDialog dialog = new OpenFileDialog(); 162 | dialog.Filter = FlowTimer.TimerFileFilter; 163 | 164 | if(dialog.ShowDialog() == DialogResult.OK) { 165 | LoadTimers(dialog.FileName, true); 166 | } 167 | } 168 | 169 | public JsonTimersFile ReadTimers(string filePath) { 170 | var json = JsonConvert.DeserializeObject(File.ReadAllText(filePath)); 171 | return json.GetType() == typeof(JArray) ? ReadJsonTimersLegacy((JArray) json) : ReadJsonTimersModern((JObject) json); 172 | } 173 | 174 | private JsonTimersFile ReadJsonTimersLegacy(JArray json) { 175 | return new JsonTimersFile(new JsonTimersHeader(), json.ToObject>()); 176 | } 177 | 178 | private JsonTimersFile ReadJsonTimersModern(JObject json) { 179 | return json.ToObject(); 180 | } 181 | 182 | public bool LoadTimers(string filePath, bool displayMessages = true) { 183 | JsonTimersFile file = ReadTimers(filePath); 184 | 185 | if(file.Timers.Count == 0) { 186 | if(displayMessages) { 187 | MessageBox.Show("Timers could not be loaded.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 188 | } 189 | return false; 190 | } 191 | 192 | ClearAllTimers(); 193 | for(int i = 0; i < file.Timers.Count; i++) { 194 | JsonTimer timer = file[i]; 195 | AddTimer(new Timer(i, timer.Name, timer.Offsets, timer.Interval, timer.NumBeeps)); 196 | } 197 | 198 | if(displayMessages) { 199 | MessageBox.Show("Timers successfully loaded from '" + filePath + "'.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); 200 | } 201 | 202 | FlowTimer.Settings.LastLoadedTimers = filePath; 203 | return true; 204 | } 205 | 206 | public void OpenSaveTimersDialog() { 207 | SaveFileDialog dialog = new SaveFileDialog(); 208 | dialog.Filter = FlowTimer.TimerFileFilter; 209 | 210 | if(dialog.ShowDialog() == DialogResult.OK) { 211 | SaveTimers(dialog.FileName, true); 212 | } 213 | } 214 | 215 | public JsonTimersFile BuildJsonTimerFile() { 216 | return new JsonTimersFile(new JsonTimersHeader(), Timers.ConvertAll(timer => new JsonTimer() { 217 | Name = timer.TextBoxName.Text, 218 | Offsets = timer.TextBoxOffset.Text, 219 | Interval = timer.TextBoxInterval.Text, 220 | NumBeeps = timer.TextBoxNumBeeps.Text, 221 | })); 222 | } 223 | 224 | public bool SaveTimers(string filePath, bool displayMessages = true) { 225 | JsonTimersFile timerFile = BuildJsonTimerFile(); 226 | 227 | try { 228 | File.WriteAllText(filePath, JsonConvert.SerializeObject(timerFile)); 229 | if(displayMessages) { 230 | MessageBox.Show("Timers successfully saved to '" + filePath + "'.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); 231 | } 232 | FlowTimer.Settings.LastLoadedTimers = filePath; 233 | return true; 234 | } catch(Exception e) { 235 | if(displayMessages) { 236 | MessageBox.Show("Timers could not be saved. Exception: " + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 237 | } 238 | return false; 239 | } 240 | } 241 | 242 | public bool HaveTimersChanged() { 243 | if(FlowTimer.Settings.LastLoadedTimers == null) { 244 | return false; 245 | } 246 | 247 | if(!File.Exists(FlowTimer.Settings.LastLoadedTimers)) { 248 | return false; 249 | } 250 | 251 | JsonTimersFile oldTimers = ReadTimers(FlowTimer.Settings.LastLoadedTimers); 252 | JsonTimersFile newTimers = BuildJsonTimerFile(); 253 | 254 | if(oldTimers.Timers.Count != newTimers.Timers.Count) { 255 | return true; 256 | } 257 | 258 | for(int i = 0; i < oldTimers.Timers.Count; i++) { 259 | JsonTimer timer1 = oldTimers[i]; 260 | JsonTimer timer2 = newTimers[i]; 261 | if(timer1.Name != timer2.Name || 262 | timer1.Offsets != timer2.Offsets || 263 | timer1.Interval != timer2.Interval || 264 | timer1.NumBeeps != timer2.NumBeeps) { 265 | return true; 266 | } 267 | } 268 | 269 | return false; 270 | } 271 | } 272 | 273 | public class TimerInfo { 274 | 275 | public uint[] Offsets; 276 | public uint MaxOffset; 277 | public uint Interval; 278 | public uint NumBeeps; 279 | } 280 | 281 | public class Timer { 282 | 283 | public const int X = 165; 284 | public const int Y = 31; 285 | public const int Size = 28; 286 | 287 | public TextBox TextBoxName; 288 | public TextBox TextBoxOffset; 289 | public TextBox TextBoxInterval; 290 | public TextBox TextBoxNumBeeps; 291 | public RadioButton RadioButton; 292 | public Button RemoveButton; 293 | public List Controls; 294 | 295 | public List TextBoxes { 296 | get { return Controls.FindAll(control => control.GetType() == typeof(TextBox)).ConvertAll(control => (TextBox) control); } 297 | } 298 | 299 | public int Index { 300 | get { 301 | return (TextBoxes[0].Location.Y - Y) / Size; 302 | } 303 | set { 304 | int yPosition = Y + value * Size; 305 | int xOffset = 0; 306 | 307 | for(int i = 0; i < TextBoxes.Count; i++) { 308 | TextBoxes[i].SetBounds(X + xOffset, yPosition, 65, 21); 309 | xOffset += TextBoxes[i].Width + 5; 310 | } 311 | 312 | RadioButton.SetBounds(X - 21, yPosition + 4, 14, 13); 313 | 314 | Rectangle lastbox = TextBoxes.Last().Bounds; 315 | RemoveButton.SetBounds(lastbox.X + lastbox.Width + 5, lastbox.Y, 38, 21); 316 | } 317 | } 318 | 319 | public Timer(int index, string name = "Timer", string offset = "5000", string interval = "500", string numBeeps = "5") { 320 | Controls = new List(); 321 | 322 | TextBoxName = new TextBox(); 323 | TextBoxName.Text = name; 324 | Controls.Add(TextBoxName); 325 | 326 | TextBoxOffset = new TextBox(); 327 | TextBoxOffset.Text = offset; 328 | Controls.Add(TextBoxOffset); 329 | 330 | TextBoxInterval = new TextBox(); 331 | TextBoxInterval.Text = interval; 332 | Controls.Add(TextBoxInterval); 333 | 334 | TextBoxNumBeeps = new TextBox(); 335 | TextBoxNumBeeps.Text = numBeeps; 336 | Controls.Add(TextBoxNumBeeps); 337 | 338 | foreach(TextBox textbox in TextBoxes) { 339 | textbox.Font = new Font(textbox.Font.FontFamily, 9.0f); 340 | textbox.TextChanged += DataChanged; 341 | textbox.TabStop = false; 342 | } 343 | 344 | RadioButton = new RadioButton(); 345 | RadioButton.Click += RadioButton_Click; 346 | Controls.Add(RadioButton); 347 | 348 | RemoveButton = new Button(); 349 | RemoveButton.Text = "-"; 350 | RemoveButton.Click += RemoveButton_Click; 351 | RemoveButton.TabStop = false; 352 | RemoveButton.DisableSelect(); 353 | Controls.Add(RemoveButton); 354 | 355 | Index = index; 356 | } 357 | 358 | public TimerError GetTimerInfo(out TimerInfo info) { 359 | info = new TimerInfo(); 360 | 361 | if(!uint.TryParse(TextBoxInterval.Text, out info.Interval)) { 362 | return TimerError.InvalidInterval; 363 | } 364 | 365 | if(!uint.TryParse(TextBoxNumBeeps.Text, out info.NumBeeps)) { 366 | return TimerError.InvalidNumBeeps; 367 | } 368 | 369 | string[] offsetsStr = TextBoxOffset.Text.Split('/'); 370 | uint[] offsets = new uint[offsetsStr.Length]; 371 | 372 | for(int i = 0; i < offsetsStr.Length; i++) { 373 | if(!uint.TryParse(offsetsStr[i], out offsets[i])) { 374 | return TimerError.InvalidOffset; 375 | } 376 | } 377 | 378 | Array.Sort(offsets); 379 | info.Offsets = offsets; 380 | info.MaxOffset = offsets.Last(); 381 | 382 | // << 9 on all of these to avoid going over the 32-bit integer limit when rebuilding the pcm 383 | // makes the maximum offset be 33553919 384 | if(info.Interval >= ushort.MaxValue << 9) { 385 | return TimerError.InvalidInterval; 386 | } 387 | 388 | if(info.NumBeeps >= ushort.MaxValue << 9) { 389 | return TimerError.InvalidNumBeeps; 390 | } 391 | 392 | foreach(uint offset in info.Offsets) { 393 | if(offset >= ushort.MaxValue << 9 || offset < info.Interval * (info.NumBeeps - 1)) { 394 | return TimerError.InvalidOffset; 395 | } 396 | } 397 | 398 | return TimerError.NoError; 399 | } 400 | 401 | private void RadioButton_Click(object sender, EventArgs e) { 402 | FlowTimer.FixedOffset.SelectTimer(this); 403 | } 404 | 405 | private void DataChanged(object sender, EventArgs e) { 406 | FlowTimer.FixedOffset.SelectTimer(this); 407 | } 408 | 409 | private void RemoveButton_Click(object sender, EventArgs e) { 410 | FlowTimer.FixedOffset.RemoveTimer(this); 411 | } 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /FlowTimer/FlowTimer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using Newtonsoft.Json; 6 | using System.Media; 7 | using System.Windows.Forms; 8 | using System.Drawing; 9 | using System.IO; 10 | using System.Runtime.InteropServices; 11 | using System.Reflection; 12 | using static FlowTimer.Win32; 13 | using static FlowTimer.SDL; 14 | 15 | namespace FlowTimer { 16 | 17 | public static class FlowTimer { 18 | 19 | public const string TimerFileFilter = "Json files (*.json)|*.json"; 20 | public const string BeepFileFilter = "WAV files (*.wav)|*.wav"; 21 | 22 | public static readonly string Folder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/flowtimer/"; 23 | public static readonly string Beeps = Folder + "beeps/"; 24 | public static readonly string SettingsFile = Folder + "settings.json"; 25 | 26 | public static MainForm MainForm; 27 | public static int MainFormBaseHeight; 28 | 29 | public static List TimerTabs; 30 | public static FixedOffsetTimer FixedOffset; 31 | public static VariableOffsetTimer VariableOffset; 32 | public static IGTTracking IGTTracking; 33 | public static BaseTimer CurrentTab { 34 | get { return TimerTabs[MainForm.TabControl.SelectedIndex]; } 35 | } 36 | 37 | public static TabPage LockedTab; 38 | 39 | public static SettingsForm SettingsForm; 40 | public static Settings Settings; 41 | 42 | public static SpriteSheet PinSheet; 43 | 44 | public static AudioContext AudioContext; 45 | public static byte[] BeepSound; 46 | public static byte[] BeepSoundUnadjusted; 47 | public static byte[] PCM; 48 | public static double MaxOffset; 49 | 50 | public static bool IsTimerRunning; 51 | public static double TimerStart; 52 | public static Thread TimerUpdateThread; 53 | 54 | public static Proc KeyboardCallback; 55 | public static IntPtr KeyboardHook; 56 | private static int[] LastKeyEvent = new int[256]; 57 | 58 | public static void Init() { 59 | Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; 60 | Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; 61 | Win32.InitTiming(); 62 | 63 | if(File.Exists(SettingsFile)) { 64 | try { 65 | Settings = JsonConvert.DeserializeObject(File.ReadAllText(SettingsFile)); 66 | } catch(Exception e) { 67 | MessageBox.Show("The settings could not be loaded and have been reset to their default values.\n" + e.Source + ": " + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 68 | } 69 | } 70 | 71 | if(Settings == null) { 72 | Settings = new Settings(); 73 | } 74 | 75 | PinSheet = new SpriteSheet(new Bitmap(FileSystem.ReadPackedResourceStream("FlowTimer.Resources.pin.png")), 16, 16); 76 | Pin(Settings.Pinned); 77 | 78 | AudioContext.GlobalInit(); 79 | ChangeBeepSound(Settings.Beep, false); 80 | 81 | TimerUpdateThread = new Thread(TimerUpdateCallback); 82 | 83 | MainForm.TabControl.Selected += TabControl_Selected; 84 | MainForm.TabControl.Deselecting += TabControl_Deselecting; 85 | 86 | MainForm.ButtonAdd.DisableSelect(); 87 | MainForm.ButtonStart.DisableSelect(); 88 | MainForm.ButtonStop.DisableSelect(); 89 | MainForm.ButtonSettings.DisableSelect(); 90 | MainForm.ButtonLoadTimers.DisableSelect(); 91 | MainForm.ButtonSaveTimers.DisableSelect(); 92 | 93 | KeyboardCallback = Keycallback; 94 | KeyboardHook = SetHook(WH_KEYBOARD_LL, KeyboardCallback); 95 | 96 | foreach(BaseTimer timer in TimerTabs) { 97 | timer.OnInit(); 98 | } 99 | } 100 | 101 | public static void Destroy() { 102 | if(FixedOffset.HaveTimersChanged()) { 103 | if(MessageBox.Show("[Fixed Offset] You've changed your timers without saving. Would you like to save your timers?", "Save timers?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { 104 | FixedOffset.SaveTimers(Settings.LastLoadedTimers, true); 105 | } 106 | } 107 | IGTTracking.SaveDelayersFile(); 108 | if(IGTTracking.HaveTimersChanged()) { 109 | if(MessageBox.Show("[IGT Tracking] You've changed your timers without saving. Would you like to save your timers?", "Save timers?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { 110 | IGTTracking.SaveTimers(Settings.LastLoadedIGTTimers, true); 111 | } 112 | } 113 | 114 | UnhookWindowsHookEx(KeyboardHook); 115 | TimerUpdateThread.AbortIfAlive(); 116 | AudioContext.ClearQueuedAudio(); 117 | AudioContext.Destroy(); 118 | AudioContext.GlobalDestroy(); 119 | File.WriteAllText(SettingsFile, JsonConvert.SerializeObject(Settings)); 120 | } 121 | 122 | public static int GetCurrentBuild() { 123 | return Assembly.GetExecutingAssembly().GetName().Version.Major; 124 | } 125 | 126 | public static void SetMainForm(MainForm mainForm) { 127 | MainForm = mainForm; 128 | MainFormBaseHeight = mainForm.Height; 129 | MainForm.RemoveKeyControls(); 130 | 131 | int buildVersion = Assembly.GetExecutingAssembly().GetName().Version.Major; 132 | MainForm.Text += " (Build " + buildVersion + ")"; 133 | } 134 | 135 | public static void RemoveKeyControls(this Control control) { 136 | foreach(Control ctrl in control.Controls) { 137 | ctrl.RemoveKeyControls(); 138 | ctrl.PreviewKeyDown += MainForm_PreviewKeyDown; 139 | ctrl.KeyDown += MainForm_KeyDownEvent; 140 | } 141 | } 142 | 143 | private static void MainForm_PreviewKeyDown(object sender, PreviewKeyDownEventArgs args) { 144 | switch(args.KeyCode) { 145 | case Keys.Up: 146 | case Keys.Down: 147 | case Keys.Left: 148 | case Keys.Right: 149 | case Keys.Tab: 150 | args.IsInputKey = true; 151 | break; 152 | } 153 | } 154 | 155 | private static void MainForm_KeyDownEvent(object sender, KeyEventArgs args) { 156 | switch(args.KeyCode) { 157 | case Keys.Escape: 158 | MainForm.ActiveControl = null; 159 | args.Handled = true; 160 | args.SuppressKeyPress = true; 161 | break; 162 | } 163 | } 164 | 165 | private static IntPtr Keycallback(int nCode, int wParam, IntPtr lParam) { 166 | if((SettingsForm == null || !SettingsForm.Visible) && nCode >= 0) { 167 | Keys key = (Keys) Marshal.ReadInt32(lParam); 168 | 169 | if(Settings.KeyMethod.IsActivatedByEvent(wParam) && wParam != LastKeyEvent[(int) key]) { 170 | if(Settings.Start.IsPressed(key)) { 171 | StartTimer(); 172 | } else if(Settings.Stop.IsPressed(key)) { 173 | StopTimer(false); 174 | } 175 | 176 | CurrentTab.OnKeyEvent(key); 177 | } 178 | LastKeyEvent[(int) key] = wParam; 179 | } 180 | 181 | return CallNextHookEx(KeyboardHook, nCode, wParam, lParam); 182 | } 183 | 184 | public static void RegisterTabs(TabPage fixedOffset, TabPage variableOffset, TabPage TabPageIGTTracking) { 185 | Control[] copyControls = { MainForm.ButtonStart, MainForm.ButtonStop, MainForm.ButtonSettings, MainForm.LabelTimer, MainForm.PictureBoxPin }; 186 | FixedOffset = new FixedOffsetTimer(fixedOffset, copyControls); 187 | VariableOffset = new VariableOffsetTimer(variableOffset, copyControls); 188 | IGTTracking = new IGTTracking(TabPageIGTTracking, copyControls); 189 | TimerTabs = new List() { FixedOffset, VariableOffset, IGTTracking }; 190 | } 191 | 192 | public static void TabControl_Selected(object sender, TabControlEventArgs e) { 193 | foreach(TabPage tab in MainForm.TabControl.TabPages) { 194 | tab.SetDrawing(false); 195 | } 196 | 197 | TimerTabs[e.TabPageIndex].OnLoad(); 198 | 199 | e.TabPage.SetDrawing(true); 200 | e.TabPage.Refresh(); 201 | } 202 | 203 | public static void TabControl_Deselecting(object sender, TabControlCancelEventArgs e) { 204 | if(e.TabPage == LockedTab) { 205 | SystemSounds.Beep.Play(); 206 | e.Cancel = true; 207 | return; 208 | } 209 | } 210 | 211 | public static void ResizeForm(int width, int height) { 212 | Size size = new Size() { 213 | Width = width, 214 | Height = height, 215 | }; 216 | 217 | MainForm.MinimumSize = size; 218 | MainForm.MaximumSize = size; 219 | MainForm.Size = size; 220 | MainForm.TabControl.Height = size.Height; 221 | } 222 | 223 | public static void EnableControls(bool enabled) { 224 | MainForm.ButtonSettings.Enabled = enabled; 225 | } 226 | 227 | public static void TogglePin() { 228 | Pin(!Settings.Pinned); 229 | } 230 | 231 | public static void Pin(bool pin) { 232 | MainForm.TopMost = pin; 233 | if(SettingsForm != null) SettingsForm.TopMost = pin; 234 | Settings.Pinned = pin; 235 | MainForm.PictureBoxPin.Image = PinSheet[pin ? 1 : 0]; 236 | } 237 | 238 | public static void UpdatePCM(double[] offsets, uint interval, uint numBeeps, bool garbageCollect = true) { 239 | // try to force garbage collection on the old pcm data 240 | if(garbageCollect) GC.Collect(); 241 | double maxOffset = offsets.Max(); 242 | 243 | PCM = new byte[((int) Math.Ceiling(maxOffset / 1000.0 * AudioContext.SampleRate)) * AudioContext.NumChannels * AudioContext.BytesPerSample + BeepSound.Length]; 244 | 245 | foreach(double offset in offsets) { 246 | for(int i = 0; i < numBeeps; i++) { 247 | int destOffset = (int) ((offset - i * interval) / 1000.0 * AudioContext.SampleRate) * AudioContext.NumChannels * 2; 248 | Array.Copy(BeepSound, 0, PCM, destOffset, BeepSound.Length); 249 | } 250 | } 251 | } 252 | 253 | public static void StartTimer() { 254 | AudioContext.ClearQueuedAudio(); 255 | 256 | IsTimerRunning = true; 257 | TimerStart = Win32.GetTime(); 258 | CurrentTab.OnTimerStart(); 259 | 260 | if(!MainForm.ButtonStart.Enabled) { 261 | AudioContext.ClearQueuedAudio(); 262 | return; 263 | } 264 | 265 | VariableOffset.OnDataChange(); 266 | 267 | EnableControls(false); 268 | LockedTab = MainForm.TabControl.SelectedTab; 269 | 270 | TimerUpdateThread.AbortIfAlive(); 271 | TimerUpdateThread = new Thread(TimerUpdateCallback); 272 | TimerUpdateThread.Start(); 273 | CurrentTab.OnVisualTimerStart(); 274 | } 275 | 276 | public static void StopTimer(bool timerExpired) { 277 | if(IsTimerRunning) { 278 | if(!timerExpired) { 279 | AudioContext.ClearQueuedAudio(); 280 | TimerUpdateThread.AbortIfAlive(); 281 | } 282 | 283 | IsTimerRunning = false; 284 | CurrentTab.OnTimerStop(); 285 | EnableControls(true); 286 | LockedTab = null; 287 | VariableOffset.OnDataChange(); 288 | } 289 | } 290 | 291 | private static void TimerUpdateCallback() { 292 | Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; 293 | Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; 294 | const uint resolution = 15; 295 | 296 | MethodInvoker inv; 297 | double currentTime = 0.0f; 298 | 299 | do { 300 | inv = delegate { 301 | currentTime = CurrentTab.TimerCallback(TimerStart); 302 | MainForm.LabelTimer.Text = currentTime.ToFormattedString(); 303 | }; 304 | MainForm.Invoke(inv); 305 | 306 | SDL_Delay(resolution); 307 | } while(currentTime > 0.0); 308 | 309 | inv = delegate { 310 | StopTimer(true); 311 | }; 312 | MainForm.Invoke(inv); 313 | } 314 | 315 | public static void OpenSettingsForm() { 316 | SettingsForm = new SettingsForm(); 317 | SettingsForm.TopMost = Settings.Pinned; 318 | SettingsForm.StartPosition = FormStartPosition.CenterParent; 319 | SettingsForm.RemoveKeyControls(); 320 | SettingsForm.ShowDialog(MainForm); 321 | } 322 | 323 | public static void OpenImportBeepSoundDialog() { 324 | OpenFileDialog dialog = new OpenFileDialog(); 325 | dialog.Filter = BeepFileFilter; 326 | 327 | if(dialog.ShowDialog() == DialogResult.OK) { 328 | ImportBeepSound(dialog.FileName, true); 329 | } 330 | } 331 | 332 | public static bool ImportBeepSound(string filePath, bool displayMessages = true) { 333 | string fileName = Path.GetFileName(filePath); 334 | string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath); 335 | 336 | if(File.Exists(Beeps + fileName)) { 337 | if(displayMessages) { 338 | MessageBox.Show("Beep sound '" + fileNameWithoutExtension + "' already exists.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 339 | } 340 | return false; 341 | } 342 | 343 | SDL_AudioSpec audioSpec; 344 | Wave.LoadWAV(filePath, out _, out audioSpec); 345 | 346 | if(audioSpec.format != AUDIO_S16LSB) { 347 | if(displayMessages) { 348 | MessageBox.Show("FlowTimer does not support this audio file (yet).", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 349 | } 350 | return false; 351 | } 352 | 353 | File.Copy(filePath, Beeps + fileName); 354 | if(SettingsForm != null) SettingsForm.ComboBoxBeep.Items.Add(fileNameWithoutExtension); 355 | MessageBox.Show("Beep sucessfully imported from '" + filePath + "'.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); 356 | return true; 357 | } 358 | 359 | public static void ChangeBeepSound(string beepName, bool playSound = true) { 360 | if(AudioContext != null) AudioContext.Destroy(); 361 | 362 | SDL_AudioSpec audioSpec; 363 | Wave.LoadWAV(Beeps + beepName + ".wav", out BeepSoundUnadjusted, out audioSpec); 364 | AudioContext = new AudioContext(audioSpec.freq, audioSpec.format, audioSpec.channels); 365 | AdjustBeepSoundVolume(Settings.Volume); 366 | CurrentTab.OnBeepSoundChange(); 367 | Settings.Beep = beepName; 368 | 369 | if(playSound) { 370 | AudioContext.QueueAudio(BeepSound); 371 | } 372 | } 373 | 374 | public static void AdjustBeepSoundVolume(int newVolume) { 375 | float vol = newVolume / 100.0f; 376 | BeepSound = new byte[BeepSoundUnadjusted.Length]; 377 | for(int i = 0; i < BeepSound.Length; i += 2) { 378 | short sample = (short) (BeepSoundUnadjusted[i] | (BeepSoundUnadjusted[i + 1] << 8)); 379 | float floatSample = sample; 380 | 381 | floatSample *= vol; 382 | 383 | if(floatSample < short.MinValue) floatSample = short.MinValue; 384 | if(floatSample > short.MaxValue) floatSample = short.MaxValue; 385 | 386 | sample = (short) floatSample; 387 | BeepSound[i] = (byte) (sample & 0xFF); 388 | BeepSound[i + 1] = (byte) (sample >> 8); 389 | } 390 | 391 | CurrentTab.OnBeepVolumeChange(); 392 | } 393 | 394 | public static void ChangeKeyMethod(KeyMethod newMethod) { 395 | Settings.KeyMethod = newMethod; 396 | } 397 | } 398 | } 399 | -------------------------------------------------------------------------------- /FlowTimer/IGTTracking.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Windows.Forms; 4 | using System.Drawing; 5 | using System.Threading; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using Newtonsoft.Json; 9 | using Newtonsoft.Json.Linq; 10 | 11 | namespace FlowTimer { 12 | 13 | public class IGTTracking : BaseTimer { 14 | 15 | public ComboBox ComboBoxGame; 16 | public ComboBox ComboBoxFPS; 17 | public Button ButtonAdd; 18 | 19 | public bool Playing; 20 | public double CurrentOffset; 21 | public double Adjusted; 22 | 23 | public List Delayers; 24 | public static readonly string DelayersFile = FlowTimer.Folder + "igtdelayers.json"; 25 | JsonIGTDelayersSettings DelayersSettings; 26 | 27 | public List Timers; 28 | public IGTTimer SelectedTimer; 29 | 30 | public IGTTracking(TabPage tab, params Control[] copyControls) : base(tab, null, copyControls) { 31 | ComboBoxGame = FlowTimer.MainForm.ComboBoxGame; 32 | ComboBoxFPS = FlowTimer.MainForm.ComboBoxFPS3; 33 | ButtonAdd = FlowTimer.MainForm.ButtonAdd3; 34 | 35 | TimerCallback = TimerCallbackFn; 36 | ComboBoxGame.SelectedIndexChanged += (sender, e) => { FlowTimer.Settings.IGTGame = ComboBoxGame.SelectedItem as string; UpdateGame(); }; 37 | ComboBoxFPS.SelectedIndexChanged += (sender, e) => FlowTimer.Settings.IGTFPS = ComboBoxFPS.SelectedItem as string; 38 | 39 | Timers = new List(); 40 | Delayers = new List(); 41 | } 42 | 43 | public override void OnInit() { 44 | LoadDelayersFile(); 45 | if(FlowTimer.Settings.IGTGame != null) ComboBoxGame.SelectedItem = FlowTimer.Settings.IGTGame; else ComboBoxGame.SelectedItem = ComboBoxGame.Items[0]; 46 | ComboBoxFPS.SelectedItem = FlowTimer.Settings.IGTFPS; 47 | 48 | if(FlowTimer.Settings.LastLoadedIGTTimers != null && File.Exists(FlowTimer.Settings.LastLoadedIGTTimers)) { 49 | LoadTimers(FlowTimer.Settings.LastLoadedIGTTimers, false); 50 | } else { 51 | AddTimer(); 52 | } 53 | } 54 | 55 | public override void OnLoad() { 56 | base.OnLoad(); 57 | RepositionAddButton(); 58 | SelectTimer(SelectedTimer); 59 | OnTimerStop(); 60 | } 61 | 62 | public override void OnTimerStart() { 63 | CurrentOffset = double.MaxValue; 64 | Playing = false; 65 | EnableControls(true, false); 66 | foreach(IGTDelayer delayer in Delayers) 67 | delayer.Count.Text = "0"; 68 | Adjusted = 0; 69 | } 70 | 71 | public override void OnVisualTimerStart() { 72 | } 73 | 74 | public override void OnTimerStop() { 75 | Playing = false; 76 | EnableControls(false, false); 77 | FlowTimer.MainForm.LabelTimer.Text = 0.0.ToFormattedString(); 78 | FlowTimer.MainForm.LabelTimer.Focus(); 79 | foreach(IGTDelayer delayer in Delayers) 80 | delayer.Count.Text = "0"; 81 | Adjusted = 0; 82 | } 83 | 84 | public override void OnKeyEvent(Keys key) { 85 | if(FlowTimer.Settings.AddFrame.IsPressed(key)) { 86 | if(Delayers.Count > 0) Delayers[0].Plus.PerformClick(); 87 | } else if(FlowTimer.Settings.SubFrame.IsPressed(key)) { 88 | if(Delayers.Count > 0) Delayers[0].Minus.PerformClick(); 89 | } else if(FlowTimer.Settings.Add2.IsPressed(key)) { 90 | if(Delayers.Count > 1) Delayers[1].Plus.PerformClick(); 91 | } else if(FlowTimer.Settings.Sub2.IsPressed(key)) { 92 | if(Delayers.Count > 1) Delayers[1].Minus.PerformClick(); 93 | } else if(FlowTimer.Settings.Add3.IsPressed(key)) { 94 | if(Delayers.Count > 2) Delayers[2].Plus.PerformClick(); 95 | } else if(FlowTimer.Settings.Sub3.IsPressed(key)) { 96 | if(Delayers.Count > 2) Delayers[2].Minus.PerformClick(); 97 | } else if(FlowTimer.Settings.Add4.IsPressed(key)) { 98 | if(Delayers.Count > 3) Delayers[3].Plus.PerformClick(); 99 | } else if(FlowTimer.Settings.Sub4.IsPressed(key)) { 100 | if(Delayers.Count > 3) Delayers[3].Minus.PerformClick(); 101 | } else if(FlowTimer.Settings.Add5.IsPressed(key)) { 102 | if(Delayers.Count > 4) Delayers[4].Plus.PerformClick(); 103 | } else if(FlowTimer.Settings.Sub5.IsPressed(key)) { 104 | if(Delayers.Count > 4) Delayers[4].Minus.PerformClick(); 105 | } else if(FlowTimer.Settings.Add6.IsPressed(key)) { 106 | if(Delayers.Count > 5) Delayers[5].Plus.PerformClick(); 107 | } else if(FlowTimer.Settings.Sub6.IsPressed(key)) { 108 | if(Delayers.Count > 5) Delayers[5].Minus.PerformClick(); 109 | } else if(FlowTimer.Settings.Undo.IsPressed(key) && FlowTimer.MainForm.ButtonUndoPlay.Enabled) { 110 | Undo(); 111 | } else if(FlowTimer.Settings.Play.IsPressed(key) && FlowTimer.MainForm.ButtonPlay.Enabled) { 112 | Play(); 113 | } else if(FlowTimer.Settings.Up.IsPressed(key)) { 114 | MoveSelectedTimerIndex(-1); 115 | } else if(FlowTimer.Settings.Down.IsPressed(key)) { 116 | MoveSelectedTimerIndex(+1); 117 | } 118 | } 119 | 120 | public override void OnBeepSoundChange() { 121 | } 122 | 123 | public override void OnBeepVolumeChange() { 124 | } 125 | 126 | public void UpdateGame() { 127 | foreach(IGTDelayer delayer in Delayers) { 128 | Tab.Controls.Remove(delayer.Name); 129 | Tab.Controls.Remove(delayer.Count); 130 | Tab.Controls.Remove(delayer.Minus); 131 | Tab.Controls.Remove(delayer.Plus); 132 | } 133 | Delayers.Clear(); 134 | 135 | foreach(JsonIGTDelayersGame g in DelayersSettings.Games) { 136 | if(ComboBoxGame.Text == g.Game) { 137 | foreach(JsonIGTDelayer d in g.Delayers) { 138 | AddDelayer(d.Name, d.Delay); 139 | } 140 | break; 141 | } 142 | } 143 | 144 | UpdateTimersBaseY(); 145 | } 146 | 147 | public void LoadDelayersFile() { 148 | if(File.Exists(DelayersFile)) { 149 | try { 150 | DelayersSettings = JsonConvert.DeserializeObject(File.ReadAllText(DelayersFile)); 151 | if(DelayersSettings.Header.Version < FlowTimer.GetCurrentBuild()) DelayersSettings = null; 152 | } catch(Exception e) { 153 | MessageBox.Show("Error reading delayers settings.\n" + e.Source + ": " + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 154 | } 155 | } 156 | 157 | if(DelayersSettings == null) { 158 | DelayersSettings = new JsonIGTDelayersSettings { 159 | Header = new JsonTimersHeader(), 160 | Games = new List 161 | { 162 | new JsonIGTDelayersGame 163 | { 164 | Game = "Red/Blue", 165 | Delayers = new List 166 | { 167 | new JsonIGTDelayer { Name = "Encounter", Delay = 9.10 }, 168 | new JsonIGTDelayer { Name = "Encounter (forest)", Delay = 6.35 }, 169 | new JsonIGTDelayer { Name = "Heal/Swap (outdoors)", Delay = 6.60 }, 170 | new JsonIGTDelayer { Name = "Heal/Swap (indoors)", Delay = 3.90 }, 171 | new JsonIGTDelayer { Name = "Heal (in battle)", Delay = 1.05 }, 172 | } 173 | }, 174 | new JsonIGTDelayersGame 175 | { 176 | Game = "Yellow", 177 | Delayers = new List 178 | { 179 | new JsonIGTDelayer { Name = "Color lag", Delay = 2.00 }, 180 | new JsonIGTDelayer { Name = "Pika death", Delay = 83.00 }, 181 | new JsonIGTDelayer { Name = "Encounter", Delay = 51.35 }, 182 | new JsonIGTDelayer { Name = "Encounter (forest)", Delay = 15.85 }, 183 | new JsonIGTDelayer { Name = "Heal (indoors)", Delay = 12.00 }, 184 | new JsonIGTDelayer { Name = "Heal (in battle)", Delay = 5.00 }, 185 | } 186 | }, 187 | } 188 | }; 189 | } 190 | 191 | foreach(JsonIGTDelayersGame g in DelayersSettings.Games) 192 | ComboBoxGame.Items.Add(g.Game); 193 | } 194 | 195 | public void SaveDelayersFile() { 196 | File.WriteAllText(DelayersFile, JsonConvert.SerializeObject(DelayersSettings)); 197 | } 198 | 199 | public void UpdateTimersBaseY() { 200 | IGTTimer.Y = 30 + Math.Max(5, Delayers.Count) * IGTDelayer.Size; 201 | 202 | FlowTimer.MainForm.LabelName3.Top = IGTTimer.Y - 16; 203 | FlowTimer.MainForm.LabelFrame3.Top = IGTTimer.Y - 16; 204 | FlowTimer.MainForm.LabelOffset3.Top = IGTTimer.Y - 16; 205 | FlowTimer.MainForm.LabelInterval3.Top = IGTTimer.Y - 16; 206 | FlowTimer.MainForm.LabelBeeps3.Top = IGTTimer.Y - 16; 207 | for(int i = 0; i < Timers.Count; ++i) 208 | Timers[i].Index = i; 209 | 210 | RepositionAddButton(); 211 | } 212 | 213 | public void AddDelayer(string name, double delay) { 214 | Delayers.Add(new IGTDelayer(Delayers.Count, name, delay)); 215 | } 216 | 217 | public double TimerCallbackFn(double start) { 218 | OnDataChange(); 219 | return Math.Max((Win32.GetTime() - start) / 1000.0, 0.001); 220 | } 221 | 222 | public void OnDataChange() { 223 | double currentTime = double.Parse(FlowTimer.MainForm.LabelTimer.Text); 224 | if(Playing && CurrentOffset < currentTime) { 225 | Playing = false; 226 | EnableControls(true, false); 227 | } 228 | } 229 | 230 | public void Play() { 231 | if(!FlowTimer.IsTimerRunning) return; 232 | IGTTimerInfo info; 233 | TimerError error = SelectedTimer.GetTimerInfo(out info); 234 | if(error != TimerError.NoError) return; 235 | double now = Win32.GetTime(); 236 | double offset = (info.Frame / info.FPS * 1000.0f) - (now - FlowTimer.TimerStart) + info.Offset + Adjusted; 237 | while(offset - info.Interval * (info.NumBeeps - 1) < 0.0f) 238 | offset += 60.0f / info.FPS * 1000.0f; 239 | double[] offsets = new double[10]; 240 | for(int i = 0; i < offsets.Length; ++i) 241 | offsets[i] = offset + 60.0f / info.FPS * 1000.0f * i; 242 | FlowTimer.AudioContext.ClearQueuedAudio(); 243 | FlowTimer.UpdatePCM(offsets, info.Interval, info.NumBeeps, false); 244 | FlowTimer.AudioContext.QueueAudio(FlowTimer.PCM); 245 | CurrentOffset = (offsets[9] + now - FlowTimer.TimerStart) / 1000.0f; 246 | Playing = true; 247 | EnableControls(true, true); 248 | } 249 | 250 | public void Undo() { 251 | if(!Playing) return; 252 | Playing = false; 253 | EnableControls(true, false); 254 | CurrentOffset = double.MaxValue; 255 | FlowTimer.AudioContext.ClearQueuedAudio(); 256 | } 257 | 258 | public void ChangeAudio(double numFrames) { 259 | FlowTimer.AudioContext.ClearQueuedAudio(); 260 | IGTTimerInfo info; 261 | SelectedTimer.GetTimerInfo(out info); 262 | double amount = numFrames * 1000.0 / info.FPS; 263 | Adjusted += amount; 264 | if(Playing) Play(); 265 | } 266 | 267 | public void EnableControls(bool play, bool undo) { 268 | FlowTimer.MainForm.ButtonPlay.Enabled = play; 269 | FlowTimer.MainForm.ButtonUndoPlay.Enabled = undo; 270 | } 271 | 272 | public void RepositionAddButton() { 273 | ButtonAdd.SetBounds(IGTTimer.X, IGTTimer.Y + IGTTimer.Size * Timers.Count - 2, ButtonAdd.Bounds.Width, ButtonAdd.Bounds.Height); 274 | if(Selected) { 275 | FlowTimer.ResizeForm(FlowTimer.MainForm.Width, FlowTimer.MainFormBaseHeight + 10 276 | + Math.Max(Delayers.Count - 5, 0) * IGTDelayer.Size 277 | + Math.Max(Timers.Count - 1, 0) * IGTTimer.Size); 278 | } 279 | } 280 | 281 | public void AddTimer() { 282 | AddTimer(new IGTTimer(Timers.Count)); 283 | } 284 | 285 | public void AddTimer(IGTTimer timer) { 286 | Timers.Add(timer); 287 | 288 | foreach(Control control in timer.Controls) { 289 | Tab.Controls.Add(control); 290 | control.RemoveKeyControls(); 291 | } 292 | 293 | Timers[0].RemoveButton.Visible = Timers.Count > 1; 294 | RepositionAddButton(); 295 | SelectTimer(timer); 296 | } 297 | 298 | public void RemoveTimer(IGTTimer timer) { 299 | int timerIndex = Timers.IndexOf(timer); 300 | timer.Controls.ForEach(Tab.Controls.Remove); 301 | Timers.Remove(timer); 302 | for(int i = timerIndex; i < Timers.Count; i++) { 303 | Timers[i].Index = Timers[i].Index - 1; 304 | } 305 | 306 | Timers[0].RemoveButton.Visible = Timers.Count > 1; 307 | RepositionAddButton(); 308 | if(SelectedTimer == timer) SelectTimer(Timers[0]); 309 | } 310 | 311 | public void ClearAllTimers() { 312 | for(int i = 0; i < Timers.Count; i++) { 313 | Timers[i].Controls.ForEach(Tab.Controls.Remove); 314 | } 315 | Timers.Clear(); 316 | RepositionAddButton(); 317 | } 318 | 319 | public void SelectTimer(IGTTimer timer) { 320 | if(timer == null) return; 321 | 322 | SelectedTimer = timer; 323 | timer.RadioButton.Checked = true; 324 | } 325 | 326 | public void MoveSelectedTimerIndex(int amount) { 327 | if(SelectedTimer == null || Timers.Count == 0 || !Selected) { 328 | return; 329 | } 330 | 331 | int selectedTimerIndex = Timers.IndexOf(SelectedTimer); 332 | selectedTimerIndex = (((selectedTimerIndex + amount) % Timers.Count) + Timers.Count) % Timers.Count; 333 | SelectTimer(Timers[selectedTimerIndex]); 334 | } 335 | 336 | public void OpenLoadTimersDialog() { 337 | OpenFileDialog dialog = new OpenFileDialog(); 338 | dialog.Filter = FlowTimer.TimerFileFilter; 339 | 340 | if(dialog.ShowDialog() == DialogResult.OK) { 341 | LoadTimers(dialog.FileName, true); 342 | } 343 | } 344 | 345 | public JsonIGTTimersFile ReadTimers(string filePath) { 346 | var json = JsonConvert.DeserializeObject(File.ReadAllText(filePath)); 347 | return json.GetType() == typeof(JArray) ? ReadJsonIGTTimersLegacy((JArray) json) : ReadJsonIGTTimersModern((JObject) json); 348 | } 349 | 350 | private JsonIGTTimersFile ReadJsonIGTTimersLegacy(JArray json) { 351 | return new JsonIGTTimersFile(new JsonTimersHeader(), json.ToObject>()); 352 | } 353 | 354 | private JsonIGTTimersFile ReadJsonIGTTimersModern(JObject json) { 355 | return json.ToObject(); 356 | } 357 | 358 | public bool LoadTimers(string filePath, bool displayMessages = true) { 359 | JsonIGTTimersFile file = ReadTimers(filePath); 360 | 361 | if(file.Timers.Count == 0) { 362 | if(displayMessages) { 363 | MessageBox.Show("Timers could not be loaded.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 364 | } 365 | return false; 366 | } 367 | 368 | ClearAllTimers(); 369 | for(int i = 0; i < file.Timers.Count; i++) { 370 | JsonIGTTimer timer = file[i]; 371 | AddTimer(new IGTTimer(i, timer.Name, timer.Frame, timer.Offsets, timer.Interval, timer.NumBeeps)); 372 | } 373 | 374 | if(displayMessages) { 375 | MessageBox.Show("Timers successfully loaded from '" + filePath + "'.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); 376 | } 377 | 378 | FlowTimer.Settings.LastLoadedIGTTimers = filePath; 379 | return true; 380 | } 381 | 382 | public void OpenSaveTimersDialog() { 383 | SaveFileDialog dialog = new SaveFileDialog(); 384 | dialog.Filter = FlowTimer.TimerFileFilter; 385 | 386 | if(dialog.ShowDialog() == DialogResult.OK) { 387 | SaveTimers(dialog.FileName, true); 388 | } 389 | } 390 | 391 | public JsonIGTTimersFile BuildJsonIGTTimerFile() { 392 | return new JsonIGTTimersFile(new JsonTimersHeader(), Timers.ConvertAll(timer => new JsonIGTTimer() { 393 | Name = timer.TextBoxName.Text, 394 | Frame = timer.TextBoxFrame.Text, 395 | Offsets = timer.TextBoxOffset.Text, 396 | Interval = timer.TextBoxInterval.Text, 397 | NumBeeps = timer.TextBoxNumBeeps.Text, 398 | })); 399 | } 400 | 401 | public bool SaveTimers(string filePath, bool displayMessages = true) { 402 | JsonIGTTimersFile timerFile = BuildJsonIGTTimerFile(); 403 | 404 | try { 405 | File.WriteAllText(filePath, JsonConvert.SerializeObject(timerFile)); 406 | if(displayMessages) { 407 | MessageBox.Show("Timers successfully saved to '" + filePath + "'.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); 408 | } 409 | FlowTimer.Settings.LastLoadedIGTTimers = filePath; 410 | return true; 411 | } catch(Exception e) { 412 | if(displayMessages) { 413 | MessageBox.Show("Timers could not be saved. Exception: " + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 414 | } 415 | return false; 416 | } 417 | } 418 | 419 | public bool HaveTimersChanged() { 420 | if(FlowTimer.Settings.LastLoadedIGTTimers == null) { 421 | return false; 422 | } 423 | 424 | if(!File.Exists(FlowTimer.Settings.LastLoadedIGTTimers)) { 425 | return false; 426 | } 427 | 428 | JsonIGTTimersFile oldTimers = ReadTimers(FlowTimer.Settings.LastLoadedIGTTimers); 429 | JsonIGTTimersFile newTimers = BuildJsonIGTTimerFile(); 430 | 431 | if(oldTimers.Timers.Count != newTimers.Timers.Count) { 432 | return true; 433 | } 434 | 435 | for(int i = 0; i < oldTimers.Timers.Count; i++) { 436 | JsonIGTTimer timer1 = oldTimers[i]; 437 | JsonIGTTimer timer2 = newTimers[i]; 438 | if(timer1.Name != timer2.Name || 439 | timer1.Frame != timer2.Frame || 440 | timer1.Offsets != timer2.Offsets || 441 | timer1.Interval != timer2.Interval || 442 | timer1.NumBeeps != timer2.NumBeeps) { 443 | return true; 444 | } 445 | } 446 | 447 | return false; 448 | } 449 | } 450 | 451 | public class IGTDelayer { 452 | 453 | public Label Name; 454 | public TextBox Count; 455 | public Button Minus; 456 | public Button Plus; 457 | 458 | public const int Size = 26; 459 | 460 | public IGTDelayer(int index, string name, double delay) { 461 | Control.ControlCollection parent = FlowTimer.IGTTracking.Tab.Controls; 462 | int y = index * Size; 463 | 464 | Name = new Label(); 465 | Name.Text = name + ":"; 466 | Name.SetBounds(161, 14 + y, 115, 13); 467 | parent.Add(Name); 468 | 469 | Count = new TextBox(); 470 | Count.Text = "0"; 471 | Count.SetBounds(277, 10 + y, 23, 20); 472 | Count.TextAlign = HorizontalAlignment.Center; 473 | Count.Enabled = false; 474 | parent.Add(Count); 475 | 476 | Minus = new Button(); 477 | Minus.Text = "-"; 478 | Minus.SetBounds(303, 9 + y, 22, 22); 479 | Minus.Click += (s, e) => { Count.Text = (int.Parse(Count.Text) - 1).ToString(); FlowTimer.IGTTracking.ChangeAudio(-delay); }; 480 | parent.Add(Minus); 481 | 482 | Plus = new Button(); 483 | Plus.Text = "+"; 484 | Plus.SetBounds(327, 9 + y, 22, 22); 485 | Plus.Click += (s, e) => { Count.Text = (int.Parse(Count.Text) + 1).ToString(); FlowTimer.IGTTracking.ChangeAudio(+delay); }; 486 | parent.Add(Plus); 487 | } 488 | } 489 | 490 | public struct IGTTimerInfo { 491 | public uint Frame; 492 | public int Offset; 493 | public uint Interval; 494 | public uint NumBeeps; 495 | public double FPS; 496 | } 497 | 498 | public class IGTTimer { 499 | public const int X = 165; 500 | public static int Y = 160; 501 | public const int Size = 28; 502 | 503 | public TextBox TextBoxName; 504 | public TextBox TextBoxFrame; 505 | public TextBox TextBoxOffset; 506 | public TextBox TextBoxInterval; 507 | public TextBox TextBoxNumBeeps; 508 | public RadioButton RadioButton; 509 | public Button RemoveButton; 510 | public List Controls; 511 | 512 | public List TextBoxes { 513 | get { return Controls.FindAll(control => control.GetType() == typeof(TextBox)).ConvertAll(control => (TextBox) control); } 514 | } 515 | 516 | public int Index { 517 | get { 518 | return (TextBoxes[0].Location.Y - Y) / Size; 519 | } 520 | set { 521 | int yPosition = Y + value * Size; 522 | 523 | TextBoxName.SetBounds(X, yPosition, 65, 21); 524 | TextBoxFrame.SetBounds(X + 70, yPosition, 40, 21); 525 | TextBoxOffset.SetBounds(X + 115, yPosition, 65, 21); 526 | TextBoxInterval.SetBounds(X + 185, yPosition, 50, 21); 527 | TextBoxNumBeeps.SetBounds(X + 240, yPosition, 40, 21); 528 | 529 | RadioButton.SetBounds(X - 21, yPosition + 4, 14, 13); 530 | 531 | Rectangle lastbox = TextBoxes.Last().Bounds; 532 | RemoveButton.SetBounds(lastbox.X + lastbox.Width + 5, lastbox.Y, 38, 21); 533 | } 534 | } 535 | 536 | public IGTTimer(int index, string name = "Timer", string frame = "0", string offset = "0", string interval = "250", string numBeeps = "3") { 537 | Controls = new List(); 538 | 539 | TextBoxName = new TextBox(); 540 | TextBoxName.Text = name; 541 | Controls.Add(TextBoxName); 542 | 543 | TextBoxFrame = new TextBox(); 544 | TextBoxFrame.Text = frame; 545 | Controls.Add(TextBoxFrame); 546 | 547 | TextBoxOffset = new TextBox(); 548 | TextBoxOffset.Text = offset; 549 | Controls.Add(TextBoxOffset); 550 | 551 | TextBoxInterval = new TextBox(); 552 | TextBoxInterval.Text = interval; 553 | Controls.Add(TextBoxInterval); 554 | 555 | TextBoxNumBeeps = new TextBox(); 556 | TextBoxNumBeeps.Text = numBeeps; 557 | Controls.Add(TextBoxNumBeeps); 558 | 559 | foreach(TextBox textbox in TextBoxes) { 560 | textbox.Font = new Font(textbox.Font.FontFamily, 9.0f); 561 | textbox.TextChanged += DataChanged; 562 | textbox.TabStop = false; 563 | } 564 | 565 | RadioButton = new RadioButton(); 566 | RadioButton.Click += RadioButton_Click; 567 | Controls.Add(RadioButton); 568 | 569 | RemoveButton = new Button(); 570 | RemoveButton.Text = "-"; 571 | RemoveButton.Click += RemoveButton_Click; 572 | RemoveButton.TabStop = false; 573 | RemoveButton.DisableSelect(); 574 | Controls.Add(RemoveButton); 575 | 576 | Index = index; 577 | } 578 | 579 | public TimerError GetTimerInfo(out IGTTimerInfo info) { 580 | info = new IGTTimerInfo(); 581 | 582 | if(!uint.TryParse(TextBoxFrame.Text, out info.Frame)) { 583 | return TimerError.InvalidFrame; 584 | } 585 | 586 | if(!int.TryParse(TextBoxOffset.Text, out info.Offset)) { 587 | return TimerError.InvalidOffset; 588 | } 589 | 590 | if(!uint.TryParse(TextBoxInterval.Text, out info.Interval)) { 591 | return TimerError.InvalidInterval; 592 | } 593 | 594 | if(!uint.TryParse(TextBoxNumBeeps.Text, out info.NumBeeps)) { 595 | return TimerError.InvalidNumBeeps; 596 | } 597 | 598 | if(!double.TryParse(FlowTimer.IGTTracking.ComboBoxFPS.SelectedItem as string, out info.FPS)) { 599 | return TimerError.InvalidFPS; 600 | } 601 | 602 | if(info.Frame >= ushort.MaxValue << 8) { 603 | return TimerError.InvalidFrame; 604 | } 605 | 606 | if(info.Offset >= ushort.MaxValue << 9) { 607 | return TimerError.InvalidOffset; 608 | } 609 | 610 | if(info.Interval >= ushort.MaxValue << 9) { 611 | return TimerError.InvalidInterval; 612 | } 613 | 614 | if(info.NumBeeps >= ushort.MaxValue << 9) { 615 | return TimerError.InvalidNumBeeps; 616 | } 617 | 618 | return TimerError.NoError; 619 | } 620 | 621 | private void RadioButton_Click(object sender, EventArgs e) { 622 | FlowTimer.IGTTracking.SelectTimer(this); 623 | } 624 | 625 | private void DataChanged(object sender, EventArgs e) { 626 | FlowTimer.IGTTracking.SelectTimer(this); 627 | } 628 | 629 | private void RemoveButton_Click(object sender, EventArgs e) { 630 | FlowTimer.IGTTracking.RemoveTimer(this); 631 | } 632 | } 633 | } 634 | -------------------------------------------------------------------------------- /FlowTimer/MainForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace FlowTimer { 2 | partial class MainForm { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if(disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | this.LabelTimer = new System.Windows.Forms.Label(); 27 | this.ButtonStart = new System.Windows.Forms.Button(); 28 | this.ButtonStop = new System.Windows.Forms.Button(); 29 | this.ButtonAdd = new System.Windows.Forms.Button(); 30 | this.ButtonSettings = new System.Windows.Forms.Button(); 31 | this.ButtonLoadTimers = new System.Windows.Forms.Button(); 32 | this.ButtonSaveTimers = new System.Windows.Forms.Button(); 33 | this.PictureBoxPin = new System.Windows.Forms.PictureBox(); 34 | this.LabelBeeps = new System.Windows.Forms.Label(); 35 | this.LabelInterval = new System.Windows.Forms.Label(); 36 | this.LabelName = new System.Windows.Forms.Label(); 37 | this.LabelOffset = new System.Windows.Forms.Label(); 38 | this.TabControl = new System.Windows.Forms.TabControl(); 39 | this.TabPageFixedOffset = new System.Windows.Forms.TabPage(); 40 | this.TabPageVariableOffset = new System.Windows.Forms.TabPage(); 41 | this.ButtonUndo = new System.Windows.Forms.Button(); 42 | this.ButtonMinus = new System.Windows.Forms.Button(); 43 | this.ButtonPlus = new System.Windows.Forms.Button(); 44 | this.ComboBoxFPS = new System.Windows.Forms.ComboBox(); 45 | this.ButtonSubmit = new System.Windows.Forms.Button(); 46 | this.LabelBeeps2 = new System.Windows.Forms.Label(); 47 | this.TextBoxBeeps = new System.Windows.Forms.TextBox(); 48 | this.LabelInterval2 = new System.Windows.Forms.Label(); 49 | this.TextBoxInterval = new System.Windows.Forms.TextBox(); 50 | this.LabelOffset2 = new System.Windows.Forms.Label(); 51 | this.TextBoxOffset = new System.Windows.Forms.TextBox(); 52 | this.LabelFPS = new System.Windows.Forms.Label(); 53 | this.LabelFrame = new System.Windows.Forms.Label(); 54 | this.TextBoxFrame = new System.Windows.Forms.TextBox(); 55 | this.TabPageIGTTracking = new System.Windows.Forms.TabPage(); 56 | this.ButtonAdd3 = new System.Windows.Forms.Button(); 57 | this.ButtonSaveTimers3 = new System.Windows.Forms.Button(); 58 | this.ButtonLoadTimers3 = new System.Windows.Forms.Button(); 59 | this.ButtonUndoPlay = new System.Windows.Forms.Button(); 60 | this.ComboBoxGame = new System.Windows.Forms.ComboBox(); 61 | this.ComboBoxFPS3 = new System.Windows.Forms.ComboBox(); 62 | this.ButtonPlay = new System.Windows.Forms.Button(); 63 | this.LabelBeeps3 = new System.Windows.Forms.Label(); 64 | this.LabelInterval3 = new System.Windows.Forms.Label(); 65 | this.LabelGame = new System.Windows.Forms.Label(); 66 | this.LabelOffset3 = new System.Windows.Forms.Label(); 67 | this.LabelFPS3 = new System.Windows.Forms.Label(); 68 | this.LabelName3 = new System.Windows.Forms.Label(); 69 | this.LabelFrame3 = new System.Windows.Forms.Label(); 70 | ((System.ComponentModel.ISupportInitialize)(this.PictureBoxPin)).BeginInit(); 71 | this.TabControl.SuspendLayout(); 72 | this.TabPageFixedOffset.SuspendLayout(); 73 | this.TabPageVariableOffset.SuspendLayout(); 74 | this.TabPageIGTTracking.SuspendLayout(); 75 | this.SuspendLayout(); 76 | // 77 | // LabelTimer 78 | // 79 | this.LabelTimer.AutoSize = true; 80 | this.LabelTimer.Font = new System.Drawing.Font("Microsoft Sans Serif", 27.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 81 | this.LabelTimer.Location = new System.Drawing.Point(5, 4); 82 | this.LabelTimer.Name = "LabelTimer"; 83 | this.LabelTimer.Size = new System.Drawing.Size(117, 42); 84 | this.LabelTimer.TabIndex = 0; 85 | this.LabelTimer.Text = "0.000"; 86 | // 87 | // ButtonStart 88 | // 89 | this.ButtonStart.Location = new System.Drawing.Point(12, 57); 90 | this.ButtonStart.Name = "ButtonStart"; 91 | this.ButtonStart.Size = new System.Drawing.Size(121, 25); 92 | this.ButtonStart.TabIndex = 1; 93 | this.ButtonStart.TabStop = false; 94 | this.ButtonStart.Text = "Start"; 95 | this.ButtonStart.UseVisualStyleBackColor = true; 96 | this.ButtonStart.Click += new System.EventHandler(this.ButtonStart_Click); 97 | // 98 | // ButtonStop 99 | // 100 | this.ButtonStop.Location = new System.Drawing.Point(12, 85); 101 | this.ButtonStop.Name = "ButtonStop"; 102 | this.ButtonStop.Size = new System.Drawing.Size(121, 25); 103 | this.ButtonStop.TabIndex = 2; 104 | this.ButtonStop.TabStop = false; 105 | this.ButtonStop.Text = "Stop"; 106 | this.ButtonStop.UseVisualStyleBackColor = true; 107 | this.ButtonStop.Click += new System.EventHandler(this.ButtonStop_Click); 108 | // 109 | // ButtonAdd 110 | // 111 | this.ButtonAdd.Location = new System.Drawing.Point(0, 0); 112 | this.ButtonAdd.Name = "ButtonAdd"; 113 | this.ButtonAdd.Size = new System.Drawing.Size(67, 23); 114 | this.ButtonAdd.TabIndex = 28; 115 | this.ButtonAdd.TabStop = false; 116 | this.ButtonAdd.Text = "Add"; 117 | this.ButtonAdd.UseVisualStyleBackColor = true; 118 | this.ButtonAdd.Click += new System.EventHandler(this.ButtonAdd_Click); 119 | // 120 | // ButtonSettings 121 | // 122 | this.ButtonSettings.Location = new System.Drawing.Point(12, 113); 123 | this.ButtonSettings.Name = "ButtonSettings"; 124 | this.ButtonSettings.Size = new System.Drawing.Size(121, 25); 125 | this.ButtonSettings.TabIndex = 30; 126 | this.ButtonSettings.TabStop = false; 127 | this.ButtonSettings.Text = "Settings"; 128 | this.ButtonSettings.UseVisualStyleBackColor = true; 129 | this.ButtonSettings.Click += new System.EventHandler(this.ButtonSettings_Click); 130 | // 131 | // ButtonLoadTimers 132 | // 133 | this.ButtonLoadTimers.Location = new System.Drawing.Point(12, 141); 134 | this.ButtonLoadTimers.Name = "ButtonLoadTimers"; 135 | this.ButtonLoadTimers.Size = new System.Drawing.Size(121, 25); 136 | this.ButtonLoadTimers.TabIndex = 31; 137 | this.ButtonLoadTimers.TabStop = false; 138 | this.ButtonLoadTimers.Text = "Load Timers"; 139 | this.ButtonLoadTimers.UseVisualStyleBackColor = true; 140 | this.ButtonLoadTimers.Click += new System.EventHandler(this.ButtonLoadTimers_Click); 141 | // 142 | // ButtonSaveTimers 143 | // 144 | this.ButtonSaveTimers.Location = new System.Drawing.Point(12, 169); 145 | this.ButtonSaveTimers.Name = "ButtonSaveTimers"; 146 | this.ButtonSaveTimers.Size = new System.Drawing.Size(121, 25); 147 | this.ButtonSaveTimers.TabIndex = 32; 148 | this.ButtonSaveTimers.TabStop = false; 149 | this.ButtonSaveTimers.Text = "Save Timers"; 150 | this.ButtonSaveTimers.UseVisualStyleBackColor = true; 151 | this.ButtonSaveTimers.Click += new System.EventHandler(this.ButtonSaveTimers_Click); 152 | // 153 | // PictureBoxPin 154 | // 155 | this.PictureBoxPin.Location = new System.Drawing.Point(478, 4); 156 | this.PictureBoxPin.Name = "PictureBoxPin"; 157 | this.PictureBoxPin.Size = new System.Drawing.Size(16, 16); 158 | this.PictureBoxPin.TabIndex = 33; 159 | this.PictureBoxPin.TabStop = false; 160 | this.PictureBoxPin.Click += new System.EventHandler(this.PictureBoxPin_Click); 161 | // 162 | // LabelBeeps 163 | // 164 | this.LabelBeeps.AutoSize = true; 165 | this.LabelBeeps.Location = new System.Drawing.Point(374, 14); 166 | this.LabelBeeps.Name = "LabelBeeps"; 167 | this.LabelBeeps.Size = new System.Drawing.Size(37, 13); 168 | this.LabelBeeps.TabIndex = 16; 169 | this.LabelBeeps.Text = "Beeps"; 170 | // 171 | // LabelInterval 172 | // 173 | this.LabelInterval.AutoSize = true; 174 | this.LabelInterval.Location = new System.Drawing.Point(303, 14); 175 | this.LabelInterval.Name = "LabelInterval"; 176 | this.LabelInterval.Size = new System.Drawing.Size(42, 13); 177 | this.LabelInterval.TabIndex = 13; 178 | this.LabelInterval.Text = "Interval"; 179 | // 180 | // LabelName 181 | // 182 | this.LabelName.AutoSize = true; 183 | this.LabelName.Location = new System.Drawing.Point(161, 14); 184 | this.LabelName.Name = "LabelName"; 185 | this.LabelName.Size = new System.Drawing.Size(35, 13); 186 | this.LabelName.TabIndex = 7; 187 | this.LabelName.Text = "Name"; 188 | // 189 | // LabelOffset 190 | // 191 | this.LabelOffset.AutoSize = true; 192 | this.LabelOffset.Location = new System.Drawing.Point(232, 14); 193 | this.LabelOffset.Name = "LabelOffset"; 194 | this.LabelOffset.Size = new System.Drawing.Size(35, 13); 195 | this.LabelOffset.TabIndex = 12; 196 | this.LabelOffset.Text = "Offset"; 197 | // 198 | // TabControl 199 | // 200 | this.TabControl.Controls.Add(this.TabPageFixedOffset); 201 | this.TabControl.Controls.Add(this.TabPageVariableOffset); 202 | this.TabControl.Controls.Add(this.TabPageIGTTracking); 203 | this.TabControl.Location = new System.Drawing.Point(-3, 0); 204 | this.TabControl.Name = "TabControl"; 205 | this.TabControl.SelectedIndex = 0; 206 | this.TabControl.Size = new System.Drawing.Size(536, 250); 207 | this.TabControl.TabIndex = 34; 208 | // 209 | // TabPageFixedOffset 210 | // 211 | this.TabPageFixedOffset.BackColor = System.Drawing.SystemColors.Control; 212 | this.TabPageFixedOffset.Controls.Add(this.ButtonAdd); 213 | this.TabPageFixedOffset.Controls.Add(this.LabelTimer); 214 | this.TabPageFixedOffset.Controls.Add(this.PictureBoxPin); 215 | this.TabPageFixedOffset.Controls.Add(this.LabelBeeps); 216 | this.TabPageFixedOffset.Controls.Add(this.ButtonStart); 217 | this.TabPageFixedOffset.Controls.Add(this.LabelInterval); 218 | this.TabPageFixedOffset.Controls.Add(this.ButtonSaveTimers); 219 | this.TabPageFixedOffset.Controls.Add(this.LabelOffset); 220 | this.TabPageFixedOffset.Controls.Add(this.ButtonStop); 221 | this.TabPageFixedOffset.Controls.Add(this.LabelName); 222 | this.TabPageFixedOffset.Controls.Add(this.ButtonLoadTimers); 223 | this.TabPageFixedOffset.Controls.Add(this.ButtonSettings); 224 | this.TabPageFixedOffset.Location = new System.Drawing.Point(4, 22); 225 | this.TabPageFixedOffset.Name = "TabPageFixedOffset"; 226 | this.TabPageFixedOffset.Padding = new System.Windows.Forms.Padding(3); 227 | this.TabPageFixedOffset.Size = new System.Drawing.Size(528, 224); 228 | this.TabPageFixedOffset.TabIndex = 0; 229 | this.TabPageFixedOffset.Text = "Fixed Offset"; 230 | // 231 | // TabPageVariableOffset 232 | // 233 | this.TabPageVariableOffset.BackColor = System.Drawing.SystemColors.Control; 234 | this.TabPageVariableOffset.Controls.Add(this.ButtonUndo); 235 | this.TabPageVariableOffset.Controls.Add(this.ButtonMinus); 236 | this.TabPageVariableOffset.Controls.Add(this.ButtonPlus); 237 | this.TabPageVariableOffset.Controls.Add(this.ComboBoxFPS); 238 | this.TabPageVariableOffset.Controls.Add(this.ButtonSubmit); 239 | this.TabPageVariableOffset.Controls.Add(this.LabelBeeps2); 240 | this.TabPageVariableOffset.Controls.Add(this.TextBoxBeeps); 241 | this.TabPageVariableOffset.Controls.Add(this.LabelInterval2); 242 | this.TabPageVariableOffset.Controls.Add(this.TextBoxInterval); 243 | this.TabPageVariableOffset.Controls.Add(this.LabelOffset2); 244 | this.TabPageVariableOffset.Controls.Add(this.TextBoxOffset); 245 | this.TabPageVariableOffset.Controls.Add(this.LabelFPS); 246 | this.TabPageVariableOffset.Controls.Add(this.LabelFrame); 247 | this.TabPageVariableOffset.Controls.Add(this.TextBoxFrame); 248 | this.TabPageVariableOffset.Location = new System.Drawing.Point(4, 22); 249 | this.TabPageVariableOffset.Name = "TabPageVariableOffset"; 250 | this.TabPageVariableOffset.Padding = new System.Windows.Forms.Padding(3); 251 | this.TabPageVariableOffset.Size = new System.Drawing.Size(528, 224); 252 | this.TabPageVariableOffset.TabIndex = 1; 253 | this.TabPageVariableOffset.Text = "Variable Offset"; 254 | // 255 | // ButtonUndo 256 | // 257 | this.ButtonUndo.Location = new System.Drawing.Point(326, 38); 258 | this.ButtonUndo.Name = "ButtonUndo"; 259 | this.ButtonUndo.Size = new System.Drawing.Size(80, 22); 260 | this.ButtonUndo.TabIndex = 14; 261 | this.ButtonUndo.Text = "Undo"; 262 | this.ButtonUndo.UseVisualStyleBackColor = true; 263 | this.ButtonUndo.Click += new System.EventHandler(this.ButtonUndo_Click); 264 | // 265 | // ButtonMinus 266 | // 267 | this.ButtonMinus.Location = new System.Drawing.Point(413, 12); 268 | this.ButtonMinus.Name = "ButtonMinus"; 269 | this.ButtonMinus.Size = new System.Drawing.Size(22, 22); 270 | this.ButtonMinus.TabIndex = 13; 271 | this.ButtonMinus.Text = "-"; 272 | this.ButtonMinus.UseVisualStyleBackColor = true; 273 | this.ButtonMinus.Click += new System.EventHandler(this.ButtonMinus_Click); 274 | // 275 | // ButtonPlus 276 | // 277 | this.ButtonPlus.Location = new System.Drawing.Point(437, 12); 278 | this.ButtonPlus.Name = "ButtonPlus"; 279 | this.ButtonPlus.Size = new System.Drawing.Size(22, 22); 280 | this.ButtonPlus.TabIndex = 12; 281 | this.ButtonPlus.Text = "+"; 282 | this.ButtonPlus.UseVisualStyleBackColor = true; 283 | this.ButtonPlus.Click += new System.EventHandler(this.ButtonPlus_Click); 284 | // 285 | // ComboBoxFPS 286 | // 287 | this.ComboBoxFPS.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 288 | this.ComboBoxFPS.FormattingEnabled = true; 289 | this.ComboBoxFPS.Items.AddRange(new object[] { 290 | "59.7275", 291 | "59.6555", 292 | "59.8261", 293 | "60.0", 294 | "30.0", 295 | "25.0", 296 | "15.0"}); 297 | this.ComboBoxFPS.Location = new System.Drawing.Point(240, 39); 298 | this.ComboBoxFPS.Name = "ComboBoxFPS"; 299 | this.ComboBoxFPS.Size = new System.Drawing.Size(80, 21); 300 | this.ComboBoxFPS.TabIndex = 11; 301 | this.ComboBoxFPS.SelectedIndexChanged += new System.EventHandler(this.VariableTimer_DataChange); 302 | // 303 | // ButtonSubmit 304 | // 305 | this.ButtonSubmit.Location = new System.Drawing.Point(326, 12); 306 | this.ButtonSubmit.Name = "ButtonSubmit"; 307 | this.ButtonSubmit.Size = new System.Drawing.Size(80, 22); 308 | this.ButtonSubmit.TabIndex = 10; 309 | this.ButtonSubmit.Text = "Submit"; 310 | this.ButtonSubmit.UseVisualStyleBackColor = true; 311 | this.ButtonSubmit.Click += new System.EventHandler(this.ButtonSubmit_Click); 312 | // 313 | // LabelBeeps2 314 | // 315 | this.LabelBeeps2.AutoSize = true; 316 | this.LabelBeeps2.Location = new System.Drawing.Point(192, 120); 317 | this.LabelBeeps2.Name = "LabelBeeps2"; 318 | this.LabelBeeps2.Size = new System.Drawing.Size(40, 13); 319 | this.LabelBeeps2.TabIndex = 9; 320 | this.LabelBeeps2.Text = "Beeps:"; 321 | // 322 | // TextBoxBeeps 323 | // 324 | this.TextBoxBeeps.Location = new System.Drawing.Point(240, 117); 325 | this.TextBoxBeeps.Name = "TextBoxBeeps"; 326 | this.TextBoxBeeps.Size = new System.Drawing.Size(80, 20); 327 | this.TextBoxBeeps.TabIndex = 8; 328 | this.TextBoxBeeps.TextChanged += new System.EventHandler(this.VariableTimer_DataChange); 329 | // 330 | // LabelInterval2 331 | // 332 | this.LabelInterval2.AutoSize = true; 333 | this.LabelInterval2.Location = new System.Drawing.Point(192, 94); 334 | this.LabelInterval2.Name = "LabelInterval2"; 335 | this.LabelInterval2.Size = new System.Drawing.Size(45, 13); 336 | this.LabelInterval2.TabIndex = 7; 337 | this.LabelInterval2.Text = "Interval:"; 338 | // 339 | // TextBoxInterval 340 | // 341 | this.TextBoxInterval.Location = new System.Drawing.Point(240, 91); 342 | this.TextBoxInterval.Name = "TextBoxInterval"; 343 | this.TextBoxInterval.Size = new System.Drawing.Size(80, 20); 344 | this.TextBoxInterval.TabIndex = 6; 345 | this.TextBoxInterval.TextChanged += new System.EventHandler(this.VariableTimer_DataChange); 346 | // 347 | // LabelOffset2 348 | // 349 | this.LabelOffset2.AutoSize = true; 350 | this.LabelOffset2.Location = new System.Drawing.Point(192, 68); 351 | this.LabelOffset2.Name = "LabelOffset2"; 352 | this.LabelOffset2.Size = new System.Drawing.Size(38, 13); 353 | this.LabelOffset2.TabIndex = 5; 354 | this.LabelOffset2.Text = "Offset:"; 355 | // 356 | // TextBoxOffset 357 | // 358 | this.TextBoxOffset.Location = new System.Drawing.Point(240, 65); 359 | this.TextBoxOffset.Name = "TextBoxOffset"; 360 | this.TextBoxOffset.Size = new System.Drawing.Size(80, 20); 361 | this.TextBoxOffset.TabIndex = 4; 362 | this.TextBoxOffset.TextChanged += new System.EventHandler(this.VariableTimer_DataChange); 363 | // 364 | // LabelFPS 365 | // 366 | this.LabelFPS.AutoSize = true; 367 | this.LabelFPS.Location = new System.Drawing.Point(192, 42); 368 | this.LabelFPS.Name = "LabelFPS"; 369 | this.LabelFPS.Size = new System.Drawing.Size(30, 13); 370 | this.LabelFPS.TabIndex = 3; 371 | this.LabelFPS.Text = "FPS:"; 372 | // 373 | // LabelFrame 374 | // 375 | this.LabelFrame.AutoSize = true; 376 | this.LabelFrame.Location = new System.Drawing.Point(192, 16); 377 | this.LabelFrame.Name = "LabelFrame"; 378 | this.LabelFrame.Size = new System.Drawing.Size(39, 13); 379 | this.LabelFrame.TabIndex = 1; 380 | this.LabelFrame.Text = "Frame:"; 381 | // 382 | // TextBoxFrame 383 | // 384 | this.TextBoxFrame.Location = new System.Drawing.Point(240, 13); 385 | this.TextBoxFrame.Name = "TextBoxFrame"; 386 | this.TextBoxFrame.Size = new System.Drawing.Size(80, 20); 387 | this.TextBoxFrame.TabIndex = 0; 388 | this.TextBoxFrame.TextChanged += new System.EventHandler(this.VariableTimer_DataChange); 389 | // 390 | // TabPageIGTTracking 391 | // 392 | this.TabPageIGTTracking.BackColor = System.Drawing.SystemColors.Control; 393 | this.TabPageIGTTracking.Controls.Add(this.ButtonAdd3); 394 | this.TabPageIGTTracking.Controls.Add(this.ButtonSaveTimers3); 395 | this.TabPageIGTTracking.Controls.Add(this.ButtonLoadTimers3); 396 | this.TabPageIGTTracking.Controls.Add(this.ButtonUndoPlay); 397 | this.TabPageIGTTracking.Controls.Add(this.ComboBoxGame); 398 | this.TabPageIGTTracking.Controls.Add(this.ComboBoxFPS3); 399 | this.TabPageIGTTracking.Controls.Add(this.ButtonPlay); 400 | this.TabPageIGTTracking.Controls.Add(this.LabelBeeps3); 401 | this.TabPageIGTTracking.Controls.Add(this.LabelInterval3); 402 | this.TabPageIGTTracking.Controls.Add(this.LabelGame); 403 | this.TabPageIGTTracking.Controls.Add(this.LabelOffset3); 404 | this.TabPageIGTTracking.Controls.Add(this.LabelFPS3); 405 | this.TabPageIGTTracking.Controls.Add(this.LabelName3); 406 | this.TabPageIGTTracking.Controls.Add(this.LabelFrame3); 407 | this.TabPageIGTTracking.Location = new System.Drawing.Point(4, 22); 408 | this.TabPageIGTTracking.Name = "TabPageIGTTracking"; 409 | this.TabPageIGTTracking.Padding = new System.Windows.Forms.Padding(3); 410 | this.TabPageIGTTracking.Size = new System.Drawing.Size(528, 224); 411 | this.TabPageIGTTracking.TabIndex = 2; 412 | this.TabPageIGTTracking.Text = "IGT Tracking"; 413 | // 414 | // ButtonAdd3 415 | // 416 | this.ButtonAdd3.Location = new System.Drawing.Point(0, 0); 417 | this.ButtonAdd3.Name = "ButtonAdd3"; 418 | this.ButtonAdd3.Size = new System.Drawing.Size(67, 23); 419 | this.ButtonAdd3.TabIndex = 33; 420 | this.ButtonAdd3.TabStop = false; 421 | this.ButtonAdd3.Text = "Add"; 422 | this.ButtonAdd3.UseVisualStyleBackColor = true; 423 | this.ButtonAdd3.Click += new System.EventHandler(this.ButtonAdd3_Click); 424 | // 425 | // ButtonSaveTimers3 426 | // 427 | this.ButtonSaveTimers3.Location = new System.Drawing.Point(12, 169); 428 | this.ButtonSaveTimers3.Name = "ButtonSaveTimers3"; 429 | this.ButtonSaveTimers3.Size = new System.Drawing.Size(121, 25); 430 | this.ButtonSaveTimers3.TabIndex = 35; 431 | this.ButtonSaveTimers3.TabStop = false; 432 | this.ButtonSaveTimers3.Text = "Save Timers"; 433 | this.ButtonSaveTimers3.UseVisualStyleBackColor = true; 434 | this.ButtonSaveTimers3.Click += new System.EventHandler(this.ButtonSaveTimers3_Click); 435 | // 436 | // ButtonLoadTimers3 437 | // 438 | this.ButtonLoadTimers3.Location = new System.Drawing.Point(12, 141); 439 | this.ButtonLoadTimers3.Name = "ButtonLoadTimers3"; 440 | this.ButtonLoadTimers3.Size = new System.Drawing.Size(121, 25); 441 | this.ButtonLoadTimers3.TabIndex = 34; 442 | this.ButtonLoadTimers3.TabStop = false; 443 | this.ButtonLoadTimers3.Text = "Load Timers"; 444 | this.ButtonLoadTimers3.UseVisualStyleBackColor = true; 445 | this.ButtonLoadTimers3.Click += new System.EventHandler(this.ButtonLoadTimers3_Click); 446 | // 447 | // ButtonUndoPlay 448 | // 449 | this.ButtonUndoPlay.Location = new System.Drawing.Point(407, 114); 450 | this.ButtonUndoPlay.Name = "ButtonUndoPlay"; 451 | this.ButtonUndoPlay.Size = new System.Drawing.Size(80, 22); 452 | this.ButtonUndoPlay.TabIndex = 14; 453 | this.ButtonUndoPlay.Text = "Undo"; 454 | this.ButtonUndoPlay.UseVisualStyleBackColor = true; 455 | this.ButtonUndoPlay.Click += new System.EventHandler(this.ButtonUndoPlay_Click); 456 | // 457 | // ComboBoxGame 458 | // 459 | this.ComboBoxGame.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 460 | this.ComboBoxGame.FormattingEnabled = true; 461 | this.ComboBoxGame.Location = new System.Drawing.Point(407, 35); 462 | this.ComboBoxGame.Name = "ComboBoxGame"; 463 | this.ComboBoxGame.Size = new System.Drawing.Size(80, 21); 464 | this.ComboBoxGame.TabIndex = 11; 465 | this.ComboBoxGame.SelectedIndexChanged += new System.EventHandler(this.ComboBoxFPS3_DataChange); 466 | // 467 | // ComboBoxFPS3 468 | // 469 | this.ComboBoxFPS3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 470 | this.ComboBoxFPS3.FormattingEnabled = true; 471 | this.ComboBoxFPS3.Items.AddRange(new object[] { 472 | "59.7275", 473 | "59.6555", 474 | "59.8261", 475 | "60.0", 476 | "30.0", 477 | "25.0", 478 | "15.0"}); 479 | this.ComboBoxFPS3.Location = new System.Drawing.Point(407, 62); 480 | this.ComboBoxFPS3.Name = "ComboBoxFPS3"; 481 | this.ComboBoxFPS3.Size = new System.Drawing.Size(80, 21); 482 | this.ComboBoxFPS3.TabIndex = 11; 483 | this.ComboBoxFPS3.SelectedIndexChanged += new System.EventHandler(this.ComboBoxFPS3_DataChange); 484 | // 485 | // ButtonPlay 486 | // 487 | this.ButtonPlay.Location = new System.Drawing.Point(407, 88); 488 | this.ButtonPlay.Name = "ButtonPlay"; 489 | this.ButtonPlay.Size = new System.Drawing.Size(80, 22); 490 | this.ButtonPlay.TabIndex = 10; 491 | this.ButtonPlay.Text = "Play"; 492 | this.ButtonPlay.UseVisualStyleBackColor = true; 493 | this.ButtonPlay.Click += new System.EventHandler(this.ButtonPlay_Click); 494 | // 495 | // LabelBeeps3 496 | // 497 | this.LabelBeeps3.AutoSize = true; 498 | this.LabelBeeps3.Location = new System.Drawing.Point(401, 144); 499 | this.LabelBeeps3.Name = "LabelBeeps3"; 500 | this.LabelBeeps3.Size = new System.Drawing.Size(37, 13); 501 | this.LabelBeeps3.TabIndex = 9; 502 | this.LabelBeeps3.Text = "Beeps"; 503 | // 504 | // LabelInterval3 505 | // 506 | this.LabelInterval3.AutoSize = true; 507 | this.LabelInterval3.Location = new System.Drawing.Point(346, 144); 508 | this.LabelInterval3.Name = "LabelInterval3"; 509 | this.LabelInterval3.Size = new System.Drawing.Size(42, 13); 510 | this.LabelInterval3.TabIndex = 7; 511 | this.LabelInterval3.Text = "Interval"; 512 | // 513 | // LabelGame 514 | // 515 | this.LabelGame.AutoSize = true; 516 | this.LabelGame.Location = new System.Drawing.Point(368, 38); 517 | this.LabelGame.Name = "LabelGame"; 518 | this.LabelGame.Size = new System.Drawing.Size(38, 13); 519 | this.LabelGame.TabIndex = 3; 520 | this.LabelGame.Text = "Game:"; 521 | // 522 | // LabelOffset3 523 | // 524 | this.LabelOffset3.AutoSize = true; 525 | this.LabelOffset3.Location = new System.Drawing.Point(276, 144); 526 | this.LabelOffset3.Name = "LabelOffset3"; 527 | this.LabelOffset3.Size = new System.Drawing.Size(35, 13); 528 | this.LabelOffset3.TabIndex = 5; 529 | this.LabelOffset3.Text = "Offset"; 530 | // 531 | // LabelFPS3 532 | // 533 | this.LabelFPS3.AutoSize = true; 534 | this.LabelFPS3.Location = new System.Drawing.Point(368, 65); 535 | this.LabelFPS3.Name = "LabelFPS3"; 536 | this.LabelFPS3.Size = new System.Drawing.Size(30, 13); 537 | this.LabelFPS3.TabIndex = 3; 538 | this.LabelFPS3.Text = "FPS:"; 539 | // 540 | // LabelName3 541 | // 542 | this.LabelName3.AutoSize = true; 543 | this.LabelName3.Location = new System.Drawing.Point(161, 144); 544 | this.LabelName3.Name = "LabelName3"; 545 | this.LabelName3.Size = new System.Drawing.Size(35, 13); 546 | this.LabelName3.TabIndex = 1; 547 | this.LabelName3.Text = "Name"; 548 | // 549 | // LabelFrame3 550 | // 551 | this.LabelFrame3.AutoSize = true; 552 | this.LabelFrame3.Location = new System.Drawing.Point(231, 144); 553 | this.LabelFrame3.Name = "LabelFrame3"; 554 | this.LabelFrame3.Size = new System.Drawing.Size(36, 13); 555 | this.LabelFrame3.TabIndex = 1; 556 | this.LabelFrame3.Text = "Frame"; 557 | // 558 | // MainForm 559 | // 560 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 561 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 562 | this.ClientSize = new System.Drawing.Size(500, 228); 563 | this.Controls.Add(this.TabControl); 564 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 565 | this.Name = "MainForm"; 566 | this.Text = "FlowTimer"; 567 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); 568 | ((System.ComponentModel.ISupportInitialize)(this.PictureBoxPin)).EndInit(); 569 | this.TabControl.ResumeLayout(false); 570 | this.TabPageFixedOffset.ResumeLayout(false); 571 | this.TabPageFixedOffset.PerformLayout(); 572 | this.TabPageVariableOffset.ResumeLayout(false); 573 | this.TabPageVariableOffset.PerformLayout(); 574 | this.TabPageIGTTracking.ResumeLayout(false); 575 | this.TabPageIGTTracking.PerformLayout(); 576 | this.ResumeLayout(false); 577 | 578 | } 579 | 580 | #endregion 581 | 582 | public System.Windows.Forms.Label LabelTimer; 583 | public System.Windows.Forms.Button ButtonStart; 584 | public System.Windows.Forms.Button ButtonStop; 585 | public System.Windows.Forms.Button ButtonAdd; 586 | public System.Windows.Forms.Button ButtonSettings; 587 | public System.Windows.Forms.Button ButtonLoadTimers; 588 | public System.Windows.Forms.Button ButtonSaveTimers; 589 | public System.Windows.Forms.PictureBox PictureBoxPin; 590 | public System.Windows.Forms.Label LabelBeeps; 591 | public System.Windows.Forms.Label LabelInterval; 592 | public System.Windows.Forms.Label LabelName; 593 | public System.Windows.Forms.Label LabelOffset; 594 | public System.Windows.Forms.TabControl TabControl; 595 | private System.Windows.Forms.TabPage TabPageFixedOffset; 596 | private System.Windows.Forms.TabPage TabPageVariableOffset; 597 | private System.Windows.Forms.TabPage TabPageIGTTracking; 598 | public System.Windows.Forms.TextBox TextBoxFrame; 599 | public System.Windows.Forms.Label LabelFrame; 600 | public System.Windows.Forms.Label LabelFrame3; 601 | public System.Windows.Forms.Label LabelBeeps2; 602 | public System.Windows.Forms.Label LabelBeeps3; 603 | public System.Windows.Forms.TextBox TextBoxBeeps; 604 | public System.Windows.Forms.Label LabelInterval2; 605 | public System.Windows.Forms.Label LabelInterval3; 606 | public System.Windows.Forms.TextBox TextBoxInterval; 607 | public System.Windows.Forms.Label LabelOffset2; 608 | public System.Windows.Forms.Label LabelOffset3; 609 | public System.Windows.Forms.TextBox TextBoxOffset; 610 | public System.Windows.Forms.Label LabelFPS; 611 | public System.Windows.Forms.Label LabelFPS3; 612 | public System.Windows.Forms.Button ButtonSubmit; 613 | public System.Windows.Forms.Button ButtonPlay; 614 | public System.Windows.Forms.ComboBox ComboBoxFPS; 615 | public System.Windows.Forms.ComboBox ComboBoxFPS3; 616 | public System.Windows.Forms.Button ButtonMinus; 617 | public System.Windows.Forms.Button ButtonPlus; 618 | public System.Windows.Forms.Button ButtonUndo; 619 | public System.Windows.Forms.Button ButtonUndoPlay; 620 | public System.Windows.Forms.Label LabelName3; 621 | public System.Windows.Forms.Button ButtonAdd3; 622 | public System.Windows.Forms.Button ButtonSaveTimers3; 623 | public System.Windows.Forms.Button ButtonLoadTimers3; 624 | public System.Windows.Forms.ComboBox ComboBoxGame; 625 | public System.Windows.Forms.Label LabelGame; 626 | } 627 | } 628 | --------------------------------------------------------------------------------