private final int numChannels;
private final PandaMapping[] pandaMappings;
- private PandaMapping activeMapping;
- private int mappingChannelIndex;
+ private PandaMapping activePanda;
+ private ChannelMapping activeChannel;
MappingTool(GLucose glucose, PandaMapping[] pandaMappings) {
super(glucose);
}
private void setChannel() {
- mappingChannelIndex = channelIndex % PandaMapping.CHANNELS_PER_BOARD;
- activeMapping = pandaMappings[channelIndex / PandaMapping.CHANNELS_PER_BOARD];
+ activePanda = pandaMappings[channelIndex / PandaMapping.CHANNELS_PER_BOARD];
+ activeChannel = activePanda.channelList[channelIndex % PandaMapping.CHANNELS_PER_BOARD];
}
- private int cubeInChannel(Cube c) {
- int i = 1;
- for (int index : activeMapping.channelList[mappingChannelIndex]) {
- if (c == model.getCubeByRawIndex(index)) {
- return i;
+ private int indexOfCubeInChannel(Cube c) {
+ if (activeChannel.mode == ChannelMapping.MODE_CUBES) {
+ int i = 1;
+ for (int index : activeChannel.objectIndices) {
+ if (c == model.getCubeByRawIndex(index)) {
+ return i;
+ }
+ ++i;
}
- ++i;
}
return 0;
}
int ci = 0;
for (Cube cube : model.cubes) {
boolean cubeOn = false;
- int channelIndex = cubeInChannel(cube);
+ int indexOfCubeInChannel = indexOfCubeInChannel(cube);
switch (mappingMode) {
case MAPPING_MODE_ALL: cubeOn = true; break;
case MAPPING_MODE_SINGLE_CUBE: cubeOn = (cubeIndex == ci); break;
if (cubeOn) {
if (mappingMode == MAPPING_MODE_CHANNEL) {
color cc = off;
- switch (channelIndex) {
+ switch (indexOfCubeInChannel) {
case 1: cc = r; break;
case 2: cc = r|g; break;
case 3: cc = g; break;
println("Total setup: " + (millis() - startMillis) + "ms");
println("Hit the 'p' key to toggle Panda Board output");
-
- println(glucose.model.bassBox.points.size());
}
void controllerChangeReceived(rwmidi.Controller cc) {
public PandaMapping[] buildPandaList() {
return new PandaMapping[] {
new PandaMapping(
- "10.200.1.28", new int[][] {
- { 1, 2, 3, 4 }, // ch1
- { 5, 6, 7, 8 }, // ch2
- { 9, 10, 11, 12 }, // ch3
- { 13, 14, 15, 16 }, // ch4
- { 17, 18, 19, 20 }, // ch5
- { 21, 22, 23, 24 }, // ch6
- { 25, 26, 27, 28 }, // ch7
- { 29, 30, 31, 32 }, // ch8
+ "10.200.1.28", new ChannelMapping[] {
+ new ChannelMapping(ChannelMapping.MODE_BASS),
+ new ChannelMapping(ChannelMapping.MODE_FLOOR),
+ new ChannelMapping(ChannelMapping.MODE_SPEAKER, 0),
+ new ChannelMapping(ChannelMapping.MODE_SPEAKER, 1),
+ new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 1, 2, 3, 4 }),
+ new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 5, 6, 7, 8 }),
+ new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 9, 10, 11, 12 }),
+ new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 13, 14, 15, 16 }),
}),
new PandaMapping(
- "10.200.1.29", new int[][] {
- { 33, 34, 35, 36 }, // ch9
- { 37, 38, 39, 40 }, // ch10
- { 41, 42, 43, 44 }, // ch11
- { 45, 46, 47, 48 }, // ch12
- { 33, 34, 35, 36 }, // ch13
- { 37, 38, 39, 40 }, // ch14
- { 41, 42, 43, 44 }, // ch15
- { 45, 46, 47, 48 }, // ch16
+ "10.200.1.29", new ChannelMapping[] {
+ new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 17, 18, 19, 20 }),
+ new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 21, 22, 23, 24 }),
+ new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 25, 26, 27, 28 }),
+ new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 29, 30, 31, 32 }),
+ new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 33, 34, 35, 36 }),
+ new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 37, 38, 39, 40 }),
+ new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 41, 42, 43, 44 }),
+ new ChannelMapping(ChannelMapping.MODE_CUBES, new int[] { 45, 46, 47, 48 }),
}),
-
};
}
// How many channels are on the panda board
public final static int CHANNELS_PER_BOARD = 8;
- // How many cubes per channel xc_PB is configured for
- public final static int CUBES_PER_CHANNEL = 4;
-
- // How many total pixels on each channel
- public final static int PIXELS_PER_CHANNEL = Cube.POINTS_PER_CUBE * CUBES_PER_CHANNEL;
-
// How many total pixels on the whole board
- public final static int PIXELS_PER_BOARD = PIXELS_PER_CHANNEL * CHANNELS_PER_BOARD;
+ public final static int PIXELS_PER_BOARD = ChannelMapping.PIXELS_PER_CHANNEL * CHANNELS_PER_BOARD;
final String ip;
- final int[][] channelList = new int[CHANNELS_PER_BOARD][CUBES_PER_CHANNEL];
+ final ChannelMapping[] channelList = new ChannelMapping[CHANNELS_PER_BOARD];
- PandaMapping(String ip, int[][] rawChannelList) {
+ PandaMapping(String ip, ChannelMapping[] rawChannelList) {
this.ip = ip;
- for (int chi = 0; chi < CHANNELS_PER_BOARD; ++chi) {
- int[] cubes = (chi < rawChannelList.length) ? rawChannelList[chi] : new int[]{};
- for (int cui = 0; cui < CUBES_PER_CHANNEL; ++cui) {
- channelList[chi][cui] = (cui < cubes.length) ? cubes[cui] : 0;
- }
+ for (int i = 0; i < channelList.length; ++i) {
+ channelList[i] = (i < rawChannelList.length) ? rawChannelList[i] : new ChannelMapping();
}
}
}
+class ChannelMapping {
+
+ // How many cubes per channel xc_PB is configured for
+ public final static int CUBES_PER_CHANNEL = 4;
+ // How many total pixels on each channel
+ public final static int PIXELS_PER_CHANNEL = Cube.POINTS_PER_CUBE * CUBES_PER_CHANNEL;
+
+ public static final int MODE_NULL = 0;
+ public static final int MODE_CUBES = 1;
+ public static final int MODE_BASS = 2;
+ public static final int MODE_SPEAKER = 3;
+ public static final int MODE_FLOOR = 4;
+ public static final int MODE_INVALID = 5;
+
+ public static final int NO_OBJECT = -1;
+
+ final int mode;
+ final int[] objectIndices = new int[CUBES_PER_CHANNEL];
+
+ ChannelMapping() {
+ this(MODE_NULL);
+ }
+
+ ChannelMapping(int mode) {
+ this(mode, new int[]{});
+ }
+
+ ChannelMapping(int mode, int rawObjectIndex) {
+ this(mode, new int[]{ rawObjectIndex });
+ }
+
+ ChannelMapping(int mode, int[] rawObjectIndices) {
+ if (mode < 0 || mode >= MODE_INVALID) {
+ throw new RuntimeException("Invalid channel mapping mode: " + mode);
+ }
+ if (mode == MODE_SPEAKER) {
+ if (rawObjectIndices.length != 1) {
+ throw new RuntimeException("Speaker channel mapping mode must specify one speaker index");
+ }
+ int speakerIndex = rawObjectIndices[0];
+ if (speakerIndex < 0 || speakerIndex >= glucose.model.speakers.size()) {
+ throw new RuntimeException("Invalid speaker channel mapping: " + speakerIndex);
+ }
+ } else if ((mode == MODE_FLOOR) || (mode == MODE_BASS) || (mode == MODE_NULL)) {
+ if (rawObjectIndices.length > 0) {
+ throw new RuntimeException("Bass/floor/null mappings cannot specify object indices");
+ }
+ } else if (mode == MODE_CUBES) {
+ for (int rawCubeIndex : rawObjectIndices) {
+ if (glucose.model.getCubeByRawIndex(rawCubeIndex) == null) {
+ throw new RuntimeException("Non-existing cube specified in cube mapping: " + rawCubeIndex);
+ }
+ }
+ }
+
+ this.mode = mode;
+ for (int i = 0; i < objectIndices.length; ++i) {
+ objectIndices[i] = (i < rawObjectIndices.length) ? rawObjectIndices[i] : NO_OBJECT;
+ }
+ }
+}
class DebugUI {
- final int[][] channelList;
+ final ChannelMapping[] channelList;
final int debugX = 10;
final int debugY = 42;
final int debugXSpacing = 28;
DebugUI(PandaMapping[] pandaMappings) {
int totalChannels = pandaMappings.length * PandaMapping.CHANNELS_PER_BOARD;
- channelList = new int[totalChannels][];
+ channelList = new ChannelMapping[totalChannels];
int channelIndex = 0;
for (PandaMapping pm : pandaMappings) {
- for (int[] channel : pm.channelList) {
+ for (ChannelMapping channel : pm.channelList) {
channelList[channelIndex++] = channel;
}
}
rect(4, 32, 172, 388);
int channelNum = 0;
- for (int[] channel : channelList) {
+ for (ChannelMapping channel : channelList) {
int xPos = xBase;
drawNumBox(xPos, yPos, channelNum+1, debugState[channelNum][0]);
+ xPos += debugXSpacing;
- boolean first = true;
- int cubeNum = 0;
- for (int cube : channel) {
- if (cube <= 0) {
+ switch (channel.mode) {
+ case ChannelMapping.MODE_CUBES:
+ int stateIndex = 0;
+ boolean first = true;
+ for (int rawCubeIndex : channel.objectIndices) {
+ if (rawCubeIndex < 0) {
+ break;
+ }
+ if (first) {
+ first = false;
+ } else {
+ stroke(#999999);
+ line(xPos - 12, yPos + 8, xPos, yPos + 8);
+ }
+ drawNumBox(xPos, yPos, rawCubeIndex, debugState[channelNum][stateIndex+1]);
+ ++stateIndex;
+ xPos += debugXSpacing;
+ }
break;
- }
- xPos += debugXSpacing;
- if (first) {
- first = false;
- } else {
- stroke(#999999);
- line(xPos - 12, yPos + 8, xPos, yPos + 8);
- }
- drawNumBox(xPos, yPos, cube, debugState[channelNum][cubeNum+1]);
- ++cubeNum;
- }
+ case ChannelMapping.MODE_BASS:
+ drawNumBox(xPos, yPos, "B", debugState[channelNum][1]);
+ break;
+ case ChannelMapping.MODE_SPEAKER:
+ drawNumBox(xPos, yPos, "S" + channel.objectIndices[0], debugState[channelNum][1]);
+ break;
+ case ChannelMapping.MODE_FLOOR:
+ drawNumBox(xPos, yPos, "F", debugState[channelNum][1]);
+ break;
+ case ChannelMapping.MODE_NULL:
+ break;
+ default:
+ throw new RuntimeException("Unhandled channel mapping mode: " + channel.mode);
+ }
yPos += debugYSpacing;
++channelNum;
color white = #FFFFFF;
color off = #000000;
int channelIndex = 0;
- for (int[] channel : channelList) {
- int cubeIndex = 1;
- for (int rawCubeIndex : channel) {
- if (rawCubeIndex > 0) {
- int state = debugState[channelIndex][cubeIndex];
- if (state != DEBUG_STATE_ANIM) {
- color debugColor = (state == DEBUG_STATE_WHITE) ? white : off;
- Cube cube = glucose.model.getCubeByRawIndex(rawCubeIndex);
- for (Point p : cube.points) {
- colors[p.index] = debugColor;
+ int state;
+ for (ChannelMapping channel : channelList) {
+ switch (channel.mode) {
+ case ChannelMapping.MODE_CUBES:
+ int cubeIndex = 1;
+ for (int rawCubeIndex : channel.objectIndices) {
+ if (rawCubeIndex >= 0) {
+ state = debugState[channelIndex][cubeIndex];
+ if (state != DEBUG_STATE_ANIM) {
+ color debugColor = (state == DEBUG_STATE_WHITE) ? white : off;
+ Cube cube = glucose.model.getCubeByRawIndex(rawCubeIndex);
+ for (Point p : cube.points) {
+ colors[p.index] = debugColor;
+ }
+ }
}
+ ++cubeIndex;
}
- }
- ++cubeIndex;
+ break;
+
+ case ChannelMapping.MODE_BASS:
+ state = debugState[channelIndex][1];
+ if (state != DEBUG_STATE_ANIM) {
+ color debugColor = (state == DEBUG_STATE_WHITE) ? white : off;
+ for (Point p : glucose.model.bassBox.points) {
+ colors[p.index] = debugColor;
+ }
+ }
+ break;
+
+ case ChannelMapping.MODE_FLOOR:
+ state = debugState[channelIndex][1];
+ if (state != DEBUG_STATE_ANIM) {
+ color debugColor = (state == DEBUG_STATE_WHITE) ? white : off;
+ for (Point p : glucose.model.boothFloor.points) {
+ colors[p.index] = debugColor;
+ }
+ }
+ break;
+
+ case ChannelMapping.MODE_SPEAKER:
+ state = debugState[channelIndex][1];
+ if (state != DEBUG_STATE_ANIM) {
+ color debugColor = (state == DEBUG_STATE_WHITE) ? white : off;
+ for (Point p : glucose.model.speakers.get(channel.objectIndices[0]).points) {
+ colors[p.index] = debugColor;
+ }
+ }
+ break;
+
+ case ChannelMapping.MODE_NULL:
+ break;
+
+ default:
+ throw new RuntimeException("Unhandled channel mapping mode: " + channel.mode);
}
++channelIndex;
}
// Packet data
private final byte[] packet = new byte[4*352]; // TODO: de-magic-number, UDP related?
+ private static final int NO_POINT = -1;
+
public PandaDriver(String ip) {
this.ip = ip;
this.address = new NetAddress(ip, 9001);
};
int pi = 0;
- for (int[] channel : pm.channelList) {
- for (int cubeNumber : channel) {
- if (cubeNumber <= 0) {
- for (int i = 0; i < Cube.POINTS_PER_CUBE; ++i) {
- points[pi++] = 0;
+ for (ChannelMapping channel : pm.channelList) {
+ switch (channel.mode) {
+ case ChannelMapping.MODE_CUBES:
+ for (int rawCubeIndex : channel.objectIndices) {
+ if (rawCubeIndex < 0) {
+ for (int i = 0; i < Cube.POINTS_PER_CUBE; ++i) {
+ points[pi++] = NO_POINT;
+ }
+ } else {
+ Cube cube = model.getCubeByRawIndex(rawCubeIndex);
+ int stripOrderIndex = 0;
+ switch (cube.wiring) {
+ case FRONT_LEFT: stripOrderIndex = 0; break;
+ case FRONT_RIGHT: stripOrderIndex = 1; break;
+ case REAR_LEFT: stripOrderIndex = 2; break;
+ case REAR_RIGHT: stripOrderIndex = 3; break;
+ }
+ for (int stripIndex : stripOrderings[stripOrderIndex]) {
+ Strip s = cube.strips.get(stripIndex);
+ for (int j = s.points.size() - 1; j >= 0; --j) {
+ points[pi++] = s.points.get(j).index;
+ }
+ }
+ }
}
- } else {
- Cube cube = model.getCubeByRawIndex(cubeNumber);
- if (cube == null) {
- throw new RuntimeException("Non-zero, non-existing cube specified in channel mapping (" + cubeNumber + ")");
+ break;
+
+ case ChannelMapping.MODE_BASS:
+ // TODO(mapping): figure out how these strips are wired
+ for (int i = 0; i < ChannelMapping.PIXELS_PER_CHANNEL; ++i) {
+ points[pi++] = NO_POINT;
}
- int stripOrderIndex = 0;
- switch (cube.wiring) {
- case FRONT_LEFT: stripOrderIndex = 0; break;
- case FRONT_RIGHT: stripOrderIndex = 1; break;
- case REAR_LEFT: stripOrderIndex = 2; break;
- case REAR_RIGHT: stripOrderIndex = 3; break;
+ break;
+
+ case ChannelMapping.MODE_FLOOR:
+ // TODO(mapping): figure out how these strips are wired
+ for (int i = 0; i < ChannelMapping.PIXELS_PER_CHANNEL; ++i) {
+ points[pi++] = NO_POINT;
}
- for (int stripIndex : stripOrderings[stripOrderIndex]) {
- Strip s = cube.strips.get(stripIndex);
- for (int j = s.points.size() - 1; j >= 0; --j) {
- points[pi++] = s.points.get(j).index;
- }
+ break;
+
+ case ChannelMapping.MODE_SPEAKER:
+ // TODO(mapping): figure out how these strips are wired
+ for (int i = 0; i < ChannelMapping.PIXELS_PER_CHANNEL; ++i) {
+ points[pi++] = NO_POINT;
+ }
+ break;
+
+ case ChannelMapping.MODE_NULL:
+ for (int i = 0; i < ChannelMapping.PIXELS_PER_CHANNEL; ++i) {
+ points[pi++] = NO_POINT;
}
- }
+ break;
+
+ default:
+ throw new RuntimeException("Invalid/unhandled channel mapping mode: " + channel.mode);
}
}
}
int len = 0;
int packetNum = 0;
for (int index : points) {
- int c = colors[index];
+ int c = (index < 0) ? 0 : colors[index];
byte r = (byte) ((c >> 16) & 0xFF);
byte g = (byte) ((c >> 8) & 0xFF);
byte b = (byte) ((c) & 0xFF);