From f83e5e2452dacd0ce7d3900e7f32021e2cf98543 Mon Sep 17 00:00:00 2001 From: Micah Elizabeth Scott <micah@scanlime.org> Date: Mon, 10 Feb 2014 00:15:27 -0800 Subject: [PATCH] Tasty FFT spiral demo for the 32x16 panel Music not included this time, sadly. --- .../grid32x16z_particle_fft/OPC.pde | 349 ++++++++++++++++++ .../grid32x16z_particle_fft/data/colors.png | Bin 0 -> 1974 bytes .../grid32x16z_particle_fft/data/dot.png | Bin 0 -> 2501 bytes .../grid32x16z_particle_fft.pde | 75 ++++ 4 files changed, 424 insertions(+) create mode 100644 examples/processing/grid32x16z_particle_fft/OPC.pde create mode 100644 examples/processing/grid32x16z_particle_fft/data/colors.png create mode 100644 examples/processing/grid32x16z_particle_fft/data/dot.png create mode 100644 examples/processing/grid32x16z_particle_fft/grid32x16z_particle_fft.pde diff --git a/examples/processing/grid32x16z_particle_fft/OPC.pde b/examples/processing/grid32x16z_particle_fft/OPC.pde new file mode 100644 index 0000000..917035d --- /dev/null +++ b/examples/processing/grid32x16z_particle_fft/OPC.pde @@ -0,0 +1,349 @@ +/* + * Simple Open Pixel Control client for Processing, + * designed to sample each LED's color from some point on the canvas. + * + * Micah Elizabeth Scott, 2013 + * This file is released into the public domain. + */ + +import java.net.*; +import java.util.Arrays; + +public class OPC +{ + Socket socket; + OutputStream output; + String host; + int port; + + int[] pixelLocations; + byte[] packetData; + byte firmwareConfig; + String colorCorrection; + boolean enableShowLocations; + + OPC(PApplet parent, String host, int port) + { + this.host = host; + this.port = port; + this.enableShowLocations = true; + parent.registerDraw(this); + } + + // Set the location of a single LED + void led(int index, int x, int y) + { + // For convenience, automatically grow the pixelLocations array. We do want this to be an array, + // instead of a HashMap, to keep draw() as fast as it can be. + if (pixelLocations == null) { + pixelLocations = new int[index + 1]; + } else if (index >= pixelLocations.length) { + pixelLocations = Arrays.copyOf(pixelLocations, index + 1); + } + + pixelLocations[index] = x + width * y; + } + + // Set the location of several LEDs arranged in a strip. + // Angle is in radians, measured clockwise from +X. + // (x,y) is the center of the strip. + void ledStrip(int index, int count, float x, float y, float spacing, float angle, boolean reversed) + { + float s = sin(angle); + float c = cos(angle); + for (int i = 0; i < count; i++) { + led(reversed ? (index + count - 1 - i) : (index + i), + (int)(x + (i - (count-1)/2.0) * spacing * c + 0.5), + (int)(y + (i - (count-1)/2.0) * spacing * s + 0.5)); + } + } + + // Set the location of several LEDs arranged in a grid. The first strip is + // at 'angle', measured in radians clockwise from +X. + // (x,y) is the center of the grid. + void ledGrid(int index, int stripLength, int numStrips, float x, float y, + float ledSpacing, float stripSpacing, float angle, boolean zigzag) + { + float s = sin(angle + HALF_PI); + float c = cos(angle + HALF_PI); + for (int i = 0; i < numStrips; i++) { + ledStrip(index + stripLength * i, stripLength, + x + (i - (numStrips-1)/2.0) * stripSpacing * c, + y + (i - (numStrips-1)/2.0) * stripSpacing * s, ledSpacing, + angle, zigzag && (i % 2) == 1); + } + } + + // Set the location of 64 LEDs arranged in a uniform 8x8 grid. + // (x,y) is the center of the grid. + void ledGrid8x8(int index, float x, float y, float spacing, float angle, boolean zigzag) + { + ledGrid(index, 8, 8, x, y, spacing, spacing, angle, zigzag); + } + + // Should the pixel sampling locations be visible? This helps with debugging. + // Showing locations is enabled by default. You might need to disable it if our drawing + // is interfering with your processing sketch, or if you'd simply like the screen to be + // less cluttered. + void showLocations(boolean enabled) + { + enableShowLocations = enabled; + } + + // Enable or disable dithering. Dithering avoids the "stair-stepping" artifact and increases color + // resolution by quickly jittering between adjacent 8-bit brightness levels about 400 times a second. + // Dithering is on by default. + void setDithering(boolean enabled) + { + if (enabled) + firmwareConfig &= ~0x01; + else + firmwareConfig |= 0x01; + sendFirmwareConfigPacket(); + } + + // Enable or disable frame interpolation. Interpolation automatically blends between consecutive frames + // in hardware, and it does so with 16-bit per channel resolution. Combined with dithering, this helps make + // fades very smooth. Interpolation is on by default. + void setInterpolation(boolean enabled) + { + if (enabled) + firmwareConfig &= ~0x02; + else + firmwareConfig |= 0x02; + sendFirmwareConfigPacket(); + } + + // Put the Fadecandy onboard LED under automatic control. It blinks any time the firmware processes a packet. + // This is the default configuration for the LED. + void statusLedAuto() + { + firmwareConfig &= 0x0C; + sendFirmwareConfigPacket(); + } + + // Manually turn the Fadecandy onboard LED on or off. This disables automatic LED control. + void setStatusLed(boolean on) + { + firmwareConfig |= 0x04; // Manual LED control + if (on) + firmwareConfig |= 0x08; + else + firmwareConfig &= ~0x08; + sendFirmwareConfigPacket(); + } + + // Set the color correction parameters + void setColorCorrection(float gamma, float red, float green, float blue) + { + colorCorrection = "{ \"gamma\": " + gamma + ", \"whitepoint\": [" + red + "," + green + "," + blue + "]}"; + sendColorCorrectionPacket(); + } + + // Set custom color correction parameters from a string + void setColorCorrection(String s) + { + colorCorrection = s; + sendColorCorrectionPacket(); + } + + // Send a packet with the current firmware configuration settings + void sendFirmwareConfigPacket() + { + if (output == null) { + // We'll do this when we reconnect + return; + } + + byte[] packet = new byte[9]; + packet[0] = 0; // Channel (reserved) + packet[1] = (byte)0xFF; // Command (System Exclusive) + packet[2] = 0; // Length high byte + packet[3] = 5; // Length low byte + packet[4] = 0x00; // System ID high byte + packet[5] = 0x01; // System ID low byte + packet[6] = 0x00; // Command ID high byte + packet[7] = 0x02; // Command ID low byte + packet[8] = firmwareConfig; + + try { + output.write(packet); + } catch (Exception e) { + dispose(); + } + } + + // Send a packet with the current color correction settings + void sendColorCorrectionPacket() + { + if (colorCorrection == null) { + // No color correction defined + return; + } + if (output == null) { + // We'll do this when we reconnect + return; + } + + byte[] content = colorCorrection.getBytes(); + int packetLen = content.length + 4; + byte[] header = new byte[8]; + header[0] = 0; // Channel (reserved) + header[1] = (byte)0xFF; // Command (System Exclusive) + header[2] = (byte)(packetLen >> 8); + header[3] = (byte)(packetLen & 0xFF); + header[4] = 0x00; // System ID high byte + header[5] = 0x01; // System ID low byte + header[6] = 0x00; // Command ID high byte + header[7] = 0x01; // Command ID low byte + + try { + output.write(header); + output.write(content); + } catch (Exception e) { + dispose(); + } + } + + // Automatically called at the end of each draw(). + // This handles the automatic Pixel to LED mapping. + // If you aren't using that mapping, this function has no effect. + // In that case, you can call setPixelCount(), setPixel(), and writePixels() + // separately. + void draw() + { + if (pixelLocations == null) { + // No pixels defined yet + return; + } + + if (output == null) { + // Try to (re)connect + connect(); + } + if (output == null) { + return; + } + + int numPixels = pixelLocations.length; + int ledAddress = 4; + + setPixelCount(numPixels); + loadPixels(); + + for (int i = 0; i < numPixels; i++) { + int pixelLocation = pixelLocations[i]; + int pixel = pixels[pixelLocation]; + + packetData[ledAddress] = (byte)(pixel >> 16); + packetData[ledAddress + 1] = (byte)(pixel >> 8); + packetData[ledAddress + 2] = (byte)pixel; + ledAddress += 3; + + if (enableShowLocations) { + pixels[pixelLocation] = 0xFFFFFF ^ pixel; + } + } + + writePixels(); + + if (enableShowLocations) { + updatePixels(); + } + } + + // Change the number of pixels in our output packet. + // This is normally not needed; the output packet is automatically sized + // by draw() and by setPixel(). + void setPixelCount(int numPixels) + { + int numBytes = 3 * numPixels; + int packetLen = 4 + numBytes; + if (packetData == null || packetData.length != packetLen) { + // Set up our packet buffer + packetData = new byte[packetLen]; + packetData[0] = 0; // Channel + packetData[1] = 0; // Command (Set pixel colors) + packetData[2] = (byte)(numBytes >> 8); + packetData[3] = (byte)(numBytes & 0xFF); + } + } + + // Directly manipulate a pixel in the output buffer. This isn't needed + // for pixels that are mapped to the screen. + void setPixel(int number, color c) + { + int offset = 4 + number * 3; + if (packetData == null || packetData.length < offset + 3) { + setPixelCount(number + 1); + } + + packetData[offset] = (byte) (c >> 16); + packetData[offset + 1] = (byte) (c >> 8); + packetData[offset + 2] = (byte) c; + } + + // Read a pixel from the output buffer. If the pixel was mapped to the display, + // this returns the value we captured on the previous frame. + color getPixel(int number) + { + int offset = 4 + number * 3; + if (packetData == null || packetData.length < offset + 3) { + return 0; + } + return (packetData[offset] << 16) | (packetData[offset + 1] << 8) | packetData[offset + 2]; + } + + // Transmit our current buffer of pixel values to the OPC server. This is handled + // automatically in draw() if any pixels are mapped to the screen, but if you haven't + // mapped any pixels to the screen you'll want to call this directly. + void writePixels() + { + if (packetData == null || packetData.length == 0) { + // No pixel buffer + return; + } + if (output == null) { + // Try to (re)connect + connect(); + } + if (output == null) { + return; + } + + try { + output.write(packetData); + } catch (Exception e) { + dispose(); + } + } + + void dispose() + { + // Destroy the socket. Called internally when we've disconnected. + if (output != null) { + println("Disconnected from OPC server"); + } + socket = null; + output = null; + } + + void connect() + { + // Try to connect to the OPC server. This normally happens automatically in draw() + try { + socket = new Socket(host, port); + socket.setTcpNoDelay(true); + output = socket.getOutputStream(); + println("Connected to OPC server"); + } catch (ConnectException e) { + dispose(); + } catch (IOException e) { + dispose(); + } + + sendColorCorrectionPacket(); + sendFirmwareConfigPacket(); + } +} + diff --git a/examples/processing/grid32x16z_particle_fft/data/colors.png b/examples/processing/grid32x16z_particle_fft/data/colors.png new file mode 100644 index 0000000000000000000000000000000000000000..b9d7c42e1050c400036adebc3c83146b18809523 GIT binary patch literal 1974 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 literal 0 HcmV?d00001 diff --git a/examples/processing/grid32x16z_particle_fft/data/dot.png b/examples/processing/grid32x16z_particle_fft/data/dot.png new file mode 100644 index 0000000000000000000000000000000000000000..908720a8a24f56f8c0add1c05dfe1ddbad25d6d6 GIT binary patch literal 2501 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 P00000NkvXXu0mjfn%0?+ literal 0 HcmV?d00001 diff --git a/examples/processing/grid32x16z_particle_fft/grid32x16z_particle_fft.pde b/examples/processing/grid32x16z_particle_fft/grid32x16z_particle_fft.pde new file mode 100644 index 0000000..fdbceae --- /dev/null +++ b/examples/processing/grid32x16z_particle_fft/grid32x16z_particle_fft.pde @@ -0,0 +1,75 @@ +// Some real-time FFT! This visualizes music in the frequency domain using a +// polar-coordinate particle system. Particle size and radial distance are modulated +// using a filtered FFT. Color is sampled from an image. + +import ddf.minim.analysis.*; +import ddf.minim.*; + +OPC opc; +PImage dot; +PImage colors; +Minim minim; +AudioPlayer sound; +FFT fft; +float[] fftFilter; + +String filename = "/Users/micah/Dropbox/music/Mark Farina - Mushroom Jazz Vol 5.mp3"; + +float spin = 0.001; +float radiansPerBucket = radians(2); +float decay = 0.97; +float opacity = 50; +float minSize = 0.1; +float sizeScale = 0.6; + +void setup() +{ + size(600, 300, P3D); + + minim = new Minim(this); + + // Small buffer size! + sound = minim.loadFile(filename, 512); + sound.loop(); + fft = new FFT(sound.bufferSize(), sound.sampleRate()); + fftFilter = new float[fft.specSize()]; + + dot = loadImage("dot.png"); + colors = loadImage("colors.png"); + + // Connect to the local instance of fcserver + opc = new OPC(this, "127.0.0.1", 7890); + + opc.ledGrid8x8(0 * 64, width * 1/8, height * 1/4, height/16, 0, true); + opc.ledGrid8x8(1 * 64, width * 3/8, height * 1/4, height/16, 0, true); + opc.ledGrid8x8(2 * 64, width * 5/8, height * 1/4, height/16, 0, true); + opc.ledGrid8x8(3 * 64, width * 7/8, height * 1/4, height/16, 0, true); + opc.ledGrid8x8(4 * 64, width * 1/8, height * 3/4, height/16, 0, true); + opc.ledGrid8x8(5 * 64, width * 3/8, height * 3/4, height/16, 0, true); + opc.ledGrid8x8(6 * 64, width * 5/8, height * 3/4, height/16, 0, true); + opc.ledGrid8x8(7 * 64, width * 7/8, height * 3/4, height/16, 0, true); +} + +void draw() +{ + background(0); + + fft.forward(sound.mix); + for (int i = 0; i < fftFilter.length; i++) { + fftFilter[i] = max(fftFilter[i] * decay, log(1 + fft.getBand(i))); + } + + for (int i = 0; i < fftFilter.length; i += 3) { + color rgb = colors.get(int(map(i, 0, fftFilter.length-1, 0, colors.width-1)), colors.height/2); + tint(rgb, fftFilter[i] * opacity); + blendMode(ADD); + + float size = height * (minSize + sizeScale * fftFilter[i]); + PVector center = new PVector(width * (fftFilter[i] * 0.2), 0); + center.rotate(millis() * spin + i * radiansPerBucket); + center.add(new PVector(width * 0.5, height * 0.5)); + + image(dot, center.x - size/2, center.y - size/2, size, size); + } +} + -- GitLab