Skip to content

Instantly share code, notes, and snippets.

@Toqaro
Created January 7, 2016 21:57
Show Gist options
  • Save Toqaro/85331f5734584ffe32fc to your computer and use it in GitHub Desktop.
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).
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