Recently Written · git

subrep-android

Subrep on Android: live captions of the sound of the phone, made on the phone by Whisper, shared by link.

git clone https://github.com/equwal/subrep-android

Log | Files | Refs


app/src/main/kotlin/com/honjimaku/subrep/Hallucinations.kt (2617 bytes)

1 package com.honjimaku.subrep
2 
3 /**
4  * Lines that Whisper invents over silence or noise: the credits of the subtitles it learned
5  * from. The list is that of the desktop app.
6  */
7 object Hallucinations {
8 
9     private val patterns = listOf(
10         // Finnish
11         "^tekstitys",
12         "^tekstityksen tuotti",
13         "^k[aä]ä?nn[oö]s",
14         "^kiitos( kun katsoit| paljon| katsomisesta)?[.!]?$",
15         "^suomennos",
16         // Japanese, which Whisper emits over silence all the time
17         "ご(視聴|清聴)(いただき)?ありがとうございま",
18         "チャンネル登録",
19         "^字幕",
20         "^おわり[。]?$",
21         "^(お|ご)?しまい[。]?$",
22         "^ありがとうございま(した|す)[。]?$",
23         "^(えー|あー|ん|う)+[ー。]?$",
24         // Russian
25         "^субтитры",
26         "^спасибо за просмотр",
27         "^продолжение следует",
28         "^редактор субтитров",
29         // English and generic
30         "^subtitles? by",
31         "^amara\\.org",
32         "^thanks? for watching",
33         "^thank you[.!]?$",
34         "^please subscribe",
35         // Marks, symbols and spaces only: no word in any script. (Java's \W is ASCII-only.)
36         "^[\\p{P}\\p{S}\\p{Z}\\s]*$",
37     ).map { Regex(it, RegexOption.IGNORE_CASE) }
38 
39     /**
40      * A piece repeated four times or more: "о-о-о-о-о-о", "はいはいはいはいはい", or a phrase
41      * of up to [LOOP_PIECE_MAX] characters. The lazy group finds the shortest piece.
42      */
43     private val loop = Regex("(.{1,$LOOP_PIECE_MAX}?)\\1{4,}")
44 
45     /** True when [text] is one of the invented lines, or has no word at all. */
46     fun isHallucination(text: String): Boolean {
47         val line = text.trim()
48         return patterns.any { it.containsMatchIn(line) }
49     }
50 
51     /**
52      * [text] with each loop cut. Whisper falls into a loop on singing or noise. A short piece
53      * (up to [SHORT_PIECE_MAX] characters) keeps three repeats, so that a real "no, no, no" stays.
54      * A phrase keeps one. The cut runs again until nothing changes, so a loop that a cut uncovers
55      * is cut too.
56      */
57     fun collapse(text: String): String {
58         var line = text
59         while (true) {
60             val cut = loop.replace(line) { match ->
61                 val piece = match.groupValues[1]
62                 if (piece.length <= SHORT_PIECE_MAX) piece.repeat(3) else piece
63             }
64             if (cut == line) return line
65             line = cut
66         }
67     }
68 
69     private const val LOOP_PIECE_MAX = 40
70     private const val SHORT_PIECE_MAX = 6
71 }