I went for the Arduino + Processing solution. Works like a charm and gives me all of the controls I need.
If anybody else is in a similar pickle, here's a demo for Arduino and Processing with 6 buttons and 6 LEDs.
. This hideous Arduino Nano was NOT soldered by me — it was the work of a beginner new to soldering. But it works! :D
import processing.serial.*;
import processing.video.*;
Serial port;
String[] videos = {
"video1.mp4",
"video2.mp4",
"video3.mp4",
"video4.mp4",
"video5.mp4",
"video6.mp4"
};
Movie[] videoPlayers = new Movie[6];
// Coordinates of the videos:
PVector[] videoCoordinates;
int currentVideoIndex = 0;
boolean videoPlaying = false;
float fadeInDuration = 1; // Duration for fade-in in seconds
float fadeOutDuration = 1; // Duration for fade-out in seconds
float fadeStartTime = 0;
void setup() {
size(1920, 1080);
// Set the sketch to full-screen
fullScreen();
// Initialize video coordinates based on layout parameters
videoCoordinates = new PVector[]{
new PVector(0.0 * width, 0.0 * height), // Video 1 coordinates (top-left)
new PVector(0.6 * width, 0.0 * height), // Video 2 coordinates (top-right)
new PVector(0.0 * width, 0.4 * height), // Video 3 coordinates (center-left)
new PVector(0.6 * width, 0.4 * height), // Video 4 coordinates (bcenter-right)
new PVector(0.0 * width, 0.8 * height), // Video 5 coordinates (bottom-left)
new PVector(0.6 * width, 0.8 * height) // Video 6 coordinates (bottom-right)
};
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
printArray(Serial.list());
String portName = Serial.list()[7]; // Change to the appropriate Arduino port name
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
port = new Serial(this, portName, 9600);
// Load all videos but start playing only the first one
for (int i = 0; i < videos.length; i++) {
videoPlayers[i] = new Movie(this, videos[i]);
videoPlayers[i].noLoop(); // Do not loop initially
}
}
void draw() {
background(0); // Fill the background with black
// Draw the current video at its specified coordinates
if (videoPlaying && videoPlayers[currentVideoIndex] != null) {
PVector coords = videoCoordinates[currentVideoIndex];
float videoWidth = width / 2;
float videoHeight = height / 3;
// Check if fade-in is in progress
if (fadeStartTime > 0) {
float fadeProgress = (millis() - fadeStartTime) / (fadeInDuration * 1000);
tint(255, map(fadeProgress, 0, 1, 0, 255));
}
image(videoPlayers[currentVideoIndex], coords.x, coords.y, videoWidth, videoHeight);
// Check if the video has played once
if (videoPlayers[currentVideoIndex].time() >= videoPlayers[currentVideoIndex].duration()) {
// Start the fade-out
if (fadeStartTime == 0) {
fadeStartTime = millis(); // Record the start time of the fade
}
// Calculate fade progress
float fadeProgress = (millis() - fadeStartTime) / (fadeOutDuration * 1000);
// Check if fade-out is complete
if (fadeProgress >= 1.0) {
videoPlayers[currentVideoIndex].pause(); // Pause the current video
videoPlayers[currentVideoIndex].jump(0); // Jump to the beginning
fadeStartTime = 0; // Reset fade start time
videoPlaying = false;
// Send a message to Arduino to switch off the corresponding LED
port.write("LED_OFF\n");
}
}
}
}
void serialEvent(Serial p) {
String message = p.readStringUntil('\n');
if (message != null) {
message = message.trim();
int buttonIndex = int(message);
if (buttonIndex >= 0 && buttonIndex < videos.length) {
switchVideo(buttonIndex);
}
}
}
void switchVideo(int index) {
if (index >= 0 && index < videos.length) {
println("Switching to Video: " + videos[index]);
// Pause all videos
for (Movie player : videoPlayers) {
if (player != null) {
player.pause();
}
}
// If the same button is pressed again, start the video from the beginning
if (index == currentVideoIndex) {
videoPlayers[currentVideoIndex].jump(0); // Jump to the beginning
} else {
currentVideoIndex = index;
}
videoPlayers[currentVideoIndex].play(); // Start playing
// Start the fade-in
fadeStartTime = millis();
videoPlaying = true;
}
}
void movieEvent(Movie m) {
m.read(); // Read the next available frame
}