Compare commits

...

3 Commits

Author SHA1 Message Date
4f6d1de8e2 Always clean-build in Docker to avoid stale binary
The Docker build target now runs `make clean all` instead of bare
`make`, so it rebuilds regardless of an existing build/sdlamp2.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:44:14 +01:00
a0f95c7252 Combine Play/Stop into toggle, add Previous Cassette button
Merge the separate Stop and Play buttons into one slot that toggles
between play/stop and shows the corresponding icon. Use the freed
slot for a Previous Cassette button, mirroring Next Cassette.

Layout: [Volume] [Prev] [Rewind] [Play/Stop] [FF] [Next]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:38:17 +01:00
2daf9f7955 Start paused, respect pause state on next-tape, quiet joystick log
Player was autoplaying on startup and on file switch. Now switch_file()
preserves the current paused state so the app launches paused and
"next tape" only continues playing if already playing. Joystick name
printf moved behind --debug flag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:30:33 +01:00
3 changed files with 40 additions and 24 deletions

View File

@ -18,7 +18,7 @@ shell:
endif endif
build: .build build: .build
docker run --platform linux/arm64 --rm -v "$(PROJECT_DIR)":/workspace $(IMAGE_NAME) make docker run --platform linux/arm64 --rm -v "$(PROJECT_DIR)":/workspace $(IMAGE_NAME) make clean all
clean: clean:
docker rmi $(IMAGE_NAME) docker rmi $(IMAGE_NAME)

View File

@ -7,7 +7,7 @@
| Version | 1.0 | | Version | 1.0 |
| Status | Draft | | Status | Draft |
| Created | 2026-02-10 | | Created | 2026-02-10 |
| Updated | 2026-02-10 | | Updated | 2026-02-13 |
## 1. Purpose ## 1. Purpose
@ -43,6 +43,18 @@ This document specifies the functional requirements for an SDL2 based media play
## 6. Changelog ## 6. Changelog
### 2026-02-13 — Combine Play/Stop, add Previous Cassette button
- **Play/Stop combined**: The separate Stop and Play buttons are merged into a single toggle button that shows the play icon (▶) when paused and the stop icon (■) when playing.
- **Previous Cassette**: A new "Previous Cassette" button is added at the left of the transport controls, mirroring "Next Cassette". Wraps from the first tape to the last.
- **New layout**: `[Volume] [Prev] [Rewind] [Play/Stop] [FF] [Next]` — same 6 focusable elements, same positions.
### 2026-02-13 — Start paused, fix next-tape autoplay, quiet joystick log
- **Start paused**: The player no longer autoplays on startup; it opens the last file at the saved position but waits for the user to press Play.
- **Next tape respects pause state**: `switch_file()` no longer forces playback. Pressing "next tape" while paused switches the file and stays paused; pressing it while playing switches and continues playing.
- **Joystick log behind --debug**: The "Joystick: ..." message is now only printed when `--debug` is passed.
### 2026-02-13 — arm64 Docker build container ### 2026-02-13 — arm64 Docker build container
- **New build container**: Replaced the broken Buildroot cross-compilation toolchain (`docker/`) with an arm64 Ubuntu 22.04 Docker container (`docker-arm64/`). Runs via QEMU user-mode emulation on x86 hosts and matches the target device exactly (same distro, same glibc, same library versions). The project's native `make` works as-is inside the container — no cross-compilation flags needed. - **New build container**: Replaced the broken Buildroot cross-compilation toolchain (`docker/`) with an arm64 Ubuntu 22.04 Docker container (`docker-arm64/`). Runs via QEMU user-mode emulation on x86 hosts and matches the target device exactly (same distro, same glibc, same library versions). The project's native `make` works as-is inside the container — no cross-compilation flags needed.

View File

