Created
January 7, 2016 21:57
-
-
Save Toqaro/85331f5734584ffe32fc to your computer and use it in GitHub Desktop.
Example of a JavaFX Application controlling a RGB LED Strip through communicating with an Arduino Uno to create a cheap DIY ambient backlight (single-color).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.molaynoxx.ambilightcontrol; | |
import gnu.io.CommPortIdentifier; | |
import gnu.io.SerialPort; | |
import javafx.application.Application; | |
import javafx.application.Platform; | |
import javafx.beans.property.ObjectProperty; | |
import javafx.beans.property.SimpleObjectProperty; | |
import javafx.collections.FXCollections; | |
import javafx.geometry.Insets; | |
import javafx.scene.Scene; | |
import javafx.scene.control.*; | |
import javafx.scene.control.Button; | |
import javafx.scene.layout.HBox; | |
import javafx.scene.layout.VBox; | |
import javafx.scene.paint.Color; | |
import javafx.scene.paint.Paint; | |
import javafx.scene.shape.Rectangle; | |
import javafx.stage.Stage; | |
import javafx.util.Callback; | |
import org.bytedeco.javacv.*; | |
import java.awt.image.BufferedImage; | |
import java.io.OutputStream; | |
import java.util.ArrayList; | |
import java.util.Enumeration; | |
import static org.bytedeco.javacpp.avutil.*; | |
import static org.bytedeco.javacpp.avcodec.*; | |
public class AmbilightControl extends Application { | |
private SerialPort serialPort; | |
private OutputStream output; | |
private int color = 0; | |
private ObjectProperty<Paint> colorDisplayFill = new SimpleObjectProperty<>(); | |
private boolean calculateScreenAverage = false; | |
public static final FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("video=screen-capture-recorder"); | |
public static final Java2DFrameConverter converter = new Java2DFrameConverter(); | |
public static void main(String[] args) { | |
launch(args); | |
} | |
@Override | |
public void start(Stage primaryStage) throws Exception { | |
Enumeration portEnum = CommPortIdentifier.getPortIdentifiers(); | |
ArrayList<CommPortIdentifier> comPorts = new ArrayList<>(); | |
while (portEnum.hasMoreElements()) { | |
comPorts.add((CommPortIdentifier) portEnum.nextElement()); | |
} | |
primaryStage.setTitle("Ambilight Control"); | |
VBox hbSelectRoot = new VBox(); | |
Scene sceneSelectPort = new Scene(hbSelectRoot, 200, 40); | |
primaryStage.setResizable(false); | |
Callback<ListView<CommPortIdentifier>, ListCell<CommPortIdentifier>> cellFactory = new Callback<ListView<CommPortIdentifier>, ListCell<CommPortIdentifier>>() { | |
@Override | |
public ListCell<CommPortIdentifier> call(ListView<CommPortIdentifier> param) { | |
return new ListCell<CommPortIdentifier>() { | |
@Override | |
protected void updateItem(CommPortIdentifier item, boolean empty) { | |
super.updateItem(item, empty); | |
if(!empty) setText(item.getName()); | |
} | |
}; | |
} | |
}; | |
ComboBox<CommPortIdentifier> portSelector = new ComboBox<>(); | |
portSelector.setCellFactory(cellFactory); | |
portSelector.setButtonCell(cellFactory.call(null)); | |
portSelector.setItems(FXCollections.observableArrayList(comPorts)); | |
portSelector.getSelectionModel().select(0); | |
Button buttonSelect = new Button("Select"); | |
buttonSelect.setOnAction(action -> loadProgram(primaryStage, portSelector.getSelectionModel().getSelectedItem())); | |
buttonSelect.prefWidthProperty().bind(hbSelectRoot.widthProperty()); | |
portSelector.prefWidthProperty().bind(hbSelectRoot.widthProperty()); | |
hbSelectRoot.getChildren().addAll(portSelector, buttonSelect); | |
primaryStage.setScene(sceneSelectPort); | |
primaryStage.show(); | |
} | |
private void loadProgram(Stage primaryStage, CommPortIdentifier comPort) { | |
VBox rootPane = new VBox(); | |
Scene mainScene = new Scene(rootPane, 400, 200); | |
primaryStage.setResizable(false); | |
primaryStage.setScene(mainScene); | |
ToggleGroup group = new ToggleGroup(); | |
RadioButton rbUseColor = new RadioButton("Use selected color"); | |
rbUseColor.setToggleGroup(group); | |
rbUseColor.setSelected(true); | |
RadioButton rbUseScreenAverage = new RadioButton("Use average screen color"); | |
rbUseScreenAverage.setToggleGroup(group); | |
HBox hbRadioButtons = new HBox(); | |
VBox.setMargin(hbRadioButtons, new Insets(10, 10, 10, 10)); | |
hbRadioButtons.setSpacing(20); | |
hbRadioButtons.getChildren().addAll(rbUseColor, rbUseScreenAverage); | |
ColorPicker colorPicker = new ColorPicker(Color.BLACK); | |
colorPicker.prefWidthProperty().bind(rootPane.widthProperty().subtract(20)); | |
VBox.setMargin(colorPicker, new Insets(0, 10, 0, 10)); | |
rootPane.getChildren().addAll(hbRadioButtons, colorPicker); | |
colorDisplayFill.set(colorPicker.getValue()); | |
colorPicker.setOnAction(action -> { | |
if(rbUseColor.isSelected()) { | |
Color c = colorPicker.getValue(); | |
color = getIntFromColor((int) (c.getRed() * 255), (int) (c.getGreen() * 255), (int) (c.getBlue() * 255)); | |
colorDisplayFill.set(c); | |
} | |
}); | |
rbUseColor.setOnAction(action -> { | |
rbUseScreenAverage.setDisable(true); | |
calculateScreenAverage = false; | |
new Thread(() -> { | |
try { | |
Thread.sleep(100); | |
grabber.stop(); | |
} catch (Exception ignored) {} | |
Platform.runLater(() -> { | |
Color c = colorPicker.getValue(); | |
color = getIntFromColor((int) (c.getRed() * 255), (int) (c.getGreen() * 255), (int) (c.getBlue() * 255)); | |
colorDisplayFill.set(c); | |
rbUseScreenAverage.setDisable(false); | |
}); | |
}).start(); | |
}); | |
rbUseScreenAverage.setOnAction(action -> { | |
try { | |
grabber.start(); | |
} catch (FrameGrabber.Exception ignored) {} | |
calculateScreenAverage = true; | |
}); | |
Rectangle colorDisplay = new Rectangle(); | |
colorDisplay.widthProperty().bind(rootPane.widthProperty().subtract(40)); | |
colorDisplay.setHeight(100); | |
colorDisplay.fillProperty().bind(colorDisplayFill); | |
rootPane.getChildren().add(colorDisplay); | |
VBox.setMargin(colorDisplay, new Insets(20, 20, 20, 20)); | |
try { | |
serialPort = (SerialPort) comPort.open(this.getClass().getName(), 2000); | |
serialPort.setSerialPortParams(9600, | |
SerialPort.DATABITS_8, | |
SerialPort.STOPBITS_1, | |
SerialPort.PARITY_NONE); | |
output = serialPort.getOutputStream(); | |
grabber.setFormat("dshow"); | |
grabber.setFrameRate(30); | |
grabber.setVideoBitrate(5000); | |
grabber.setPixelFormat(AV_PIX_FMT_BGR24); | |
grabber.setVideoCodec(AV_CODEC_ID_RAWVIDEO); | |
final Thread thSend = new Thread(() -> { | |
while(true) { | |
try { | |
if(calculateScreenAverage) { | |
calculateAverageScreenColor(); | |
} | |
output.write(0xFF); | |
output.write(0xFF & (byte) (color >> 16 & 0xFF)); | |
output.write(0xFF & (byte) (color >> 8 & 0xFF)); | |
output.write(0xFF & (byte) (color & 0xFF)); | |
Thread.sleep(1); | |
} catch (Exception e) { | |
break; | |
} | |
} | |
}); | |
thSend.setDaemon(true); | |
thSend.start(); | |
primaryStage.setOnCloseRequest(event -> { | |
thSend.interrupt(); | |
new Thread(() -> { | |
try { | |
Thread.sleep(250); | |
grabber.stop(); | |
} catch (Exception ignored) {} | |
}).start(); | |
}); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
public void calculateAverageScreenColor() { | |
try { | |
Frame grabbedFrame = grabber.grabImage(); | |
BufferedImage frame = converter.convert(grabbedFrame); | |
long rSum = 0, gSum = 0, bSum = 0; | |
int width = frame.getWidth(); | |
int height = frame.getHeight(); | |
int rgb; | |
for(int x = 0; x < width; x += 3) { | |
for(int y = 0; y < height; y += 3) { | |
rgb = frame.getRGB(x, y); | |
bSum += rgb & 0xFF; | |
gSum += (rgb & 0xFF00) >> 8; | |
rSum += (rgb & 0xFF0000) >> 16; | |
} | |
} | |
rSum /= width * height / 9; | |
gSum /= width * height / 9; | |
bSum /= width * height / 9; | |
color = getIntFromColor((int) rSum, (int) gSum, (int) bSum); | |
final Color c = Color.rgb((int) rSum, (int) gSum, (int) bSum); | |
Platform.runLater(() -> { | |
colorDisplayFill.set(c); | |
}); | |
} catch (FrameGrabber.Exception ex) { | |
ex.printStackTrace(); | |
} | |
} | |
public int getIntFromColor(int red, int green, int blue){ | |
return (red << 16) + (green << 8) + blue; | |
} | |
@Override | |
public void stop() throws Exception { | |
super.stop(); | |
if (serialPort != null) { | |
serialPort.close(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment