Created
January 16, 2021 03:03
-
-
Save Trumeet/fd4e93d69d78dc4ac14d5cac42dfff7e to your computer and use it in GitHub Desktop.
Linux Terminal has the capability of displaying 8-bit colour, so why not play something on it?
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
import org.jcodec.api.FrameGrab; | |
import org.jcodec.common.io.NIOUtils; | |
import org.jcodec.common.model.Picture; | |
import org.jcodec.scale.AWTUtil; | |
import java.awt.image.BufferedImage; | |
import java.io.*; | |
import javax.sound.sampled.AudioInputStream; | |
import javax.sound.sampled.AudioSystem; | |
import javax.sound.sampled.Clip; | |
/** | |
* Linux Terminal has the capability of displaying 8-bit colour, so why not play something on it? | |
* | |
* Libraries: | |
* <pre> | |
* implementation 'org.jcodec:jcodec:0.2.5' | |
* implementation 'org.jcodec:jcodec-javase:0.2.5' | |
* </pre> | |
* | |
* Pre-scaled video required. Separate wav audio required. Scale the video so that the size will not exceed $COLUMNS pixels in width by $LINES pixels in height. | |
* $COLUMNS and $LINES variables required. | |
* WTFPL | |
* | |
* https://gist.github.com/Trumeet | |
* | |
* @author YuutaW | |
*/ | |
public class JPlayer { | |
public static void main(String... args) throws Throwable { | |
final int mspf = 1000 / 30; | |
final int columns = Integer.parseInt(System.getenv("COLUMNS")); | |
final int lines = Integer.parseInt(System.getenv("LINES")); | |
final FrameGrab grab = FrameGrab.createFrameGrab(NIOUtils.readableChannel(new File(args[0]))); | |
int[][] framebuffer = new int[columns][lines]; | |
AudioInputStream audioIn = AudioSystem.getAudioInputStream(new File(args[1])); | |
Clip clip = AudioSystem.getClip(); | |
clip.open(audioIn); | |
System.out.print("\033[2J"); | |
System.out.print("Press any key to start..."); | |
System.in.read(); | |
System.out.print("\033[?25l"); | |
Runtime.getRuntime().addShutdownHook(new Thread(() -> { | |
System.out.print("\033[?25h\u001B[0m"); | |
})); | |
final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out), | |
2 ^ 32); | |
clip.start(); | |
while(true) { | |
final long start = System.currentTimeMillis(); | |
final Picture picture = grab.getNativeFrame(); | |
if(picture == null) break; | |
final BufferedImage image = AWTUtil.toBufferedImage(picture); | |
for (int row = 0; row < image.getHeight(); row ++) { | |
for (int column = 0; column < image.getWidth(); column ++) { | |
final int gray = image.getRGB(column, row) & 0xFF; | |
if(framebuffer[column][row] != gray) { | |
render(gray, row, column, writer); | |
} | |
framebuffer[column][row] = gray; | |
} | |
} | |
writer.flush(); | |
final long took = System.currentTimeMillis() - start; | |
if(took <= mspf) { | |
Thread.sleep(mspf - took); | |
} | |
} | |
} | |
private static void render(final int gray, final int line, final int column, | |
Writer writer) throws IOException { | |
int foregroundColorCode; | |
int backgroundColorCode; | |
if(gray <= 63) { | |
// Black. | |
foregroundColorCode = 30; | |
backgroundColorCode = 40; | |
} else if(gray <= 126) { | |
// Dark Gray | |
foregroundColorCode = 90; | |
backgroundColorCode = 100; | |
} else if(gray <= 189) { | |
// Light Gray | |
foregroundColorCode = 37; | |
backgroundColorCode = 47; | |
} else { | |
// White | |
foregroundColorCode = 97; | |
backgroundColorCode = 107; | |
} | |
// We don't use format here, since it's slow. | |
// '[%d;%dH[%dm[%dm ' | |
final StringBuilder stringBuilder = new StringBuilder() | |
.append("\033[") | |
.append(line) | |
.append(";") | |
.append(column) | |
.append("H\u001B[") | |
.append(foregroundColorCode) | |
.append("m\u001b[") | |
.append(backgroundColorCode) | |
.append("m "); | |
writer.write(stringBuilder.toString()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment