Commit | Line | Data |
---|---|---|
e73ef85d MS |
1 | import netP5.*; |
2 | import oscP5.*; | |
3 | ||
e28f168c AG |
4 | //import hypermedia.net.*; |
5 | ||
6 | ||
7 | ||
8 | ||
e73ef85d MS |
9 | /** |
10 | * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND | |
11 | * | |
12 | * //\\ //\\ //\\ //\\ | |
13 | * ///\\\ ///\\\ ///\\\ ///\\\ | |
14 | * \\\/// \\\/// \\\/// \\\/// | |
15 | * \\// \\// \\// \\// | |
16 | * | |
17 | * EXPERTS ONLY!! EXPERTS ONLY!! | |
18 | * | |
19 | * This class implements the output function to the Panda Boards. It | |
20 | * will be moved into GLucose once stabilized. | |
21 | */ | |
e28f168c AG |
22 | public static class PandaDriver extends Thread{ |
23 | int lastSeen; | |
24 | void start(){super.start();} | |
25 | void run(){ | |
26 | while(true){ | |
27 | if(queue.size()>0) { | |
28 | for(int i=0; i<queue.size(); i++){ | |
29 | this.sendNow(queue.get(queue.size()-1)); | |
30 | queue.clear(); | |
31 | } | |
32 | } else { | |
33 | try{sleep(1);} catch(Exception e){} | |
34 | } | |
35 | } | |
36 | } | |
37 | ||
38 | //UDP udp; // define the UDP object | |
e73ef85d | 39 | |
79ae8245 MS |
40 | // IP address |
41 | public final String ip; | |
42 | ||
e73ef85d MS |
43 | // Address to send to |
44 | private final NetAddress address; | |
45 | ||
79ae8245 MS |
46 | // Whether board output is enabled |
47 | private boolean enabled = false; | |
48 | ||
e73ef85d MS |
49 | // OSC message |
50 | private final OscMessage message; | |
51 | ||
a922e963 | 52 | // List of point indices that get sent to this board |
e4d0d812 | 53 | private final int[] points; |
44b8de9c | 54 | |
e73ef85d | 55 | // Packet data |
a922e963 | 56 | private final byte[] packet = new byte[4*352]; // magic number, our UDP packet size |
e73ef85d | 57 | |
84086fa3 MS |
58 | private static final int NO_POINT = -1; |
59 | ||
44b8de9c | 60 | public PandaDriver(String ip) { |
79ae8245 | 61 | this.ip = ip; |
a922e963 | 62 | |
e28f168c AG |
63 | //udp = new UDP(this, 10000); |
64 | ||
a922e963 MS |
65 | // Initialize our OSC output stuff |
66 | address = new NetAddress(ip, 9001); | |
e73ef85d | 67 | message = new OscMessage("/shady/pointbuffer"); |
e28f168c | 68 | |
a922e963 | 69 | // Build the array of points, initialize all to nothing |
44b8de9c MS |
70 | points = new int[PandaMapping.PIXELS_PER_BOARD]; |
71 | for (int i = 0; i < points.length; ++i) { | |
a922e963 | 72 | points[i] = NO_POINT; |
44b8de9c | 73 | } |
e28f168c | 74 | this.start(); |
44b8de9c MS |
75 | } |
76 | ||
29674806 MS |
77 | private final static int FORWARD = -1; |
78 | private final static int BACKWARD = -2; | |
79 | ||
a3ccf23a MS |
80 | //////////////////////////////////////////////////////////////// |
81 | // | |
82 | // READ THIS RIGHT NOW BEFORE YOU MODIFY THE BELOW!!!!!!!!!!!!! | |
83 | // READ THIS RIGHT NOW BEFORE YOU MODIFY THE BELOW!!!!!!!!!!!!! | |
84 | // READ THIS RIGHT NOW BEFORE YOU MODIFY THE BELOW!!!!!!!!!!!!! | |
85 | // | |
86 | // The mappings below indicate the physical order of strips | |
87 | // connected to a pandaboard channel. The strip numbers are a | |
88 | // reflection of how the model is built. | |
89 | // | |
90 | // For ANYTHING in the model which is a rectangular prism, | |
91 | // which means Cubes, the BassBox, and each Speaker, the | |
92 | // strips are numbered incrementally by face. The first | |
93 | // face is always the FRONT, which you are looking at. | |
94 | // The next face is the RIGHT, then the BACK, then the LEFT. | |
95 | // | |
96 | // For every face, the strips are ordered numerically moving | |
97 | // clockwise from the the TOP LEFT. | |
98 | // | |
99 | // So, for a cube: | |
100 | // | |
101 | // Strip 0: front face, top strip, left to right | |
102 | // Strip 1: front face, right strip, top to bottom | |
103 | // Strip 2: front face, bottom strip, right to left | |
104 | // Strip 3: front face, left strip, bottom to top | |
105 | // | |
106 | // Strip 4: right face, top strip, left to right | |
107 | // ... and so on | |
108 | // Strip 14: left face, bottom strip, right to left | |
109 | // Strip 15: left face, left strip, bottom to top | |
110 | // | |
111 | //////////////////////////////////////////////////////////////// | |
112 | ||
113 | ||
a922e963 MS |
114 | /** |
115 | * These constant arrays indicate the order in which the strips of a cube | |
116 | * are wired. There are four different options, depending on which bottom | |
117 | * corner of the cube the data wire comes in. | |
118 | */ | |
29674806 | 119 | private final static int[][] CUBE_STRIP_ORDERINGS = new int[][] { |
a922e963 MS |
120 | { 2, 1, 0, 3, 13, 12, 15, 14, 4, 7, 6, 5, 11, 10, 9, 8 }, // FRONT_LEFT |
121 | { 6, 5, 4, 7, 1, 0, 3, 2, 8, 11, 10, 9, 15, 14, 13, 12 }, // FRONT_RIGHT | |
122 | { 14, 13, 12, 15, 9, 8, 11, 10, 0, 3, 2, 1, 7, 6, 5, 4 }, // REAR_LEFT | |
123 | { 10, 9, 8, 11, 5, 4, 7, 6, 12, 15, 14, 13, 3, 2, 1, 0 }, // REAR_RIGHT | |
124 | }; | |
29674806 MS |
125 | |
126 | private final static int[][] BASS_STRIP_ORDERING = { | |
1d75c8a9 | 127 | // front face, counterclockwise from bottom front left |
a3ccf23a MS |
128 | {2, BACKWARD /* if this strip has extra pixels, you can add them here */ /*, 4 */ }, |
129 | {1, BACKWARD /* if this strip is short some pixels, substract them here */ /*, -3 */ }, | |
1d75c8a9 MS |
130 | {0, BACKWARD }, |
131 | {3, BACKWARD }, | |
132 | ||
133 | // left face, counterclockwise from bottom front left | |
134 | {13, BACKWARD }, | |
135 | {12, BACKWARD }, | |
136 | {15, BACKWARD }, | |
137 | {14, BACKWARD }, | |
138 | ||
139 | // back face, counterclockwise from bottom rear left | |
140 | {9, BACKWARD }, | |
141 | {8, BACKWARD }, | |
142 | {11, BACKWARD }, | |
143 | {10, BACKWARD }, | |
144 | ||
145 | // right face, counterclockwise from bottom rear right | |
146 | {5, BACKWARD }, | |
147 | {4, BACKWARD }, | |
148 | {7, BACKWARD }, | |
149 | {6, BACKWARD }, | |
150 | }; | |
151 | ||
152 | private final static int[][] STRUT_STRIP_ORDERING = { | |
153 | {6, BACKWARD}, | |
154 | {5, FORWARD}, | |
155 | {4, BACKWARD}, | |
156 | {3, FORWARD}, | |
157 | {2, BACKWARD}, | |
158 | {1, FORWARD}, | |
a3ccf23a | 159 | {0, BACKWARD}, |
1d75c8a9 | 160 | {7, FORWARD}, |
29674806 MS |
161 | }; |
162 | ||
163 | private final static int[][] FLOOR_STRIP_ORDERING = { | |
164 | {0, FORWARD}, | |
165 | {1, FORWARD}, | |
166 | {2, FORWARD}, | |
1d75c8a9 | 167 | {3, BACKWARD}, |
29674806 MS |
168 | }; |
169 | ||
1d75c8a9 MS |
170 | // The speakers are currently configured to be wired the same |
171 | // as cubes with Wiring.FRONT_LEFT. If this needs to be changed, | |
172 | // remove this null assignment and change the below to have mappings | |
173 | // for the LEFT and RIGHT speaker | |
174 | private final static int[][][] SPEAKER_STRIP_ORDERING = null; /* { | |
175 | // Left speaker | |
176 | { | |
177 | // Front face, counter-clockwise from bottom left | |
178 | {2, BACKWARD }, | |
179 | {1, BACKWARD }, | |
180 | {0, BACKWARD }, | |
181 | {3, BACKWARD }, | |
182 | }, | |
183 | // Right speaker | |
184 | { | |
185 | // Front face, counter-clockwise from bottom left | |
186 | {2, BACKWARD }, | |
187 | {1, BACKWARD }, | |
188 | {0, BACKWARD }, | |
189 | {3, BACKWARD }, | |
190 | } | |
191 | };*/ | |
192 | ||
193 | private final static int[][] LEFT_SPEAKER_STRIP_ORDERING = { | |
29674806 | 194 | }; |
e28f168c AG |
195 | |
196 | ArrayList<int[]> queue; | |
29674806 | 197 | |
44b8de9c MS |
198 | public PandaDriver(String ip, Model model, PandaMapping pm) { |
199 | this(ip); | |
e28f168c AG |
200 | |
201 | queue = new ArrayList<int[]>(); | |
e73ef85d | 202 | |
a922e963 MS |
203 | // Ok, we are initialized, time to build the array if points in order to |
204 | // send out. We start at the head of our point buffer, and work our way | |
205 | // down. This is the order in which points will be sent down the wire. | |
206 | int ci = -1; | |
207 | ||
208 | // Iterate through all our channels | |
84086fa3 | 209 | for (ChannelMapping channel : pm.channelList) { |
a922e963 MS |
210 | ++ci; |
211 | int pi = ci * ChannelMapping.PIXELS_PER_CHANNEL; | |
212 | ||
84086fa3 | 213 | switch (channel.mode) { |
a922e963 | 214 | |
84086fa3 | 215 | case ChannelMapping.MODE_CUBES: |
a922e963 | 216 | // We have a list of cubes per channel |
84086fa3 MS |
217 | for (int rawCubeIndex : channel.objectIndices) { |
218 | if (rawCubeIndex < 0) { | |
a922e963 MS |
219 | // No cube here, skip ahead in the buffer |
220 | pi += Cube.POINTS_PER_CUBE; | |
84086fa3 | 221 | } else { |
a922e963 MS |
222 | // The cube exists, check which way it is wired to |
223 | // figure out the order of strips. | |
84086fa3 MS |
224 | Cube cube = model.getCubeByRawIndex(rawCubeIndex); |
225 | int stripOrderIndex = 0; | |
226 | switch (cube.wiring) { | |
227 | case FRONT_LEFT: stripOrderIndex = 0; break; | |
228 | case FRONT_RIGHT: stripOrderIndex = 1; break; | |
229 | case REAR_LEFT: stripOrderIndex = 2; break; | |
230 | case REAR_RIGHT: stripOrderIndex = 3; break; | |
231 | } | |
a922e963 MS |
232 | |
233 | // Iterate through all the strips on the cube and add the points | |
234 | for (int stripIndex : CUBE_STRIP_ORDERINGS[stripOrderIndex]) { | |
235 | // We go backwards here... in the model strips go clockwise, but | |
236 | // the physical wires are run counter-clockwise | |
29674806 | 237 | pi = mapStrip(cube.strips.get(stripIndex), BACKWARD, points, pi); |
84086fa3 MS |
238 | } |
239 | } | |
e73ef85d | 240 | } |
84086fa3 MS |
241 | break; |
242 | ||
243 | case ChannelMapping.MODE_BASS: | |
29674806 MS |
244 | for (int[] config : BASS_STRIP_ORDERING) { |
245 | pi = mapStrip(model.bassBox.strips.get(config[0]), config[1], points, pi); | |
1d75c8a9 | 246 | if (config.length >= 3) pi += config[2]; |
29674806 | 247 | } |
84086fa3 MS |
248 | break; |
249 | ||
1d75c8a9 MS |
250 | case ChannelMapping.MODE_STRUTS_AND_FLOOR: |
251 | for (int[] config : STRUT_STRIP_ORDERING) { | |
252 | pi = mapStrip(model.bassBox.struts.get(config[0]), config[1], points, pi); | |
253 | if (config.length >= 3) pi += config[2]; | |
254 | } | |
29674806 MS |
255 | for (int[] config : FLOOR_STRIP_ORDERING) { |
256 | pi = mapStrip(model.boothFloor.strips.get(config[0]), config[1], points, pi); | |
1d75c8a9 | 257 | if (config.length >= 3) pi += config[2]; |
29674806 | 258 | } |
84086fa3 MS |
259 | break; |
260 | ||
261 | case ChannelMapping.MODE_SPEAKER: | |
1d75c8a9 MS |
262 | int [][] speakerStripOrdering; |
263 | if (SPEAKER_STRIP_ORDERING == null) { | |
264 | // Copy the cube strip ordering | |
265 | int[] frontLeftCubeWiring = CUBE_STRIP_ORDERINGS[0]; | |
266 | speakerStripOrdering = new int[frontLeftCubeWiring.length][]; | |
267 | for (int i = 0; i < frontLeftCubeWiring.length; ++i) { | |
268 | speakerStripOrdering[i] = new int[] { frontLeftCubeWiring[0], BACKWARD }; | |
269 | } | |
270 | } else { | |
271 | speakerStripOrdering = SPEAKER_STRIP_ORDERING[channel.objectIndices[0]]; | |
272 | } | |
273 | for (int[] config : speakerStripOrdering) { | |
29674806 MS |
274 | Speaker speaker = model.speakers.get(channel.objectIndices[0]); |
275 | pi = mapStrip(speaker.strips.get(config[0]), config[1], points, pi); | |
1d75c8a9 | 276 | if (config.length >= 3) pi += config[2]; |
29674806 | 277 | } |
84086fa3 MS |
278 | break; |
279 | ||
280 | case ChannelMapping.MODE_NULL: | |
a922e963 | 281 | // No problem, nothing on this channel! |
84086fa3 MS |
282 | break; |
283 | ||
284 | default: | |
285 | throw new RuntimeException("Invalid/unhandled channel mapping mode: " + channel.mode); | |
e73ef85d | 286 | } |
a922e963 | 287 | |
e73ef85d | 288 | } |
e73ef85d | 289 | } |
29674806 MS |
290 | |
291 | private int mapStrip(Strip s, int direction, int[] points, int pi) { | |
292 | if (direction == FORWARD) { | |
293 | for (Point p : s.points) { | |
294 | points[pi++] = p.index; | |
295 | } | |
296 | } else if (direction == BACKWARD) { | |
297 | for (int i = s.points.size()-1; i >= 0; --i) { | |
298 | points[pi++] = s.points.get(i).index; | |
299 | } | |
300 | } else { | |
301 | throw new RuntimeException("Unidentified strip mapping direction: " + direction); | |
302 | } | |
303 | return pi; | |
304 | } | |
e73ef85d | 305 | |
1d75c8a9 MS |
306 | public void disable() { |
307 | if (enabled) { | |
308 | enabled = false; | |
309 | println("PandaBoard/" + ip + ": OFF"); | |
310 | } | |
311 | } | |
312 | ||
313 | public void enable() { | |
314 | if (!enabled) { | |
315 | enabled = true; | |
316 | println("PandaBoard/" + ip + ": ON"); | |
317 | } | |
318 | } | |
319 | ||
a922e963 MS |
320 | public void toggle() { |
321 | enabled = !enabled; | |
322 | println("PandaBoard/" + ip + ": " + (enabled ? "ON" : "OFF")); | |
323 | } | |
324 | ||
e73ef85d | 325 | public final void send(int[] colors) { |
e28f168c AG |
326 | queue.add(colors); |
327 | } | |
328 | public final void sendNow(int[] colors) { | |
329 | if (!enabled || colors==null) { | |
79ae8245 MS |
330 | return; |
331 | } | |
e73ef85d MS |
332 | int len = 0; |
333 | int packetNum = 0; | |
e28f168c AG |
334 | if(points==null) { return; } |
335 | for (int index: points) { | |
336 | int c = 0; | |
337 | if(index>0) { c= colors[index]; } | |
e73ef85d MS |
338 | byte r = (byte) ((c >> 16) & 0xFF); |
339 | byte g = (byte) ((c >> 8) & 0xFF); | |
340 | byte b = (byte) ((c) & 0xFF); | |
a922e963 | 341 | packet[len++] = 0; // alpha channel, unused but makes for 4-byte alignment |
e73ef85d MS |
342 | packet[len++] = r; |
343 | packet[len++] = g; | |
344 | packet[len++] = b; | |
345 | ||
346 | // Flush once packet is full buffer size | |
347 | if (len >= packet.length) { | |
f584b5eb | 348 | sendPacket(packetNum++); |
e73ef85d MS |
349 | len = 0; |
350 | } | |
351 | } | |
352 | ||
353 | // Flush any remaining data | |
354 | if (len > 0) { | |
f584b5eb | 355 | sendPacket(packetNum++); |
e73ef85d MS |
356 | } |
357 | } | |
358 | ||
e28f168c | 359 | |
f584b5eb | 360 | private void sendPacket(int packetNum) { |
e73ef85d MS |
361 | message.clearArguments(); |
362 | message.add(packetNum); | |
f584b5eb | 363 | message.add(packet.length); |
e73ef85d MS |
364 | message.add(packet); |
365 | try { | |
e28f168c | 366 | //udp.send(packet, "10.200.1.29", 9001); |
bfff6bc2 | 367 | OscP5.flush(message, address); |
e73ef85d MS |
368 | } catch (Exception x) { |
369 | x.printStackTrace(); | |
370 | } | |
371 | } | |
372 | } |