Skip to content

feat(player): add volume control and shortcuts - #31

Merged
houko merged 2 commits into
mainfrom
feat/player-volume
Nov 9, 2026
Merged

houko merged 2 commits into
mainfrom
feat/player-volume

Conversation

@houko

@houko houko commented Nov 9, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Adds a volume control to the player's transport: a speaker button that mutes and unmutes (its glyph shows loud / quiet / muted) and a short slider drawn like the scrubber.
  • Adds shortcuts: ↑ / ↓ change the volume by 10%, M mutes. Each shows an OSD message on the picture (音量 70%, 静音). They work as soon as a film is open, and are listed in the cheat sheet and the menu bar.
  • The volume and mute are saved in settings.json (volume, muted in savedPlayer) and carry over from film to film.

How it works

The sound plays through ffplay -nodisp, which takes its volume only at start (-volume N; the afplay fallback gets -v). A change therefore restarts the sound at the playhead. Changes are debounced by 250 ms, so holding an arrow key or dragging the slider restarts it once when it rests instead of cutting the sound at every step. A held (paused) film drops its prepared sound and starts it at the new volume on play. A film still opening picks up a volume changed meanwhile.

Muted with ↑/↓ unmutes from the kept volume; M on a volume of 0 brings it back to 50% so it can be heard.

While muted (or at 0) there is no sound process at all; unmuting starts one at the playhead. Back from the background, a volume change restarts the sound where it has played on to, not where the paused picture stands. A change made within 250 ms of quitting is still saved.

Behavior change in the fallback: without ffplay the sound plays through afplay, which cannot start partway into a file. It used to play the film's start under a later picture after any seek; now it gives no sound when asked to start partway (it still plays from 0:00). Homebrew's ffmpeg ships ffplay, so this only affects standalone ffmpeg builds.

Screenshots

Playing, controls shown:

Before After
before: playing after: playing

Muted with M (new scene player-muted):

after: muted

Cheat sheet:

Before After
before: cheat sheet after: cheat sheet

Test plan

  • go vet ./... and go test ./apps/kando/... pass locally (macOS, with ffmpeg/ffplay)
  • GOOS=windows go vet ./apps/kando (the audio_stub.go build)
  • New tests: TestVolumeKeys, TestMuteButtonMutes, TestVolumeSettingsRoundTrip, TestAudioCommandSetsTheVolume, TestHeldFilmStartsAtTheNewVolume, TestAfplayPlaysOnlyFromTheStart, TestMutedFilmHasNoSound, TestQuittingKeepsTheVolume, TestSoundRestartsWhereItPlayedOn
  • Manual check in the real app with a film that has sound: the gap when the sound restarts after a volume change is short and audio stays in sync

Add a speaker button and a volume slider to the player's transport, with ↑/↓ to change the volume by 10% and M to mute. The volume and mute are kept in settings.json and carry over from film to film.

ffplay runs without its window and takes its volume only at start, so a new volume restarts the sound at the playhead once the volume has rested for 250 ms, instead of cutting it at every key repeat or slider step.
@houko
houko force-pushed the feat/player-volume branch from 6e27b89 to b3b0b10 Compare November 9, 2026 05:58
@github-actions github-actions Bot added area: app The app shell and state in apps/kando area: ui The screens: sidebar, films, film, settings, account and cheatsheet platform: macos macOS-only code (*_darwin.go) feature A new feature (feat) size/M 100 to 499 changed lines labels Nov 9, 2026
- Restart the sound where it has played on to when the app is back from the background, not where the frozen picture stands.
- Make the sound for a film that finished opening at the current volume instead of starting it and restarting it at once.
- End the sound process while muted instead of playing silence, and start it again on unmute.
- Give no sound when afplay, the fallback without ffplay, is asked to start partway, since it can only play from the start; before, a seek or a volume change played the film's start under a later picture.
- Keep a volume changed just before quitting, which the 250 ms settle never saved.
- Prefix the player-only volume helpers with player, as AGENTS.md asks.
@houko
houko merged commit c8333b0 into main Nov 9, 2026
17 of 18 checks passed
houko added a commit that referenced this pull request Nov 9, 2026
Resolve the conflict with the volume control (#31). A picture taking over from the background keeps the sound and the volume keep had, and soundAt reads the sound clock through a returning field from the moment the app comes back until the picture takes over or fails, since backgroundAt is cleared then. backgroundResumeAt becomes soundClock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: app The app shell and state in apps/kando area: ui The screens: sidebar, films, film, settings, account and cheatsheet feature A new feature (feat) platform: macos macOS-only code (*_darwin.go) size/M 100 to 499 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant