/** * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND * * //\\ //\\ //\\ //\\ * ///\\\ ///\\\ ///\\\ ///\\\ * \\\/// \\\/// \\\/// \\\/// * \\// \\// \\// \\//H * * EXPERTS ONLY!! EXPERTS ONLY!! * * If you are an artist, you may ignore this file! It contains * the code to drive grizzly board outputs. */ GrizzlyOutput[] buildGrizzlies() throws SocketException, UnknownHostException { return new GrizzlyOutput[] { new GrizzlyOutput(lx, "192.168.88.100", 6, 5, 6, 7, 7, 8, 1, 2, 4, 3, 11, 10, 9, 9, 12, 13), new GrizzlyOutput(lx, "192.168.88.101", 25, 23, 24, 43, 45, 44, 1, 1, 1, 1, 1, 41, 42, 21, 20, 22), new GrizzlyOutput(lx, "192.168.88.104", 26, 28, 27, 19, 18, 17, 1, 1, 18, 19, 15, 16, 14, 29, 30, 31), new GrizzlyOutput(lx, "192.168.88.105", 1, 1, 1, 39, 38, 40, 34, 35, 33, 32, 37, 37, 1, 1, 1, 1), }; } /** * Grizzly Output, sends packets to one grizzly board with a fixed IP and a number * of channels. */ class GrizzlyOutput extends LXDatagramOutput { public final String ipAddress; private int frameNumber = 0; public GrizzlyOutput(LX lx, String ipAddress, int ... cubeIndices) throws UnknownHostException, SocketException { super(lx); this.ipAddress = ipAddress; int channelNum = 0; for (int rawCubeIndex : cubeIndices) { if (rawCubeIndex > 0) { Cube cube = model.getCubeByRawIndex(rawCubeIndex); addDatagram(new GrizzlyDatagram(this, channelNum, cube).setAddress(ipAddress)); } ++channelNum; } this.enabled.setValue(false); } protected void beforeSend(int[] colors) { ++frameNumber; } public int getFrameNumber() { return this.frameNumber; } } /** * Datagram to a Grizzlyboard. A simple fixed OSC packet. */ static class GrizzlyDatagram extends LXDatagram { private static byte[] oscString(String s) { int len = s.length(); int padding = (4 - ((len + 1) % 4)) % 4; byte[] bytes = new byte[len + 1 + padding]; System.arraycopy(s.getBytes(), 0, bytes, 0, len); for (int i = len; i < bytes.length; ++i) { bytes[i] = 0; } return bytes; } private static int oscIntCopy(int i, byte[] buffer, int pos) { buffer[pos] = (byte) ((i >> 24) & 0xff); buffer[pos + 1] = (byte) ((i >> 16) & 0xff); buffer[pos + 2] = (byte) ((i >> 8) & 0xff); buffer[pos + 3] = (byte) (i & 0xff); return 4; } private final static int[] STRIP_ORDERING = new int[] { 9, 8, 11, 5, 4, 7, 6, 10, 14, 2, 1, 0, 3, 13, 12, 15 }; private static int[] cubePointIndices(Cube cube) { int[] pointIndices = new int[Cube.POINTS_PER_CUBE - 2]; int pi = 0; for (int stripIndex : STRIP_ORDERING) { Strip strip = cube.strips.get(stripIndex); int stripLen = ((stripIndex == 9) || (stripIndex == 15)) ? 15 : 16; for (int i = stripLen-1; i >= 0; --i) { pointIndices[pi++] = strip.points.get(i).index; } } return pointIndices; } private final static byte[] OSC_ADDRESS = oscString("/shady/pointbuffer"); private final static byte[] OSC_TYPETAG = oscString(",iiiiibi"); private final static int HEADER_LENGTH = OSC_ADDRESS.length + OSC_TYPETAG.length + 24; private final static int FOOTER_LENGTH = 4; private final static int OSC_PORT = 779; private GrizzlyOutput output; private int[] pointIndices; private final int frameNumberPos; private final int dataPos; public GrizzlyDatagram(GrizzlyOutput output, int channelNum, Cube cube) { this(output, channelNum, cubePointIndices(cube)); } public GrizzlyDatagram(GrizzlyOutput output, int channelNum, int[] pointIndices) { super(HEADER_LENGTH + 4*pointIndices.length + FOOTER_LENGTH); setPort(OSC_PORT); this.output = output; this.pointIndices = pointIndices; int dataLength = 4*pointIndices.length; int pos = 0; // OSC address System.arraycopy(OSC_ADDRESS, 0, this.buffer, pos, OSC_ADDRESS.length); pos += OSC_ADDRESS.length; // OSC typetag System.arraycopy(OSC_TYPETAG, 0, this.buffer, pos, OSC_TYPETAG.length); pos += OSC_TYPETAG.length; this.frameNumberPos = pos; pos += oscIntCopy(0, this.buffer, pos); // placeholder for frame number pos += oscIntCopy(0xDEADBEEF, this.buffer, pos); pos += oscIntCopy(channelNum, this.buffer, pos); pos += oscIntCopy(0xFEEDBEEF, this.buffer, pos); pos += oscIntCopy(dataLength, this.buffer, pos); pos += oscIntCopy(dataLength, this.buffer, pos); this.dataPos = pos; // end header oscIntCopy(0xBEFFFFEB, this.buffer, this.buffer.length - 4); } void onSend(int[] colors) { oscIntCopy(this.output.getFrameNumber(), this.buffer, frameNumberPos); int dataIndex = this.dataPos; for (int index : this.pointIndices) { color c = (index >= 0) ? colors[index] : 0; this.buffer[dataIndex] = (byte) 0; // unused, alpha this.buffer[dataIndex + 1] = (byte) ((c >> 16) & 0xff); // r this.buffer[dataIndex + 2] = (byte) ((c >> 8) & 0xff); // g this.buffer[dataIndex + 3] = (byte) (c & 0xff); // b dataIndex += 4; } } }