Remove build artifacts, dev tool configs, unused dependencies, and third-party source dumps. Add proper README, update .gitignore, clean up Makefile.
271 lines
7.8 KiB
Java
271 lines
7.8 KiB
Java
package com.tiedup.remake.dialogue;
|
|
|
|
import java.util.Set;
|
|
import java.util.regex.Pattern;
|
|
|
|
/**
|
|
* Detects and applies emotional context to gagged speech.
|
|
*/
|
|
public class EmotionalContext {
|
|
|
|
/**
|
|
* Types of emotional expression that affect speech transformation.
|
|
*/
|
|
public enum EmotionType {
|
|
NORMAL,
|
|
SHOUTING,
|
|
WHISPERING,
|
|
QUESTIONING,
|
|
PLEADING,
|
|
DISTRESSED,
|
|
LAUGHING,
|
|
CRYING,
|
|
}
|
|
|
|
// Pattern for extended vowels (nooooo, heeelp, pleeease)
|
|
private static final Pattern EXTENDED_VOWELS = Pattern.compile(
|
|
"([aeiou])\\1{2,}",
|
|
Pattern.CASE_INSENSITIVE
|
|
);
|
|
|
|
// Pattern for laughing (haha, hehe, lol)
|
|
private static final Pattern LAUGHING_PATTERN = Pattern.compile(
|
|
"(ha){2,}|(he){2,}|(hi){2,}|lol|lmao|xd",
|
|
Pattern.CASE_INSENSITIVE
|
|
);
|
|
|
|
// Pattern for crying indicators
|
|
private static final Pattern CRYING_PATTERN = Pattern.compile(
|
|
"\\*(?:sob|cry|sniff|whimper)s?\\*|;-?;|T[-_]T|:'+\\(",
|
|
Pattern.CASE_INSENSITIVE
|
|
);
|
|
|
|
// Pleading keywords
|
|
private static final Set<String> PLEADING_WORDS = Set.of(
|
|
"please",
|
|
"help",
|
|
"stop",
|
|
"no",
|
|
"don't",
|
|
"dont",
|
|
"wait",
|
|
"s'il vous plait",
|
|
"svp",
|
|
"aide",
|
|
"aidez",
|
|
"arrete",
|
|
"arreter",
|
|
"non",
|
|
"mercy",
|
|
"spare",
|
|
"let me go",
|
|
"release"
|
|
);
|
|
|
|
// Whispering indicators
|
|
private static final Set<String> WHISPERING_INDICATORS = Set.of(
|
|
"*whisper*",
|
|
"*whispers*",
|
|
"*quietly*",
|
|
"*softly*",
|
|
"(whisper)",
|
|
"(quietly)"
|
|
);
|
|
|
|
/**
|
|
* Detect the emotional context of a message or word.
|
|
*
|
|
* @param text The text to analyze
|
|
* @return The detected emotion type
|
|
*/
|
|
public static EmotionType detectEmotion(String text) {
|
|
if (text == null || text.isEmpty()) {
|
|
return EmotionType.NORMAL;
|
|
}
|
|
|
|
String lower = text.toLowerCase();
|
|
|
|
// Check for explicit emotional markers first
|
|
if (CRYING_PATTERN.matcher(text).find()) {
|
|
return EmotionType.CRYING;
|
|
}
|
|
|
|
if (LAUGHING_PATTERN.matcher(text).find()) {
|
|
return EmotionType.LAUGHING;
|
|
}
|
|
|
|
// Check for whispering indicators
|
|
for (String indicator : WHISPERING_INDICATORS) {
|
|
if (lower.contains(indicator)) {
|
|
return EmotionType.WHISPERING;
|
|
}
|
|
}
|
|
|
|
// Check for shouting (ALL CAPS with 3+ letters, or multiple !)
|
|
String letters = text.replaceAll("[^a-zA-Z]", "");
|
|
if (letters.length() >= 3 && letters.equals(letters.toUpperCase())) {
|
|
return EmotionType.SHOUTING;
|
|
}
|
|
// Require at least 2 exclamation marks for shouting
|
|
if (text.contains("!!") || text.contains("!?") || text.contains("?!")) {
|
|
return EmotionType.SHOUTING;
|
|
}
|
|
|
|
// Check for questioning
|
|
if (text.endsWith("?") || text.endsWith("??")) {
|
|
return EmotionType.QUESTIONING;
|
|
}
|
|
|
|
// Check for distress (extended vowels: nooooo, heeelp)
|
|
if (EXTENDED_VOWELS.matcher(text).find()) {
|
|
return EmotionType.DISTRESSED;
|
|
}
|
|
|
|
// Check for pleading keywords
|
|
for (String word : PLEADING_WORDS) {
|
|
if (lower.contains(word)) {
|
|
return EmotionType.PLEADING;
|
|
}
|
|
}
|
|
|
|
return EmotionType.NORMAL;
|
|
}
|
|
|
|
/**
|
|
* Apply emotional modifiers to a muffled message.
|
|
*
|
|
* @param muffled The muffled text
|
|
* @param emotion The detected emotion type
|
|
* @return Modified text with emotional expression
|
|
*/
|
|
public static String applyEmotionalModifiers(
|
|
String muffled,
|
|
EmotionType emotion
|
|
) {
|
|
if (muffled == null || muffled.isEmpty()) {
|
|
return muffled;
|
|
}
|
|
|
|
switch (emotion) {
|
|
case SHOUTING:
|
|
// Convert to uppercase and emphasize
|
|
return (
|
|
muffled.toUpperCase() + (muffled.endsWith("!") ? "!" : "!!")
|
|
);
|
|
case WHISPERING:
|
|
// Softer, shorter sounds
|
|
return "*" + muffled.toLowerCase() + "*";
|
|
case QUESTIONING:
|
|
// Rising intonation
|
|
String question = muffled.endsWith("?")
|
|
? muffled
|
|
: muffled + "?";
|
|
return question.replace("mm", "mn").replace("nn", "nh");
|
|
case PLEADING:
|
|
// Add whimpering
|
|
return muffled + "-mm..";
|
|
case DISTRESSED:
|
|
// Extend the dominant sound
|
|
return extendDominantSound(muffled);
|
|
case LAUGHING:
|
|
// Muffled laughter
|
|
return muffled.replace("mm", "hm").replace("nn", "hn") + "-hm";
|
|
case CRYING:
|
|
// Add sobbing sounds
|
|
return "*hic* " + muffled + " *mm*";
|
|
case NORMAL:
|
|
default:
|
|
return muffled;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extend the dominant/repeated sound in a muffled word for distress effect.
|
|
*/
|
|
private static String extendDominantSound(String text) {
|
|
if (text == null || text.length() < 2) {
|
|
return text;
|
|
}
|
|
|
|
StringBuilder result = new StringBuilder();
|
|
char prev = 0;
|
|
int repeatCount = 0;
|
|
|
|
for (int i = 0; i < text.length(); i++) {
|
|
char c = text.charAt(i);
|
|
|
|
if (c == prev && Character.isLetter(c)) {
|
|
repeatCount++;
|
|
// Extend repeated sounds
|
|
if (repeatCount <= 3) {
|
|
result.append(c);
|
|
}
|
|
} else {
|
|
result.append(c);
|
|
repeatCount = 1;
|
|
}
|
|
prev = c;
|
|
}
|
|
|
|
// If no extension happened, extend the last vowel-like sound
|
|
String str = result.toString();
|
|
if (str.equals(text)) {
|
|
// Find last 'm' or 'n' or vowel and extend it
|
|
int lastExtendable = -1;
|
|
for (int i = str.length() - 1; i >= 0; i--) {
|
|
char c = Character.toLowerCase(str.charAt(i));
|
|
if (c == 'm' || c == 'n' || c == 'a' || c == 'o' || c == 'u') {
|
|
lastExtendable = i;
|
|
break;
|
|
}
|
|
}
|
|
if (lastExtendable >= 0) {
|
|
char ext = str.charAt(lastExtendable);
|
|
return (
|
|
str.substring(0, lastExtendable + 1) +
|
|
ext +
|
|
ext +
|
|
str.substring(lastExtendable + 1)
|
|
);
|
|
}
|
|
}
|
|
|
|
return result.toString();
|
|
}
|
|
|
|
/**
|
|
* Get the intensity multiplier for an emotion.
|
|
* Higher intensity = more muffling effect.
|
|
*
|
|
* @param emotion The emotion type
|
|
* @return Intensity multiplier (1.0 = normal)
|
|
*/
|
|
public static float getIntensityMultiplier(EmotionType emotion) {
|
|
switch (emotion) {
|
|
case SHOUTING:
|
|
return 1.3f; // Harder to understand when shouting
|
|
case WHISPERING:
|
|
return 0.7f; // Easier (softer, clearer)
|
|
case DISTRESSED:
|
|
return 1.2f;
|
|
case CRYING:
|
|
return 1.4f;
|
|
case LAUGHING:
|
|
return 1.1f;
|
|
case PLEADING:
|
|
case QUESTIONING:
|
|
case NORMAL:
|
|
default:
|
|
return 1.0f;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if the emotion should preserve more of the original text.
|
|
* Some emotions (like whispering) are clearer.
|
|
*/
|
|
public static boolean shouldPreserveMore(EmotionType emotion) {
|
|
return emotion == EmotionType.WHISPERING;
|
|
}
|
|
}
|