From 84086fa3d2cb987859754986bba3feb92716343d Mon Sep 17 00:00:00 2001 From: Mark Slee Date: Tue, 20 Aug 2013 17:46:41 -0700 Subject: [PATCH] Update mapping and debugging tools to support bassbin, speakers, and booth floor --- TestPatterns.pde | 26 +++++----- _Internals.pde | 2 - _Mappings.pde | 117 ++++++++++++++++++++++++++++++++------------- _Overlay.pde | 121 +++++++++++++++++++++++++++++++++++------------ _PandaDriver.pde | 77 +++++++++++++++++++++--------- 5 files changed, 243 insertions(+), 100 deletions(-) diff --git a/TestPatterns.pde b/TestPatterns.pde index 51402f2..bdaa78a 100644 --- a/TestPatterns.pde +++ b/TestPatterns.pde @@ -237,8 +237,8 @@ class MappingTool extends TestPattern { 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); @@ -248,17 +248,19 @@ class MappingTool extends TestPattern { } 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; } @@ -292,7 +294,7 @@ class MappingTool extends TestPattern { 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; @@ -301,7 +303,7 @@ class MappingTool extends TestPattern { 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; diff --git a/_Internals.pde b/_Internals.pde index fc06832..580e173 100644 --- a/_Internals.pde +++ b/_Internals.pde @@ -122,8 +122,6 @@ void setup() { 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) { diff --git a/_Mappings.pde b/_Mappings.pde index d7d0951..fb80ff1 100644 --- a/_Mappings.pde +++ b/_Mappings.pde @@ -230,29 +230,28 @@ public Model buildModel() { 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 }), }), - }; } @@ -261,27 +260,79 @@ class PandaMapping { // 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; + } + } +} diff --git a/_Overlay.pde b/_Overlay.pde index a02aa12..2ee00c8 100644 --- a/_Overlay.pde +++ b/_Overlay.pde @@ -748,7 +748,7 @@ class MappingUI extends OverlayUI { class DebugUI { - final int[][] channelList; + final ChannelMapping[] channelList; final int debugX = 10; final int debugY = 42; final int debugXSpacing = 28; @@ -761,10 +761,10 @@ class DebugUI { 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; } } @@ -784,26 +784,44 @@ class DebugUI { 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; @@ -851,20 +869,61 @@ class DebugUI { 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; } diff --git a/_PandaDriver.pde b/_PandaDriver.pde index ef86dbe..3f5f011 100644 --- a/_PandaDriver.pde +++ b/_PandaDriver.pde @@ -34,6 +34,8 @@ public class PandaDriver { // 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); @@ -70,31 +72,62 @@ public class PandaDriver { }; 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); } } } @@ -106,7 +139,7 @@ public class PandaDriver { 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); -- 2.34.1