I’m a caption volunteer for Emacsconf 2024, so far I have used subed, an Emacs package, for editing the captions of two presentations. In this article, I share my personal subed customizations which have been of great help for me when editing subtitles. I hope these customizations can help other subed users or caption volunteers to edit captions efficiently.
I wrote most of these configurations some months ago while I was working on a project where I used subed to correct the timestamps of subtitles of 765 *.mp3 audio files in Spanish (approximately 46 hours of audio).
(defun my-subed-mpv-jump-to-0% () (interactive) (let ((cur-sub-start (subed-subtitle-msecs-start)) (cur-sub-stop (subed-subtitle-msecs-stop))) (subed-mpv-jump cur-sub-start))) (defun my-subed-mpv-jump-to-25% () (interactive) (let ((cur-sub-start (subed-subtitle-msecs-start)) (cur-sub-stop (subed-subtitle-msecs-stop))) (subed-mpv-jump (+ cur-sub-start (* (- cur-sub-stop cur-sub-start) 0.25))))) (defun my-subed-mpv-jump-to-50% () (interactive) (let ((cur-sub-start (subed-subtitle-msecs-start)) (cur-sub-stop (subed-subtitle-msecs-stop))) (subed-mpv-jump (+ cur-sub-start (* (- cur-sub-stop cur-sub-start) 0.5))))) (defun my-subed-mpv-jump-to-75% () (interactive) (let ((cur-sub-start (subed-subtitle-msecs-start)) (cur-sub-stop (subed-subtitle-msecs-stop))) (subed-mpv-jump (+ cur-sub-start (* (- cur-sub-stop cur-sub-start) 0.75))))) (defun my-subed-mpv-jump-to-90% () (interactive) (let ((cur-sub-start (subed-subtitle-msecs-start)) (cur-sub-stop (subed-subtitle-msecs-stop))) (subed-mpv-jump (+ cur-sub-start (* (- cur-sub-stop cur-sub-start) 0.90))))) (defmacro my-subed-define-functions-change-time-by-ms (milliseconds) "Define functions for increasing and decreasing start-time and stop-time for a given number of milliseconds." `(progn (defun ,(intern (concat "my-subed-increase-start-time-by-" (number-to-string milliseconds) "ms")) () (interactive) (subed-adjust-subtitle-time-start ,milliseconds) (when (subed-loop-over-current-subtitle-p) (subed--set-subtitle-loop))) (defun ,(intern (concat "my-subed-decrease-start-time-by-" (number-to-string milliseconds) "ms")) () (interactive) (subed-adjust-subtitle-time-start ,(* -1 milliseconds)) (when (subed-loop-over-current-subtitle-p) (subed--set-subtitle-loop))) (defun ,(intern (concat "my-subed-increase-stop-time-by-" (number-to-string milliseconds) "ms")) () (interactive) (subed-adjust-subtitle-time-stop ,milliseconds) (when (subed-loop-over-current-subtitle-p) (subed--set-subtitle-loop))) (defun ,(intern (concat "my-subed-decrease-stop-time-by-" (number-to-string milliseconds) "ms")) () (interactive) (subed-adjust-subtitle-time-stop ,(* -1 milliseconds)) (when (subed-loop-over-current-subtitle-p) (subed--set-subtitle-loop))))) (my-subed-define-functions-change-time-by-ms 500) (my-subed-define-functions-change-time-by-ms 1000) ;; I sometimes set the playback speed to 0.5 when I can't clearly ;; identify what the speaker said. Playing it in low speed helps me to ;; focus on the part that I find difficult. ;; ;; I also set playback speed to 0.5 when the speaker speaks so fast ;; and I want to split a subtitle in a very specific part. (defun my-subed-mpv-playback-speed-0.5 () (interactive) (subed-mpv-playback-speed 0.5)) (defun my-subed-mpv-playback-speed-1 () (interactive) (subed-mpv-playback-speed 1)) (defun my-subed-mpv-playback-speed-2 () (interactive) (subed-mpv-playback-speed 2)) (defun my-subed-mpv-playback-speed-3 () (interactive) (subed-mpv-playback-speed 3)) (defun my-subed-mpv-playback-speed-4 () (interactive) (subed-mpv-playback-speed 4)) (defun my-subed-mpv-seek-backward () (interactive) (subed-mpv-seek -100)) (defun my-subed-mpv-seek-forward () (interactive) (subed-mpv-seek 100)) (define-minor-mode my-subed-custom-keys-minor-mode "Custom keys for `subed-mode'." :keymap (let ((map (make-sparse-keymap))) ;; Default keybindings (define-key map (kbd "C-M-f") 'forward-sexp) (define-key map (kbd "C-M-b") 'backward-sexp) (define-key map (kbd "C-k") 'subed-kill-subtitle) ;; Jump to specific parts in the audio (define-key map (kbd "M-H") 'my-subed-mpv-jump-to-0%) (define-key map (kbd "M-J") 'my-subed-mpv-jump-to-25%) (define-key map (kbd "M-K") 'my-subed-mpv-jump-to-50%) (define-key map (kbd "M-L") 'my-subed-mpv-jump-to-75%) (define-key map (kbd "M-:") 'my-subed-mpv-jump-to-90%) ;; Increase and decrease start time and stop time by 500ms (define-key map (kbd "M-s-[") 'my-subed-decrease-start-time-by-1000ms) (define-key map (kbd "M-s-]") 'my-subed-increase-start-time-by-1000ms) (define-key map (kbd "M-s-{") 'my-subed-decrease-stop-time-by-1000ms) (define-key map (kbd "M-s-}") 'my-subed-increase-stop-time-by-1000ms) ;; Increase and decrease start time and stop time by 1000ms (define-key map (kbd "s-[") 'my-subed-decrease-start-time-by-500ms) (define-key map (kbd "s-]") 'my-subed-increase-start-time-by-500ms) (define-key map (kbd "s-{") 'my-subed-decrease-stop-time-by-500ms) (define-key map (kbd "s-}") 'my-subed-increase-stop-time-by-500ms) ;; Change playback speed (define-key map (kbd "M-0") 'my-subed-mpv-playback-speed-0.5) (define-key map (kbd "M-1") 'my-subed-mpv-playback-speed-1) (define-key map (kbd "M-2") 'my-subed-mpv-playback-speed-2) (define-key map (kbd "M-3") 'my-subed-mpv-playback-speed-3) (define-key map (kbd "M-4") 'my-subed-mpv-playback-speed-4) ;; Seek (define-key map (kbd "M-;") 'my-subed-mpv-seek-backward) (define-key map (kbd "M-'") 'my-subed-mpv-seek-forward) map)) (defun my-subed-set-fill-column-to-60 () ;; We set it to 60 because 2024-11-01T15:28:46+0000, https://emacsconf.org/captioning/ states: ;; ;; #+BEGIN_QUOTE ;; First, let's start with reflowing. We like to have one line of ;; captions about 60 characters long so that they'll display nicely ;; in the stream. ;; #+END_QUOTE (set-fill-column 60)) (defun my-subed-set-sentence-end () ;; We set sentence-end to period and comma so that we can jump to the ;; end of a sentence by using backward-sentence (by default bound to ;; M-a in global-map in GNU Emacs 29.4) and forward-sentence (by ;; default bound to M-e in global-map in GNU Emacs 29.4) ;; ;; We use setq-local to set sentence-end because the value of ;; sentence-end is used by other modes and we don't want to disrupt ;; them. For example, M-q calls fill-paragraph. fill-paragraph -> ;; fill-region -> fill-region-as-paragraph -> fill-delete-newlines ;; -> canonically-space-region -> sentence-end (the arrow "->" ;; denotes "calls"). The function sentence-end returns the value of ;; the variable sentence-end when set. (setq-local sentence-end "\\(\\.\\|,\\|\\?\\)")) (setq subed-mode-hook '(display-fill-column-indicator-mode subed-waveform-minor-mode subed-enable-replay-adjusted-subtitle subed-enable-loop-over-current-subtitle subed-enable-sync-player-to-point my-subed-set-fill-column-to-60 my-subed-set-sentence-end my-subed-custom-keys-minor-mode)) ;; 2024-11-05T23:43:19-0500: These variables have not yet merged to ;; development branch. See pull request: ;; https://github.com/sachac/subed/pull/78 (setq subed-waveform-image-width 800) (setq subed-waveform-image-height 100)