@ -57,15 +57,15 @@ static int current_file_index = 0;
/* --- Focus / navigation --- */ /* --- Focus / navigation --- */
#define FOCUS_VOLUME 0 #define FOCUS_VOLUME 0
#define FOCUS_REWIND 1 #define FOCUS_PREV 1
#define FOCUS_STOP 2 #define FOCUS_REWIND 2
#define FOCUS_PLAY 3 #define FOCUS_PLAYSTOP 3
#define FOCUS_FF 4 #define FOCUS_FF 4
#define FOCUS_NEXT 5 #define FOCUS_NEXT 5
#define FOCUS_COUNT 6 #define FOCUS_COUNT 6
static float volume = 0.5f; static float volume = 0.5f;
static int focus_index = FOCUS_PLAY; static int focus_index = FOCUS_PLAYSTOP;
static SDL_GameController* controller = NULL; static SDL_GameController* controller = NULL;
static SDL_Joystick* joystick = NULL; static SDL_Joystick* joystick = NULL;
static int debug_mode = 0; static int debug_mode = 0;
@ -478,8 +478,7 @@ static void switch_file(int index) {
snprintf(title, sizeof(title), "SDLamp2 - %s", current_file); snprintf(title, sizeof(title), "SDLamp2 - %s", current_file);
SDL_SetWindowTitle(window, title); SDL_SetWindowTitle(window, title);
paused = SDL_FALSE; SDL_PauseAudioDevice(audio_device, paused);
SDL_PauseAudioDevice(audio_device, 0);
return; return;
} }
@ -495,13 +494,22 @@ static void switch_file(int index) {
static void activate_focused_button(void) { static void activate_focused_button(void) {
switch (focus_index) { switch (focus_index) {
case FOCUS_PREV:
if (num_audio_files > 0) {
int prev = (current_file_index - 1 + num_audio_files) % num_audio_files;
switch_file(prev);
}
break;
case FOCUS_REWIND: { case FOCUS_REWIND: {
double pos = get_current_seconds() - SEEK_SECONDS; double pos = get_current_seconds() - SEEK_SECONDS;
decoder_seek(pos < 0.0 ? 0.0 : pos); decoder_seek(pos < 0.0 ? 0.0 : pos);
break; break;
} }
case FOCUS_STOP: case FOCUS_PLAYSTOP:
if (!paused) { if (paused && num_audio_files > 0) {
paused = SDL_FALSE;
SDL_PauseAudioDevice(audio_device, 0);
} else if (!paused) {
paused = SDL_TRUE; paused = SDL_TRUE;
SDL_PauseAudioDevice(audio_device, 1); SDL_PauseAudioDevice(audio_device, 1);
if (current_file[0]) { if (current_file[0]) {
@ -509,12 +517,6 @@ static void activate_focused_button(void) {
} }
} }
break; break;
case FOCUS_PLAY:
if (paused && num_audio_files > 0) {
paused = SDL_FALSE;
SDL_PauseAudioDevice(audio_device, 0);
}
break;
case FOCUS_FF: { case FOCUS_FF: {
double pos = get_current_seconds() + SEEK_SECONDS; double pos = get_current_seconds() + SEEK_SECONDS;
double dur = get_duration_seconds(); double dur = get_duration_seconds();
@ -554,21 +556,22 @@ int main(int argc, char** argv) {
const SDL_Rect volume_bg = {25, 390, 30, 80}; const SDL_Rect volume_bg = {25, 390, 30, 80};
/* Button positions (bottom of window, centered) */ /* Button positions (bottom of window, centered) */
const SDL_Rect rewind_btn = {80, 390, 80, 80}; const SDL_Rect prev_btn = {80, 390, 80, 80};
const SDL_Rect stop_btn = {180, 390, 80, 80}; const SDL_Rect rewind_btn = {180, 390, 80, 80};
const SDL_Rect play_btn = {280, 390, 80, 80}; const SDL_Rect playstop_btn = {280, 390, 80, 80};
const SDL_Rect ff_btn = {380, 390, 80, 80}; const SDL_Rect ff_btn = {380, 390, 80, 80};
const SDL_Rect next_btn = {480, 390, 80, 80}; const SDL_Rect next_btn = {480, 390, 80, 80};
/* Array of focusable rects indexed by FOCUS_* constants */ /* Array of focusable rects indexed by FOCUS_* constants */
const SDL_Rect* focus_rects[FOCUS_COUNT] = {&volume_bg, &rewind_btn, &stop_btn, const SDL_Rect* focus_rects[FOCUS_COUNT] = {&volume_bg, &prev_btn, &rewind_btn,
&play_btn, &ff_btn, &next_btn}; &playstop_btn, &ff_btn, &next_btn};
/* Sprite sheet source rects */ /* Sprite sheet source rects */
const SDL_Rect rewind_sprite = {0, 0, 200, 200}; const SDL_Rect rewind_sprite = {0, 0, 200, 200};
const SDL_Rect play_sprite = {220, 0, 200, 200}; const SDL_Rect play_sprite = {220, 0, 200, 200};
const SDL_Rect ff_sprite = {440, 0, 200, 200}; const SDL_Rect ff_sprite = {440, 0, 200, 200};
const SDL_Rect stop_sprite = {0, 220, 200, 200}; const SDL_Rect stop_sprite = {0, 220, 200, 200};
const SDL_Rect prev_sprite = {440, 220, 200, 200}; /* same circle as next */
const SDL_Rect next_sprite = {440, 220, 200, 200}; const SDL_Rect next_sprite = {440, 220, 200, 200};
/* Progress bar area */ /* Progress bar area */
@ -630,7 +633,7 @@ int main(int argc, char** argv) {
for (int i = 0; i < num_joy; i++) { for (int i = 0; i < num_joy; i++) {
joystick = SDL_JoystickOpen(i); joystick = SDL_JoystickOpen(i);
if (joystick) { if (joystick) {
printf("Joystick: %s\n", SDL_JoystickName(joystick)); if (debug_mode) printf("Joystick: %s\n", SDL_JoystickName(joystick));
break; break;
} }
} }
@ -902,9 +905,10 @@ int main(int argc, char** argv) {
} }
/* Buttons */ /* Buttons */
SDL_RenderCopy(renderer, controls_texture, &prev_sprite, &prev_btn);
SDL_RenderCopy(renderer, controls_texture, &rewind_sprite, &rewind_btn); SDL_RenderCopy(renderer, controls_texture, &rewind_sprite, &rewind_btn);
SDL_RenderCopy(renderer, controls_texture, &stop_sprite, &stop_btn); SDL_RenderCopy(renderer, controls_texture, paused ? &play_sprite : &stop_sprite,
SDL_RenderCopy(renderer, controls_texture, &play_sprite, &play_btn); &playstop_btn);
SDL_RenderCopy(renderer, controls_texture, &ff_sprite, &ff_btn); SDL_RenderCopy(renderer, controls_texture, &ff_sprite, &ff_btn);
SDL_RenderCopy(renderer, controls_texture, &next_sprite, &next_btn); SDL_RenderCopy(renderer, controls_texture, &next_sprite, &next_btn);