My subed customizations for editing captions of Emacsconf 2024

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)

Leave a Reply

Your email address will not be published. Required fields are marked *