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 ================================================  ================================================ 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 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 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 GetMixerSessions() { var mixerSessions = new List(); var sessionsByIdent = new Dictionary>(); 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(); 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(); 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 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 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(); 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