Advertisement
Guest User

Untitled

a guest
Jan 6th, 2016
212
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.70 KB | None | 0 0
  1. // "Adalight" is a do-it-yourself facsimile of the Philips Ambilight concept
  2. // for desktop computers and home theater PCs. This is the host PC-side code
  3. // written in Processing, intended for use with a USB-connected Arduino
  4. // microcontroller running the accompanying LED streaming code. Requires one
  5. // or more strands of Digital RGB LED Pixels (Adafruit product ID #322,
  6. // specifically the newer WS2801-based type, strand of 25) and a 5 Volt power
  7. // supply (such as Adafruit #276). You may need to adapt the code and the
  8. // hardware arrangement for your specific display configuration.
  9. // Screen capture adapted from code by Cedrik Kiefer (processing.org forum)
  10.  
  11. // --------------------------------------------------------------------
  12. // This file is part of Adalight.
  13.  
  14. // Adalight is free software: you can redistribute it and/or modify
  15. // it under the terms of the GNU Lesser General Public License as
  16. // published by the Free Software Foundation, either version 3 of
  17. // the License, or (at your option) any later version.
  18.  
  19. // Adalight is distributed in the hope that it will be useful,
  20. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. // GNU Lesser General Public License for more details.
  23.  
  24. // You should have received a copy of the GNU Lesser General Public
  25. // License along with Adalight. If not, see
  26. // <http://www.gnu.org/licenses/>.
  27. // --------------------------------------------------------------------
  28.  
  29. import java.awt.*;
  30. import java.awt.image.*;
  31. import processing.serial.*;
  32.  
  33. // CONFIGURABLE PROGRAM CONSTANTS --------------------------------------------
  34.  
  35. // Minimum LED brightness; some users prefer a small amount of backlighting
  36. // at all times, regardless of screen content. Higher values are brighter,
  37. // or set to 0 to disable this feature.
  38.  
  39. static final short minBrightness = 120;
  40.  
  41. // LED transition speed; it's sometimes distracting if LEDs instantaneously
  42. // track screen contents (such as during bright flashing sequences), so this
  43. // feature enables a gradual fade to each new LED state. Higher numbers yield
  44. // slower transitions (max of 255), or set to 0 to disable this feature
  45. // (immediate transition of all LEDs).
  46.  
  47. static final short fade = 75;
  48.  
  49. // Pixel size for the live preview image.
  50.  
  51. static final int pixelSize = 20;
  52.  
  53. // Depending on many factors, it may be faster either to capture full
  54. // screens and process only the pixels needed, or to capture multiple
  55. // smaller sub-blocks bounding each region to be processed. Try both,
  56. // look at the reported frame rates in the Processing output console,
  57. // and run with whichever works best for you.
  58.  
  59. static final boolean useFullScreenCaps = true;
  60.  
  61. // Serial device timeout (in milliseconds), for locating Arduino device
  62. // running the corresponding LEDstream code. See notes later in the code...
  63. // in some situations you may want to entirely comment out that block.
  64.  
  65. static final int timeout = 5000; // 5 seconds
  66.  
  67.  
  68.  
  69. static final int displays[][] = new int[][] {
  70. {0, 17, 10} // Screen 0, 9 LEDs across, 6 LEDs down
  71. //,{1,9,6} // Screen 1, also 9 LEDs across and 6 LEDs down
  72. };
  73.  
  74. // PER-LED INFORMATION -------------------------------------------------------
  75.  
  76. // This array contains the 2D coordinates corresponding to each pixel in the
  77. // LED strand, in the order that they're connected (i.e. the first element
  78. // here belongs to the first LED in the strand, second element is the second
  79. // LED, and so forth). Each triplet in this array consists of a display
  80. // number (an index into the display array above, NOT necessarily the same as
  81. // the system screen number) and an X and Y coordinate specified in the grid
  82. // units given for that display. {0,0,0} is the top-left corner of the first
  83. // display in the array.
  84. // For our example purposes, the coordinate list below forms a ring around
  85. // the perimeter of a single screen, with a one pixel gap at the bottom to
  86. // accommodate a monitor stand. Modify this to match your own setup:
  87.  
  88. static final int leds[][] = new int[][] {
  89.  
  90. {0, 8, 9}, {0, 7, 9}, {0, 6, 9}, {0, 5, 9}, {0, 4, 9}, {0, 3, 9}, {0, 2, 9}, {0, 1, 9}, {0, 0, 9}, // bottom left
  91.  
  92. {0, 0, 8}, {0, 0, 7}, {0, 0, 6}, {0, 0, 5}, {0, 0, 4}, {0, 0, 3}, {0, 0, 2}, {0, 0, 1}, {0, 0, 0}, // left
  93.  
  94. {0, 1, 0}, {0, 2, 0}, {0, 3, 0}, {0, 4, 0}, {0, 5, 0}, {0, 6, 0}, {0, 7, 0}, {0, 8, 0}, {0, 9, 0},
  95. {0, 10, 0}, {0, 11, 0}, {0, 12, 0}, {0, 13, 0}, {0, 14, 0}, {0, 15, 0}, // top
  96.  
  97. {0, 16, 0}, {0, 16, 1}, {0, 16, 2}, {0, 16, 3}, {0, 16, 4}, {0, 16, 5}, {0, 16, 6}, {0, 16, 7},
  98. {0, 16, 8}, // right
  99.  
  100. {0, 16, 9}, {0, 15, 9}, {0, 14, 9}, {0, 13, 9}, {0, 12, 9}, {0, 11, 9}, {0, 10, 9}, {0, 9, 9} // bottom right
  101.  
  102. // {0,3,5}, {0,2,5}, {0,1,5}, {0,0,5}, // Bottom edge, left half
  103. // {0,0,4}, {0,0,3}, {0,0,2}, {0,0,1}, // Left edge
  104. // {0,0,0}, {0,1,0}, {0,2,0}, {0,3,0}, {0,4,0}, // Top edge
  105. // {0,5,0}, {0,6,0}, {0,7,0}, {0,8,0}, // More top edge
  106. // {0,8,1}, {0,8,2}, {0,8,3}, {0,8,4}, // Right edge
  107. // {0,8,5}, {0,7,5}, {0,6,5}, {0,5,5} // Bottom edge, right half
  108.  
  109.  
  110. };
  111.  
  112. // GLOBAL VARIABLES ---- You probably won't need to modify any of this -------
  113.  
  114. byte[] serialData = new byte[6 + leds.length * 3];
  115. short[][] ledColor = new short[leds.length][3],
  116. prevColor = new short[leds.length][3];
  117. byte[][] gamma = new byte[256][3];
  118. int nDisplays = displays.length;
  119. Robot[] bot = new Robot[displays.length];
  120. Rectangle[] dispBounds = new Rectangle[displays.length],
  121. ledBounds; // Alloc'd only if per-LED captures
  122. int[][] pixelOffset = new int[leds.length][256],
  123. screenData; // Alloc'd only if full-screen captures
  124. PImage[] preview = new PImage[displays.length];
  125. Serial port;
  126. DisposeHandler dh; // For disabling LEDs on exit
  127.  
  128. // INITIALIZATION ------------------------------------------------------------
  129.  
  130. void setup() {
  131. GraphicsEnvironment ge;
  132. GraphicsConfiguration[] gc;
  133. GraphicsDevice[] gd;
  134. int d, i, totalWidth, maxHeight, row, col, rowOffset;
  135. int[] x = new int[16], y = new int[16];
  136. float f, range, step, start;
  137.  
  138. dh = new DisposeHandler(this); // Init DisposeHandler ASAP
  139.  
  140. // Open serial port. As written here, this assumes the Arduino is the
  141. // first/only serial device on the system. If that's not the case,
  142. // change "Serial.list()[0]" to the name of the port to be used:
  143. port = new Serial(this, Serial.list()[1], 115200);
  144. // Alternately, in certain situations the following line can be used
  145. // to detect the Arduino automatically. But this works ONLY with SOME
  146. // Arduino boards and versions of Processing! This is so convoluted
  147. // to explain, it's easier just to test it yourself and see whether
  148. // it works...if not, leave it commented out and use the prior port-
  149. // opening technique.
  150. // port = openPort();
  151. // And finally, to test the software alone without an Arduino connected,
  152. // don't open a port...just comment out the serial lines above.
  153.  
  154. // Initialize screen capture code for each display's dimensions.
  155. dispBounds = new Rectangle[displays.length];
  156. if (useFullScreenCaps == true) {
  157. screenData = new int[displays.length][];
  158. // ledBounds[] not used
  159. } else {
  160. ledBounds = new Rectangle[leds.length];
  161. // screenData[][] not used
  162. }
  163. ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
  164. gd = ge.getScreenDevices();
  165. if (nDisplays > gd.length) nDisplays = gd.length;
  166. totalWidth = maxHeight = 0;
  167. for (d=0; d<nDisplays; d++) { // For each display...
  168. try {
  169. bot[d] = new Robot(gd[displays[d][0]]);
  170. }
  171. catch(AWTException e) {
  172. System.out.println("new Robot() failed");
  173. continue;
  174. }
  175. gc = gd[displays[d][0]].getConfigurations();
  176. dispBounds[d] = gc[0].getBounds();
  177. dispBounds[d].x = dispBounds[d].y = 0;
  178. preview[d] = createImage(displays[d][1], displays[d][2], RGB);
  179. preview[d].loadPixels();
  180. totalWidth += displays[d][1];
  181. if (d > 0) totalWidth++;
  182. if (displays[d][2] > maxHeight) maxHeight = displays[d][2];
  183. }
  184.  
  185. // Precompute locations of every pixel to read when downsampling.
  186. // Saves a bunch of math on each frame, at the expense of a chunk
  187. // of RAM. Number of samples is now fixed at 256; this allows for
  188. // some crazy optimizations in the downsampling code.
  189. for (i=0; i<leds.length; i++) { // For each LED...
  190. d = leds[i][0]; // Corresponding display index
  191.  
  192. // Precompute columns, rows of each sampled point for this LED
  193. range = (float)dispBounds[d].width / (float)displays[d][1];
  194. step = range / 16.0;
  195. start = range * (float)leds[i][1] + step * 0.5;
  196. for (col=0; col<16; col++) x[col] = (int)(start + step * (float)col);
  197. range = (float)dispBounds[d].height / (float)displays[d][2];
  198. step = range / 16.0;
  199. start = range * (float)leds[i][2] + step * 0.5;
  200. for (row=0; row<16; row++) y[row] = (int)(start + step * (float)row);
  201.  
  202. if (useFullScreenCaps == true) {
  203. // Get offset to each pixel within full screen capture
  204. for (row=0; row<16; row++) {
  205. for (col=0; col<16; col++) {
  206. pixelOffset[i][row * 16 + col] =
  207. y[row] * dispBounds[d].width + x[col];
  208. }
  209. }
  210. } else {
  211. // Calc min bounding rect for LED, get offset to each pixel within
  212. ledBounds[i] = new Rectangle(x[0], y[0], x[15]-x[0]+1, y[15]-y[0]+1);
  213. for (row=0; row<16; row++) {
  214. for (col=0; col<16; col++) {
  215. pixelOffset[i][row * 16 + col] =
  216. (y[row] - y[0]) * ledBounds[i].width + x[col] - x[0];
  217. }
  218. }
  219. }
  220. }
  221.  
  222. for (i=0; i<prevColor.length; i++) {
  223. prevColor[i][0] = prevColor[i][1] = prevColor[i][2] =
  224. minBrightness / 3;
  225. }
  226.  
  227. // Preview window shows all screens side-by-side
  228. // size(totalWidth * pixelSize, maxHeight * pixelSize, JAVA2D);
  229. size(340,200, JAVA2D);
  230. noSmooth();
  231.  
  232. // A special header / magic word is expected by the corresponding LED
  233. // streaming code running on the Arduino. This only needs to be initialized
  234. // once (not in draw() loop) because the number of LEDs remains constant:
  235. serialData[0] = 'A'; // Magic word
  236. serialData[1] = 'd';
  237. serialData[2] = 'a';
  238. serialData[3] = (byte)((leds.length - 1) >> 8); // LED count high byte
  239. serialData[4] = (byte)((leds.length - 1) & 0xff); // LED count low byte
  240. serialData[5] = (byte)(serialData[3] ^ serialData[4] ^ 0x55); // Checksum
  241.  
  242. // Pre-compute gamma correction table for LED brightness levels:
  243. for (i=0; i<256; i++) {
  244. f = pow((float)i / 255.0, 2.8);
  245. gamma[i][0] = (byte)(f * 255.0);
  246. gamma[i][1] = (byte)(f * 240.0);
  247. gamma[i][2] = (byte)(f * 220.0);
  248. }
  249. }
  250.  
  251. // Open and return serial connection to Arduino running LEDstream code. This
  252. // attempts to open and read from each serial device on the system, until the
  253. // matching "Ada\n" acknowledgement string is found. Due to the serial
  254. // timeout, if you have multiple serial devices/ports and the Arduino is late
  255. // in the list, this can take seemingly forever...so if you KNOW the Arduino
  256. // will always be on a specific port (e.g. "COM6"), you might want to comment
  257. // out most of this to bypass the checks and instead just open that port
  258. // directly! (Modify last line in this method with the serial port name.)
  259.  
  260. Serial openPort() {
  261. String[] ports;
  262. String ack;
  263. int i, start;
  264. Serial s;
  265.  
  266. ports = Serial.list(); // List of all serial ports/devices on system.
  267.  
  268. for (i=0; i<ports.length; i++) { // For each serial port...
  269. System.out.format("Trying serial port %s\n", ports[i]);
  270. try {
  271. s = new Serial(this, ports[i], 115200);
  272. }
  273. catch(Exception e) {
  274. // Can't open port, probably in use by other software.
  275. continue;
  276. }
  277. // Port open...watch for acknowledgement string...
  278. start = millis();
  279. while ((millis() - start) < timeout) {
  280. if ((s.available() >= 4) &&
  281. ((ack = s.readString()) != null) &&
  282. ack.contains("Ada\n")) {
  283. return s; // Got it!
  284. }
  285. }
  286. // Connection timed out. Close port and move on to the next.
  287. s.stop();
  288. }
  289.  
  290. // Didn't locate a device returning the acknowledgment string.
  291. // Maybe it's out there but running the old LEDstream code, which
  292. // didn't have the ACK. Can't say for sure, so we'll take our
  293. // changes with the first/only serial device out there...
  294. return new Serial(this, ports[0], 115200);
  295. }
  296.  
  297.  
  298. // PER_FRAME PROCESSING ------------------------------------------------------
  299.  
  300. void draw () {
  301. BufferedImage img;
  302. int d, i, j, o, c, weight, rb, g, sum, deficit, s2;
  303. int[] pxls, offs;
  304.  
  305. if (useFullScreenCaps == true ) {
  306. // Capture each screen in the displays array.
  307. for (d=0; d<nDisplays; d++) {
  308. img = bot[d].createScreenCapture(dispBounds[d]);
  309. // Get location of source pixel data
  310. screenData[d] =
  311. ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
  312. }
  313. }
  314.  
  315. weight = 257 - fade; // 'Weighting factor' for new frame vs. old
  316. j = 6; // Serial led data follows header / magic word
  317.  
  318. // This computes a single pixel value filtered down from a rectangular
  319. // section of the screen. While it would seem tempting to use the native
  320. // image scaling in Processing/Java, in practice this didn't look very
  321. // good -- either too pixelated or too blurry, no happy medium. So
  322. // instead, a "manual" downsampling is done here. In the interest of
  323. // speed, it doesn't actually sample every pixel within a block, just
  324. // a selection of 256 pixels spaced within the block...the results still
  325. // look reasonably smooth and are handled quickly enough for video.
  326.  
  327. for (i=0; i<leds.length; i++) { // For each LED...
  328. d = leds[i][0]; // Corresponding display index
  329. if (useFullScreenCaps == true) {
  330. // Get location of source data from prior full-screen capture:
  331. pxls = screenData[d];
  332. } else {
  333. // Capture section of screen (LED bounds rect) and locate data::
  334. img = bot[d].createScreenCapture(ledBounds[i]);
  335. pxls = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
  336. }
  337. offs = pixelOffset[i];
  338. rb = g = 0;
  339. for (o=0; o<256; o++) {
  340. c = pxls[offs[o]];
  341. rb += c & 0x00ff00ff; // Bit trickery: R+B can accumulate in one var
  342. g += c & 0x0000ff00;
  343. }
  344.  
  345. // Blend new pixel value with the value from the prior frame
  346. ledColor[i][0] = (short)((((rb >> 24) & 0xff) * weight +
  347. prevColor[i][0] * fade) >> 8);
  348. ledColor[i][1] = (short)(((( g >> 16) & 0xff) * weight +
  349. prevColor[i][1] * fade) >> 8);
  350. ledColor[i][2] = (short)((((rb >> 8) & 0xff) * weight +
  351. prevColor[i][2] * fade) >> 8);
  352.  
  353. // Boost pixels that fall below the minimum brightness
  354. sum = ledColor[i][0] + ledColor[i][1] + ledColor[i][2];
  355. if (sum < minBrightness) {
  356. if (sum == 0) { // To avoid divide-by-zero
  357. deficit = minBrightness / 3; // Spread equally to R,G,B
  358. ledColor[i][0] += deficit;
  359. ledColor[i][1] += deficit;
  360. ledColor[i][2] += deficit;
  361. } else {
  362. deficit = minBrightness - sum;
  363. s2 = sum * 2;
  364.  
  365. ledColor[i][0] += deficit * (sum - ledColor[i][0]) / s2;
  366. ledColor[i][1] += deficit * (sum - ledColor[i][1]) / s2;
  367. ledColor[i][2] += deficit * (sum - ledColor[i][2]) / s2;
  368. }
  369. }
  370.  
  371. // Apply gamma curve and place in serial output buffer
  372. serialData[j++] = gamma[ledColor[i][0]][0];
  373. serialData[j++] = gamma[ledColor[i][1]][1];
  374. serialData[j++] = gamma[ledColor[i][2]][2];
  375. // Update pixels in preview image
  376. preview[d].pixels[leds[i][2] * displays[d][1] + leds[i][1]] =
  377. (ledColor[i][0] << 16) | (ledColor[i][1] << 8) | ledColor[i][2];
  378. }
  379.  
  380. if (port != null) port.write(serialData); // Issue data to Arduino
  381.  
  382. // Show live preview image(s)
  383. scale(pixelSize);
  384. for (i=d=0; d<nDisplays; d++) {
  385. preview[d].updatePixels();
  386. image(preview[d], i, 0);
  387. i += displays[d][1] + 1;
  388. }
  389.  
  390. println(frameRate); // How are we doing?
  391.  
  392. // Copy LED color data to prior frame array for next pass
  393. arraycopy(ledColor, 0, prevColor, 0, ledColor.length);
  394. }
  395.  
  396.  
  397.  
  398.  
  399. public class DisposeHandler {
  400. DisposeHandler(PApplet pa) {
  401. // pa.registerDispose(this);
  402. }
  403. public void dispose() {
  404. // Fill serialData (after header) with 0's, and issue to Arduino...
  405. // Arrays.fill(serialData, 6, serialData.length, (byte)0);
  406. java.util.Arrays.fill(serialData, 6, serialData.length, (byte)0);
  407. if (port != null) port.write(serialData);
  408. }
  409. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement