Full Code of ho0ber/NK2Tray for AI

master d6fdc587b24f cached
43 files
256.0 KB
112.7k tokens
150 symbols
1 requests
Download .txt
Showing preview only (269K chars total). Download the full file or copy to clipboard to get everything.
Repository: ho0ber/NK2Tray
Branch: master
Commit: d6fdc587b24f
Files: 43
Total size: 256.0 KB

Directory structure:
gitextract_koccivs3/

├── .gitattributes
├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.md
│       └── feature_request.md
├── .gitignore
├── NK2Tray/
│   ├── App.config
│   ├── AudioDevice.cs
│   ├── Button.cs
│   ├── ConfigSaver.cs
│   ├── DevicePathMapper.cs
│   ├── EasyControl.cs
│   ├── Fader.cs
│   ├── FodyWeavers.xml
│   ├── FodyWeavers.xsd
│   ├── IconExtractor.cs
│   ├── MediaTools.cs
│   ├── MidiDevice.cs
│   ├── MixerSession.cs
│   ├── NK2Tray.csproj
│   ├── NanoKontrol2.cs
│   ├── OP1.cs
│   ├── Program.cs
│   ├── Properties/
│   │   ├── AssemblyInfo.cs
│   │   ├── Resources.Designer.cs
│   │   ├── Resources.resx
│   │   ├── Settings.Designer.cs
│   │   └── Settings.settings
│   ├── WindowTools.cs
│   ├── XtouchMini.cs
│   └── packages.config
├── NK2Tray.sln
├── NK2Tray_Display/
│   ├── BOM.md
│   ├── NK2DisplayCaseA.stl
│   ├── NK2DisplayCaseB.stl
│   ├── NK2DisplayLatch.stl
│   ├── NK2Tray_DisplayFW.ino
│   ├── NK2Tray_DisplayFW_V2/
│   │   ├── NK2Tray_DisplayFW_V2.ino
│   │   ├── eeprom.ino
│   │   ├── logos.h
│   │   ├── readme.md
│   │   └── webServer.ino
│   └── Readme.md
├── README.md
└── license.txt

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs     diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following 
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln       merge=binary
#*.csproj    merge=binary
#*.vbproj    merge=binary
#*.vcxproj   merge=binary
#*.vcproj    merge=binary
#*.dbproj    merge=binary
#*.fsproj    merge=binary
#*.lsproj    merge=binary
#*.wixproj   merge=binary
#*.modelproj merge=binary
#*.sqlproj   merge=binary
#*.wwaproj   merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg   binary
#*.png   binary
#*.gif   binary

###############################################################################
# diff behavior for common document formats
# 
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the 
# entries below.
###############################################################################
#*.doc   diff=astextplain
#*.DOC   diff=astextplain
#*.docx  diff=astextplain
#*.DOCX  diff=astextplain
#*.dot   diff=astextplain
#*.DOT   diff=astextplain
#*.pdf   diff=astextplain
#*.PDF   diff=astextplain
#*.rtf   diff=astextplain
#*.RTF   diff=astextplain


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
 - OS: [e.g. iOS]
 - Browser [e.g. chrome, safari]
 - Version [e.g. 22]

**Midi device:**
 - e.g. Behringer XTouch Mini
 - e.g. Korg NanoControl

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: "[feature]"
labels: enhancement
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/

# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# DNX
project.lock.json
project.fragment.lock.json
artifacts/

*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm

# SQL Server files
*.mdf
*.ldf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# JetBrains Rider
.idea/
*.sln.iml

# CodeRush
.cr/

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
NK2Tray.exe.Config
Sam's\ Releases/


================================================
FILE: NK2Tray/App.config
================================================
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
</configuration>

================================================
FILE: NK2Tray/AudioDevice.cs
================================================
using NAudio.CoreAudioApi;
using NAudio.CoreAudioApi.Interfaces;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace NK2Tray
{
    public class AudioDevice
    {
        public MidiDevice midiDevice;
        public MMDeviceCollection outputDevices;
        public MMDeviceCollection inputDevices;


        private List<MixerSession> mixerSessionListCache;
        private long currentCacheDate;
        private int cacheExpireTime = 1500;

        private void UpdateDevices()
        {
            // Add audio devices to each fader menu items
            var deviceEnumerator = new MMDeviceEnumerator();

            //device = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
            //device.AudioSessionManager.OnSessionCreated += OnSessionCreated;
            //deviceVolume = device.AudioEndpointVolume;

            outputDevices = deviceEnumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active);
            inputDevices = deviceEnumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active);
            
            foreach (MMDevice mmDevice in outputDevices)
            {
                mmDevice.AudioSessionManager.OnSessionCreated += OnSessionCreated;
            }

            foreach (MMDevice mmDevice in inputDevices)
            {
                mmDevice.AudioSessionManager.OnSessionCreated += OnSessionCreated;
            }
        }

        public AudioEndpointVolume GetDeviceVolumeObject(String deviceIdentifier)
        {
            // Used to handle "COM object that has been separated from its underlying RCW cannot be used" issue.
            UpdateDevices();
            return GetDeviceByIdentifier(deviceIdentifier).AudioEndpointVolume;
        }

        private void OnSessionCreated(object sender, IAudioSessionControl newSession)
        {
            Console.WriteLine("OnSessionCreated");        
            midiDevice.LoadAssignments();

            // These correspond with the below events handlers
            //NAudioEventCallbacks callbacks = new NAudioEventCallbacks();
            //AudioSessionEventsCallback notifications = new AudioSessionEventsCallback(callbacks);
            //audioSession.RegisterEventClient(callbacks);
        }



        /*
        // Saving these for later because I'll definitely need them.
        public class NAudioEventCallbacks : IAudioSessionEventsHandler
        {
            public void OnChannelVolumeChanged(uint channelCount, IntPtr newVolumes, uint channelIndex) { Console.WriteLine("OnChannelVolumeChanged"); }

            public void OnDisplayNameChanged(string displayName) { Console.WriteLine("OnDisplayNameChanged"); }

            public void OnGroupingParamChanged(ref Guid groupingId) { Console.WriteLine("OnGroupingParamChanged"); }

            public void OnIconPathChanged(string iconPath) { Console.WriteLine("OnIconPathChanged"); }

            public void OnSessionDisconnected(AudioSessionDisconnectReason disconnectReason) { Console.WriteLine("OnSessionDisconnected"); }

            public void OnStateChanged(AudioSessionState state) { Console.WriteLine("OnStateChanged"); }

            public void OnVolumeChanged(float volume, bool isMuted) { Console.WriteLine("OnVolumeChanged"); }
        }
        */

        public List<MixerSession> GetCachedMixerSessions()
        {
            if (mixerSessionListCache != null && (currentCacheDate + cacheExpireTime > DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond))
            {
                return mixerSessionListCache;
            }

            mixerSessionListCache = GetMixerSessions();
            currentCacheDate = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
            return mixerSessionListCache;
        }

        private List<MixerSession> GetMixerSessions()
        {
            var mixerSessions = new List<MixerSession>();
            var sessionsByIdent = new Dictionary<String, List<AudioSessionControl>>();

            UpdateDevices();
            for (int j = 0; j < outputDevices.Count; j++)
            {
                var sessions = outputDevices[j].AudioSessionManager.Sessions;

                for (int i = 0; i < sessions.Count; i++)
                {
                    var session = sessions[i];
                    if (session.State != AudioSessionState.AudioSessionStateExpired)
                    {
                        String searchIdentifier = session.GetSessionIdentifier.Substring(session.GetSessionIdentifier.IndexOf("|") + 1, session.GetSessionIdentifier.Length - session.GetSessionIdentifier.IndexOf("|") - 1);
                        if (!sessionsByIdent.ContainsKey(searchIdentifier))
                            sessionsByIdent[searchIdentifier] = new List<AudioSessionControl>();

                        sessionsByIdent[searchIdentifier].Add(session);
                    }
                }
            }

            /*
            // Commented out Iput device code because it breaks the session lists when input and ouput is controlled at the same time
            for (int j = 0; j < inputDevices.Count; j++)
            {
                var sessions = inputDevices[j].AudioSessionManager.Sessions;
                for (int i = 0; i < sessions.Count; i++)
                {
                    var session = sessions[i];
                    if (session.State != AudioSessionState.AudioSessionStateExpired)
                    {
                        String searchIdentifier = session.GetSessionIdentifier.Substring(session.GetSessionIdentifier.IndexOf("|") + 1, session.GetSessionIdentifier.Length - session.GetSessionIdentifier.IndexOf("|") - 1);
                        if (!sessionsByIdent.ContainsKey(searchIdentifier))
                            sessionsByIdent[searchIdentifier] = new List<AudioSessionControl>();

                        sessionsByIdent[searchIdentifier].Add(session);
                    }
                }
            }
            */

            foreach (var ident in sessionsByIdent.Keys.ToList())
            {
                var identSessions = sessionsByIdent[ident]; //.OrderBy(i => (int)Process.GetProcessById((int)i.GetProcessID).MainWindowHandle).ToList();

                bool dup = identSessions.Count > 1;

                var process = FindLivingProcess(identSessions);
                string label = (process != null) ? process.ProcessName : ident;

                if (HasSystemSoundsSession(identSessions))
                    label = "System Sounds";
                                
                var mixerSession = new MixerSession(this, label, ident, identSessions, SessionType.Application);
                mixerSessions.Add(mixerSession);
            }

            return mixerSessions;
        }
                
        public MMDevice GetDeviceByIdentifier(String identifier)
        {
            UpdateDevices();

            String deviceId = (identifier==null?"":identifier);
            if (deviceId.IndexOf("|")>-1) { //work with session identifier also
                deviceId = deviceId.Substring(0, deviceId.IndexOf("|"));
            };

            for (int i = 0; i < outputDevices.Count; i++)
            {
                if (outputDevices[i].ID.Equals(deviceId))
                    return outputDevices[i];
            }
            for (int i = 0; i < inputDevices.Count; i++)
            {
                if (inputDevices[i].ID.Equals(deviceId))
                    return inputDevices[i];
            }
            //return default if none found (config/save retro-compatibility)
            var deviceEnumerator = new MMDeviceEnumerator();
            return deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
        }

        public Process FindLivingProcess(List<AudioSessionControl> sessions)
        {
            Process process = null;
            foreach (var session in sessions)
            {
                try
                {
                    process = Process.GetProcessById((int)session.GetProcessID);
                    return process;
                }
                catch (ArgumentException e)
                {
                    Console.WriteLine($@"Failed to find process {session.GetProcessID}");
                }
            }
            return process;
        }

        public bool HasSystemSoundsSession(List<AudioSessionControl> sessions)
        {
            foreach (var session in sessions)
                if (session.IsSystemSoundsSession)
                    return true;

            return false;
        }

        public MixerSession FindMixerSession(string sessionIdentifier)
        {
            var mixerSessions = GetCachedMixerSessions();

            foreach (var mixerSession in mixerSessions)
            {
                foreach (var session in mixerSession.audioSessions)
                {
                    if (session.GetSessionIdentifier.Contains(sessionIdentifier)) 
                        return mixerSession;                  
                }
            }

            return null;
        }

        private struct ProcessCache
        {
            public Process process;
            public int id;

            public ProcessCache(Process process, int id)
            {
                this.process = process;
                this.id = id;
            }
        }
        private ProcessCache processCache = new ProcessCache(null, -1);
              
        public MixerSession FindMixerSession(int pid)
        {
            var mixerSessions = GetCachedMixerSessions();

            foreach (var mixerSession in mixerSessions)
                foreach (var session in mixerSession.audioSessions)
                    if (session.GetProcessID == pid)
                        return mixerSession;

            // if mixer session was not found becuase the pid does not match the pid of any audio session try finding it by process name
            // this is necessary since chrome and some other applications have some weired process structure
            if (processCache.id != pid)
            {
                processCache = new ProcessCache(Process.GetProcessById(pid), pid);
            }
            Process process = processCache.process;

            foreach (var mixerSession in mixerSessions)
            {
                if (mixerSession.label.Equals(process.ProcessName))
                {
                    return mixerSession;
                }
            }

            return null;
        }
    }
}


================================================
FILE: NK2Tray/Button.cs
================================================
using NAudio.Midi;
using System;

namespace NK2Tray
{
    public enum ButtonType
    {
        MediaPlay,
        MediaStop,
        MediaPrevious,
        MediaNext,
        MediaRecord
    }

    public class Button
    {
        private bool activeHandling = false;

        private bool light = false;

        public ButtonType buttonType;
        public int controller;
        public MidiCommandCode commandCode;
        public int channel;
        public MidiOut midiOut;

        public Button(ref MidiOut midiOutRef, ButtonType butType, int cont, bool initialState, MidiCommandCode code=MidiCommandCode.ControlChange)
        {
            commandCode = code;
            channel = 1;
            buttonType = butType;
            controller = cont;
            midiOut = midiOutRef;
            SetLight(initialState);
        }

        public void SetLight(bool state)
        {
            light = state;
            if (commandCode == MidiCommandCode.ControlChange)
                midiOut.Send(new ControlChangeEvent(0, channel, (MidiController)(controller), state ? 127 : 0).GetAsShortMessage());
            else if (commandCode == MidiCommandCode.NoteOn)
                midiOut.Send(new NoteOnEvent(0, 1, controller, state ? 127 : 0, 0).GetAsShortMessage());
        }

        public bool HandleEvent(MidiInMessageEventArgs e, MidiDevice device)
        {
            if (!IsHandling())
            {
                SetHandling(true);

                if (e.MidiEvent.CommandCode != commandCode)
                    return false;

                int c;

                if (commandCode == MidiCommandCode.ControlChange)
                {
                    var me = (ControlChangeEvent)e.MidiEvent;

                    if (me.Channel != channel || me.ControllerValue != 127) // Only on correct channel and button-down (127)
                        return false;

                    c = (int)me.Controller;
                }
                else if (commandCode == MidiCommandCode.NoteOn)
                {
                    var me = (NoteEvent)e.MidiEvent;

                    if (me.Channel != channel || me.Velocity != 127) // Only on correct channel and button-down (127)
                        return false;

                    c = me.NoteNumber;
                }
                else
                    return false;

                if (c == controller)
                {
                    switch (buttonType)
                    {
                        case ButtonType.MediaNext:
                            MediaTools.Next();
                            break;
                        case ButtonType.MediaPrevious:
                            MediaTools.Previous();
                            break;
                        case ButtonType.MediaStop:
                            MediaTools.Stop();
                            break;
                        case ButtonType.MediaPlay:
                            MediaTools.Play();
                            break;
                        case ButtonType.MediaRecord:
                            device.LightShow();
                            break;
                        default:
                            break;
                    }
                    return true;
                }
            }

            return false;
        }

        public bool IsHandling()
        {
            return activeHandling;
        }

        public void SetHandling(bool handling)
        {
            activeHandling = handling;
        }

        public bool GetLight() { return light; }

    }
}


================================================
FILE: NK2Tray/ConfigSaver.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;


namespace NK2Tray
{
    class ConfigSaver
    {
        public static void AddOrUpdateAppSettings(string key, string value)
        {
            try
            {
                var configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                var settings = configFile.AppSettings.Settings;
                if (settings[key] == null)
                {
                    settings.Add(key, value);
                }
                else
                {
                    settings[key].Value = value;
                }
                configFile.Save(ConfigurationSaveMode.Modified);
                ConfigurationManager.RefreshSection(configFile.AppSettings.SectionInformation.Name);
            }
            catch (ConfigurationErrorsException)
            {
                Console.WriteLine("Error writing app settings");
            }
        }

        public static string GetAppSettings(string key)
        {
            try
            {
                var configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                var settings = configFile.AppSettings.Settings;
                if (settings[key] != null)
                    return settings[key].Value;
                else
                    return null;
            }
            catch
            {
                Console.WriteLine("Error getting app settings");
            }
            return null;
        }
    }

}


