diff --git a/README.md b/README.md index c49c08f..6511301 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ `xtc` is a new, energyXT-inspired digital audio workstation project. It is not a clone and contains no energyXT source code or assets. +![xtc promotional overview: arrangement, piano roll, mixer and modular effects](screenshots/overview.png) + ## Design decisions - **Modular engine first:** sequencer, instruments, effects, mixers and I/O are nodes in one processing graph. diff --git a/screenshots/README.md b/screenshots/README.md index 399e901..4a6f8e9 100644 --- a/screenshots/README.md +++ b/screenshots/README.md @@ -3,10 +3,24 @@ The complete gallery was refreshed after Phases 100–105 on 2026-11-06, including MIDI preferences/import, the sample editor, creative workflows and real installed VST3 insert chains. -These are actual rendered application windows and menus on a 1440 × 960 virtual -X11 screen, without mockups, compositing or image edits. +The gallery captures are actual rendered application windows and menus on a +1440 × 960 virtual X11 screen. The promotional `overview.png` is a designed +composite of cropped, proportionally scaled captures with added headings; the +other captures have no mockups, compositing or image edits. -![Arrangement with named sections](overview.png) +![xtc promotional overview: arrangement, piano roll, mixer and modular effects](overview.png) + +Regenerate the 2400 × 1800 promotional overview after refreshing the gallery: + +```sh +python3 tools/create_promotional_overview.py +``` + +This optional image composition script requires Pillow, Fontconfig (`fc-match`) +and DejaVu Sans. It uses `sequencer-loop.png`, `piano-roll-velocity.png`, +`mixer-playing.png` and `track-inserts-vst3.png`; no application UI is fabricated. +The full screenshot capture target replaces `overview.png` with a raw capture, +so run the composition script afterwards to restore the promotional version. The temporary demonstration project starts from `examples/projects/modern-session.xtc`, adding Intro/Verse/Chorus/Outro markers, diff --git a/screenshots/overview.png b/screenshots/overview.png index 3ea5ba2..47c8822 100644 Binary files a/screenshots/overview.png and b/screenshots/overview.png differ diff --git a/tools/create_promotional_overview.py b/tools/create_promotional_overview.py new file mode 100644 index 0000000..8f6a60a --- /dev/null +++ b/tools/create_promotional_overview.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +"""Compose the promotional overview from real gallery captures (requires Pillow).""" +from pathlib import Path +import subprocess + +from PIL import Image, ImageDraw, ImageFont + +ROOT = Path(__file__).resolve().parents[1] +GALLERY = ROOT / "screenshots" +WIDTH, HEIGHT = 2400, 1800 + + +def font(size, bold=False): + family = "DejaVu Sans:style=Bold" if bold else "DejaVu Sans" + path = subprocess.check_output( + ["fc-match", family, "-f", "%{file}"], text=True + ).strip() + return ImageFont.truetype(path, size) + + +def main(): + canvas = Image.new("RGB", (WIDTH, HEIGHT)) + draw = ImageDraw.Draw(canvas) + # A quiet ink/teal gradient keeps the actual application views prominent. + for y in range(HEIGHT): + t = y / HEIGHT + draw.line((0, y, WIDTH, y), fill=(int(13 + 6*t), int(24 + 4*t), int(31 + 5*t))) + white, muted, teal = "#edf5f5", "#a4b7c1", "#74dbc1" + + def text(x, y, value, size, color=white, bold=False): + draw.text((x, y), value, font=font(size, bold), fill=color) + + def capture(name, box, x, y, w, h): + with Image.open(GALLERY / (name + ".png")) as source: + crop = source.convert("RGB").crop(box) + # Fit without distorting UI proportions; the remaining space is the card. + scale = min(w / crop.width, h / crop.height) + crop = crop.resize( + (round(crop.width * scale), round(crop.height * scale)), + Image.Resampling.LANCZOS, + ) + canvas.paste(crop, (x + (w-crop.width)//2, y + (h-crop.height)//2)) + + text(80, 38, "xtc", 150, teal, True) + draw.line((370, 75, 370, 207), fill="#35525a", width=2) + text(420, 63, "A lightweight, modular DAW.", 65, bold=True) + text(424, 154, "Compose. Shape your sound. Connect your ideas.", 35, muted) + text(82, 252, "ARRANGE YOUR IDEAS", 23, teal, True) + text(2320 - draw.textlength("Audio + MIDI sequencing", font=font(23)), 252, + "Audio + MIDI sequencing", 23, muted) + + draw.rounded_rectangle((76, 304, 2324, 1158), radius=15, fill="#080f15", outline="#36515b", width=2) + capture("sequencer-loop", (0, 20, 1400, 540), 82, 310, 2236, 842) + + cards = [ + (80, "01", "Write melodies", "Piano roll + note velocities", + "piano-roll-velocity", (24, 230, 930, 804)), + (836, "02", "Build your mix", "Track / bus routing + automation", + "mixer-playing", (24, 230, 1380, 804)), + (1592, "03", "Connect your effects", "Modular track insert chains", + "track-inserts-vst3", (24, 188, 1356, 425)), + ] + for x, number, title, caption, source, box in cards: + text(x, 1205, number, 23, teal, True) + text(x+48, 1196, title, 34, bold=True) + text(x, 1253, caption, 24, muted) + draw.rounded_rectangle((x, 1309, x+728, 1634), radius=12, fill="#181d23", outline="#354852", width=2) + capture(source, box, x+10, 1319, 708, 305) + + draw.line((80, 1690, 2320, 1690), fill="#354852", width=2) + text(80, 1720, "LINUX FIRST / OPEN SOURCE / MIT LICENSE", 23, teal, True) + text(1580, 1720, "In development · Actual application captures", 23, muted) + canvas.save(GALLERY / "overview.png", optimize=True) + + +if __name__ == "__main__": + main()