├── 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 |
--------------------------------------------------------------------------------