summaryrefslogtreecommitdiffhomepage
path: root/.rules
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-04-18 17:48:09 +0900
committerAdam Malczewski <[email protected]>2026-04-18 17:48:09 +0900
commit48e26ce606644de7b529f819d6aeffcdceef2ffc (patch)
tree8f53acbb51dac6289e6c528ce25ed2c73435cc8d /.rules
downloadstudy-player-48e26ce606644de7b529f819d6aeffcdceef2ffc.tar.gz
study-player-48e26ce606644de7b529f819d6aeffcdceef2ffc.zip
initial
Diffstat (limited to '.rules')
-rw-r--r--.rules/plan/phase1.md40
-rw-r--r--.rules/plan/phase2.md34
-rw-r--r--.rules/plan/phase3.md44
-rw-r--r--.rules/plan/plan.md138
4 files changed, 256 insertions, 0 deletions
diff --git a/.rules/plan/phase1.md b/.rules/plan/phase1.md
new file mode 100644
index 0000000..d1a1a55
--- /dev/null
+++ b/.rules/plan/phase1.md
@@ -0,0 +1,40 @@
+# Phase 1 — Audio Playback & Controls
+
+Core audio functionality. After this phase the app is usable as a keyboard-driven player with no visual progress display.
+
+---
+
+## 1.1 Drag & drop file loading
+
+- Use `IsFileDropped()` / `LoadDroppedFiles()` / `UnloadDroppedFiles()`.
+- Validate file extension is `.mp3` (case-insensitive).
+- Unload previous `MusicStream` if one is already loaded.
+- `LoadMusicStream()`, `PlayMusicStream()`, set `state.loaded = true`, `state.playing = true`.
+- Store duration via `GetMusicTimeLength()`.
+- Extract basename from path for `state.filename`.
+
+## 1.2 Play/pause with rewind
+
+- `Space` toggles play/pause.
+- On pause: capture current position via `GetMusicTimePlayed()`, call `PauseMusicStream()`, then `SeekMusicStream()` back 1 second (clamped to 0).
+- On resume: `ResumeMusicStream()`.
+
+## 1.3 Arrow key seeking
+
+- `Left Arrow`: seek backward 5 seconds (clamped to 0).
+- `Right Arrow`: seek forward 5 seconds (clamped to duration).
+- Works in both playing and paused states.
+
+## 1.4 Music stream update
+
+- Call `UpdateMusicStream()` every frame when `state.loaded` is true.
+- This is required for Raylib's streaming audio to function.
+
+---
+
+## Acceptance criteria
+
+- Can drag an MP3 onto the window and hear it play immediately.
+- Space pauses (with 1s rewind) and resumes.
+- Arrow keys seek ±5s with no audible delay.
+- Dropping a new file replaces the current one cleanly (no audio glitches).
diff --git a/.rules/plan/phase2.md b/.rules/plan/phase2.md
new file mode 100644
index 0000000..eeb0eff
--- /dev/null
+++ b/.rules/plan/phase2.md
@@ -0,0 +1,34 @@
+# Phase 2 — Progress Bar & Seeking UI
+
+Visual progress display and mouse-based seeking.
+
+---
+
+## 2.1 Progress bar rendering
+
+- Horizontal bar centered at ~Y=360, width = 80% of window (1024px), height = 20px.
+- Background: dark gray rectangle via `DrawRectangleRec()`.
+- Fill: accent color rectangle, width proportional to `currentTime / duration`.
+- Only drawn when `state.loaded` is true.
+
+## 2.2 Time labels
+
+- Left of bar: current time formatted as `MM:SS` or `HH:MM:SS` (if ≥ 3600s).
+- Right of bar: total duration in the same format.
+- Helper function: `void format_time(float seconds, char *buf, int bufsize)`.
+
+## 2.3 Click-to-seek
+
+- On `IsMouseButtonPressed(MOUSE_BUTTON_LEFT)`, check if click is within progress bar bounding box.
+- Compute target time: `(mouseX - barX) / barWidth * duration`.
+- Call `SeekMusicStream()` to that position.
+- Works in both playing and paused states.
+
+---
+
+## Acceptance criteria
+
+- Progress bar visually tracks playback position in real time.
+- Time labels update every frame and format correctly (MM:SS vs HH:MM:SS).
+- Clicking anywhere on the bar seeks to the corresponding position instantly.
+- Clicking outside the bar does nothing.
diff --git a/.rules/plan/phase3.md b/.rules/plan/phase3.md
new file mode 100644
index 0000000..ead88b4
--- /dev/null
+++ b/.rules/plan/phase3.md
@@ -0,0 +1,44 @@
+# Phase 3 — Polish & Status Display
+
+Final UI elements and visual polish.
+
+---
+
+## 3.1 Title text
+
+- Draw "Study Player" centered near top of window (large font size, ~30px).
+
+## 3.2 Filename display
+
+- Below title: show loaded filename, or "Drag an MP3 file here" when nothing is loaded.
+- Centered horizontally.
+
+## 3.3 Playback status
+
+- Below progress bar: display "PLAYING" or "PAUSED" centered.
+- Show nothing when no file is loaded.
+
+## 3.4 Help text
+
+- Bottom area of window: "Space: play/pause ←/→: seek 5s Click bar: seek".
+- Smaller font, muted color.
+
+## 3.5 Window title update
+
+- Call `SetWindowTitle()` to include the filename when a file is loaded (e.g., "Study Player - chapter1.mp3").
+- Reset to "Study Player" if relevant.
+
+## 3.6 Visual refinements
+
+- Consistent font sizes and vertical spacing across all text elements.
+- Color scheme: dark background (#1a1a2e or similar), light text (#eaeaea), accent color for progress fill (#e94560 or similar).
+- Ensure all text is readable and well-positioned at 1280×720.
+
+---
+
+## Acceptance criteria
+
+- All text elements are visible and properly centered.
+- Status reflects actual playback state and updates immediately on play/pause.
+- Window title bar shows the current filename.
+- The app looks clean and intentional — no overlapping elements, no clipping.
diff --git a/.rules/plan/plan.md b/.rules/plan/plan.md
new file mode 100644
index 0000000..d8a94c0
--- /dev/null
+++ b/.rules/plan/plan.md
@@ -0,0 +1,138 @@
+# Study Player — Implementation Plan
+
+## Overview
+
+A single-file C application using Raylib that plays MP3 audiobooks with minimal UI focused on keyboard-driven study workflow.
+
+---
+
+## Build
+
+- **Build system:** Single `Makefile` at project root with cross-platform support.
+- **Linux:** Compile with gcc, link against raylib, `-lm -lpthread -ldl -lrt -lX11`.
+- **Windows:** Cross-compile with `x86_64-w64-mingw32-gcc`, link against raylib, `-lgdi32 -lwinmm`. Alternatively native MSVC or MinGW on Windows.
+- **Makefile targets:** `make` (Linux default), `make PLATFORM=WINDOWS` (cross-compile for Windows).
+- **Dependencies:** `deps/raylib` (already present), `deps/raygui` (already present — optional).
+- **Source:** `src/main.c` (single file).
+- **Output:** `build/study-player` (Linux), `build/study-player.exe` (Windows).
+- **Compile raylib as static library** first (`deps/raylib/src/`), then link.
+- **bin/build:** Shell script wrapping `make`.
+- **bin/run:** Shell script wrapping `make && ./build/study-player`.
+
+---
+
+## Application State
+
+```c
+typedef struct {
+ Music music; // Raylib Music stream (MP3)
+ bool loaded; // Whether a file is loaded
+ bool playing; // Whether audio is currently playing
+ char filepath[512]; // Path of loaded file
+ char filename[256]; // Display name (basename)
+ float duration; // Total length in seconds
+} AppState;
+```
+
+No persistence. All state is in-memory only.
+
+---
+
+## Window
+
+- **Size:** 1920×1080, not resizable.
+- **Title:** "Study Player" (update to include filename when loaded).
+- **Target FPS:** 60.
+- **Background:** Dark solid color.
+
+---
+
+## Audio Approach — Instant Seek
+
+Raylib's `Music` type is a streaming audio type. Key functions:
+
+- `LoadMusicStream(path)` — load MP3.
+- `PlayMusicStream(music)` / `PauseMusicStream(music)` / `ResumeMusicStream(music)`.
+- `SeekMusicStream(music, positionInSeconds)` — instant seek.
+- `GetMusicTimePlayed(music)` — current position.
+- `GetMusicTimeLength(music)` — total duration.
+- `UpdateMusicStream(music)` — **must be called every frame** to feed audio buffer.
+
+These provide instant play/pause/seek with no delay.
+
+---
+
+## UI Layout (1920×1080)
+
+```
++--------------------------------------------------+
+| |
+| "Study Player" (title) |
+| |
+| [filename or "Drop an MP3 file"] |
+| |
+| |
+| 03:42 [=======>-----------------] 58:31 |
+| ^ progress bar (clickable) |
+| |
+| PLAYING / PAUSED / (empty) |
+| |
+| Space: play/pause ←→: seek 5s |
+| |
++--------------------------------------------------+
+```
+
+---
+
+## Scaffolding (before phases)
+
+Set up before any feature work:
+
+1. **Makefile** — cross-platform (Linux + Windows cross-compile). Compile raylib as static lib, compile+link `src/main.c`.
+2. **bin/build, bin/run** — convenience shell scripts.
+3. **src/main.c skeleton** — `InitWindow`, `InitAudioDevice`, empty main loop with `ClearBackground`, `CloseWindow`. Confirms build pipeline works.
+4. **build/ in .gitignore**.
+
+---
+
+## Phases
+
+Implementation is split into three phases, each in its own file:
+
+- **[Phase 1 — Audio Playback & Controls](phase1.md):** Drag-and-drop loading, play/pause with 1s rewind, arrow key seeking.
+- **[Phase 2 — Progress Bar & Seeking UI](phase2.md):** Progress bar rendering, time labels, click-to-seek.
+- **[Phase 3 — Polish & Status Display](phase3.md):** Title, filename, status text, help text, window title, visual refinements.
+
+---
+
+## File Structure
+
+```
+study-player/
+├── .rules/plan/
+│ ├── plan.md # This file (overview + shared context)
+│ ├── phase1.md # Audio playback & controls
+│ ├── phase2.md # Progress bar & seeking UI
+│ └── phase3.md # Polish & status display
+├── deps/
+│ ├── raylib/ # Already present
+│ └── raygui/ # Already present (optional use)
+├── src/
+│ └── main.c # All application code
+├── Makefile # Build raylib + application (Linux & Windows)
+├── bin/
+│ ├── build # make
+│ └── run # make && ./build/study-player
+└── build/ # Build artifacts (gitignored)
+```
+
+---
+
+## Constraints
+
+- MP3 only.
+- No playlist / queue — one file at a time.
+- No persistence — position lost on close.
+- No volume control (system volume is sufficient).
+- Window fixed at 1920×1080.
+- Must build for Windows (cross-compile from Linux with MinGW, or native MinGW on Windows).