diff --git a/Animation.pde b/Animation.pde
new file mode 100644
index 0000000..da2a8e6
--- /dev/null
+++ b/Animation.pde
@@ -0,0 +1,114 @@
+class MText extends Object {
+ float x;
+ float y;
+ int endx;
+ int endy;
+ int startx;
+ int starty;
+ int duration;
+ int step;
+ boolean animate = false;
+ boolean done = false;
+ String text;
+
+ MText(String text, int x, int y) {
+ this.x = (float)x;
+ this.y = (float)y;
+ this.startx = x;
+ this.starty = y;
+ this.text = text;
+ }
+
+ void animate() {
+ animate = true;
+ }
+
+ void setDuration(int duration) {
+ this.duration = duration;
+ }
+
+ void setDestination(int x, int y) {
+ endx = x;
+ endy = y;
+ }
+
+ boolean isDone() {
+ return done;
+ }
+
+ void draw() {
+ if (step < duration) {
+ x = (x + ((float)(endx-startx)/(float)duration));
+ y = (y + ((float)(endy-starty)/(float)duration));
+ step++;
+ if (step == duration) done = true;
+ }
+ if (animate) {
+ fill(#FFFFFF);
+ textFont(f40, 40);
+ textAlign(CENTER);
+ smooth();
+ text(text, (int)x, (int)y);
+ }
+ }
+
+}
+
+class MLine extends Object {
+ float x;
+ float y;
+ int endx;
+ int endy;
+ int startx;
+ int starty;
+ int duration;
+ int step;
+ boolean animate = false;
+ boolean done = false;
+
+ MLine(int x, int y) {
+ this.x = (float)x;
+ this.y = (float)y;
+ this.startx = x;
+ this.starty = y;
+ }
+
+ void animate() {
+ animate = true;
+ }
+
+ void setDuration(int duration) {
+ this.duration = duration;
+ }
+
+ void setDestination(int x, int y) {
+ endx = x;
+ endy = y;
+ }
+
+ boolean isDone() {
+ return done;
+ }
+
+ void draw() {
+ if (step < duration) {
+ x = (x + ((float)(endx-startx)/(float)duration));
+ y = (y + ((float)(endy-starty)/(float)duration));
+ step++;
+ if (step == duration) done = true;
+ }
+ if (animate) {
+ stroke(#FFFFFF);
+ smooth();
+ line(startx, starty, x, y);
+ }
+ }
+
+}
+
+void sleep(int m) {
+ float now = millis();
+ while(millis() < now + (float)m) {
+
+ }
+}
diff --git a/Builds/Base/Katapult Documentation.pdf b/Builds/Base/Katapult Documentation.pdf
new file mode 100644
index 0000000..83110b5
Binary files /dev/null and b/Builds/Base/Katapult Documentation.pdf differ
diff --git a/Builds/Base/Read me.txt b/Builds/Base/Read me.txt
new file mode 100755
index 0000000..ad7e2d7
--- /dev/null
+++ b/Builds/Base/Read me.txt
@@ -0,0 +1,125 @@
+Version 1.2
+
+Thank you for downloading Katapult!
+
+To get started, read the "Katapult Documentation" PDF.
+
+Important!
+You need Java installed to run Katapult. You need at least version 1.6.
+
+Very important!!
+If you are using Mac, DO NOT run Katapult in 64-bit mode. Katapult is NOT STABLE in 64-bit mode! There is an issue with java when running in 64-bit mode that messes up Katapults thread syncronization. This will most probably make Katapult crash at some point, or make it eat a lot of CPU power. If you are experiencing problems, please check whether Katapult runs in 64-bit mode. You can do this by opening up "Activity Monitor". If "Intel (64-bit)" is listed in the "Kind" column for Katapult, please change you Java settings to use the 32-bit VM instead. Open up "Java Preferences" and drag the 32-bit option to the top of the list. Also, try showing info (cmd-i) on the Katapult application, and check the "Run in 32-bit mode" checkbox.
+
+If you are using Snow Leopard, make sure you have updated to at least 10.6.2, since there was a Java bug in prior versions, that will make Katapult cry. Also, make sure you have the latest Java updates from
+Apple.
+
+If you are having trouble starting Katapult, please update your Java version. Also, if Katapult cannot detect your Launchpad, please make sure that you have the latest Launchpad USB drivers installed. These can be dawnloaded from Novations website at http://www.novationmusic.com/support/launchpad/.
+
+www.midikatapult.com
+info@midikatapult.com
+
+Important! Please see the "Known Issues" section, for notes on a bug related to Carbon based virtual MIDI drivers (seems to have been fixed
+in Apples latest Java updated as of 25th of May 2010).
+
+-----------
+Changes from 1.139 -> 1.2
+ - Ability to run Katapult in usermode 1/2
+ alongside Ableton Live's native Launchpad
+ functions
+ - Ability to map the round side-buttons
+ - Ability to invert drumrack note order
+ - Ability to customize velocity on buttons
+ - Ability to use note controls in toggle mode
+ - New control: Cross-fader
+ - New control: inverted vertical fader
+ - New control: inverted horizontal fader
+ - Fixed a bug where octaves weren't recognized
+ properly.
+ - A lot of small optimizations and fixes :)
+ - Updated documentation
+
+Changes from 1.132 -> 1.139
+ - iPad support
+ - Multi-client network support
+
+Changes from 1.13 -> 1.132
+ - Fixed a bug that caused button color
+ to get out of sync with Traktor 1.2.6
+
+Changes from 1.12 -> 1.13
+ - Added a persistence option to buttons
+ - Added new control: PC
+ - Added "headless" mode
+ - Added text-scroller demo (see config.txt)
+ - Fixed a bug with flipping pages when there was
+ gaps between the pages
- Fixed a bug with note-off events not being sent
+ on custom channels
- Fixed a bug where chained controls wouldnt send
+ updates (see docs)
- Fixed a bug where custom channels would send on
+ the wrong channel
- Documentation updated
+
+Changes from 1.12 -> 1.121
+ - Fixed a bug that caused drumracks to be duplicated
+
+Changes from 1.1 -> 1.12
+ - Added new control: CC
+ - Added new control: Raw note
+ - Added new control: Drumrack
+ - Added new control: Keyboard shortcut
+ - Added ability to customise send channels
+ - Added ability to reload layouts on the fly
+ - Updated documentation (+ fixed some errors)
+
+Changes from 1.052 -> 1.1
+ - Added new control: Progress bar
+ - Added new control: Meter
+ - Added new control: Note
+ - Optimised takeover implementation
+ - Various code optimisations
+ - Bugfixes
+ - Documentation updated
+
+Changes from 1.05 -> 1.052
+ - Minor bugfixes
+ - Updated user-interface explanations
+ - Minor updates to documentation
+
+Changes from 1.042 -> 1.05
+ - Updated documentation
+ - Fixed a bug that caused the Launchpad buttons
+ to lock up right after starting
+ - Fixed a bug that prevented the Launchpad buttons from when
+ Katapult was started
+
+Changes from 1.04 -> 1.042
+ - Fixed a bug that might have made controls lock up
+ for some users.
+ - Extended documentation (takeover, added more descriptions)
+
+Changes from 1.03 -> 1.04
+ - Implemented 2D pads
+ - Implemented custom inertia/takeover on faders
+ - Implemented custom inertia/takeover on pads
+ - Added ability to save device configuration
+ - Added page naming
+ - Fixed text file formatting in Windows
+
+Changes from 1.02 -> 1.03
+
+ - Fixed a bug where bi-directional communication would
+ only work for the first page
+ - Added indicator controls
+ - Added ability to customize colors;
+ default & on individual controls
+ - Added configuration file
+ - Added ability to specify devices in configuration file
+ - Added silent mode (specified in configuration file)
+
+-----------
+Known issues:
+
+- [This issue has been resolved as of Apple's May 2010 Java update]
+Sometimes, Katapult won't start due to a memory access error, if other MIDI applications are already running. If this happens, try closing the other applications, start Katapult, and then start up the other applications again. Please see the documentation pdf for more info on this issue. Unfortunately, I am unable to provide a fix at this time, since the bug resides in Apples Java implementation. You can get around this issue by using IAC drivers.
+
+-----------
+
+Happy Katapulting!
\ No newline at end of file
diff --git a/Builds/Base/Samples/Katapult.tsi b/Builds/Base/Samples/Katapult.tsi
new file mode 100644
index 0000000..d4b290f
--- /dev/null
+++ b/Builds/Base/Samples/Katapult.tsi
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/Builds/Base/Samples/Mapping.jpg b/Builds/Base/Samples/Mapping.jpg
new file mode 100644
index 0000000..303857d
Binary files /dev/null and b/Builds/Base/Samples/Mapping.jpg differ
diff --git a/Builds/Base/Samples/sample layout.txt b/Builds/Base/Samples/sample layout.txt
new file mode 100644
index 0000000..ef6a5f0
--- /dev/null
+++ b/Builds/Base/Samples/sample layout.txt
@@ -0,0 +1,133 @@
+// <- These two slashes means "this line is a comment"
+
+// Lets start creating a layout.
+// First of all, we need at least one page, so lets create one by
+// writing:
+
+page 1
+
+// So far, so good. Now let's create some controls.
+// First some hold and toggle buttons:
+
+button 0 0 toggle
+button 1 0 toggle
+button 2 0 hold
+button 3 0 toggle
+
+button 0 1 hold
+button 1 1 hold
+button 2 1 hold
+button 3 1 hold
+
+// And then some faders:
+
+yfader 0 7 5
+yfader 1 7 5
+yfader 2 7 5
+yfader 3 7 5
+
+// That fills up the left half of the Launchpad
+// Lets just duplicate it for the other half,
+// This will serve as a good starting point
+// for controlling two decks in Traktor, for
+// example.
+
+button 4 0 toggle
+button 5 0 toggle
+button 6 0 hold
+button 7 0 toggle
+
+button 4 1 hold
+button 5 1 hold
+button 6 1 hold
+button 7 1 hold
+
+yfader 4 7 5
+yfader 5 7 5
+yfader 6 7 5
+yfader 7 7 5
+
+button 0 2 toggle
+button 1 2 toggle
+button 2 2 hold
+button 3 2 hold
+button 4 2 toggle
+button 5 2 toggle
+button 6 2 hold
+button 7 2 hold
+
+// For demonstration purposes, heres a couple more pages.
+// These are not mapped to anything in the supplied
+// Traktor layouts, so you can go ahead and assign the
+// controls yourself :) Get creative!
+
+page 2
+
+button 0 0 hold
+button 1 0 hold
+button 2 0 hold
+
+button 5 0 hold
+button 6 0 hold
+button 7 0 hold
+
+yslider 0 6 6 5
+yslider 1 6 6 5
+yslider 2 6 6 5
+
+yslider 5 6 6 5
+yslider 6 6 6 5
+yslider 7 6 6 5
+
+button 0 7 hold
+button 1 7 hold
+button 2 7 hold
+
+button 5 7 hold
+button 6 7 hold
+button 7 7 hold
+
+yslider 3 6 7 4
+yslider 4 6 7 4
+
+button 3 7 toggle
+button 4 7 toggle
+
+page 3
+
+button 0 0 hold
+button 1 0 hold
+button 2 0 hold
+button 3 0 hold
+button 4 0 hold
+button 5 0 hold
+button 6 0 hold
+button 7 0 hold
+
+button 0 1 hold
+button 1 1 hold
+button 2 1 hold
+button 3 1 hold
+button 4 1 hold
+button 5 1 hold
+button 6 1 hold
+button 7 1 hold
+
+button 0 2 hold
+button 1 2 hold
+button 2 2 hold
+button 3 2 hold
+button 4 2 hold
+button 5 2 hold
+button 6 2 hold
+button 7 2 hold
+
+button 0 3 hold
+button 1 3 hold
+button 2 3 hold
+button 3 3 hold
+button 4 3 hold
+button 5 3 hold
+button 6 3 hold
+button 7 3 hold
+
diff --git a/Builds/Base/config.txt b/Builds/Base/config.txt
new file mode 100755
index 0000000..2da5da4
--- /dev/null
+++ b/Builds/Base/config.txt
@@ -0,0 +1,91 @@
+// <- These two slashes means, that a line is a comment.
+// If you want to use any of the example configuration lines
+// in this file, remove the slashes, and the following space,
+// so the config command is the very first thing on the line.
+// For example: "in=Traktor Out".
+
+// This is Katapults config file. In this file, you can
+// define various options, such as MIDI input/output devices
+// and default colors for controls
+
+// You can set the input device with a line like this:
+
+// in=Traktor Out
+
+// You can set the output device with a line like this:
+
+// out=Traktor In
+
+// If you want to use Katapult with Ableton Live, you can
+// enable Live's Launchpad functionality like this:
+
+// abletonlive=yes
+// liveusermode=1
+
+// This will put Katapult in usermode 1.
+
+// If you want Katapult to run in silent mode, use this line:
+
+// silent=yes
+
+// In silent mode, Katapult will not display any user interface,
+// thus saving CPU and making updates faster. This is especially
+// a good idea if you want to use MIDI intensive displays on the
+// Launchpad, such as volume output meters.
+// Note that you MUST specify input and output devices in this
+// file if you want Katapult to run in silent mode.
+
+// You can also define the default colors for the controls here.
+// The following lines are an example:
+
+// holdoncolor=green
+// holdoffcolor=amber
+// toggleoncolor=green
+// toggleoffcolor=amber
+// faderoncolor=red
+// faderoffcolor=green
+// slideroncolor=greenlow
+// slideroffcolor=green
+// pagebuttononcolor=amber
+// pagebuttonoffcolor=amber
+// indicatoroncolor=green
+// indicatoroffcolor=red
+// padoncolor=yellow
+// padoffcolor=amber
+// meteroncolor=green
+// meteroffcolor=amber
+// progressoncolor=green
+// progressoffcolor=red
+// noteoncolor=green
+// noteoffcolor=yellow
+// cconcolor=yellow
+// ccoffcolor=yellow
+// kbdoncolor=greenl
+// kbdoffcolor=green
+
+// See the documentation for a list of usable colors.
+
+// Katapult can be started in headless mode by using this line:
+
+// headless=yes
+
+// You can customize the demo being shown when pressing the mixer
+// button by using the following commands:
+
+// demochoice=6
+
+// choices here are 1-6, where 6 is a text scroller. Specify the
+// text like this:
+
+// demotext=katapult
+// demotextcolor=green
+
+// Or create your own bitmap and display whatever you want
+// like this
+
+// b1= # # ##### # #
+// b2= # # ## # # # ## # #
+// b3= ## ## # # # # # # # # # # ## ##
+// b4= # # # # ## # # # ## # # # #
+// b5= ## ## # # ## # # # ## # # ## ##
+// b6= ### ## # ## ##### ## # ## ##
diff --git a/Builds/Base/layout.txt b/Builds/Base/layout.txt
new file mode 100644
index 0000000..57d9c33
--- /dev/null
+++ b/Builds/Base/layout.txt
@@ -0,0 +1,276 @@
+// <- These two slashes means "this line is a comment"
+
+// Lets start creating a layout.
+// First of all, we need at least one page, so lets create one by
+// writing:
+
+page 1 Transport & EQ
+
+// Pages can be named as well. Just write the name after the
+// page number, as above.
+
+// So far, so good. Now let's create some controls.
+// First some hold and toggle buttons:
+
+button 0 0 toggle
+button 1 0 toggle
+button 2 0 hold
+button 3 0 toggle
+
+button 0 1 hold
+button 1 1 hold
+button 2 1 hold
+button 3 1 hold
+
+// And then some faders:
+
+yfader 0 7 5 takeover=1
+yfader 1 7 5 takeover=1
+yfader 2 7 5 takeover=1
+yfader 3 7 5 takeover=1
+
+// These faders have takeover activated on them.
+// This means that they'll move smoothly from one
+// value to another. This takeover is set to the
+// fastest (1 millisecond). If you want them to
+// update slower, increase the takeover value.
+
+// That fills up the left half of the Launchpad
+// Lets just duplicate it for the other half,
+// This will serve as a good starting point
+// for controlling two decks in Traktor, for
+// example.
+
+button 4 0 toggle
+button 5 0 toggle
+button 6 0 hold
+button 7 0 toggle
+
+button 4 1 hold
+button 5 1 hold
+button 6 1 hold
+button 7 1 hold
+
+yfader 4 7 5 takeover=1
+yfader 5 7 5 takeover=1
+yfader 6 7 5 takeover=1
+yfader 7 7 5 takeover=1
+
+button 0 2 toggle
+button 1 2 toggle
+button 2 2 hold
+button 3 2 hold
+button 4 2 toggle
+button 5 2 toggle
+button 6 2 hold
+button 7 2 hold
+
+// For demonstration purposes, heres a couple more pages.
+// These are not mapped to anything in the supplied
+// Traktor layouts, so you can go ahead and assign the
+// controls yourself :) Get creative!
+
+page 2 Some sliders and buttons
+
+button 0 0 hold
+button 1 0 hold
+button 2 0 hold
+
+button 5 0 hold
+button 6 0 hold
+button 7 0 hold
+
+yslider 0 6 6 5
+yslider 1 6 6 5
+yslider 2 6 6 5
+
+yslider 5 6 6 5
+yslider 6 6 6 5
+yslider 7 6 6 5
+
+button 0 7 hold
+button 1 7 hold
+button 2 7 hold
+
+button 5 7 hold
+button 6 7 hold
+button 7 7 hold
+
+yslider 3 6 7 4
+yslider 4 6 7 4
+
+button 3 7 toggle
+button 4 7 toggle
+
+page 3 More buttons
+
+button 0 0 hold
+button 1 0 hold
+button 2 0 hold
+button 3 0 hold
+button 4 0 hold
+button 5 0 hold
+button 6 0 hold
+button 7 0 hold
+
+button 0 1 hold
+button 1 1 hold
+button 2 1 hold
+button 3 1 hold
+button 4 1 hold
+button 5 1 hold
+button 6 1 hold
+button 7 1 hold
+
+button 0 2 hold
+button 1 2 hold
+button 2 2 hold
+button 3 2 hold
+button 4 2 hold
+button 5 2 hold
+button 6 2 hold
+button 7 2 hold
+
+button 0 3 hold
+button 1 3 hold
+button 2 3 hold
+button 3 3 hold
+button 4 3 hold
+button 5 3 hold
+button 6 3 hold
+button 7 3 hold
+
+page 4 Controller chaining
+
+xfader 0 0 8 takeover=8
+yfader 0 7 6
+yfader 2 7 5
+yfader 4 7 3
+yfader 6 7 4
+
+button 4 2 toggle
+button 5 3 toggle
+button 6 2 toggle
+button 7 2 toggle
+
+chain 0 0 to 0 7
+chain 0 7 to 2 7
+chain 2 7 to 4 7
+chain 4 7 to 6 7
+
+chain 4 2 to 0 7
+chain 5 3 to 2 7
+chain 6 2 to 4 7
+chain 7 2 to 6 7
+
+page 5 Custom color examples
+
+oncolor green
+
+offcolor yellow
+button 0 0 toggle
+button 0 1 toggle
+button 0 2 toggle
+button 0 3 toggle
+button 0 4 toggle
+button 0 5 toggle
+button 0 6 toggle
+button 0 7 toggle
+
+offcolor red
+button 1 0 toggle
+button 1 1 toggle
+button 1 2 toggle
+button 1 3 toggle
+button 1 4 toggle
+button 1 5 toggle
+button 1 6 toggle
+button 1 7 toggle
+
+offcolor yellow
+button 2 0 toggle
+button 2 1 toggle
+button 2 2 toggle
+button 2 3 toggle
+button 2 4 toggle
+button 2 5 toggle
+button 2 6 toggle
+button 2 7 toggle
+
+offcolor red
+button 3 0 toggle
+button 3 1 toggle
+button 3 2 toggle
+button 3 3 toggle
+button 3 4 toggle
+button 3 5 toggle
+button 3 6 toggle
+button 3 7 toggle
+
+offcolor yellow
+button 4 0 toggle
+button 4 1 toggle
+button 4 2 toggle
+button 4 3 toggle
+button 4 4 toggle
+button 4 5 toggle
+button 4 6 toggle
+button 4 7 toggle
+
+offcolor red
+button 5 0 toggle
+button 5 1 toggle
+button 5 2 toggle
+button 5 3 toggle
+button 5 4 toggle
+button 5 5 toggle
+button 5 6 toggle
+button 5 7 toggle
+
+offcolor yellow
+button 6 0 toggle
+button 6 1 toggle
+button 6 2 toggle
+button 6 3 toggle
+button 6 4 toggle
+button 6 5 toggle
+button 6 6 toggle
+button 6 7 toggle
+
+offcolor red
+button 7 0 toggle
+button 7 1 toggle
+button 7 2 toggle
+button 7 3 toggle
+button 7 4 toggle
+button 7 5 toggle
+button 7 6 toggle
+button 7 7 toggle
+
+defaultcolors
+
+page 6 Four 2D Pads
+
+offcolor green
+oncolor red
+pad 0 0 4 4 takeover=1
+pad 4 4 4 4 takeover=1
+
+offcolor amber
+pad 4 0 4 4
+pad 0 4 4 4
+
+page 7 One big 2D Pad
+pad 0 0 8 8 takeover=4
+
+defaultcolors
+
+page 8 Drumracks
+drumrack 0 0 4 4 3 c 115
+drumrack 4 4 4 4 5 c 120
+
+page 9 Keyboard shortcuts
+kbd 0 0 command+tab
+kbd 1 0 shift+a
+kbd 2 0 b
+kbd 3 0 c
\ No newline at end of file
diff --git a/Builds/Base/license.txt b/Builds/Base/license.txt
new file mode 100755
index 0000000..8bbe98e
--- /dev/null
+++ b/Builds/Base/license.txt
@@ -0,0 +1 @@
+Replace this line with your license key
\ No newline at end of file
diff --git a/Builds/Releases/Katapult Template.dmg b/Builds/Releases/Katapult Template.dmg
new file mode 100644
index 0000000..1d2a605
Binary files /dev/null and b/Builds/Releases/Katapult Template.dmg differ
diff --git a/Colors.pde b/Colors.pde
new file mode 100644
index 0000000..258cf1f
--- /dev/null
+++ b/Colors.pde
@@ -0,0 +1,170 @@
+// LED color definitions
+static final int BLACK = 12;
+static final int REDL = 13;
+static final int RED = 15;
+static final int AMBERL = 29;
+static final int AMBER = 63;
+static final int YELLOW = 62;
+static final int GREEN = 60;
+static final int GREENL = 28;
+static final int REDF = 11;
+static final int AMBERF = 59;
+static final int YELLOWF = 58;
+static final int GREENF = 56;
+
+static final int BLUE = 90;
+static final int BLUEL = 91;
+static final int CYAN = 92;
+static final int CYANL = 93;
+static final int MAGENTA = 94;
+static final int MAGENTAL = 95;
+static final int WHITE = 96;
+static final int WHITEL = 97;
+
+int colors[] = new int[12];
+
+static int SCROLLERCOLOR = RED;
+
+// Button color definitions
+static int BUTTONIDLECOLOR = AMBER;
+static int HOLDONCOLOR = GREEN;
+static int TOGGLEONCOLOR = GREEN;
+static int TOGGLEOFFCOLOR = AMBER;
+static int FADERIDLECOLOR = RED;
+static int FADERACTIVECOLOR = GREEN;
+static int SLIDERIDLECOLOR = GREENL;
+static int SLIDERACTIVECOLOR = GREEN;
+static int PAGEBUTTONIDLECOLOR = AMBER;
+static int PAGEBUTTONACTIVECOLOR = GREEN;
+static int INDICATOROFFCOLOR = RED;
+static int INDICATORONCOLOR = GREEN;
+static int PADOFFCOLOR = AMBER;
+static int PADONCOLOR = YELLOW;
+static int METERIDLECOLOR = AMBER;
+static int METERACTIVECOLOR = GREEN;
+static int PROGRESSIDLECOLOR = RED;
+static int PROGRESSACTIVECOLOR = GREEN;
+static int NOTEIDLECOLOR = YELLOW;
+static int NOTEACTIVECOLOR = GREEN;
+static int CCIDLECOLOR = YELLOW;
+static int CCACTIVECOLOR = GREEN;
+static int KBDIDLECOLOR = GREENL;
+static int KBDACTIVECOLOR = GREEN;
+static int PCIDLECOLOR = YELLOW;
+static int PCACTIVECOLOR = GREEN;
+int[] cdef = new int[26];
+
+void storeDefaultColors() {
+ cdef[0] = BUTTONIDLECOLOR;
+ cdef[1] = HOLDONCOLOR;
+ cdef[2] = TOGGLEONCOLOR;
+ cdef[3] = TOGGLEOFFCOLOR;
+ cdef[4] = FADERIDLECOLOR;
+ cdef[5] = FADERACTIVECOLOR;
+ cdef[6] = SLIDERIDLECOLOR;
+ cdef[7] = SLIDERACTIVECOLOR;
+ cdef[8] = PAGEBUTTONIDLECOLOR;
+ cdef[9] = PAGEBUTTONACTIVECOLOR;
+ cdef[10] = INDICATOROFFCOLOR;
+ cdef[11] = INDICATORONCOLOR;
+ cdef[12] = PADOFFCOLOR;
+ cdef[13] = PADONCOLOR;
+ cdef[14] = METERIDLECOLOR;
+ cdef[15] = METERACTIVECOLOR;
+ cdef[16] = PROGRESSIDLECOLOR;
+ cdef[17] = PROGRESSACTIVECOLOR;
+ cdef[18] = NOTEIDLECOLOR;
+ cdef[19] = NOTEACTIVECOLOR;
+ cdef[20] = CCIDLECOLOR;
+ cdef[21] = CCACTIVECOLOR;
+ cdef[22] = KBDIDLECOLOR;
+ cdef[23] = KBDACTIVECOLOR;
+ cdef[24] = PCIDLECOLOR;
+ cdef[25] = PCACTIVECOLOR;
+}
+
+void defaultColors() {
+ BUTTONIDLECOLOR = cdef[0];
+ HOLDONCOLOR = cdef[1];
+ TOGGLEONCOLOR = cdef[2];
+ TOGGLEOFFCOLOR = cdef[3];
+ FADERIDLECOLOR = cdef[4];
+ FADERACTIVECOLOR = cdef[5];
+ SLIDERIDLECOLOR = cdef[6];
+ SLIDERACTIVECOLOR = cdef[7];
+ PAGEBUTTONIDLECOLOR = cdef[8];
+ PAGEBUTTONACTIVECOLOR = cdef[9];
+ INDICATOROFFCOLOR = cdef[10];
+ INDICATORONCOLOR = cdef[11];
+ PADOFFCOLOR = cdef[12];
+ PADONCOLOR = cdef[13];
+ METERIDLECOLOR = cdef[14];
+ METERACTIVECOLOR = cdef[15];
+ PROGRESSIDLECOLOR = cdef[16];
+ PROGRESSACTIVECOLOR = cdef[17];
+ NOTEIDLECOLOR = cdef[18];
+ NOTEACTIVECOLOR = cdef[19];
+ CCIDLECOLOR = cdef[20];
+ CCACTIVECOLOR = cdef[21];
+ KBDIDLECOLOR = cdef[22];
+ KBDACTIVECOLOR = cdef[23];
+ PCIDLECOLOR = cdef[24];
+ PCACTIVECOLOR = cdef[25];
+}
+
+int parseColor(String input) {
+ int scolor = BLACK;
+ if (input.equals("black")) { scolor = BLACK; }
+ if (input.equals("redlow")) { scolor = REDL; }
+ if (input.equals("red")) { scolor = RED; }
+ if (input.equals("amberlow")) { scolor = AMBERL; }
+ if (input.equals("amber")) { scolor = AMBER; }
+ if (input.equals("yellow")) { scolor = YELLOW; }
+ if (input.equals("greenlow")) { scolor = GREENL; }
+ if (input.equals("green")) { scolor = GREEN; }
+
+ if (input.equals("blue")) { scolor = BLUE; }
+ if (input.equals("bluelow")) { scolor = BLUEL ; }
+ if (input.equals("cyan")) { scolor = CYAN; }
+ if (input.equals("cyanlow")) { scolor = CYANL; }
+ if (input.equals("magenta")) { scolor = MAGENTA; }
+ if (input.equals("magentalow")) { scolor = MAGENTAL; }
+ if (input.equals("white")) { scolor = WHITE; }
+ if (input.equals("whitelow")) { scolor = WHITEL ; }
+
+ if (input.equals("redflash")) { scolor = REDF; }
+ if (input.equals("amberflash")) { scolor = AMBERF; }
+ if (input.equals("yellowflash")) { scolor = YELLOWF; }
+ if (input.equals("greenflash")) { scolor = GREENF; }
+ return scolor;
+}
+
+void setOffColors(int scolor) {
+ BUTTONIDLECOLOR = scolor;
+ TOGGLEOFFCOLOR = scolor;
+ FADERIDLECOLOR = scolor;
+ SLIDERIDLECOLOR = scolor;
+ INDICATOROFFCOLOR = scolor;
+ PADOFFCOLOR = scolor;
+ METERIDLECOLOR = scolor;
+ PROGRESSIDLECOLOR = scolor;
+ NOTEIDLECOLOR = scolor;
+ CCIDLECOLOR = scolor;
+ KBDIDLECOLOR = scolor;
+ PCIDLECOLOR = scolor;
+}
+
+void setOnColors(int scolor) {
+ HOLDONCOLOR = scolor;
+ TOGGLEONCOLOR = scolor;
+ FADERACTIVECOLOR = scolor;
+ SLIDERACTIVECOLOR = scolor;
+ INDICATORONCOLOR = scolor;
+ PADONCOLOR = scolor;
+ METERACTIVECOLOR = scolor;
+ PROGRESSACTIVECOLOR = scolor;
+ NOTEACTIVECOLOR = scolor;
+ CCACTIVECOLOR = scolor;
+ KBDACTIVECOLOR = scolor;
+ PCACTIVECOLOR = scolor;
+}
diff --git a/Configuration.pde b/Configuration.pde
new file mode 100644
index 0000000..54e78fb
--- /dev/null
+++ b/Configuration.pde
@@ -0,0 +1,171 @@
+// Configuration
+static String VERSION = "1.2";
+static int FRAMERATE = 60;
+static int WINDOWSIZE = 400;
+static int FRAMEBORDER = 10;
+static String SOFTWARE = "";
+static String SOFTWAREIN = "";
+static String SOFTWARESEL = "";
+static String SOFTWAREINSEL = "";
+static boolean DEBUG = false;
+static boolean NOSEND = false;
+static boolean NETWORK = false;
+static boolean HEADLESS = false;
+static boolean LIVECONTROL = false;
+static boolean LIVEENABLED = false;
+static int USERMODE = 1;
+static int MAXCHAINS = 64;
+static boolean DEMO = false;
+static String DEMOTEXT = "katapult";
+static int DEMOCHOICE = -1;
+static boolean SILENTMODE = false;
+
+// Internal variables
+static String config[];
+static int CLINESIN = -1;
+static int CLINESOUT = -1;
+static boolean DEVICECONFIG = false;
+
+void loadConfig() {
+ config = loadStrings("config.txt");
+ for (int i = 0; i < config.length; i++) {
+ String configLine[] = split(config[i], "=");
+ if (configLine[0].equals("in")) {
+ SOFTWAREIN = configLine[1];
+ SOFTWAREINSEL = configLine[1];
+ CLINESIN = i;
+ }
+ if (configLine[0].equals("out")) {
+ SOFTWARE = configLine[1];
+ SOFTWARESEL = configLine[1];
+ CLINESOUT = i;
+ }
+ if (configLine[0].equals("abletonlive")) {
+ if (configLine[1].equals("yes")) { LIVEENABLED = true; }
+ }
+ if (configLine[0].equals("liveusermode")) {
+ if (configLine[1].equals("2")) { USERMODE = 2; }
+ }
+ if (configLine[0].equals("silent")) {
+ if (configLine[1].equals("yes")) { SILENTMODE = true; WINDOWSIZE = 150; }
+ }
+ if (configLine[0].equals("headless")) {
+ if (configLine[1].equals("yes")) { HEADLESS = true; VERSION = VERSION + " headless"; }
+ }
+ if (configLine[0].equals("holdoffcolor")) {
+ BUTTONIDLECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("holdoncolor")) {
+ HOLDONCOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("toggleoffcolor")) {
+ TOGGLEOFFCOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("toggleoncolor")) {
+ TOGGLEONCOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("faderoffcolor")) {
+ FADERIDLECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("faderoncolor")) {
+ FADERACTIVECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("slideroffcolor")) {
+ SLIDERIDLECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("slideroncolor")) {
+ SLIDERACTIVECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("pagebuttononcolor")) {
+ PAGEBUTTONACTIVECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("pagebuttonoffcolor")) {
+ PAGEBUTTONIDLECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("meteroffcolor")) {
+ METERIDLECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("meteroncolor")) {
+ METERACTIVECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("progressoffcolor")) {
+ PROGRESSIDLECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("progressoncolor")) {
+ PROGRESSACTIVECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("noteoffcolor")) {
+ NOTEIDLECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("noteoncolor")) {
+ NOTEACTIVECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("cconcolor")) {
+ CCACTIVECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("ccoffcolor")) {
+ CCIDLECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("pconcolor")) {
+ PCACTIVECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("pcoffcolor")) {
+ PCIDLECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("kbdoncolor")) {
+ KBDACTIVECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("kbdoffcolor")) {
+ KBDIDLECOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("demotextcolor")) {
+ SCROLLERCOLOR = parseColor(configLine[1]);
+ }
+ if (configLine[0].equals("demotext")) {
+ DEMOTEXT = configLine[1];
+ }
+ if (configLine[0].equals("demochoice")) {
+ DEMOCHOICE = Integer.parseInt(configLine[1]);
+ }
+ if (configLine[0].equals("b1")) {
+ overrideBitmap(0, configLine[1]);
+ }
+ if (configLine[0].equals("b2")) {
+ overrideBitmap(1, configLine[1]);
+ }
+ if (configLine[0].equals("b3")) {
+ overrideBitmap(2, configLine[1]);
+ }
+ if (configLine[0].equals("b4")) {
+ overrideBitmap(3, configLine[1]);
+ }
+ if (configLine[0].equals("b5")) {
+ overrideBitmap(4, configLine[1]);
+ }
+ if (configLine[0].equals("b6")) {
+ overrideBitmap(5, configLine[1]);
+ }
+
+ }
+ if (!SOFTWAREIN.equals("") && !SOFTWARE.equals("")) DEVICECONFIG = true;
+ storeDefaultColors();
+}
+
+void saveConfig() {
+ //debug("saveConfig()");
+ int configlength = config.length;
+ //debug("Configlength "+configlength);
+ if (CLINESIN == -1) { CLINESIN = configlength; configlength++; }
+ if (CLINESOUT == -1) { CLINESOUT = configlength; configlength++; }
+ //debug("Configlength "+configlength);
+ String[] newconfig = new String[configlength];
+ for (int i = 0; i < config.length; i++) {
+ newconfig[i] = config[i];
+ }
+ //debug("linein"+CLINESIN);
+ //debug("lineout"+CLINESOUT);
+ //debug("Configlength "+newconfig.length);
+ newconfig[CLINESIN] = "in="+SOFTWAREIN;
+ newconfig[CLINESOUT] = "out="+SOFTWARE;
+ saveStrings("config.txt", newconfig);
+}
diff --git a/Controls.pde b/Controls.pde
new file mode 100644
index 0000000..07361c3
--- /dev/null
+++ b/Controls.pde
@@ -0,0 +1,2224 @@
+import java.awt.Robot;
+import java.awt.event.InputEvent.*;
+
+class Control {
+ int x;
+ int y;
+ int xsize;
+ int ysize;
+ int type;
+ int state;
+ int number;
+ int value;
+ int velocity;
+ int softwarevalue;
+ boolean persistence;
+ boolean sn;
+ int timeout;
+ int idlecolor;
+ int activecolor;
+ int controlID;
+ int page;
+ int channel = customChannel;
+ int takeover;
+ int updatedelay = 50;
+ boolean TLOCK = false;
+ boolean TAKEOVER = false;
+ long threadsSpawned = 0;
+ long threadsFinished = 0;
+ Control owner;
+ boolean isChained = false;
+ int numberOfChains = 0;
+ boolean chainSendFlags[] = new boolean[MAXCHAINS];
+ Control chains[] = new Control[MAXCHAINS];
+
+ void up(){}
+ void down(){}
+ void on(){}
+ void off(){}
+ void send(){}
+ void setValue(int ivalue){}
+ void takeoverSetValue(int ivalue){}
+ void nakedSetValue(int ivalue) {}
+ void update(){}
+ void cancelSchedule(){}
+
+
+ void chainTo(Control control, boolean send) {
+ chains[numberOfChains] = control;
+ //debug("Chaining "+this+" to "+control);
+ chainSendFlags[numberOfChains] = send;
+ numberOfChains++;
+ isChained = true;
+ }
+
+ void propagateToChainedControls() {
+ if(isChained) {
+ for (int i = 0; i < numberOfChains; i++) {
+ //println("Chained update to c on "+chains[i].page+". now on "+selectedPage);
+ chains[i].setValue(value);
+ if (chainSendFlags[i]) chains[i].send();
+ if (chains[i].page == selectedPage) chains[i].update();
+ }
+ }
+ }
+}
+
+class GridSegment extends Control {
+ void on() {
+ debug(this+"x="+this.x+" y="+this.y+" on()");
+ ledOn(x, y, activecolor);
+ }
+
+ void off() {
+ debug(this+"x="+this.x+" y="+this.y+" off()");
+ ledOn(x, y, idlecolor);
+ }
+}
+
+class PageButton extends GridSegment {
+ int number;
+
+ PageButton(int inumber) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ number = inumber;
+ idlecolor = PAGEBUTTONIDLECOLOR;
+ activecolor = PAGEBUTTONACTIVECOLOR;
+ grid[number] = this;
+ ledOn((number % 8), (int)((float)number / 8), idlecolor);
+ }
+
+ void down() {
+ ledOn((number % 8), (int)((float)number / 8), activecolor);
+ loadLayout(number+1);
+ }
+
+ void update() {
+ x = 8;
+ y = 7;
+ off();
+ }
+}
+
+class LED extends GridSegment {
+ LED(int ix, int iy) {
+ controlID = (int)random(1000);
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ value = 0;
+ state = OFF;
+ timeout = 0;
+ grid[y*8+x] = this;
+ idlecolor = INDICATOROFFCOLOR;
+ activecolor = INDICATORONCOLOR;
+ ledOn(x, y, idlecolor);
+ }
+
+ void send() {
+ controlOut(x, y, state);
+ }
+
+ void down() {
+
+ }
+
+ void up() {
+
+ }
+
+ void update() {
+ if (value == 0) {
+ off();
+ }
+ if (value > 0) {
+ on();
+ }
+ }
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ if (ivalue > 0) { value = ivalue; state = ON; } else { value = OFF; state = OFF; }
+ propagateToChainedControls();
+ }
+}
+
+class Button extends GridSegment {
+ Button(int ix, int iy, int itype) {
+ controlID = (int)random(1000);
+ page = selectedPage;
+ persistence = false;
+ sn = true;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ type = itype;
+ value = 0;
+ velocity = -1;
+ state = OFF;
+ timeout = 0;
+ if (x < 8) {
+ grid[y*8+x] = this;
+ }
+ if (x == 8 && y < 7) {
+ //debug("Mapping side button:"+this);
+ grid[65+y] = this;
+ }
+ if (type == HOLD) {
+ idlecolor = BUTTONIDLECOLOR;
+ activecolor = HOLDONCOLOR;
+ }
+ if (type == TOGGLE) {
+ idlecolor = TOGGLEOFFCOLOR;
+ activecolor = TOGGLEONCOLOR;
+ }
+ ledOn(x, y, idlecolor);
+ }
+
+ void setVelocity(int vel) {
+ velocity = vel;
+ }
+
+ void send() {
+ debug("Velocity is "+velocity);
+ if (channel != 0) outputChannel = channel-1;
+ if (velocity == -1) controlOut(x, y, state);
+ if (velocity != -1 && state == ON) controlOut(x, y, velocity);
+ if (velocity != -1 && state == OFF) controlOut(x, y, state);
+ }
+
+ void down() {
+ sn = false;
+ if (type == HOLD) {
+ state = ON;
+ setValue(ON);
+ send();
+ if (!persistence) on();
+ }
+ if (type == TOGGLE) {
+ if (state == OFF) {
+ state = ON;
+ send();
+ if (!persistence) on();
+ setValue(ON);
+ } else if (state == ON) {
+ state = OFF;
+ send();
+ if (!persistence) off();
+ setValue(OFF);
+ }
+ }
+ }
+
+ void up() {
+ sn = false;
+ if (type == HOLD) {
+ //state = OFF;
+ setValue(OFF);
+ send();
+ if (!persistence) off();
+ }
+ }
+
+ void update() {
+ debug("sn "+sn);
+ if (value == 0) {
+ off();
+ }
+ if (value == 127 || softwarevalue == 127) {
+ on();
+ }
+ }
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ softwarevalue = ivalue;
+ if (ivalue > 0) { value = 127; state = ON; } else { value = 0; state = OFF; }
+ propagateToChainedControls();
+ sn = true;
+ }
+}
+
+class Note extends GridSegment {
+ int note;
+ int vel;
+ boolean sendoff = false;
+ boolean toggle = false;
+
+ Note(int ix, int iy, int octave, String note, int vel) {
+ controlID = (int)random(1000);
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ value = 0;
+ state = OFF;
+ timeout = 0;
+ idlecolor = NOTEIDLECOLOR;
+ activecolor = NOTEACTIVECOLOR;
+ this.note = addOctave(parseNote(note), octave);
+ this.vel = vel;
+
+ if (x < 8) {
+ grid[y*8+x] = this;
+ }
+ if (x == 8 && y < 7) {
+ //debug("Mapping side button:"+this);
+ grid[65+y] = this;
+ }
+
+ ledOn(x, y, idlecolor);
+ }
+
+ Note(int ix, int iy, int midi, int vel) {
+ controlID = (int)random(1000);
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ value = 0;
+ state = OFF;
+ timeout = 0;
+ idlecolor = NOTEIDLECOLOR;
+ activecolor = NOTEACTIVECOLOR;
+ this.note = midi;
+ this.vel = vel;
+
+ if (x < 8) {
+ grid[y*8+x] = this;
+ }
+ if (x == 8 && y < 7) {
+ //debug("Mapping side button:"+this);
+ grid[65+y] = this;
+ }
+
+ ledOn(x, y, idlecolor);
+ }
+
+ int parseNote(String note) {
+ note = note.toLowerCase();
+ if (note.equals("c")) return 0;
+ if (note.equals("c#")) return 1;
+ if (note.equals("d")) return 2;
+ if (note.equals("d#")) return 3;
+ if (note.equals("e")) return 4;
+ if (note.equals("f")) return 5;
+ if (note.equals("f#")) return 6;
+ if (note.equals("g")) return 7;
+ if (note.equals("g#")) return 8;
+ if (note.equals("a")) return 9;
+ if (note.equals("a#")) return 10;
+ if (note.equals("b")) return 11;
+ return 0;
+ }
+
+ int addOctave(int note, int octave) {
+ octave = octave+1;
+ return note+(octave*12);
+ }
+
+ void send() {
+ if (channel != 0) outputChannel = channel-1;
+ if (sendoff) {
+ noteOut(note, 0);
+ sendoff = false;
+ } else {
+ //debug("sendOn");
+ noteOut(note, vel);
+ }
+ }
+
+ void sendOff() {
+ if (channel != 0) outputChannel = channel-1;
+ //debug("sendOff");
+ noteOut(note, 0);
+ }
+
+ void down() {
+ if (!toggle) {
+ send();
+ on();
+ } else {
+ if (state == OFF) {
+ send();
+ on();
+ state = ON;
+ } else if (state == ON) {
+ sendOff();
+ off();
+ state = OFF;
+ }
+ }
+ }
+
+ void up() {
+ if (!toggle) {
+ sendOff();
+ off();
+ }
+ }
+
+ void update() {
+ if (value == 0) {
+ off();
+ }
+ if (value == 127) {
+ on();
+ }
+ }
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ value = ivalue;
+ if (ivalue == 0) {
+ sendoff = true;
+ }
+ propagateToChainedControls();
+ }
+}
+
+class CC extends GridSegment {
+ int note;
+ int vel;
+ boolean toggle;
+ boolean sendoff = false;
+
+ CC(int ix, int iy, int cc, int vel) {
+ controlID = (int)random(1000);
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ value = 0;
+ state = OFF;
+ timeout = 0;
+ idlecolor = CCIDLECOLOR;
+ activecolor = CCACTIVECOLOR;
+ this.note = cc;
+ this.vel = vel;
+ toggle = false;
+
+ if (x < 8) {
+ grid[y*8+x] = this;
+ }
+ if (x == 8 && y < 7) {
+ //debug("Mapping side button:"+this);
+ grid[65+y] = this;
+ }
+
+ ledOn(x, y, idlecolor);
+ }
+
+ void send() {
+ if (channel != 0) outputChannel = channel-1;
+ if (sendoff) {
+ sendOff();
+ sendoff = false;
+ } else {
+ //debug("sendOn");
+ controlOut(note, vel);
+ }
+ }
+
+ void sendOff() {
+ //debug("sendOff");
+ //controlOut(note, 0);
+ }
+
+ void down() {
+ send();
+ on();
+ }
+
+ void up() {
+ sendOff();
+ off();
+ }
+
+ void update() {
+ if (value == 0) {
+ off();
+ }
+ if (value == 127) {
+ on();
+ }
+ }
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ value = ivalue;
+ if (ivalue == 0) {
+ sendoff = true;
+ }
+ propagateToChainedControls();
+ }
+}
+
+class PC extends GridSegment {
+ int note;
+ int vel;
+ boolean sendoff = false;
+
+ PC(int ix, int iy, int program) {
+ controlID = (int)random(1000);
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ value = 0;
+ state = OFF;
+ timeout = 0;
+ idlecolor = PCIDLECOLOR;
+ activecolor = PCACTIVECOLOR;
+ this.note = program;
+
+ if (x < 8) {
+ grid[y*8+x] = this;
+ }
+ if (x == 8 && y < 7) {
+ //debug("Mapping side button:"+this);
+ grid[65+y] = this;
+ }
+
+ ledOn(x, y, idlecolor);
+ }
+
+ void send() {
+ if (channel != 0) outputChannel = channel-1;
+ if (sendoff) {
+ sendOff();
+ sendoff = false;
+ } else {
+ //debug("sendOn");
+ programOut(note);
+ }
+ }
+
+ void sendOff() {
+ //debug("sendOff");
+ //controlOut(note, 0);
+ }
+
+ void down() {
+ send();
+ on();
+ }
+
+ void up() {
+ sendOff();
+ off();
+
+ }
+
+ void update() {
+ if (value == 0) {
+ off();
+ }
+ if (value == 127) {
+ on();
+ }
+ }
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ value = ivalue;
+ if (ivalue == 0) {
+ sendoff = true;
+ }
+ propagateToChainedControls();
+ }
+}
+
+class Kbd extends GridSegment {
+ int note;
+ int vel;
+ boolean sendoff = false;
+ Robot robot;
+ int keys[];
+
+ Kbd(int ix, int iy, String keystring) {
+ controlID = (int)random(1000);
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ value = 0;
+ state = OFF;
+ timeout = 0;
+ idlecolor = KBDIDLECOLOR;
+ activecolor = KBDACTIVECOLOR;
+ try {
+ robot = new Robot();
+ } catch (AWTException e) {
+ debug("Exception while creating robot "+e);
+ }
+ String keysS[] = split(keystring, "+");
+ keys = new int[keysS.length];
+ for (int i = 0; i < keys.length; i++) {
+ keys[i] = parseKey(keysS[i]);
+ }
+
+ if (x < 8) {
+ grid[y*8+x] = this;
+ }
+ if (x == 8 && y < 7) {
+ //debug("Mapping side button:"+this);
+ grid[65+y] = this;
+ }
+
+ ledOn(x, y, idlecolor);
+ }
+
+ int parseKey(String keystr) {
+ if (keystr.equals("shift")) return KeyEvent.VK_SHIFT;
+ if (keystr.equals("control")) return KeyEvent.VK_CONTROL;
+ if (keystr.equals("alt")) return KeyEvent.VK_ALT;
+ if (keystr.equals("altgr")) return KeyEvent.VK_ALT_GRAPH;
+ if (keystr.equals("command")) return KeyEvent.VK_META;
+ if (keystr.equals("1")) return KeyEvent.VK_1;
+ if (keystr.equals("2")) return KeyEvent.VK_2;
+ if (keystr.equals("3")) return KeyEvent.VK_3;
+ if (keystr.equals("4")) return KeyEvent.VK_4;
+ if (keystr.equals("5")) return KeyEvent.VK_5;
+ if (keystr.equals("6")) return KeyEvent.VK_6;
+ if (keystr.equals("7")) return KeyEvent.VK_7;
+ if (keystr.equals("8")) return KeyEvent.VK_8;
+ if (keystr.equals("9")) return KeyEvent.VK_9;
+ if (keystr.equals("0")) return KeyEvent.VK_0;
+ if (keystr.equals("a")) return KeyEvent.VK_A;
+ if (keystr.equals("b")) return KeyEvent.VK_B;
+ if (keystr.equals("c")) return KeyEvent.VK_C;
+ if (keystr.equals("d")) return KeyEvent.VK_D;
+ if (keystr.equals("e")) return KeyEvent.VK_E;
+ if (keystr.equals("f")) return KeyEvent.VK_F;
+ if (keystr.equals("g")) return KeyEvent.VK_G;
+ if (keystr.equals("h")) return KeyEvent.VK_H;
+ if (keystr.equals("i")) return KeyEvent.VK_I;
+ if (keystr.equals("j")) return KeyEvent.VK_J;
+ if (keystr.equals("k")) return KeyEvent.VK_K;
+ if (keystr.equals("l")) return KeyEvent.VK_L;
+ if (keystr.equals("m")) return KeyEvent.VK_M;
+ if (keystr.equals("n")) return KeyEvent.VK_N;
+ if (keystr.equals("o")) return KeyEvent.VK_O;
+ if (keystr.equals("p")) return KeyEvent.VK_P;
+ if (keystr.equals("q")) return KeyEvent.VK_Q;
+ if (keystr.equals("r")) return KeyEvent.VK_R;
+ if (keystr.equals("s")) return KeyEvent.VK_S;
+ if (keystr.equals("t")) return KeyEvent.VK_T;
+ if (keystr.equals("u")) return KeyEvent.VK_U;
+ if (keystr.equals("v")) return KeyEvent.VK_V;
+ if (keystr.equals("w")) return KeyEvent.VK_W;
+ if (keystr.equals("x")) return KeyEvent.VK_X;
+ if (keystr.equals("y")) return KeyEvent.VK_Y;
+ if (keystr.equals("z")) return KeyEvent.VK_Z;
+ if (keystr.equals("f1")) return KeyEvent.VK_F1;
+ if (keystr.equals("f2")) return KeyEvent.VK_F2;
+ if (keystr.equals("f3")) return KeyEvent.VK_F3;
+ if (keystr.equals("f4")) return KeyEvent.VK_F4;
+ if (keystr.equals("f5")) return KeyEvent.VK_F5;
+ if (keystr.equals("f6")) return KeyEvent.VK_F6;
+ if (keystr.equals("f7")) return KeyEvent.VK_F7;
+ if (keystr.equals("f8")) return KeyEvent.VK_F8;
+ if (keystr.equals("f9")) return KeyEvent.VK_F9;
+ if (keystr.equals("f10")) return KeyEvent.VK_F10;
+ if (keystr.equals("f11")) return KeyEvent.VK_F11;
+ if (keystr.equals("f12")) return KeyEvent.VK_F12;
+ if (keystr.equals("f13")) return KeyEvent.VK_F13;
+ if (keystr.equals("f14")) return KeyEvent.VK_F14;
+ if (keystr.equals("f15")) return KeyEvent.VK_F15;
+ if (keystr.equals("f16")) return KeyEvent.VK_F16;
+ if (keystr.equals("f17")) return KeyEvent.VK_F17;
+ if (keystr.equals("f18")) return KeyEvent.VK_F18;
+ if (keystr.equals("f19")) return KeyEvent.VK_F19;
+ if (keystr.equals("esc")) return KeyEvent.VK_ESCAPE;
+ if (keystr.equals("space")) return KeyEvent.VK_SPACE;
+ if (keystr.equals("enter")) return KeyEvent.VK_ENTER;
+ if (keystr.equals("tab")) return KeyEvent.VK_TAB;
+ if (keystr.equals("backspace")) return KeyEvent.VK_BACK_SPACE;
+ if (keystr.equals("delete")) return KeyEvent.VK_DELETE;
+ if (keystr.equals("caps")) return KeyEvent.VK_CAPS_LOCK;
+ if (keystr.equals("down")) return KeyEvent.VK_DOWN;
+ if (keystr.equals("up")) return KeyEvent.VK_UP;
+ if (keystr.equals("left")) return KeyEvent.VK_LEFT;
+ if (keystr.equals("right")) return KeyEvent.VK_RIGHT;
+ if (keystr.equals(",")) return KeyEvent.VK_COMMA;
+ if (keystr.equals(".")) return KeyEvent.VK_PERIOD;
+ if (keystr.equals("+")) return KeyEvent.VK_PLUS;
+ if (keystr.equals("-")) return KeyEvent.VK_MINUS;
+ return 0;
+ }
+
+ void send() {
+ for (int i = 0; i < keys.length; i++) {
+ if (keys[i] != 0) {
+ debug("Sending keyPress "+keys[i]);
+ robot.keyPress(keys[i]);
+ }
+ }
+ }
+
+ void sendOff() {
+ for (int i = 0; i < keys.length; i++) {
+ if (keys[i] != 0) {
+ robot.keyRelease(keys[i]);
+ }
+ }
+ }
+
+ void down() {
+ send();
+ on();
+ }
+
+ void up() {
+ sendOff();
+ off();
+ }
+
+ void update() {
+ if (value == 0) {
+ off();
+ }
+ if (value == 127) {
+ on();
+ }
+ }
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ value = ivalue;
+ if (ivalue == 0) {
+ sendoff = true;
+ }
+ propagateToChainedControls();
+ }
+}
+
+class FaderSegment extends GridSegment {
+ Fader owner;
+
+ FaderSegment(int ix, int iy, Fader iowner) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ owner = iowner;
+ controlID = (int)random(1000);
+ grid[y*8+x] = this;
+ idlecolor = FADERIDLECOLOR;
+ activecolor = FADERACTIVECOLOR;
+ off();
+ }
+
+ void down() {
+ owner.faderAction(this);
+ }
+
+ void update() {
+ if (x == owner.x && y == owner.y) {
+ owner.update();
+ }
+ }
+
+ void send() {
+ if (x == owner.x && y == owner.y) {
+ owner.send();
+ }
+ }
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ if (x == owner.x && y == owner.y) {
+ owner.nakedSetValue(ivalue);
+ }
+ }
+
+ void chainTo(Control control, boolean send) {
+ owner.chainTo(control, send);
+ }
+
+ void propagateToChainedControls() {
+ owner.propagateToChainedControls();
+ }
+}
+
+class Fader extends Control {
+ FaderSegment[] segments;
+ long lastupdate;
+ boolean schedule;
+
+
+ void faderAction(FaderSegment sender){}
+
+ boolean canUpdate() {
+ long now = (new Date()).getTime();
+ debug("l "+lastupdate);
+ debug("n "+now);
+ debug("d "+(now-lastupdate));
+ if (now - lastupdate < 100) {
+ lastupdate = now;
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ void schedule() {
+ schedule = true;
+ new Scheduler(this);
+ }
+
+ void cancelSchedule() {
+ schedule = false;
+ }
+
+ boolean hasSchedule() {
+ return schedule;
+ }
+
+}
+
+class XFader extends Fader {
+ int sx;
+ int lastsx;
+
+ XFader(int ix, int iy, int ixsize) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ xsize = ixsize;
+ sx = x;
+ lastsx = -1;
+ takeover = 0;
+ segments = new FaderSegment[xsize];
+ for (int i = 0; i < xsize; i++) {
+ segments[i] = new FaderSegment(x+i, y, this);
+ }
+ grid[y*8+x].on();
+ }
+
+ void faderAction(FaderSegment sender) {
+ sx = sender.x;
+ float relx = sx - x;
+ float quantum = relx * (1 / ((float)xsize - 1));
+ float amount = ((relx + quantum) / (float)xsize) * 127;
+ setValue((int)amount);
+ }
+
+ void update() {
+ //if (lastsx == sx && !hasSchedule()) schedule();
+ if (lastsx != sx || canUpdate()) {
+ if (page == selectedPage) {
+ for(int i = 0; i < xsize; i++) {
+ if (i <= sx) { grid[y*8+x+i].on(); } else { grid[y*8+x+i].off(); }
+ }
+ lastsx = sx;
+ lastupdate = (new Date()).getTime();
+ }
+ }
+ }
+
+ void send() {
+ if (channel != 0) outputChannel = channel-1;
+ controlOut(x, y, value);
+ }
+
+ void setTakeover(int itakeover) {
+ //debug("setTakeover()");
+ takeover = itakeover;
+ }
+
+ void nakedSetValue(int ivalue) {
+ if (TAKEOVER == false) {
+ float step = 127 / (xsize-1);
+ sx = (int)(ivalue / step);
+ value = ivalue;
+ propagateToChainedControls();
+ } else {
+ //debug("nakedSetValueX() blocked because takeover is in progress.");
+ }
+ }
+
+ void takeoverSetValue(int ivalue) {
+ float step = 127 / (xsize-1);
+ sx = (int)(ivalue / step);
+ value = ivalue;
+ send();
+ if (lastsx != sx) update();
+ propagateToChainedControls();
+ }
+
+ void setValue(int ivalue) {
+ if (takeover == 0) {
+ float step = 127 / (xsize-1);
+ sx = (int)(ivalue / step);
+ value = ivalue;
+ send();
+ if (lastsx != sx) update();
+ propagateToChainedControls();
+ } else {
+ //debug("Delegating control to takeover routine");
+ takeover(ivalue);
+ }
+ }
+
+ void takeover(int ivalue) {
+ threadsSpawned++;
+ TakeOver takeover = new TakeOver(this, ivalue, this.takeover);
+ }
+
+}
+
+class IXFader extends Fader {
+ int sx;
+ int lastsx;
+
+ IXFader(int ix, int iy, int ixsize) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ xsize = ixsize;
+ sx = 0;
+ lastsx = -1;
+ takeover = 0;
+ segments = new FaderSegment[xsize];
+ for (int i = 0; i < xsize; i++) {
+ segments[i] = new FaderSegment(x+i, y, this);
+ }
+ grid[y*8+x].on();
+ }
+
+ void faderAction(FaderSegment sender) {
+ sx = sender.x;
+ float relx = sx - x;
+ float quantum = relx * (1 / ((float)xsize - 1));
+ float amount = ((relx + quantum) / (float)xsize) * 127;
+ setValue(127-(int)amount);
+ }
+
+ void update() {
+ //if (lastsx == sx && !hasSchedule()) schedule();
+ if (lastsx != sx || canUpdate()) {
+ if (page == selectedPage) {
+ for(int i = xsize-1; i >= 0; i--) {
+ debug("sx="+((xsize-1)-sx)+" i="+i);
+ if (i >= (xsize-1)-sx) { grid[y*8+x+i].on(); } else { grid[y*8+x+i].off(); }
+ //if (i <= sx) { grid[y*8+x+i].on(); } else { grid[y*8+x+i].off(); }
+ }
+ lastsx = sx;
+ lastupdate = (new Date()).getTime();
+ }
+ }
+ }
+
+ void send() {
+ if (channel != 0) outputChannel = channel-1;
+ controlOut(x, y, value);
+ }
+
+ void setTakeover(int itakeover) {
+ //debug("setTakeover()");
+ takeover = itakeover;
+ }
+
+ void nakedSetValue(int ivalue) {
+ if (TAKEOVER == false) {
+ float step = 127 / (xsize-1);
+ sx = (int)(ivalue / step);
+ value = ivalue;
+ propagateToChainedControls();
+ } else {
+ //debug("nakedSetValueX() blocked because takeover is in progress.");
+ }
+ }
+
+ void takeoverSetValue(int ivalue) {
+ float step = 127 / (xsize-1);
+ sx = (int)(ivalue / step);
+ value = ivalue;
+ send();
+ if (lastsx != sx) update();
+ propagateToChainedControls();
+ }
+
+ void setValue(int ivalue) {
+ if (takeover == 0) {
+ float step = 127 / (xsize-1);
+ sx = (int)(ivalue / step);
+ value = ivalue;
+ send();
+ if (lastsx != sx) update();
+ propagateToChainedControls();
+ } else {
+ //debug("Delegating control to takeover routine");
+ takeover(ivalue);
+ }
+ }
+
+ void takeover(int ivalue) {
+ threadsSpawned++;
+ TakeOver takeover = new TakeOver(this, ivalue, this.takeover);
+ }
+
+}
+
+class YFader extends Fader {
+ int sy;
+ int lastsy;
+
+ YFader(int ix, int iy, int iysize) {
+ controlID = (int)random(1000);
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ ysize = iysize;
+ sy = y;
+ lastsy = -1;
+ value = 0;
+ segments = new FaderSegment[ysize];
+ for (int i = 0; i < ysize; i++) {
+ segments[i] = new FaderSegment(x, y - i, this);
+ }
+ grid[y*8+x].on();
+ }
+
+ void faderAction(FaderSegment sender) {
+ sy = sender.y;
+ float rely = sy - y;
+ float quantum = rely * (1 / ((float)ysize - 1));
+ float amount = ((rely + quantum) / (float)ysize) * 127;
+ setValue(0 - ((int)amount));
+ }
+
+ void update() {
+ //if (lastsy == sy && !hasSchedule()) schedule();
+ if (lastsy != sy || canUpdate()) {
+
+ if (page == selectedPage) {
+ for(int i = y; i > y - ysize; i--) {
+ if (i >= sy) { grid[i*8+x].on(); } else { grid[i*8+x].off(); }
+ }
+ lastsy = sy;
+ lastupdate = (new Date()).getTime();
+ }
+ }
+ }
+
+ void send() {
+ if (channel != 0) outputChannel = channel-1;
+ controlOut(x, y, value);
+ }
+
+ void setTakeover(int itakeover) {
+ //debug("setTakeover()");
+ takeover = itakeover;
+ }
+
+ void nakedSetValue(int ivalue) {
+ if (TAKEOVER == false) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(ivalue / step));
+ sy = y - rely;
+ value = ivalue;
+ propagateToChainedControls();
+ } else {
+ //debug("nakedSetValue() blocked because takeover is in progress.");
+ }
+ }
+
+ void takeoverSetValue(int ivalue) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(ivalue / step));
+ sy = y - rely;
+ value = ivalue;
+ send();
+ //debug("sy "+sy+" lastsy "+lastsy);
+ if (lastsy != sy) update();
+ propagateToChainedControls();
+ }
+
+ void setValue(int ivalue) {
+ if (takeover == 0) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(ivalue / step));
+ sy = y - rely;
+ value = ivalue;
+ send();
+ if (lastsy != sy) update();
+ propagateToChainedControls();
+ } else {
+ //debug("Delegating control to takeover routine");
+ takeover(ivalue);
+ }
+ }
+
+ void takeover(int ivalue) {
+ threadsSpawned++;
+ TakeOver takeover = new TakeOver(this, ivalue, this.takeover);
+ }
+}
+
+class IYFader extends Fader {
+ int sy;
+ int lastsy;
+
+ IYFader(int ix, int iy, int iysize) {
+ controlID = (int)random(1000);
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ ysize = iysize;
+ sy = y;
+ lastsy = -1;
+ value = 0;
+ segments = new FaderSegment[ysize];
+ for (int i = 0; i < ysize; i++) {
+ segments[i] = new FaderSegment(x, y - i, this);
+ }
+ grid[y*8+x].on();
+ }
+
+ void faderAction(FaderSegment sender) {
+ sy = sender.y;
+ debug("!! sy="+sy);
+ float rely = sy - y;
+ float quantum = rely * (1 / ((float)ysize - 1));
+ float amount = ((rely + quantum) / (float)ysize) * 127;
+ setValue(127 + ((int)amount));
+ debug("!! value="+(127 + ((int)amount)));
+ }
+
+ void update() {
+ if (lastsy != sy || canUpdate()) {
+
+ if (page == selectedPage) {
+ for(int i = 0; i < ysize; i++) {
+ int pos = y - sy;
+ pos = (ysize-1)-pos;
+ debug("i="+i+" sy="+sy+" pos="+pos);
+ if (i >= pos ) { grid[(y-i)*8+x].on(); } else { grid[(y-i)*8+x].off(); }
+ }
+
+ lastsy = sy;
+ lastupdate = (new Date()).getTime();
+ }
+ }
+ }
+
+ void send() {
+ if (channel != 0) outputChannel = channel-1;
+ controlOut(x, y, value);
+ }
+
+ void setTakeover(int itakeover) {
+ //debug("setTakeover()");
+ takeover = itakeover;
+ }
+
+ void nakedSetValue(int ivalue) {
+ if (TAKEOVER == false) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(ivalue / step));
+ sy = y - rely;
+ value = ivalue;
+ propagateToChainedControls();
+ } else {
+ //debug("nakedSetValue() blocked because takeover is in progress.");
+ }
+ }
+
+ void takeoverSetValue(int ivalue) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(ivalue / step));
+ sy = y - rely;
+ value = ivalue;
+ send();
+ //debug("sy "+sy+" lastsy "+lastsy);
+ if (lastsy != sy) update();
+ propagateToChainedControls();
+ }
+
+ void setValue(int ivalue) {
+ if (takeover == 0) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(ivalue / step));
+ sy = y - rely;
+ value = ivalue;
+ send();
+ if (lastsy != sy) update();
+ propagateToChainedControls();
+ } else {
+ //debug("Delegating control to takeover routine");
+ takeover(ivalue);
+ }
+ }
+
+ void takeover(int ivalue) {
+ threadsSpawned++;
+ TakeOver takeover = new TakeOver(this, ivalue, this.takeover);
+ }
+}
+
+class SliderSegment extends GridSegment {
+ Slider owner;
+
+ SliderSegment(int ix, int iy, Slider iowner) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ owner = iowner;
+ grid[y*8+x] = this;
+ idlecolor = SLIDERIDLECOLOR;
+ activecolor = SLIDERACTIVECOLOR;
+ off();
+ }
+
+ void down() {
+ owner.sliderAction(this);
+ }
+
+ void up() {
+ owner.liftFinger(this);
+ }
+
+ void update() {
+ off();
+ }
+
+ void send() {
+ if (x == owner.x && y == owner.y) {
+ owner.send();
+ }
+ }
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ if (x == owner.x && y == owner.y) {
+ owner.setValue(ivalue);
+ }
+ }
+
+ void chainTo(Control control, boolean send) {
+ owner.chainTo(control, send);
+ }
+
+ void propagateToChainedControls() {
+ owner.propagateToChainedControls();
+ }
+}
+
+class Slider extends Control {
+ SliderSegment[] segments;
+
+ void sliderAction(SliderSegment sender){}
+ void liftFinger(SliderSegment sender){};
+
+ void send() {
+ if (channel != 0) outputChannel = channel-1;
+ controlOut(x, y, value);
+ }
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ value = ivalue;
+ propagateToChainedControls();
+ }
+}
+
+class XSlider extends Slider {
+ int lastSegment;
+ int segment;
+ int granularity;
+
+ XSlider(int ix, int iy, int ixsize, int igranularity) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ xsize = ixsize;
+ value = SLIDERDEFAULTVALUE;
+ granularity = igranularity;
+ segments = new SliderSegment[xsize];
+ for (int i = 0; i < xsize; i++) {
+ segments[i] = new SliderSegment(x+i, y, this);
+ }
+ }
+
+ void sliderAction(SliderSegment sender) {
+ segment = sender.x;
+ if (lastSegment - 1 == segment) {
+ decrease();
+ //debug("Decreasing "+this+" to "+value);
+ }
+ if (lastSegment + 1 == segment) {
+ increase();
+ //debug("Increasing "+this+" to "+value);
+ }
+ sender.on();
+ lastSegment = segment;
+ }
+
+ void liftFinger(SliderSegment sender) {
+ sender.off();
+ }
+
+ void increase() {
+ if (value < 127) {
+ value += granularity;
+ }
+ if (value > 127) {
+ value = 127;
+ }
+ send();
+ propagateToChainedControls();
+ }
+
+ void decrease() {
+ if (value > 0) {
+ value -= granularity;
+ }
+ if (value < 0) {
+ value = 0;
+ }
+ send();
+ propagateToChainedControls();
+ }
+}
+
+class YSlider extends Slider {
+ int lastSegment;
+ int segment;
+ int granularity;
+
+ YSlider(int ix, int iy, int iysize, int igranularity) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ ysize = iysize;
+ value = SLIDERDEFAULTVALUE;
+ granularity = igranularity;
+ segments = new SliderSegment[ysize];
+ for (int i = 0; i < ysize; i++) {
+ segments[i] = new SliderSegment(x, y - i, this);
+ }
+ }
+
+ void sliderAction(SliderSegment sender) {
+ segment = sender.y;
+ if (lastSegment - 1 == segment) {
+ increase();
+ }
+ if (lastSegment + 1 == segment) {
+ decrease();
+ }
+ sender.on();
+ lastSegment = segment;
+ }
+
+ void liftFinger(SliderSegment sender) {
+ sender.off();
+ }
+
+ void increase() {
+ if (value < 127) {
+ value += granularity;
+ }
+ if (value > 127) {
+ value = 127;
+ }
+ send();
+ propagateToChainedControls();
+ }
+
+ void decrease() {
+ if (value > 0) {
+ value -= granularity;
+ }
+ if (value < 0) {
+ value = 0;
+ }
+ send();
+ propagateToChainedControls();
+ }
+}
+
+class PadSegment extends GridSegment {
+ Pad owner;
+
+ PadSegment(int ix, int iy, Pad iowner) {
+ x = ix;
+ y = iy;
+ owner = iowner;
+ grid[y*8+x] = this;
+ idlecolor = PADOFFCOLOR;
+ activecolor = PADONCOLOR;
+ off();
+ }
+
+ void down() {
+ owner.padAction(this);
+ }
+
+ void update() {
+ owner.update(this);
+ }
+
+ void send() {
+ if (x == owner.x && y == owner.y) {
+ owner.sendX();
+ }
+ if (x == owner.x+1 && y == owner.y) {
+ owner.sendY();
+ }
+ }
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ if (x == owner.x && y == owner.y) {
+ owner.nakedSetValueX(ivalue);
+ }
+ if (x == owner.x+1 && y == owner.y) {
+ owner.nakedSetValueY(ivalue);
+ }
+ }
+
+ void chainTo(Control control, boolean send) {
+ owner.chainTo(control, send);
+ }
+
+ void propagateToChainedControls() {
+ owner.propagateToChainedControls();
+ }
+}
+
+class Pad extends Control {
+ PadSegment[] padSegments;
+ int sx;
+ int sy;
+ int lastsx;
+ int lastsy;
+ int xvalue;
+ int yvalue;
+ boolean invertx = false;
+ boolean inverty = false;
+ boolean TAKEOVER;
+ long threadsSpawned = 0;
+ long threadsFinished = 0;
+
+ Pad(int ix, int iy, int ixsize, int iysize) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ xsize = ixsize;
+ ysize = iysize;
+ sx = x;
+ sy = y+ysize-1;
+ takeover = 0;
+ yvalue = 0;
+ xvalue = 0;
+ lastsx = x;
+ lastsy = y+ysize-1;
+ padSegments = new PadSegment[xsize*ysize];
+
+ for (int yi = 0; yi < ysize; yi++) {
+ for (int xi = 0; xi < xsize; xi++) {
+ padSegments[yi*ysize+xi] = new PadSegment(xi+x, yi+y, this);
+ }
+ }
+ }
+
+ void padAction(PadSegment sender) {
+ //debug("padAction()");
+ sy = sender.y;
+ float rely = sy - y;
+ float quantum = rely * (1 / ((float)ysize - 1));
+ float amounty = 127-((rely + quantum) / (float)ysize) * 127;
+ sx = sender.x;
+ float relx = sx - x;
+ quantum = relx * (1 / ((float)xsize - 1));
+ float amountx = ((relx + quantum) / (float)xsize) * 127;
+ setValue((int)amountx, (int)amounty);
+ }
+
+ void send() {
+ sendX();
+ sendY();
+ }
+
+ void sendX() {
+ if (channel != 0) outputChannel = channel-1;
+ if (!invertx) controlOut(x, y, xvalue);
+ if (invertx) controlOut(x, y, 127-xvalue);
+ }
+
+ void sendY() {
+ if (channel != 0) outputChannel = channel-1;
+ if (!inverty) controlOut(x+1, y, yvalue);
+ if (inverty) controlOut(x+1, y, 127-yvalue);
+
+ }
+
+ void setTakeover(int takeover) {
+ this.takeover = takeover;
+ }
+
+ void invertx() {
+ invertx = true;
+ }
+
+ void inverty() {
+ inverty = true;
+ }
+
+ void nakedSetValueX(int ivalue) {
+ if (invertx) ivalue = 127-ivalue;
+ if (TAKEOVER == false) {
+ float step = 127 / (xsize-1);
+ sx = x+(int)(ivalue / step);
+ xvalue = ivalue;
+ propagateToChainedControls();
+ //debug("nakedSetValueX() succeeded.");
+ } else {
+ //debug("nakedSetValueX() blocked because takeover is in progress.");
+ }
+ }
+
+ void nakedSetValueY(int ivalue) {
+ if (inverty) ivalue = 127-ivalue;
+ if (TAKEOVER == false) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(ivalue / step));
+ sy = (ysize-1)+(y - rely);
+ yvalue = ivalue;
+ propagateToChainedControls();
+ //debug("nakedSetValueY() succeeded.");
+ } else {
+ //debug("nakedSetValueY() blocked because takeover is in progress.");
+ }
+ }
+
+ void takeoverSetValue(int ixvalue, int iyvalue) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(iyvalue / step));
+ sy = (ysize-1)+(y - rely);
+ yvalue = iyvalue;
+
+ step = 127 / (xsize-1);
+ sx = x+(int)(ixvalue / step);
+ xvalue = ixvalue;
+ //debug("xvalue = "+xvalue+". sx = "+sx);
+ //debug("yvalue = "+yvalue+". sy = "+sy);
+ send();
+ if (lastsy != sy || lastsx != sx) update();
+ propagateToChainedControls();
+ }
+
+ void setValue(int ixvalue, int iyvalue) {
+ if (takeover == 0) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(iyvalue / step));
+ sy = (ysize-1)+(y - rely);
+ yvalue = iyvalue;
+
+ step = 127 / (xsize-1);
+ sx = x+(int)(ixvalue / step);
+ xvalue = ixvalue;
+ //debug("xvalue = "+xvalue+". sx = "+sx);
+ //debug("yvalue = "+yvalue+". sy = "+sy);
+ send();
+ if (lastsy != sy || lastsx != sx) update();
+ propagateToChainedControls();
+ } else {
+ //debug("Delegating control to takeover routine");
+ takeover(ixvalue, iyvalue);
+ }
+ }
+
+ void takeover(int ixvalue, int iyvalue) {
+ threadsSpawned++;
+ TakeOver2d takeover = new TakeOver2d(this, ixvalue, iyvalue, this.takeover);
+ }
+
+ void update() {
+ if (page == selectedPage) {
+ //debug("update() sx="+sx+" sy="+sy);
+ grid[lastsx+lastsy*8].off();
+ grid[sx+sy*8].on();
+ lastsx = sx;
+ lastsy = sy;
+ }
+ }
+
+ void update(PadSegment sender) {
+ //debug("update("+sender+") sx="+sx+" sy="+sy);
+ if ((sender.x == x && sender.y == y) || (sender.x-1 == x && sender.y == y)) {
+ //debug("is the case");
+ if (sender.x == sx && sender.y == sy) { sender.on(); } else { sender.off(); }
+ grid[sx+sy*8].on();
+ if (lastsx != sx || lastsy != sy) {
+ grid[lastsx+lastsy*8].off();
+ lastsx = sx;
+ lastsy = sy;
+ }
+
+ } else {
+ //debug("not the case");
+ if (sender.x == sx && sender.y == sy) sender.on();
+ if (sender.x != sx || sender.y != sy) sender.off();
+ }
+ /*lastsx = sx;
+ lastsy = sy;*/
+
+ }
+}
+
+class Drumrack extends Control {
+
+ Drumrack(int ix, int iy, int ixsize, int iysize, int startOctave, String startNote, int vel, boolean invert) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ xsize = ixsize;
+ ysize = iysize;
+ Note note;
+
+ int snote = addOctave(parseNote(startNote), startOctave);
+
+ if (invert) {
+ for (int yi = 0; yi < ysize; yi++) {
+ for (int xi = 0; xi < xsize; xi++) {
+ new Note(xi+x, ((ysize-1)-yi)+y, snote, vel);
+ snote++;
+ }
+ }
+ } else {
+ for (int yi = 0; yi < ysize; yi++) {
+ for (int xi = 0; xi < xsize; xi++) {
+ new Note(xi+x, yi+y, snote, vel);
+ snote++;
+ }
+ }
+ }
+ }
+
+ int parseNote(String note) {
+ note = note.toLowerCase();
+ if (note.equals("c")) return 0;
+ if (note.equals("c#")) return 1;
+ if (note.equals("d")) return 2;
+ if (note.equals("d#")) return 3;
+ if (note.equals("e")) return 4;
+ if (note.equals("f")) return 5;
+ if (note.equals("f#")) return 6;
+ if (note.equals("g")) return 7;
+ if (note.equals("g#")) return 8;
+ if (note.equals("a")) return 9;
+ if (note.equals("a#")) return 10;
+ if (note.equals("b")) return 11;
+ return 0;
+ }
+
+ int addOctave(int note, int octave) {
+ octave = octave+1;
+ return note+(octave*12);
+ }
+
+}
+
+class MeterSegment extends GridSegment {
+ Meter owner;
+
+ MeterSegment(int ix, int iy, Meter iowner) {
+ page = selectedPage;
+ x = ix;
+ y = iy;
+ owner = iowner;
+ controlID = (int)random(1000);
+ grid[y*8+x] = this;
+ idlecolor = METERIDLECOLOR;
+ activecolor = METERACTIVECOLOR;
+ off();
+ }
+
+ void update() {
+ if (x == owner.x && y == owner.y) {
+ owner.update();
+ }
+ }
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ if (x == owner.x && y == owner.y) {
+ owner.nakedSetValue(ivalue);
+ }
+ }
+
+ void chainTo(Control control, boolean send) {
+ owner.chainTo(control, send);
+ }
+
+ void propagateToChainedControls() {
+ owner.propagateToChainedControls();
+ }
+}
+
+class Meter extends Control {
+ MeterSegment[] segments;
+ long lastupdate;
+
+ boolean canUpdate() {
+ long now = (new Date()).getTime();
+ if (now - lastupdate > updatedelay) {
+ lastupdate = now;
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+class XMeter extends Meter {
+ int sx;
+ float sxf;
+ int lastsx;
+ float lastsxf;
+
+ XMeter(int ix, int iy, int ixsize) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ xsize = ixsize;
+ sx = x;
+ lastsx = -1;
+ takeover = 0;
+ segments = new MeterSegment[xsize];
+ for (int i = 0; i < xsize; i++) {
+ segments[i] = new MeterSegment(x+i, y, this);
+ }
+ grid[y*8+x].on();
+ }
+
+ void update() {
+
+ if (canUpdate()) {
+ //debug("go");
+ if (page == selectedPage) {
+ for(int i = 0; i < xsize; i++) {
+
+ if (i != sx && i != sx+1) {
+ grid[y*8+x+i].off();
+ }
+
+ if (i == sx+1 && !(sxf - sx >= 0.56) && !(((sxf - sx) > 0.43 && (sxf - sx) < 0.56) && sx != xsize-1)) {
+ grid[y*8+x+i].off();
+ }
+
+ if (i == sx) {
+ if (sxf - sx <= 0.43) {
+ grid[y*8+x+i].on();
+ }
+
+ if (sxf - sx >= 0.56) {
+ grid[y*8+x+(i+1)].on();
+ }
+
+ if (((sxf - sx) > 0.43 && (sxf - sx) < 0.56) && sx != xsize-1) {
+ grid[y*8+x+i].on();
+ grid[y*8+x+i+1].on();
+ }
+ }
+
+ }
+ lastsx = sx;
+ lastsxf = sxf;
+ }
+ } else { /*debug("filtered");*/ }
+ }
+
+
+ void nakedSetValue(int ivalue) {
+ float step = 127 / (xsize-1);
+ sxf = (ivalue / step);
+ //println(sxf);
+ sx = (int)sxf;
+ value = ivalue;
+ propagateToChainedControls();
+ }
+
+
+ void setValue(int ivalue) {
+ float step = 127 / (xsize-1);
+ sxf = (ivalue / step);
+ //println(sxf);
+ sx = (int)sxf;
+ value = ivalue;
+ send();
+ if (lastsxf != sxf) update();
+ propagateToChainedControls();
+
+ }
+
+}
+
+class YMeter extends Meter {
+ int sy;
+ int lastsy;
+ float syf;
+ float lastsyf;
+
+ YMeter(int ix, int iy, int iysize) {
+ controlID = (int)random(1000);
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ ysize = iysize;
+ sy = y;
+ lastsy = -1;
+ value = 0;
+ segments = new MeterSegment[ysize];
+ for (int i = 0; i < ysize; i++) {
+ segments[i] = new MeterSegment(x, y - i, this);
+ }
+ grid[y*8+x].on();
+ }
+
+ void update() {
+ //debug("diff: "+abs((lastsy-lastsyf) - (sy-syf)));
+
+ if (page == selectedPage && canUpdate()) {
+ for(int i = y; i >= y-(ysize-1); i--) {
+ if (i != sy && i != sy-1) {
+ grid[i*8+x].off();
+ //debug("turning off 0");
+ }
+
+ if (i == sy-1 && !(sy - syf >= 0.56) && !(((sy - syf) > 0.43 && (syf - sy) < 0.56))) {
+ grid[(i)*8+x].off();
+ }
+
+
+ if (i == sy) {
+ if (sy - syf <= 0.43) {
+ grid[i*8+x].on();
+ //debug("turning on 0");
+ }
+
+ if (sy - syf >= 0.56) {
+ grid[(i-1)*8+x].on();
+ //debug("turning on -1");
+ }
+
+ if (((sy - syf) > 0.43 && (syf - sy) < 0.56) && sy != y-ysize-1) {
+ grid[i*8+x].on();
+ grid[(i-1)*8+x].on();
+ //debug("turning on both");
+ }
+ }
+
+ }
+
+ lastsy = sy;
+ lastsyf = syf;
+ }
+
+ }
+
+
+
+ void nakedSetValue(int ivalue) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(ivalue / step));
+ syf = y - abs((ivalue / step));
+ sy = y - rely;
+ //debug("syf: "+syf);
+ value = ivalue;
+ propagateToChainedControls();
+ }
+
+ void setValue(int ivalue) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(ivalue / step));
+ sy = y - rely;
+ syf = y - abs((ivalue / step));
+ //debug("syf: "+syf);
+ value = ivalue;
+ send();
+ if (lastsyf != syf) update();
+ propagateToChainedControls();
+ }
+}
+
+class ProgressSegment extends GridSegment {
+ Progress owner;
+
+ ProgressSegment(int ix, int iy, Progress iowner) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ owner = iowner;
+ controlID = (int)random(1000);
+ grid[y*8+x] = this;
+ idlecolor = PROGRESSIDLECOLOR;
+ activecolor = PROGRESSACTIVECOLOR;
+ off();
+ }
+
+
+ void update() {
+ if (x == owner.x && y == owner.y) {
+ owner.update();
+ }
+ }
+
+
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ if (x == owner.x && y == owner.y) {
+ owner.nakedSetValue(ivalue);
+ }
+ }
+
+ void chainTo(Control control, boolean send) {
+ owner.chainTo(control, send);
+ }
+
+ void propagateToChainedControls() {
+ owner.propagateToChainedControls();
+ }
+}
+
+class Progress extends Control {
+ ProgressSegment[] segments;
+ long lastupdate;
+
+ boolean canUpdate() {
+ long now = (new Date()).getTime();
+ if (now - lastupdate > updatedelay) {
+ lastupdate = now;
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+class XProgress extends Progress {
+ int sx;
+ int lastsx;
+
+ XProgress(int ix, int iy, int ixsize) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ xsize = ixsize;
+ sx = x;
+ lastsx = -1;
+ takeover = 0;
+ segments = new ProgressSegment[xsize];
+ for (int i = 0; i < xsize; i++) {
+ segments[i] = new ProgressSegment(x+i, y, this);
+ }
+ grid[y*8+x].on();
+ }
+
+ void update() {
+ //if (lastsx != sx) {
+ if (page == selectedPage && canUpdate()) {
+ for(int i = 0; i < xsize; i++) {
+ if (i <= sx) { grid[y*8+x+i].on(); } else { grid[y*8+x+i].off(); }
+ }
+ lastsx = sx;
+ }
+ //}
+ }
+
+ void nakedSetValue(int ivalue) {
+ float step = 127 / (xsize-1);
+ sx = (int)(ivalue / step);
+ value = ivalue;
+ propagateToChainedControls();
+ }
+
+
+ void setValue(int ivalue) {
+ float step = 127 / (xsize-1);
+ sx = (int)(ivalue / step);
+ value = ivalue;
+ send();
+ if (lastsx != sx) update();
+ propagateToChainedControls();
+ }
+}
+
+class YProgress extends Progress {
+ int sy;
+ int lastsy;
+
+ YProgress(int ix, int iy, int iysize) {
+ controlID = (int)random(1000);
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ ysize = iysize;
+ sy = y;
+ lastsy = -1;
+ value = 0;
+ segments = new ProgressSegment[ysize];
+ for (int i = 0; i < ysize; i++) {
+ segments[i] = new ProgressSegment(x, y - i, this);
+ }
+ grid[y*8+x].on();
+ }
+
+ void update() {
+ //if (lastsy != sy) {
+ if (page == selectedPage && canUpdate()) {
+ for(int i = y; i > y - ysize; i--) {
+ if (i >= sy) { grid[i*8+x].on(); } else { grid[i*8+x].off(); }
+ }
+ lastsy = sy;
+ }
+ //}
+ }
+
+ void nakedSetValue(int ivalue) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(ivalue / step));
+ sy = y - rely;
+ value = ivalue;
+ propagateToChainedControls();
+ }
+
+ void setValue(int ivalue) {
+ float step = 127 / (ysize-1);
+ int rely = ((int)(ivalue / step));
+ sy = y - rely;
+ value = ivalue;
+ send();
+ if (lastsy != sy) update();
+ propagateToChainedControls();
+
+ }
+}
+
+class CrsFaderSegment extends GridSegment {
+ CrsFader owner;
+
+ CrsFaderSegment(int ix, int iy, CrsFader iowner) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ owner = iowner;
+ controlID = (int)random(1000);
+ grid[y*8+x] = this;
+ idlecolor = FADERIDLECOLOR;
+ activecolor = FADERACTIVECOLOR;
+ off();
+ }
+
+ void down() {
+ owner.faderAction(this);
+ }
+
+ void up() {
+ owner.stopAction();
+ }
+
+ void update() {
+ if (x == owner.x && y == owner.y) {
+ owner.update();
+ }
+ }
+
+ void send() {
+ if (x == owner.x && y == owner.y) {
+ owner.send();
+ }
+ }
+
+ void nakedSetValue(int ivalue) { setValue(ivalue); }
+ void setValue(int ivalue) {
+ if (x == owner.x && y == owner.y) {
+ owner.nakedSetValue(ivalue);
+ }
+ }
+
+ void chainTo(Control control, boolean send) {
+ owner.chainTo(control, send);
+ }
+
+ void propagateToChainedControls() {
+ owner.propagateToChainedControls();
+ }
+}
+
+class CrsFader extends Control {
+ int sx;
+ float sxf;
+ int lastsx;
+ float lastsxf;
+ float factor;
+ boolean running;
+ CrsFaderSegment[] segments;
+ long lastupdate;
+
+ CrsFader(int ix, int iy, int ixsize) {
+ page = selectedPage;
+ //println(this+" on page "+page);
+ x = ix;
+ y = iy;
+ xsize = ixsize;
+ sx = x;
+ lastsx = -1;
+ takeover = 0;
+ factor = 0;
+ running = false;
+ segments = new CrsFaderSegment[xsize];
+ for (int i = 0; i < xsize; i++) {
+ segments[i] = new CrsFaderSegment(x+i, y, this);
+ }
+ grid[y*8+x].on();
+ }
+
+ void stopAction() {
+ running = false;
+ factor = 0;
+ }
+
+ void faderAction(CrsFaderSegment sender) {
+ if (xsize % 2 != 0) {
+ int relx = sender.x - x + 1;
+ int center = abs(round(((float)xsize/2)));
+ float relpos = ((relx - center)/((float)xsize-1))*2;
+
+ debug("CrsFader update. Sender x = "+sender.x+", relx="+relx);
+ debug("Relative position: "+relpos+", center="+center);
+ } else {
+ int relx = sender.x - x + 1;
+ int center = xsize/2;
+ float relpos = 0;
+
+ if (relx <= center) {
+ relpos = (center - (relx-1))/(float)center * -1;
+ } else {
+ relpos = (relx - center)/(float)center;
+ }
+
+ debug("CrsFader update. Sender x = "+sender.x+", relx="+relx);
+ debug("Relative position: "+relpos+", center="+center);
+
+ factor = relpos;
+ if (factor != 0 && !running) {
+ running = true;
+ spawnThread();
+ }
+ }
+ }
+
+ void spawnThread() {
+ CrsUpdater updater = new CrsUpdater(this);
+ }
+
+ void updateValue() {
+ int nvalue = (int)(value+(factor*6));
+ if (nvalue < 0) nvalue = 0;
+ if (nvalue > 127) nvalue = 127;
+ setValue(nvalue);
+ }
+
+ boolean canUpdate() {
+ long now = (new Date()).getTime();
+ if (now - lastupdate > updatedelay) {
+ lastupdate = now;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void update() {
+
+ if (canUpdate()) {
+ //debug("go");
+ if (page == selectedPage) {
+ for(int i = 0; i < xsize; i++) {
+
+ if (i != sx && i != sx+1) {
+ grid[y*8+x+i].off();
+ }
+
+ if (i == sx+1 && !(sxf - sx >= 0.56) && !(((sxf - sx) > 0.43 && (sxf - sx) < 0.56) && sx != xsize-1)) {
+ grid[y*8+x+i].off();
+ }
+
+ if (i == sx) {
+ if (sxf - sx <= 0.43) {
+ grid[y*8+x+i].on();
+ }
+
+ if (sxf - sx >= 0.56) {
+ grid[y*8+x+(i+1)].on();
+ }
+
+ if (((sxf - sx) > 0.43 && (sxf - sx) < 0.56) && sx != xsize-1) {
+ grid[y*8+x+i].on();
+ grid[y*8+x+i+1].on();
+ }
+ }
+
+ }
+ lastsx = sx;
+ lastsxf = sxf;
+ }
+ } else { /*debug("filtered");*/ }
+ }
+
+
+ void nakedSetValue(int ivalue) {
+ if (!running) {
+ float step = 127 / (xsize-1);
+ sxf = (ivalue / step);
+ //println(sxf);
+ sx = (int)sxf;
+ value = ivalue;
+ propagateToChainedControls();
+ } else {
+ debug("Blocked because thread is running");
+ }
+ }
+
+
+ void setValue(int ivalue) {
+ float step = 127 / (xsize-1);
+ sxf = (ivalue / step);
+ //println(sxf);
+ sx = (int)sxf;
+ value = ivalue;
+ send();
+ if (lastsxf != sxf) update();
+ propagateToChainedControls();
+ }
+
+ void send() {
+ controlOut(x, y, value);
+ }
+
+}
+
diff --git a/Defines.pde b/Defines.pde
new file mode 100644
index 0000000..4c4e6b2
--- /dev/null
+++ b/Defines.pde
@@ -0,0 +1,22 @@
+// Static variables
+static final int PAGESELECTOR = 64;
+static final int MFINAL = 9999;
+
+// Button type definitions
+static final int INDICATOR = 0;
+static final int HOLD = 1;
+static final int TOGGLE = 2;
+
+// Control defaults definitions
+static final int SLIDERDEFAULTVALUE = 63;
+static final int FADERDEFAULTVALUE = 0;
+
+// Control out definitions
+static final int ON = 127;
+static final int OFF = 0;
+
+// MIDI status codes
+static final int NOTEON = 0x90;
+static final int NOTEOFF = 0x80;
+static final int CTLCHG = 0xB0;
+static final int PRGCHG = 0xC0;
diff --git a/Demos.pde b/Demos.pde
new file mode 100644
index 0000000..041d230
--- /dev/null
+++ b/Demos.pde
@@ -0,0 +1,266 @@
+int yline = 1;
+int ylinel = yline;
+int xline = 1;
+int xlinel = xline;
+int linedelay = 10;
+int tick = 0;
+int demo = 0;
+int sdemo = 0;
+int dtick = 0;
+int stick = 0;
+int skip = 0;
+int dtime = 450;
+boolean shuffledemos = true;
+boolean scrollerSetup = false;
+String letters[] = new String[27];
+static int totalrows;
+String msg;
+boolean msgoverride = false;
+int ml = 9999;
+
+int msgindexes[];
+static boolean bitmap[][];
+
+void setupDemos() {
+ if (DEMOCHOICE != -1) { demo = DEMOCHOICE; shuffledemos = false; }
+ msg = DEMOTEXT.toLowerCase();
+ msgindexes = new int[msg.length()+1];
+ scrollerSetup();
+ sdemo = demo;
+ demo = 0;
+}
+
+void demos() {
+ dtick++;
+ if (dtick == dtime && shuffledemos == true) {
+ dtick = 0;
+ demo = (int)random(0, 7);
+ clearDisplay();
+ //println(demo);
+ }
+ if (demo == 0) randomOnOff();
+ if (demo == 1) ylines();
+ if (demo == 2) xlines();
+ if (demo == 3) xylines();
+ if (demo == 4) xlinesn();
+ if (demo == 5) ylinesn();
+ if (demo == 6) scroller();
+}
+
+void overrideBitmap(int line, String data) {
+ if (!scrollerSetup) msgindexes = new int[data.length()+1];
+ msgoverride = true;
+ ml = data.length()+6;
+ msg = "";
+ for (int i = 0; i < ml; i=i+6) {
+ msg = msg+" ";
+ }
+ scrollerSetup();
+ for (int i = 0; i < data.length(); i++) {
+ if (i < ml) bitmap[i][line] = data.substring(i, 1+i).equals("#");
+ }
+
+ /*println("Bitmap:");
+ for (int y = 0; y < 6; y++) {
+ print("\n");
+ for (int x = 0; x < totalrows; x++) {
+ if (bitmap[x][y] == true) { print("#"); } else { print(" "); }
+ }
+ }
+ println("");*/
+}
+
+void scrollerSetup() {
+ if (!scrollerSetup) {
+ if (!msgoverride) msg = DEMOTEXT.toLowerCase();
+ if (!msgoverride) msg = " "+msg;
+ letters[0] = "000000000000000000000000000000000000";
+ letters[1] = "00#0000#0#00#000#0#####0#000#0#000#0";
+ letters[2] = "####00#000#0####00#000#0#000#0#####0";
+ letters[3] = "00##000#00#0#00000#000000#00#000##00";
+ letters[4] = "###000#00#00#000#0#000#0#00#00###000";
+ letters[5] = "#####0#00000####00#00000#00000#####0";
+ letters[6] = "#####0#00000####00#00000#00000#00000";
+ letters[7] = "00##000#00#0#00000#00##00#00#000##00";
+ letters[8] = "#000#0#000#0#####0#000#0#000#0#000#0";
+ letters[9] = "0###0000#00000#00000#00000#0000###00";
+ letters[10] = "00###0000#00000#00000#00#00#000##000";
+ letters[11] = "#00#00#0#000##0000#0#000#00#00#000#0";
+ letters[12] = "#00000#00000#00000#00000#00000#####0";
+ letters[13] = "0#0#00#0#0#0#0#0#0#000#0#000#0#000#0";
+ letters[14] = "0#00#0#0#0#0#0#0#0#0#0#0#0#0#0#00#00";
+ letters[15] = "0###00#000#0#000#0#000#0#000#00###00";
+ letters[16] = "####00#000#0####00#00000#00000#00000";
+ letters[17] = "0###00#000#0#000#0#0#0#0#00#000##0#0";
+ letters[18] = "####00#000#0####00#0#000#00#00#000#0";
+ letters[19] = "0####0#000000##000000##00000#0####00";
+ letters[20] = "#####000#00000#00000#00000#00000#000";
+ letters[21] = "#000#0#000#0#000#0#000#0#000#00###00";
+ letters[22] = "#000#0#000#00#0#000#0#0000#00000#000";
+ letters[23] = "#000#0#000#0#000#0#0#0#0#0#0#00#0#00";
+ letters[24] = "#000#00#0#0000#0000#0#00#000#0#000#0";
+ letters[25] = "#000#00#0#0000#00000#00000#00000#000";
+ letters[26] = "#####00000#000##000#0000#00000#####0";
+
+ totalrows = msg.length()*6;
+ bitmap = new boolean[totalrows+8][7];
+ for (int i = 0; i < msg.length(); i++) {
+ if (msg.charAt(i) == ' ') msgindexes[i] = 0;
+ if (msg.charAt(i) == 'a') msgindexes[i] = 1;
+ if (msg.charAt(i) == 'b') msgindexes[i] = 2;
+ if (msg.charAt(i) == 'c') msgindexes[i] = 3;
+ if (msg.charAt(i) == 'd') msgindexes[i] = 4;
+ if (msg.charAt(i) == 'e') msgindexes[i] = 5;
+ if (msg.charAt(i) == 'f') msgindexes[i] = 6;
+ if (msg.charAt(i) == 'g') msgindexes[i] = 7;
+ if (msg.charAt(i) == 'h') msgindexes[i] = 8;
+ if (msg.charAt(i) == 'i') msgindexes[i] = 9;
+ if (msg.charAt(i) == 'j') msgindexes[i] = 10;
+ if (msg.charAt(i) == 'k') msgindexes[i] = 11;
+ if (msg.charAt(i) == 'l') msgindexes[i] = 12;
+ if (msg.charAt(i) == 'm') msgindexes[i] = 13;
+ if (msg.charAt(i) == 'n') msgindexes[i] = 14;
+ if (msg.charAt(i) == 'o') msgindexes[i] = 15;
+ if (msg.charAt(i) == 'p') msgindexes[i] = 16;
+ if (msg.charAt(i) == 'q') msgindexes[i] = 17;
+ if (msg.charAt(i) == 'r') msgindexes[i] = 18;
+ if (msg.charAt(i) == 's') msgindexes[i] = 19;
+ if (msg.charAt(i) == 't') msgindexes[i] = 20;
+ if (msg.charAt(i) == 'u') msgindexes[i] = 21;
+ if (msg.charAt(i) == 'v') msgindexes[i] = 22;
+ if (msg.charAt(i) == 'w') msgindexes[i] = 23;
+ if (msg.charAt(i) == 'x') msgindexes[i] = 24;
+ if (msg.charAt(i) == 'y') msgindexes[i] = 25;
+ if (msg.charAt(i) == 'z') msgindexes[i] = 26;
+ }
+ // Construct the bitmap
+ for (int i = 0; i < msg.length(); i++) {
+ for (int x = 0; x < 6; x++) {
+ for (int y = 0; y < 6; y++) {
+ bitmap[x+i*6][y] = letters[msgindexes[i]].substring(x+y*6, 1+x+y*6).equals("#");
+ }
+ }
+ }
+ /*println("Bitmap:");
+ for (int y = 0; y < 6; y++) {
+ print("\n");
+ for (int x = 0; x < totalrows; x++) {
+ if (bitmap[x][y] == true) { print("#"); } else { print(" "); }
+ }
+ }*/
+ scrollerSetup = true;
+ println("");
+ }
+}
+
+void scroller() {
+ // Run the scroller
+ if (skip == 8) {
+ clearDisplay();
+ for (int x = 0; x < 8; x++) {
+ for (int y = 0; y < 7; y++) {
+ if (bitmap[x+(stick%totalrows)][y] == true) ledOn(x, y+1, SCROLLERCOLOR);
+ }
+ }
+ stick++;
+ skip = 0;
+ }
+ skip++;
+}
+
+void randomOnOff() {
+ ledOn((int)random(8), (int)random(8), randomColor());
+ //ledOff((int)random(8), (int)random(8));
+}
+
+void ylines() {
+ tick++;
+ if (tick == linedelay) {
+ tick = 0;
+ int colorl = BLACK;
+ while (colorl == BLACK) colorl = randomColor();
+ for (int y = 0; y < 8; y++) {
+ ledOff(ylinel, y);
+ }
+ for (int y = 0; y < 8; y++) {
+ ledOn(yline, y, colorl);
+ }
+ ylinel = yline;
+ yline++;
+ if (yline == 8) yline = 0;
+ }
+}
+
+void ylinesn() {
+ tick++;
+ if (tick == linedelay) {
+ tick = 0;
+ int colorl = BLACK;
+ while (colorl == BLACK) colorl = randomColor();
+ /*for (int y = 0; y < 8; y++) {
+ ledOff(ylinel, y);
+ }*/
+ for (int y = 0; y < 8; y++) {
+ ledOn(yline, y, colorl);
+ }
+ ylinel = yline;
+ yline++;
+ if (yline == 8) yline = 0;
+ }
+}
+
+void xlines() {
+ tick++;
+ if (tick == linedelay) {
+ tick = 0;
+ int colorl = BLACK;
+ while (colorl == BLACK) colorl = randomColor();
+ for (int x = 0; x < 8; x++) {
+ ledOff(x, xlinel);
+ }
+ for (int x = 0; x < 8; x++) {
+ ledOn(x, xline, colorl);
+ }
+ xlinel = xline;
+ xline++;
+ if (xline == 8) xline = 0;
+ }
+}
+
+void xlinesn() {
+ tick++;
+ if (tick == linedelay) {
+ tick = 0;
+ int colorl = BLACK;
+ while (colorl == BLACK) colorl = randomColor();
+ /*for (int x = 0; x < 8; x++) {
+ ledOff(x, xlinel);
+ }*/
+ for (int x = 0; x < 8; x++) {
+ ledOn(x, xline, colorl);
+ }
+ xlinel = xline;
+ xline++;
+ if (xline == 8) xline = 0;
+ }
+}
+
+void xylines() {
+ tick++;
+ if (tick == linedelay) {
+ tick = 0;
+ int colorl = BLACK;
+ while (colorl == BLACK) colorl = randomColor();
+ for (int x = 0; x < 8; x++) {
+ ledOff(x, xlinel);
+ ledOff(xlinel, x);
+ }
+ for (int x = 0; x < 8; x++) {
+ ledOn(x, xline, colorl);
+ ledOn(xline, x, colorl);
+ }
+ xlinel = xline;
+ xline++;
+ if (xline == 8) xline = 0;
+ }
+}
diff --git a/Display.pde b/Display.pde
new file mode 100644
index 0000000..5da8322
--- /dev/null
+++ b/Display.pde
@@ -0,0 +1,118 @@
+int frameSize = WINDOWSIZE - FRAMEBORDER*2;
+int buttonSize = frameSize / 8;
+int offset = 1;
+boolean displaystate = false;
+boolean dlock = false;
+
+void display(int x, int y, int lcolor) {
+ if (!SILENTMODE) {
+ while (dlock);
+ dlock = true;
+ fill(64, 64, 64);
+ noStroke();
+ smooth();
+ int o = 150;
+ if (x >= 0 && x < 8 && y >= 0 && y < 8 && displaystate) rect((x*buttonSize)+FRAMEBORDER+offset, (y*buttonSize)+FRAMEBORDER+offset, buttonSize-offset, buttonSize-offset);
+ if (lcolor == RED) fill(255, 0, 0, o);
+ if (lcolor == REDL) fill(127, 0, 0, o);
+ if (lcolor == GREEN) fill(0, 255, 0, o);
+ if (lcolor == GREENL) fill(0, 127, 0, o);
+ if (lcolor == AMBER) fill(255, 127, 0, o);
+ if (lcolor == AMBERL) fill(127, 63, 0, o);
+ if (lcolor == YELLOW) fill(255, 255, 0, o);
+
+ if (lcolor == BLUE) fill(0, 0, 255, o);
+ if (lcolor == BLUEL) fill(0, 0, 127, o);
+ if (lcolor == CYAN) fill(0, 255, 255, o);
+ if (lcolor == CYANL) fill(0, 127, 127, o);
+ if (lcolor == MAGENTA) fill(255, 0, 255, o);
+ if (lcolor == MAGENTAL) fill(127, 0, 127, o);
+ if (lcolor == WHITE) fill(230, 230, 230, o);
+ if (lcolor == WHITEL) fill(180, 180, 200, o);
+
+
+ if (x >= 0 && x < 8 && y >= 0 && y < 8 && displaystate) {
+ rect((x*buttonSize)+FRAMEBORDER+offset, (y*buttonSize)+FRAMEBORDER+offset, buttonSize-offset, buttonSize-offset);
+ o = 16;
+ if (lcolor == RED) fill(255, 0, 0, o);
+ if (lcolor == REDL) fill(127, 0, 0, o);
+ if (lcolor == GREEN) fill(0, 255, 0, o);
+ if (lcolor == GREENL) fill(0, 127, 0, o);
+ if (lcolor == AMBER) fill(255, 127, 0, o);
+ if (lcolor == AMBERL) fill(127, 63, 0, o);
+ if (lcolor == YELLOW) fill(255, 255, 0, o);
+ if (lcolor == BLUE) fill(0, 0, 255, o);
+ if (lcolor == BLUEL) fill(0, 0, 127, o);
+ if (lcolor == CYAN) fill(0, 255, 255, o);
+ if (lcolor == CYANL) fill(0, 127, 127, o);
+ if (lcolor == MAGENTA) fill(255, 0, 255, o);
+ if (lcolor == MAGENTAL) fill(127, 0, 127, o);
+ if (lcolor == WHITE) fill(230, 230, 230, o);
+ if (lcolor == WHITEL) fill(200, 200, 200, o);
+
+ noStroke();
+ for(int i = 30; i > 0; i--) {
+ ellipse((x*buttonSize+(buttonSize/2))+FRAMEBORDER+offset, (y*buttonSize+(buttonSize/2))+FRAMEBORDER+offset, (buttonSize-i*2)-4, (buttonSize-i*2)-4);
+ }
+ }
+ dlock = false;
+ }
+}
+
+void ledOn(int x, int y, int lcolor) {
+ if (displaystate) {
+ int address = (y*16) + x;
+ if (!NETWORK) launchpad.sendOn(address, lcolor, 0);
+ if (online) slaveOn(x, y, lcolor);
+ display(x, y, lcolor);
+ }
+}
+
+void ledOff(int x, int y) {
+ if (displaystate) {
+ int address = (y*16) + x;
+ if (!NETWORK) launchpad.sendOn(address, 0, 0);
+ }
+}
+
+void clearDisplay() {
+ if (displaystate) {
+ if (!NETWORK) launchpad.sendCtl(0, 0, 0);
+ if (online) slaveClear();
+ fill(64, 64, 64);
+ for (int i = 0; i < 64; i++) {
+ if (!SILENTMODE) rect(((i%8)*buttonSize)+FRAMEBORDER+offset, ((int)((float)i/8)*buttonSize)+FRAMEBORDER+offset, buttonSize-offset, buttonSize-offset);
+ }
+ }
+}
+
+void pageName() {
+ pagename = pagenames[selectedPage-1];
+ //debug("pageName()"+pagename);
+ fill(#000000);
+ noStroke();
+ rect(0, WINDOWSIZE-12, 250, 12);
+ fill(#FFFFFF);
+ textFont(f11, 11);
+ smooth();
+ textAlign(LEFT);
+ if (pagename != null) text(pagename, 2, WINDOWSIZE-2);
+}
+
+int randomColor() {
+ colors[0] = BLACK;
+ colors[1] = BLUE;
+ colors[2] = RED;
+ colors[3] = CYAN;
+ colors[4] = AMBER;
+ colors[5] = YELLOW;
+ colors[6] = GREEN;
+ colors[7] = MAGENTA;
+ colors[8] = BLACK;
+ colors[9] = BLACK;
+ colors[10] = BLACK;
+
+ int select = (int)random(11);
+
+ return colors[select];
+}
diff --git a/Documentation/Control types.txt b/Documentation/Control types.txt
new file mode 100644
index 0000000..71e2e37
--- /dev/null
+++ b/Documentation/Control types.txt
@@ -0,0 +1,72 @@
+This is a brief description of the various control types, for use in the layout.txt file.
+
+Hold button
+ Created with : button X Y hold
+ example : button 3 3 hold
+
+Toggle button
+ Created with : button X Y toggle
+ example : button 3 3 toggle
+
+Horizontal fader
+ Created with : xfader X Y SIZE
+ example : xfader 0 0 8
+
+Vertical fader
+ Created with : yfader X Y SIZE
+ example : yfader 0 7 8
+
+Horizontal slider
+ Created with : xslider X Y SIZE GRANULARITY
+ example : xslider 0 0 8 5
+
+Vertical slider
+ Created with : yslider X Y SIZE GRANULARITY
+ example : yslider 0 7 8 5
+
+Indicator LED
+ Created with : led X Y
+ example : led 0 7
+
+2D Pad
+ Created with : pad X Y XSIZE YSIZE
+ example : pad 0 0 7 7
+
+Other entries:
+
+New page
+ Created with : page NUMBER
+ example : page 1
+ Description : All controls must be mapped to a specific page
+ Start the layouts.txt file with the line "page 1"
+ Define controls for this page beneath it.
+ When done, add another page entry, "page 2",
+ define more controls and so forth.
+
+Controller chaining
+ Created with : chain X1 Y1 to X2 Y2 [send]
+ example : chain 0 0 to 4 0 yes
+ Description : The above example chains the controller at 0,0
+ in the grid to the controller at 4,0 and
+ instructs the chained controller to also transmit
+ midi when it is updated. The send argument is
+ optional, and if it is not set, only the internal
+ state of the controller will be updated, but it
+ won't transmit any MIDI, unless physically
+ activated. Chaining is very useful when you want
+ several different controls to influence the same
+ parameter in a piece of software for example.
+ This will ensure that all the controls on the
+ Launchpad are in sync, so you wont get any
+ erratic "jumps".
+
+Bi-directional communication
+ Katapult can handle bi-directional communication, meaning that, as
+ an example, when you change a virtual fader in a piece of software,
+ the corresponding fader on the Launchpad surface is also updated.
+ To use this feature, you must assign MIDI output from your software
+ using the following format: "MIDI channel=Page, CC=(Y*8+X)".
+ So, the MIDI channel your software outputs to determines what page
+ the control is located on, and the CC number corresponds to the
+ position in the grid, where the control is located.
+
diff --git a/Documentation/Katapult Documentation.pages b/Documentation/Katapult Documentation.pages
new file mode 100644
index 0000000..22201ac
Binary files /dev/null and b/Documentation/Katapult Documentation.pages differ
diff --git a/Documentation/Libraries.zip b/Documentation/Libraries.zip
new file mode 100644
index 0000000..f6cf7b0
Binary files /dev/null and b/Documentation/Libraries.zip differ
diff --git a/Documentation/PD MIDI Settings.png b/Documentation/PD MIDI Settings.png
new file mode 100644
index 0000000..c39f949
Binary files /dev/null and b/Documentation/PD MIDI Settings.png differ
diff --git a/Documentation/Required libraries.txt b/Documentation/Required libraries.txt
new file mode 100644
index 0000000..a3b015d
--- /dev/null
+++ b/Documentation/Required libraries.txt
@@ -0,0 +1,6 @@
+These libraries are required for Katapult to work:
+
+ - oscP5
+ - proMIDI
+
+Also, on OS X at least the "libmmj" java extension is required for Katapult to be able to receive MIDI input.
\ No newline at end of file
diff --git a/Editor.pde b/Editor.pde
new file mode 100644
index 0000000..e69de29
diff --git a/Graphics/DMG background.ai b/Graphics/DMG background.ai
new file mode 100644
index 0000000..3f94cf0
--- /dev/null
+++ b/Graphics/DMG background.ai
@@ -0,0 +1,915 @@
+%PDF-1.5
%
+1 0 obj
<>/OCGs[14 0 R]>>/Type/Catalog>>
endobj
40 0 obj
<>stream
+
+
+
+
+ application/pdf
+
+
+ DMG background
+
+
+
+
+ Adobe Illustrator CS3
+ 2010-01-20T16:32:48+11:00
+ 2010-01-20T16:32:49+11:00
+ 2010-01-20T16:32:49+11:00
+
+
+
+ 256
+ 160
+ JPEG
+ /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA
AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK
DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f
Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAoAEAAwER
AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA
AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB
UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE
1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ
qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy
obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp
0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo
+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A4G7sGIBIAJoK4skVHYzS
fZuofkZQv66YqiU8v6q4qjIw8RID+rFV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf
+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV
3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9
Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf
+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxV3+G9Y/yf+DxVRutG1G0jEs9P
T5BTRq9fbFUuk+23zOKWsVbVmU1UkHxG2KomLVNRi+xcyAeBYkfca4qjIfNGqp9pkl/1lp/xHjiq
Oh839BNbfNkb+BH8cCo+DzLpUtAztET2dT+sVGKEfDdW04rDKkn+qwP6sVVcVdirsVdirsVdirsV
dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVSvzJ/wAc3/non68VYbJ9tvmcKWsVdirsVdir
sVdirgSDUbEdDiqNt9Z1OCnC4Ygfst8Q/wCGriqZW/m64WguIVcfzISp/GuBU0tvMmlzUDOYWPaQ
UH3ioxQmUckci8o2DqejKQR+GKrsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVSv
zJ/xzf8Anon68VYc4+NvmcKraYpdTFXUxV1MVdTFXUxV1MVdTFXUxV1MVVIZ54W5wyNG3ipI/Viq
a2vmjUYqCbjOv+UOLfeP6YoTi08y6dPQSEwP4P0/4IfxwKmqOjqGRgynoymoP3Yq3irsVdirsVdi
rsVdirsVdirsVdirsVdirsVdirsVSvzJ/wAc3/non68KsPf7bfM4qtxS7FXYq7FXYq7FXYq7FXYq
7FXYq7FXYqrW93c2zcoJWjPeh2PzHQ4qnNn5rmWi3cYkH+/E2b7uh/DFU9s9TsbsfuJQW7odm+44
oRWKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVK/Mf/HN/wCeifrxViDj42+ZxStpirqYq6mKupir
qYq6mKupirqYq6mKupirqYq6mKupirqYq6mKtjY1GxHQ4qmtj5jvreiyn14x2f7X0N/XFWQ2GtWF
5RUf05T/ALrfY/R2OKEfirsVdirsVdirsVdirsVdirsVdirsVSvzJ/xzf+eifrxViDj42+ZxVqmK
upirqYq6mKupirqYq6mKupirqYq6mKupirqYq6mKupirqYq6mKupirqYqmmn+YL61orn14R+y53H
ybFWS2GrWV6KRNSTvE2zf2/RiqNpirqYq6mKupirqYq6mKupirqYq6mKupiqV+ZB/uN/56J+vFWI
uPiPzOKraYpdTFXUxV1MVdTFXUxV1MVdTFXUxV1MVdTFXUxV1MVdTFXUxV1MVdTFXUxV1MVbWqkM
pII3BHXFU803zLPFSO7Blj6CQfbHz8cUMkt7iC4iEsDh0PcfxxVVpirqYq6mKupirqYq6mKupirq
YqlXmQf7jf8Anon68VYmw+I/PFWqYpdTFXUxV1MVdTFXUxV1MVdTFXUxV1MVdTFXUxV1MVdTFXUx
V1MVdTFXUxV1MVdTFXUxVWtLu5tJfUgco3cdj7EYqyrS9dt7ykctIrj+U/Zb/VP8MUJpTFXUxV1M
VdTFXUxV1MVdTFUr8yD/AHGn/jIn68VYoV3OKtccVdxxV3HFXccVdxxV3HFXccVdxxV3HFXccVdx
xV3HFXccVdxxV3HFXccVdxxV3HFXccVdxxV3HFXccVdxxVPdJ8wPFSG8JePosvVl+fiMVZKhR0Do
QysKqw3BGKt8cVdxxV3HFXccVdxxVK/Mi/7jD/rr+vFWLFd8Va4Yq7hiruGKu4Yq7hiruGKu4Yq7
hiruGKu4Yq7hiruGKu4Yq7hiruGKu4Yq7hiruGKu4Yq7hiruGKu4Yq7hiruGKphpWqz2L8d3tz9q
Pw91xVl1tPDcwrLCwZG7+/gcVVeOKu44q7jiruOKpV5kX/cYf9df14qxgJtiruGKu4Yq7hiruGKu
4Yq7hiruGKu4Yq7hiruGKu4Yq7hiruGKu4Yq7hiruGKu4Yq7hiruGKu4Yq7hiruGKu4Yq7hiruGK
ovTr6exm5x7odnjPQjFWX2dzDdwCaE1U9R3B8DiqvxxQ7jiruOKpV5kX/cW3+suKsaWP4R8sVb9P
FXenirvTxV3p4q708Vd6eKu9PFXenirvTxV3p4q708Vd6eKu9PFXenirvTxV3p4q708Vd6eKu9PF
XenirvTxV3p4q708Vd6eKu9PFXeniqK068msp/UTdTtInYj+uKswtpYriFZojVG/D2OKqvDFXcMV
SrzKn+4t/wDWXFWOxx/AvyGKrvT9sVd6ftirvT9sVd6ftirvT9sVd6ftirvT9sVd6ftirvT9sVd6
ftirvT9sVd6ftirvT9sVd6ftirvT9sVd6ftirvT9sVd6ftirvT9sVd6ftirvT9sVd6ftirvT9sVd
6ftirvT9sVd6ftirvT9sVR+k3z2U9GqYH/vF8P8AKGKssUKyhlNVIqCOhBxVvhiqV+ZU/wBxUnzG
KpBFF+6Tb9kfqwqv9L2xV3pe2Ku9L2xV3pe2Ku9L2xV3pe2Ku9L2xV3pe2Ku9L2xV3pe2Ku9L2xV
3pe2Ku9L2xV3pe2Ku9L2xV3pe2Ku9L2xV3pe2Ku9L2xV3pe2Ku9L2xV3pe2Ku9L2xV3pe2Ku9L2x
V3pe2Ku9L2xV3pe2Kp3oF5Q/U5Tsd4Se3iuKp96ftgVK/Msf+4mXbuMVSOCGsEZ/yV/VhQqej7Yq
70fbFXej7Yq70fbFXej7Yq70fbFXej7Yq70fbFXej7Yq70fbFXej7Yq70fbFXej7Yq70fbFXej7Y
q70fbFXej7Yq70fbFXej7Yq70fbFXej7Yq70fbFXej7Yq70fbFXej7Yq70fbFXej7Yq70fbFW1jK
sGXZgagjsRirLNOnF1bLJ+2PhkH+UMVQnmaL/cRN/n2OKpRawVtYdv2F/UMVVfQ9sVd6HtirvQ9s
Vd6HtirvQ9sVd6HtirvQ9sVd6HtirvQ9sVd6HtirvQ9sVd6HtirvQ9sVd6HtirvQ9sVd6HtirvQ9
sVd6HtirvQ9sVd6HtirvQ9sVd6HtirvQ9sVd6HtirvQ9sVd6HtirvQ9sVd6HtirvQ9sVR+jSehdB
G/u5fhPz7HFUf5mh/wBw0+3b+BxVLLK3rZwGnWNP+IjFVf6v7Yq76v7Yq76v7Yq76v7Yq76v7Yq7
6v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Y
q76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76v7Yq76vTFUz1o
+v5cll/aKHl8wDXFVDT7athbGnWJP+IjFCI+re2FXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbF
XfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVv
bFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFXfVvbFW7xKaDfxEdEZx/
wJBwKyyz/LTzzDaQQvosheKNUYie0oSqgGlZxjaq3/KuvO//AFZJf+R9n/1XxtXf8q687/8AVkl/
5H2f/VfG1d/yrrzv/wBWSX/kfZ/9V8bV3/KuvO//AFZJf+R9n/1XxtXf8q687/8AVkl/5H2f/VfG
1d/yrrzv/wBWSX/kfZ/9V8bV3/KuvO//AFZJf+R9n/1XxtXf8q687/8AVkl/5H2f/VfG1d/yrrzv
/wBWSX/kfZ/9V8bV3/KuvO//AFZJf+R9n/1XxtXf8q687/8AVkl/5H2f/VfG1d/yrrzv/wBWSX/k
fZ/9V8bV3/KuvO//AFZJf+R9n/1XxtXf8q687/8AVkl/5H2f/VfG1d/yrrzv/wBWSX/kfZ/9V8bV
3/KuvO//AFZJf+R9n/1XxtXf8q687/8AVkl/5H2f/VfG1d/yrrzv/wBWSX/kfZ/9V8bV3/KuvO//
AFZJf+R9n/1XxtXf8q687/8AVkl/5H2f/VfG1d/yrrzv/wBWSX/kfZ/9V8bV3/KuvO//AFZJf+R9
n/1XxtXf8q687/8AVkl/5H2f/VfG1d/yrrzv/wBWSX/kfZ/9V8bV3/KuvO//AFZJf+R9n/1XxtXf
8q687/8AVkl/5H2f/VfG1d/yrrzv/wBWSX/kfZ/9V8bV3/KuvO//AFZJf+R9n/1XxtXf8q687/8A
Vkl/5H2f/VfG1d/yrrzv/wBWSX/kfZ/9V8bVRvPy088zWk8KaLIHljZFJntKAspArSc42r//2Q==
+
+
+
+
+
+ uuid:AF33DFC47307DF1191BC9DB221D61255
+ uuid:f1607388-19d9-2441-af51-47f3bd441e31
+
+ uuid:f1c065ce-15b7-453e-bf4a-735a529f4226
+ uuid:65E6390686CF11DBA6E2D887CEACB407
+
+
+
+ Document
+ Web
+
+
+ 1
+ True
+ False
+
+ 14400.000000
+ 14400.000000
+ Pixels
+
+
+
+ Cyan
+ Magenta
+ Yellow
+ Black
+
+
+
+
+
+ Default Swatch Group
+ 0
+
+
+
+ White
+ RGB
+ PROCESS
+ 255
+ 255
+ 255
+
+
+ Black
+ RGB
+ PROCESS
+ 0
+ 0
+ 0
+
+
+ RGB Red
+ RGB
+ PROCESS
+ 255
+ 0
+ 0
+
+
+ RGB Yellow
+ RGB
+ PROCESS
+ 255
+ 255
+ 0
+
+
+ RGB Green
+ RGB
+ PROCESS
+ 0
+ 255
+ 0
+
+
+ RGB Cyan
+ RGB
+ PROCESS
+ 0
+ 255
+ 255
+
+
+ RGB Blue
+ RGB
+ PROCESS
+ 0
+ 0
+ 255
+
+
+ RGB Magenta
+ RGB
+ PROCESS
+ 255
+ 0
+ 255
+
+
+ R=193 G=39 B=45
+ RGB
+ PROCESS
+ 193
+ 39
+ 45
+
+
+ R=237 G=28 B=36
+ RGB
+ PROCESS
+ 237
+ 28
+ 36
+
+
+ R=241 G=90 B=36
+ RGB
+ PROCESS
+ 241
+ 90
+ 36
+
+
+ R=247 G=147 B=30
+ RGB
+ PROCESS
+ 247
+ 147
+ 30
+
+
+ R=251 G=176 B=59
+ RGB
+ PROCESS
+ 251
+ 176
+ 59
+
+
+ R=252 G=238 B=33
+ RGB
+ PROCESS
+ 252
+ 238
+ 33
+
+
+ R=217 G=224 B=33
+ RGB
+ PROCESS
+ 217
+ 224
+ 33
+
+
+ R=140 G=198 B=63
+ RGB
+ PROCESS
+ 140
+ 198
+ 63
+
+
+ R=57 G=181 B=74
+ RGB
+ PROCESS
+ 57
+ 181
+ 74
+
+
+ R=0 G=146 B=69
+ RGB
+ PROCESS
+ 0
+ 146
+ 69
+
+
+ R=0 G=104 B=55
+ RGB
+ PROCESS
+ 0
+ 104
+ 55
+
+
+ R=34 G=181 B=115
+ RGB
+ PROCESS
+ 34
+ 181
+ 115
+
+
+ R=0 G=169 B=157
+ RGB
+ PROCESS
+ 0
+ 169
+ 157
+
+
+ R=41 G=171 B=226
+ RGB
+ PROCESS
+ 41
+ 171
+ 226
+
+
+ R=0 G=113 B=188
+ RGB
+ PROCESS
+ 0
+ 113
+ 188
+
+
+ R=46 G=49 B=146
+ RGB
+ PROCESS
+ 46
+ 49
+ 146
+
+
+ R=27 G=20 B=100
+ RGB
+ PROCESS
+ 27
+ 20
+ 100
+
+
+ R=102 G=45 B=145
+ RGB
+ PROCESS
+ 102
+ 45
+ 145
+
+
+ R=147 G=39 B=143
+ RGB
+ PROCESS
+ 147
+ 39
+ 143
+
+
+ R=158 G=0 B=93
+ RGB
+ PROCESS
+ 158
+ 0
+ 93
+
+
+ R=212 G=20 B=90
+ RGB
+ PROCESS
+ 212
+ 20
+ 90
+
+
+ R=237 G=30 B=121
+ RGB
+ PROCESS
+ 237
+ 30
+ 121
+
+
+ R=199 G=178 B=153
+ RGB
+ PROCESS
+ 199
+ 178
+ 153
+
+
+ R=153 G=134 B=117
+ RGB
+ PROCESS
+ 153
+ 134
+ 117
+
+
+ R=115 G=99 B=87
+ RGB
+ PROCESS
+ 115
+ 99
+ 87
+
+
+ R=83 G=71 B=65
+ RGB
+ PROCESS
+ 83
+ 71
+ 65
+
+
+ R=198 G=156 B=109
+ RGB
+ PROCESS
+ 198
+ 156
+ 109
+
+
+ R=166 G=124 B=82
+ RGB
+ PROCESS
+ 166
+ 124
+ 82
+
+
+ R=140 G=98 B=57
+ RGB
+ PROCESS
+ 140
+ 98
+ 57
+
+
+ R=117 G=76 B=36
+ RGB
+ PROCESS
+ 117
+ 76
+ 36
+
+
+ R=96 G=56 B=19
+ RGB
+ PROCESS
+ 96
+ 56
+ 19
+
+
+ R=66 G=33 B=11
+ RGB
+ PROCESS
+ 66
+ 33
+ 11
+
+
+
+
+
+ Web Color Group
+ 1
+
+
+
+ R=236 G=28 B=36
+ RGB
+ PROCESS
+ 236
+ 28
+ 36
+
+
+ R=0 G=169 B=157
+ RGB
+ PROCESS
+ 0
+ 169
+ 157
+
+
+ R=102 G=45 B=145
+ RGB
+ PROCESS
+ 102
+ 45
+ 145
+
+
+ R=139 G=146 B=152 1
+ RGB
+ PROCESS
+ 139
+ 146
+ 152
+
+
+
+
+
+ Grayscale
+ 1
+
+
+
+ K=100
+ GRAY
+ PROCESS
+ 255
+
+
+ K=90
+ GRAY
+ PROCESS
+ 229
+
+
+ K=80
+ GRAY
+ PROCESS
+ 204
+
+
+ K=70
+ GRAY
+ PROCESS
+ 178
+
+
+ K=60
+ GRAY
+ PROCESS
+ 153
+
+
+ K=50
+ GRAY
+ PROCESS
+ 127
+
+
+ K=40
+ GRAY
+ PROCESS
+ 101
+
+
+ K=30
+ GRAY
+ PROCESS
+ 76
+
+
+ K=20
+ GRAY
+ PROCESS
+ 50
+
+
+ K=10
+ GRAY
+ PROCESS
+ 25
+
+
+ K=5
+ GRAY
+ PROCESS
+ 12
+
+
+
+
+
+
+
+
+ Adobe PDF library 8.00
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
endstream
endobj
2 0 obj
<>
endobj
14 0 obj
<>
endobj
15 0 obj
[/View/Design]
endobj
16 0 obj
<>>>
endobj
13 0 obj
[14 0 R]
endobj
5 0 obj
<>/ArtBox[6990.5 7074.5 7410.5 7324.5]/Group 33 0 R/MediaBox[0.0 0.0 14400.0 14400.0]/Thumb 39 0 R/TrimBox[0.0 0.0 14400.0 14400.0]/Resources<>/Shading<>/ColorSpace<>/Properties<>/ExtGState<>>>/Type/Page/LastModified(D:20100120163248+11'00')>>
endobj
32 0 obj
<>stream
+HP10@qSemZ
+2!DZ-7 FUЍJ1rΡd'{Llye~.q(cũLa_+ޙaƻ٠`WΓqc#AÎ-VƔaD=zz32krn >
endobj
39 0 obj
<>stream
+8;Wk$5n\hn$j?;ZmO=4E;uVuQmi]a[$I"l:+09L!'!$!GeO;Zfg0A@q%NLNDfi+@2
+/9"S4-Ybc6bq9Z8(esYdRI/GhgVZ,'pC(<9^"(JVS%4>ik?7;N.@uRpL[h`$_Qor^
+e!fJ/;B+KM[#D]f'[qr%GQO;[4Ql7Albar;9]:\<2lRCj+j,k?$18G-M7a.OkhQo)
+MDULM^B">sCOc-~>
endstream
endobj
27 0 obj
<>
endobj
31 0 obj
<>
endobj
28 0 obj
[/ICCBased 29 0 R]
endobj
29 0 obj
<>stream
+HyTSwoɞc
[5laQIBHADED2mtFOE.c}088GNg9w߽ '0 ֠Jb
+ 2y.-;!KZ ^i"L0-
@8(r;q7Ly&Qq4j|9
+V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'K t;\
ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹Aom?W=
+x- [ 0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?c. R
߁-25 S>ӣVd`rn~Y&+`;A4 A9 =-tl`;~p Gp| [`L`< "AYA+Cb(R, *T2B-
+ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r9\A&GrQhE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_(0J:EAiQ(()ӔWT6U@P+!~mDeԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel
}}Cq9
+N')].uJr
+wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó tizf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4
+n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY.=b?SƕƩȺy
چk5%4m7lqlioZlG+Zzmzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś
nLl<9O [$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD- u`ֲK³8%yhYѹJº;.!
+zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs
2F[p(@Xr4Pm8Ww)Km
endstream
endobj
20 0 obj
<>
endobj
18 0 obj
[/ICCBased 29 0 R]
endobj
21 0 obj
<>
endobj
22 0 obj
<>
endobj
30 0 obj
<>/ExtGState<>>>/BBox[6991.0 7325.0 7411.67 7074.33]>>stream
+/CS0 cs 1 1 1 scn
+1 i
+/GS0 gs
+q 1 0 0 1 7411.0811 7074.333 cm
+0 0 m
+0.588 0 l
+0.588 250.667 l
+-420.08 250.667 l
+-420.08 249.974 l
+-198.113 222.683 -27.572 115.699 0 0 c
+f
+Q
+
endstream
endobj
25 0 obj
<>
endobj
37 0 obj
[/Indexed/DeviceRGB 255 38 0 R]
endobj
38 0 obj
<>stream
+8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0
+b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup`
+E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn
+6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O(
+l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~>
endstream
endobj
34 0 obj
[/ICCBased 29 0 R]
endobj
6 0 obj
<>
endobj
7 0 obj
<>
endobj
8 0 obj
<>stream
+%!PS-Adobe-3.0
%%Creator: Adobe Illustrator(R) 13.0
%%AI8_CreatorVersion: 13.0.0
%%For: (Mark Qvist) ()
%%Title: (Untitled-1)
%%CreationDate: 1/20/10 4:32 PM
%%BoundingBox: -36 -36 456 286
%%HiResBoundingBox: -36 -36 456 286
%%DocumentProcessColors: Cyan Magenta Yellow Black
%AI5_FileFormat 9.0
%AI12_BuildNumber: 406
%AI3_ColorUsage: Color
%AI7_ImageSettings: 0
%%RGBProcessColor: 0 0 0 ([Registration])
%AI3_Cropmarks: 0 0 420 250
%AI3_TemplateBox: 210 125 210 125
%AI3_TileBox: -193 -154.5 590 404.5
%AI3_DocumentPreview: None
%AI5_ArtSize: 14400 14400
%AI5_RulerUnits: 6
%AI9_ColorModel: 1
%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0
%AI5_TargetResolution: 800
%AI5_NumLayers: 1
%AI9_OpenToView: -177.5 359.1665 1.5 1166 715 18 0 0 73 75 0 0 1 1 1 0 1
%AI5_OpenViewLayers: 7
%%PageOrigin:-256.3003 -441.2988
%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9
%AI9_Flatten: 1
%AI12_CMSettings: 00.MS
%%EndComments
endstream
endobj
9 0 obj
<>stream
+%%BoundingBox: -14 -11 426 261
%%HiResBoundingBox: -13.333 -10.001 425.666 260.333
%AI7_Thumbnail: 128 80 8
%%BeginData: 17168 Hex Bytes
%0000330000660000990000CC0033000033330033660033990033CC0033FF
%0066000066330066660066990066CC0066FF009900009933009966009999
%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66
%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333
%3333663333993333CC3333FF3366003366333366663366993366CC3366FF
%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99
%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033
%6600666600996600CC6600FF6633006633336633666633996633CC6633FF
%6666006666336666666666996666CC6666FF669900669933669966669999
%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33
%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF
%9933009933339933669933999933CC9933FF996600996633996666996699
%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33
%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF
%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399
%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933
%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF
%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC
%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699
%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33
%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100
%000011111111220000002200000022222222440000004400000044444444
%550000005500000055555555770000007700000077777777880000008800
%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB
%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF
%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF
%524C45FD8027F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F82727F8FD7E27F827F8FD06275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%2752275227522752275227522752275227522752275227272720272727F8
%27212720FD04275227FD6F52272027F827F827F827F827F827F827F827F8
%FD04275227524B5227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752FD0527F8272727F8272727F8
%272727F8272027F8FD04275227524B5252524B5252522752525227525252
%275252522752525227525252275252522752525227525252275252522752
%525227525252275252522752525227525252275252522752525227525252
%27525252275252522752525227525252275252522752525220F827F827F8
%27F827F827F827F827F827F827F827F827F827F827F8FD04275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%FD0527202727272027272720272727202727272027272720272727F82721
%27274B27524BFD07524B5252524B5252524B5252524B5252524B5252524B
%5252524B5252524B5252524B5252524B5252524B5252524B5252524B5252
%524B5252524B5252524B5252524B5252524B5252524B5252524B5252524B
%52525227F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827274B275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%27522752275227522752272727F8272727F8272727F8272727F8272727F8
%272727F8272727F8272727F8272727F8272127F8FD04275252524B525252
%275252522752525227525252275252522752525227525252275252522752
%525227525252275252522752525227525252275252522752525227525252
%275252522752525227525252275252524B5227F827F827F827F827F827F8
%27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8
%27F827275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522727272027
%272720272727202727272027272720272727202727272027272720272727
%202727272027272720272127202727FD05524B5252524B5252524B525252
%4B5252524B5252524B5252524B5252524B5252524B5252524B5252524B52
%52524B5252524B5252524B5252524B5252524B5252524B5252524BFD0552
%272027F827F827F827F827F827F827F827F827F827F827F827F827F827F8
%27F827F827F827F827F827F827F827F827F827F827275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%52275227522752FD0527F8272727F8272727F8272727F8272727F8272727
%F8272727F8272727F8272727F8272727F8272727F8272727F8272027F827
%27524B525252275252522752525227525252275252522752525227525252
%275252522752525227525252275252522752525227525252275252522752
%525227525252275252522752525220F827F827F827F827F827F827F827F8
%27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8
%27F827F827F827F827F82727522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752FD0527202727272027
%272720272727202727272027272720272727202727272027272720272727
%20272727202727272027272720272727F8FD04275252524C5252524C5252
%524C5252524C5252524C5252524C5252524C5252524C5252524C5252524C
%5252524C5252524C5252524C5252524C5252524C5252524C52525227F827
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827204B
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%27522752272727F8272727F8272727F8272727F8272727F8272727F82727
%27F8272727F8272727F8272727F8272727F8272727F8272727F8272727F8
%272727F8272727F82727524B525252275252522752525227525252275252
%522752525227525252275252522752525227525252275252522752525227
%52525227525252275252524B5227F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F827F827F82727522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227272720272727212727
%272127272721272727212727272127272721272727212727272127272721
%272727212727272127272721272727212727272127272721272727F82727
%524B5252524C5252524C5252524C5252524C5252524C5252524C5252524C
%5252524C5252524C5252524C5252524C5252524C5252524CFD0552272027
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F82727524C52275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%27522752FD05272027272720272727202727272027272720272727202727
%272027272720272727202727272027272720272727202727272027272720
%272727202727272027272720272727204B27524C52525227525252275252
%522752525227525252275252522752525227525252275252522752525227
%525252275252522752525221F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F827F827F827F827F827F82727522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752FD4D2721524BFD315227F827
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F8272752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522727272027272720272727202727272027272720272727202727
%272027272720272727202727272027272720272727202727272027272720
%2727272027272720272727202727272027272720272727F827274B275252
%524B5252524B5252524B5252524B5252524B5252524B5252524B5252524B
%5252524B5252524BFD055227F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827
%F827F8272752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752FD5327202727FD2B52272727
%F8272027F8272027F8272027F8272027F8272027F8272027F8272027F827
%2027F8272027F8272027F8272027F8272027F8272027F8272027F8272027
%F8272027F8272027F8272027F8272027F8272027F827F82727524B522752
%4B5227524B5227524B5227524B5227524B5227524B5227524B5227524B52
%27524B52FD05272127272721272727212727272127272721272727212727
%272127272721272727212727272127272721272727212727272127272721
%27272721272727212727272127272721272727212727272127272721FD04
%275252524B5252524C5252524C5252524C5252524C5252524C5252524C52
%52524C5252524C52525227F827F827F827F827F827F827F827F827F827F8
%27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8
%27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8
%27F827F827F827F82720524B522752275227522752275227522752275227
%5227522752275227522752275227522752FD5C27FD255227F8272027F827
%2027F8272027F8272027F8272027F8272027F8272027F8272027F8272027
%F8272027F8272027F8272027F8272027F8272027F8272027F8272027F827
%2027F8272027F8272027F8272027F8272027F8272027F827F82727525252
%27524B5227524B5227524B5227524B5227524B5227524B5227524B522752
%FD6027FD2152272127F8272027F8272027F8272027F8272027F8272027F8
%272027F8272027F8272027F8272027F8272027F8272027F8272027F82720
%27F8272027F8272027F8272027F8272027F8272027F8272027F8272027F8
%272027F8272027F827F82721525252275227522752275227522752275227
%52275227522752275227522752FD6227FD1F522727272027272720272727
%202727272027272720272727202727272027272720272727202727272027
%272720272727202727272027272720272727202727272027272720272727
%202727272027272720272727202727272027272720272727202720272752
%5252275252522752525227525252275252522752525227525252FD6627FD
%1B5227F8272727F8272727F8272727F8272727F8272727F8272727F82727
%27F8272727F8272727F8272727F8272727F8272727F8272727F8272727F8
%272727F8272727F8272727F8272727F8272727F8272727F8272727F82727
%27F8272727F8272727F827202727524C5227524B5227524B5227524B5227
%524B5227524B522752FD6827FD1952FD6527212727FD05524B5252524B52
%52524B5252524B5252524B52FD6A27FD1752272727202727272027272720
%272727202727272027272720272727202727272027272720272727202727
%272027272720272727202727272027272720272727202727272027272720
%272727202727272027272720272727202727272027272720272727202727
%27202727FD055227525252275252522752525227525252FD6C27FD1552FD
%6C27FD1352FD6E27FD1352FD6E275252524C5252524C5252524C5252524C
%5227274C2727275227272752272727522727275227272752272727522727
%275227272752272727522727275227272752272727522727275227272752
%272727522727275227272752272727522727275227272752272727522727
%2752272727522727275227272752272727522727274C2752527752525277
%52525277525252775252FD7027FD10522727275227272752272727522727
%275227272752272727522727275227272752272727522727275227272752
%272727522727275227272752272727522727275227272752272727522727
%275227272752272727522727275227272752272727522727275227272752
%272727522727274B27FD0F52FD7227FD0D5227274C275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%5227522752275227522752274C277D5276527D5276527D52765252FD0427
%4C2727274C2727274C2727274C2727274C2727274C2727274C2727274C27
%27274C2727274C2727274C2727274C2727274C2727274C2727274C272727
%4C2727274C2727274C2727274C2727274C2727274C2727274C2727274C27
%27274C2727274C2727274C2727274C2727274C272727FD0C522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%52275227522752275227522752275227522752274B4C7D5252527D525252
%7D525227274C2727274B2727274B2727274B2727274B2727274B2727274B
%2727274B2727274B2727274B2727274B2727274B2727274B2727274B2727
%274B2727274B2727274B2727274B2727274B2727274B2727274B2727274B
%2727274B2727274B2727274B2727274B2727274B2727274B2727274C2727
%27FD0A522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%2752527D527D527D527D5252272752274C2752274C2752274C2752274C27
%52274C2752274C2752274C2752274C2752274C2752274C2752274C275227
%4C2752274C2752274C2752274C2752274C2752274C2752274C2752274C27
%52274C2752274C2752274C2752274C2752274C2752274C2752274C275227
%4C2752274C2752274C27FD04527D5252527D522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%52275227522752275227522752275227522752527D527D527D527D272752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227522752
%275227522752275227522752275227522752275227522752275227FD0452
%7D5252524C52275252522752525227525252275252522752525227525252
%275252522752525227525252275252522752525227525252275252522752
%525227525252275252522752525227525252275252522752525227525252
%275252522752525227525252275252522752525227525252275252522752
%52522752527D527D527D527D272752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%52275227522752275227522752527D527D527DFD05522752525227525252
%275252522752525227525252275252522752525227525252275252522752
%525227525252275252522752525227525252275252522752525227525252
%275252522752525227525252275252522752525227525252275252522752
%5252275252522752525227525252275252522752527D527D527D27275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275227
%522752275227522752275227522752275227522752275227522752275252
%7D527D5228FD7A527D527D7D7D2727525252275252522752525227525252
%275252522752525227525252275252522752525227525252275252522752
%525227525252275252522752525227525252275252522752525227525252
%275252522752525227525252275252522752525227525252275252522752
%52522752525227525252275252522752527D527DFD7D52537D527D522752
%285252522852525228525252285252522852525228525252285252522852
%525228525252285252522852525228525252285252522852525228525252
%285252522852525228525252285252522852525228525252285252522852
%525228525252285252522852525228525252285252522852525228525252
%2753527DFD04527D5252527D5252527D5252527D5252527D5252527D5252
%527D5252527D5252527D5252527D5252527D5252527D5252527D5252527D
%5252527D5252527D5252527D5252527D5252527D5252527D5252527D5252
%527D5252527D5252527D5252527D5252527D5252527D5252527D5252527D
%5252527D52525253527D7D7D5228FD7C527DFD045253527D5253527D5253
%527D5253527D5253527D5253527D5253527D5253527D5253527D5253527D
%5253527D5253527D5253527D5253527D5253527D5253527D5253527D5253
%527D5253527D5253527D5253527D5253527D5253527D5253527D5253527D
%5253527D5253527D5253527D5253527D5253527D5253527D7D7D522753FD
%7B527D535252527D527D527D527D527D527D527D527D527D527D527D527D
%527D527D527D527D527D527D527D527D527D527D527D527D527D527D527D
%527D527D527D527D527D527D527D527D527D527D527D527D527D527D527D
%527D527D527D527D527D527D527D527D527D527D527D527D527D527D527D
%527D527D527D527D527D7D7D52527D5253527D5253527D5253527D525352
%7D5253527D5253527D5253527D5253527D5253527D5253527D5253527D52
%53527D5253527D5253527D5253527D5253527D5253527D5253527D525352
%7D5253527D5253527D5253527D5253527D5253527D5253527D5253527D52
%53527D5253527D5253527D5253527D5253527D7D5252527D527D527D527D
%527D527D527D527D527D527D527D527D527D527D527D527D527D527D527D
%527D527D527D527D527D527D527D527D527D527D527D527D527D527D527D
%527D527D527D527D527D527D527D527D527D527D527D527D527D527D527D
%527D527D527D527D527D527D527D527D527D527D527D527D527D527D5252
%7D527D527D527D527D527D527D527D527D527D527D527D527D527D527D52
%7D527D527D527D527D527D527D527D527D527D527D527D527D527D527D52
%7D527D527D527D527D527D527D527D527D527D527D527D527D527D527D52
%7D527D527D527D527D527D527D527D527D527D527D527D527D527D527D52
%7D527D527D525253FD047D597D7D7D597D7D7D597D7D7D597D7D7D597D7D
%7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D59
%7D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D
%7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D59
%7D7D7D597D7D7D597D7D7D597D52527D527D537D527D537D527D537D527D
%537D527D537D527D537D527D537D527D537D527D537D527D537D527D537D
%527D537D527D537D527D537D527D537D527D537D527D537D527D537D527D
%537D527D537D527D537D527D537D527D537D527D537D527D537D527D537D
%527D537D527D537D527D537D527D537D527D537D59527D597D527D537D52
%7D537D527D537D527D537D527D537D527D537D527D537D527D537D527D53
%7D527D537D527D537D527D537D527D537D527D537D527D537D527D537D52
%7D537D527D537D527D537D527D537D527D537D527D537D527D537D527D53
%7D527D537D527D537D527D537D527D537D527D537D527D537D527D537D52
%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF
%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF
%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF
%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF
%A8FFA8FFA8FFA8FF
%%EndData
endstream
endobj
10 0 obj
<>stream
+%AI12_CompressedDataxi%Gr ?~#"jVs[3=DfLD(m_#|Y-R#2-yann}_;7sէ}л?}Я~ŝ/Ov?|O3|Wy?O_ÛOo_?Ow_/uW⇻0}cw˟7{?|ӻ?}÷퇏?W~Ww۷nշǕoo.3ywӻ~]
?J3o]Oeh}4Eb?opR/ۏ~xWVGz,ݘ^mY2XV̏I'iA?)ާ~=SWy_w?[0п|S.B߽~[ϟ߾Gxן}x'DYPWy
?|8/4ӝ/3g
+wS7z|+:nle~o10eb2ϴwu+npB^F^ӧy&1(1e>P&o?|Oӟ/?@߿)7OTw?߿+:_c9_՛yMWo~jڻ~-tԟ~ӛO2}?Ç:O:L#yk߾X[ӗPs7|ß|{A^VZ?xw
W?O=ʥe
wwu
7+'s]~~k?߀o_%$`iFK{CnM:sc9\dN'?Sbib~:( Tsk_m87hs^.2MRns},Ppʷ/cI/; 'S#~ÿ)&Da }a`_AzgP~?6oO;>yOD}>wAi(By fEL /- \NPQ\ѫ +?CD,KK s-3MqT[v
Ӧu<
+vBZ9L7=a_j0qӆi;J+8SlPp~ޏ>s/eO<81Rw9B R^q(1Ƙb-c