Recently Written · git

scan-subread

PDF reader that follows an audiobook with the SubRead suite

git clone https://github.com/equwal/scan-subread

Log | Files | Refs


src/follower.ts (2494 bytes)

1 // The reader follows the audio: which cue to mark, which page to show. Pure.
2 //
3 // At each step the follow compares the page of the audio (see audioPage
4 // in cue-pages.ts) with the page shown, and turns the page when they
5 // differ. A page turn by the user holds the follow: the page stays until
6 // the audio reaches it, the audio jumps, or the user asks to follow again.
7 
8 export type FollowMode = 'highlight' | 'pages' | 'off';
9 
10 /**
11  * Why the follow runs.
12  *
13  * - `tick`: a new state of the clock in the normal run.
14  * - `realign`: the alignment changed, because a page was read.
15  * - `seek`: the audio jumped. The player jumped, or the user moved the
16  *   audio from this app.
17  * - `follow`: the user asked to follow: the Follow button, or a new
18  *   follow mode.
19  */
20 export type FollowEvent = 'tick' | 'realign' | 'seek' | 'follow';
21 
22 export interface FollowInput {
23   mode: FollowMode;
24   currentPage: number;
25   /** The cue of now, or -1 before the first cue. */
26   cue: number;
27   /** True when the cue of now is matched: it has tokens on a page. */
28   matched: boolean;
29   /** The page of the audio now, or null when no page stands for the cue of now. */
30   page: number | null;
31   /** True while a page turn by the user holds the follow. */
32   held: boolean;
33   event: FollowEvent;
34 }
35 
36 export interface FollowOutput {
37   /** The cue to mark on the page, or null for no mark. */
38   highlightCue: number | null;
39   /** The page to show, or null to stay. */
40   turnToPage: number | null;
41   /** True when the follow stays held. */
42   held: boolean;
43 }
44 
45 /**
46  * One step of the follow.
47  *
48  * A seek or the Follow button ends the hold. The hold also ends when the
49  * audio is on the page shown. A tick or a new alignment does not end it.
50  * While the follow is held, the page does not turn. Else the page turns
51  * when the page of the audio is not the page shown. In `highlight` mode a
52  * matched cue is marked, also while the follow is held: the mark shows
53  * only on the page of the cue. `off` does nothing.
54  */
55 export function follow(input: FollowInput): FollowOutput {
56   const { mode, currentPage, cue, matched, page, event } = input;
57   let held = input.held && (event === 'tick' || event === 'realign');
58   if (mode === 'off' || cue < 0) return { highlightCue: null, turnToPage: null, held };
59   if (page === currentPage) held = false;
60   return {
61     highlightCue: mode === 'highlight' && matched ? cue : null,
62     turnToPage: !held && page !== null && page !== currentPage ? page : null,
63     held,
64   };
65 }