From 83fc9f632c2a9c79f018e197387d5b6392725ee1 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 23 May 2023 20:53:56 +0200 Subject: [PATCH] =?UTF-8?q?Initial=20Commit=20f=C3=BCr=20Launchpad=20Code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Launchpad PasswortMaster.sln | 28 + Launchpad PasswortMaster/App.config | 6 + .../Launchpad PasswortMaster.csproj | 66 + Launchpad PasswortMaster/Program.cs | 36 + .../Properties/AssemblyInfo.cs | 36 + launchpad-dot-net-master/.gitattributes | 17 + launchpad-dot-net-master/.gitignore | 47 + launchpad-dot-net-master/README.md | 18 + .../launchpad-dot-net-master.zip | Bin 0 -> 60353 bytes .../launchpad-dot-net.sln | 22 + .../launchpad-dot-net/Interface.cs | 374 +++++ .../Properties/AssemblyInfo.cs | 36 + .../launchpad-dot-net.csproj | 58 + launchpad-master/.gitattributes | 22 + launchpad-master/.gitignore | 120 ++ launchpad-master/IntelOrca.Launchpad.sln | 26 + .../IntelOrca.Launchpad/App.config | 6 + .../ButtonPressEventArgs.cs | 56 + .../IntelOrca.Launchpad.csproj | 62 + .../IntelOrca.Launchpad/LaunchpadButton.cs | 82 + .../IntelOrca.Launchpad/LaunchpadDevice.cs | 181 +++ .../IntelOrca.Launchpad/LaunchpadException.cs | 10 + .../Properties/AssemblyInfo.cs | 36 + .../IntelOrca.Launchpad/packages.config | 4 + .../IntelOrca.LaunchpadTests/App.config | 6 + .../IntelOrca.LaunchpadTests/Bulldog.cs | 158 ++ .../GeometrischeTests.cs | 1395 +++++++++++++++++ .../IntelOrca.LaunchpadTests.csproj | 75 + .../IntelOrca.LaunchpadTests/PWGenerator.cs | 1261 +++++++++++++++ .../IntelOrca.LaunchpadTests/Program.cs | 90 ++ .../Properties/AssemblyInfo.cs | 36 + .../IntelOrca.LaunchpadTests/RainSequencer.cs | 266 ++++ .../IntelOrca.LaunchpadTests/Reversi.cs | 279 ++++ .../ScrollingLetters.cs | 177 +++ .../IntelOrca.LaunchpadTests/Snake.cs | 233 +++ .../IntelOrca.LaunchpadTests/ToggleGrid.cs | 60 + .../IntelOrca.LaunchpadTests/packages.config | 4 + .../IntelOrca.LaunchpadTests/text.txt | 293 ++++ launchpad-master/launchpad-master.zip | Bin 0 -> 22112 bytes launchpad-master/readme.md | 8 + 40 files changed, 5690 insertions(+) create mode 100644 Launchpad PasswortMaster.sln create mode 100644 Launchpad PasswortMaster/App.config create mode 100644 Launchpad PasswortMaster/Launchpad PasswortMaster.csproj create mode 100644 Launchpad PasswortMaster/Program.cs create mode 100644 Launchpad PasswortMaster/Properties/AssemblyInfo.cs create mode 100644 launchpad-dot-net-master/.gitattributes create mode 100644 launchpad-dot-net-master/.gitignore create mode 100644 launchpad-dot-net-master/README.md create mode 100644 launchpad-dot-net-master/launchpad-dot-net-master.zip create mode 100644 launchpad-dot-net-master/launchpad-dot-net.sln create mode 100644 launchpad-dot-net-master/launchpad-dot-net/Interface.cs create mode 100644 launchpad-dot-net-master/launchpad-dot-net/Properties/AssemblyInfo.cs create mode 100644 launchpad-dot-net-master/launchpad-dot-net/launchpad-dot-net.csproj create mode 100644 launchpad-master/.gitattributes create mode 100644 launchpad-master/.gitignore create mode 100644 launchpad-master/IntelOrca.Launchpad.sln create mode 100644 launchpad-master/IntelOrca.Launchpad/App.config create mode 100644 launchpad-master/IntelOrca.Launchpad/ButtonPressEventArgs.cs create mode 100644 launchpad-master/IntelOrca.Launchpad/IntelOrca.Launchpad.csproj create mode 100644 launchpad-master/IntelOrca.Launchpad/LaunchpadButton.cs create mode 100644 launchpad-master/IntelOrca.Launchpad/LaunchpadDevice.cs create mode 100644 launchpad-master/IntelOrca.Launchpad/LaunchpadException.cs create mode 100644 launchpad-master/IntelOrca.Launchpad/Properties/AssemblyInfo.cs create mode 100644 launchpad-master/IntelOrca.Launchpad/packages.config create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/App.config create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/Bulldog.cs create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/GeometrischeTests.cs create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/IntelOrca.LaunchpadTests.csproj create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/PWGenerator.cs create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/Program.cs create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/Properties/AssemblyInfo.cs create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/RainSequencer.cs create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/Reversi.cs create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/ScrollingLetters.cs create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/Snake.cs create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/ToggleGrid.cs create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/packages.config create mode 100644 launchpad-master/IntelOrca.LaunchpadTests/text.txt create mode 100644 launchpad-master/launchpad-master.zip create mode 100644 launchpad-master/readme.md diff --git a/Launchpad PasswortMaster.sln b/Launchpad PasswortMaster.sln new file mode 100644 index 0000000..3adb674 --- /dev/null +++ b/Launchpad PasswortMaster.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Launchpad PasswortMaster", "Launchpad PasswortMaster\Launchpad PasswortMaster.csproj", "{7E3C8B4B-791D-4AAB-982B-858952CAF172}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "launchpad-dot-net", "..\..\Launchpad Coding Tests\launchpad-dot-net-master\launchpad-dot-net\launchpad-dot-net.csproj", "{2784C4AF-3B96-471B-91B0-1A11C342D375}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7E3C8B4B-791D-4AAB-982B-858952CAF172}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E3C8B4B-791D-4AAB-982B-858952CAF172}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E3C8B4B-791D-4AAB-982B-858952CAF172}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E3C8B4B-791D-4AAB-982B-858952CAF172}.Release|Any CPU.Build.0 = Release|Any CPU + {2784C4AF-3B96-471B-91B0-1A11C342D375}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2784C4AF-3B96-471B-91B0-1A11C342D375}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2784C4AF-3B96-471B-91B0-1A11C342D375}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2784C4AF-3B96-471B-91B0-1A11C342D375}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Launchpad PasswortMaster/App.config b/Launchpad PasswortMaster/App.config new file mode 100644 index 0000000..d740e88 --- /dev/null +++ b/Launchpad PasswortMaster/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Launchpad PasswortMaster/Launchpad PasswortMaster.csproj b/Launchpad PasswortMaster/Launchpad PasswortMaster.csproj new file mode 100644 index 0000000..9871798 --- /dev/null +++ b/Launchpad PasswortMaster/Launchpad PasswortMaster.csproj @@ -0,0 +1,66 @@ + + + + + Debug + AnyCPU + {7E3C8B4B-791D-4AAB-982B-858952CAF172} + Exe + Properties + Launchpad_PasswortMaster + Launchpad PasswortMaster + v4.5.2 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + {2784c4af-3b96-471b-91b0-1a11c342d375} + launchpad-dot-net + + + + + \ No newline at end of file diff --git a/Launchpad PasswortMaster/Program.cs b/Launchpad PasswortMaster/Program.cs new file mode 100644 index 0000000..5f2121e --- /dev/null +++ b/Launchpad PasswortMaster/Program.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using LaunchpadNET; + +namespace Launchpad_PasswortMaster +{ + class Program + { + static void Main(string[] args) + { + Interface LaunchInterface = new Interface(); + LaunchpadNET.Interface.LaunchpadDevice Device = new Interface.LaunchpadDevice("Meins"); + + LaunchInterface.connect(LaunchInterface.getConnectedLaunchpads()[0]); //Connects with your Launchpad + + LaunchInterface.clearAllLEDs(); + int veloCount = 1; + //for (int indexerX = 0; indexerX < 8; indexerX++) + //{ + //for (int indexerY = 0; indexerY < 8; indexerY++) + //{ + LaunchInterface.setLED(3,0, 1); + //} + + //} + + + + Console.ReadLine(); + LaunchInterface.clearAllLEDs(); + } + } +} diff --git a/Launchpad PasswortMaster/Properties/AssemblyInfo.cs b/Launchpad PasswortMaster/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..efbf39b --- /dev/null +++ b/Launchpad PasswortMaster/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("Launchpad PasswortMaster")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Orgname")] +[assembly: AssemblyProduct("Launchpad PasswortMaster")] +[assembly: AssemblyCopyright("Copyright © Orgname 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("7e3c8b4b-791d-4aab-982b-858952caf172")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/launchpad-dot-net-master/.gitattributes b/launchpad-dot-net-master/.gitattributes new file mode 100644 index 0000000..bdb0cab --- /dev/null +++ b/launchpad-dot-net-master/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.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 diff --git a/launchpad-dot-net-master/.gitignore b/launchpad-dot-net-master/.gitignore new file mode 100644 index 0000000..cd2946a --- /dev/null +++ b/launchpad-dot-net-master/.gitignore @@ -0,0 +1,47 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/launchpad-dot-net-master/README.md b/launchpad-dot-net-master/README.md new file mode 100644 index 0000000..729a626 --- /dev/null +++ b/launchpad-dot-net-master/README.md @@ -0,0 +1,18 @@ +# Launchpad.NET +*The easy way to create applications for the NOVATION Launchpad* + +**Welcome to LAUNCHPAD.NET** +This is a simple C# library which allows you to interact with the NOVATION LAUNCHPAD. +You can view the GitHub Wiki over here, in order to learn how the functions work together. +This is only possible because of **jstnryan** and his https://github.com/jstnryan/midi-dot-net + +**Features** +This comes with a few features right now, but I will extend this in the future! + + Automatic MIDI Device sorting (you only see Launchpads, not other MIDI devices + + Easy LED state setting (set LEDs only giving coordinates and velocity) + + LED rect filling (with start and end coordinates) + + Easy handling of Launchpads with a own class for Launchpad + + Easy connecting / disconnecting + +**Bugs?** +No problem, thats why GitHub is here. :joy: diff --git a/launchpad-dot-net-master/launchpad-dot-net-master.zip b/launchpad-dot-net-master/launchpad-dot-net-master.zip new file mode 100644 index 0000000000000000000000000000000000000000..775abe8299c24f7b7a5707ac3ea61cd46fe99b43 GIT binary patch literal 60353 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

Po0hoAP&8r>}I9>nt_8ZB-cN6)jcYrJoYF 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 MI{FNHtgNj6A6SV9_y7O^ literal 0 HcmV?d00001 diff --git a/launchpad-dot-net-master/launchpad-dot-net.sln b/launchpad-dot-net-master/launchpad-dot-net.sln new file mode 100644 index 0000000..3a0d645 --- /dev/null +++ b/launchpad-dot-net-master/launchpad-dot-net.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 14 for Windows Desktop +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "launchpad-dot-net", "launchpad-dot-net\launchpad-dot-net.csproj", "{2784C4AF-3B96-471B-91B0-1A11C342D375}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2784C4AF-3B96-471B-91B0-1A11C342D375}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2784C4AF-3B96-471B-91B0-1A11C342D375}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2784C4AF-3B96-471B-91B0-1A11C342D375}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2784C4AF-3B96-471B-91B0-1A11C342D375}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/launchpad-dot-net-master/launchpad-dot-net/Interface.cs b/launchpad-dot-net-master/launchpad-dot-net/Interface.cs new file mode 100644 index 0000000..6e2718c --- /dev/null +++ b/launchpad-dot-net-master/launchpad-dot-net/Interface.cs @@ -0,0 +1,374 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Midi; + +namespace LaunchpadNET +{ + public class Interface + { + private Pitch[,] notes = new Pitch[8, 8] { + { Pitch.A5, Pitch.ASharp5, Pitch.B5, Pitch.C6, Pitch.CSharp6, Pitch.D6, Pitch.DSharp6, Pitch.E6 }, + { Pitch.B4, Pitch.C5, Pitch.CSharp5, Pitch.D5, Pitch.DSharp5, Pitch.E5, Pitch.F5, Pitch.FSharp5 }, + { Pitch.CSharp4, Pitch.D4, Pitch.DSharp4, Pitch.E4, Pitch.F4, Pitch.FSharp4, Pitch.G4, Pitch.GSharp4 }, + { Pitch.DSharp3, Pitch.E3, Pitch.F3, Pitch.FSharp3, Pitch.G3, Pitch.GSharp3, Pitch.A3, Pitch.ASharp3 }, + { Pitch.F2, Pitch.FSharp2, Pitch.G2, Pitch.GSharp2, Pitch.A2, Pitch.ASharp2, Pitch.B2, Pitch.C3 }, + { Pitch.G1, Pitch.GSharp1, Pitch.A1, Pitch.ASharp1, Pitch.B1, Pitch.C2, Pitch.CSharp2, Pitch.D2 }, + { Pitch.A0, Pitch.ASharp0, Pitch.B0, Pitch.C1, Pitch.CSharp1, Pitch.D1, Pitch.DSharp1, Pitch.E1 }, + { Pitch.BNeg1, Pitch.C0, Pitch.CSharp0, Pitch.D0, Pitch.DSharp0, Pitch.E0, Pitch.F0, Pitch.FSharp0 } + }; + + private Pitch[] rightLEDnotes = new Pitch[] { + Pitch.F6, Pitch.G5, Pitch.A4, Pitch.B3, Pitch.CSharp3, Pitch.DSharp2, Pitch.F1, Pitch.G0 + }; + + public InputDevice targetInput; + public OutputDevice targetOutput; + + public delegate void LaunchpadKeyEventHandler(object source, LaunchpadKeyEventArgs e); + + public delegate void LaunchpadCCKeyEventHandler(object source, LaunchpadCCKeyEventArgs e); + + ///

+ /// Event Handler when a Launchpad Key is pressed. + /// + public event LaunchpadKeyEventHandler OnLaunchpadKeyPressed; + public event LaunchpadCCKeyEventHandler OnLaunchpadCCKeyPressed; + + public class LaunchpadCCKeyEventArgs : EventArgs + { + private int val; + public LaunchpadCCKeyEventArgs(int _val) + { + val = _val; + } + public int GetVal() + { + return val; + } + } + + /// + /// EventArgs for pressed Launchpad Key + /// + public class LaunchpadKeyEventArgs : EventArgs + { + private int x; + private int y; + public LaunchpadKeyEventArgs(int _pX, int _pY) + { + x = _pX; + y = _pY; + } + public int GetX() + { + return x; + } + public int GetY() + { + return y; + } + } + + /// + /// Creates a text scroll. + /// + /// + /// + /// + /// + public void createTextScroll(string text, int speed, bool looping, int velo) + { + byte[] sysexHeader = { 240, 00, 32, 41, 2, 4 }; + byte[] sysexStop = { 247 }; + byte operation = 20; + + byte _velocity = (byte)velo; + byte _speed = (byte)speed; + byte _loop = Convert.ToByte(looping); + byte[] _text = { }; + + byte[] finalArgs = { operation, _velocity, _loop, _speed }; + + List charList = new List(); + foreach(char c in text) + { + int unicode = c; + if (unicode < 128) + charList.Add(Convert.ToByte(unicode)); + } + _text = charList.ToArray(); + + byte[] finalBytes = sysexHeader.Concat(finalArgs.Concat(_text.Concat(sysexStop))).ToArray(); + + targetOutput.SendSysEx(finalBytes); + } + + public void stopLoopingTextScroll() + { + byte[] stop = { 240, 0, 32, 41, 2, 24, 20, 247 }; + targetOutput.SendSysEx(stop); + } + + private void sysExAnswer(SysExMessage m) + { + byte[] msg = m.Data; + byte[] stopBytes = { 240, 0, 32, 41, 2, 24, 21, 247 }; + } + + private void midiPress(Midi.NoteOnMessage msg) + { + if (OnLaunchpadKeyPressed != null && !rightLEDnotes.Contains(msg.Pitch)) + { + OnLaunchpadKeyPressed(this, new LaunchpadKeyEventArgs(midiNoteToLed(msg.Pitch)[0], midiNoteToLed(msg.Pitch)[1])); + } + else if (OnLaunchpadKeyPressed != null && rightLEDnotes.Contains(msg.Pitch)) + { + OnLaunchpadCCKeyPressed(this, new LaunchpadCCKeyEventArgs(midiNoteToSideLED(msg.Pitch))); + } + } + + public int midiNoteToSideLED(Pitch p) + { + for (int y = 0; y <= 7; y++) + { + if (rightLEDnotes[y] == p) + { + return y; + } + } + return 0; + } + + /// + /// Returns the LED coordinates of a MIdi note + /// + /// The Midi Note. + /// The X,Y coordinates. + public int[] midiNoteToLed(Pitch p) + { + for (int x = 0; x <= 7; x++) + { + for (int y = 0; y <= 7; y++) + { + if (notes[x,y] == p) + { + int[] r1 = { x, y }; + return r1; + } + } + } + int[] r2 = { 0, 0 }; + return r2; + } + + /// + /// Returns the equilavent Midi Note to X and Y coordinates. + /// + /// The X coordinate of the LED + /// The Y coordinate of the LED + /// The midi note + public Pitch ledToMidiNote(int x, int y) + { + return notes[x, y]; + } + + public void clearAllLEDs() + { + for (int x = 0; x < 8; x++) + { + for (int y = 0; y < 8; y++) + { + setLED(x, y, 0); + } + } + + for (int ry = 0; ry < 8; ry++) + { + setSideLED(ry, 0); + } + + for (int tx = 1; tx < 9; tx++) + { + setTopLEDs(tx, 0); + } + } + + /// + /// Fills Top Row LEDs. + /// + /// + /// + /// + public void fillTopLEDs(int startX, int endX, int velo) + { + for (int x = 1; x < 9; x++) + { + if (x >= startX && x <= endX) + { + setTopLEDs(x, velo); + } + } + } + + /// + /// Fills a region of Side LEDs. + /// + /// + /// + /// + public void fillSideLEDs(int startY, int endY, int velo) + { + for (int y = 0; y < rightLEDnotes.Length; y++) + { + if (y >= startY && y <= endY) + { + setSideLED(y, velo); + } + } + } + + /// + /// Creates a rectangular mesh of LEDs. + /// + /// Start X coordinate + /// Start Y coordinate + /// End X coordinate + /// End Y coordinate + /// Painting velocity + public void fillLEDs(int startX, int startY, int endX, int endY, int velo) + { + for (int x = 0; x < notes.Length; x++) + { + for (int y = 0; y < notes.Length; y++) + { + if (x >= startX && y >= startY && x <= endX && y <= endY) + setLED(x, y, velo); + } + } + } + + /// + /// Sets a Top LED of the launchpad + /// + /// + /// + public void setTopLEDs(int x, int velo) + { + byte[] data = { 240, 0, 32, 41, 2, 24, 10, Convert.ToByte(103+x), Convert.ToByte(velo), 247 }; + targetOutput.SendSysEx(data); + } + + /// + /// Sets a Side LED of the Launchpad. + /// + /// The height of the right Side LED. + /// Velocity index. + public void setSideLED(int y, int velo) + { + targetOutput.SendNoteOn(Channel.Channel1, rightLEDnotes[y], velo); + } + + /// + /// Sets a LED of the Launchpad. + /// + /// The X coordinate. + /// The Y coordinate. + /// The velocity. + public void setLED(int x, int y, int velo) + { + try + { + targetOutput.SendNoteOn(Channel.Channel1, notes[x, y], velo); + } + catch (Midi.DeviceException) + { + Console.WriteLine("<< LAUNCHPAD.NET >> Midi.DeviceException"); + throw; + } + } + + /// + /// Returns all connected and installed Launchpads. + /// + /// Returns LaunchpadDevice array. + public LaunchpadDevice[] getConnectedLaunchpads() + { + List tempDevices = new List(); + + foreach (InputDevice id in Midi.InputDevice.InstalledDevices) + { + foreach (OutputDevice od in Midi.OutputDevice.InstalledDevices) + { + if (id.Name == od.Name) + { + if (id.Name.ToLower().Contains("launchpad")) + { + tempDevices.Add(new LaunchpadDevice(id.Name)); + } + } + } + } + + return tempDevices.ToArray(); + } + + /// + /// Function to connect with a LaunchpadDevice + /// + /// The Launchpad to connect to. + /// Returns bool if connection was successful. + public bool connect(LaunchpadDevice device) + { + foreach(InputDevice id in Midi.InputDevice.InstalledDevices) + { + if (id.Name.ToLower() == device._midiName.ToLower()) + { + targetInput = id; + id.Open(); + targetInput.NoteOn += new InputDevice.NoteOnHandler(midiPress); + targetInput.StartReceiving(null); + } + } + foreach (OutputDevice od in Midi.OutputDevice.InstalledDevices) + { + if (od.Name.ToLower() == device._midiName.ToLower()) + { + targetOutput = od; + od.Open(); + } + } + + return true; // targetInput.IsOpen && targetOutput.IsOpen; + } + + /// + /// Disconnects a given LaunchpadDevice + /// + /// The Launchpad to disconnect. + /// Returns bool if disconnection was successful. + public bool disconnect(LaunchpadDevice device) + { + if (targetInput.IsOpen && targetOutput.IsOpen) + { + targetInput.StopReceiving(); + targetInput.Close(); + targetOutput.Close(); + } + return !targetInput.IsOpen && !targetOutput.IsOpen; + } + + public class LaunchpadDevice + { + public string _midiName; + //public int _midiDeviceId; + + public LaunchpadDevice(string name) + { + _midiName = name; + } + } + } +} diff --git a/launchpad-dot-net-master/launchpad-dot-net/Properties/AssemblyInfo.cs b/launchpad-dot-net-master/launchpad-dot-net/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..031cc01 --- /dev/null +++ b/launchpad-dot-net-master/launchpad-dot-net/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("launchpad-dot-net")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("launchpad-dot-net")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(true)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("2784c4af-3b96-471b-91b0-1a11c342d375")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/launchpad-dot-net-master/launchpad-dot-net/launchpad-dot-net.csproj b/launchpad-dot-net-master/launchpad-dot-net/launchpad-dot-net.csproj new file mode 100644 index 0000000..1074635 --- /dev/null +++ b/launchpad-dot-net-master/launchpad-dot-net/launchpad-dot-net.csproj @@ -0,0 +1,58 @@ + + + + + Debug + AnyCPU + {2784C4AF-3B96-471B-91B0-1A11C342D375} + Library + Properties + launchpad_dot_net + launchpad-dot-net + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + bin\Debug\Midi.dll + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/launchpad-master/.gitattributes b/launchpad-master/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/launchpad-master/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.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 diff --git a/launchpad-master/.gitignore b/launchpad-master/.gitignore new file mode 100644 index 0000000..5fc9ea3 --- /dev/null +++ b/launchpad-master/.gitignore @@ -0,0 +1,120 @@ +# App specific + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +Icon? +ehthumbs.db +Thumbs.db + +# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) +[Bb]in/ +[Oo]bj/ + +# mstest test results +TestResults + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +.vs/ +*.suo +*.user +*.sln.docstates +*.userprefs + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.log +*.vspscc +*.vssscc +.builds + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper* + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# 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 +*.Publish.xml + +# NuGet Packages Directory +packages + +# Windows Azure Build Output +csx +*.build.csdef + +# Others +[Bb]in +[Oo]bj +sql +TestResults +[Tt]est[Rr]esult* +*.Cache +ClientBin +[Ss]tyle[Cc]op.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# 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 \ No newline at end of file diff --git a/launchpad-master/IntelOrca.Launchpad.sln b/launchpad-master/IntelOrca.Launchpad.sln new file mode 100644 index 0000000..4c42cf2 --- /dev/null +++ b/launchpad-master/IntelOrca.Launchpad.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntelOrca.Launchpad", "IntelOrca.Launchpad\IntelOrca.Launchpad.csproj", "{FCF77754-C985-4F3D-BE0B-E14011C5DC5B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntelOrca.LaunchpadTests", "IntelOrca.LaunchpadTests\IntelOrca.LaunchpadTests.csproj", "{C2C7FD5A-D56C-4C74-91E3-2D9F5D92FCA3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FCF77754-C985-4F3D-BE0B-E14011C5DC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FCF77754-C985-4F3D-BE0B-E14011C5DC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FCF77754-C985-4F3D-BE0B-E14011C5DC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FCF77754-C985-4F3D-BE0B-E14011C5DC5B}.Release|Any CPU.Build.0 = Release|Any CPU + {C2C7FD5A-D56C-4C74-91E3-2D9F5D92FCA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2C7FD5A-D56C-4C74-91E3-2D9F5D92FCA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2C7FD5A-D56C-4C74-91E3-2D9F5D92FCA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2C7FD5A-D56C-4C74-91E3-2D9F5D92FCA3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/launchpad-master/IntelOrca.Launchpad/App.config b/launchpad-master/IntelOrca.Launchpad/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/launchpad-master/IntelOrca.Launchpad/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/launchpad-master/IntelOrca.Launchpad/ButtonPressEventArgs.cs b/launchpad-master/IntelOrca.Launchpad/ButtonPressEventArgs.cs new file mode 100644 index 0000000..a8c15f6 --- /dev/null +++ b/launchpad-master/IntelOrca.Launchpad/ButtonPressEventArgs.cs @@ -0,0 +1,56 @@ +using System; + +namespace IntelOrca.Launchpad +{ + public class ButtonPressEventArgs : EventArgs + { + private ButtonType mType; + private ToolbarButton mToolbarButton; + private SideButton mSidebarButton; + private int mX, mY; + + public ButtonPressEventArgs(ToolbarButton toolbarButton) + { + mType = ButtonType.Toolbar; + mToolbarButton = toolbarButton; + } + + public ButtonPressEventArgs(SideButton sideButton) + { + mType = ButtonType.Side; + mSidebarButton = sideButton; + } + + public ButtonPressEventArgs(int x, int y) + { + mType = ButtonType.Grid; + mX = x; + mY = y; + } + + public ButtonType Type + { + get { return mType; } + } + + public ToolbarButton ToolbarButton + { + get { return mToolbarButton; } + } + + public SideButton SidebarButton + { + get { return mSidebarButton; } + } + + public int X + { + get { return mX; } + } + + public int Y + { + get { return mY; } + } + } +} diff --git a/launchpad-master/IntelOrca.Launchpad/IntelOrca.Launchpad.csproj b/launchpad-master/IntelOrca.Launchpad/IntelOrca.Launchpad.csproj new file mode 100644 index 0000000..36ee93f --- /dev/null +++ b/launchpad-master/IntelOrca.Launchpad/IntelOrca.Launchpad.csproj @@ -0,0 +1,62 @@ + + + + + Debug + AnyCPU + {FCF77754-C985-4F3D-BE0B-E14011C5DC5B} + Library + Properties + IntelOrca.Launchpad + IntelOrca.Launchpad + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + ..\packages\midi-dot-net.1.1.0\lib\net35\Midi.dll + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/launchpad-master/IntelOrca.Launchpad/LaunchpadButton.cs b/launchpad-master/IntelOrca.Launchpad/LaunchpadButton.cs new file mode 100644 index 0000000..86f804d --- /dev/null +++ b/launchpad-master/IntelOrca.Launchpad/LaunchpadButton.cs @@ -0,0 +1,82 @@ +using Midi; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IntelOrca.Launchpad +{ + public enum ButtonType { Grid, Toolbar, Side } + public enum ButtonBrightness { Off, Low, Medium, Full }; + public enum ButtonPressState { Up = 0, Down = 127 }; + + public class LaunchpadButton + { + private LaunchpadDevice mLaunchpadDevice; + private ButtonBrightness mRedBrightness, mGreenBrightness; + private ButtonPressState mState; + + private ButtonType mType; + private int mIndex; + + internal LaunchpadButton(LaunchpadDevice launchpadDevice, ButtonType type, int index) + { + mLaunchpadDevice = launchpadDevice; + mType = type; + mIndex = index; + } + + public void TurnOnLight() + { + SetBrightness(ButtonBrightness.Full, ButtonBrightness.Full); + } + + public void TurnOffLight() + { + SetBrightness(ButtonBrightness.Off, ButtonBrightness.Off); + } + + public void SetBrightness(ButtonBrightness red, ButtonBrightness green) + { + if (mRedBrightness == red && mGreenBrightness == green) + return; + + mRedBrightness = red; + mGreenBrightness = green; + + int vel = ((int)mGreenBrightness << 4) | (int)mRedBrightness; + + if (!mLaunchpadDevice.DoubleBuffered) + vel |= 12; + + SetLED(vel); + } + + private void SetLED(int value) + { + if (mType == ButtonType.Toolbar) + mLaunchpadDevice.OutputDevice.SendControlChange(Channel.Channel1, (Control)mIndex, value); + else + mLaunchpadDevice.OutputDevice.SendNoteOn(Channel.Channel1, (Pitch)mIndex, value); + } + + public ButtonBrightness RedBrightness + { + get { return mRedBrightness; } + internal set { mRedBrightness = value; } + } + + public ButtonBrightness GreenBrightness + { + get { return mGreenBrightness; } + internal set { mGreenBrightness = value; } + } + + public ButtonPressState State + { + get { return mState; } + internal set { mState = value; } + } + } +} diff --git a/launchpad-master/IntelOrca.Launchpad/LaunchpadDevice.cs b/launchpad-master/IntelOrca.Launchpad/LaunchpadDevice.cs new file mode 100644 index 0000000..65d6ae9 --- /dev/null +++ b/launchpad-master/IntelOrca.Launchpad/LaunchpadDevice.cs @@ -0,0 +1,181 @@ +using Midi; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace IntelOrca.Launchpad +{ + public enum ToolbarButton { Up, Down, Left, Right, Session, User1, User2, Mixer } + public enum SideButton { Volume, Pan, SoundA, SoundB, Stop, TrackOn, Solo, Arm } + + public class LaunchpadDevice + { + private InputDevice mInputDevice; + private OutputDevice mOutputDevice; + + private bool mDoubleBuffered; + private bool mDoubleBufferedState; + + private readonly LaunchpadButton[] mToolbar = new LaunchpadButton[8]; + private readonly LaunchpadButton[] mSide = new LaunchpadButton[8]; + private readonly LaunchpadButton[,] mGrid = new LaunchpadButton[8, 8]; + + public event EventHandler ButtonPressed; + + public LaunchpadDevice() : this(0) { } + + public LaunchpadDevice(int index) + { + InitialiseButtons(); + + int i = 0; + mInputDevice = InputDevice.InstalledDevices.Where(x => x.Name.Contains("Launchpad")). + FirstOrDefault(x => i++ == index); + i = 0; + mOutputDevice = OutputDevice.InstalledDevices.Where(x => x.Name.Contains("Launchpad")). + FirstOrDefault(x => i++ == index); + + if (mInputDevice == null) + throw new LaunchpadException("Unable to find input device."); + if (mOutputDevice == null) + throw new LaunchpadException("Unable to find output device."); + + mInputDevice.Open(); + mOutputDevice.Open(); + + mInputDevice.StartReceiving(new Clock(120)); + mInputDevice.NoteOn += mInputDevice_NoteOn; + mInputDevice.ControlChange += mInputDevice_ControlChange; + + Reset(); + } + + + private void InitialiseButtons() + { + for (int i = 0; i < 8; i++) + { + mToolbar[i] = new LaunchpadButton(this, ButtonType.Toolbar, 104 + i); + mSide[i] = new LaunchpadButton(this, ButtonType.Side, i * 16 + 8); + } + + for (int y = 0; y < 8; y++) + for (int x = 0; x < 8; x++) + mGrid[x, y] = new LaunchpadButton(this, ButtonType.Grid, y * 16 + x); + } + + private void StartDoubleBuffering() + { + mDoubleBuffered = true; + mDoubleBufferedState = false; + mOutputDevice.SendControlChange(Channel.Channel1, (Control)0, 32 | 16 | 1); + } + + public void Refresh() + { + if (!mDoubleBufferedState) + mOutputDevice.SendControlChange(Channel.Channel1, (Control)0, 32 | 16 | 4); + else + mOutputDevice.SendControlChange(Channel.Channel1, (Control)0, 32 | 16 | 1); + mDoubleBufferedState = !mDoubleBufferedState; + } + + private void EndDoubleBuffering() + { + mOutputDevice.SendControlChange(Channel.Channel1, (Control)0, 32 | 16); + mDoubleBuffered = false; + } + + public void Reset() + { + mOutputDevice.SendControlChange(Channel.Channel1, (Control)0, 0); + Buttons.ToList().ForEach(x => x.RedBrightness = x.GreenBrightness = ButtonBrightness.Off); + } + + private void mInputDevice_NoteOn(NoteOnMessage msg) + { + LaunchpadButton button = GetButton(msg.Pitch); + if (button == null) + return; + + button.State = (ButtonPressState)msg.Velocity; + + if (ButtonPressed != null && button.State == ButtonPressState.Down) + { + if ((int)msg.Pitch % 16 == 8) + ButtonPressed.Invoke(this, new ButtonPressEventArgs((SideButton)((int)msg.Pitch / 16))); + else + ButtonPressed.Invoke(this, new ButtonPressEventArgs((int)msg.Pitch % 16, (int)msg.Pitch / 16)); + } + } + + private void mInputDevice_ControlChange(ControlChangeMessage msg) + { + ToolbarButton toolbarButton = (ToolbarButton)((int)msg.Control - 104); + + LaunchpadButton button = GetButton(toolbarButton); + if (button == null) + return; + + button.State = (ButtonPressState)msg.Value; + if (ButtonPressed != null && button.State == ButtonPressState.Down) + { + ButtonPressed.Invoke(this, new ButtonPressEventArgs(toolbarButton)); + } + } + + public LaunchpadButton GetButton(ToolbarButton toolbarButton) + { + return mToolbar[(int)toolbarButton]; + } + + public LaunchpadButton GetButton(SideButton sideButton) + { + return mSide[(int)sideButton]; + } + + private LaunchpadButton GetButton(Pitch pitch) + { + int x = (int)pitch % 16; + int y = (int)pitch / 16; + if (x < 8 && y < 8) + return mGrid[x, y]; + else if (x == 8 && y < 8) + return mSide[y]; + + return null; + } + + public bool DoubleBuffered + { + get { return mDoubleBuffered; } + set + { + if (mDoubleBuffered) + EndDoubleBuffering(); + else + StartDoubleBuffering(); + } + } + + public LaunchpadButton this[int x, int y] + { + get { return mGrid[x, y]; } + } + + public IEnumerable Buttons + { + get + { + for (int y = 0; y < 8; y++) + for (int x = 0; x < 8; x++) + yield return mGrid[x, y]; + } + } + + internal OutputDevice OutputDevice + { + get { return mOutputDevice; } + } + } +} diff --git a/launchpad-master/IntelOrca.Launchpad/LaunchpadException.cs b/launchpad-master/IntelOrca.Launchpad/LaunchpadException.cs new file mode 100644 index 0000000..7ee6c4c --- /dev/null +++ b/launchpad-master/IntelOrca.Launchpad/LaunchpadException.cs @@ -0,0 +1,10 @@ +using System; + +namespace IntelOrca.Launchpad +{ + public class LaunchpadException : Exception + { + public LaunchpadException() : base() { } + public LaunchpadException(string message) : base(message) { } + } +} diff --git a/launchpad-master/IntelOrca.Launchpad/Properties/AssemblyInfo.cs b/launchpad-master/IntelOrca.Launchpad/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..04fde0d --- /dev/null +++ b/launchpad-master/IntelOrca.Launchpad/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("IntelOrca.Launchpad")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("IntelOrca.Launchpad")] +[assembly: AssemblyCopyright("Copyright © Ted John 2013")] +[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("02ec4a3e-47d6-4482-a424-0bde02acc866")] + +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/launchpad-master/IntelOrca.Launchpad/packages.config b/launchpad-master/IntelOrca.Launchpad/packages.config new file mode 100644 index 0000000..32a593b --- /dev/null +++ b/launchpad-master/IntelOrca.Launchpad/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/launchpad-master/IntelOrca.LaunchpadTests/App.config b/launchpad-master/IntelOrca.LaunchpadTests/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/launchpad-master/IntelOrca.LaunchpadTests/Bulldog.cs b/launchpad-master/IntelOrca.LaunchpadTests/Bulldog.cs new file mode 100644 index 0000000..e7c8b31 --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/Bulldog.cs @@ -0,0 +1,158 @@ +using IntelOrca.Launchpad; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Media; + +namespace IntelOrca.LaunchpadTests +{ + class Bulldog + { + private LaunchpadDevice mLaunchpadDevice; + private List mDogs = new List(); + private Random mRandom = new Random(); + + private long mCurrentTicks = 0; + private long mNextDogTick = 2000; + + private int mMisses, mHits; + + public Bulldog(LaunchpadDevice device) + { + mLaunchpadDevice = device; + mLaunchpadDevice.ButtonPressed += mLaunchpadDevice_ButtonPressed; + } + + private void mLaunchpadDevice_ButtonPressed(object sender, ButtonPressEventArgs e) + { + if (e.Type == ButtonType.Grid) { + var pressedDogs = mDogs.Where(d => (int)d.X == e.X && (int)d.Y == e.Y); + pressedDogs.ToList().ForEach(d => d.Killed = true); + if (pressedDogs.Count() > 0) + SystemSounds.Beep.Play(); + } + } + + public void Play() + { + long last_tick = Environment.TickCount; + long delay = 12; + + while (true) { + if (Environment.TickCount - last_tick < delay) + continue; + mCurrentTicks += Environment.TickCount - last_tick; + last_tick = Environment.TickCount; + + Update(); + Draw(); + } + } + + private void Update() + { + mDogs.ForEach(d => d.Update()); + + var missedDogs = mDogs.Where(d => d.LeftGrid); + var hitDogs = mDogs.Where(d => d.Killed); + + mMisses += missedDogs.Count(); + mHits += hitDogs.Count(); + + mDogs.RemoveAll(d => d.LeftGrid || d.Killed); + + if (mDogs.Count < 8) { + if (mCurrentTicks > mNextDogTick) { + mDogs.Add(GetNewDog()); + mNextDogTick = mCurrentTicks + mRandom.Next(100, 1000); + } + } + + Console.Clear(); + Console.WriteLine("Misses: {0:0000}", mMisses); + Console.WriteLine("Hits: {0:0000}", mHits); + Console.WriteLine("Score: {0:0000}", (mHits * 10) - (mMisses * 5)); + } + + private void Draw() + { + ButtonBrightness[,] redgrid = new ButtonBrightness[8, 8]; + ButtonBrightness[,] greengrid = new ButtonBrightness[8, 8]; + + mDogs.ForEach(d => { + if (!(d.X >= 0 && d.X < 8 && d.Y >= 0 && d.Y < 8)) + return; + + redgrid[(int)d.X, (int)d.Y] = ButtonBrightness.Full; + greengrid[(int)d.X, (int)d.Y] = ButtonBrightness.Full; + }); + + for (int y = 0; y < 8; y++) + for (int x = 0; x < 8; x++) + mLaunchpadDevice[x, y].SetBrightness(redgrid[x, y], greengrid[x, y]); + mLaunchpadDevice.Refresh(); + } + + private Dog GetNewDog() + { + Dog dog = new Dog(); + + if (mRandom.Next(2) == 0) { + // Row + dog.Y = mRandom.Next(8); + dog.VX = mRandom.NextDouble() / 5; + dog.X = 0; + if (mRandom.Next(2) == 0) { + dog.X = 7; + dog.VX *= -1; + } + + } else { + // Column + dog.X = mRandom.Next(8); + dog.VY = mRandom.NextDouble() / 5; + dog.Y = 0; + if (mRandom.Next(2) == 0) { + dog.Y = 7; + dog.VY *= -1; + } + } + + return dog; + } + + class Dog + { + public Dog() { } + + public void Update() + { + X += VX; + Y += VY; + } + + public bool LeftGrid + { + get + { + if (X < 0 && VX <= 0) + return true; + if (Y < 0 && VY <= 0) + return true; + if (X >= 8 && VX >= 0) + return true; + if (Y >= 8 && VY >= 0) + return true; + return false; + } + } + + public bool Killed { get; set; } + public int Colour { get; set; } + public double X { get; set; } + public double Y { get; set; } + public double VX { get; set; } + public double VY { get; set; } + } + } +} diff --git a/launchpad-master/IntelOrca.LaunchpadTests/GeometrischeTests.cs b/launchpad-master/IntelOrca.LaunchpadTests/GeometrischeTests.cs new file mode 100644 index 0000000..00f8060 --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/GeometrischeTests.cs @@ -0,0 +1,1395 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using IntelOrca.Launchpad; + +namespace IntelOrca.LaunchpadTests +{ + + class GeometrischeTests + { + private LaunchpadDevice mLaunchpadDevice; + private long mCurrentTicks = 0; + + //Default speed is 4, valid speed frame is 1 - 9 + private int speed = 4; + long delay; + private List availablePointGroups = new List(); + private int activeGroup = -1; + + private bool paused = false; + private bool gameEnd = false; + + // Rects for centerbased Animation (only 4 max possible) + private Point[] Rect_center; + private Point[] Rect_inner; + private Point[] Rect_outer; + private Point[] Rect_edge; + + // Lines for Horizontal based animation (8 Lines) + private Point[] Line_Top; + private Point[] Line_Upper; + private Point[] Line_UpperMid; + private Point[] Line_MidTop; + private Point[] Line_MidBot; + private Point[] Line_LowerMid; + private Point[] Line_Lower; + private Point[] Line_Bottom; + + // Lines for Vertical based animation (8 Lines) + private Point[] Line_LeftEdge; + private Point[] Line_LeftOuter; + private Point[] Line_LeftInner; + private Point[] Line_MidLeft; + private Point[] Line_MidRight; + private Point[] Line_RightInner; + private Point[] Line_RightOuter; + private Point[] Line_RightEdge; + + Random Randomizer; + + //Specialty Groups + Point[] refreshGroup; + Point[] decayGroup; + int refreshIndexer; + int decayIndexer; + int raisingIndexer; + List currentActiveGroup ; + List currentRaisingActiveGroup = new List(); + + + public enum animationColorMode { Green2Red, Red2Green, Green2Green, Red2Red }; + animationColorMode currentMode = animationColorMode.Green2Green; + + public enum animationStyle { CenteredIn, CenteredOut, HorizontalDown, HorizontalUp, VerticalRight, VerticalLeft, Noise, SmoothNoise, Matrix, WaveHorizontal, WaveVertical }; + animationStyle currentStyle = animationStyle.VerticalRight; + public GeometrischeTests(LaunchpadDevice device) + { + mLaunchpadDevice = device; + + //Add Buttonpress-Handler + mLaunchpadDevice.ButtonPressed += mLaunchpadDevice_ButtonPressed; + + mLaunchpadDevice.GetButton(SideButton.Arm).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice.GetButton(SideButton.Solo).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Full); + + mLaunchpadDevice.GetButton(SideButton.Volume).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + mLaunchpadDevice.GetButton(SideButton.Pan).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Medium); + + mLaunchpadDevice.GetButton(SideButton.SoundA).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Full); + mLaunchpadDevice.GetButton(SideButton.SoundB).SetBrightness(ButtonBrightness.Low, ButtonBrightness.Low); + + //Randomizer with always different Timestamp + Randomizer = new Random(Convert.ToInt32((new TimeSpan(DateTime.UtcNow.Ticks - new DateTime(2013, 06, 08).Ticks).TotalMinutes))); + + Init(); + } + + private void mLaunchpadDevice_ButtonPressed(object sender, ButtonPressEventArgs e) + { + // If gridbutton was pressed + if (e.Type == ButtonType.Grid) + { + + } + + // If SNDA button is pressed -> Cycle to next Style + if (e.Type == ButtonType.Side) + { + if (e.SidebarButton == SideButton.SoundA) + { + Init(); + } + } + + // If SNDB button is pressed -> Cycle to next Colormode + if (e.Type == ButtonType.Side) + { + if (e.SidebarButton == SideButton.SoundB) + { + switch (currentMode) + { + case animationColorMode.Green2Green: + currentMode = animationColorMode.Green2Red; + break; + case animationColorMode.Red2Red: + currentMode = animationColorMode.Red2Green; + break; + case animationColorMode.Green2Red: + currentMode = animationColorMode.Red2Red; + break; + case animationColorMode.Red2Green: + currentMode = animationColorMode.Green2Green; + break; + } + } + } + + // If Volume button is pressed -> accelerate animation + if (e.Type == ButtonType.Side) + { + if (e.SidebarButton == SideButton.Volume) + { + if (speed > 1) + { + speed--; + } + } + } + + // If Pan button is pressed -> decelerate animation + if (e.Type == ButtonType.Side) + { + if (e.SidebarButton == SideButton.Pan) + { + if (speed < 9) + { + speed++; + } + } + } + + // if Solobutton is pressed -> toggle pause animation + if (e.Type == ButtonType.Side) + { + if (e.SidebarButton == SideButton.Solo) + { + if (paused) + paused = false; + else + paused = true; + } + } + + //if armbutton is pressed -> end the loop + if (e.Type == ButtonType.Side) + { + if (e.SidebarButton == SideButton.Arm) + { + mLaunchpadDevice.Reset(); + gameEnd = true; + } + } + + } + + private void Init() + { + + // Set the different styles and modes + switch (currentStyle) + { + case animationStyle.CenteredIn: + currentStyle = animationStyle.CenteredOut; + break; + case animationStyle.CenteredOut: + currentStyle = animationStyle.HorizontalDown; + break; + case animationStyle.HorizontalDown: + currentStyle = animationStyle.HorizontalUp; + break; + case animationStyle.HorizontalUp: + currentStyle = animationStyle.VerticalLeft; + break; + case animationStyle.Noise: + currentStyle = animationStyle.SmoothNoise; + break; + case animationStyle.VerticalLeft: + currentStyle = animationStyle.VerticalRight; + break; + case animationStyle.VerticalRight: + currentStyle = animationStyle.Noise; + break; + case animationStyle.WaveHorizontal: + break; + case animationStyle.WaveVertical: + break; + case animationStyle.SmoothNoise: + currentStyle = animationStyle.CenteredIn; + break; + case animationStyle.Matrix: + currentStyle = animationStyle.CenteredIn; + break; + } + + //Reset Group indexer + activeGroup = 0; + + + #region CenteredIn/Out + // Fill valid Points of all rectangles + if (currentStyle == animationStyle.CenteredIn || currentStyle == animationStyle.CenteredOut) + { + Rect_center = null; + Rect_center = new Point[4]; + Rect_center[0] = new Point(3, 3); + Rect_center[1] = new Point(3, 4); + Rect_center[2] = new Point(4, 3); + Rect_center[3] = new Point(4, 4); + + Rect_inner = null; + Rect_inner = new Point[12]; + Rect_inner[0] = new Point(2, 2); + Rect_inner[1] = new Point(3, 2); + Rect_inner[2] = new Point(4, 2); + Rect_inner[3] = new Point(5, 2); + Rect_inner[4] = new Point(2, 3); + Rect_inner[5] = new Point(5, 3); + Rect_inner[6] = new Point(2, 4); + Rect_inner[7] = new Point(5, 4); + Rect_inner[8] = new Point(2, 5); + Rect_inner[9] = new Point(3, 5); + Rect_inner[10] = new Point(4, 5); + Rect_inner[11] = new Point(5, 5); + + Rect_outer = null; + Rect_outer = new Point[20]; + Rect_outer[0] = new Point(1, 1); + Rect_outer[1] = new Point(1, 2); + Rect_outer[2] = new Point(1, 3); + Rect_outer[3] = new Point(1, 4); + Rect_outer[4] = new Point(1, 5); + Rect_outer[5] = new Point(1, 6); + Rect_outer[6] = new Point(2, 1); + Rect_outer[7] = new Point(2, 6); + Rect_outer[8] = new Point(3, 1); + Rect_outer[9] = new Point(3, 6); + Rect_outer[10] = new Point(4, 1); + Rect_outer[11] = new Point(4, 6); + Rect_outer[12] = new Point(5, 1); + Rect_outer[13] = new Point(5, 6); + Rect_outer[14] = new Point(6, 1); + Rect_outer[15] = new Point(6, 2); + Rect_outer[16] = new Point(6, 3); + Rect_outer[17] = new Point(6, 4); + Rect_outer[18] = new Point(6, 5); + Rect_outer[19] = new Point(6, 6); + + Rect_edge = null; + Rect_edge = new Point[28]; + Rect_edge[0] = new Point(0, 0); + Rect_edge[1] = new Point(1, 0); + Rect_edge[2] = new Point(2, 0); + Rect_edge[3] = new Point(3, 0); + Rect_edge[4] = new Point(4, 0); + Rect_edge[5] = new Point(5, 0); + Rect_edge[6] = new Point(6, 0); + Rect_edge[7] = new Point(7, 0); + Rect_edge[8] = new Point(0, 1); + Rect_edge[9] = new Point(7, 1); + Rect_edge[10] = new Point(0, 2); + Rect_edge[11] = new Point(7, 2); + Rect_edge[12] = new Point(0, 3); + Rect_edge[13] = new Point(7, 3); + Rect_edge[14] = new Point(0, 4); + Rect_edge[15] = new Point(7, 4); + Rect_edge[16] = new Point(0, 5); + Rect_edge[17] = new Point(7, 5); + Rect_edge[18] = new Point(0, 6); + Rect_edge[19] = new Point(7, 6); + Rect_edge[20] = new Point(0, 7); + Rect_edge[21] = new Point(1, 7); + Rect_edge[22] = new Point(2, 7); + Rect_edge[23] = new Point(3, 7); + Rect_edge[24] = new Point(4, 7); + Rect_edge[25] = new Point(5, 7); + Rect_edge[26] = new Point(6, 7); + Rect_edge[27] = new Point(7, 7); + + // Clear all remaining groups to prevent overload of unassagned point groups + availablePointGroups.Clear(); + if (currentStyle == animationStyle.CenteredOut) + { + + availablePointGroups.Add(Rect_center); + availablePointGroups.Add(Rect_inner); + availablePointGroups.Add(Rect_outer); + availablePointGroups.Add(Rect_edge); + } + else + { + availablePointGroups.Add(Rect_edge); + availablePointGroups.Add(Rect_outer); + availablePointGroups.Add(Rect_inner); + availablePointGroups.Add(Rect_center); + } + } + #endregion + + #region Horizontal Up/Down + // Fill valid Points of all rectangles + if (currentStyle == animationStyle.HorizontalUp || currentStyle == animationStyle.HorizontalDown) + { + + Line_Top = null; + Line_Top = new Point[8]; + Line_Top[0] = new Point(0, 0); + Line_Top[1] = new Point(1, 0); + Line_Top[2] = new Point(2, 0); + Line_Top[3] = new Point(3, 0); + Line_Top[4] = new Point(4, 0); + Line_Top[5] = new Point(5, 0); + Line_Top[6] = new Point(6, 0); + Line_Top[7] = new Point(7, 0); + + Line_Upper = null; + Line_Upper = new Point[8]; + Line_Upper[0] = new Point(0, 1); + Line_Upper[1] = new Point(1, 1); + Line_Upper[2] = new Point(2, 1); + Line_Upper[3] = new Point(3, 1); + Line_Upper[4] = new Point(4, 1); + Line_Upper[5] = new Point(5, 1); + Line_Upper[6] = new Point(6, 1); + Line_Upper[7] = new Point(7, 1); + + + Line_UpperMid = null; + Line_UpperMid = new Point[8]; + Line_UpperMid[0] = new Point(0, 2); + Line_UpperMid[1] = new Point(1, 2); + Line_UpperMid[2] = new Point(2, 2); + Line_UpperMid[3] = new Point(3, 2); + Line_UpperMid[4] = new Point(4, 2); + Line_UpperMid[5] = new Point(5, 2); + Line_UpperMid[6] = new Point(6, 2); + Line_UpperMid[7] = new Point(7, 2); + + + Line_MidTop = null; + Line_MidTop = new Point[8]; + Line_MidTop[0] = new Point(0, 3); + Line_MidTop[1] = new Point(1, 3); + Line_MidTop[2] = new Point(2, 3); + Line_MidTop[3] = new Point(3, 3); + Line_MidTop[4] = new Point(4, 3); + Line_MidTop[5] = new Point(5, 3); + Line_MidTop[6] = new Point(6, 3); + Line_MidTop[7] = new Point(7, 3); + + Line_MidBot = null; + Line_MidBot = new Point[8]; + Line_MidBot[0] = new Point(0, 4); + Line_MidBot[1] = new Point(1, 4); + Line_MidBot[2] = new Point(2, 4); + Line_MidBot[3] = new Point(3, 4); + Line_MidBot[4] = new Point(4, 4); + Line_MidBot[5] = new Point(5, 4); + Line_MidBot[6] = new Point(6, 4); + Line_MidBot[7] = new Point(7, 4); + + Line_LowerMid = null; + Line_LowerMid = new Point[8]; + Line_LowerMid[0] = new Point(0, 5); + Line_LowerMid[1] = new Point(1, 5); + Line_LowerMid[2] = new Point(2, 5); + Line_LowerMid[3] = new Point(3, 5); + Line_LowerMid[4] = new Point(4, 5); + Line_LowerMid[5] = new Point(5, 5); + Line_LowerMid[6] = new Point(6, 5); + Line_LowerMid[7] = new Point(7, 5); + + Line_Lower = null; + Line_Lower = new Point[8]; + Line_Lower[0] = new Point(0, 6); + Line_Lower[1] = new Point(1, 6); + Line_Lower[2] = new Point(2, 6); + Line_Lower[3] = new Point(3, 6); + Line_Lower[4] = new Point(4, 6); + Line_Lower[5] = new Point(5, 6); + Line_Lower[6] = new Point(6, 6); + Line_Lower[7] = new Point(7, 6); + + Line_Bottom = null; + Line_Bottom = new Point[8]; + Line_Bottom[0] = new Point(0, 7); + Line_Bottom[1] = new Point(1, 7); + Line_Bottom[2] = new Point(2, 7); + Line_Bottom[3] = new Point(3, 7); + Line_Bottom[4] = new Point(4, 7); + Line_Bottom[5] = new Point(5, 7); + Line_Bottom[6] = new Point(6, 7); + Line_Bottom[7] = new Point(7, 7); + + + // Clear all remaining groups to prevent overload of unassagned point groups + availablePointGroups.Clear(); + if (currentStyle == animationStyle.HorizontalDown) + { + + availablePointGroups.Add(Line_Top); + availablePointGroups.Add(Line_Upper); + availablePointGroups.Add(Line_UpperMid); + availablePointGroups.Add(Line_MidTop); + availablePointGroups.Add(Line_MidBot); + availablePointGroups.Add(Line_LowerMid); + availablePointGroups.Add(Line_Lower); + availablePointGroups.Add(Line_Bottom); + + } + else + { + availablePointGroups.Add(Line_Bottom); + availablePointGroups.Add(Line_Lower); + availablePointGroups.Add(Line_LowerMid); + availablePointGroups.Add(Line_MidBot); + availablePointGroups.Add(Line_MidTop); + availablePointGroups.Add(Line_UpperMid); + availablePointGroups.Add(Line_Upper); + availablePointGroups.Add(Line_Top); + } + } + #endregion + + #region Vertical Left/Right + if (currentStyle == animationStyle.HorizontalUp || currentStyle == animationStyle.HorizontalDown) + { + + + Line_LeftEdge = null; + Line_LeftEdge = new Point[8]; + Line_LeftEdge[0] = new Point(0, 0); + Line_LeftEdge[1] = new Point(0, 1); + Line_LeftEdge[2] = new Point(0, 2); + Line_LeftEdge[3] = new Point(0, 3); + Line_LeftEdge[4] = new Point(0, 4); + Line_LeftEdge[5] = new Point(0, 5); + Line_LeftEdge[6] = new Point(0, 6); + Line_LeftEdge[7] = new Point(0, 7); + + Line_LeftOuter = null; + Line_LeftOuter = new Point[8]; + Line_LeftOuter[0] = new Point(1, 0); + Line_LeftOuter[1] = new Point(1, 1); + Line_LeftOuter[2] = new Point(1, 2); + Line_LeftOuter[3] = new Point(1, 3); + Line_LeftOuter[4] = new Point(1, 4); + Line_LeftOuter[5] = new Point(1, 5); + Line_LeftOuter[6] = new Point(1, 6); + Line_LeftOuter[7] = new Point(1, 7); + + + Line_LeftInner = null; + Line_LeftInner = new Point[8]; + Line_LeftInner[0] = new Point(2, 0); + Line_LeftInner[1] = new Point(2, 1); + Line_LeftInner[2] = new Point(2, 2); + Line_LeftInner[3] = new Point(2, 3); + Line_LeftInner[4] = new Point(2, 4); + Line_LeftInner[5] = new Point(2, 5); + Line_LeftInner[6] = new Point(2, 6); + Line_LeftInner[7] = new Point(2, 7); + + + Line_MidLeft = null; + Line_MidLeft = new Point[8]; + Line_MidLeft[0] = new Point(3, 0); + Line_MidLeft[1] = new Point(3, 1); + Line_MidLeft[2] = new Point(3, 2); + Line_MidLeft[3] = new Point(3, 3); + Line_MidLeft[4] = new Point(3, 4); + Line_MidLeft[5] = new Point(3, 5); + Line_MidLeft[6] = new Point(3, 6); + Line_MidLeft[7] = new Point(3, 7); + + Line_MidRight = null; + Line_MidRight = new Point[8]; + Line_MidRight[0] = new Point(4, 0); + Line_MidRight[1] = new Point(4, 1); + Line_MidRight[2] = new Point(4, 2); + Line_MidRight[3] = new Point(4, 3); + Line_MidRight[4] = new Point(4, 4); + Line_MidRight[5] = new Point(4, 5); + Line_MidRight[6] = new Point(4, 6); + Line_MidRight[7] = new Point(4, 7); + + Line_RightInner = null; + Line_RightInner = new Point[8]; + Line_RightInner[0] = new Point(5, 0); + Line_RightInner[1] = new Point(5, 1); + Line_RightInner[2] = new Point(5, 2); + Line_RightInner[3] = new Point(5, 3); + Line_RightInner[4] = new Point(5, 4); + Line_RightInner[5] = new Point(5, 5); + Line_RightInner[6] = new Point(5, 6); + Line_RightInner[7] = new Point(5, 7); + + Line_RightOuter = null; + Line_RightOuter = new Point[8]; + Line_RightOuter[0] = new Point(6, 0); + Line_RightOuter[1] = new Point(6, 1); + Line_RightOuter[2] = new Point(6, 2); + Line_RightOuter[3] = new Point(6, 3); + Line_RightOuter[4] = new Point(6, 4); + Line_RightOuter[5] = new Point(6, 5); + Line_RightOuter[6] = new Point(6, 6); + Line_RightOuter[7] = new Point(6, 7); + + Line_RightEdge = null; + Line_RightEdge = new Point[8]; + Line_RightEdge[0] = new Point(7, 0); + Line_RightEdge[1] = new Point(7, 1); + Line_RightEdge[2] = new Point(7, 2); + Line_RightEdge[3] = new Point(7, 3); + Line_RightEdge[4] = new Point(7, 4); + Line_RightEdge[5] = new Point(7, 5); + Line_RightEdge[6] = new Point(7, 6); + Line_RightEdge[7] = new Point(7, 7); + + + // Clear all remaining groups to prevent overload of unassagned point groups + availablePointGroups.Clear(); + if (currentStyle == animationStyle.VerticalRight) + { + + availablePointGroups.Add(Line_LeftEdge); + availablePointGroups.Add(Line_LeftOuter); + availablePointGroups.Add(Line_LeftInner); + availablePointGroups.Add(Line_MidLeft); + availablePointGroups.Add(Line_MidRight); + availablePointGroups.Add(Line_RightInner); + availablePointGroups.Add(Line_RightOuter); + availablePointGroups.Add(Line_RightEdge); + + } + else + { + availablePointGroups.Add(Line_RightEdge); + availablePointGroups.Add(Line_RightOuter); + availablePointGroups.Add(Line_RightInner); + availablePointGroups.Add(Line_MidRight); + availablePointGroups.Add(Line_MidLeft); + availablePointGroups.Add(Line_LeftInner); + availablePointGroups.Add(Line_LeftOuter); + availablePointGroups.Add(Line_LeftEdge); + } + } + #endregion + } + + public void Run() + { + // Control of internal ticks (6*12 Frames are skipped to simulate a propper time pass) + long last_tick = Environment.TickCount; + + + // GameLoop + while (true && !gameEnd) + { + delay = 24 * speed; + if (Environment.TickCount - last_tick < delay) + continue; + mCurrentTicks += Environment.TickCount - last_tick; + last_tick = Environment.TickCount; + + // Possibility to freeze the animation) + if (!paused) + { + // Update of all rect-colors + Update(); + // redraw the new information on the board + Draw(); + } + + } + } + private void Update() + { + + //Rotate to next group of points (restart at first at the end) + // potential inserting point of Rebounce cycling (start2end -> end2start instead of start2end->start2end) + if (activeGroup >= availablePointGroups.Count() - 1) + { + activeGroup = 0; + } + else + { + activeGroup++; + } + + // only refresh active rect -> other ones "decay into next phase" + switch (currentStyle) + { + #region CenteredIn + case animationStyle.CenteredIn: + switch (activeGroup) + { + case 0: + decayGroupColors(Rect_center); + decayGroupColors(Rect_inner); + decayGroupColors(Rect_outer); + refreshGroupColors(Rect_edge); + break; + case 1: + decayGroupColors(Rect_center); + decayGroupColors(Rect_inner); + refreshGroupColors(Rect_outer); + decayGroupColors(Rect_edge); + break; + case 2: + decayGroupColors(Rect_center); + refreshGroupColors(Rect_inner); + decayGroupColors(Rect_outer); + decayGroupColors(Rect_edge); + break; + case 3: + refreshGroupColors(Rect_center); + decayGroupColors(Rect_inner); + decayGroupColors(Rect_outer); + decayGroupColors(Rect_edge); + break; + + } + + break; + #endregion + #region CenteredOut + case animationStyle.CenteredOut: + switch (activeGroup) + { + case 0: + refreshGroupColors(Rect_center); + decayGroupColors(Rect_inner); + decayGroupColors(Rect_outer); + decayGroupColors(Rect_edge); + break; + case 1: + decayGroupColors(Rect_center); + refreshGroupColors(Rect_inner); + decayGroupColors(Rect_outer); + decayGroupColors(Rect_edge); + break; + case 2: + decayGroupColors(Rect_center); + decayGroupColors(Rect_inner); + refreshGroupColors(Rect_outer); + decayGroupColors(Rect_edge); + break; + case 3: + decayGroupColors(Rect_center); + decayGroupColors(Rect_inner); + decayGroupColors(Rect_outer); + refreshGroupColors(Rect_edge); + break; + + } + break; + #endregion + #region HorizontalDown + case animationStyle.HorizontalDown: + switch (activeGroup) + { + case 0: + refreshGroupColors(Line_Top); + decayGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 1: + decayGroupColors(Line_Top); + refreshGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 2: + decayGroupColors(Line_Top); + decayGroupColors(Line_Upper); + refreshGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 3: + decayGroupColors(Line_Top); + decayGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + refreshGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 4: + decayGroupColors(Line_Top); + decayGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + refreshGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 5: + decayGroupColors(Line_Top); + decayGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + refreshGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 6: + decayGroupColors(Line_Top); + decayGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + refreshGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 7: + decayGroupColors(Line_Top); + decayGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + refreshGroupColors(Line_Bottom); + break; + + } + break; + #endregion + #region HorizontalUp + case animationStyle.HorizontalUp: + switch (activeGroup) + { + case 0: + decayGroupColors(Line_Top); + decayGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + refreshGroupColors(Line_Bottom); + break; + case 1: + decayGroupColors(Line_Top); + decayGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + refreshGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 2: + decayGroupColors(Line_Top); + decayGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + refreshGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 3: + decayGroupColors(Line_Top); + decayGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + refreshGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 4: + decayGroupColors(Line_Top); + decayGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + refreshGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 5: + decayGroupColors(Line_Top); + decayGroupColors(Line_Upper); + refreshGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 6: + decayGroupColors(Line_Top); + refreshGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + case 7: + refreshGroupColors(Line_Top); + decayGroupColors(Line_Upper); + decayGroupColors(Line_UpperMid); + decayGroupColors(Line_MidTop); + decayGroupColors(Line_MidBot); + decayGroupColors(Line_LowerMid); + decayGroupColors(Line_Lower); + decayGroupColors(Line_Bottom); + break; + + } + break; + #endregion + #region Noise + case animationStyle.Noise: + //Pick certain amount of cells randomly and decay the rest + currentActiveGroup = new List(); + + while (currentActiveGroup.Count() < 8) + { + //create new Random point + Point TempPoint = new Point(Randomizer.Next(0, 8), Randomizer.Next(0, 8)); + + //check if new Point is allready in the current group + if (currentActiveGroup.Contains(TempPoint)) + { + continue; + } + else + { + //if its not in the activeGroup it must be added + currentActiveGroup.Add(TempPoint); + } + } + + Point[] refreshGroup = new Point[8]; + int refreshIndexer = 0; + + Point[] decayGroup = new Point[56]; + int decayIndexer = 0; + + //When the new group is picked, they shall be refreshed while the rest decays + for (int yPos = 0; yPos < 8; yPos++) + { + for (int xPos = 0; xPos < 8; xPos++) + { + Point TempPoint = new Point(xPos, yPos); + if (currentActiveGroup.Contains(TempPoint)) + { + refreshGroup[refreshIndexer++] = new Point(TempPoint.X, TempPoint.Y); + } + else + { + decayGroup[decayIndexer++] = new Point(TempPoint.X, TempPoint.Y); + } + } + } + + //after groups are sorted update their lightinformation + refreshGroupColors(refreshGroup); + decayGroupColors(decayGroup); + + break; + #endregion + #region SmoothNoise + case animationStyle.SmoothNoise: + //Pick certain amount of cells randomly from the decaying group + + while (currentRaisingActiveGroup.Count() < 32) + { + //create new Random point + Point TempPoint = new Point(Randomizer.Next(0, 8), Randomizer.Next(0, 8)); + + //check if new Point is allready in the current group + if (currentRaisingActiveGroup.Contains(TempPoint)) + { + continue; + } + else + { + //if its not in the activeGroup it must be added + currentRaisingActiveGroup.Add(TempPoint); + } + } + + //check if maxed ListEntries need to be put to decay again + List RemoveList = new List(); + foreach (Point P in currentRaisingActiveGroup) + { + if (checkIfMaxedPoint(P)) + { + RemoveList.Add(P); + } + } + + foreach (Point P in RemoveList) + { + currentRaisingActiveGroup.Remove(P); + } + + //technicly no Tiles are directly refreshed-> all will first be set to increase slowly + refreshGroup = new Point[currentRaisingActiveGroup.Count()]; + refreshIndexer = 0; + + //An Point not in the List of RaisingPoints will decay + decayGroup = new Point[64 - currentRaisingActiveGroup.Count()]; + decayIndexer = 0; + + //When the new group is picked, they shall be refreshed while the rest decays + for (int yPos = 0; yPos < 8; yPos++) + { + for (int xPos = 0; xPos < 8; xPos++) + { + Point TempPoint = new Point(xPos, yPos); + if (currentRaisingActiveGroup.Contains(TempPoint)) + { + refreshGroup[refreshIndexer++] = new Point(TempPoint.X, TempPoint.Y); + } + else + { + decayGroup[decayIndexer++] = new Point(TempPoint.X, TempPoint.Y); + } + } + } + + //after groups are sorted update their lightinformation + increaseGroupColors(refreshGroup); + decayGroupColors(decayGroup); + + + break; + #endregion + #region VerticalLeft + case animationStyle.VerticalLeft: + switch (activeGroup) + { + case 0: + decayGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + refreshGroupColors(Line_RightEdge); + break; + case 1: + decayGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + refreshGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 2: + decayGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + refreshGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 3: + decayGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + refreshGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 4: + decayGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + refreshGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 5: + decayGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + refreshGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 6: + decayGroupColors(Line_LeftEdge); + refreshGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 7: + refreshGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + } + break; + #endregion + #region VerticalRight + case animationStyle.VerticalRight: + switch (activeGroup) + { + case 0: + refreshGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 1: + decayGroupColors(Line_LeftEdge); + refreshGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 2: + decayGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + refreshGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 3: + decayGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + refreshGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 4: + decayGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + refreshGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 5: + decayGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + refreshGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 6: + decayGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + refreshGroupColors(Line_RightOuter); + decayGroupColors(Line_RightEdge); + break; + case 7: + decayGroupColors(Line_LeftEdge); + decayGroupColors(Line_LeftOuter); + decayGroupColors(Line_LeftInner); + decayGroupColors(Line_MidLeft); + decayGroupColors(Line_MidRight); + decayGroupColors(Line_RightInner); + decayGroupColors(Line_RightOuter); + refreshGroupColors(Line_RightEdge); + break; + } + break; + #endregion + case animationStyle.WaveHorizontal: + break; + case animationStyle.WaveVertical: + break; + } + + } + + + private bool checkIfMaxedPoint(Point p) + { + switch (currentMode) + { + case animationColorMode.Green2Green: + if (mLaunchpadDevice[p.X, p.Y].GreenBrightness == ButtonBrightness.Full) + return true; + else + return false; + case animationColorMode.Green2Red: + if (mLaunchpadDevice[p.X, p.Y].GreenBrightness == ButtonBrightness.Full) + return true; + else + return false; + case animationColorMode.Red2Red: + if (mLaunchpadDevice[p.X, p.Y].RedBrightness == ButtonBrightness.Full) + return true; + else + return false; + case animationColorMode.Red2Green: + if (mLaunchpadDevice[p.X, p.Y].RedBrightness == ButtonBrightness.Full) + return true; + else + return false; + default: + return false; + + } + + } + private void refreshGroupColors(Point[] currentGroup) + { + //Depending on Colormode refresh the Brightness of that button group + switch (currentMode) + { + case animationColorMode.Green2Green: + foreach (Point Tile in currentGroup) + { + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + } + break; + case animationColorMode.Green2Red: + foreach (Point Tile in currentGroup) + { + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + } + break; + case animationColorMode.Red2Green: + foreach (Point Tile in currentGroup) + { + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + } + break; + case animationColorMode.Red2Red: + foreach (Point Tile in currentGroup) + { + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + } + break; + } + + } + + private void increaseGroupColors(Point[] currentGroup) + { + // Depending on colormode decay + + switch (currentMode) + { + case animationColorMode.Green2Green: + foreach (Point Tile in currentGroup) + { + switch (mLaunchpadDevice[Tile.X, Tile.Y].GreenBrightness) + { + case ButtonBrightness.Off: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Low); + break; + case ButtonBrightness.Medium: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + break; + case ButtonBrightness.Low: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Medium); + break; + + } + } + break; + case animationColorMode.Green2Red: + foreach (Point Tile in currentGroup) + { + switch (mLaunchpadDevice[Tile.X, Tile.Y].GreenBrightness) + { + case ButtonBrightness.Off: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Medium, ButtonBrightness.Low); + break; + case ButtonBrightness.Medium: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + break; + case ButtonBrightness.Low: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Low, ButtonBrightness.Medium); + break; + + } + } + break; + case animationColorMode.Red2Green: + foreach (Point Tile in currentGroup) + { + switch (mLaunchpadDevice[Tile.X, Tile.Y].RedBrightness) + { + case ButtonBrightness.Off: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Low, ButtonBrightness.Medium); + break; + case ButtonBrightness.Medium: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + break; + case ButtonBrightness.Low: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Medium, ButtonBrightness.Low); + break; + + } + + + } + break; + case animationColorMode.Red2Red: + foreach (Point Tile in currentGroup) + { + switch (mLaunchpadDevice[Tile.X, Tile.Y].RedBrightness) + { + case ButtonBrightness.Off: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Low, ButtonBrightness.Off); + break; + case ButtonBrightness.Medium: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + break; + case ButtonBrightness.Low: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Medium, ButtonBrightness.Off); + break; + + } + + } + break; + } + } + + private void decayGroupColors(Point[] currentGroup) + { + // Depending on colormode decay + + switch (currentMode) + { + case animationColorMode.Green2Green: + foreach (Point Tile in currentGroup) + { + switch (mLaunchpadDevice[Tile.X, Tile.Y].GreenBrightness) + { + case ButtonBrightness.Full: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Medium); + break; + case ButtonBrightness.Medium: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Low); + break; + case ButtonBrightness.Low: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Off); + break; + + } + } + break; + case animationColorMode.Green2Red: + foreach (Point Tile in currentGroup) + { + switch (mLaunchpadDevice[Tile.X, Tile.Y].GreenBrightness) + { + case ButtonBrightness.Full: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Low, ButtonBrightness.Medium); + break; + case ButtonBrightness.Medium: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Medium, ButtonBrightness.Low); + break; + case ButtonBrightness.Low: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + break; + + } + } + break; + case animationColorMode.Red2Green: + foreach (Point Tile in currentGroup) + { + switch (mLaunchpadDevice[Tile.X, Tile.Y].RedBrightness) + { + case ButtonBrightness.Full: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Medium, ButtonBrightness.Low); + break; + case ButtonBrightness.Medium: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Low, ButtonBrightness.Medium); + break; + case ButtonBrightness.Low: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + break; + + } + + + } + break; + case animationColorMode.Red2Red: + foreach (Point Tile in currentGroup) + { + switch (mLaunchpadDevice[Tile.X, Tile.Y].RedBrightness) + { + case ButtonBrightness.Full: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Medium, ButtonBrightness.Off); + break; + case ButtonBrightness.Medium: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Low, ButtonBrightness.Off); + break; + case ButtonBrightness.Low: + mLaunchpadDevice[Tile.X, Tile.Y].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Off); + break; + + } + + } + break; + } + } + private void Draw() + { + + // Refresh devices light information + mLaunchpadDevice.Refresh(); + } + } +} diff --git a/launchpad-master/IntelOrca.LaunchpadTests/IntelOrca.LaunchpadTests.csproj b/launchpad-master/IntelOrca.LaunchpadTests/IntelOrca.LaunchpadTests.csproj new file mode 100644 index 0000000..12a6d2a --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/IntelOrca.LaunchpadTests.csproj @@ -0,0 +1,75 @@ + + + + + Debug + AnyCPU + {C2C7FD5A-D56C-4C74-91E3-2D9F5D92FCA3} + Exe + Properties + IntelOrca.LaunchpadTests + IntelOrca.LaunchpadTests + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\midi-dot-net.1.1.0\lib\net35\Midi.dll + + + + + + + + + + + + + + + + + + + + + + {fcf77754-c985-4f3d-be0b-e14011c5dc5b} + IntelOrca.Launchpad + + + + + PreserveNewest + + + + + \ No newline at end of file diff --git a/launchpad-master/IntelOrca.LaunchpadTests/PWGenerator.cs b/launchpad-master/IntelOrca.LaunchpadTests/PWGenerator.cs new file mode 100644 index 0000000..ce27511 --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/PWGenerator.cs @@ -0,0 +1,1261 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using IntelOrca.Launchpad; + +namespace IntelOrca.LaunchpadTests +{ + public struct virtualButton + { + public int PosX; + public int PosY; + public ButtonBrightness IntensityGreen; + public ButtonBrightness IntensityRed; + } + + public class PWGenerator + { + private LaunchpadDevice mLaunchpadDevice; + private long mCurrentTicks = 0; + private bool appFrozen = false; + private bool appEnd = false; + private int screenSaverTimer = 0; + //Default speed is 4, valid speed frame is 1 - 9 + private int speed = 4; + long delay; + virtualButton[,] virtualGrid = new virtualButton[8, 8]; + + Random NoiseRandomizer; + Random FieldValRandomizer; + //Default Seed + int Seed = 12345; + //Default Private Key + int privateKey = 54321; + Random CharValRandomizer; + + + List currentRaisingActiveGroup = new List(); + int refreshIndexer; + int decayIndexer; + Point[] refreshGroup; + Point[] decayGroup; + Point ChosenPoint; + int[,] FieldValues; + Point currentTrailPoint; + Point TargetPoint; + + bool isDigitalDevice = true; + bool screenSaverWasKilled = false; + bool setUpNewRound = true; + + // Building a trail is 0 = inactive, 1 = initializing, 2 = in process + int buildingTrail = 0; + string currentPasswordChunk = ""; + string finalPassword = ""; + Queue ImageList; + private string processMode = "Horizontal_Green"; + + //Default Codepage + private string codePage = "65001 Unicode"; + // codepageMode activates a certain codepage to be used as characters + private string characterSet = "CharsOnly"; + // in combination with codepage gives the range of chars that are + + private bool screensaverOn = false; + + public enum animationColorMode { Green2Red, Red2Green, Green2Green, Red2Red }; + animationColorMode currentMode = animationColorMode.Green2Green; + + public enum animationStyle { CenteredIn, CenteredOut, HorizontalDown, HorizontalUp, VerticalRight, VerticalLeft, Noise, SmoothNoise, Matrix, WaveHorizontal, WaveVertical }; + animationStyle currentStyle = animationStyle.VerticalRight; + + public PWGenerator(LaunchpadDevice device) + { + if (device != null) + { + mLaunchpadDevice = device; + mLaunchpadDevice.ButtonPressed += mLaunchpadDevice_ButtonPressed; + mLaunchpadDevice.GetButton(SideButton.Arm).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice.GetButton(SideButton.Solo).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Full); + mLaunchpadDevice.GetButton(SideButton.TrackOn).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + mLaunchpadDevice.GetButton(ToolbarButton.Session).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + isDigitalDevice = false; + } + + //Randomizer with always different Timestamp + NoiseRandomizer = new Random(Convert.ToInt32((new TimeSpan(DateTime.UtcNow.Ticks - new DateTime(2013, 06, 08).Ticks).TotalMinutes))); + //Randomizer to always generate different chars from the current field value depending on the current chunk + CharValRandomizer = new Random(privateKey); + //Set field Values depending on given private Key + FieldValues = new int[8, 8]; + fillFieldValues(Seed); + + ImageList = new Queue(); + + } + + public PWGenerator(LaunchpadDevice device, int Seed, int privateKey, string charSet = "Nur Buchstaben", string codePage = "65001 Unicode") + { + if (device != null) + {//if no launchpad is available use a virtual field logic + mLaunchpadDevice = device; + mLaunchpadDevice.ButtonPressed += mLaunchpadDevice_ButtonPressed; + mLaunchpadDevice.GetButton(SideButton.Arm).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice.GetButton(SideButton.Solo).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Full); + mLaunchpadDevice.GetButton(SideButton.TrackOn).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + mLaunchpadDevice.GetButton(ToolbarButton.Session).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + isDigitalDevice = false; + } + else + { + mLaunchpadDevice = null; + fillVirtualGrid(); + } + + this.Seed = Seed; + this.privateKey = privateKey; + this.characterSet = charSet; + this.codePage = codePage; + + + + //Randomizer with always different Timestamp + NoiseRandomizer = new Random(Convert.ToInt32((new TimeSpan(DateTime.UtcNow.Ticks - new DateTime(2013, 06, 08).Ticks).TotalMinutes))); + //Randomizer to always generate different chars from the current field value depending on the current chunk + CharValRandomizer = new Random(privateKey); + //Set field Values depending on given private Key + FieldValues = new int[8, 8]; + fillFieldValues(Seed); + + ImageList = new Queue(); + } + + + private void mLaunchpadDevice_ButtonPressed(object sender, ButtonPressEventArgs e) + { + + + if (e.Type == ButtonType.Grid) + { + if (screensaverOn) + { + screensaverOn = false; + screenSaverTimer = 0; + screenSaverWasKilled = true; + speed = 2; + + } + else + { + // to prevent multiinput buttonpress while trailing Proccess has no use + if (buildingTrail == 0 && !appFrozen) + { + mLaunchpadDevice[e.X, e.Y].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Full); + ChosenPoint.X = e.X; + ChosenPoint.Y = e.Y; + buildingTrail = 1; + + createImageFromPoint(ChosenPoint); + + } + //Reset Screensavertimer + screensaverOn = false; + screenSaverTimer = 0; + } + } + + if (e.Type == ButtonType.Toolbar) + { + if (e.ToolbarButton == ToolbarButton.Session && buildingTrail == 0) + { + if (mLaunchpadDevice.GetButton(ToolbarButton.Session).RedBrightness == ButtonBrightness.Full) + { + mLaunchpadDevice.GetButton(ToolbarButton.Session).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + //Enlight Targetpoints in corresponding Color + mLaunchpadDevice[3, 3].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice[3, 4].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice[4, 3].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice[4, 4].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + processMode = "Horizontal_Green"; + currentMode = animationColorMode.Green2Green; + } + else + { + mLaunchpadDevice.GetButton(ToolbarButton.Session).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + //Enlight Targetpoints in corresponding Color + mLaunchpadDevice[3, 3].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + mLaunchpadDevice[3, 4].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + mLaunchpadDevice[4, 3].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + mLaunchpadDevice[4, 4].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + processMode = "Vertical_Red"; + currentMode = animationColorMode.Red2Red; + } + mLaunchpadDevice.Refresh(); + } + } + + if (e.Type == ButtonType.Side) + { + if (e.SidebarButton == SideButton.TrackOn) + { + + mLaunchpadDevice.Reset(); + appEnd = true; + } + } + + if (e.Type == ButtonType.Side) + { + // Disable freezing the calculating process to prevent aborting a pathcalculation with a screensaver + if (e.SidebarButton == SideButton.Solo && buildingTrail == 0) + { + if (appFrozen) + appFrozen = false; + else + appFrozen = true; + } + } + + if (e.Type == ButtonType.Side) + { + if (e.SidebarButton == SideButton.Arm) + { + clearAllFields(); + finalPassword = ""; + appEnd = true; + } + } + + + } + + public void virtualButtonPress(string buttonType, int buttonX, int buttonY) + { + switch (buttonType) + { + case "Grid": + // to prevent multiinput buttonpress while trailing Proccess has no use + if (buildingTrail == 0 && !appFrozen) + { + virtualGrid[buttonX, buttonY].IntensityGreen = ButtonBrightness.Full; + virtualGrid[buttonX, buttonY].IntensityRed = ButtonBrightness.Full; + ChosenPoint.X = buttonX; + ChosenPoint.Y = buttonY; + buildingTrail = 1; + + createImageFromPoint(ChosenPoint); + + } + break; + case "Enter": + appEnd = true; + break; + case "ChangeProcessMode": + if (buildingTrail == 0) + { + if (processMode == "VerticalRed") + { + //If change to Horizontal green is imminent, change center field colors to red + virtualGrid[3, 3].IntensityGreen = ButtonBrightness.Off; + virtualGrid[3, 3].IntensityRed = ButtonBrightness.Full; + virtualGrid[3, 4].IntensityGreen = ButtonBrightness.Off; + virtualGrid[3, 4].IntensityRed = ButtonBrightness.Full; + virtualGrid[4, 3].IntensityGreen = ButtonBrightness.Off; + virtualGrid[4, 3].IntensityRed = ButtonBrightness.Full; + virtualGrid[4, 4].IntensityGreen = ButtonBrightness.Off; + virtualGrid[4, 4].IntensityRed = ButtonBrightness.Full; + processMode = "Horizontal_Green"; + currentMode = animationColorMode.Green2Green; + + } + else + { + virtualGrid[3, 3].IntensityGreen = ButtonBrightness.Full; + virtualGrid[3, 3].IntensityRed = ButtonBrightness.Off; + virtualGrid[3, 4].IntensityGreen = ButtonBrightness.Full; + virtualGrid[3, 4].IntensityRed = ButtonBrightness.Off; + virtualGrid[4, 3].IntensityGreen = ButtonBrightness.Full; + virtualGrid[4, 3].IntensityRed = ButtonBrightness.Off; + virtualGrid[4, 4].IntensityGreen = ButtonBrightness.Full; + virtualGrid[4, 4].IntensityRed = ButtonBrightness.Off; + processMode = "Vertical_Red"; + currentMode = animationColorMode.Red2Red; + } + + } + + + break; + case "Cancel": + + finalPassword = ""; + appEnd = true; + + break; + } + + } + public bool Run() + { + //Function for external operation (with device) + // controll over internal tick clock (6*12 Frames are skipped until next action to achieve a viewable result) + long last_tick = Environment.TickCount; + + + // main processing loop + while (true && !appEnd) + { + delay = 24 * speed; + if (Environment.TickCount - last_tick < delay) + continue; + + screenSaverTimer++; + + + if (screenSaverWasKilled || setUpNewRound) + { + // Kill all screensaver lights + for (int xPos = 0; xPos < 8; xPos++) + { + for (int yPos = 0; yPos < 8; yPos++) + { + mLaunchpadDevice[xPos, yPos].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Off); + } + } + // Restore the regular lights + setUpNewEntry(); + screenSaverWasKilled = false; + setUpNewRound = false; + } + + if (screenSaverTimer >= 300) + { + screensaverOn = true; + speed = 6; + } + mCurrentTicks += Environment.TickCount - last_tick; + last_tick = Environment.TickCount; + + // Basic update-routine with integrated "freeze" function + if (!appFrozen) + { + // Update aller Positionen + Update(); + // Neu Zeichnen des Brettes + Draw(); + } + + } + + if (finalPassword == "") + { + return true; + } + else + { + return false; + } + } + + + private void Update() + { + //TODO + // Methode zum setzen von virtuellem oder launchpad feld erstellen und hier integrieren + + + //Features to implement: + //- CodePages integrieren + //- Externes Inputdevice in eigenem Window integrieren + //- Prüfungen ob alle Konfig-Felder gefüllt sind bevor PW-Generierung gestartet wird + //- Prüfungen, ob alle Konfig-Felder legitime Inhalte haben -> Seed und Key müssen int sein + //- Scale der Icons dynamisch an Eingabemenge anpassen (Egal wie viele Buttons gedrückt werden Icons müssen immer in die Groupbox passen) + //- Mehrzeilen Finales Passwort muss bei mehr als 2 Stellen Scrollbar sein + // If screensaver is active display the noise light pattern + if (screensaverOn) + { + while (currentRaisingActiveGroup.Count() < 32) + { + //create new Random point + Point TempPoint = new Point(NoiseRandomizer.Next(0, 8), NoiseRandomizer.Next(0, 8)); + + //check if new Point is allready in the current group + if (currentRaisingActiveGroup.Contains(TempPoint)) + { + continue; + } + else + { + //if its not in the activeGroup it must be added + currentRaisingActiveGroup.Add(TempPoint); + } + } + + //check if maxed ListEntries need to be put to decay again + List RemoveList = new List(); + foreach (Point P in currentRaisingActiveGroup) + { + if (checkIfMaxedPoint(P)) + { + RemoveList.Add(P); + } + } + + foreach (Point P in RemoveList) + { + currentRaisingActiveGroup.Remove(P); + } + + //technicly no Tiles are directly refreshed-> all will first be set to increase slowly + refreshGroup = new Point[currentRaisingActiveGroup.Count()]; + refreshIndexer = 0; + + //An Point not in the List of RaisingPoints will decay + decayGroup = new Point[64 - currentRaisingActiveGroup.Count()]; + decayIndexer = 0; + + //When the new group is picked, they shall be refreshed while the rest decays + for (int yPos = 0; yPos < 8; yPos++) + { + for (int xPos = 0; xPos < 8; xPos++) + { + Point TempPoint = new Point(xPos, yPos); + if (currentRaisingActiveGroup.Contains(TempPoint)) + { + refreshGroup[refreshIndexer++] = new Point(TempPoint.X, TempPoint.Y); + } + else + { + decayGroup[decayIndexer++] = new Point(TempPoint.X, TempPoint.Y); + } + } + } + + //after groups are sorted update their lightinformation + increaseGroupColors(refreshGroup); + decayGroupColors(decayGroup); + } + else + { + + //if a Trail needs to be build, don't set a new Target + if (buildingTrail == 1) + { + //Check to whitch of the middle ref fields it is closest to + switch (checkQuadrantOfPoint(ChosenPoint)) + { + case "UpLeft": + TargetPoint.X = 3; + TargetPoint.Y = 3; + break; + case "UpRight": + TargetPoint.X = 4; + TargetPoint.Y = 3; + break; + case "DownLeft": + TargetPoint.X = 3; + TargetPoint.Y = 4; + break; + case "DownRight": + TargetPoint.X = 4; + TargetPoint.Y = 4; + break; + } + + //Building a trail is initiated + currentTrailPoint = ChosenPoint; + buildingTrail = 2; + //Chosenpoint will also be picked for the pw-char + currentPasswordChunk = createPathChar("UTF-16"); + } + else + { + if (buildingTrail == 2) + { + //before stepping on in the trail, old trailpoint needs to be turned off + setFieldColor(currentTrailPoint.X, currentTrailPoint.Y, "Green2Red", ButtonBrightness.Off, ButtonBrightness.Off); + + + //Depending on process mode horizontal or vertical path is prefered + switch (processMode) + { + case "Horizontal_Green": + if (currentTrailPoint.X != TargetPoint.X) + {//Currentpoint needs to make a step to the side depending on quarter it is in + switch (checkQuadrantOfPoint(ChosenPoint)) + { + case "UpLeft": + case "DownLeft": + currentTrailPoint.X++; + break; + case "UpRight": + case "DownRight": + currentTrailPoint.X--; + break; + } + //Depending on ColorMode enlight current processed Button in corresponding color + switch (currentMode) + { + case animationColorMode.Green2Green: + setFieldColor(currentTrailPoint.X, currentTrailPoint.Y, "Green", ButtonBrightness.Off, ButtonBrightness.Full); + break; + case animationColorMode.Red2Red: + setFieldColor(currentTrailPoint.X, currentTrailPoint.Y, "Red", ButtonBrightness.Full, ButtonBrightness.Off); + break; + } + + //next char needs to be read from the field and calculated + currentPasswordChunk += createPathChar("UTF-16"); + break; + } + + if (currentTrailPoint.Y != TargetPoint.Y) + {// Currentpoint needs to make a step verticaly depending on quadrant + switch (checkQuadrantOfPoint(ChosenPoint)) + { + case "UpLeft": + case "UpRight": + currentTrailPoint.Y++; + break; + + case "DownLeft": + case "DownRight": + currentTrailPoint.Y--; + break; + } + //Depending on ColorMode enlight current processed Button in corresponding color + switch (currentMode) + { + case animationColorMode.Green2Green: + setFieldColor(currentTrailPoint.X, currentTrailPoint.Y, "Green", ButtonBrightness.Off, ButtonBrightness.Full); + break; + case animationColorMode.Red2Red: + setFieldColor(currentTrailPoint.X, currentTrailPoint.Y, "Red", ButtonBrightness.Full, ButtonBrightness.Off); + break; + } + //next char needs to be read from the field and calculated + currentPasswordChunk += createPathChar("UTF-16"); + break; + } + //if end of trail is reached, close trailbuilding process and initiate new field pic + if (currentTrailPoint.X == TargetPoint.X && currentTrailPoint.Y == TargetPoint.Y) + { + //Initialize all temporal Points + buildingTrail = 0; + setFieldColor(ChosenPoint.X, ChosenPoint.Y, "Green2Red", ButtonBrightness.Off, ButtonBrightness.Off); + setFieldColor(currentTrailPoint.X, currentTrailPoint.Y, "Green2Red", ButtonBrightness.Off, ButtonBrightness.Off); + + currentTrailPoint.X = -1; + currentTrailPoint.Y = -1; + + //Depending on ColorMode reset target button in corresponding color + switch (currentMode) + { + case animationColorMode.Green2Green: + setFieldColor(TargetPoint.X, TargetPoint.Y, "Green", ButtonBrightness.Full, ButtonBrightness.Off); + break; + case animationColorMode.Red2Red: + setFieldColor(TargetPoint.X, TargetPoint.Y, "Red", ButtonBrightness.Off, ButtonBrightness.Full); + break; + } + TargetPoint.X = -1; + TargetPoint.Y = -1; + ChosenPoint.X = -1; + ChosenPoint.Y = -1; + + finalPassword += currentPasswordChunk; + Console.WriteLine(finalPassword); + } + break; + case "Vertical_Red": + if (currentTrailPoint.Y != TargetPoint.Y) + {// Currentpoint needs to make a step verticaly depending on quadrant + switch (checkQuadrantOfPoint(ChosenPoint)) + { + case "UpLeft": + case "UpRight": + currentTrailPoint.Y++; + break; + + case "DownLeft": + case "DownRight": + currentTrailPoint.Y--; + break; + } + //Depending on ColorMode enlight current processed Button in corresponding color + switch (currentMode) + { + case animationColorMode.Green2Green: + setFieldColor(currentTrailPoint.X, currentTrailPoint.Y, "Green", ButtonBrightness.Off, ButtonBrightness.Full); + break; + case animationColorMode.Red2Red: + setFieldColor(currentTrailPoint.X, currentTrailPoint.Y, "Red", ButtonBrightness.Full, ButtonBrightness.Off); + break; + } + //next char needs to be read from the field and calculated + currentPasswordChunk += createPathChar("UTF-16"); + break; + } + + if (currentTrailPoint.X != TargetPoint.X) + {//Currentpoint needs to make a step to the side depending on quarter it is in + switch (checkQuadrantOfPoint(ChosenPoint)) + { + case "UpLeft": + case "DownLeft": + currentTrailPoint.X++; + break; + case "UpRight": + case "DownRight": + currentTrailPoint.X--; + break; + } + //Depending on ColorMode enlight current processed Button in corresponding color + switch (currentMode) + { + case animationColorMode.Green2Green: + setFieldColor(currentTrailPoint.X, currentTrailPoint.Y, "Green", ButtonBrightness.Off, ButtonBrightness.Full); + break; + case animationColorMode.Red2Red: + setFieldColor(currentTrailPoint.X, currentTrailPoint.Y, "Red", ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice[currentTrailPoint.X, currentTrailPoint.Y].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + break; + } + //next char needs to be read from the field and calculated + currentPasswordChunk += createPathChar("UTF-16"); + break; + } + + if (currentTrailPoint.X == TargetPoint.X && currentTrailPoint.Y == TargetPoint.Y) + { + //Initialize all temporal Points + buildingTrail = 0; + setFieldColor(ChosenPoint.X, ChosenPoint.Y, "Green2Red", ButtonBrightness.Off, ButtonBrightness.Off); + setFieldColor(currentTrailPoint.X, currentTrailPoint.Y, "Green2Red", ButtonBrightness.Off, ButtonBrightness.Off); + + currentTrailPoint.X = -1; + currentTrailPoint.Y = -1; + //Depending on ColorMode reset target button in corresponding color + switch (currentMode) + { + case animationColorMode.Green2Green: + setFieldColor(TargetPoint.X, TargetPoint.Y, "Green", ButtonBrightness.Full, ButtonBrightness.Off); + break; + case animationColorMode.Red2Red: + setFieldColor(TargetPoint.X, TargetPoint.Y, "Red", ButtonBrightness.Off, ButtonBrightness.Full); + break; + } + TargetPoint.X = -1; + TargetPoint.Y = -1; + ChosenPoint.X = -1; + ChosenPoint.Y = -1; + + finalPassword += currentPasswordChunk; + Console.WriteLine(finalPassword); + } + break; + } + } + } + + + } + } + + private string checkQuadrantOfPoint(Point p) + { + // Check in whitch quater the point is in + if (p.X < 4 && p.Y < 4) + { + return "UpLeft"; + } + + if (p.X < 4 && p.Y > 3) + { + return "DownLeft"; + } + + if (p.X > 3 && p.Y < 4) + { + return "UpRight"; + } + + if (p.X > 3 && p.Y > 3) + { + return "DownRight"; + } + + return "Error"; + + } + + private void setUpNewEntry() + { + //clear all current active buttons + clearAllFields(); + + //Set up Reference Fields + mLaunchpadDevice[3, 3].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice[4, 3].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice[3, 4].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice[4, 4].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + + //Set up ConfigButtons + mLaunchpadDevice.GetButton(SideButton.Arm).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice.GetButton(SideButton.Solo).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Full); + mLaunchpadDevice.GetButton(SideButton.TrackOn).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + mLaunchpadDevice.GetButton(ToolbarButton.Session).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + } + + private bool checkIfMaxedPoint(Point p) + { + switch (currentMode) + { + case animationColorMode.Green2Green: + if (getFieldColor(p.X, p.Y, "Green") == ButtonBrightness.Full) + return true; + else + return false; + case animationColorMode.Green2Red: + if (getFieldColor(p.X, p.Y, "Green") == ButtonBrightness.Full) + return true; + else + return false; + case animationColorMode.Red2Red: + if (getFieldColor(p.X, p.Y, "Red") == ButtonBrightness.Full) + return true; + else + return false; + case animationColorMode.Red2Green: + if (getFieldColor(p.X, p.Y, "Red") == ButtonBrightness.Full) + return true; + else + return false; + default: + return false; + + } + + } + + private void refreshGroupColors(Point[] currentGroup) + { + //Depending on Colormode refresh the Brightness of that button group + switch (currentMode) + { + case animationColorMode.Green2Green: + foreach (Point Tile in currentGroup) + { + setFieldColor(Tile.X, Tile.Y, "Green", ButtonBrightness.Off, ButtonBrightness.Full); + } + break; + case animationColorMode.Green2Red: + foreach (Point Tile in currentGroup) + { + setFieldColor(Tile.X, Tile.Y, "Green2Red", ButtonBrightness.Off, ButtonBrightness.Full); + } + break; + case animationColorMode.Red2Green: + foreach (Point Tile in currentGroup) + { + setFieldColor(Tile.X, Tile.Y, "Red2Green", ButtonBrightness.Full, ButtonBrightness.Off); + } + break; + case animationColorMode.Red2Red: + foreach (Point Tile in currentGroup) + { + setFieldColor(Tile.X, Tile.Y, "Red", ButtonBrightness.Full, ButtonBrightness.Off); + } + break; + } + + } + + private void increaseGroupColors(Point[] currentGroup) + { + // Depending on colormode decay + + switch (currentMode) + { + case animationColorMode.Green2Green: + foreach (Point Tile in currentGroup) + { + switch (getFieldColor(Tile.X, Tile.Y, "Green")) + { + case ButtonBrightness.Off: + setFieldColor(Tile.X, Tile.Y, "Green", ButtonBrightness.Off, ButtonBrightness.Low); + break; + case ButtonBrightness.Medium: + setFieldColor(Tile.X, Tile.Y, "Green", ButtonBrightness.Off, ButtonBrightness.Full); + break; + case ButtonBrightness.Low: + setFieldColor(Tile.X, Tile.Y, "Green", ButtonBrightness.Off, ButtonBrightness.Medium); + break; + + } + } + break; + case animationColorMode.Green2Red: + foreach (Point Tile in currentGroup) + { + switch (getFieldColor(Tile.X, Tile.Y, "Green")) + { + case ButtonBrightness.Off: + setFieldColor(Tile.X, Tile.Y, "Green2Red", ButtonBrightness.Medium, ButtonBrightness.Low); + break; + case ButtonBrightness.Medium: + setFieldColor(Tile.X, Tile.Y, "Green2Red", ButtonBrightness.Off, ButtonBrightness.Full); + break; + case ButtonBrightness.Low: + setFieldColor(Tile.X, Tile.Y, "Green2Red", ButtonBrightness.Low, ButtonBrightness.Medium); + break; + + } + } + break; + case animationColorMode.Red2Green: + foreach (Point Tile in currentGroup) + { + switch (getFieldColor(Tile.X, Tile.Y, "Red")) + { + case ButtonBrightness.Off: + setFieldColor(Tile.X, Tile.Y, "Red2Green", ButtonBrightness.Low, ButtonBrightness.Medium); + break; + case ButtonBrightness.Medium: + setFieldColor(Tile.X, Tile.Y, "Red2Green", ButtonBrightness.Full, ButtonBrightness.Off); + break; + case ButtonBrightness.Low: + setFieldColor(Tile.X, Tile.Y, "Red2Green", ButtonBrightness.Medium, ButtonBrightness.Low); + break; + + } + + + } + break; + case animationColorMode.Red2Red: + foreach (Point Tile in currentGroup) + { + switch (getFieldColor(Tile.X, Tile.Y, "Red")) + { + case ButtonBrightness.Off: + setFieldColor(Tile.X, Tile.Y, "Red", ButtonBrightness.Low, ButtonBrightness.Off); + break; + case ButtonBrightness.Medium: + setFieldColor(Tile.X, Tile.Y, "Red", ButtonBrightness.Full, ButtonBrightness.Off); + break; + case ButtonBrightness.Low: + setFieldColor(Tile.X, Tile.Y, "Red", ButtonBrightness.Medium, ButtonBrightness.Off); + break; + + } + + } + break; + } + } + + private void decayGroupColors(Point[] currentGroup) + { + // Depending on colormode decay + + switch (currentMode) + { + case animationColorMode.Green2Green: + foreach (Point Tile in currentGroup) + { + switch (getFieldColor(Tile.X, Tile.Y, "Green")) + { + case ButtonBrightness.Full: + setFieldColor(Tile.X, Tile.Y, "Green", ButtonBrightness.Off, ButtonBrightness.Medium); + break; + case ButtonBrightness.Medium: + setFieldColor(Tile.X, Tile.Y, "Green", ButtonBrightness.Off, ButtonBrightness.Low); + break; + case ButtonBrightness.Low: + setFieldColor(Tile.X, Tile.Y, "Green", ButtonBrightness.Off, ButtonBrightness.Off); + break; + + } + } + break; + case animationColorMode.Green2Red: + foreach (Point Tile in currentGroup) + { + switch (getFieldColor(Tile.X, Tile.Y, "Green")) + { + case ButtonBrightness.Full: + setFieldColor(Tile.X, Tile.Y, "Green2Red", ButtonBrightness.Low, ButtonBrightness.Medium); + break; + case ButtonBrightness.Medium: + setFieldColor(Tile.X, Tile.Y, "Green2Red", ButtonBrightness.Medium, ButtonBrightness.Low); + break; + case ButtonBrightness.Low: + setFieldColor(Tile.X, Tile.Y, "Green2Red", ButtonBrightness.Full, ButtonBrightness.Off); + break; + + } + } + break; + case animationColorMode.Red2Green: + foreach (Point Tile in currentGroup) + { + switch (getFieldColor(Tile.X, Tile.Y, "Red")) + { + case ButtonBrightness.Full: + setFieldColor(Tile.X, Tile.Y, "Red2Green", ButtonBrightness.Medium, ButtonBrightness.Low); + break; + case ButtonBrightness.Medium: + setFieldColor(Tile.X, Tile.Y, "Red2Green", ButtonBrightness.Low, ButtonBrightness.Medium); + break; + case ButtonBrightness.Low: + setFieldColor(Tile.X, Tile.Y, "Red2Green", ButtonBrightness.Off, ButtonBrightness.Full); + break; + + } + + + } + break; + case animationColorMode.Red2Red: + foreach (Point Tile in currentGroup) + { + switch (getFieldColor(Tile.X, Tile.Y, "Red")) + { + case ButtonBrightness.Full: + setFieldColor(Tile.X, Tile.Y,"Red", ButtonBrightness.Medium, ButtonBrightness.Off); + break; + case ButtonBrightness.Medium: + setFieldColor(Tile.X, Tile.Y, "Red", ButtonBrightness.Low, ButtonBrightness.Off); + break; + case ButtonBrightness.Low: + setFieldColor(Tile.X, Tile.Y, "Red", ButtonBrightness.Off, ButtonBrightness.Off); + break; + + } + + } + break; + } + } + + private void fillFieldValues(int Seed) + { + // This method sets up the values behind all fields to build paths on; every field is unique + // and stays the same per private key + FieldValRandomizer = new Random(Seed); + + for (int yPos = 0; yPos < 8; yPos++) + { + for (int xPos = 0; xPos < 8; xPos++) + { + FieldValues[xPos, yPos] = FieldValRandomizer.Next(0, 100); + Console.Write(FieldValues[xPos, yPos] + " |"); + } + Console.WriteLine(""); + } + } + + private void fillVirtualGrid() + { + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + virtualButton tempButton = new virtualButton(); + tempButton.PosX = i; + tempButton.PosY = j; + tempButton.IntensityGreen = ButtonBrightness.Off; + tempButton.IntensityRed = ButtonBrightness.Off; + virtualGrid[i, j] = tempButton; + } + } + + switch (currentMode) + { + case animationColorMode.Green2Green: + case animationColorMode.Green2Red: + virtualGrid[3, 3].IntensityRed = ButtonBrightness.Full; + virtualGrid[3, 3].IntensityGreen = ButtonBrightness.Off; + virtualGrid[3, 4].IntensityRed = ButtonBrightness.Full; + virtualGrid[3, 4].IntensityGreen = ButtonBrightness.Off; + virtualGrid[4, 3].IntensityRed = ButtonBrightness.Full; + virtualGrid[4, 3].IntensityGreen = ButtonBrightness.Off; + virtualGrid[4, 4].IntensityRed = ButtonBrightness.Full; + virtualGrid[4, 4].IntensityGreen = ButtonBrightness.Off; + break; + case animationColorMode.Red2Green: + case animationColorMode.Red2Red: + virtualGrid[3, 3].IntensityRed = ButtonBrightness.Off; + virtualGrid[3, 3].IntensityGreen = ButtonBrightness.Full; + virtualGrid[3, 4].IntensityRed = ButtonBrightness.Off; + virtualGrid[3, 4].IntensityGreen = ButtonBrightness.Full; + virtualGrid[4, 3].IntensityRed = ButtonBrightness.Off; + virtualGrid[4, 3].IntensityGreen = ButtonBrightness.Full; + virtualGrid[4, 4].IntensityRed = ButtonBrightness.Off; + virtualGrid[4, 4].IntensityGreen = ButtonBrightness.Full; + break; + } + + + + } + + private void setFieldColor(int PosX, int PosY, string color, ButtonBrightness intensityR,ButtonBrightness intensityG ) + { + if (mLaunchpadDevice == null) + {//no device was given so virtualBoard is used + virtualGrid[PosX, PosY].IntensityGreen = intensityG; + virtualGrid[PosX, PosY].IntensityRed = intensityR; + + } + else + { + mLaunchpadDevice[PosX, PosY].SetBrightness(intensityR, intensityG); + } + } + + public ButtonBrightness getFieldColor(int PosX, int PosY, string neededColor) + { + ButtonBrightness TempColor; + if (mLaunchpadDevice == null) + {//no device was given so virtualBoard is used + switch (neededColor) + { + case "Red": + TempColor = virtualGrid[PosX, PosY].IntensityRed; + break; + case "Green": + TempColor = virtualGrid[PosX, PosY].IntensityGreen; + break; + default: + TempColor = ButtonBrightness.Off; + break; + + } + return TempColor; + } + else + { + switch (neededColor) + { + case "Red": + TempColor = mLaunchpadDevice[PosX, PosY].RedBrightness; + break; + case "Green": + TempColor = mLaunchpadDevice[PosX, PosY].GreenBrightness; + break; + default: + TempColor = ButtonBrightness.Off; + break; + + } + + return TempColor; + } + } + + private string createPathChar(string codepage) + { + //This method creates a codepage char to add it to the current path + int tempValue = 0; + string tempString; + int lowerCap = 0; + int upperCap = 0; + char c = ' '; + switch (codepage) + { + // ToDo: Insert Codepage specific caps + find out how to cast INTs to them + case "ASCII": + case "UTF-16": + case "65001 UTF-8 (Unicode)": + switch (characterSet) + { + case "Nur Buchstaben": + lowerCap = 65; + upperCap = 121; + break; + case "Nur Zahlen": + lowerCap = 48; + upperCap = 58; + break; + case "Keine Steuerzeichen": + default: + lowerCap = 32; + upperCap = 126; + break; + } + + break; + } + //Pick a valid codepage-value which is allowed by target system (needs to be ensured beforehand!!) + //because 0 can be a FieldValue too, at least 1 charactervalue needs to be generated + if (characterSet == "Nur Buchstaben") + { + //in characters only mode there are codepages (like ascii/Unicode) which do not have all chars grouped together + //for such codepages there must be a special tempValue-Calc procedure + bool characterIsInvalid = true; + switch (codePage) + { + case "65001 UTF-8 (Unicode)": + default: + for (int indexer = -1; indexer < FieldValues[currentTrailPoint.X, currentTrailPoint.Y] || characterIsInvalid; indexer++) + { + //lower and uppder cap have all chars implemented but only valid chars need allow for continuing of the process + tempValue = CharValRandomizer.Next(lowerCap, upperCap); + c = (char)tempValue; + + switch (c) + { + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + characterIsInvalid = false; + break; + default: + characterIsInvalid = true; + break; + } + + } + break; + } + + } + else + { + // If searched chars are grouped together this process is enough to determine all needed chars + for (int indexer = -1; indexer < FieldValues[currentTrailPoint.X, currentTrailPoint.Y]; indexer++) + { + tempValue = CharValRandomizer.Next(lowerCap, upperCap); + } + + c = (char)tempValue; + } + + if (c == '\\') + { + tempString = c.ToString(); + tempString += c.ToString(); + } + + return c.ToString(); + } + + private void createImageFromPoint(Point nextImage) + { + + string Tempstring = nextImage.X.ToString() + "_" + nextImage.Y.ToString(); + string PathString = "../../Assets/Graphics/"; + string extension = ".png"; + string proccessString; + if (processMode == "Horizontal_Green") + proccessString = "Hor_"; + else + proccessString = "Ver_"; + string finalPath = PathString + proccessString + Tempstring + extension; + ImageList.Enqueue(finalPath); + } + + + + private void Draw() + { + // Refresh devices light information + mLaunchpadDevice.Refresh(); + } + + public string GetFinalPassword() + { + return finalPassword; + } + + public Queue GetInstructionList() + { + return ImageList; + } + + public void clearAllFields() + { + //Clear all Grid Fields + for (int yPos = 0; yPos < 8; yPos++) + { + for (int xPos = 0; xPos < 8; xPos++) + { + mLaunchpadDevice[xPos, xPos].TurnOffLight(); + } + } + + //ToolbarButtons + mLaunchpadDevice.GetButton(ToolbarButton.Down).TurnOffLight(); + mLaunchpadDevice.GetButton(ToolbarButton.Left).TurnOffLight(); + mLaunchpadDevice.GetButton(ToolbarButton.Mixer).TurnOffLight(); + mLaunchpadDevice.GetButton(ToolbarButton.Right).TurnOffLight(); + mLaunchpadDevice.GetButton(ToolbarButton.Session).TurnOffLight(); + mLaunchpadDevice.GetButton(ToolbarButton.Up).TurnOffLight(); + mLaunchpadDevice.GetButton(ToolbarButton.User1).TurnOffLight(); + mLaunchpadDevice.GetButton(ToolbarButton.User2).TurnOffLight(); + + //SidebarButtons + mLaunchpadDevice.GetButton(SideButton.Arm).TurnOffLight(); + mLaunchpadDevice.GetButton(SideButton.Pan).TurnOffLight(); + mLaunchpadDevice.GetButton(SideButton.Solo).TurnOffLight(); + mLaunchpadDevice.GetButton(SideButton.SoundA).TurnOffLight(); + mLaunchpadDevice.GetButton(SideButton.SoundB).TurnOffLight(); + mLaunchpadDevice.GetButton(SideButton.Stop).TurnOffLight(); + mLaunchpadDevice.GetButton(SideButton.TrackOn).TurnOffLight(); + mLaunchpadDevice.GetButton(SideButton.Volume).TurnOffLight(); + + } + } +} diff --git a/launchpad-master/IntelOrca.LaunchpadTests/Program.cs b/launchpad-master/IntelOrca.LaunchpadTests/Program.cs new file mode 100644 index 0000000..406cd55 --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/Program.cs @@ -0,0 +1,90 @@ +using IntelOrca.Launchpad; +using System; + +namespace IntelOrca.LaunchpadTests +{ + class Program + { + static void Main(string[] args) + { + LaunchpadDevice device; + + Console.WriteLine("Launchpad Tests"); + Console.WriteLine("Ted John 2013"); + Console.WriteLine(""); + + try + { + device = new LaunchpadDevice(); + device.DoubleBuffered = true; + + Console.WriteLine("Launchpad found"); + } + catch + { + Console.WriteLine("No launchpad found"); + Console.ReadLine(); + return; + } + + Console.WriteLine(""); + Console.WriteLine("0: Grid toggle"); + Console.WriteLine("1: Scrolling message"); + Console.WriteLine("2: Bulldog"); + Console.WriteLine("3: Rain sequencer"); + Console.WriteLine("4: Reversi"); + Console.WriteLine("5: Snake"); + Console.WriteLine("6: Geometrische Tests"); + Console.WriteLine("7: Super Safe Password Generator"); + + int i; + while (!Int32.TryParse(Console.ReadLine(), out i)) + { + Console.WriteLine("Try again..."); + } + + switch (i) + { + case 0: + ToggleGrid toggleGrid = new ToggleGrid(device); + toggleGrid.Run(); + break; + case 1: + Console.Write("Type a message:"); + string message = Console.ReadLine(); + + ScrollingLetters scrollingLetters = new ScrollingLetters(device); + scrollingLetters.Text = message.ToUpper(); + scrollingLetters.ScrollText(); + break; + case 2: + Bulldog bulldog = new Bulldog(device); + bulldog.Play(); + break; + case 3: + RainSequencer rain = new RainSequencer(device); + rain.Run(); + break; + case 4: + Reversi reversi = new Reversi(device); + reversi.Run(); + break; + case 5: + Snake snake = new Snake(device); + snake.Run(); + break; + case 6: + GeometrischeTests geoTest = new GeometrischeTests(device); + geoTest.Run(); + break; + case 7: + PWGenerator pwGen = new PWGenerator(device); + pwGen.Run(); + break; + default: + Console.WriteLine("No such application"); + break; + } + } + } +} diff --git a/launchpad-master/IntelOrca.LaunchpadTests/Properties/AssemblyInfo.cs b/launchpad-master/IntelOrca.LaunchpadTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..45d24de --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("IntelOrca.LaunchpadTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("IntelOrca.LaunchpadTests")] +[assembly: AssemblyCopyright("Copyright © Ted John 2013")] +[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("3ca042d4-ea08-4d51-a0c7-1f1ef0ece762")] + +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/launchpad-master/IntelOrca.LaunchpadTests/RainSequencer.cs b/launchpad-master/IntelOrca.LaunchpadTests/RainSequencer.cs new file mode 100644 index 0000000..2ebc65d --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/RainSequencer.cs @@ -0,0 +1,266 @@ +using IntelOrca.Launchpad; +using Midi; +using System; +using System.Threading; + +namespace IntelOrca.LaunchpadTests +{ + class RainSequencer + { + const int NumCols = 16; + const int NumRows = 8; + + private LaunchpadDevice mLaunchpadDevice; + + private bool[,] mSequence = new bool[NumCols, NumRows]; + private bool[] mRemove = new bool[NumCols]; + private int mSequenceOffset; + private int mColumnOffset; + private int mMode, mSoundType; + private int mConfirmTime; + private bool mForceDraw; + + private int mTempo = 8 * 60; + + OutputDevice mOutputDevice; + private int mInstrument = 0; + private int mPercussion = 0; + + public RainSequencer(LaunchpadDevice device) + { + mLaunchpadDevice = device; + mOutputDevice = OutputDevice.InstalledDevices[0]; + mOutputDevice.Open(); + + mLaunchpadDevice.ButtonPressed += mLaunchpadDevice_ButtonPressed; + +/* + Random rand = new Random(); + for (int y = 0; y < NumRows; y++) + for (int x = 0; x < NumRows; x++) + if (rand.Next(0, 12) == 0) + mSequence[x, y] = true; + * */ + } + + private int SeqYtoButtonY(int y) + { + return 0; + } + + private int ButtonYtoSeqY(int y) + { + return (y + NumRows - mSequenceOffset) % NumRows; + } + + private int ButtonXtoColX(int x) + { + return (x + NumCols - mColumnOffset) % NumCols; + } + + private void mLaunchpadDevice_ButtonPressed(object sender, ButtonPressEventArgs e) + { + if (e.Type == ButtonType.Grid) { + if (e.Y == 7) { + mRemove[ButtonXtoColX(e.X)] = !mRemove[ButtonXtoColX(e.X)]; + if (mMode == 1) + PlayNoise(e.X); + } else { + mSequence[ButtonXtoColX(e.X), ButtonYtoSeqY(e.Y)] = !mSequence[ButtonXtoColX(e.X), ButtonYtoSeqY(e.Y)]; + } + + mForceDraw = true; + } else if (e.Type == ButtonType.Toolbar) { + switch (e.ToolbarButton) { + case ToolbarButton.Up: + mSequenceOffset = (mSequenceOffset + NumRows - 1) % NumRows; + break; + case ToolbarButton.Down: + mSequenceOffset = (mSequenceOffset + 1) % NumRows; + break; + case ToolbarButton.Left: + mColumnOffset = (mColumnOffset + NumCols - 1) % NumCols; + break; + case ToolbarButton.Right: + mColumnOffset = (mColumnOffset + 1) % NumCols; + break; + case ToolbarButton.Mixer: + mMode = (mMode == 0 ? 1 : 0); + break; + case ToolbarButton.Session: + if (mConfirmTime > 0) { + for (int y = 0; y < NumRows; y++) + for (int x = 0; x < NumCols; x++) + mSequence[x, y] = false; + mConfirmTime = 0; + } else { + mConfirmTime = 1000; + } + break; + } + mForceDraw = true; + } else if (e.Type == ButtonType.Side) { + switch (e.SidebarButton) { + case SideButton.Volume: + if (mTempo < 1980) + mTempo += 20; + break; + case SideButton.Pan: + if (mTempo > 20) + mTempo -= 20; + break; + case SideButton.SoundA: + if (mSoundType == 0) { + mPercussion = (mPercussion + 1) % (Percussion.OpenTriangle - Percussion.BassDrum2 + 1); + mOutputDevice.SendProgramChange(Channel.Channel1, (Instrument)mInstrument); + } else { + mSoundType = 0; + } + break; + case SideButton.SoundB: + if (mSoundType == 1) { + mInstrument = (mInstrument + 1) % 127; + mOutputDevice.SendProgramChange(Channel.Channel1, (Instrument)mInstrument); + } else { + mSoundType = 1; + } + break; + case SideButton.Arm: + mSequenceOffset = 0; + mColumnOffset = 0; + break; + } + } + } + + public void Run() + { + long last_tick = Environment.TickCount; + long delay = 1; + long duration = 0; + + while (true) { + delay = (int)(1000.0 / (mTempo / 60.0)); + duration = Environment.TickCount - last_tick; + if (duration < delay) { + if (mForceDraw) { + Draw(); + mForceDraw = false; + } + + continue; + } + last_tick = Environment.TickCount; + mConfirmTime = Math.Max(0, mConfirmTime - (int)duration); + + Update(); + Draw(); + } + } + + private void PlayNoise(int tone) + { + new Thread(new ThreadStart(() => { + if (mSoundType == 0) { + mOutputDevice.SendPercussion(Percussion.BassDrum2 + mPercussion + tone, 127); + } else { + mOutputDevice.SendNoteOn(Channel.Channel1, Pitch.A4 + tone, 127); + Thread.Sleep((int)(1000.0 / (mTempo / 60.0))); + mOutputDevice.SendNoteOff(Channel.Channel1, Pitch.A4 + tone, 127); + } + + // Console.Beep(100 * (tone + 3), 100); + })).Start(); + } + + private void DiscardRedBeats() + { + int y = ButtonYtoSeqY(7); + for (int x = 0; x < NumCols; x++) + if (mSequence[x, y] && mRemove[x]) + mSequence[x, y] = false; + } + + private void PlayBeats() + { + int y = ButtonYtoSeqY(7); + for (int x = 0; x < NumCols; x++) + if (mSequence[x, y]) + PlayNoise(x); + } + + private void Update() + { + if (mMode == 1) + return; + + DiscardRedBeats(); + mSequenceOffset = (mSequenceOffset + 1) % NumRows; + PlayBeats(); + } + + private void Draw() + { + ButtonBrightness[,] redgrid = new ButtonBrightness[8, 8]; + ButtonBrightness[,] greengrid = new ButtonBrightness[8, 8]; + + for (int y = 0; y < 7; y++) + for (int x = 0; x < 8; x++) + if (mSequence[ButtonXtoColX(x), ButtonYtoSeqY(y)]) + redgrid[x, y] = greengrid[x, y] = ButtonBrightness.Full; + + for (int x = 0; x < 8; x++) { + ButtonBrightness brightness = (mSequence[ButtonXtoColX(x), ButtonYtoSeqY(7)] ? ButtonBrightness.Full : ButtonBrightness.Low); + + if (!mRemove[ButtonXtoColX(x)]) + greengrid[x, 7] = brightness; + else + redgrid[x, 7] = brightness; + } + + for (int y = 0; y < 8; y++) + for (int x = 0; x < 8; x++) + mLaunchpadDevice[x, y].SetBrightness(redgrid[x, y], greengrid[x, y]); + + if (mConfirmTime > 0) + mLaunchpadDevice.GetButton(ToolbarButton.Session).TurnOnLight(); + else + mLaunchpadDevice.GetButton(ToolbarButton.Session).TurnOffLight(); + + if (mMode == 1) + mLaunchpadDevice.GetButton(ToolbarButton.Mixer).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + else + mLaunchpadDevice.GetButton(ToolbarButton.Mixer).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + + if (mSoundType == 0) { + mLaunchpadDevice.GetButton(SideButton.SoundA).TurnOnLight(); + mLaunchpadDevice.GetButton(SideButton.SoundB).TurnOffLight(); + } else { + mLaunchpadDevice.GetButton(SideButton.SoundA).TurnOffLight(); + mLaunchpadDevice.GetButton(SideButton.SoundB).TurnOnLight(); + } + + mLaunchpadDevice.GetButton(ToolbarButton.Up).TurnOnLight(); + mLaunchpadDevice.GetButton(ToolbarButton.Down).TurnOnLight(); + mLaunchpadDevice.GetButton(ToolbarButton.Left).TurnOnLight(); + mLaunchpadDevice.GetButton(ToolbarButton.Right).TurnOnLight(); + + mLaunchpadDevice.GetButton(SideButton.Volume).TurnOnLight(); + mLaunchpadDevice.GetButton(SideButton.Pan).TurnOnLight(); + mLaunchpadDevice.GetButton(SideButton.Arm).TurnOnLight(); + + mLaunchpadDevice.Refresh(); + + Console.SetCursorPosition(0, 0); + for (int y = 0; y < NumRows; y++) { + for (int x = 0; x < NumCols; x++) { + if (mSequence[x, y]) + Console.Write("X"); + else + Console.Write("."); + } + Console.WriteLine(); + } + } + } +} diff --git a/launchpad-master/IntelOrca.LaunchpadTests/Reversi.cs b/launchpad-master/IntelOrca.LaunchpadTests/Reversi.cs new file mode 100644 index 0000000..1e54c48 --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/Reversi.cs @@ -0,0 +1,279 @@ +using IntelOrca.Launchpad; +using Midi; +using System; +using System.Collections.Generic; + +namespace IntelOrca.LaunchpadTests +{ + class Reversi + { + private Random mRandom = new Random(); + + private LaunchpadDevice mLaunchpadDevice; + private int[,] mGrid = new int[8, 8]; + private int mPlayerTurn = 1; + private int mPlayerWinning = 0; + + private int mConfirmTime; + private bool mForceDraw; + private int mGameState = 0; + private bool mShowPossibleMoves = true; + private bool mSolo = false; + + private int mFlashCounter = 0; + + private List> mPossiblePlaces = new List>(); + + OutputDevice mOutputDevice; + + public Reversi(LaunchpadDevice device) + { + mLaunchpadDevice = device; + + mLaunchpadDevice.DoubleBuffered = false; + mLaunchpadDevice.ButtonPressed += mLaunchpadDevice_ButtonPressed; + + mOutputDevice = OutputDevice.InstalledDevices[0]; + mOutputDevice.Open(); + + Restart(); + } + + private void mLaunchpadDevice_ButtonPressed(object sender, ButtonPressEventArgs e) + { + if (e.Type == ButtonType.Grid) { + if (CanPlaceAt(e.X, e.Y)) + PlaceAt(e.X, e.Y); + } else if (e.Type == ButtonType.Side) { + if (e.SidebarButton == SideButton.TrackOn) { + mShowPossibleMoves = !mShowPossibleMoves; + mForceDraw = true; + } else if (e.SidebarButton == SideButton.Solo) { + mSolo = !mSolo; + } + } else if (e.Type == ButtonType.Toolbar) { + if (e.ToolbarButton == ToolbarButton.Session) { + if (mConfirmTime > 0) { + Restart(); + mConfirmTime = 0; + } else { + mConfirmTime = 1000; + } + } + } + } + + private void Restart() + { + for (int y = 0; y < 8; y++) + for (int x = 0; x < 8; x++) + mGrid[x, y] = 0; + + mGrid[3, 3] = 1; + mGrid[4, 3] = 2; + mGrid[3, 4] = 2; + mGrid[4, 4] = 1; + mGameState = 0; + SetPlayerGo(1); + + mOutputDevice.SendPercussion(Percussion.LongWhistle, 127); + } + + private bool CanPlaceAt(int x, int y) + { + if (mGrid[x, y] != 0) + return false; + + for (int dy = -1; dy <= 1; dy++) + for (int dx = -1; dx <= 1; dx++) + if (CheckDirection(x, y, dx, dy)) + return true; + + return false; + } + + private bool CheckDirection(int x, int y, int dx, int dy, int state = 0) + { + x += dx; + y += dy; + if (!InBounds(x, y)) + return false; + if (state == 0) { + if (mGrid[x, y] == 0 || mGrid[x, y] == mPlayerTurn) + return false; + return CheckDirection(x, y, dx, dy, 1); + } else if (state == 1) { + if (mGrid[x, y] == 0) + return false; + if (mGrid[x, y] == mPlayerTurn) + return true; + return CheckDirection(x, y, dx, dy, 1); + } + + return false; + } + + private bool InBounds(int x, int y) + { + return (x >= 0 && y >= 0 && x < 8 && y < 8); + } + + private void PlaceAt(int x, int y) + { + if (mPlayerTurn == 1) + mOutputDevice.SendPercussion(Percussion.SnareDrum1, 127); + else if (mPlayerTurn == 2) + mOutputDevice.SendPercussion(Percussion.SnareDrum2, 127); + + for (int dy = -1; dy <= 1; dy++) + for (int dx = -1; dx <= 1; dx++) + if (CheckDirection(x, y, dx, dy)) + SwapDirection(x, y, dx, dy); + + mGrid[x, y] = mPlayerTurn; + SetPlayerGo((mPlayerTurn == 1 ? 2 : 1)); + } + + private void SwapDirection(int x, int y, int dx, int dy) + { + x += dx; + y += dy; + if (mGrid[x, y] != mPlayerTurn) { + mGrid[x, y] = mPlayerTurn; + SwapDirection(x, y, dx, dy); + } + } + + private void UpdatePossiblePlaces() + { + mPossiblePlaces.Clear(); + for (int y = 0; y < 8; y++) + for (int x = 0; x < 8; x++) + if (CanPlaceAt(x, y)) + mPossiblePlaces.Add(new Tuple(x, y)); + } + + private void SetPlayerGo(int n) + { + mPlayerTurn = n; + UpdatePossiblePlaces(); + if (mPossiblePlaces.Count == 0) { + mPlayerTurn = (mPlayerTurn == 1 ? 2 : 1); + UpdatePossiblePlaces(); + if (mPossiblePlaces.Count == 0) { + mGameState = 1; + mConfirmTime = int.MaxValue; + mOutputDevice.SendPercussion(Percussion.CrashCymbal1, 127); + } + } + mPlayerWinning = GetWinner(); + + if (mPossiblePlaces.Count > 0 && mSolo && mPlayerTurn == 2) { + int p = mRandom.Next(0, mPossiblePlaces.Count); + PlaceAt(mPossiblePlaces[p].Item1, mPossiblePlaces[p].Item2); + } + + mForceDraw = true; + } + + private int GetWinner() + { + int p1 = 0, p2 = 0; + for (int y = 0; y < 8; y++) { + for (int x = 0; x < 8; x++) { + if (mGrid[x, y] == 1) + p1++; + else if (mGrid[x, y] == 2) + p2++; + } + } + if (p1 > p2) + return 1; + else if (p2 > p1) + return 2; + else + return 0; + } + + public void Run() + { + long last_tick = Environment.TickCount; + long delay = 1; + long duration = 0; + + while (true) { + delay = (int)(1000.0 / 60.0); + duration = Environment.TickCount - last_tick; + if (duration < delay) { + if (mForceDraw) { + Draw(); + mForceDraw = false; + } + + continue; + } + last_tick = Environment.TickCount; + mConfirmTime = Math.Max(0, mConfirmTime - (int)duration); + mFlashCounter = (mFlashCounter + 1) % 20; + + Draw(); + } + } + + private void Draw() + { + ButtonBrightness[,] redgrid = new ButtonBrightness[8, 8]; + ButtonBrightness[,] greengrid = new ButtonBrightness[8, 8]; + + for (int y = 0; y < 8; y++) { + for (int x = 0; x < 8; x++) { + if (mGrid[x, y] == 1) { + redgrid[x, y] = ButtonBrightness.Full; + } else if (mGrid[x, y] == 2) { + greengrid[x, y] = ButtonBrightness.Full; + } else if (mShowPossibleMoves && mFlashCounter < 10) { + if (mPossiblePlaces.Exists(p => p.Item1 == x && p.Item2 == y)) { + redgrid[x, y] = ButtonBrightness.Low; + greengrid[x, y] = ButtonBrightness.Low; + } + } + } + } + + for (int y = 0; y < 8; y++) + for (int x = 0; x < 8; x++) + mLaunchpadDevice[x, y].SetBrightness(redgrid[x, y], greengrid[x, y]); + + if (mPlayerTurn == 1) { + mLaunchpadDevice.GetButton(ToolbarButton.User1).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice.GetButton(ToolbarButton.User2).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Off); + } else if (mPlayerTurn == 2) { + mLaunchpadDevice.GetButton(ToolbarButton.User1).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Off); + mLaunchpadDevice.GetButton(ToolbarButton.User2).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + } + + if (mPlayerWinning == 1) { + mLaunchpadDevice.GetButton(ToolbarButton.Mixer).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + } else if (mPlayerWinning == 2) { + mLaunchpadDevice.GetButton(ToolbarButton.Mixer).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + } else { + mLaunchpadDevice.GetButton(ToolbarButton.Mixer).SetBrightness(ButtonBrightness.Off, ButtonBrightness.Off); + } + + if (mShowPossibleMoves) + mLaunchpadDevice.GetButton(SideButton.TrackOn).TurnOnLight(); + else + mLaunchpadDevice.GetButton(SideButton.TrackOn).TurnOffLight(); + + if (mConfirmTime > 0) + mLaunchpadDevice.GetButton(ToolbarButton.Session).TurnOnLight(); + else + mLaunchpadDevice.GetButton(ToolbarButton.Session).TurnOffLight(); + + if (mSolo) + mLaunchpadDevice.GetButton(SideButton.Solo).TurnOnLight(); + else + mLaunchpadDevice.GetButton(SideButton.Solo).TurnOffLight(); + } + } +} diff --git a/launchpad-master/IntelOrca.LaunchpadTests/ScrollingLetters.cs b/launchpad-master/IntelOrca.LaunchpadTests/ScrollingLetters.cs new file mode 100644 index 0000000..03597ef --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/ScrollingLetters.cs @@ -0,0 +1,177 @@ +using IntelOrca.Launchpad; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace IntelOrca.LaunchpadTests +{ + class ScrollingLetters + { + class CharacterDefinition + { + private char mKey; + private int mWidth; + private int mHeight; + private List mPoints = new List(); + + public CharacterDefinition(StringReader sr) + { + string key; + + // Read key + while ((key = sr.ReadLine()).Length == 0); + mKey = key[0]; + + // Read characters + mHeight = 5; + for (int y = 0; y < mHeight; y++) { + string line = sr.ReadLine(); + for (int x = 0; x < line.Length; x++) + if (line[x] == 'X') + mPoints.Add(new Point(x, y)); + } + + mWidth = mPoints.Max(p => p.X) + 1; + } + + public override int GetHashCode() + { + return mKey.GetHashCode(); + } + + public char Key + { + get { return mKey; } + } + + public int Width + { + get { return mWidth; } + set { mWidth = value; } + } + + public int Height + { + get { return mHeight; } + set { mHeight = value; } + } + + public Point[] Points + { + get { return mPoints.ToArray(); } + } + + public struct Point + { + public Point(int x, int y) : this() { X = x; Y = y; } + + public int X { get; set; } + public int Y { get; set; } + + public override string ToString() + { + return String.Format("X = {0} Y = {1}", X, Y); + } + } + } + + private static Dictionary mCharacterDefinitions = GetCharacterDefinitions(); + + private LaunchpadDevice mLaunchpadDevice; + private string mText = String.Empty; + private int mTextOffset = 8; + + private bool[,] mGrid = new bool[8, 8]; + + private static Dictionary GetCharacterDefinitions() + { + var defs = new Dictionary(); + + StringReader sr = new StringReader(File.ReadAllText("text.txt")); + while (sr.Peek() != -1) { + if (Char.IsWhiteSpace((char)sr.Peek())) { + sr.Read(); + continue; + } + + CharacterDefinition cd = new CharacterDefinition(sr); + defs.Add(cd.Key, cd); + } + + return defs; + } + + public ScrollingLetters(LaunchpadDevice device) + { + mLaunchpadDevice = device; + } + + private void SetGrid(int x, int y, bool value) + { + if (x >= 0 && y >= 0 && x < 8 && y < 8) + mGrid[x, y] = value; + } + + private int RenderCharacter(char c, int x, int y) + { + if (mCharacterDefinitions.ContainsKey(c)) { + CharacterDefinition cd = mCharacterDefinitions[c]; + if (x + cd.Width >= 0 && x < 8) + Array.ForEach(cd.Points, p => SetGrid(x + p.X, y + p.Y, true)); + return cd.Width; + } + + return 4; + } + + private void RenderGrid() + { + int x, y; + + for (y = 0; y < 8; y++) + for (x = 0; x < 8; x++) + mGrid[x, y] = false; + + x = mTextOffset; + y = 1; + foreach (char c in mText) { + x += RenderCharacter(c, x, y) + 1; + } + + for (y = 0; y < 8; y++) { + for (x = 0; x < 8; x++) { + if (mGrid[x, y]) + mLaunchpadDevice[x, y].SetBrightness(ButtonBrightness.Full, ButtonBrightness.Full); + else + mLaunchpadDevice[x, y].SetBrightness(ButtonBrightness.Off, ButtonBrightness.Off); + } + } + mLaunchpadDevice.Refresh(); + } + + public void ScrollText() + { + long last_tick = 0; + long delay = 100; + + while (true) { + if (Environment.TickCount - last_tick < delay) + continue; + last_tick = Environment.TickCount; + + RenderGrid(); + + mTextOffset--; + if (mTextOffset < -(mText.Length * 5 + 2)) + mTextOffset = 8; + } + } + + public string Text + { + get { return mText; } + set { mText = value; } + } + } +} diff --git a/launchpad-master/IntelOrca.LaunchpadTests/Snake.cs b/launchpad-master/IntelOrca.LaunchpadTests/Snake.cs new file mode 100644 index 0000000..bf7dc3c --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/Snake.cs @@ -0,0 +1,233 @@ +using IntelOrca.Launchpad; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace IntelOrca.LaunchpadTests +{ + struct Point + { + public Point(int x, int y) + : this() + { + X = x; + Y = y; + } + + public int X { get; set; } + public int Y { get; set; } + } + + class Snake + { + private LaunchpadDevice mLaunchpadDevice; + + private Random mRandom = new Random(); + private long mCurrentTicks = 0; + + private Point[] mBody; + private Point mDirection; + + private bool mFoodActive; + private Point mFood; + + private bool snakeFrozen = false; + private bool gameEnd = false; + + public Snake(LaunchpadDevice device) + { + mLaunchpadDevice = device; + + mLaunchpadDevice.ButtonPressed += mLaunchpadDevice_ButtonPressed; + mLaunchpadDevice.GetButton(SideButton.Arm).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + mLaunchpadDevice.GetButton(SideButton.Solo).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Full); + + Restart(); + } + + private void mLaunchpadDevice_ButtonPressed(object sender, ButtonPressEventArgs e) + { + if (e.Type == ButtonType.Grid) { + mFood.X = e.X; + mFood.Y = e.Y; + mFoodActive = true; + } + + if (e.Type == ButtonType.Side) + { + if(e.SidebarButton == SideButton.Solo) + { + if (snakeFrozen) + snakeFrozen = false; + else + snakeFrozen = true; + } + } + + if (e.Type == ButtonType.Side) + { + if (e.SidebarButton == SideButton.Arm) + { + mLaunchpadDevice.Reset(); + gameEnd = true; + } + } + + } + + public void Run() + { + // Kontrolle des internen Ticks (6*12 Frames werden pro "nächster" aktion verstreichen gelassen) + long last_tick = Environment.TickCount; + long delay = 12; + delay = 12 * 6; + + // GameLoop + while (true && !gameEnd) { + if (Environment.TickCount - last_tick < delay) + continue; + mCurrentTicks += Environment.TickCount - last_tick; + last_tick = Environment.TickCount; + + // Spielroutine (mit "Pause" Funktion [bei einem Snake Spiel natürlich quasi cheaten xD]) + if(!snakeFrozen) + { + // Update aller Positionen + Update(); + // Neu Zeichnen des Brettes + Draw(); + } + + } + } + + private void Update() + { + SetSnakeDirection(); + MoveSnake(); + } + + private void Draw() + { + ButtonBrightness[,] redgrid = new ButtonBrightness[8, 8]; + ButtonBrightness[,] greengrid = new ButtonBrightness[8, 8]; + + // Draw snake + foreach (Point p in mBody) + if (InBounds(p)) + greengrid[p.X, p.Y] = ButtonBrightness.Full; + if (InBounds(mBody[0])) + redgrid[mBody[0].X, mBody[0].Y] = ButtonBrightness.Full; + + // Draw food + if (mFoodActive) + redgrid[mFood.X, mFood.Y] = ButtonBrightness.Full; + + // Invalidate (Neu Zeichnen aller Kacheln anhand was in red/greengrid drin steht + for (int y = 0; y < 8; y++) + for (int x = 0; x < 8; x++) + mLaunchpadDevice[x, y].SetBrightness(redgrid[x, y], greengrid[x, y]); + // Refresh um neue Konstellation anzuzeigen + mLaunchpadDevice.Refresh(); + } + + private void Restart() + { + mBody = new Point[4]; + for (int y = 8; y < 8 + 4; y++) + mBody[y - 8] = new Point(3, y); + mDirection = new Point(0, -1); + + mFoodActive = false; + + // PlaceFood(); + } + + private void MoveSnake() + { + // Nachrücken aller Steine nach vorne (Letzer wird entfernt) + for (int i = mBody.Length - 1; i > 0; i--) + mBody[i] = mBody[i - 1]; + //Kopf wird in Richtung bewegt + mBody[0].X += mDirection.X; + mBody[0].Y += mDirection.Y; + + // Wenn + for (int i = 1; i < mBody.Length; i++) { + if (mBody[0].X == mBody[i].X && mBody[0].Y == mBody[i].Y) + Restart(); + } + + if (mBody[0].X == mFood.X && mBody[0].Y == mFood.Y) { + mFoodActive = false; + ExtendSnake(); + // PlaceFood(); + } + } + + public void SetSnakeDirection() + { + bool danger = false; + + Point head = mBody[0]; + if (head.X == 7 && mDirection.X > 0) { + if (head.Y < 7) mDirection = new Point(0, 1); + else mDirection = new Point(0, -1); + danger = true; + } else if (head.X == 0 && mDirection.X < 0) { + if (head.Y < 7) mDirection = new Point(0, 1); + else mDirection = new Point(0, -1); + danger = true; + } + + if (head.Y == 7 && mDirection.Y > 0) { + if (head.X < 7) mDirection = new Point(1, 0); + else mDirection = new Point(-1, 0); + danger = true; + } else if (head.Y == 0 && mDirection.Y < 0) { + if (head.X < 7) mDirection = new Point(1, 0); + else mDirection = new Point(-1, 0); + danger = true; + } + + if (!danger && mFoodActive) { + if (mDirection.X != 0) { + if (mBody[0].X == mFood.X) { + if (mBody[0].Y < mFood.Y) mDirection = new Point(0, 1); + else mDirection = new Point(0, -1); + } + } else { + if (mBody[0].Y == mFood.Y) { + if (mBody[0].X < mFood.X) mDirection = new Point(1, 0); + else mDirection = new Point(-1, 0); + } + } + } + } + + private void ExtendSnake() + { + Point[] newBody = new Point[mBody.Length + 1]; + newBody[0] = mBody[0]; + Array.Copy(mBody, 0, newBody, 1, mBody.Length); + mBody = newBody; + } + + private void PlaceFood() + { + List possiblePlaces = new List(); + for (int y = 0; y < 8; y++) + for (int x = 0; x < 8; x++) + if (!mBody.Contains(new Point(x, y))) + possiblePlaces.Add(new Point(x, y)); + + int index = mRandom.Next(possiblePlaces.Count); + mFood = possiblePlaces[index]; + } + + private bool InBounds(Point p) + { + return (p.X >= 0 && p.Y >= 0 && p.X < 8 && p.Y < 8); + } + } +} diff --git a/launchpad-master/IntelOrca.LaunchpadTests/ToggleGrid.cs b/launchpad-master/IntelOrca.LaunchpadTests/ToggleGrid.cs new file mode 100644 index 0000000..b84ce49 --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/ToggleGrid.cs @@ -0,0 +1,60 @@ +using IntelOrca.Launchpad; + +namespace IntelOrca.LaunchpadTests +{ + class ToggleGrid + { + private LaunchpadDevice mLaunchpadDevice; + + public ToggleGrid(LaunchpadDevice device) + { + mLaunchpadDevice = device; + + mLaunchpadDevice.DoubleBuffered = false; + mLaunchpadDevice.ButtonPressed += mLaunchpadDevice_ButtonPressed; + + mLaunchpadDevice.GetButton(ToolbarButton.Session).SetBrightness(ButtonBrightness.Full, ButtonBrightness.Full); + + + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + mLaunchpadDevice[x, y].SetBrightness((ButtonBrightness)x, (ButtonBrightness)y); + } + } + + } + + private void mLaunchpadDevice_ButtonPressed(object sender, ButtonPressEventArgs e) + { + if (e.Type == ButtonType.Grid) { + LaunchpadButton button = mLaunchpadDevice[e.X, e.Y]; + if (button.RedBrightness == ButtonBrightness.Off && button.GreenBrightness == ButtonBrightness.Off) + button.SetBrightness(ButtonBrightness.Full, ButtonBrightness.Full); + else + button.SetBrightness(ButtonBrightness.Off, ButtonBrightness.Off); + + /* + if (button.RedBrightness == ButtonBrightness.Off && button.GreenBrightness == ButtonBrightness.Off) + button.SetBrightness(ButtonBrightness.Full, ButtonBrightness.Off); + else if (button.RedBrightness == ButtonBrightness.Full && button.GreenBrightness == ButtonBrightness.Off) + button.SetBrightness(ButtonBrightness.Off, ButtonBrightness.Full); + else if (button.RedBrightness == ButtonBrightness.Off && button.GreenBrightness == ButtonBrightness.Full) + button.SetBrightness(ButtonBrightness.Full, ButtonBrightness.Full); + else + button.SetBrightness(ButtonBrightness.Off, ButtonBrightness.Off); + */ + } else if (e.Type == ButtonType.Toolbar) { + if (e.ToolbarButton == ToolbarButton.Session) { + for (int y = 0; y < 8; y++) + for (int x = 0; x < 8; x++) + mLaunchpadDevice[x, y].TurnOffLight(); + } + } + } + + public void Run() + { + while (true) ; + } + } +} diff --git a/launchpad-master/IntelOrca.LaunchpadTests/packages.config b/launchpad-master/IntelOrca.LaunchpadTests/packages.config new file mode 100644 index 0000000..32a593b --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/launchpad-master/IntelOrca.LaunchpadTests/text.txt b/launchpad-master/IntelOrca.LaunchpadTests/text.txt new file mode 100644 index 0000000..3dee2bc --- /dev/null +++ b/launchpad-master/IntelOrca.LaunchpadTests/text.txt @@ -0,0 +1,293 @@ +! +X +X +X + +X + +" +X X +X X + + + + +# + X X +XXXXX + X X +XXXXX + X X + +' +X +X + + + + +, + + + +X +X + +. + + + + +X + +0 + XX +X X +X X +X X + XX + +1 +X +X +X +X +X + +2 +XXX + X + X + X +XXXX + +3 +XXX + X + XX + X +XXX + +4 + X + XX + X X +XXXXX + X + +5 +XXXX +X +XXX + X +XXX + +6 + XX +X +XXX +X X + XX + +7 +XXXX + X + X + X +X + +8 +XXXX +X X +XXXX +X X +XXXX + +9 + XX +X X + XXX + X + XX + +A +XXXX +X X +XXXX +X X +X X + +B +XXX +X X +XXXX +X X +XXX + +C + XXX +X +X +X + XXX + +D +XXX +X X +X X +X X +XXX + +E +XXXX +X +XXX +X +XXXX + +F +XXXX +X +XXX +X +X + +G + XXX +X +X XX +X X + XXX + +H +X X +X X +XXXX +X X +X X + +I +X +X +X +X +X + +J +XXXX + X + X + X +XXX + +K +X X +X X +XX +X X +X X + +L +X +X +X +X +XXXX + +M +X X +XX XX +X X X +X X +X X + +N +X X +XX X +X X X +X XX +X X + +O + XXX +X X +X X +X X + XXX + +P +XXX +X X +XXX +X +X + +Q + XX +X X +X X + XX + XX + +R +XXX +X X +XXX +X X +X X + +S + XXX +X + XX + X +XXX + +T +XXXXX + X + X + X + X + +U +X X +X X +X X +X X + XXX + +V +X X +X X + X X + X X + X + +W +X X +X X +X X +X X X + X X + +X +X X + X X + X + X X +X X + +Y +X X + X X + X + X + X + +Z +XXXXX + X + X + X +XXXXX \ No newline at end of file diff --git a/launchpad-master/launchpad-master.zip b/launchpad-master/launchpad-master.zip new file mode 100644 index 0000000000000000000000000000000000000000..2a29c4cce566fea3b457cbd593bca8d992af4ec1 GIT binary patch literal 22112 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 ini?i1nwqDW8ylG!n