================================================
FILE: NK2Tray/DevicePathMapper.cs
================================================
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace NK2Tray
{
    public static class DevicePathMapper
    {
        [DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
        private static extern uint QueryDosDevice([In] string lpDeviceName, [Out] StringBuilder lpTargetPath, [In] int ucchMax);

        public static string FromDevicePath(string devicePath)
        {
            var drive = Array.Find(DriveInfo.GetDrives(), d => devicePath.StartsWith(d.GetDevicePath(), StringComparison.InvariantCultureIgnoreCase));
            return drive != null ?
                devicePath.ReplaceFirst(drive.GetDevicePath(), drive.GetDriveLetter()) :
                null;
        }

        private static string GetDevicePath(this DriveInfo driveInfo)
        {
            var devicePathBuilder = new StringBuilder(128);
            return QueryDosDevice(driveInfo.GetDriveLetter(), devicePathBuilder, devicePathBuilder.Capacity + 1) != 0 ?
                devicePathBuilder.ToString() :
                null;
        }

        private static string GetDriveLetter(this DriveInfo driveInfo)
        {
            return driveInfo.Name.Substring(0, 2);
        }

        private static string ReplaceFirst(this string text, string search, string replace)
        {
            int pos = text.IndexOf(search);
            if (pos < 0)
            {
                return text;
            }
            return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
        }
    }
}


================================================
FILE: NK2Tray/EasyControl.cs
================================================
using NAudio.Midi;
using System.Collections.Generic;
using System.Linq;

namespace NK2Tray
{
    public class EasyControl : MidiDevice
    {
        public override string SearchString => "easy";
        public FaderDef FirstFourFaderDef => new FaderDef(
            false, // delta
            127f,   // range
            1,     // channel
            false,  // selectPresent
            true,  // mutePresent
            false,  // recordPresent
            false, // subfaderPresent
            14,     // faderOffset
            0,    // selectOffset
            23,    // muteOffset
            0,    // recordOffset
            0,    // subFaderOffset
            MidiCommandCode.ControlChange, // faderCode
            MidiCommandCode.ControlChange, // selectCode
            MidiCommandCode.ControlChange, // muteCode
            MidiCommandCode.ControlChange, // recordCode
            MidiCommandCode.ControlChange  // subFaderCode
        );

        public FaderDef SecondFiveFaderDef => new FaderDef(
            false, // delta
            127f,   // range
            1,     // channel
            false,  // selectPresent
            true,  // mutePresent
            false,  // recordPresent
            false,  // subfaderPresent
            14,     // faderOffset
            0,    // selectOffset
            24,    // muteOffset
            0,    // recordOffset
            0,    // subfaderOffset
            MidiCommandCode.ControlChange, // faderCode
            MidiCommandCode.ControlChange, // selectCode
            MidiCommandCode.ControlChange, // muteCode
            MidiCommandCode.ControlChange, // recordCode
            MidiCommandCode.ControlChange  // subFaderCode
        );

        public FaderDef KnobFaderDef => new FaderDef(
            false, // delta
            127f,   // range
            1,     // channel
            true,  // selectPresent
            true,  // mutePresent
            false,  // recordPresent
            false,  // subfaderPresent
            1,     // faderOffset
            55,    // selectOffset
            58,    // muteOffset
            0,    // recordOffset
            0,    // subfaderOffset
            MidiCommandCode.ControlChange, // faderCode
            MidiCommandCode.ControlChange, // selectCode
            MidiCommandCode.ControlChange, // muteCode
            MidiCommandCode.ControlChange, // recordCode
            MidiCommandCode.ControlChange  // subFaderCode
        );

        public FaderDef HorizontalFaderDef => new FaderDef(
            false, // delta
            127f,   // range
            1,     // channel
            true,  // selectPresent
            true,  // mutePresent
            true,  // recordPresent
            false, // subfaderPresent
            -1,     // faderOffset
            -8,    // selectOffset
            -9,    // muteOffset
            17,    // recordOffset
            0,     // subfaderOffset
            MidiCommandCode.ControlChange, // faderCode
            MidiCommandCode.ControlChange, // selectCode
            MidiCommandCode.ControlChange, // muteCode
            MidiCommandCode.ControlChange, // recordCode
            MidiCommandCode.ControlChange  // subFaderCode
        );

        public EasyControl(AudioDevice audioDev)
        {
            audioDevices = audioDev;
            FindMidiIn();
            FindMidiOut();

            if (Found)
            {
                ResetAllLights();
                InitFaders();
                InitButtons();
                LoadAssignments();
                ListenForMidi();
            }
        }

        public override void ResetAllLights()
        {
            foreach (var i in Enumerable.Range(0, 128))
                midiOut.Send(new ControlChangeEvent(0, 1, (MidiController)i, 0).GetAsShortMessage());
        }

        public override void SetLight(int controller, bool state)
        {
            midiOut.Send(new ControlChangeEvent(0, 1, (MidiController)(controller), state ? 127 : 0).GetAsShortMessage());
        }

        public override void InitFaders()
        {
            faders = new List<Fader>();

            foreach (var i in Enumerable.Range(0, 11))
            {
                Fader fader = new Fader(this, i, SelectFaderDef(i));
                fader.ResetLights();
                faders.Add(fader);
            }
        }

        public FaderDef SelectFaderDef(int faderNum)
        {
            if (faderNum < 4) return FirstFourFaderDef;
            if (faderNum < 9) return SecondFiveFaderDef;
            if (faderNum == 9) return KnobFaderDef;

            return HorizontalFaderDef;
        }

        public override void InitButtons()
        {
            buttons = new List<Button>();
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaPrevious, 34, true));
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaNext,     35, true));
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaStop,     36, false));
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaPlay,     37, true));
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaRecord,   38, false));
        }
    }
}


================================================
FILE: NK2Tray/Fader.cs
================================================
using NAudio.Midi;
using System;
using System.Collections.Generic;
using System.Linq;

namespace NK2Tray
{
    public class FaderDef
    {
        public bool delta;
        public float faderRange;
        public int channel;
        public bool selectPresent;
        public bool mutePresent;
        public bool recordPresent;
        public bool subFaderPresent;
        public int faderOffset;
        public int selectOffset;
        public int muteOffset;
        public int recordOffset;
        public int subFaderOffset;
        public MidiCommandCode faderCode;
        public MidiCommandCode selectCode;
        public MidiCommandCode muteCode;
        public MidiCommandCode recordCode;
        public MidiCommandCode subFaderCode;
        public int faderChannelOverride;
        public int selectChannelOverride;
        public int muteChannelOverride;
        public int recordChannelOverride;
        public int subFaderChannelOverride;

        public FaderDef(
            bool _delta, float _faderRange, int _channel,
            bool _selectPresent, bool _mutePresent, bool _recordPresent, bool _subFaderPresent,
            int _faderOffset, int _selectOffset, int _muteOffset, int _recordOffset, int _subFaderOffset,
            MidiCommandCode _faderCode, MidiCommandCode _selectCode, MidiCommandCode _muteCode, MidiCommandCode _recordCode, MidiCommandCode _subFaderCode,
            int _faderChannelOverride = -1, int _selectChannelOverride = -1, int _muteChannelOverride = -1, int _recordChannelOverride = -1, int _subFaderChannelOverride = -1)
        {
            delta = _delta;
            faderRange = _faderRange;
            channel = _channel;
            selectPresent = _selectPresent;
            mutePresent = _mutePresent;
            recordPresent = _recordPresent;
            subFaderPresent = _subFaderPresent;
            faderOffset = _faderOffset;
            selectOffset = _selectOffset;
            muteOffset = _muteOffset;
            recordOffset = _recordOffset;
            subFaderOffset = _subFaderOffset;
            faderCode = _faderCode;
            selectCode = _selectCode;
            muteCode = _muteCode;
            recordCode = _recordCode;
            subFaderCode = _subFaderCode;
            faderChannelOverride = _faderChannelOverride;
            selectChannelOverride = _selectChannelOverride;
            muteChannelOverride = _muteChannelOverride;
            recordChannelOverride = _recordChannelOverride;
            subFaderChannelOverride = _subFaderChannelOverride;
        }
    }

    public class Fader
    {
        private bool activeHandling = false;

        private bool selectLight = false;
        private bool muteLight = false;
        private bool recordLight = false;

        public int faderNumber;
        public FaderDef faderDef;
        public MixerSession assignment;
        public bool assigned;
        public MidiOut midiOut;
        public MidiDevice parent;
        public string identifier;
        public string applicationPath;
        public string applicationName;

        public float[] steps;
        public float pow;
        public float faderPositionMultiplier;
        private int primaryFaderHardwareValue;

        public Fader(MidiDevice midiDevice, int faderNum)
        {
            parent = midiDevice;
            midiOut = midiDevice.midiOut;
            faderNumber = faderNum;
            faderDef = parent.DefaultFaderDef;
            SetCurve(1f);
        }

        public Fader(MidiDevice midiDevice, int faderNum, FaderDef _faderDef)
        {
            parent = midiDevice;
            midiOut = midiDevice.midiOut;
            faderNumber = faderNum;
            faderDef = _faderDef;
            SetCurve(1f);
        }

        public void SetCurve(float _pow)
        {
            pow = _pow;
            steps = calculateSteps();
            parent.SetVolumeIndicator(faderNumber, assignment != null ? assignment.GetVolume() : -1);
        }

        private float[] calculateSteps()
        {
            if (!faderDef.delta) return new float[0];

            return Enumerable.Range(0, (int)faderDef.faderRange + 1).Select(stage => getVolumeFromHardwarePositionUsingMultiplier(stage)).ToArray();
        }

        public float getVolumeFromHardwarePositionUsingMultiplier(float position)
        {
            var virtualPosition = position * faderPositionMultiplier;
            return (float)Math.Pow(virtualPosition / faderDef.faderRange, pow);
        }

        public float getVolFromStage(int stage)
        {
            return (float)Math.Pow((double)stage / faderDef.faderRange, pow);
        }

        private int inputController => faderNumber + faderDef.faderOffset;
        private int selectController => faderNumber + faderDef.selectOffset;
        private int muteController => faderNumber + faderDef.muteOffset;
        private int recordController => faderNumber + faderDef.recordOffset;

        public void ResetLights()
        {
            SetSelectLight(false);
            SetMuteLight(false);
            SetRecordLight(false);
        }

        public void Assign(MixerSession mixerSession)
        {
            assigned = true;
            assignment = mixerSession;
            identifier = mixerSession.sessionIdentifier;
            convertToApplicationPath(identifier);
            applicationName = mixerSession.label;
            SetSelectLight(true);
            SetRecordLight(false);
            SetMuteLight(mixerSession.GetMute());

            if (faderDef.delta)
                parent.SetVolumeIndicator(faderNumber, mixerSession.GetVolume());
        }

        public void AssignInactive(string ident)
        {
            identifier = ident;
            convertToApplicationPath(identifier);
            assigned = false;
            SetSelectLight(true);
            SetRecordLight(true);
            SetMuteLight(false);
        }

        public void Unassign()
        {
            assigned = false;
            assignment = null;
            SetSelectLight(false);
            SetRecordLight(false);
            SetMuteLight(false);
            identifier = "";
            if (faderDef.delta)
                parent.SetVolumeIndicator(faderNumber, -1);
        }

        private void convertToApplicationPath(string ident)
        {
            if (
                ident != null
                && ident != ""
                && !ident.StartsWith("#")
                && !ident.Substring(0, Math.Min(10, ident.Length)).Equals("__MASTER__")
                && ident != "__FOCUS__"
            ) // TODO cleaner handling of special fader types
            {
                //"{0.0.0.00000000}.{...}|\\Device\\HarddiskVolume8\\Users\\Dr. Locke\\AppData\\Roaming\\Spotify\\Spotify.exe%b{00000000-0000-0000-0000-000000000000}"
                int deviceIndex = ident.IndexOf("\\Device");
                int endIndex = ident.IndexOf("%b{");
                ident = ident.Substring(deviceIndex, endIndex - deviceIndex);
                applicationPath = DevicePathMapper.FromDevicePath(ident);
            }
        }

        public void SetSelectLight(bool state)
        {
            selectLight = state;
            parent.SetLight(selectController, state);
        }

        public void SetMuteLight(bool state)
        {
            muteLight = state;
            parent.SetLight(muteController, state);
        }

        public void SetRecordLight(bool state)
        {
            recordLight = state;
            parent.SetLight(recordController, state);
        }

        public bool GetSelectLight() { return selectLight; }

        public bool GetMuteLight() { return muteLight; }

        public bool GetRecordLight() { return recordLight; }

        public bool Match(int faderNumber, MidiEvent midiEvent, MidiCommandCode code, int offset, int channel = -1)
        {
            if (channel < 0)
                channel = faderDef.channel;
            if (midiEvent.Channel != channel)
                return false;
            if (midiEvent.CommandCode != code)
                return false;
            if (code == MidiCommandCode.ControlChange)
            {
                var me = (ControlChangeEvent)midiEvent;
                if ((int)me.Controller == faderNumber + offset)
                    return true;
            }
            else if (code == MidiCommandCode.NoteOn)
            {
                var me = (NoteEvent)midiEvent;
                if (me.NoteNumber == faderNumber + offset)
                    return true;
            }
            else if (code == MidiCommandCode.PitchWheelChange)
            {
                return true;
            }

            return false;
        }

        public int GetValue(MidiEvent midiEvent)
        {
            if (midiEvent.CommandCode == MidiCommandCode.ControlChange)
            {
                var me = (ControlChangeEvent)midiEvent;
                return me.ControllerValue;
            }
            else if (midiEvent.CommandCode == MidiCommandCode.NoteOn)
            {
                var me = (NoteEvent)midiEvent;
                return me.Velocity;
            }
            else if (midiEvent.CommandCode == MidiCommandCode.PitchWheelChange)
            {
                var me = (PitchWheelChangeEvent)midiEvent;
                return me.Pitch;
            }

            return 0;
        }

        public List<Fader> GetMatchingFaders()
        {
            MixerSession focusMixerSession = null;

            bool haveFocusSlider = parent.faders.Any(fader => (
                fader.assignment != null
                && fader.assignment.sessionType == SessionType.Focus
            ));

            if (haveFocusSlider)
            {
                int pid = WindowTools.GetForegroundPID();
                focusMixerSession = assignment.devices.FindMixerSession(pid);
            }

            if (assignment == null)
                return parent.faders.FindAll(fader => fader.assignment == null);

            if (assignment.sessionType == SessionType.Master)
            {
                return parent.faders.FindAll(fader =>
                {
                    if (fader == this) return true;
                    if (fader.assignment == null) return false;
                    if (fader.assignment.sessionType != SessionType.Master) return false;

                    return fader.assignment.parentDeviceIdentifier == assignment.parentDeviceIdentifier;
                });
            }

            if (assignment.sessionType == SessionType.Focus)
            {
                return parent.faders.FindAll(fader =>
                {
                    if (fader == this) return true;
                    if (fader.assignment == null) return false;
                    if (fader.assignment.sessionType == SessionType.Focus) return true;

                    return fader.assignment.HasCrossoverProcesses(focusMixerSession);
                });
            }

            if (assignment.sessionType == SessionType.Application)
            {
                return parent.faders.FindAll(fader =>
                {
                    if (fader == this) return true;
                    if (fader.assignment == null) return false;

                    if (fader.assignment.sessionType == SessionType.Application)
                        return fader.assignment.HasCrossoverProcesses(assignment);

                    if (fader.assignment.sessionType == SessionType.Focus)
                        return assignment.HasCrossoverProcesses(focusMixerSession);

                    return false;
                });
            }

            if (assignment.sessionType == SessionType.SystemSounds)
            {
                return parent.faders.FindAll(fader => (
                    fader.assignment != null
                    && fader.assignment.sessionType == SessionType.SystemSounds
                ));
            }

            return new List<Fader>();
        }

        public bool HandleEvent(MidiInMessageEventArgs e)
        {
            if (!IsHandling())
            {
                SetHandling(true);

                //if loaded inactive, search again
                if (!assigned && identifier != null && !identifier.Equals(""))
                {
                    assignment = parent.audioDevices.FindMixerSession(identifier);
                    if (assignment != null)
                    {
                        assigned = true;
                    }
                }

                // Fader match
                if (assigned && Match(faderNumber, e.MidiEvent, faderDef.faderCode, faderDef.faderOffset, faderDef.faderChannelOverride))
                {

                    var hardwareValue = GetValue(e.MidiEvent);
                    primaryFaderHardwareValue = hardwareValue;
                    return SetVolume(hardwareValue);
                }

                // SubFader Match
                if (
                    faderDef.subFaderPresent
                    && assigned
                    && Match(faderNumber, e.MidiEvent, faderDef.subFaderCode, faderDef.subFaderOffset, faderDef.subFaderChannelOverride))
                {

                    this.faderPositionMultiplier = GetValue(e.MidiEvent) / faderDef.faderRange;
                    this.steps = calculateSteps();

                    return SetVolume(primaryFaderHardwareValue);
                }

                // Select match
                if (
                    faderDef.selectPresent
                    && Match(faderNumber, e.MidiEvent, faderDef.selectCode, faderDef.selectOffset, faderDef.selectChannelOverride)
                )
                {
                    if (GetValue(e.MidiEvent) != 127) // Only on button-down
                        return true;

                    Console.WriteLine($@"Attempting to assign current window to fader {faderNumber}");
                    if (assigned)
                    {
                        Unassign();
                        parent.SaveAssignments();
                    }
                    else
                    {
                        var pid = WindowTools.GetForegroundPID();
                        var mixerSession = parent.audioDevices.FindMixerSession(pid);
                        if (mixerSession != null)
                        {
                            Assign(mixerSession);
                            parent.SaveAssignments();
                        }
                        else
                            Console.WriteLine($@"MixerSession not found for pid {pid}");
                    }
                    return true;
                }

                // Mute match
                if (
                    faderDef.mutePresent
                    && assigned
                    && Match(faderNumber, e.MidiEvent, faderDef.muteCode, faderDef.muteOffset, faderDef.muteChannelOverride)
                )
                {
                    if (GetValue(e.MidiEvent) != 127) // Only on button-down
                        return true;

                    var muteStatus = assignment.ToggleMute();
                    SetMuteLight(muteStatus);

                    if (assignment.IsDead())
                    {
                        SetRecordLight(true);

                        return true;
                    }

                    List<Fader> fadersToAffect = GetMatchingFaders();
                    fadersToAffect.ForEach(fader => fader.SetMuteLight(muteStatus));

                    return true;
                }

                // Record match
                if (
                    faderDef.recordPresent
                    && assigned
                    && applicationPath != null
                    && Match(faderNumber, e.MidiEvent, faderDef.recordCode, faderDef.recordOffset, faderDef.recordChannelOverride)
                )
                {
                    if (GetValue(e.MidiEvent) != 127) // Only on button-down
                        return true;

                    if (WindowTools.IsProcessByNameRunning(applicationName))
                        SetRecordLight(false);
                    else
                    {
                        WindowTools.StartApplication(applicationPath);
                    }

                    return true;
                }
            }
            return false;

        }

        /// <summary>
        /// This method sets the volume of the assigned ( if any ) application.
        /// It utilises the "hardwarePosition" passed as a parameter but in sub calls it will
        /// augment that value with the value of "faderPositionMultiplier" which is set elsewhere
        /// </summary>
        /// <param name="hardwarePosition">Position of the primary fader as reported by the MidiDevice</param>
        /// <returns></returns>
        private bool SetVolume(int hardwarePosition)
        {
            if (assignment.sessionType == SessionType.Application)
            {
                MixerSession
                    newAssignment =
                        assignment.devices.FindMixerSession(assignment
                            .sessionIdentifier); //update list for re-routered app, but only overrides
                if (newAssignment == null) //if there is new assignments, otherwise, there is no more a inactive
                {
                    //MixerSession to hold the label
                    SetRecordLight(true);
                    return true;
                }

                assignment = newAssignment;
            }

            float newVol;

            if (faderDef.delta)
            {
                var val = hardwarePosition;
                var volNow = assignment.GetVolume();
                var nearestStep = steps.Select((x, i) => new { Index = i, Distance = Math.Abs(volNow - x) })
                    .OrderBy(x => x.Distance).First().Index;
                int nextStepIndex;
                var volumeGoingDown = val > faderDef.faderRange / 2;

                if (volumeGoingDown)
                    nextStepIndex = Math.Max(nearestStep - 1, 0);
                else
                    nextStepIndex = Math.Min(nearestStep + 1, steps.Length - 1);

                newVol = steps[nextStepIndex];
                assignment.SetVolume(newVol);
            }
            else
            {
                newVol = getVolumeFromHardwarePositionUsingMultiplier(hardwarePosition);
                assignment.SetVolume(newVol);
            }

            if (assignment.IsDead())
            {
                SetRecordLight(true);

                return true;
            }

            List<Fader> fadersToAffect = GetMatchingFaders();
            fadersToAffect.ForEach(fader => fader.parent.SetVolumeIndicator(fader.faderNumber, newVol));

            return true;
        }

        public bool IsHandling()
        {
            return activeHandling;
        }

        public void SetHandling(bool handling)
        {
            activeHandling = handling;
        }
    }
}


================================================
FILE: NK2Tray/FodyWeavers.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <Costura />
</Weavers>

================================================
FILE: NK2Tray/FodyWeavers.xsd
================================================
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
  <xs:element name="Weavers">
    <xs:complexType>
      <xs:all>
        <xs:element name="Costura" minOccurs="0" maxOccurs="1">
          <xs:complexType>
            <xs:all>
              <xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
                <xs:annotation>
                  <xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
                <xs:annotation>
                  <xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
                <xs:annotation>
                  <xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
                <xs:annotation>
                  <xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
                <xs:annotation>
                  <xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
                </xs:annotation>
              </xs:element>
            </xs:all>
            <xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="DisableCompression" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="DisableCleanup" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="LoadAtModuleInit" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
              <xs:annotation>
                <xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="ExcludeAssemblies" type="xs:string">
              <xs:annotation>
                <xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="IncludeAssemblies" type="xs:string">
              <xs:annotation>
                <xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="Unmanaged32Assemblies" type="xs:string">
              <xs:annotation>
                <xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="Unmanaged64Assemblies" type="xs:string">
              <xs:annotation>
                <xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
            <xs:attribute name="PreloadOrder" type="xs:string">
              <xs:annotation>
                <xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
              </xs:annotation>
            </xs:attribute>
          </xs:complexType>
        </xs:element>
      </xs:all>
      <xs:attribute name="VerifyAssembly" type="xs:boolean">
        <xs:annotation>
          <xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
        </xs:annotation>
      </xs:attribute>
      <xs:attribute name="VerifyIgnoreCodes" type="xs:string">
        <xs:annotation>
          <xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
        </xs:annotation>
      </xs:attribute>
      <xs:attribute name="GenerateXsd" type="xs:boolean">
        <xs:annotation>
          <xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
        </xs:annotation>
      </xs:attribute>
    </xs:complexType>
  </xs:element>
</xs:schema>

================================================
FILE: NK2Tray/IconExtractor.cs
================================================
using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace NK2Tray
{
    public class IconExtractor
    {
        public static Icon Extract(string file, int number, bool largeIcon)
        {
            IntPtr large;
            IntPtr small;
            ExtractIconEx(file, number, out large, out small, 1);
            try
            {
                return Icon.FromHandle(largeIcon ? large : small);
            }
            catch
            {
                return null;
            }
        }

        [DllImport("Shell32.dll")]
        private static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons);
    }
}


================================================
FILE: NK2Tray/MediaTools.cs
================================================
using System;
using System.Runtime.InteropServices;

namespace NK2Tray
{
    class MediaTools
    {
        [DllImport("user32.dll", SetLastError = true)]
        public static extern void keybd_event(byte virtualKey, byte scanCode, uint flags, IntPtr extraInfo);

        public const int VK_MEDIA_NEXT_TRACK = 0xB0;
        public const int VK_MEDIA_PLAY_PAUSE = 0xB3;
        public const int VK_MEDIA_PREV_TRACK = 0xB1;
        public const int VK_MEDIA_STOP = 0xB2;
        public const int KEYEVENTF_EXTENDEDKEY = 0x0001; //Key down flag
        public const int KEYEVENTF_KEYUP = 0x0002; //Key up flag

        public static void Play()
        {
            keybd_event(VK_MEDIA_PLAY_PAUSE, 0, KEYEVENTF_EXTENDEDKEY, IntPtr.Zero);
            keybd_event(VK_MEDIA_PLAY_PAUSE, 0, KEYEVENTF_KEYUP, IntPtr.Zero);
        }

        public static void Stop()
        {
            keybd_event(VK_MEDIA_STOP, 0, KEYEVENTF_EXTENDEDKEY, IntPtr.Zero);
            keybd_event(VK_MEDIA_STOP, 0, KEYEVENTF_KEYUP, IntPtr.Zero);
        }

        public static void Next()
        {
            keybd_event(VK_MEDIA_NEXT_TRACK, 0, KEYEVENTF_EXTENDEDKEY, IntPtr.Zero);
            keybd_event(VK_MEDIA_NEXT_TRACK, 0, KEYEVENTF_KEYUP, IntPtr.Zero);
        }

        public static void Previous()
        {
            keybd_event(VK_MEDIA_PREV_TRACK, 0, KEYEVENTF_EXTENDEDKEY, IntPtr.Zero);
            keybd_event(VK_MEDIA_PREV_TRACK, 0, KEYEVENTF_KEYUP, IntPtr.Zero);
        }
    }
}


================================================
FILE: NK2Tray/MidiDevice.cs
================================================
using NAudio.CoreAudioApi;
using NAudio.Midi;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;

namespace NK2Tray
{
    public enum SendEvent
    {
        AssignedState,
        MuteState,
        ErrorState,
        MediaPlay,
        MediaStop,
        MediaPrevious,
        MediaNext,
        MediaRecord
    }

    public class MidiDevice
    {
        public MidiIn midiIn;
        public MidiOut midiOut;

        public List<Fader> faders;
        public List<Button> buttons;
        public Hashtable buttonsMappingTable;

        public AudioDevice audioDevices;

        public virtual string SearchString => "wobbo";

        public virtual FaderDef DefaultFaderDef =>
            new FaderDef(
                false,
                1f,
                1,
                true,
                true,
                true,
                true,
                0,
                0,
                0,
                0,
                0,
                MidiCommandCode.ControlChange,
                MidiCommandCode.ControlChange,
                MidiCommandCode.ControlChange,
                MidiCommandCode.ControlChange,
                MidiCommandCode.ControlChange);

        public MidiDevice()
        {
            Console.WriteLine($@"Initializing Midi Device {SearchString}");
        }

        public bool Found => (midiIn != null && midiOut != null);

        public void FindMidiIn()
        {
            for (int i = 0; i < MidiIn.NumberOfDevices; i++)
            {
                Console.WriteLine("MIDI IN: " + MidiIn.DeviceInfo(i).ProductName);
                if (MidiIn.DeviceInfo(i).ProductName.ToLower().Contains(SearchString))
                {
                    midiIn = new MidiIn(i);
                    Console.WriteLine($@"Assigning MidiIn: {MidiIn.DeviceInfo(i).ProductName}");
                    break;
                }
            }
        }

        public void FindMidiOut()
        {
            for (int i = 0; i < MidiOut.NumberOfDevices; i++)
            {
                Console.WriteLine("MIDI OUT: " + MidiOut.DeviceInfo(i).ProductName);
                if (MidiOut.DeviceInfo(i).ProductName.ToLower().Contains(SearchString))
                {
                    midiOut = new MidiOut(i);
                    Console.WriteLine($@"Assigning MidiOut: {MidiOut.DeviceInfo(i).ProductName}");
                    break;
                }
            }
        }

        public void ListenForMidi()
        {
            midiIn.MessageReceived += midiIn_MessageReceived;
            midiIn.ErrorReceived += midiIn_ErrorReceived;
            midiIn.Start();
        }

        public void midiIn_ErrorReceived(object sender, MidiInMessageEventArgs e)
        {
            Console.WriteLine(String.Format("Time {0} Message 0x{1:X8} Event {2}",
                e.Timestamp, e.RawMessage, e.MidiEvent));
        }

        public virtual void midiIn_MessageReceived(object sender, MidiInMessageEventArgs e)
        {
            //WindowTools.Dump(e.MidiEvent);

            foreach (var fader in faders)
            {
                fader.HandleEvent(e);
                fader.SetHandling(false);
            }

            //ControlChangeEvent midiController = null;
            //
            //try
            //{
            //    midiController = (ControlChangeEvent)e.MidiEvent;
            //}
            //catch (System.InvalidCastException exc)
            //{
            //    return;
            //}
            //
            //if (midiController == null)
            //    return;
            ////key UP...!
            //if (midiController.ControllerValue == 0)
            //    return;
            //
            //var obj = buttonsMappingTable[(int)midiController.Controller];
            //if (obj != null)
            //{
            //    Button button = (Button)obj;
            //    button.HandleEvent(e, this);
            //    button.SetHandling(false);
            //}
            //else
            {
                foreach (var button in buttons)
                {
                    button.HandleEvent(e, this);
                    button.SetHandling(false);
                }
            }
        }

        public virtual void ResetAllLights() { }

        public virtual void LightShow() { }

        public virtual void SetVolumeIndicator(int fader, float level) { }

        public virtual void SetLight(int controller, bool state) {}

        public virtual void InitFaders()
        {
            faders = new List<Fader>();
        }

        public virtual void InitButtons()
        {
            buttons = new List<Button>();
        }

        public void SetCurve(float pow)
        {
            faders.ForEach(fader => fader.SetCurve(pow));
        }

        public void LoadAssignments()
        {
            bool foundAssignments = false;

            foreach (var fader in faders)
            {
                Console.WriteLine("Getting setting: " + fader.faderNumber.ToString());
                var ident = ConfigSaver.GetAppSettings(fader.faderNumber.ToString());

                Console.WriteLine("Got setting: " + ident);
                if (ident != null)
                {
                    if (ident.Equals("__FOCUS__"))
                    {
                        foundAssignments = true;
                        fader.Assign(new MixerSession("", audioDevices, "Focus", SessionType.Focus));
                    }
                    else if (ident.Equals("__MASTER__") || (ident.Substring(0, Math.Min(10, ident.Length)).Equals("__MASTER__")))
                    {
                        foundAssignments = true;
                        MMDevice mmDevice = audioDevices.GetDeviceByIdentifier(ident.IndexOf("|") >= 0 ? ident.Substring(ident.IndexOf("|")+1) : "");
                        fader.Assign(new MixerSession(mmDevice.ID, audioDevices, "Master", SessionType.Master));
                    }                    
                    else if (ident.Length > 0)
                    {
                        foundAssignments = true;
                        var matchingSession = audioDevices.FindMixerSession(ident);
                        if (matchingSession != null)
                            fader.Assign(matchingSession);
                        else
                            fader.AssignInactive(ident);
                    }
                    else
                    {
                        fader.Unassign();
                    }
                }

                var savedSubFaderPosition = ConfigSaver.GetAppSettings(fader.faderNumber.ToString() + "m");
                fader.faderPositionMultiplier = savedSubFaderPosition != null ? float.Parse(savedSubFaderPosition) : 1;
            }            
            
            // Load fader 8 as master volume control as default if no faders are set
            if (!foundAssignments)
            {
                if (faders.Count > 0)
                {
                    faders.Last().Assign(new MixerSession(audioDevices.GetDeviceByIdentifier("").ID, audioDevices, "Master", SessionType.Master));
                    SaveAssignments();
                }
            }
            
        }

        public void SaveAssignments()
        {
            Console.WriteLine("Saving Assignments");
            foreach (var fader in faders)
            {
                if (fader.assigned)
                {
                    if (fader.assignment.sessionType == SessionType.Master)
                        ConfigSaver.AddOrUpdateAppSettings(fader.faderNumber.ToString(), "__MASTER__|" + fader.assignment.parentDeviceIdentifier );
                    else if (fader.assignment.sessionType == SessionType.Focus)
                        ConfigSaver.AddOrUpdateAppSettings(fader.faderNumber.ToString(), "__FOCUS__");
                    else
                        ConfigSaver.AddOrUpdateAppSettings(fader.faderNumber.ToString(), fader.assignment.sessionIdentifier);
                }
                else
                {
                    ConfigSaver.AddOrUpdateAppSettings(fader.faderNumber.ToString(), "");
                }

                ConfigSaver.AddOrUpdateAppSettings(fader.faderNumber.ToString() + "m", fader.faderPositionMultiplier.ToString());
            }
        }

    }
}


================================================
FILE: NK2Tray/MixerSession.cs
================================================
using NAudio.CoreAudioApi;
using NAudio.CoreAudioApi.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;

namespace NK2Tray
{
    public enum SessionType
    {
        Application,
        SystemSounds,
        Master,
        Focus
    }

    /// <summary>
    /// A <c>MixerSession</c> represents a running application and all audio sessions belonging to it.
    /// </summary>
    public class MixerSession
    {
        public string label;
        public string sessionIdentifier;
        public List<AudioSessionControl> audioSessions;
        public SessionType sessionType;
        public AudioDevice devices;
        public String parentDeviceIdentifier;

        public MixerSession(AudioDevice devices, string labl, string identifier, List<AudioSessionControl> sessions, SessionType sesType)
        {
            this.devices = devices;
   
            sessionIdentifier = identifier;
            audioSessions = sessions;
            sessionType = sesType;
            label = labl;
        }

        public MixerSession(String deviceIdentifier, AudioDevice devices, string labl, SessionType sesType)
        {
            this.devices = devices;
            
            sessionIdentifier = "";
            audioSessions = new List<AudioSessionControl>();
            sessionType = sesType;
            parentDeviceIdentifier = deviceIdentifier;

            if (sesType == SessionType.Focus)
                label = "Focus";
            else
                label = devices.GetDeviceByIdentifier(deviceIdentifier).FriendlyName + ": " + labl;
        }

        public bool IsDead()
        {
            bool alive = false;
            if (sessionType == SessionType.Application)
            {
                foreach (var session in audioSessions)
                    if (session.State != AudioSessionState.AudioSessionStateExpired)
                        alive = true;
            }
            else
                alive = true;

            return !alive;
        }

        public void SetVolume(float volume)
        {
            if (sessionType == SessionType.Application)
            {
                foreach (var session in audioSessions)
                    session.SimpleAudioVolume.Volume = volume;
            }
            else if (sessionType == SessionType.Master)
            {
                try
                {
                    devices.GetDeviceByIdentifier(parentDeviceIdentifier).AudioEndpointVolume.MasterVolumeLevelScalar = volume;                                   
                }
                catch (System.Runtime.InteropServices.InvalidComObjectException e)
                {
                    // This catch handles the "COM object that has been separated from its underlying RCW cannot be used" issue.
                    // I believe this happens when we refresh the device when opening the menu, but this is fine for now.
                    Console.WriteLine($@"Error when setting master volume: {e.Message}");
                    AudioEndpointVolume deviceVolume = devices.GetDeviceByIdentifier(parentDeviceIdentifier).AudioEndpointVolume;
                    deviceVolume.MasterVolumeLevelScalar = volume;
                    Console.WriteLine($@"RETRY: Setting master volume to {volume}");
                }
                catch (System.Runtime.InteropServices.COMException e)
                {
                    //TODO find out where this exception comes from and actually fix it
                    Console.WriteLine("COM Execption" + e);
                }
                catch (System.InvalidCastException e)
                {                    
                    Console.WriteLine("InvalidCastException Exeception" + e);
                }
            } 
            else if (sessionType == SessionType.Focus)
            {
                var pid = WindowTools.GetForegroundPID();
                var mixerSession = devices.FindMixerSession(pid);
                // Check if null since mixer session might not exist for currently focused window
                if (mixerSession != null)
                {
                    foreach (var session in mixerSession.audioSessions)
                    {
                        session.SimpleAudioVolume.Volume = volume;
                    }
                }
            }
        }

        public float GetVolume()
        {
            if (sessionType == SessionType.Application)
            {
                foreach (var session in audioSessions)
                {
                    var curVol = session.SimpleAudioVolume.Volume;
                    if (curVol < 0)
                        curVol = 0;
                    if (curVol > 1)
                        curVol = 1;
                    return curVol;
                }
            }
            else if (sessionType == SessionType.Master)
            {
                try
                {
                    var curVol = devices.GetDeviceByIdentifier(parentDeviceIdentifier).AudioEndpointVolume.MasterVolumeLevelScalar;
                    if (curVol < 0)
                        curVol = 0;
                    if (curVol > 1)
                        curVol = 1;
                    return curVol;
                }
                catch (System.Runtime.InteropServices.InvalidComObjectException e)
                {
                    // This catch handles the "COM object that has been separated from its underlying RCW cannot be used" issue.
                    // I believe this happens when we refresh the device when opening the menu, but this is fine for now.
                    Console.WriteLine($@"Error when getting master volume: {e.Message}");
                    var curVol = devices.GetDeviceByIdentifier(parentDeviceIdentifier).AudioEndpointVolume.MasterVolumeLevelScalar;
                    if (curVol < 0)
                        curVol = 0;
                    if (curVol > 1)
                        curVol = 1;
                    return curVol;
                }
            }
            else if (sessionType == SessionType.Focus)
            {
                var pid = WindowTools.GetForegroundPID();
                var mixerSession = devices.FindMixerSession(pid);
                if (mixerSession != null)
                {
                    foreach (var session in mixerSession.audioSessions)
                    {
                        var curVol = session.SimpleAudioVolume.Volume;
                        if (curVol < 0)
                            curVol = 0;
                        if (curVol > 1)
                            curVol = 1;
                        return curVol;
                    }
                }
            }
            return -1;
        }

        public float ChangeVolume(float change)
        {
            float retVol = -1;
            if (sessionType == SessionType.Application)
            {
                foreach (var session in audioSessions)
                {
                    if (retVol < 0)
                    {
                        var curVol = session.SimpleAudioVolume.Volume;
                        curVol += change;
                        if (curVol < 0)
                            curVol = 0;
                        if (curVol > 1)
                            curVol = 1;
                        session.SimpleAudioVolume.Volume = curVol;
                        retVol = curVol;
                    }
                    {
                        session.SimpleAudioVolume.Volume = retVol;
                    }
                }
            }
            else if (sessionType == SessionType.Master)
            {
                try
                {
                    var curVol = devices.GetDeviceByIdentifier(parentDeviceIdentifier).AudioEndpointVolume.MasterVolumeLevelScalar;
                    curVol += change;
                    if (curVol < 0)
                        curVol = 0;
                    if (curVol > 1)
                        curVol = 1;
                    devices.GetDeviceByIdentifier(parentDeviceIdentifier).AudioEndpointVolume.MasterVolumeLevelScalar = curVol;
                    retVol = curVol;
                }
                catch (System.Runtime.InteropServices.InvalidComObjectException e)
                {
                    // This catch handles the "COM object that has been separated from its underlying RCW cannot be used" issue.
                    // I believe this happens when we refresh the device when opening the menu, but this is fine for now.
                    Console.WriteLine($@"Error when setting master volume: {e.Message}");
                    AudioEndpointVolume deviceVolume = devices.GetDeviceByIdentifier(parentDeviceIdentifier).AudioEndpointVolume;
                    var curVol = deviceVolume.MasterVolumeLevelScalar;
                    curVol += change;
                    if (curVol < 0)
                        curVol = 0;
                    if (curVol > 1)
                        curVol = 1;
                    deviceVolume.MasterVolumeLevelScalar = curVol;
                    Console.WriteLine($@"RETRY: Setting master volume by {change}");
                }

            }
            else if (sessionType == SessionType.Focus)
            {
                var pid = WindowTools.GetForegroundPID();
                var mixerSession = devices.FindMixerSession(pid);
                if( mixerSession != null)
                {
                    foreach (var session in mixerSession.audioSessions)
                    {
                        var curVol = session.SimpleAudioVolume.Volume;
                        curVol += change;
                        if (curVol < 0)
                            curVol = 0;
                        if (curVol > 1)
                            curVol = 1;
                        session.SimpleAudioVolume.Volume = curVol;
                        retVol = curVol;
                    }
                }
            }
            return retVol;
        }

