diff options
| author | Adam Malczewski <[email protected]> | 2026-04-18 17:48:09 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-04-18 17:48:09 +0900 |
| commit | 48e26ce606644de7b529f819d6aeffcdceef2ffc (patch) | |
| tree | 8f53acbb51dac6289e6c528ce25ed2c73435cc8d /.rules | |
| download | study-player-48e26ce606644de7b529f819d6aeffcdceef2ffc.tar.gz study-player-48e26ce606644de7b529f819d6aeffcdceef2ffc.zip | |
initial
Diffstat (limited to '.rules')
| -rw-r--r-- | .rules/plan/phase1.md | 40 | ||||
| -rw-r--r-- | .rules/plan/phase2.md | 34 | ||||
| -rw-r--r-- | .rules/plan/phase3.md | 44 | ||||
| -rw-r--r-- | .rules/plan/plan.md | 138 |
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). |
