Skip to content

Commit

Permalink
Screenshot Mode
Browse files Browse the repository at this point in the history
  • Loading branch information
graphemecluster committed Aug 31, 2024
1 parent 135d611 commit 811edaf
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 31 deletions.
6 changes: 3 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ <h1 class="max-sm:col-span-2">香港圍頭話及客家話文字轉語音</h1>
<h2 class="group-hover:text-slate-700 group-hover:text-opacity-90 transition-[color]">香港<span class="text-[#d80818] group-hover:text-[#900612] group-hover:text-opacity-90 transition-[color]">本土語言</span>保育協會</h2>
</a>
</div>
<button id="btn-show" type="button" class="btn btn-ghost gap-1.5 hover:bg-opacity-10 max-sm:text-xl max-sm:font-normal max-sm:relative max-sm:left-4 sm:btn-lg sm:text-1.5xl sm:text-slate-500 sm:col-start-3 sm:row-start-1 sm:row-end-3">
<span class="icon-info"></span>關於
<button id="btn-show" type="button" class="btn btn-ghost gap-1.5 font-[650] hover:bg-opacity-10 max-sm:text-xl max-sm:font-normal max-sm:relative max-sm:left-4 sm:btn-lg sm:text-1.5xl sm:text-slate-500 sm:col-start-3 sm:row-start-1 sm:row-end-3">
<span class="icon-info mt-0.5"></span>關於
</button>
</header>
<main id="root"></main>
Expand All @@ -70,7 +70,7 @@ <h2 class="group-hover:text-slate-700 group-hover:text-opacity-90 transition-[co
<form method="dialog">
<button type="submit" class="btn btn-ghost w-14 h-14 min-h-14 text-4.5xl absolute right-3 top-3 text-slate-500 hover:bg-opacity-10" aria-label="關閉"><span class="icon-close"></span></button>
</form>
<h3 class="flex items-center gap-2 mx-6 mt-6 mb-4.5"><span class="icon-info"></span>關於</h3>
<h3 class="flex items-center gap-2 mx-6 mt-5.5 mb-5"><span class="icon-info mt-1"></span>關於</h3>
<hr />
<div class="flex-1 overflow-x-hidden overflow-y-auto">
<p>歡迎使用<b>香港圍頭話及客家話文字轉語音(Text-to-Speech)</b>朗讀器!</p>
Expand Down
40 changes: 29 additions & 11 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,21 @@ export default function App() {
}
}, [textArea, btnAddSentence]);