        public bool ToggleMute()
        {
            if (sessionType == SessionType.Application)
            {
                var muted = !audioSessions.First().SimpleAudioVolume.Mute;
                foreach (var session in audioSessions)
                    session.SimpleAudioVolume.Mute = muted;
                return muted;
            }
            else if (sessionType == SessionType.Master)
            {
                try
                {
                    var muted = !devices.GetDeviceByIdentifier(parentDeviceIdentifier).AudioEndpointVolume.Mute;
                    devices.GetDeviceByIdentifier(parentDeviceIdentifier).AudioEndpointVolume.Mute = muted;
                    return muted;
                }
                catch (System.Runtime.InteropServices.InvalidComObjectException e)
                {
                    // This catch handles the "COM object that has been separated from its underlying RCW cannot be used" issue.
                    // I believe this happens when we refresh the device when opening the menu, but this is fine for now.
                    Console.WriteLine($@"Error when toggling mute on master volume: {e.Message}");
                    AudioEndpointVolume deviceVolume = devices.GetDeviceByIdentifier(parentDeviceIdentifier).AudioEndpointVolume;
                    var muted = !deviceVolume.Mute;
                    deviceVolume.Mute = muted;

                    Console.WriteLine("RETRY: toggling mute on master volume");
                    return muted;
                }
            }
            else if (sessionType == SessionType.Focus)
            {
                var pid = WindowTools.GetForegroundPID();
                var mixerSession = devices.FindMixerSession(pid);
                if (mixerSession != null)
                {
                    var muted = !mixerSession.audioSessions.First().SimpleAudioVolume.Mute;
                    foreach (var session in mixerSession.audioSessions)
                        session.SimpleAudioVolume.Mute = muted;
                    return muted;
                }
            }
            return false;
        }

        public bool GetMute()
        {
            if (sessionType == SessionType.Master)
                return devices.GetDeviceByIdentifier(parentDeviceIdentifier).AudioEndpointVolume.Mute;

            var targetAudioSessions = audioSessions;

            if (sessionType == SessionType.Focus)
            {
                var pid = WindowTools.GetForegroundPID();
                var mixerSession = devices.FindMixerSession(pid);
                if (mixerSession == null) return false;
                targetAudioSessions = mixerSession.audioSessions;
            }

            return targetAudioSessions.First().SimpleAudioVolume.Mute;
        }

        public bool HasCrossoverProcesses(MixerSession other)
        {
            if (other == null) return false;

            return audioSessions.Any(session =>
                other.audioSessions.Any(otherSession =>
                    session.GetProcessID == otherSession.GetProcessID
                )
            );
        }
    }
}


================================================
FILE: NK2Tray/NK2Tray.csproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="..\packages\Costura.Fody.3.3.0\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.3.3.0\build\Costura.Fody.props')" />
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{F88D8F42-CC15-40AE-9301-043313764C4C}</ProjectGuid>
    <OutputType>WinExe</OutputType>
    <RootNamespace>NK2Tray</RootNamespace>
    <AssemblyName>NK2Tray</AssemblyName>
    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <Deterministic>true</Deterministic>
    <NuGetPackageImportStamp>
    </NuGetPackageImportStamp>
    <PublishUrl>publish\</PublishUrl>
    <Install>true</Install>
    <InstallFrom>Disk</InstallFrom>
    <UpdateEnabled>false</UpdateEnabled>
    <UpdateMode>Foreground</UpdateMode>
    <UpdateInterval>7</UpdateInterval>
    <UpdateIntervalUnits>Days</UpdateIntervalUnits>
    <UpdatePeriodically>false</UpdatePeriodically>
    <UpdateRequired>false</UpdateRequired>
    <MapFileExtensions>true</MapFileExtensions>
    <ApplicationRevision>0</ApplicationRevision>
    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
    <IsWebBootstrapper>false</IsWebBootstrapper>
    <UseApplicationTrust>false</UseApplicationTrust>
    <BootstrapperEnabled>true</BootstrapperEnabled>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup>
    <ApplicationIcon>nk2tray.ico</ApplicationIcon>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Costura, Version=3.3.0.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
      <HintPath>..\packages\Costura.Fody.3.3.0\lib\net40\Costura.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="NAudio, Version=1.8.5.0, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\packages\NAudio.1.8.5\lib\net35\NAudio.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Deployment" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Windows.Forms" />
    <Reference Include="System.Xml" />
    <Reference Include="WindowsBase" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="AudioDevice.cs" />
    <Compile Include="Button.cs" />
    <Compile Include="ConfigSaver.cs" />
    <Compile Include="DevicePathMapper.cs" />
    <Compile Include="EasyControl.cs" />
    <Compile Include="Fader.cs" />
    <Compile Include="IconExtractor.cs" />
    <Compile Include="MediaTools.cs" />
    <Compile Include="NanoKontrol2.cs" />
    <Compile Include="MidiDevice.cs" />
    <Compile Include="MixerSession.cs" />
    <Compile Include="OP1.cs" />
    <Compile Include="Program.cs">
      <SubType>Form</SubType>
    </Compile>
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="WindowTools.cs" />
    <Compile Include="XtouchMini.cs" />
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
      <SubType>Designer</SubType>
    </EmbeddedResource>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Resources.resx</DependentUpon>
      <DesignTime>True</DesignTime>
    </Compile>
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config" />
  </ItemGroup>
  <ItemGroup>
    <EmbeddedResource Include="nk2tray.ico" />
  </ItemGroup>
  <ItemGroup>
    <BootstrapperPackage Include=".NETFramework,Version=v4.6.1">
      <Visible>False</Visible>
      <ProductName>Microsoft .NET Framework 4.6.1 %28x86 und x64%29</ProductName>
      <Install>true</Install>
    </BootstrapperPackage>
    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
      <Visible>False</Visible>
      <ProductName>.NET Framework 3.5 SP1</ProductName>
      <Install>false</Install>
    </BootstrapperPackage>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="..\packages\Fody.3.3.5\build\Fody.targets" Condition="Exists('..\packages\Fody.3.3.5\build\Fody.targets')" />
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('..\packages\Fody.3.3.5\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.3.3.5\build\Fody.targets'))" />
    <Error Condition="!Exists('..\packages\Costura.Fody.3.3.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.3.3.0\build\Costura.Fody.props'))" />
  </Target>
</Project>

================================================
FILE: NK2Tray/NanoKontrol2.cs
================================================
using NAudio.Midi;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace NK2Tray
{
    public class NanoKontrol2 : MidiDevice
    {
        public override string SearchString => "nano";
        public override FaderDef DefaultFaderDef => new FaderDef(
            false, // delta
            127f,   // range
            1,     // channel
            true,  // selectPresent
            true,  // mutePresent
            true,  // recordPresent
            true,  // subFaderPresent
            0,     // faderOffset
            32,    // selectOffset
            48,    // muteOffset
            64,    // recordOffset
            16,    // subFaderOffset
            MidiCommandCode.ControlChange, // faderCode
            MidiCommandCode.ControlChange, // selectCode
            MidiCommandCode.ControlChange, // muteCode
            MidiCommandCode.ControlChange, // recordCode
            MidiCommandCode.ControlChange  // subFaderCode
        );

        public NanoKontrol2(AudioDevice audioDev)
        {
            audioDevices = audioDev;
            FindMidiIn();
            FindMidiOut();
            if (Found)
            {
                ResetAllLights();
                InitFaders();
                InitButtons();
                LightShow();
                LoadAssignments();
                ListenForMidi();
            }
        }

        public override void ResetAllLights()
        {
            foreach (var i in Enumerable.Range(0, 128))
                midiOut.Send(new ControlChangeEvent(0, 1, (MidiController)i, 0).GetAsShortMessage());
        }

        public override void SetLight(int controller, bool state)
        {
            midiOut.Send(new ControlChangeEvent(0, 1, (MidiController)(controller), state ? 127 : 0).GetAsShortMessage());
        }

        public override void InitFaders()
        {
            faders = new List<Fader>();
            foreach (var i in Enumerable.Range(0, 8))
            {
                Fader fader = new Fader(this, i);
                fader.ResetLights();
                faders.Add(fader);
            }
        }

        public override void InitButtons()
        {
            buttons = new List<Button>();
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaPrevious, 43, true));
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaNext,     44, true));
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaStop,     42, false));
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaPlay,     41, true));
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaRecord,   45, false));

            buttonsMappingTable = new Hashtable();
            foreach (var button in buttons)
            {
                buttonsMappingTable.Add(button.controller, button);
            }
        }

        public override void LightShow()
        {
            List<LightShowBackup> lightBackups = new List<LightShowBackup>();

            //backup settings and turn them all off
            foreach (var fader in faders)
            {
                lightBackups.Add(new LightShowBackup(fader));
                fader.SetSelectLight(false);
                fader.SetSelectLight(false);
                fader.SetMuteLight(false);
                fader.SetRecordLight(false);
            }
            foreach (var button in buttons)
            {
                lightBackups.Add(new LightShowBackup(button));
                button.SetLight(false);
            }

            //do light show
            int travelSpeed = 50;
            foreach (var button in buttons)
            {
                button.SetLight(true);
                System.Threading.Thread.Sleep(travelSpeed);
                button.SetLight(false);
            }
            foreach (var fader in faders)
            {
                fader.SetSelectLight(true);
                fader.SetMuteLight(true);
                fader.SetRecordLight(true);
                System.Threading.Thread.Sleep(travelSpeed);
                fader.SetSelectLight(false);
                fader.SetMuteLight(false);
                fader.SetRecordLight(false);
            }
            //reverse
            for (int i = faders.Count - 1; i >= 0; i--)
            {
                var fader = faders[i];
                fader.SetSelectLight(true);
                fader.SetMuteLight(true);
                fader.SetRecordLight(true);
                System.Threading.Thread.Sleep(travelSpeed);
            }
            for (int i = buttons.Count - 1; i >= 0; i--)
            {
                var button = buttons[i];
                button.SetLight(true);
                System.Threading.Thread.Sleep(travelSpeed);
            }
            //flash
            /*
            for (int i = 2; i > 0; i--)
            {
                System.Threading.Thread.Sleep(travelSpeed * 2);

                if (i != 3)
                {
                    foreach (var button in buttons)
                    {
                        button.SetLight(true);
                    }
                    foreach (var fader in faders)
                    {
                        fader.SetSelectLight(true);
                        fader.SetMuteLight(true);
                        fader.SetRecordLight(true);
                    }
                }

                System.Threading.Thread.Sleep(travelSpeed * 2);

                foreach (var button in buttons)
                {
                    button.SetLight(false);
                }
                foreach (var fader in faders)
                {
                    fader.SetSelectLight(false);
                    fader.SetMuteLight(false);
                    fader.SetRecordLight(false);
                }
            }

            foreach (var button in buttons)
            {
                button.SetLight(true);
            }
            */

            //reset settings
            foreach (var lightBackup in lightBackups)
            {
                lightBackup.reset();
            }

        }

        private class LightShowBackup
        {
            private bool[] lightSettings;
            private Fader fader = null;
            private Button button = null;

            public LightShowBackup(Fader inFader)
            {
                lightSettings = new bool[3];
                lightSettings[0] = inFader.GetSelectLight();
                lightSettings[1] = inFader.GetMuteLight();
                lightSettings[2] = inFader.GetRecordLight();
                fader = inFader;
            }

            public LightShowBackup(Button inButton)
            {
                lightSettings = new bool[1];
                lightSettings[0] = inButton.GetLight();
                button = inButton;
            }

            public void reset()
            {
                if (fader != null)
                {
                    fader.SetSelectLight(lightSettings[0]);
                    fader.SetMuteLight(lightSettings[1]);
                    fader.SetRecordLight(lightSettings[2]);
                }
                else if (button != null)
                {
                    button.SetLight(lightSettings[0]);
                }
            }
        }
    }
}


================================================
FILE: NK2Tray/OP1.cs
================================================
using NAudio.Midi;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace NK2Tray
{
    public class OP1 : MidiDevice
    {
        public override string SearchString => "op-1";

        public FaderDef FirstFader => new FaderDef(
            false,   // delta
            127f,    // range
            1,      // channel
            true,   // selectPresent
            true,   // mutePresent
            true,  // recordPresent
            false, // subFaderPresent
            1,      // faderOffset
            64,     // selectOffset
            50,     // muteOffset
            51,     // recordOffset
            0,      // subFaderOffset
            MidiCommandCode.ControlChange,  // faderCode
            MidiCommandCode.ControlChange,  // selectCode
            MidiCommandCode.ControlChange,  // muteCode
            MidiCommandCode.ControlChange,  // recordCode
            MidiCommandCode.ControlChange   // subFaderCode
        );

        public FaderDef SecondFader => new FaderDef(
            false,   // delta
            127f,    // range
            1,      // channel
            true,   // selectPresent
            true,   // mutePresent
            true,  // recordPresent
            false, // subFaderPresent
            1,      // faderOffset
            64,     // selectOffset
            51,     // muteOffset
            20,     // recordOffset
            0,      // subFaderOffset
            MidiCommandCode.ControlChange,  // faderCode
            MidiCommandCode.ControlChange,  // selectCode
            MidiCommandCode.ControlChange,  // muteCode
            MidiCommandCode.ControlChange,  // recordCode
            MidiCommandCode.ControlChange   // subFaderCode
        );

        public FaderDef ThirdFader => new FaderDef(
            false,   // delta
            127f,    // range
            1,      // channel
            true,   // selectPresent
            true,   // mutePresent
            true,  // recordPresent
            false, // subFaderPresent
            1,      // faderOffset
            64,     // selectOffset
            20,     // muteOffset
            21,     // recordOffset
            0,      // subFaderOffset
            MidiCommandCode.ControlChange,  // faderCode
            MidiCommandCode.ControlChange,  // selectCode
            MidiCommandCode.ControlChange,  // muteCode
            MidiCommandCode.ControlChange,  // recordCode
            MidiCommandCode.ControlChange   // subFaderCode
        );

        public FaderDef FourthFader => new FaderDef(
            false,   // delta
            127f,    // range
            1,      // channel
            true,   // selectPresent
            true,   // mutePresent
            true,  // recordPresent
            false,  // subFaderPresent
            1,      // faderOffset
            64,     // selectOffset
            21,     // muteOffset
            22,     // recordOffset
            0,      // subFaderOffset
            MidiCommandCode.ControlChange,  // faderCode
            MidiCommandCode.ControlChange,  // selectCode
            MidiCommandCode.ControlChange,  // muteCode
            MidiCommandCode.ControlChange,  // recordCode
            MidiCommandCode.ControlChange   // subFaderCode
        );

        public OP1(AudioDevice audioDev)
        {
            audioDevices = audioDev;
            FindMidiIn();
            FindMidiOut();

            if (Found)
            {
                InitFaders();
                InitButtons();
                LoadAssignments();
                ListenForMidi();
            }
        }

        public override void InitFaders()
        {
            faders = new List<Fader>
            {
                new Fader(this, 0, FirstFader),
                new Fader(this, 1, SecondFader),
                new Fader(this, 2, ThirdFader),
                new Fader(this, 3, FourthFader)
            };
        }

        public override void InitButtons()
        {
            buttons = new List<Button>
            {
                new Button(ref midiOut, ButtonType.MediaPlay, 39, true),
                new Button(ref midiOut, ButtonType.MediaStop, 40, false),
                new Button(ref midiOut, ButtonType.MediaNext, 16, true),
                new Button(ref midiOut, ButtonType.MediaPrevious, 15, true)
            };

            // is this still needed?
            buttonsMappingTable = new Hashtable();

            foreach (var button in buttons)
            {
                buttonsMappingTable.Add(button.controller, button);
            }
        }
    }
}


================================================
FILE: NK2Tray/Program.cs
================================================
using NAudio.CoreAudioApi;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Threading;
using NAudio;

namespace NK2Tray
{
    public class SysTrayApp : Form
    {
        [STAThread]
        public static void Main() => Application.Run(new SysTrayApp());

        private NotifyIcon trayIcon;
        public MidiDevice midiDevice;
        public AudioDevice audioDevices;
        public bool logarithmic;

        private Dispatcher _workerDispatcher;
        private Thread _workerThread;

        public SysTrayApp()
        {
            System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
            Console.WriteLine($@"NK2 Tray {DateTime.Now}");

            // Set up a worker thread to always run SetupDevice and PopUp inside of
            _workerThread = new Thread(new ThreadStart(() =>
            {
                _workerDispatcher = Dispatcher.CurrentDispatcher;
                Dispatcher.Run();
            }));

            _workerThread.Start();

            trayIcon = new NotifyIcon
            {
                Text = "NK2 Tray",
                Icon = new Icon(Properties.Resources.nk2tray, 40, 40),
                ContextMenu = new ContextMenu()
            };

            trayIcon.ContextMenu.Popup += (object sender, EventArgs e) =>
                _workerDispatcher.Invoke(() => OnPopup(sender, e));

            trayIcon.Visible = true; 
            
            while (_workerDispatcher == null)
            {

                /*
                 * weird threading situation.
                 * _workerDispatcher = Dispatcher.CurrentDispatcher; and Dispatcher.Run(); must be set in another thread otherwise the gui doesn't work or the program doesn't exit cleanly.
                 * but on faster machines we race to Invoke(SetupDevice); before the other thread has instantiated the _workerDispatcher
                 * easy way to solve is to just wait here until _workerDispatcher is set 
                 */

                Thread.Sleep(10);
            }

            _workerDispatcher.Invoke(SetupDevice);
        }

        private Boolean SetupDevice()
        {
            audioDevices = new AudioDevice();

            midiDevice = new NanoKontrol2(audioDevices);

            if (!midiDevice.Found)
                midiDevice = new XtouchMini(audioDevices);

            if (!midiDevice.Found)
                midiDevice = new OP1(audioDevices);

             if (!midiDevice.Found)
               midiDevice = new EasyControl(audioDevices);


            audioDevices.midiDevice = midiDevice;

            logarithmic = System.Convert.ToBoolean(ConfigSaver.GetAppSettings("logarithmic"));
            SaveLogarithmic();

            return midiDevice.Found;
        }

        private void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e)
        {
            Console.WriteLine(e.ExceptionObject.ToString());
            MessageBox.Show(e.ExceptionObject.ToString(), "NK2 Tray Error", MessageBoxButtons.OK);

            if (midiDevice != null)
            {
                try
                {
                    midiDevice.ResetAllLights();
                    midiDevice.faders.Last().SetRecordLight(true);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
            Application.Exit();
        }

        private String getProgramLabel(Fader fader)
        {
            if (fader.assigned)
                return fader.assignment.label;
            
            if(fader.identifier != null && fader.identifier.Length > 0 && fader.identifier.Contains(".exe"))
            {
                String identifier = fader.identifier;
                int lastBackSlash = identifier.LastIndexOf('\\') + 1;
                int programNameLength = identifier.IndexOf(".exe") - lastBackSlash;
                String progamName = identifier.Substring(lastBackSlash, programNameLength);
                return progamName;
            }
            return "";
        }

        private void OnPopup(object sender, EventArgs e)
        {
            ContextMenu trayMenu = (ContextMenu)sender;

            // Clean old menu items out
            var oldMenuItems = trayMenu.MenuItems.Cast<MenuItem>().ToArray();
            foreach (var item in oldMenuItems)
                item.Dispose();

            trayMenu.MenuItems.Clear();

            var mixerSessions = audioDevices.GetCachedMixerSessions();

            var masterMixerSessionList = new List<MixerSession>();
            foreach(MMDevice mmDevice in audioDevices.outputDevices)
            {
                masterMixerSessionList.Add(new MixerSession(mmDevice.ID, audioDevices, "Master", SessionType.Master));
            }

            /*
            // Commented out Iput device code because it breaks the session lists when input and ouput is controlled at the same time
            var micMixerSessionList = new List<MixerSession>();
            foreach (MMDevice mmDevice in audioDevices.inputDevices)
            {
                micMixerSessionList.Add(new MixerSession(mmDevice.ID, audioDevices, "Microphone", SessionType.Master));
            }
            */

            MixerSession focusMixerSession;
            focusMixerSession = new MixerSession("", audioDevices, "Focus", SessionType.Focus);
                        
            // Dont create context menu if no midi device is connected
            if(!midiDevice.Found)
            {
                if (!SetupDevice()) // This setup call can be removed once proper lifecycle management is implemented, for now this also adds a nice way to reconnect the controller
                {
                    MessageBox.Show("No midi device detected. Are you sure your device is plugged in correctly?");
                    return;
                }
            }
                       
            foreach (var fader in midiDevice.faders)
            {
                MenuItem faderMenu = new MenuItem($@"Fader {fader.faderNumber + 1} - " + getProgramLabel(fader));
                trayMenu.MenuItems.Add(faderMenu);

                // Add master mixerSession to menu
                foreach(MixerSession mixerSession in masterMixerSessionList)
                {
                    MenuItem masterItem = new MenuItem(mixerSession.label, AssignFader);
                    masterItem.Tag = new object[] { fader, mixerSession };
                    faderMenu.MenuItems.Add(masterItem);
                }

                /* 
                // Commented out Iput device code because it breaks the session lists when input and ouput is controlled at the same time
                faderMenu.MenuItems.Add("-");

                foreach (MixerSession mixerSession in micMixerSessionList)
                {
                    MenuItem micItem = new MenuItem(mixerSession.label, AssignFader);
                    micItem.Tag = new object[] { fader, mixerSession };
                    faderMenu.MenuItems.Add(micItem);
                }
                */

                faderMenu.MenuItems.Add("-");

                // Add focus mixerSession to menu                
                MenuItem focusItem = new MenuItem(focusMixerSession.label, AssignFader);
                focusItem.Tag = new object[] { fader, focusMixerSession };
                faderMenu.MenuItems.Add(focusItem);

                faderMenu.MenuItems.Add("-");

                // Add application mixer sessions to each fader
                foreach (var mixerSession in mixerSessions)
                {
                    MenuItem si = new MenuItem(mixerSession.label, AssignFader);
                    si.Tag = new object[] { fader, mixerSession };
                    faderMenu.MenuItems.Add(si);
                }

                faderMenu.MenuItems.Add("-");

                // Add unassign option
                MenuItem unassignItem = new MenuItem("Unassign", UnassignFader);
                unassignItem.Tag = new object[] { fader };
                faderMenu.MenuItems.Add(unassignItem);                
            }

            trayMenu.MenuItems.Add("-");

            // Add toggle option for logarithmic volume curve
            MenuItem logCheck = new MenuItem("Logarithmic", ToggleLogarithmic);
            logCheck.Checked = logarithmic;
            trayMenu.MenuItems.Add(logCheck);

            trayMenu.MenuItems.Add("-");
            trayMenu.MenuItems.Add("Exit", OnExit);
        }

        private void ToggleLogarithmic(object sender, EventArgs e)
        {
            logarithmic = !logarithmic;
            ConfigSaver.AddOrUpdateAppSettings("logarithmic", System.Convert.ToString(logarithmic));
            SaveLogarithmic();
        }

        private void SaveLogarithmic()
        {
            midiDevice.SetCurve(logarithmic ? 2f : 1f);
        }

        private void AssignFader(object sender, EventArgs e)
        {
            var fader = (Fader)((object[])((MenuItem)sender).Tag)[0];
            var mixerSession = (MixerSession)((object[])((MenuItem)sender).Tag)[1];
            fader.Assign(mixerSession);
            midiDevice.SaveAssignments();
        }

        private void UnassignFader(object sender, EventArgs e)
        {
            var fader = (Fader)((object[])((MenuItem)sender).Tag)[0];
            fader.Unassign();
            midiDevice.SaveAssignments();
        }

        protected override void OnLoad(EventArgs e)
        {
            Visible = false; // Hide form window.
            ShowInTaskbar = false; // Remove from taskbar.

            base.OnLoad(e);
        }

        private void OnExit(object sender, EventArgs e)
        {
            try
            {
                midiDevice.ResetAllLights();
                midiDevice.SaveAssignments();
            }
            catch (MmException ex)
            {
                if (ex.Message.StartsWith("InvalidHandle calling midiOutShortMsg"))
                {
                    // Exit program safely if device was already disconnected
                }
            }

            Application.Exit();
            _workerDispatcher.InvokeShutdown();
        }

    }
}


================================================
FILE: NK2Tray/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("NK2Tray")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NK2Tray")]
[assembly: AssemblyCopyright("Copyright ©  2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("f88d8f42-cc15-40ae-9301-043313764c4c")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.0.16.0")]
[assembly: AssemblyFileVersion("0.0.16.0")]


================================================
FILE: NK2Tray/Properties/Resources.Designer.cs
================================================
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace NK2Tray.Properties {
    using System;
    
    
    /// <summary>
    ///   A strongly-typed resource class, for looking up localized strings, etc.
    /// </summary>
    // This class was auto-generated by the StronglyTypedResourceBuilder
    // class via a tool like ResGen or Visual Studio.
    // To add or remove a member, edit your .ResX file then rerun ResGen
    // with the /str option, or rebuild your VS project.
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources {
        
        private static global::System.Resources.ResourceManager resourceMan;
        
        private static global::System.Globalization.CultureInfo resourceCulture;
        
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resources() {
        }
        
        /// <summary>
        ///   Returns the cached ResourceManager instance used by this class.
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if (object.ReferenceEquals(resourceMan, null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NK2Tray.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        
        /// <summary>
        ///   Overrides the current thread's CurrentUICulture property for all
        ///   resource lookups using this strongly typed resource class.
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture {
            get {
                return resourceCulture;
            }
            set {
                resourceCulture = value;
            }
        }
        
        /// <summary>
        ///   Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
        /// </summary>
        internal static System.Drawing.Icon nk2tray {
            get {
                object obj = ResourceManager.GetObject("nk2tray", resourceCulture);
                return ((System.Drawing.Icon)(obj));
            }
        }
    }
}


================================================
FILE: NK2Tray/Properties/Resources.resx
================================================
<?xml version="1.0" encoding="utf-8"?>
<root>
  <!-- 
    Microsoft ResX Schema 
    
    Version 2.0
    
    The primary goals of this format is to allow a simple XML format 
    that is mostly human readable. The generation and parsing of the 
    various data types are done through the TypeConverter classes 
    associated with the data types.
    
    Example:
    
    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
                
    There are any number of "resheader" rows that contain simple 
    name/value pairs.
    
    Each data row contains a name, and value. The row also contains a 
    type or mimetype. Type corresponds to a .NET class that support 
    text/value conversion through the TypeConverter architecture. 
    Classes that don't support this are serialized and stored with the 
    mimetype set.
    
    The mimetype is used for serialized objects, and tells the 
    ResXResourceReader how to depersist the object. This is currently not 
    extensible. For a given mimetype the value must be set accordingly:
    
    Note - application/x-microsoft.net.object.binary.base64 is the format 
    that the ResXResourceWriter will generate, however the reader can 
    read any of the formats listed below.
    
    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    
    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.

    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array 
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  <data name="nk2tray" type="System.Resources.ResXFileRef, System.Windows.Forms">
    <value>..\nk2tray.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
  </data>
</root>

================================================
FILE: NK2Tray/Properties/Settings.Designer.cs
================================================
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace NK2Tray.Properties {
    
    
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
        
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        
        public static Settings Default {
            get {
                return defaultInstance;
            }
        }

        public string SavedAssignments { get; internal set; }
    }
}


================================================
FILE: NK2Tray/Properties/Settings.settings
================================================
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>


================================================
FILE: NK2Tray/WindowTools.cs
================================================
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace NK2Tray
{
    class WindowTools
    {
        [DllImport("user32.dll")]
        public static extern IntPtr FindWindow(string strClassName, string strWindowName);

        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll", SetLastError = true)]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);

        public static int GetPidByName(string name)
        {
            Process[] processes = Process.GetProcessesByName(name);
            int pid = 0;
            int maxHandleCount = 0;
            foreach (Process process in processes)
            {
                if (process.Id != 0 && process.HandleCount > maxHandleCount)
                {
                    maxHandleCount = process.HandleCount;
                    pid = process.Id;
                }
            }
            return pid;
            /*{
                var hWnd = FindWindow(name, "");
                if (hWnd == IntPtr.Zero)
                    return 0;

                uint pID;
                GetWindowThreadProcessId(hWnd, out pID);
                if (pID == 0)
                    return 0;

                Console.WriteLine(pID);
                return Convert.ToInt32(pID);
            }*/

        }

        public static int GetForegroundPID()
        {
            var hWnd = GetForegroundWindow();
            if (hWnd == IntPtr.Zero)
                return 0;

            uint pID;
            GetWindowThreadProcessId(hWnd, out pID);
            if (pID == 0)
                return 0;

            return Convert.ToInt32(pID);
        }

        public static bool ProcessExists(uint processId)
        {
            try
            {
                var process = Process.GetProcessById((int)processId);
                return true;
            }
            catch (ArgumentException)
            {
                return false;
            }
        }

        public static void Dump(object ob)
        {
            Console.WriteLine("================");
            foreach (var prop in ob.GetType().GetProperties())
                Console.WriteLine($@"{prop.Name} = {prop.GetValue(ob, null)}");
        }

        public static bool IsProcessByNameRunning(string processName)
        {
            return GetPidByName(processName) != 0;
        }

        public static void StartApplication(string applicationPath)
        {
            try
            {
                Process.Start(new ProcessStartInfo(applicationPath));
            }
            catch (InvalidOperationException)
            {
                // Application cannot be started for this button
            } 
        }
    }
}


================================================
FILE: NK2Tray/XtouchMini.cs
================================================
using NAudio.Midi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;

namespace NK2Tray
{
    public class XtouchMini : MidiDevice
    {
        public override string SearchString => "x-touch mini";
        public override FaderDef DefaultFaderDef => new FaderDef(
            true,  // delta
            64f,   // range
            1,     // channel
            true,  // selectPresent
            true,  // mutePresent
            false, // recordPresent
            false, // subFaderPresent
            16,    // faderOffset
            32,    // selectOffset
            38,    // muteOffset
            0,     // recordOffset
            0,     // subFaderOffset
            MidiCommandCode.ControlChange, // faderCode
            MidiCommandCode.NoteOn,        // selectCode
            MidiCommandCode.NoteOn,        // muteCode
            MidiCommandCode.ControlChange, // recordCode
            MidiCommandCode.ControlChange  // subFaderCode
        );

        public FaderDef FirstTwoFaderDef => new FaderDef(
            true,  // delta
            64f,   // range
            1,     // channel
            true,  // selectPresent
            true,  // mutePresent
            false, // recordPresent
            false, // subFaderPresent
            16,    // faderOffset
            32,    // selectOffset
            89,    // muteOffset
            0,     // recordOffset
            0,     // subFaderOffset
            MidiCommandCode.ControlChange, // faderCode
            MidiCommandCode.NoteOn,        // selectCode
            MidiCommandCode.NoteOn,        // muteCode
            MidiCommandCode.ControlChange, // recordCode
            MidiCommandCode.ControlChange  // subFaderCode
        );

        public FaderDef MasterFaderDef => new FaderDef(
            false,  // delta
            16256f, // range
            1,      // channel
            true,   // selectPresent
            true,   // mutePresent
            false,  // recordPresent
            false,  // subFaderPresent
            0,      // faderOffset
            76,     // selectOffset
            77,     // muteOffset
            0,      // recordOffset
            0,      // subFaderOffset
            MidiCommandCode.PitchWheelChange, // faderCode
            MidiCommandCode.NoteOn,           // selectCode
            MidiCommandCode.NoteOn,           // muteCode
            MidiCommandCode.ControlChange,    // recordCode
            MidiCommandCode.ControlChange,    // subFaderCode
            9      // faderChannelOverride
        );

        public XtouchMini(AudioDevice audioDev)
        {
            audioDevices = audioDev;
            FindMidiIn();
            FindMidiOut();
            if (Found)
            {
                ResetAllLights();
                InitFaders();
                InitButtons();
                LoadAssignments();
                ListenForMidi();
            }
        }

        public override void SetVolumeIndicator(int faderNum, float level)
        {
            if (level >= 0)
            {
                var usedLevel = level;
                var fader = faders[faderNum];

                if (fader.faderDef.delta)
                {
                    var nearestStep = fader.steps.Select((x, i) => new { Index = i, Distance = Math.Abs(level - x) }).OrderBy(x => x.Distance).First().Index;
                    usedLevel = (float)nearestStep / (fader.steps.Length - 1);
                }

                var val = (int)Math.Round(usedLevel * 12) + 1 + 16 * 2;

                Console.WriteLine($@"Setting fader {fader} display to {usedLevel} ({val})");
                midiOut.Send(new ControlChangeEvent(0, 1, (MidiController)(faderNum + 48), val).GetAsShortMessage());
            }
            else
                midiOut.Send(new ControlChangeEvent(0, 1, (MidiController)(faderNum + 48), 0).GetAsShortMessage());
        }

        public override void ResetAllLights()
        {

            foreach (var i in Enumerable.Range(48, 8))
                midiOut.Send(new ControlChangeEvent(0, 1, (MidiController)i, 0).GetAsShortMessage());


            foreach (var i in Enumerable.Range(0, 128))
                midiOut.Send(new NoteOnEvent(0, 1, i, 0, 0).GetAsShortMessage());
        }

        public override void SetLight(int controller, bool state)
        {
            midiOut.Send(new NoteOnEvent(0, 1, controller, state ? 127 : 0, 0).GetAsShortMessage());
        }

        public override void InitFaders()
        {
            faders = new List<Fader>();
            
            foreach (var i in Enumerable.Range(0, 9))
            {
                Fader fader = new Fader(this, i, SelectFaderDef(i));
                fader.ResetLights();
                faders.Add(fader);
            }
        }

        public FaderDef SelectFaderDef(int faderNum)
        {
            if (faderNum < 2)
                return FirstTwoFaderDef;
            else if (faderNum == 8)
                return MasterFaderDef;
            else
                return DefaultFaderDef;
        }

        public override void InitButtons()
        {
            buttons = new List<Button>();

            buttons.Add(new Button(ref midiOut,  ButtonType.MediaPrevious, 91, true,  MidiCommandCode.NoteOn));
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaNext,     92, true,  MidiCommandCode.NoteOn));
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaStop,     93, false, MidiCommandCode.NoteOn));
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaPlay,     94, true,  MidiCommandCode.NoteOn));
            buttons.Add(new Button(ref midiOut,  ButtonType.MediaRecord,   95, false, MidiCommandCode.NoteOn));

            buttonsMappingTable = new Hashtable();
            foreach (var button in buttons)
            {
                buttonsMappingTable.Add(button.controller, button);
            }
        }
    }
}


================================================
FILE: NK2Tray/packages.config
================================================
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Costura.Fody" version="3.3.0" targetFramework="net461" />
  <package id="Fody" version="3.3.5" targetFramework="net461" developmentDependency="true" />
  <package id="NAudio" version="1.8.5" targetFramework="net461" />
</packages>

