Commit | Line | Data |
---|---|---|
e73ef85d MS |
1 | import netP5.*; |
2 | import oscP5.*; | |
3 | ||
4 | /** | |
5 | * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND | |
6 | * | |
7 | * //\\ //\\ //\\ //\\ | |
8 | * ///\\\ ///\\\ ///\\\ ///\\\ | |
9 | * \\\/// \\\/// \\\/// \\\/// | |
10 | * \\// \\// \\// \\// | |
11 | * | |
12 | * EXPERTS ONLY!! EXPERTS ONLY!! | |
13 | * | |
14 | * This class implements the output function to the Panda Boards. It | |
15 | * will be moved into GLucose once stabilized. | |
16 | */ | |
29674806 | 17 | public static class PandaDriver { |
e73ef85d | 18 | |
79ae8245 MS |
19 | // IP address |
20 | public final String ip; | |
21 | ||
e73ef85d MS |
22 | // Address to send to |
23 | private final NetAddress address; | |
24 | ||
79ae8245 MS |
25 | // Whether board output is enabled |
26 | private boolean enabled = false; | |
27 | ||
e73ef85d MS |
28 | // OSC message |
29 | private final OscMessage message; | |
30 | ||
a922e963 | 31 | // List of point indices that get sent to this board |
e4d0d812 | 32 | private final int[] points; |
44b8de9c | 33 | |
e73ef85d | 34 | // Packet data |
a922e963 | 35 | private final byte[] packet = new byte[4*352]; // magic number, our UDP packet size |
e73ef85d | 36 | |
84086fa3 MS |
37 | private static final int NO_POINT = -1; |
38 | ||
44b8de9c | 39 | public PandaDriver(String ip) { |
79ae8245 | 40 | this.ip = ip; |
a922e963 MS |
41 | |
42 | // Initialize our OSC output stuff | |
43 | address = new NetAddress(ip, 9001); | |
e73ef85d | 44 | message = new OscMessage("/shady/pointbuffer"); |
a922e963 MS |
45 | |
46 | // Build the array of points, initialize all to nothing | |
44b8de9c MS |
47 | points = new int[PandaMapping.PIXELS_PER_BOARD]; |
48 | for (int i = 0; i < points.length; ++i) { | |
a922e963 | 49 | points[i] = NO_POINT; |
44b8de9c MS |
50 | } |
51 | } | |
52 | ||
29674806 MS |
53 | private final static int FORWARD = -1; |
54 | private final static int BACKWARD = -2; | |
55 | ||
a922e963 MS |
56 | /** |
57 | * These constant arrays indicate the order in which the strips of a cube | |
58 | * are wired. There are four different options, depending on which bottom | |
59 | * corner of the cube the data wire comes in. | |
60 | */ | |
29674806 | 61 | private final static int[][] CUBE_STRIP_ORDERINGS = new int[][] { |
a922e963 MS |
62 | { 2, 1, 0, 3, 13, 12, 15, 14, 4, 7, 6, 5, 11, 10, 9, 8 }, // FRONT_LEFT |
63 | { 6, 5, 4, 7, 1, 0, 3, 2, 8, 11, 10, 9, 15, 14, 13, 12 }, // FRONT_RIGHT | |
64 | { 14, 13, 12, 15, 9, 8, 11, 10, 0, 3, 2, 1, 7, 6, 5, 4 }, // REAR_LEFT | |
65 | { 10, 9, 8, 11, 5, 4, 7, 6, 12, 15, 14, 13, 3, 2, 1, 0 }, // REAR_RIGHT | |
66 | }; | |
29674806 MS |
67 | |
68 | private final static int[][] BASS_STRIP_ORDERING = { | |
69 | {0, FORWARD }, | |
70 | {1, FORWARD }, | |
71 | {2, FORWARD }, | |
72 | {3, FORWARD }, | |
73 | {4, FORWARD }, | |
74 | {5, FORWARD }, | |
75 | {6, FORWARD }, | |
76 | {7, FORWARD }, | |
77 | {8, FORWARD }, | |
78 | {9, FORWARD }, | |
79 | {10, FORWARD }, | |
80 | {11, FORWARD }, | |
81 | {12, FORWARD }, | |
82 | {13, FORWARD }, | |
83 | {14, FORWARD }, | |
84 | {15, FORWARD }, | |
85 | {16, FORWARD }, | |
86 | {17, FORWARD }, | |
87 | {18, FORWARD }, | |
88 | {19, FORWARD }, | |
89 | {20, FORWARD }, | |
90 | {21, FORWARD }, | |
91 | {22, FORWARD }, | |
92 | }; | |
93 | ||
94 | private final static int[][] FLOOR_STRIP_ORDERING = { | |
95 | {0, FORWARD}, | |
96 | {1, FORWARD}, | |
97 | {2, FORWARD}, | |
98 | {3, FORWARD}, | |
99 | }; | |
100 | ||
101 | private final static int[][] SPEAKER_STRIP_ORDERING = { | |
102 | {0, FORWARD }, | |
103 | {1, FORWARD }, | |
104 | {2, FORWARD }, | |
105 | {3, FORWARD }, | |
106 | {4, FORWARD }, | |
107 | {5, FORWARD }, | |
108 | {6, FORWARD }, | |
109 | {7, FORWARD }, | |
110 | {8, FORWARD }, | |
111 | {9, FORWARD }, | |
112 | {10, FORWARD }, | |
113 | {11, FORWARD }, | |
114 | {12, FORWARD }, | |
115 | {13, FORWARD }, | |
116 | {14, FORWARD }, | |
117 | {15, FORWARD }, | |
118 | }; | |
119 | ||
44b8de9c MS |
120 | public PandaDriver(String ip, Model model, PandaMapping pm) { |
121 | this(ip); | |
e73ef85d | 122 | |
a922e963 MS |
123 | // Ok, we are initialized, time to build the array if points in order to |
124 | // send out. We start at the head of our point buffer, and work our way | |
125 | // down. This is the order in which points will be sent down the wire. | |
126 | int ci = -1; | |
127 | ||
128 | // Iterate through all our channels | |
84086fa3 | 129 | for (ChannelMapping channel : pm.channelList) { |
a922e963 MS |
130 | ++ci; |
131 | int pi = ci * ChannelMapping.PIXELS_PER_CHANNEL; | |
132 | ||
84086fa3 | 133 | switch (channel.mode) { |
a922e963 | 134 | |
84086fa3 | 135 | case ChannelMapping.MODE_CUBES: |
a922e963 | 136 | // We have a list of cubes per channel |
84086fa3 MS |
137 | for (int rawCubeIndex : channel.objectIndices) { |
138 | if (rawCubeIndex < 0) { | |
a922e963 MS |
139 | // No cube here, skip ahead in the buffer |
140 | pi += Cube.POINTS_PER_CUBE; | |
84086fa3 | 141 | } else { |
a922e963 MS |
142 | // The cube exists, check which way it is wired to |
143 | // figure out the order of strips. | |
84086fa3 MS |
144 | Cube cube = model.getCubeByRawIndex(rawCubeIndex); |
145 | int stripOrderIndex = 0; | |
146 | switch (cube.wiring) { | |
147 | case FRONT_LEFT: stripOrderIndex = 0; break; | |
148 | case FRONT_RIGHT: stripOrderIndex = 1; break; | |
149 | case REAR_LEFT: stripOrderIndex = 2; break; | |
150 | case REAR_RIGHT: stripOrderIndex = 3; break; | |
151 | } | |
a922e963 MS |
152 | |
153 | // Iterate through all the strips on the cube and add the points | |
154 | for (int stripIndex : CUBE_STRIP_ORDERINGS[stripOrderIndex]) { | |
155 | // We go backwards here... in the model strips go clockwise, but | |
156 | // the physical wires are run counter-clockwise | |
29674806 | 157 | pi = mapStrip(cube.strips.get(stripIndex), BACKWARD, points, pi); |
84086fa3 MS |
158 | } |
159 | } | |
e73ef85d | 160 | } |
84086fa3 MS |
161 | break; |
162 | ||
163 | case ChannelMapping.MODE_BASS: | |
29674806 MS |
164 | for (int[] config : BASS_STRIP_ORDERING) { |
165 | pi = mapStrip(model.bassBox.strips.get(config[0]), config[1], points, pi); | |
166 | } | |
84086fa3 MS |
167 | break; |
168 | ||
29674806 MS |
169 | case ChannelMapping.MODE_FLOOR: |
170 | for (int[] config : FLOOR_STRIP_ORDERING) { | |
171 | pi = mapStrip(model.boothFloor.strips.get(config[0]), config[1], points, pi); | |
172 | } | |
84086fa3 MS |
173 | break; |
174 | ||
175 | case ChannelMapping.MODE_SPEAKER: | |
29674806 MS |
176 | for (int[] config : SPEAKER_STRIP_ORDERING) { |
177 | Speaker speaker = model.speakers.get(channel.objectIndices[0]); | |
178 | pi = mapStrip(speaker.strips.get(config[0]), config[1], points, pi); | |
179 | } | |
84086fa3 MS |
180 | break; |
181 | ||
182 | case ChannelMapping.MODE_NULL: | |
a922e963 | 183 | // No problem, nothing on this channel! |
84086fa3 MS |
184 | break; |
185 | ||
186 | default: | |
187 | throw new RuntimeException("Invalid/unhandled channel mapping mode: " + channel.mode); | |
e73ef85d | 188 | } |
a922e963 | 189 | |
e73ef85d | 190 | } |
e73ef85d | 191 | } |
29674806 MS |
192 | |
193 | private int mapStrip(Strip s, int direction, int[] points, int pi) { | |
194 | if (direction == FORWARD) { | |
195 | for (Point p : s.points) { | |
196 | points[pi++] = p.index; | |
197 | } | |
198 | } else if (direction == BACKWARD) { | |
199 | for (int i = s.points.size()-1; i >= 0; --i) { | |
200 | points[pi++] = s.points.get(i).index; | |
201 | } | |
202 | } else { | |
203 | throw new RuntimeException("Unidentified strip mapping direction: " + direction); | |
204 | } | |
205 | return pi; | |
206 | } | |
e73ef85d | 207 | |
a922e963 MS |
208 | public void toggle() { |
209 | enabled = !enabled; | |
210 | println("PandaBoard/" + ip + ": " + (enabled ? "ON" : "OFF")); | |
211 | } | |
212 | ||
e73ef85d | 213 | public final void send(int[] colors) { |
79ae8245 MS |
214 | if (!enabled) { |
215 | return; | |
216 | } | |
e73ef85d MS |
217 | int len = 0; |
218 | int packetNum = 0; | |
219 | for (int index : points) { | |
84086fa3 | 220 | int c = (index < 0) ? 0 : colors[index]; |
e73ef85d MS |
221 | byte r = (byte) ((c >> 16) & 0xFF); |
222 | byte g = (byte) ((c >> 8) & 0xFF); | |
223 | byte b = (byte) ((c) & 0xFF); | |
a922e963 | 224 | packet[len++] = 0; // alpha channel, unused but makes for 4-byte alignment |
e73ef85d MS |
225 | packet[len++] = r; |
226 | packet[len++] = g; | |
227 | packet[len++] = b; | |
228 | ||
229 | // Flush once packet is full buffer size | |
230 | if (len >= packet.length) { | |
f584b5eb | 231 | sendPacket(packetNum++); |
e73ef85d MS |
232 | len = 0; |
233 | } | |
234 | } | |
235 | ||
236 | // Flush any remaining data | |
237 | if (len > 0) { | |
f584b5eb | 238 | sendPacket(packetNum++); |
e73ef85d MS |
239 | } |
240 | } | |
241 | ||
f584b5eb | 242 | private void sendPacket(int packetNum) { |
e73ef85d MS |
243 | message.clearArguments(); |
244 | message.add(packetNum); | |
f584b5eb | 245 | message.add(packet.length); |
e73ef85d MS |
246 | message.add(packet); |
247 | try { | |
bfff6bc2 | 248 | OscP5.flush(message, address); |
e73ef85d MS |
249 | } catch (Exception x) { |
250 | x.printStackTrace(); | |
251 | } | |
252 | } | |
253 | } | |
254 |