public BmpMain()
        {
            InitializeComponent();

            // Clear local orchestra
            InfoTabs.TabPages.Remove(localOrchestraTab);

            this.UpdatePerformance();

            BmpUpdate update = new BmpUpdate();

            // True on debugging through VS
            if (!Program.programOptions.DisableUpdate)
            {
                updateResult = update.ShowDialog();
                if (updateResult == DialogResult.Yes)
                {
                    updateTitle  = update.version.updateTitle;
                    updateText   = update.version.updateText;
                    updateResult = DialogResult.Yes;
                }
                if (updateResult == DialogResult.Ignore)
                {
                    string log = " This is a preview of a future version of BMP! Please be kind and report any bugs or unexpected behaviors to discord channel.";
                    ChatLogAll.AppendRtf(BmpChatParser.FormatRtf(log, Color.LightYellow, true));
                }
                if (!string.IsNullOrEmpty(update.version.updateLog))
                {
                    string log = string.Format("= BMP Update =\n {0} \n", update.version.updateLog);
                    ChatLogAll.AppendRtf(BmpChatParser.FormatRtf(log, Color.LightGreen, true));
                }
                string         sigVersion = "";
                List <Process> processes  = new List <Process>(Process.GetProcessesByName("ffxiv_dx11"));
                if (processes.Count > 0)
                {
                    Process proc     = processes[0];
                    string  filename = proc.MainModule.FileName;
                    string  ver      = filename.Replace("ffxiv_dx11.exe", "../boot/ffxivboot.ver");
                    if (File.Exists(ver))
                    {
                        sigVersion = File.ReadAllText(ver);
                        this.Log(string.Format("FFXIV ver: {0}", sigVersion));
                    }
                }
                update.UpdateSignature(sigVersion);
            }

            this.Text = update.version.ToString();


            FFXIV.findProcessRequest += delegate(Object o, EventArgs empty) {
                this.Invoke(t => t.FindProcess());
            };

            FFXIV.findProcessError += delegate(Object o, BmpHook.ProcessError error) {
                this.Invoke(t => t.ErrorProcess(error));
            };

            FFXIV.hotkeys.OnFileLoad += delegate(Object o, EventArgs empty) {
                this.Invoke(t => t.Hotkeys_OnFileLoad(FFXIV.hotkeys));
            };
            FFXIV.hook.OnKeyPressed += Hook_OnKeyPressed;


            FFXIV.memory.OnChatReceived += delegate(object o, ChatLogItem item) {
                this.Invoke(t => t.Memory_OnChatReceived(item));
            };
            FFXIV.memory.OnPerformanceChanged += delegate(object o, SigPerfData data) {
                this.Invoke(t => t.Memory_OnPerformanceReadyChanged(data.IsUp()));
                LocalOrchestra.Invoke(t => t.PerformerUpdate(FFXIV.memory.actorData, data));
            };
            FFXIV.memory.OnLocalPlayerLogin += delegate(object o, ActorData data) {
                FFXIVMemory memory = (o as FFXIVMemory);

                string world = memory.World;
                if (string.IsNullOrEmpty(world))
                {
                    this.Log(string.Format("Character [{0}] logged in.", data.name));
                }
                else
                {
                    this.Log(string.Format("Character [{0}] at [{1}] logged in.", data.name, world));
                }

                this.Invoke(t => t.CheckDonation(data.name, world));
                this.Invoke(t => t.UpdatePerformance());
            };
            FFXIV.memory.OnLocalPlayerLogout += delegate(object o, ActorData data) {
                string format = string.Format("Character [{0}] logged out.", data.name);
                this.Log(format);
            };


            /*
             * FFXIV.memory.OnProcessReady += delegate (object o, Process proc) {
             *      this.Log(string.Format("[{0}] Process scanned and ready.", proc.Id));
             *      if(Sharlayan.Reader.CanGetActors()) {
             *              if(!Sharlayan.Reader.CanGetCharacterId()) {
             *                      this.Log("[MEMORY] Cannot get Character ID.\n Key bindings won't be loaded, load it manually by selecting an ID in the bottom right.");
             *              }
             *              if(!Sharlayan.Reader.CanGetChatInput()) {
             *                      this.Log("[MEMORY] Cannot get chat input status.\n Automatic pausing when chatting won't work.");
             *              }
             *              if(!Sharlayan.Reader.CanGetPerformance()) {
             *                      this.Log("[MEMORY] Cannot get performance status.\n Performance detection will not work. Force it to work by ticking Settings > Force playback.");
             *              }
             *      } else {
             *              List<Sharlayan.Models.Signature> signatures = Sharlayan.Signatures.Resolve().ToList();
             *              int sigCount = signatures.Count;
             *              foreach(Sharlayan.Models.Signature sig in signatures) {
             *                      if(Sharlayan.Scanner.Instance.Locations.ContainsKey(sig.Key)) {
             *                              sigCount--;
             *                      } else {
             *                              Console.WriteLine(string.Format("Could not find signature {0}", sig.Key));
             *                      }
             *              }
             *              if(sigCount == signatures.Count) {
             *                      this.Log(string.Format("[MEMORY] Cannot read memory ({0}/{1}). Functionality will be severely limited.", sigCount, signatures.Count));
             *                      this.Invoke(t => t.ErrorProcess(BmpHook.ProcessError.ProcessNonAccessible));
             *              } else {
             *                      this.Log("[MEMORY] Cannot read actors. Local performance will be broken.");
             *              }
             *      }
             * };
             * FFXIV.memory.OnProcessLost += delegate (object o, EventArgs arg) {
             *      this.Log("Attached process exited.");
             * };
             * FFXIV.memory.OnCurrentPlayerJobChange += delegate (object o, CurrentPlayerResult res) {
             *      this.Invoke(t => t.Memory_OnCurrentPlayerJobChange(res));
             * };
             * FFXIV.memory.OnPartyChanged += delegate (object o, PartyResult res) {
             *      this.Invoke(t => t.LocalOrchestraUpdate());
             * };
             */

            Player.OnStatusChange += delegate(object o, PlayerStatus status) {
                this.Invoke(t => t.UpdatePerformance());
            };

            Player.OnSongSkip += OnSongSkip;

            Player.OnMidiProgressChange += OnPlayProgressChange;

            Player.OnMidiStatusChange += OnPlayStatusChange;
            Player.OnMidiStatusEnded  += OnPlayStatusEnded;

            Player.OnMidiNote  += OnMidiVoice;
            Player.OffMidiNote += OffMidiVoice;

            Player.Player.OpenInputDevice(Settings.GetMidiInput().name);

            Settings.OnMidiInputChange += delegate(object o, MidiInput input) {
                Player.Player.CloseInputDevice();
                if (input.id != -1)
                {
                    Player.Player.OpenInputDevice(input.name);
                    Log(string.Format("Switched to {0} ({1})", input.name, input.id));
                }
            };
            Settings.OnKeyboardTest += delegate(object o, EventArgs arg) {
                foreach (FFXIVKeybindDat.Keybind keybind in FFXIV.hotkeys.GetPerformanceKeybinds())
                {
                    FFXIV.hook.SendSyncKeybind(keybind);
                    Thread.Sleep(100);
                }
            };

            Settings.OnForcedOpen += delegate(object o, bool open)
            {
                this.Invoke(t => {
                    if (open)
                    {
                        Log(string.Format("Forced playback was enabled. You will not be able to use keybinds, such as spacebar."));
                        WarningLog("Forced playback enabled.");
                    }

                    t.UpdatePerformance();
                });
            };

            Explorer.OnBrowserVisibleChange += delegate(object o, bool visible) {
                MainTable.SuspendLayout();
                MainTable.RowStyles[MainTable.GetRow(ChatPlaylistTable)].Height   = visible ? 0 : 100;
                MainTable.RowStyles[MainTable.GetRow(ChatPlaylistTable)].SizeType = visible ? SizeType.Absolute : SizeType.Percent;
                //ChatPlaylistTable.Invoke(t => t.Visible = !visible);

                MainTable.RowStyles[MainTable.GetRow(Explorer)].Height   = visible ? 100 : 30;
                MainTable.RowStyles[MainTable.GetRow(Explorer)].SizeType = visible ? SizeType.Percent : SizeType.Absolute;
                MainTable.ResumeLayout(true);
            };
            Explorer.OnBrowserSelect += Browser_OnMidiSelect;

            Playlist.OnMidiSelect               += Playlist_OnMidiSelect;
            Playlist.OnPlaylistRequestAdd       += Playlist_OnPlaylistRequestAdd;
            Playlist.OnPlaylistManualRequestAdd += Playlist_OnPlaylistManualRequestAdd;

            this.ResizeBegin += (s, e) => {
                LocalOrchestra.SuspendLayout();
            };
            this.ResizeEnd += (s, e) => {
                LocalOrchestra.ResumeLayout(true);
            };

            if (Properties.Settings.Default.SaveLog)
            {
                FileTarget target = new NLog.Targets.FileTarget("chatlog")
                {
                    FileName          = "logs/ff14log.txt",
                    Layout            = @"${date:format=yyyy-MM-dd HH\:mm\:ss} ${message}",
                    ArchiveDateFormat = "${shortdate}",
                    ArchiveEvery      = FileArchivePeriod.Day,
                    ArchiveFileName   = "logs/ff14log-${shortdate}.txt",
                    Encoding          = Encoding.UTF8,
                    KeepFileOpen      = true,
                };

                var config = new NLog.Config.LoggingConfiguration();
                config.AddRule(LogLevel.Info, LogLevel.Info, target);
                NLog.LogManager.Configuration = config;
            }

            string upath = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoaming).FilePath;

            //Console.WriteLine(string.Format(".config: [{0}]", upath));

            Settings.RefreshMidiInput();

            Log("Bard Music Player initialized.");
        }