================================================
FILE: NK2Tray.sln
================================================

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.168
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NK2Tray", "NK2Tray\NK2Tray.csproj", "{F88D8F42-CC15-40AE-9301-043313764C4C}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{F88D8F42-CC15-40AE-9301-043313764C4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{F88D8F42-CC15-40AE-9301-043313764C4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{F88D8F42-CC15-40AE-9301-043313764C4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{F88D8F42-CC15-40AE-9301-043313764C4C}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {1808D8FF-F1C2-4A1F-A929-E9F148BFA9A1}
	EndGlobalSection
EndGlobal


================================================
FILE: NK2Tray_Display/BOM.md
================================================
## Bill of materials for the NK2Tray_Display



### Printed Parts:

 1x  	NK2DisplayCaseA.stl

 1x 	NK2DisplayCaseB.stl

 3x  	NK2DisplayLatch.stl

As the case and the latches do not bear any load, you can use a low infill. 25% is more than enough. Use 2-3 perimeter walls The case is designed to be printed with the face on the print bed, that should get you a nice surface to look at.
Print the latches as they are designed and use supports under the overhang. Use at least 3 perimeter walls, again infill doesn´t matter that much.



### Electronic  Parts:

8x OLED Display 0.96 inch, 128X64, I2C (get the white OR blue ones, not yellow&blue)
https://www.aliexpress.com/item/32713614136.html?spm=a2g0s.9042311.0.0.27424c4dUOH4Fo

1x Wemos D1 Mini V3 
https://www.aliexpress.com/item/32855727444.html?spm=a2g0s.9042311.0.0.27424c4dq7waX2

1x Adafruit TCA9548A I2C Multiplexer
https://www.amazon.de/gp/product/B017C09ETS/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1

1x Custom PCB
Gerber_PCB_NK2Tray_Display.zip



### Fastener:

3x M3x8 Hex Socket screw or similar



### Firmware:

NK2Tray_DisplayFW.ino


================================================
FILE: NK2Tray_Display/NK2Tray_DisplayFW.ino
================================================
// Display Firmware V2
// I2C Adressen:
// Oled: 0x3C
// Multi:0x70

#include "Wire.h"               // Hmmm?
#include "ssd1306.h"            // 128x64 Display Ansteuerung
#define MUX_Address 0x70        // TCA9548A Multiplexer Adresse
#include <ESP8266WiFi.h>        // Include the Wi-Fi library
#include <ESP8266Ping.h>        // Include the Ping library


const char* ssid     = "Skynet Apocalypse";         // The SSID (name) of the Wi-Fi network you want to connect to
const char* password = "chaotisch";                 // The password of the Wi-Fi network
const IPAddress remote_ip(192, 168, 178, 69);       // Zieladresse


// Anzuzeigende Bilder:
const unsigned char WinLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0xC0, 0xC0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xE0,
0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF8,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x1F, 0x03, 0x00, 0x00, 0x00, 0xF0, 0xFC, 0xF8, 0xF8, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xE0,
0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x38, 0x3F, 0x1F,
0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x3F, 0x3F, 0x7F, 0x7F, 0xFF, 0x0F,
0x03, 0x00, 0x00, 0x80, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF8, 0xF8, 0xFC, 0xFC,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF0, 0x70, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0x0F, 0x0F, 0x0F, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x1F, 0x1F, 0x0F, 0x0F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x0F, 0x00, 0x00, 0x00, 0xD0,
0xFC, 0xFF, 0xFF, 0xFE, 0xFE, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xFC,
0xFC, 0xFC, 0xFC, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x03,
0x03, 0x07, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x1F, 0x3F, 0x07, 0x00, 0x00, 0x00, 0xF0, 0xFE, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x3F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03,
0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const unsigned char ChromeLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x80, 0xC0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8, 0xFC, 0xFC, 0xFC,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x3C, 0xFE,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x7E, 0x7C, 0x78, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF8, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0xE0,
0x81, 0x07, 0x1F, 0x3F, 0xFF, 0x7F, 0x1F, 0x0F, 0x83, 0xC1, 0xF1, 0xF0, 0xF8, 0xFC, 0xFC, 0xFC,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF0, 0xF0, 0xC0, 0x80, 0x04, 0x1C, 0x7C, 0xFC, 0xFC, 0xFC,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFC, 0xF0, 0xC0, 0x80, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xF0, 0xF0, 0xE3, 0xC3, 0xC7, 0x8F, 0x8F, 0x8F,
0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x07, 0x03, 0x03, 0xC0, 0xE0, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F,
0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x7F, 0x1F, 0x07, 0x81, 0xE0, 0xF8, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F,
0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x03, 0x07, 0x07, 0x07, 0x0F, 0x0F, 0x03, 0x00,
0x00, 0x0C, 0x0E, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x03, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const unsigned char DiscordLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xE0, 0xF0,
0x70, 0x70, 0x38, 0x38, 0x18, 0x18, 0x18, 0x9C, 0x8C, 0x88, 0x80, 0x80, 0xC0, 0xC0, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0xC0, 0x80, 0x80, 0x88, 0x8C, 0x9C, 0x18, 0x18, 0x18, 0x38, 0x38, 0x70, 0x70,
0xF0, 0xE0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFC,
0xFC, 0xFC, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFC, 0xFC,
0xFC, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x3F, 0x1F, 0x3F, 0x3F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x3F, 0x1F, 0x3F, 0x3F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xF1, 0xC0, 0x80, 0x80, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x80, 0x80, 0xC0, 0xF1, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xC7, 0x8F, 0x8F,
0x1F, 0x1F, 0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x7F, 0x3F, 0x3F, 0x1F, 0x1F,
0x8F, 0x8F, 0xC7, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x1F, 0x1F, 0x1F,
0x1F, 0x1F, 0x3E, 0x3E, 0x1E, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x1E, 0x3E, 0x3E, 0x1F, 0x1F,
0x1F, 0x1F, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const unsigned char MumbleLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x20, 0x20, 0x90, 0xD0, 0xC0, 0xC8, 0xE8, 0xE0, 0xE4, 0xC4, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0xC4, 0xE0, 0xE0, 0xE8, 0xC0, 0xC0, 0x90, 0x80, 0x20, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x10, 0x04, 0x02, 0x01,
0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x07, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x03, 0x0F, 0xFF, 0xFF, 0xF8, 0x00, 0x00,
0x01, 0x02, 0x04, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0xC0, 0xC0, 0xE0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1F, 0x1F, 0x3C, 0x38, 0x30,
0x30, 0x30, 0x38, 0x1C, 0x1F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xE0, 0xC0,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFE, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFE, 0xFA, 0x02, 0x32, 0x22, 0x02, 0xFE, 0x02, 0x02,
0xFE, 0xFE, 0xFE, 0x02, 0x02, 0x32, 0x32, 0x32, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFE, 0xF8, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x30, 0xC0, 0x80, 0x1F, 0x7F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0x7F, 0x40, 0x67, 0x66, 0x60, 0x79, 0x66, 0x40,
0x47, 0x47, 0x47, 0x78, 0x40, 0x46, 0x47, 0x47, 0x7F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x3F, 0x8F, 0xC0, 0x30, 0x0E, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08,
0x11, 0x23, 0x63, 0x47, 0x87, 0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0x78,
0x78, 0x78, 0x78, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x90, 0x93, 0xCF, 0x47, 0x67, 0x23, 0x13,
0x19, 0x0C, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x02, 0x02, 0x02, 0x02, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x02, 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const unsigned char TeamspeakLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x80, 0xC0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0x78, 0x78, 0x78, 0x38, 0x38, 0x38, 0x38,
0x38, 0x38, 0x38, 0x38, 0x38, 0x78, 0x78, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xF4, 0xFE,
0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F,
0xFE, 0xF4, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xFC, 0xFF, 0x0F, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0x1F, 0xFF, 0xFC, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0xF0, 0xF8, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xE0, 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00,
0x00, 0x01, 0x0F, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1E, 0x08, 0x00,
0x00, 0x00, 0x18, 0x1C, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1F, 0x03, 0x00,
0x00, 0x00, 0xC0, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8,
0xFC, 0x7F, 0x3F, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x03, 0x03, 0x03, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const unsigned char SpotifyLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0xC0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1,
0xC1, 0xC1, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xC1, 0xC1, 0xC1, 0x81, 0x83, 0x83, 0x03, 0x07,
0x07, 0x0F, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xE7, 0xC3, 0xC1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xC1, 0xC1, 0xC3, 0x83, 0x83, 0x87, 0x07, 0x0F, 0x0F, 0x1F, 0x3F,
0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xF7, 0xE3, 0xE3, 0xE1, 0xE1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xE1, 0xE1, 0xE3, 0xE3, 0xC3, 0xC7, 0x87, 0x8F, 0x8F, 0x1F, 0xBF, 0xFE, 0xFE, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x03, 0x03, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x03, 0x03, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};


// Initialize I2C buses using TCA9548A I2C Multiplexer
void tcaselect(uint8_t i2c_bus) 
{
    if (i2c_bus > 7) return;
    Wire.beginTransmission(MUX_Address);
    Wire.write(1 << i2c_bus);
    Wire.endTransmission(); 
}

// Einmallaufender Code
void setup()
{
    Serial.begin(9600);

    delay(10);
    Serial.println('\n');
  
     WiFi.begin(ssid, password);             // Connect to the network
     Serial.print("Verbinde mit ");
     Serial.print(ssid); 
     Serial.println(" ...");

  int i = 0;
  while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
    delay(1000);
    Serial.print(++i); Serial.print(' ');
  }

  Serial.println('\n');
  Serial.println("Verbindung hergestellt!");  
  Serial.print("IP Addresse:\t");
  Serial.println(WiFi.localIP());         // Send the IP address of the ESP8266 to the computer
  
}

//Schleifencode
void loop()
{
    if(Ping.ping(remote_ip)) 
    {
      Serial.println("Monolith ONLINE!");
      Serial.println();
      
      bool ret = Ping.ping(remote_ip);
      Serial.print ("Status: ");
      Serial.println(ret);
    
      int avg_time_ms = Ping.averageTime();
      Serial.print("Ping: ");
      Serial.print(avg_time_ms);
      Serial.println("ms");
      Serial.println();


    tcaselect(0);
    ssd1306_128x64_i2c_init();
    ssd1306_setFixedFont(ssd1306xled_font6x8);
    ssd1306_clearScreen();
    ssd1306_printFixed(45,  56, "Chrome", STYLE_NORMAL);
    Serial.print("Chrome anzeigen");
    Serial.println("");
    ssd1306_drawBitmap(32, 0, 64, 56, ChromeLogo);
    Serial.print("ChromeLogo anzeigen");
    Serial.println("");
    Serial.println("");
 
    tcaselect(0);
    ssd1306_128x64_i2c_init();
    ssd1306_setFixedFont(ssd1306xled_font6x8);
    ssd1306_clearScreen();
    ssd1306_printFixed(45,  56, "Chrome", STYLE_NORMAL);
    Serial.print("Chrome anzeigen");
    Serial.println("");
    ssd1306_drawBitmap(32, 0, 64, 56, ChromeLogo);
    Serial.print("ChromeLogo anzeigen");
    Serial.println("");
    Serial.println("");
  
    tcaselect(2);
    ssd1306_128x64_i2c_init();
    ssd1306_setFixedFont(ssd1306xled_font6x8);
    ssd1306_clearScreen();
    ssd1306_printFixed(38,  56, "Teamspeak", STYLE_NORMAL);
    Serial.print("Teamspeak anzeigen");
    Serial.println("");
    ssd1306_drawBitmap(32, 0, 64, 56, TeamspeakLogo);
    Serial.print("TeamspeakLogo anzeigen");
    Serial.println("");
    Serial.println("");

    tcaselect(3);
    ssd1306_128x64_i2c_init();
    ssd1306_setFixedFont(ssd1306xled_font6x8);
    ssd1306_clearScreen();
    ssd1306_printFixed(45,  56, "Mumble", STYLE_NORMAL);
    Serial.print("Mumble anzeigen");
    Serial.println("");
    ssd1306_drawBitmap(32, 0, 64, 56, MumbleLogo);
    Serial.print("MumbleLogo anzeigen");
    Serial.println("");
    Serial.println("");

    tcaselect(4);
    ssd1306_128x64_i2c_init();
    ssd1306_setFixedFont(ssd1306xled_font6x8);
    ssd1306_clearScreen();
    ssd1306_printFixed(42,  56, "Discord", STYLE_NORMAL);
    Serial.print("Discord anzeigen");
    Serial.println("");
    ssd1306_drawBitmap(32, 0, 64, 56, DiscordLogo);
    Serial.print("DiscordLogo anzeigen");
    Serial.println("");
    Serial.println("");

    tcaselect(6);
    ssd1306_128x64_i2c_init();
    ssd1306_setFixedFont(ssd1306xled_font6x8);
    ssd1306_clearScreen();
    ssd1306_printFixed(42,  56, "Spotify", STYLE_NORMAL);
    Serial.print("Spotify anzeigen");
    Serial.println("");
    ssd1306_drawBitmap(32, 0, 64, 56, SpotifyLogo);
    Serial.print("SpotifyLogo anzeigen");
    Serial.println("");
    Serial.println("");   

    tcaselect(7);
    ssd1306_128x64_i2c_init();
    ssd1306_setFixedFont(ssd1306xled_font6x8);
    ssd1306_clearScreen();
    ssd1306_printFixed(23,  56, "Master Volume", STYLE_NORMAL);
    Serial.print("Master Volume anzeigen");
    Serial.println("");
    ssd1306_drawBitmap(32, 0, 64, 56, WinLogo);
    Serial.print("WinLogo anzeigen");
    Serial.println("");
    Serial.println("----------------");

    while(Ping.ping(remote_ip))
        {
        Serial.println("Warteschleife");
        delay (60000);
        }

    } 

    else 

    {
        Serial.println("Monolith nicht ONLINE");
        Serial.println("Clearscreen");
        Serial.println();
        tcaselect(0);
        ssd1306_clearScreen();
        tcaselect(1);
        ssd1306_clearScreen();
        tcaselect(2);
        ssd1306_clearScreen();
        tcaselect(3);
        ssd1306_clearScreen();
        tcaselect(4);
        ssd1306_clearScreen();
        tcaselect(5);
        ssd1306_clearScreen();
        tcaselect(6);
        ssd1306_clearScreen();
        tcaselect(7);
        ssd1306_clearScreen();
       
    }

  Serial.println("Verbindung unterbrochen");
  Serial.println("Clearscreen");
  Serial.println();
  tcaselect(0);
  ssd1306_clearScreen();
  tcaselect(1);
  ssd1306_clearScreen();
  tcaselect(2);
  ssd1306_clearScreen();
  tcaselect(3);
  ssd1306_clearScreen();
  tcaselect(4);
  ssd1306_clearScreen();
  tcaselect(5);
  ssd1306_clearScreen();
  tcaselect(6);
  ssd1306_clearScreen();
  tcaselect(7);
  ssd1306_clearScreen();
  delay(5000);

}


================================================
FILE: NK2Tray_Display/NK2Tray_DisplayFW_V2/NK2Tray_DisplayFW_V2.ino
================================================
// Display Firmware V2, made by piLo, tweaked by ZeeuwsGamertje
// I2C Adressen:
// Oled: 0x78
// Multi:0x70

// Directive for Serial Debugging
//#define VERBOSE

#include "Wire.h"               // Wire.h for I2c
#include <ssd1306.h>            // 128x64 Display Ansteuerung, https://github.com/lexus2k/ssd1306
#include <ESP8266WiFi.h>        // Include the Wi-Fi library
#include <ESP8266Ping.h>        // Include the Ping library, https://github.com/dancol90/ESP8266Ping
#include <DNSServer.h>          // For WiFi Manager
#include <ESP8266WebServer.h>   // For Settings from Webinterface
#include <WiFiManager.h>        // WiFiManager for AP and Client related Stuff
#include <ArduinoOTA.h>         // For flashing over the Air
#include <EEPROM.h>             // EEPROM Stuff
#include "logos.h"

#define MUX_Address 0x70        // TCA9548A Multiplexer Adresse

ESP8266WebServer webServer(80); // Init Webserver

WiFiManager wifiManager;  // Init WiFiManager
    
IPAddress remote_ip(192, 168, 178, 69);       // Default value for Remote Host
bool rem_host_online;                         // Online Status Remote Host
unsigned long previousMillis = 0;             // For periodically Checking Host
long interval = 10000;                  // default value for checking host in s
  
bool always_on = true;                        // Default value for Always On mode

bool refresh_displays = false;                // Flag for refreshing displays

String local_ip;                              // String for esp8266 IP

String MAC_address;                           // String for MAC-Address
uint8_t MAC_array[6];                         // Char Array for MAC Adress

char chartmp[50];                             // CharTemp for converting Strings to CharArrays

int display[] = { 0,0,0,0,0,0,0,0};           // Which Disp show what - default value
String icons[] = { "Chrome", "Discord", "Edge", "Empty", "Firefox", "Focus", "Gaming", "Master", "Mic", "Miscellaneous", "Mumble", "OperaGX", "Plex", "Safari", "Spotify", "System Sounds", "Teams", "Teamspeak", "Unassigned", "World of Warcraft", "YT-Music" }; //Possible Values for Icons

// Function for initializing I2C buses using TCA9548A I2C Multiplexer
void tcaselect(uint8_t i2c_bus) 
{
    if (i2c_bus > 7) return;
    Wire.beginTransmission(MUX_Address);
    Wire.write(1 << i2c_bus);
    Wire.endTransmission(); 
}

// Function to set specified display to icon
void set_display(int display, int icon)
{
      tcaselect(display);
      switch (icon) {
        case 0: 
          ssd1306_printFixed(45,  56, "Chrome", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, chromeLogo);
          break;
        case 1:
          ssd1306_printFixed(42,  56, "Discord", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, discordLogo);
          break;
        case 2:
          ssd1306_printFixed(50,  56, "Edge", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, edgeLogo);
          break;
        case 3:
          ssd1306_printFixed(62,  56, "", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, noLogo);
          break;
        case 4:
          ssd1306_printFixed(42,  56, "Firefox", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, firefoxLogo);
          break;
        case 5:
          ssd1306_printFixed(49,  56, "Focus", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, focusLogo);
          break;
        case 6:
          ssd1306_printFixed(45,  56, "Gaming", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, gamingLogo);
          break;
        case 7:
          ssd1306_printFixed(45,  56, "Master", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, masterLogo);
          break;
        case 8:
          ssd1306_printFixed(54,  56, "Mic", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, micLogo);
          break;
        case 9:
          ssd1306_printFixed(26,  56, "Miscellaneous", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, miscLogo);
          break;
        case 10:
          ssd1306_printFixed(45,  56, "Mumble", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, mumbleLogo);
          break;
        case 11:
          ssd1306_printFixed(42,  56, "OperaGX", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, operagxLogo);
          break;
        case 12:
          ssd1306_printFixed(50,  56, "Plex", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, plexLogo);
          break;
        case 13:
          ssd1306_printFixed(45,  56, "Safari", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, safariLogo);
          break;
        case 14:
          ssd1306_printFixed(42,  56, "Spotify", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, spotifyLogo);
          break;        
        case 15:
          ssd1306_printFixed(23,  56, "System Sounds", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, windowsLogo);
          break;
        case 16:
          ssd1306_printFixed(49,  56, "Teams", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, teamsLogo);
          break;
        case 17:
          ssd1306_printFixed(38,  56, "Teamspeak", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, teamspeakLogo);
          break;
        case 18:
          ssd1306_printFixed(36,  56, "Unassigned", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, unassignedLogo);
          break;
        case 19:
          ssd1306_printFixed(54,  56, "WoW", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, wowLogo);
          break;
        case 20:
          ssd1306_printFixed(40,  56, "YT-Music", STYLE_NORMAL);
          ssd1306_drawBitmap(32, 0, 64, 56, ytmusicLogo);
          break;
              
      }
}

// function to clear all screens
void clear_screens()
{
  for (int i = 0; i <= 7; i++)
  {
    tcaselect(i);
    ssd1306_clearScreen();
  }
}

// function for checking remote host
bool check_remote_host()
{
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis; 
    if (Ping.ping(remote_ip)) {
      #ifdef VERBOSE
        Serial.println("Host ONLINE!");
        Serial.println();
        int avg_time_ms = Ping.averageTime();
        Serial.print("Ping: ");
        Serial.print(avg_time_ms);
        Serial.println("ms");
        Serial.println();
      #endif
      rem_host_online = true;
    } else {
      #ifdef VERBOSE
        Serial.println("Host offline!");
      #endif
      rem_host_online=false;
    }
  }
}