const [screenshotMode, setScreenshotMode] = useState(false);
const addSentence = useCallback(() => {
if (!textArea.current) return;
if (textArea.current.value === "_screenshot") {
setScreenshotMode(true);
setSentences([
{ language: "waitau", voice: "male", inferenceMode: "online", voiceSpeed: 1, syllables: segment("天地玄黃,宇宙洪荒。") },
{ language: "waitau", voice: "female", inferenceMode: "online", voiceSpeed: 1, syllables: segment("日月盈昃,辰宿列張。") },
{ language: "hakka", voice: "male", inferenceMode: "online", voiceSpeed: 1, syllables: segment("天地玄黃,宇宙洪荒。") },
{ language: "hakka", voice: "female", inferenceMode: "online", voiceSpeed: 1, syllables: segment("日月盈昃,辰宿列張。") },
]);
textArea.current.value = "";
resizeElements();
return;
}
setSentences([
...textArea.current.value.split("\n").flatMap(text => (text.trim() ? [{ language, voice, inferenceMode, voiceSpeed, syllables: segment(text) }] : [])),
...sentences,
Expand Down Expand Up @@ -83,12 +96,12 @@ export default function App() {
<div>
<div className="text-secondary text-lg font-semibold ms-0.5 mb-0.5 tracking-widest">聲線</div>
<div className="join bg-base-100" role="group" aria-label="選擇聲線">
<Radio name="btnvoice" className="btn-secondary" state={voice} setState={setVoice} value="male" />
<Radio name="btnvoice" className="btn-secondary" state={voice} setState={setVoice} value="female" />
<Radio name="btnvoice" className="btn-secondary" state={screenshotMode ? language === "waitau" ? "male" : "female" : voice} setState={setVoice} value="male" />
<Radio name="btnvoice" className="btn-secondary" state={screenshotMode ? language === "waitau" ? "male" : "female" : voice} setState={setVoice} value="female" />
</div>
</div>
<div>
<button type="button" className="btn btn-ghost max-sm:btn-sm max-sm:px-2.5 relative flex-col flex-nowrap gap-0 text-base whitespace-nowrap h-20 min-h-20 text-slate-500 hover:bg-opacity-10" onClick={() => setCurrSettingsDialogPage("settings")}>
<button type="button" className="btn btn-ghost max-sm:btn-sm max-sm:px-2.5 relative flex-col flex-nowrap gap-0 text-base whitespace-nowrap h-20 min-h-20 text-slate-500 font-extrabold hover:bg-opacity-10" onClick={() => setCurrSettingsDialogPage("settings")}>
{currInferenceModeDownloadState !== "latest" && <MdError size="1.5em" className={`absolute -top-1 -right-1 ${DOWNLOAD_STATUS_INDICATOR_CLASS[currInferenceModeDownloadState]}`} />}
<MdSettings size="2em" />
設定
Expand Down Expand Up @@ -123,14 +136,19 @@ export default function App() {
</div>
</div>
<div className="mt-5">
{sentences.map((sentence, i) => (
<SentenceCard
key={sentences.length - i}
sentence={sentence}
hakkaToneMode={hakkaToneMode}
setDownloadState={setDownloadState}
currSettingsDialogPage={currSettingsDialogPage}
setCurrSettingsDialogPage={setCurrSettingsDialogPage} />
{sentences.flatMap((sentence, i) => (
!screenshotMode || sentence.language === language
? [
<SentenceCard
key={sentences.length - i}
sentence={sentence}
hakkaToneMode={hakkaToneMode}
setDownloadState={setDownloadState}
currSettingsDialogPage={currSettingsDialogPage}
setCurrSettingsDialogPage={setCurrSettingsDialogPage}
screenshotMode={screenshotMode} />,
]
: []
))}
</div>
</div>
Expand Down
13 changes: 8 additions & 5 deletions src/AudioPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ export default function AudioPlayer({
setDownloadState,
currSettingsDialogPage,
setCurrSettingsDialogPage,
screenshotMode,
}: SentenceComponentState) {
const overrideSeekBar = screenshotMode && syllables[0][0] === "t";

useEffect(() => void context.resume(), []);
const [buffer, setBuffer] = useState<AudioBuffer | undefined>();
const [sourceNode, setSourceNode] = useState<AudioBufferSourceNode | undefined>();
Expand Down Expand Up @@ -84,7 +87,7 @@ export default function AudioPlayer({

useEffect(() => {
async function getDownloadComponents() {
if (inferenceMode === "online" || !db || downloadVersion || currSettingsDialogPage) return;
if (screenshotMode || inferenceMode === "online" || !db || downloadVersion || currSettingsDialogPage) return;
setDownloadVersion(undefined);
setDownloadError(undefined);
setBuffer(undefined);
Expand All @@ -107,7 +110,7 @@ export default function AudioPlayer({
const [generationRetryCounter, generationRetry] = useReducer((n: number) => n + 1, 0);
const text = syllables.join(" ");
useEffect(() => {
if (inferenceMode !== "online" && !downloadVersion) return;
if (screenshotMode || inferenceMode !== "online" && !downloadVersion) return;
async function generateAudio() {
const key = `${inferenceMode}/${voiceSpeed}/${downloadVersion}/${language}/${voice}`;
let textToBuffer = audioCache.get(key);
Expand Down Expand Up @@ -225,14 +228,14 @@ export default function AudioPlayer({
onClick={isPlaying === false ? playAudio : pauseAudio}
aria-label={isPlaying === false ? "播放" : "暫停"}
tabIndex={buffer ? 0 : -1}>
{isPlaying === false ? <MdPlayArrow /> : <MdPause />}
{!overrideSeekBar && isPlaying === false ? <MdPlayArrow /> : <MdPause />}
</button>
<input
type="range"
className="range range-warning range-sm grow mx-3 sm:mx-4"
min={0}
max={1}
value={progress}
value={overrideSeekBar ? 0.36 : progress}
step="any"
{...NO_AUTO_FILL}
onMouseDown={seekBarDown}
Expand All @@ -250,7 +253,7 @@ export default function AudioPlayer({
tabIndex={buffer ? 0 : -1}>
<MdStop />
</button>
{(error || !buffer) && <div className={`absolute inset-0 flex items-center justify-center ${error ? "bg-gray-300 bg-opacity-50 text-error" : "bg-gray-500 bg-opacity-20"} rounded-lg text-xl`}>
{!screenshotMode && (error || !buffer) && <div className={`absolute inset-0 flex items-center justify-center ${error ? "bg-gray-300 bg-opacity-50 text-error" : "bg-gray-500 bg-opacity-20"} rounded-lg text-xl`}>
{error
? <div>
<MdErrorOutline size="1.1875em" className="inline align-middle mt-0.5 mr-1" />
Expand Down
12 changes: 7 additions & 5 deletions src/SentenceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export default function SentenceCard({
setDownloadState,
currSettingsDialogPage,
setCurrSettingsDialogPage,
screenshotMode,
}: SentenceCardProps) {
const [enabledEdges, setEnabledEdges] = useState(new Set<Edge>());
const edges = useMemo(() => {
Expand Down Expand Up @@ -172,7 +173,7 @@ export default function SentenceCard({
const chars: JSX.Element[] = [];
while (i < edgesInGroup.end) {
chars.push(
<ruby key={i} className="px-2 py-1">
<ruby key={i} className="p-1">
{syllables[i]}
<rt>{flattenedProns[i].length > 1 ? replaceHakkaTones(language, hakkaToneMode, flattenedProns[i]) : "\xa0"}</rt>
</ruby>,
Expand Down Expand Up @@ -235,16 +236,17 @@ export default function SentenceCard({
<div className="join">
<span className="badge badge-primary join-item">{TERMINOLOGY[language]}</span>
<span className="badge badge-secondary join-item">{TERMINOLOGY[voice]}</span>
<span className="badge badge-accent join-item">{INFERENCE_MODE_TO_LABEL[inferenceMode]}</span>
<span className="badge badge-info join-item">{voiceSpeed}×</span>
{!screenshotMode && <span className="badge badge-accent join-item">{INFERENCE_MODE_TO_LABEL[inferenceMode]}</span>}
{!screenshotMode && <span className="badge badge-info join-item">{voiceSpeed}×</span>}
</div>
<SentenceCopy syllables={syllables} prons={flattenedProns} />
<div className="text-2.5xl/none sm:text-4xl mt-1">{tables}</div>
<div className={`text-2.5xl/none sm:text-4xl mt-1${screenshotMode ? " whitespace-nowrap" : ""}`}>{tables}</div>
<AudioPlayer
sentence={{ language, voice, inferenceMode, voiceSpeed, syllables: inferenceMode === "lightweight" ? enabledEdgesProns : flattenedProns }}
setDownloadState={setDownloadState}
currSettingsDialogPage={currSettingsDialogPage}
setCurrSettingsDialogPage={setCurrSettingsDialogPage} />
setCurrSettingsDialogPage={setCurrSettingsDialogPage}
screenshotMode={screenshotMode} />
</div>
</div>;
}
6 changes: 3 additions & 3 deletions src/SettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ const SettingsDialog = forwardRef<HTMLDialogElement, SettingDialogProps>(functio
</span>
</button>
</form>
{currSettingsDialogPage && <h3 className="flex items-center gap-2 mx-6 mt-6 mb-4.5">
{currSettingsDialogPage && <h3 className="flex items-center gap-2 mx-6 mt-5.5 mb-5">
{currSettingsDialogPage === "settings"
? <>
<MdSettings size="1.125em" />設定
<MdSettings size="1.125em" className="mt-1" />設定
</>
: <>
<button type="button" className="btn btn-ghost w-14 h-14 min-h-14 text-4.5xl -ml-3 -mr-2.5 -my-7 text-slate-500 hover:bg-opacity-10" aria-label="返回" onClick={() => setCurrSettingsDialogPage("settings")}>
Expand Down Expand Up @@ -128,8 +128,8 @@ const SettingsDialog = forwardRef<HTMLDialogElement, SettingDialogProps>(functio
</div>
<div className="flex-1 flex flex-col gap-1 text-xl font-medium">客家話標調方式</div>
<div className="join" role="group">
<Radio name="hakkaToneMode" className="btn-primary h-10 min-h-10" state={hakkaToneMode} setState={setHakkaToneMode} value="digits" />
<Radio name="hakkaToneMode" className="btn-primary h-10 min-h-10" state={hakkaToneMode} setState={setHakkaToneMode} value="diacritics" />
<Radio name="hakkaToneMode" className="btn-primary h-10 min-h-10" state={hakkaToneMode} setState={setHakkaToneMode} value="digits" />
</div>
</li>
<CopyURLRow urlWithQuery={urlWithQuery} />
Expand Down
4 changes: 2 additions & 2 deletions src/consts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export const TERMINOLOGY: Record<Terminology, string> = {
hakka: "客家話",
male: "男聲",
female: "女聲",
digits: "數字",
diacritics: "調號",
digits: "數字",
};

export const VOICE_TO_ICON: Record<Voice, JSX.Element> = {
Expand Down Expand Up @@ -39,7 +39,7 @@ export const ALL_VOICES: readonly Voice[] = ["male", "female"];
export const ALL_INFERENCE_MODES: readonly InferenceMode[] = ["online", "offline", "lightweight"];
export { ALL_MODEL_COMPONENTS } from "./inference/infer";
export const ALL_AUDIO_COMPONENTS: readonly AudioComponent[] = ["chars", "words"];
export const ALL_HAKKA_TONE_MODES: readonly HakkaToneMode[] = ["digits", "diacritics"];
export const ALL_HAKKA_TONE_MODES: readonly HakkaToneMode[] = ["diacritics", "digits"];

export const MODEL_COMPONENT_TO_N_CHUNKS: Record<ModelComponent, number> = {
enc: 2,
Expand Down
3 changes: 3 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
a {
@apply link link-primary no-underline;
}
b {
@apply font-extrabold;
}
ruby {
@apply inline-flex flex-col-reverse items-center gap-0.5 align-bottom;
}
Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export interface Sentence {
export type OfflineInferenceMode = "offline" | "lightweight";
export type InferenceMode = "online" | OfflineInferenceMode;

export type HakkaToneMode = "digits" | "diacritics";
export type HakkaToneMode = "diacritics" | "digits";

export type ModelComponent = "enc" | "emb" | "sdp" | "flow" | "dec";

Expand Down Expand Up @@ -163,6 +163,7 @@ export interface SettingsDialogState {

export interface SentenceComponentState extends SetDownloadStatus, SettingsDialogState {
sentence: Sentence;
screenshotMode: boolean;
}

export interface Actions {
Expand Down
2 changes: 1 addition & 1 deletion tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default {
extend: {
fontFamily: {
serif: [
"Chiron Sung HK",
// "Chiron Sung HK",
"Chiron Sung HK WS",
"Times New Roman",
"Times",
Expand Down

0 comments on commit 811edaf

Please sign in to comment.