mirror of
https://github.com/joe-scotto/scottokeebs.git
synced 2026-08-24 10:14:29 -05:00
Add ScottoScripts
This commit is contained in:
@@ -3,3 +3,4 @@ IndentCaseLabels: true
|
|||||||
AllowShortEnumsOnASingleLine: false
|
AllowShortEnumsOnASingleLine: false
|
||||||
ColumnLimit: 120
|
ColumnLimit: 120
|
||||||
BinPackArguments: false
|
BinPackArguments: false
|
||||||
|
SortIncludes: false
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
# Read your exported hex file
|
||||||
|
with open("frames.txt", "r") as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
frames = []
|
||||||
|
current_frame = []
|
||||||
|
|
||||||
|
for line in data.splitlines():
|
||||||
|
# Skip empty lines
|
||||||
|
if not line.strip():
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If line is a frame comment, start a new frame
|
||||||
|
if line.strip().startswith("// 'out_"):
|
||||||
|
if current_frame:
|
||||||
|
frames.append(current_frame)
|
||||||
|
current_frame = []
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Extract all hex numbers in the line
|
||||||
|
hex_values = re.findall(r"0x[0-9a-fA-F]+", line)
|
||||||
|
current_frame.extend(hex_values)
|
||||||
|
|
||||||
|
# Append last frame
|
||||||
|
if current_frame:
|
||||||
|
frames.append(current_frame)
|
||||||
|
|
||||||
|
# Write formatted array to frames.c and overwrite on subsequent runs
|
||||||
|
with open("frames.c", "w") as f:
|
||||||
|
f.write(
|
||||||
|
"const uint8_t PROGMEM frames[{}][{}] = {{\n".format(
|
||||||
|
len(frames), len(frames[0])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for f_idx, frame in enumerate(frames):
|
||||||
|
f.write(" {")
|
||||||
|
f.write(", ".join(frame))
|
||||||
|
f.write("}}, // frame {}\n".format(f_idx + 1))
|
||||||
|
|
||||||
|
f.write("};\n")
|
||||||
|
|
||||||
|
print("Export complete: frames.c with {} frames.".format(len(frames)))
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Arguments
|
||||||
|
parser = argparse.ArgumentParser(description="Extract frames from video for QMK OLED")
|
||||||
|
|
||||||
|
parser.add_argument("input", help="Path to input video file")
|
||||||
|
parser.add_argument(
|
||||||
|
"--fps", type=float, default=15, help="Frames per second (default: 15)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--width", type=int, default=128, help="Output frame width (default: 128)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--height", type=int, default=64, help="Output frame height (default: 64)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--frames",
|
||||||
|
type=int,
|
||||||
|
default=120,
|
||||||
|
help="Number of frames to extract (default: 120)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--format",
|
||||||
|
default="gray",
|
||||||
|
choices=["gray", "rgb24", "monow"],
|
||||||
|
help="Output pixel format (default: gray)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--speed",
|
||||||
|
type=float,
|
||||||
|
default=1.0,
|
||||||
|
help="Playback speed, maps to setpts (default: 1.0)",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Create output directory
|
||||||
|
os.makedirs("frames", exist_ok=True)
|
||||||
|
|
||||||
|
# Create ffmpeg command
|
||||||
|
output_pattern = os.path.join(args.output, "out_%03d.png")
|
||||||
|
|
||||||
|
# setpts expression: PTS = 1/speed * PTS
|
||||||
|
setpts_expr = f"setpts={1/args.speed}*PTS"
|
||||||
|
|
||||||
|
ffmpeg_cmd = [
|
||||||
|
"ffmpeg",
|
||||||
|
"-i",
|
||||||
|
args.input,
|
||||||
|
"-filter:v",
|
||||||
|
f"{setpts_expr},fps={args.fps},scale={args.width}:{args.height},format={args.format}",
|
||||||
|
"-frames:v",
|
||||||
|
str(args.frames),
|
||||||
|
output_pattern,
|
||||||
|
]
|
||||||
|
|
||||||
|
# Print command for debugging
|
||||||
|
print("Running command:\n", " ".join(ffmpeg_cmd))
|
||||||
|
|
||||||
|
# Run command
|
||||||
|
subprocess.run(ffmpeg_cmd, check=True)
|
||||||
|
|
||||||
|
print(f"Frames exported to {args.output}")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Paste your output from Image2CPP here...
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#include "frames.c"
|
||||||
|
#include "quantum.h"
|
||||||
|
|
||||||
|
#ifdef OLED_ENABLE
|
||||||
|
|
||||||
|
oled_rotation_t oled_init_user(oled_rotation_t rotation) {
|
||||||
|
return OLED_ROTATION_180; // flips the display 180 degrees if offhand
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NUM_FRAMES 120
|
||||||
|
#define FRAME_WIDTH 128
|
||||||
|
#define FRAME_HEIGHT 64
|
||||||
|
#define FRAME_SIZE \
|
||||||
|
(FRAME_WIDTH * FRAME_HEIGHT / 8) // 128*64 / 8 = 1024 bytes per frame
|
||||||
|
#define FRAME_DELAY 100 // ms per frame, ~10 fps
|
||||||
|
|
||||||
|
bool oled_task_user(void) {
|
||||||
|
static uint16_t frame_index = 0;
|
||||||
|
static uint32_t last_update = 0;
|
||||||
|
|
||||||
|
// Only update frame every FRAME_DELAY ms
|
||||||
|
if (timer_elapsed32(last_update) > FRAME_DELAY) {
|
||||||
|
last_update = timer_read32();
|
||||||
|
|
||||||
|
// Write current frame to OLED (cast to const char* to fix signedness)
|
||||||
|
oled_write_raw_P((const char *)frames[frame_index], FRAME_SIZE);
|
||||||
|
|
||||||
|
// Advance to next frame, loop back to 0
|
||||||
|
frame_index++;
|
||||||
|
if (frame_index >= NUM_FRAMES)
|
||||||
|
frame_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // keep other OLED content disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# OLED Video
|
||||||
|
|
||||||
|
Used to convert a video for display on a QMK OLED.
|
||||||
|
|
||||||
|
This should work on pretty much any QMK keyboard but I highly recommend using a controller with a decent amount of flash memory such as an RP2040.
|
||||||
|
|
||||||
|
# Instructions
|
||||||
|
|
||||||
|
1. Generate your frames using [`extract_frames.py`](#extract_frames.py)
|
||||||
|
- They will output to the `frames` directory.
|
||||||
|
2. Upload those to [Image2CPP](https://javl.github.io/image2cpp/)
|
||||||
|
1. Choose your dithering mode if you want to play with the look.
|
||||||
|
2. Output should be set to `Draw mode: Vertical - 1 bit per pixel`
|
||||||
|
3. Click `Generate code` and copy the output to `frames.txt`.
|
||||||
|
3. Generate your `frames.c` using [create_frames_array.py](create_frames_array.py).
|
||||||
|
1. Copy `frames.c` and `oled.c` to your QMK keymap.
|
||||||
|
2. Import `oled.c` into your `keymap.c`.
|
||||||
|
3. Compile and flash.
|
||||||
|
4. You should now see a video playing on your OLED!
|
||||||
|
|
||||||
|
# extract_frames.py
|
||||||
|
|
||||||
|
This is the script used to generate the video frames you will use to convert.
|
||||||
|
| Argument | Type | Default | Description |
|
||||||
|
| ---------- | ------- | ---------- | ------------------------------------------------- |
|
||||||
|
| `input` | `str` | _required_ | Path to the input video file. |
|
||||||
|
| `--fps` | `float` | `15` | Playback frames per second. |
|
||||||
|
| `--width` | `int` | `128` | Width of the output frames in pixels. |
|
||||||
|
| `--height` | `int` | `64` | Height of the output frames in pixels. |
|
||||||
|
| `--frames` | `int` | `120` | Number of frames to extract from the video. |
|
||||||
|
| `--format` | `str` | `gray` | Output pixel format: `gray`, `rgb24`, or `monow`. |
|
||||||
|
| `--speed` | `float` | `1.0` | Playback speed of the video. |
|
||||||
Reference in New Issue
Block a user