// function for getting mac address
void getMac() {
  char MAC_char[30] = "";
  
  WiFi.macAddress(MAC_array);
  
  // Format the MAC address into string
  sprintf(MAC_char, "%02X", MAC_array[0]);
  for (int i = 1; i < 6; ++i) {
    sprintf(MAC_char, "%s : %02X", MAC_char, MAC_array[i]);
  }
  MAC_address = String(MAC_char);
}

// Setup Code
void setup()
{
    EEPROM.begin(512);
    #ifdef VERBOSE
      Serial.begin(9600);
      Serial.println("Setup Started");
    #endif
      
    wifiManager.autoConnect("NK2Tray_Display"); 

    local_ip = WiFi.localIP().toString();
    local_ip.toCharArray(chartmp, 50);

    getMac();
    
    startWebServer();

    ArduinoOTA.setHostname("NK2Tray-Display");
    ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    for ( int i = 0; i <= 7; i++)
    {
      tcaselect(i);
      ssd1306_drawBitmap(32, 0, 64, 64, firmwareLogo);
      ssd1306_printFixed(14, 56, "updating firmware", STYLE_NORMAL);
      #ifdef VERBOSE
        Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
      #endif
    }
    });
    ArduinoOTA.begin();

    loadSettings();

  for (int i = 0; i <= 7; i++)
  {
    tcaselect(i);
    delay(10);
    ssd1306_128x64_i2c_init();
    delay(10);
    ssd1306_setFixedFont(ssd1306xled_font6x8);
  }

  clear_screens();
  
  tcaselect(0);  
  delay(10);
  ssd1306_128x64_i2c_init();
  delay(10);
  ssd1306_drawBitmap(0, 0, 128, 64, piloLogo);
  tcaselect(1);
  ssd1306_printFixed(34, 32, "Booting...", STYLE_NORMAL);
  tcaselect(2);
  ssd1306_printFixed(54, 32, "IP: ", STYLE_NORMAL);
  tcaselect(3);
  ssd1306_printFixed(20, 32, chartmp, STYLE_NORMAL);
  tcaselect(4);
  ssd1306_printFixed(20, 32, "NK2Tray-Display", STYLE_NORMAL);  
  tcaselect(5);
  ssd1306_printFixed(15, 32, "Firmware by piLo", STYLE_NORMAL);
  tcaselect(6);
  ssd1306_printFixed(0, 30, "Tweaked by", STYLE_NORMAL);
  ssd1306_printFixed(0, 40, "ZeeuwsGamertje", STYLE_NORMAL);
  tcaselect(7);
  ssd1306_drawBitmap(0, 0, 128, 64, zeeuwsLogo);
  
  delay(10000);

  clear_screens();

  refresh_displays = true;
}

//Schleifencode
void loop()
{
    if(always_on == true) {
      if(refresh_displays)
      {
        clear_screens();
        for (int i = 0; i <= 7; i++)
        {
          set_display(i, display[i]);
        }
        refresh_displays = false;
      }
    
    } else if (rem_host_online) {
      if (refresh_displays)
      {  
        clear_screens();
        for (int i = 0; i <= 7; i++)
        {
          set_display(i, display[i]);
        }
        refresh_displays = false;      
      }
    } else {
        clear_screens();
        refresh_displays=true;
    }
    
    if(!always_on)
    {
      check_remote_host();
    }
    webServer.handleClient();
    ArduinoOTA.handle();
}


================================================
FILE: NK2Tray_Display/NK2Tray_DisplayFW_V2/eeprom.ino
================================================
// function to save our variables into EEPROM
// Writes OK to 500 & 501 - use this to verify a write
bool saveSettings() {
  
  #ifdef VERBOSE
    Serial.print("Saving Settings... ");
  #endif

  // Wipe our OK and check
  EEPROM.write(500, '\0');
  EEPROM.write(501, '\0');
  EEPROM.commit();
  if(EEPROM.read(500) != '\0'|| EEPROM.read(501) != '\0') {
      #ifdef VERBOSE
        Serial.println("fail!");  
      #endif  
    return false;
  }
  
  delay(100);
  
  EEPROM.write(100, remote_ip[0]);
  EEPROM.write(101, remote_ip[1]);
  EEPROM.write(102, remote_ip[2]);
  EEPROM.write(103, remote_ip[3]);
 
  if (always_on) {
    EEPROM.write(104, 1);
  } else {
    EEPROM.write(104, 0);
  }
  
  for (int i = 0; i <= 7; i++)
  {
    EEPROM.write(105+i, display[i]);
  }

  EEPROM.write(115, interval/1000);
    
  EEPROM.write(500, 'O');
  EEPROM.write(501, 'K');

  delay(100);
  
  EEPROM.commit();
  
  delay (100);

  // Verify our OK was written & return false if not
  if(EEPROM.read(500) != 'O' || EEPROM.read(501) != 'K') {
    #ifdef VERBOSE
      Serial.println("fail!");
    #endif
    return false;
  }
  #ifdef VERBOSE
    Serial.println("success!");
  #endif

  // Return true if all went well
  return true;
}



// Function to load our settings from EEPROM to our variables.
bool loadSettings() {
  #ifdef VERBOSE
    Serial.print("Loading Settings... ");
  #endif
  

// Check if we have previous saves.  If not, return false
  if(EEPROM.read(500) != 'O' || EEPROM.read(501) != 'K') {
    #ifdef VERBOSE
      Serial.println("No previous saves.");
    #endif    
    return false;
  }

   remote_ip[0] = EEPROM.read(100);
   remote_ip[1] = EEPROM.read(101);
   remote_ip[2] = EEPROM.read(102);
   remote_ip[3] = EEPROM.read(103);

   if (EEPROM.read(104) == 0)
   {
      always_on = false;
   } else {
      always_on = true;
   }
    
   for (int i = 0; i <= 7; i++)
   {
     display[i] = EEPROM.read(105+i);
   }

   interval = EEPROM.read(115) * 1000;
   
   #ifdef VERBOSE
     Serial.println("success!");
   #endif
   // Return
   return true;
}


================================================
FILE: NK2Tray_Display/NK2Tray_DisplayFW_V2/logos.h
================================================
// Anzuzeigende Bilder:

// Windows Logo
const unsigned char windowsLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0xC0, 0xC0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xE0,
0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF8,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x1F, 0x03, 0x00, 0x00, 0x00, 0xF0, 0xFC, 0xF8, 0xF8, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xE0,
0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x38, 0x3F, 0x1F,
0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x3F, 0x3F, 0x7F, 0x7F, 0xFF, 0x0F,
0x03, 0x00, 0x00, 0x80, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF8, 0xF8, 0xFC, 0xFC,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF0, 0x70, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0x0F, 0x0F, 0x0F, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x1F, 0x1F, 0x0F, 0x0F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x0F, 0x00, 0x00, 0x00, 0xD0,
0xFC, 0xFF, 0xFF, 0xFE, 0xFE, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xFC,
0xFC, 0xFC, 0xFC, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x03,
0x03, 0x07, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x1F, 0x3F, 0x07, 0x00, 0x00, 0x00, 0xF0, 0xFE, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x3F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03,
0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Chrome Logo
const unsigned char chromeLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x80, 0xC0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8, 0xFC, 0xFC, 0xFC,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x3C, 0xFE,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x7E, 0x7C, 0x78, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF8, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0xE0,
0x81, 0x07, 0x1F, 0x3F, 0xFF, 0x7F, 0x1F, 0x0F, 0x83, 0xC1, 0xF1, 0xF0, 0xF8, 0xFC, 0xFC, 0xFC,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF0, 0xF0, 0xC0, 0x80, 0x04, 0x1C, 0x7C, 0xFC, 0xFC, 0xFC,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFC, 0xF0, 0xC0, 0x80, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xF0, 0xF0, 0xE3, 0xC3, 0xC7, 0x8F, 0x8F, 0x8F,
0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x07, 0x03, 0x03, 0xC0, 0xE0, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F,
0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x7F, 0x1F, 0x07, 0x81, 0xE0, 0xF8, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F,
0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x03, 0x07, 0x07, 0x07, 0x0F, 0x0F, 0x03, 0x00,
0x00, 0x0C, 0x0E, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x03, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Discord Logo
const unsigned char discordLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xE0, 0xF0,
0x70, 0x70, 0x38, 0x38, 0x18, 0x18, 0x18, 0x9C, 0x8C, 0x88, 0x80, 0x80, 0xC0, 0xC0, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0xC0, 0x80, 0x80, 0x88, 0x8C, 0x9C, 0x18, 0x18, 0x18, 0x38, 0x38, 0x70, 0x70,
0xF0, 0xE0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFC,
0xFC, 0xFC, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFC, 0xFC,
0xFC, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x3F, 0x1F, 0x3F, 0x3F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x3F, 0x1F, 0x3F, 0x3F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xF1, 0xC0, 0x80, 0x80, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x80, 0x80, 0xC0, 0xF1, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xC7, 0x8F, 0x8F,
0x1F, 0x1F, 0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x7F, 0x3F, 0x3F, 0x1F, 0x1F,
0x8F, 0x8F, 0xC7, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x1F, 0x1F, 0x1F,
0x1F, 0x1F, 0x3E, 0x3E, 0x1E, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x1E, 0x3E, 0x3E, 0x1F, 0x1F,
0x1F, 0x1F, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Mumble Logo
const unsigned char mumbleLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x20, 0x20, 0x90, 0xD0, 0xC0, 0xC8, 0xE8, 0xE0, 0xE4, 0xC4, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0xC4, 0xE0, 0xE0, 0xE8, 0xC0, 0xC0, 0x90, 0x80, 0x20, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x10, 0x04, 0x02, 0x01,
0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x07, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x03, 0x0F, 0xFF, 0xFF, 0xF8, 0x00, 0x00,
0x01, 0x02, 0x04, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0xC0, 0xC0, 0xE0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1F, 0x1F, 0x3C, 0x38, 0x30,
0x30, 0x30, 0x38, 0x1C, 0x1F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0xE0, 0xC0,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFE, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFE, 0xFA, 0x02, 0x32, 0x22, 0x02, 0xFE, 0x02, 0x02,
0xFE, 0xFE, 0xFE, 0x02, 0x02, 0x32, 0x32, 0x32, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFE, 0xF8, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x30, 0xC0, 0x80, 0x1F, 0x7F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0x7F, 0x40, 0x67, 0x66, 0x60, 0x79, 0x66, 0x40,
0x47, 0x47, 0x47, 0x78, 0x40, 0x46, 0x47, 0x47, 0x7F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x3F, 0x8F, 0xC0, 0x30, 0x0E, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08,
0x11, 0x23, 0x63, 0x47, 0x87, 0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0x78,
0x78, 0x78, 0x78, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x90, 0x93, 0xCF, 0x47, 0x67, 0x23, 0x13,
0x19, 0x0C, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x02, 0x02, 0x02, 0x02, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x02, 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Teamspeak Logo
const unsigned char teamspeakLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x80, 0xC0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0x78, 0x78, 0x78, 0x38, 0x38, 0x38, 0x38,
0x38, 0x38, 0x38, 0x38, 0x38, 0x78, 0x78, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xF4, 0xFE,
0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F,
0xFE, 0xF4, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xFC, 0xFF, 0x0F, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0x1F, 0xFF, 0xFC, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0xF0, 0xF8, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xE0, 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00,
0x00, 0x01, 0x0F, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1E, 0x08, 0x00,
0x00, 0x00, 0x18, 0x1C, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1F, 0x03, 0x00,
0x00, 0x00, 0xC0, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8,
0xFC, 0x7F, 0x3F, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x03, 0x03, 0x03, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Spotify Logo
const unsigned char spotifyLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0xC0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1,
0xC1, 0xC1, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xC1, 0xC1, 0xC1, 0x81, 0x83, 0x83, 0x03, 0x07,
0x07, 0x0F, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xE7, 0xC3, 0xC1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xC1, 0xC1, 0xC3, 0x83, 0x83, 0x87, 0x07, 0x0F, 0x0F, 0x1F, 0x3F,
0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xF7, 0xE3, 0xE3, 0xE1, 0xE1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1,
0xF1, 0xF1, 0xE1, 0xE1, 0xE3, 0xE3, 0xC3, 0xC7, 0x87, 0x8F, 0x8F, 0x1F, 0xBF, 0xFE, 0xFE, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x03, 0x03, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x03, 0x03, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Plex Logo
const unsigned char plexLogo [] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x3f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 
0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 
0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 
0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x3f, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x0f, 0x1f, 0x7f, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xfc, 0xf8, 0xe0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xf0, 0xf8, 0xfe, 0xff, 0xff, 0xff, 
0xff, 0xff, 0x3f, 0x1f, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 
0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 
0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 
0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff  
};

// Focus Logo
const unsigned char focusLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 
0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0xc0, 0xe0, 0xe0, 0xf0, 0xf8, 0x78, 0x7c, 0x3c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x3c, 0x3c, 0x7c, 0xf8, 0xf0, 0xf0, 0xe0, 0xc0, 0x80, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xfc, 0xfe, 
0xff, 0x1f, 0x0f, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0f, 
0x0f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x1f, 0xff, 
0xfe, 0xfc, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 
0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 
0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3f, 0x7f, 
0xff, 0xf8, 0xf0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xf0, 
0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xff, 
0x7f, 0x3f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x01, 0x03, 0x07, 0x0f, 0x0f, 0x1f, 0x1e, 0x3c, 0x3c, 0x78, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x78, 0x3c, 0x3c, 0x3e, 0x1f, 0x0f, 0x0f, 0x07, 0x03, 0x01, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 
0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Microphone Logo
const unsigned char micLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xfc, 0xfc, 0xfe, 0xff, 0xff, 
0xff, 0xff, 0xfe, 0xfe, 0xfc, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x1f, 0x7f, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0f, 0x1f, 0x3f, 0x3e, 0x7c, 0x78, 0x78, 0x79, 0xf1, 0xf1, 
0xf1, 0xf1, 0xf1, 0x79, 0x78, 0x7c, 0x3c, 0x3f, 0x1f, 0x0f, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 
0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Speaker Logo
const unsigned char masterLogo [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xc0, 0xe0, 0xe0, 0xf0, 
0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x80, 0xc0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0xc0, 0xc0, 
0xe0, 0xf0, 0xf0, 0xf8, 0xf8, 0xfc, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xe0, 0xf0, 0xf0, 
0xe0, 0x80, 0x03, 0x07, 0x1f, 0x3f, 0xff, 0xfe, 0xfc, 0xf0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x30, 0xf8, 0xfc, 0xfc, 0xf0, 0xc0, 0x00, 0x01, 0x07, 0x7f, 
0xff, 0xff, 0xfe, 0xf8, 0x00, 0x00, 0x01, 0x0f, 0xff, 0xff, 0xff, 0xfe, 0xc0, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x30, 0x7f, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x00, 0x80, 0xf8, 
0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x00, 0x00, 0x07, 0x0f, 0x0f, 
0x1f, 0x1f, 0x3f, 0x7f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1f, 0x3f, 0x3f, 
0x0f, 0x07, 0x01, 0x80, 0xe0, 0xf8, 0xfe, 0xff, 0xff, 0x3f, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07, 0x07, 0x0f, 0x1f, 0x1f, 0x3f, 
0x3f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0
Download .txt
gitextract_koccivs3/

├── .gitattributes
├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.md
│       └── feature_request.md
├── .gitignore
├── NK2Tray/
│   ├── App.config
│   ├── AudioDevice.cs
│   ├── Button.cs
│   ├── ConfigSaver.cs
│   ├── DevicePathMapper.cs
│   ├── EasyControl.cs
│   ├── Fader.cs
│   ├── FodyWeavers.xml
│   ├── FodyWeavers.xsd
│   ├── IconExtractor.cs
│   ├── MediaTools.cs
│   ├── MidiDevice.cs
│   ├── MixerSession.cs
│   ├── NK2Tray.csproj
│   ├── NanoKontrol2.cs
│   ├── OP1.cs
│   ├── Program.cs
│   ├── Properties/
│   │   ├── AssemblyInfo.cs
│   │   ├── Resources.Designer.cs
│   │   ├── Resources.resx
│   │   ├── Settings.Designer.cs
│   │   └── Settings.settings
│   ├── WindowTools.cs
│   ├── XtouchMini.cs
│   └── packages.config
├── NK2Tray.sln
├── NK2Tray_Display/
│   ├── BOM.md
│   ├── NK2DisplayCaseA.stl
│   ├── NK2DisplayCaseB.stl
│   ├── NK2DisplayLatch.stl
│   ├── NK2Tray_DisplayFW.ino
│   ├── NK2Tray_DisplayFW_V2/
│   │   ├── NK2Tray_DisplayFW_V2.ino
│   │   ├── eeprom.ino
│   │   ├── logos.h
│   │   ├── readme.md
│   │   └── webServer.ino
│   └── Readme.md
├── README.md
└── license.txt
Download .txt
SYMBOL INDEX (150 symbols across 17 files)

FILE: NK2Tray/AudioDevice.cs
  class AudioDevice (line 10) | public class AudioDevice
    method UpdateDevices (line 21) | private void UpdateDevices()
    method GetDeviceVolumeObject (line 44) | public AudioEndpointVolume GetDeviceVolumeObject(String deviceIdentifier)
    method OnSessionCreated (line 51) | private void OnSessionCreated(object sender, IAudioSessionControl newS...
    method GetCachedMixerSessions (line 84) | public List<MixerSession> GetCachedMixerSessions()
    method GetMixerSessions (line 96) | private List<MixerSession> GetMixerSessions()
    method GetDeviceByIdentifier (line 159) | public MMDevice GetDeviceByIdentifier(String identifier)
    method FindLivingProcess (line 183) | public Process FindLivingProcess(List<AudioSessionControl> sessions)
    method HasSystemSoundsSession (line 201) | public bool HasSystemSoundsSession(List<AudioSessionControl> sessions)
    method FindMixerSession (line 210) | public MixerSession FindMixerSession(string sessionIdentifier)
    type ProcessCache (line 226) | private struct ProcessCache
      method ProcessCache (line 231) | public ProcessCache(Process process, int id)
    method FindMixerSession (line 239) | public MixerSession FindMixerSession(int pid)

FILE: NK2Tray/Button.cs
  type ButtonType (line 6) | public enum ButtonType
  class Button (line 15) | public class Button
    method Button (line 27) | public Button(ref MidiOut midiOutRef, ButtonType butType, int cont, bo...
    method SetLight (line 37) | public void SetLight(bool state)
    method HandleEvent (line 46) | public bool HandleEvent(MidiInMessageEventArgs e, MidiDevice device)
    method IsHandling (line 107) | public bool IsHandling()
    method SetHandling (line 112) | public void SetHandling(bool handling)
    method GetLight (line 117) | public bool GetLight() { return light; }

FILE: NK2Tray/ConfigSaver.cs
  class ConfigSaver (line 11) | class ConfigSaver
    method AddOrUpdateAppSettings (line 13) | public static void AddOrUpdateAppSettings(string key, string value)
    method GetAppSettings (line 36) | public static string GetAppSettings(string key)

FILE: NK2Tray/DevicePathMapper.cs
  class DevicePathMapper (line 11) | public static class DevicePathMapper
    method QueryDosDevice (line 13) | [DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
    method FromDevicePath (line 16) | public static string FromDevicePath(string devicePath)
    method GetDevicePath (line 24) | private static string GetDevicePath(this DriveInfo driveInfo)
    method GetDriveLetter (line 32) | private static string GetDriveLetter(this DriveInfo driveInfo)
    method ReplaceFirst (line 37) | private static string ReplaceFirst(this string text, string search, st...

FILE: NK2Tray/EasyControl.cs
  class EasyControl (line 7) | public class EasyControl : MidiDevice
    method EasyControl (line 90) | public EasyControl(AudioDevice audioDev)
    method ResetAllLights (line 106) | public override void ResetAllLights()
    method SetLight (line 112) | public override void SetLight(int controller, bool state)
    method InitFaders (line 117) | public override void InitFaders()
    method SelectFaderDef (line 129) | public FaderDef SelectFaderDef(int faderNum)
    method InitButtons (line 138) | public override void InitButtons()

FILE: NK2Tray/Fader.cs
  class FaderDef (line 8) | public class FaderDef
    method FaderDef (line 33) | public FaderDef(
  class Fader (line 65) | public class Fader
    method Fader (line 88) | public Fader(MidiDevice midiDevice, int faderNum)
    method Fader (line 97) | public Fader(MidiDevice midiDevice, int faderNum, FaderDef _faderDef)
    method SetCurve (line 106) | public void SetCurve(float _pow)
    method calculateSteps (line 113) | private float[] calculateSteps()
    method getVolumeFromHardwarePositionUsingMultiplier (line 120) | public float getVolumeFromHardwarePositionUsingMultiplier(float position)
    method getVolFromStage (line 126) | public float getVolFromStage(int stage)
    method ResetLights (line 136) | public void ResetLights()
    method Assign (line 143) | public void Assign(MixerSession mixerSession)
    method AssignInactive (line 158) | public void AssignInactive(string ident)
    method Unassign (line 168) | public void Unassign()
    method convertToApplicationPath (line 180) | private void convertToApplicationPath(string ident)
    method SetSelectLight (line 198) | public void SetSelectLight(bool state)
    method SetMuteLight (line 204) | public void SetMuteLight(bool state)
    method SetRecordLight (line 210) | public void SetRecordLight(bool state)
    method GetSelectLight (line 216) | public bool GetSelectLight() { return selectLight; }
    method GetMuteLight (line 218) | public bool GetMuteLight() { return muteLight; }
    method GetRecordLight (line 220) | public bool GetRecordLight() { return recordLight; }
    method Match (line 222) | public bool Match(int faderNumber, MidiEvent midiEvent, MidiCommandCod...
    method GetValue (line 250) | public int GetValue(MidiEvent midiEvent)
    method GetMatchingFaders (line 271) | public List<Fader> GetMatchingFaders()
    method HandleEvent (line 341) | public bool HandleEvent(MidiInMessageEventArgs e)
    method SetVolume (line 467) | private bool SetVolume(int hardwarePosition)
    method IsHandling (line 523) | public bool IsHandling()
    method SetHandling (line 528) | public void SetHandling(bool handling)

FILE: NK2Tray/IconExtractor.cs
  class IconExtractor (line 7) | public class IconExtractor
    method Extract (line 9) | public static Icon Extract(string file, int number, bool largeIcon)
    method ExtractIconEx (line 24) | [DllImport("Shell32.dll")]

FILE: NK2Tray/MediaTools.cs
  class MediaTools (line 6) | class MediaTools
    method keybd_event (line 8) | [DllImport("user32.dll", SetLastError = true)]
    method Play (line 18) | public static void Play()
    method Stop (line 24) | public static void Stop()
    method Next (line 30) | public static void Next()
    method Previous (line 36) | public static void Previous()

FILE: NK2Tray/MidiDevice.cs
  type SendEvent (line 13) | public enum SendEvent
  class MidiDevice (line 25) | public class MidiDevice
    method MidiDevice (line 58) | public MidiDevice()
    method FindMidiIn (line 65) | public void FindMidiIn()
    method FindMidiOut (line 79) | public void FindMidiOut()
    method ListenForMidi (line 93) | public void ListenForMidi()
    method midiIn_ErrorReceived (line 100) | public void midiIn_ErrorReceived(object sender, MidiInMessageEventArgs e)
    method midiIn_MessageReceived (line 106) | public virtual void midiIn_MessageReceived(object sender, MidiInMessag...
    method ResetAllLights (line 150) | public virtual void ResetAllLights() { }
    method LightShow (line 152) | public virtual void LightShow() { }
    method SetVolumeIndicator (line 154) | public virtual void SetVolumeIndicator(int fader, float level) { }
    method SetLight (line 156) | public virtual void SetLight(int controller, bool state) {}
    method InitFaders (line 158) | public virtual void InitFaders()
    method InitButtons (line 163) | public virtual void InitButtons()
    method SetCurve (line 168) | public void SetCurve(float pow)
    method LoadAssignments (line 173) | public void LoadAssignments()
    method SaveAssignments (line 227) | public void SaveAssignments()

FILE: NK2Tray/MixerSession.cs
  type SessionType (line 9) | public enum SessionType
  class MixerSession (line 20) | public class MixerSession
    method MixerSession (line 29) | public MixerSession(AudioDevice devices, string labl, string identifie...
    method MixerSession (line 39) | public MixerSession(String deviceIdentifier, AudioDevice devices, stri...
    method IsDead (line 54) | public bool IsDead()
    method SetVolume (line 69) | public void SetVolume(float volume)
    method GetVolume (line 116) | public float GetVolume()
    method ChangeVolume (line 174) | public float ChangeVolume(float change)
    method ToggleMute (line 249) | public bool ToggleMute()
    method GetMute (line 294) | public bool GetMute()
    method HasCrossoverProcesses (line 312) | public bool HasCrossoverProcesses(MixerSession other)

FILE: NK2Tray/NanoKontrol2.cs
  class NanoKontrol2 (line 8) | public class NanoKontrol2 : MidiDevice
    method NanoKontrol2 (line 31) | public NanoKontrol2(AudioDevice audioDev)
    method ResetAllLights (line 47) | public override void ResetAllLights()
    method SetLight (line 53) | public override void SetLight(int controller, bool state)
    method InitFaders (line 58) | public override void InitFaders()
    method InitButtons (line 69) | public override void InitButtons()
    method LightShow (line 85) | public override void LightShow()
    class LightShowBackup (line 185) | private class LightShowBackup
      method LightShowBackup (line 191) | public LightShowBackup(Fader inFader)
      method LightShowBackup (line 200) | public LightShowBackup(Button inButton)
      method reset (line 207) | public void reset()

FILE: NK2Tray/OP1.cs
  class OP1 (line 8) | public class OP1 : MidiDevice
    method OP1 (line 92) | public OP1(AudioDevice audioDev)
    method InitFaders (line 107) | public override void InitFaders()
    method InitButtons (line 118) | public override void InitButtons()

FILE: NK2Tray/Program.cs
  class SysTrayApp (line 13) | public class SysTrayApp : Form
    method Main (line 15) | [STAThread]
    method SysTrayApp (line 26) | public SysTrayApp()
    method SetupDevice (line 68) | private Boolean SetupDevice()
    method UnhandledExceptionTrapper (line 92) | private void UnhandledExceptionTrapper(object sender, UnhandledExcepti...
    method getProgramLabel (line 112) | private String getProgramLabel(Fader fader)
    method OnPopup (line 128) | private void OnPopup(object sender, EventArgs e)
    method ToggleLogarithmic (line 230) | private void ToggleLogarithmic(object sender, EventArgs e)
    method SaveLogarithmic (line 237) | private void SaveLogarithmic()
    method AssignFader (line 242) | private void AssignFader(object sender, EventArgs e)
    method UnassignFader (line 250) | private void UnassignFader(object sender, EventArgs e)
    method OnLoad (line 257) | protected override void OnLoad(EventArgs e)
    method OnExit (line 265) | private void OnExit(object sender, EventArgs e)

FILE: NK2Tray/Properties/Resources.Designer.cs
  class Resources (line 22) | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resource...
    method Resources (line 31) | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Mic...

FILE: NK2Tray/Properties/Settings.Designer.cs
  class Settings (line 14) | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]

FILE: NK2Tray/WindowTools.cs
  class WindowTools (line 7) | class WindowTools
    method FindWindow (line 9) | [DllImport("user32.dll")]
    method GetForegroundWindow (line 12) | [DllImport("user32.dll")]
    method GetWindowThreadProcessId (line 15) | [DllImport("user32.dll", SetLastError = true)]
    method GetPidByName (line 18) | public static int GetPidByName(string name)
    method GetForegroundPID (line 48) | public static int GetForegroundPID()
    method ProcessExists (line 62) | public static bool ProcessExists(uint processId)
    method Dump (line 75) | public static void Dump(object ob)
    method IsProcessByNameRunning (line 82) | public static bool IsProcessByNameRunning(string processName)
    method StartApplication (line 87) | public static void StartApplication(string applicationPath)

FILE: NK2Tray/XtouchMini.cs
  class XtouchMini (line 9) | public class XtouchMini : MidiDevice
    method XtouchMini (line 73) | public XtouchMini(AudioDevice audioDev)
    method SetVolumeIndicator (line 88) | public override void SetVolumeIndicator(int faderNum, float level)
    method ResetAllLights (line 110) | public override void ResetAllLights()
    method SetLight (line 121) | public override void SetLight(int controller, bool state)
    method InitFaders (line 126) | public override void InitFaders()
    method SelectFaderDef (line 138) | public FaderDef SelectFaderDef(int faderNum)
    method InitButtons (line 148) | public override void InitButtons()
Condensed preview — 43 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (273K chars).
[
  {
    "path": ".gitattributes",
    "chars": 2518,
    "preview": "###############################################################################\n# Set default behavior to automatically "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 744,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: \"[BUG]\"\nlabels: bug\nassignees: ''\n\n---\n\n**Describe"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 613,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: \"[feature]\"\nlabels: enhancement\nassignees: ''\n\n"
  },
  {
    "path": ".gitignore",
    "chars": 4342,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User"
  },
  {
    "path": "NK2Tray/App.config",
    "chars": 182,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".N"
  },
  {
    "path": "NK2Tray/AudioDevice.cs",
    "chars": 10555,
    "preview": "using NAudio.CoreAudioApi;\nusing NAudio.CoreAudioApi.Interfaces;\nusing System;\nusing System.Collections.Generic;\nusing "
  },
  {
    "path": "NK2Tray/Button.cs",
    "chars": 3597,
    "preview": "using NAudio.Midi;\nusing System;\n\nnamespace NK2Tray\n{\n    public enum ButtonType\n    {\n        MediaPlay,\n        Media"
  },
  {
    "path": "NK2Tray/ConfigSaver.cs",
    "chars": 1623,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "NK2Tray/DevicePathMapper.cs",
    "chars": 1634,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime.InteropService"
  },
  {
    "path": "NK2Tray/EasyControl.cs",
    "chars": 5203,
    "preview": "using NAudio.Midi;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace NK2Tray\n{\n    public class EasyContr"
  },
  {
    "path": "NK2Tray/Fader.cs",
    "chars": 19053,
    "preview": "using NAudio.Midi;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace NK2Tray\n{\n    public c"
  },
  {
    "path": "NK2Tray/FodyWeavers.xml",
    "chars": 176,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Weavers xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSc"
  },
  {
    "path": "NK2Tray/FodyWeavers.xsd",
    "chars": 6616,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n  <!-- This file was gen"
  },
  {
    "path": "NK2Tray/IconExtractor.cs",
    "chars": 723,
    "preview": "using System;\nusing System.Drawing;\nusing System.Runtime.InteropServices;\n\nnamespace NK2Tray\n{\n    public class IconExt"
  },
  {
    "path": "NK2Tray/MediaTools.cs",
    "chars": 1486,
    "preview": "using System;\nusing System.Runtime.InteropServices;\n\nnamespace NK2Tray\n{\n    class MediaTools\n    {\n        [DllImport("
  },
  {
    "path": "NK2Tray/MidiDevice.cs",
    "chars": 8432,
    "preview": "using NAudio.CoreAudioApi;\nusing NAudio.Midi;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;"
  },
  {
    "path": "NK2Tray/MixerSession.cs",
    "chars": 13137,
    "preview": "using NAudio.CoreAudioApi;\nusing NAudio.CoreAudioApi.Interfaces;\nusing System;\nusing System.Collections.Generic;\nusing "
  },
  {
    "path": "NK2Tray/NK2Tray.csproj",
    "chars": 6852,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbui"
  },
  {
    "path": "NK2Tray/NanoKontrol2.cs",
    "chars": 7285,
    "preview": "using NAudio.Midi;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace NK2Tray\n{\n"
  },
  {
    "path": "NK2Tray/OP1.cs",
    "chars": 4614,
    "preview": "using NAudio.Midi;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace NK2Tray\n{\n "
  },
  {
    "path": "NK2Tray/Program.cs",
    "chars": 10321,
    "preview": "using NAudio.CoreAudioApi;\nusing System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Linq;\nusi"
  },
  {
    "path": "NK2Tray/Properties/AssemblyInfo.cs",
    "chars": 1384,
    "preview": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Infor"
  },
  {
    "path": "NK2Tray/Properties/Resources.Designer.cs",
    "chars": 3156,
    "preview": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code w"
  },
  {
    "path": "NK2Tray/Properties/Resources.resx",
    "chars": 6064,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The prim"
  },
  {
    "path": "NK2Tray/Properties/Settings.Designer.cs",
    "chars": 1125,
    "preview": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code w"
  },
  {
    "path": "NK2Tray/Properties/Settings.settings",
    "chars": 240,
    "preview": "<?xml version='1.0' encoding='utf-8'?>\n<SettingsFile xmlns=\"http://schemas.microsoft.com/VisualStudio/2004/01/settings\""
  },
  {
    "path": "NK2Tray/WindowTools.cs",
    "chars": 2806,
    "preview": "using System;\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\n\nnamespace NK2Tray\n{\n    class WindowTool"
  },
  {
    "path": "NK2Tray/XtouchMini.cs",
    "chars": 5973,
    "preview": "using NAudio.Midi;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Collections;\n\nnamesp"
  },
  {
    "path": "NK2Tray/packages.config",
    "chars": 296,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Costura.Fody\" version=\"3.3.0\" targetFramework=\"net461\""
  },
  {
    "path": "NK2Tray.sln",
    "chars": 1092,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 15\nVisualStudioVersion = 15.0.28307.168\nMi"
  },
  {
    "path": "NK2Tray_Display/BOM.md",
    "chars": 1110,
    "preview": "## Bill of materials for the NK2Tray_Display\n\n\n\n### Printed Parts:\n\n 1x  \tNK2DisplayCaseA.stl\n\n 1x \tNK2DisplayCaseB.stl\n"
  },
  {
    "path": "NK2Tray_Display/NK2Tray_DisplayFW.ino",
    "chars": 22536,
    "preview": "// Display Firmware V2\n// I2C Adressen:\n// Oled: 0x3C\n// Multi:0x70\n\n#include \"Wire.h\"               // Hmmm?\n#include \""
  },
  {
    "path": "NK2Tray_Display/NK2Tray_DisplayFW_V2/NK2Tray_DisplayFW_V2.ino",
    "chars": 9649,
    "preview": "// Display Firmware V2, made by piLo, tweaked by ZeeuwsGamertje\r\n// I2C Adressen:\r\n// Oled: 0x78\r\n// Multi:0x70\r\n\r\n// Di"
  },
  {
    "path": "NK2Tray_Display/NK2Tray_DisplayFW_V2/eeprom.ino",
    "chars": 2179,
    "preview": "// function to save our variables into EEPROM\r\n// Writes OK to 500 & 501 - use this to verify a write\r\nbool saveSettings"
  },
  {
    "path": "NK2Tray_Display/NK2Tray_DisplayFW_V2/logos.h",
    "chars": 74595,
    "preview": "// Anzuzeigende Bilder:\r\n\r\n// Windows Logo\r\nconst unsigned char windowsLogo [] = {\r\n0x00, 0x00, 0x00, 0x00, 0x00, 0x00, "
  },
  {
    "path": "NK2Tray_Display/NK2Tray_DisplayFW_V2/readme.md",
    "chars": 1644,
    "preview": "# NK2Tray_Display Firmware Upgrade\r\n\r\n### Almost complete rewrite of the firmware, addressing some points by chaosgabe\r\n"
  },
  {
    "path": "NK2Tray_Display/NK2Tray_DisplayFW_V2/webServer.ino",
    "chars": 10197,
    "preview": "/*\r\n\r\nESP8266_ArtNetNode_DMX - webServer.ino\r\nCopyright (c) 2015, Matthew Tong    \r\nhttps://github.com/mtongnz/ESP8266_A"
  },
  {
    "path": "NK2Tray_Display/Readme.md",
    "chars": 1632,
    "preview": "<img src=\"https://media.discordapp.net/attachments/532233406482743300/653946714561970176/IMG_20190927_114511.jpg?width=8"
  },
  {
    "path": "README.md",
    "chars": 4112,
    "preview": "# NOTE: The group of developers for NK2Tray are largely inactive. <br> A rewrite in a language more familiar to the orig"
  },
  {
    "path": "license.txt",
    "chars": 2630,
    "preview": "Microsoft Public License (Ms-PL)\n\nThis license governs use of the accompanying software. If you use the software, you ac"
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the ho0ber/NK2Tray GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 43 files (256.0 KB), approximately 112.7k tokens, and a symbol index with 150 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!