!7 sclass VoiceOutput > DynTextArea { S voice; float volume = 1f; transient JComboBox cbAnswerMode; bool printStackTraces, showAnswerMode, mute; S switchableFields() { ret 'printStackTraces; } start { if (empty(voice)) setField(voice := first(cereprocVoices())); set cereproc_printPageLoads; dm_vmBus_answerToMessage('voiceOutputModule, func { dm_moduleID() }); } visualize { //bindComboBoxToLiveValue_debug.set(true); JComponent ta = super.visualize(); enableWordWrapForTextArea(textArea); //jPreemptEnterKey(textArea, rThread sayIt); onCtrlEnter(textArea, rThread sayIt); ret centerAndSouthWithMargins(ta, jvstackWithSpacing( centerAndEastWithMargin( centerAndEastWithMargin(withLabel("Voice:", jLiveValueComboBox(cereprocPlusLocalVoices(), dm_fieldLiveValue('voice))), jbutton("Say", rThread sayIt)), dm_checkBox('mute)), withLabel("Volume (Kevin only):", jLiveValueSlider(dm_fieldLiveValue('volume))), showAnswerMode ? jrightAlignedLine(jlabel("AI should answer (not really used yet):"), jComboBoxOnTrimmedFileContents(ll("Always", "Sometimes", "Never"), ai_answerModeFile(), ai_answerModeDefault())) : null)); } // API void say(S s) enter { { lock lock; setText(s); } if (printStackTraces) printStackTrace(); sayIt(); } void sayIt enter { S s = trim(getText()); if (empty(s)) ret; if (mute) ret with infoBox(s); if (empty(voice)) ret; for (fS line : tlft(s)) { if (isCereprocVoice(voice)) cereproc_silent(voice, line); // preload, then send message on bus flatInfoBox_topRightCorner(line); vmBus_send('talking, line, voice); afterwards { vmBus_send('doneTalking, line, voice); } new Matches m; if (isCereprocVoice(voice)) cereproc(voice, line); else if (swic(voice, "flite/", m)) flite_say(m.rest(), line); else if (eq(voice, 'Kevin)) { kevin_volume = volume; kevin(line); kevin_wait(); } else print("Missing voice: " + voice); } } void clear() { setText(""); } void setVoice(S voice) { setField(+voice); } void preload(S s) { temp enter(); if (isCereprocVoice(voice)) cereproc_preload(voice, s); } }