Remove build artifacts, dev tool configs, unused dependencies, and third-party source dumps. Add proper README, update .gitignore, clean up Makefile.
508 lines
20 KiB
Java
508 lines
20 KiB
Java
package com.tiedup.remake.util;
|
|
|
|
import java.util.EnumMap;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Random;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* Phonetic transformation system for gagged speech.
|
|
* Maps original phonemes to muffled equivalents based on gag material.
|
|
*/
|
|
public class PhoneticMapper {
|
|
|
|
private static final Random RANDOM = new Random();
|
|
|
|
// Phonetic categories
|
|
private static final Set<Character> VOWELS = Set.of(
|
|
'a',
|
|
'e',
|
|
'i',
|
|
'o',
|
|
'u',
|
|
'y'
|
|
);
|
|
private static final Set<Character> PLOSIVES = Set.of(
|
|
'b',
|
|
'd',
|
|
'g',
|
|
'k',
|
|
'p',
|
|
't'
|
|
);
|
|
private static final Set<Character> NASALS = Set.of('m', 'n');
|
|
private static final Set<Character> FRICATIVES = Set.of(
|
|
'f',
|
|
'h',
|
|
's',
|
|
'v',
|
|
'z'
|
|
);
|
|
private static final Set<Character> LIQUIDS = Set.of('l', 'r');
|
|
|
|
// Material-specific phoneme mappings
|
|
private static final Map<
|
|
GagMaterial,
|
|
Map<Character, String[]>
|
|
> CONSONANT_MAPS = new EnumMap<>(GagMaterial.class);
|
|
private static final Map<GagMaterial, Map<Character, String[]>> VOWEL_MAPS =
|
|
new EnumMap<>(GagMaterial.class);
|
|
|
|
static {
|
|
initializeClothMappings();
|
|
initializeBallMappings();
|
|
initializeTapeMappings();
|
|
initializeStuffedMappings();
|
|
initializePanelMappings();
|
|
initializeLatexMappings();
|
|
initializeRingMappings();
|
|
initializeBiteMappings();
|
|
initializeSpongeMappings();
|
|
initializeBaguetteMappings();
|
|
}
|
|
|
|
private static void initializeClothMappings() {
|
|
Map<Character, String[]> consonants = new HashMap<>();
|
|
consonants.put('b', new String[] { "m", "mph" });
|
|
consonants.put('c', new String[] { "h", "kh" });
|
|
consonants.put('d', new String[] { "n", "nd" });
|
|
consonants.put('f', new String[] { "f", "ph" });
|
|
consonants.put('g', new String[] { "ng", "gh" });
|
|
consonants.put('h', new String[] { "h", "hh" });
|
|
consonants.put('j', new String[] { "zh", "jh" });
|
|
consonants.put('k', new String[] { "kh", "gh" });
|
|
consonants.put('l', new String[] { "l", "hl" });
|
|
consonants.put('m', new String[] { "m", "mm" });
|
|
consonants.put('n', new String[] { "n", "nn" });
|
|
consonants.put('p', new String[] { "mph", "m" });
|
|
consonants.put('q', new String[] { "kh", "gh" });
|
|
consonants.put('r', new String[] { "r", "hr" });
|
|
consonants.put('s', new String[] { "s", "sh" });
|
|
consonants.put('t', new String[] { "th", "n" });
|
|
consonants.put('v', new String[] { "f", "vh" });
|
|
consonants.put('w', new String[] { "wh", "u" });
|
|
consonants.put('x', new String[] { "ks", "kh" });
|
|
consonants.put('z', new String[] { "z", "s" });
|
|
CONSONANT_MAPS.put(GagMaterial.CLOTH, consonants);
|
|
|
|
Map<Character, String[]> vowels = new HashMap<>();
|
|
vowels.put('a', new String[] { "ah", "a" });
|
|
vowels.put('e', new String[] { "eh", "e" });
|
|
vowels.put('i', new String[] { "ih", "e" });
|
|
vowels.put('o', new String[] { "oh", "o" });
|
|
vowels.put('u', new String[] { "uh", "u" });
|
|
vowels.put('y', new String[] { "ih", "e" });
|
|
VOWEL_MAPS.put(GagMaterial.CLOTH, vowels);
|
|
}
|
|
|
|
private static void initializeBallMappings() {
|
|
Map<Character, String[]> consonants = new HashMap<>();
|
|
// Ball gag forces mouth open around ball - tongue and lips blocked
|
|
consonants.put('b', new String[] { "m", "mm" });
|
|
consonants.put('c', new String[] { "g", "kh" });
|
|
consonants.put('d', new String[] { "n", "nn" });
|
|
consonants.put('f', new String[] { "h", "hh" });
|
|
consonants.put('g', new String[] { "ng", "g" });
|
|
consonants.put('h', new String[] { "h", "hh" });
|
|
consonants.put('j', new String[] { "g", "ng" });
|
|
consonants.put('k', new String[] { "g", "gh" });
|
|
consonants.put('l', new String[] { "u", "l" });
|
|
consonants.put('m', new String[] { "m", "mm" });
|
|
consonants.put('n', new String[] { "n", "nn" });
|
|
consonants.put('p', new String[] { "m", "mm" });
|
|
consonants.put('q', new String[] { "g", "gh" });
|
|
consonants.put('r', new String[] { "u", "r" });
|
|
consonants.put('s', new String[] { "h", "s" });
|
|
consonants.put('t', new String[] { "n", "nn" });
|
|
consonants.put('v', new String[] { "h", "f" });
|
|
consonants.put('w', new String[] { "u", "w" });
|
|
consonants.put('x', new String[] { "g", "ks" });
|
|
consonants.put('z', new String[] { "s", "z" });
|
|
CONSONANT_MAPS.put(GagMaterial.BALL, consonants);
|
|
|
|
Map<Character, String[]> vowels = new HashMap<>();
|
|
// Ball forces all vowels toward "oo/uu" (rounded)
|
|
vowels.put('a', new String[] { "a", "o" });
|
|
vowels.put('e', new String[] { "u", "o" });
|
|
vowels.put('i', new String[] { "u", "i" });
|
|
vowels.put('o', new String[] { "o", "oo" });
|
|
vowels.put('u', new String[] { "u", "uu" });
|
|
vowels.put('y', new String[] { "u", "i" });
|
|
VOWEL_MAPS.put(GagMaterial.BALL, vowels);
|
|
}
|
|
|
|
private static void initializeTapeMappings() {
|
|
Map<Character, String[]> consonants = new HashMap<>();
|
|
// Tape seals mouth almost completely - only nasal sounds
|
|
consonants.put('b', new String[] { "m", "mm" });
|
|
consonants.put('c', new String[] { "n", "m" });
|
|
consonants.put('d', new String[] { "n", "nn" });
|
|
consonants.put('f', new String[] { "m", "hm" });
|
|
consonants.put('g', new String[] { "n", "ng" });
|
|
consonants.put('h', new String[] { "m", "hm" });
|
|
consonants.put('j', new String[] { "n", "m" });
|
|
consonants.put('k', new String[] { "n", "m" });
|
|
consonants.put('l', new String[] { "n", "m" });
|
|
consonants.put('m', new String[] { "m", "mm" });
|
|
consonants.put('n', new String[] { "n", "nn" });
|
|
consonants.put('p', new String[] { "m", "mm" });
|
|
consonants.put('q', new String[] { "n", "m" });
|
|
consonants.put('r', new String[] { "n", "m" });
|
|
consonants.put('s', new String[] { "m", "s" });
|
|
consonants.put('t', new String[] { "n", "nn" });
|
|
consonants.put('v', new String[] { "m", "f" });
|
|
consonants.put('w', new String[] { "m", "u" });
|
|
consonants.put('x', new String[] { "n", "m" });
|
|
consonants.put('z', new String[] { "n", "m" });
|
|
CONSONANT_MAPS.put(GagMaterial.TAPE, consonants);
|
|
|
|
Map<Character, String[]> vowels = new HashMap<>();
|
|
// Tape muffles all vowels to near silence
|
|
vowels.put('a', new String[] { "m", "mm" });
|
|
vowels.put('e', new String[] { "n", "m" });
|
|
vowels.put('i', new String[] { "n", "m" });
|
|
vowels.put('o', new String[] { "m", "mm" });
|
|
vowels.put('u', new String[] { "m", "u" });
|
|
vowels.put('y', new String[] { "n", "m" });
|
|
VOWEL_MAPS.put(GagMaterial.TAPE, vowels);
|
|
}
|
|
|
|
private static void initializeStuffedMappings() {
|
|
Map<Character, String[]> consonants = new HashMap<>();
|
|
// Stuffed gag - nearly silent
|
|
for (char c = 'a'; c <= 'z'; c++) {
|
|
if (!VOWELS.contains(c)) {
|
|
consonants.put(c, new String[] { "mm", "m", "" });
|
|
}
|
|
}
|
|
CONSONANT_MAPS.put(GagMaterial.STUFFED, consonants);
|
|
|
|
Map<Character, String[]> vowels = new HashMap<>();
|
|
for (char c : VOWELS) {
|
|
vowels.put(c, new String[] { "mm", "m", "" });
|
|
}
|
|
VOWEL_MAPS.put(GagMaterial.STUFFED, vowels);
|
|
}
|
|
|
|
private static void initializePanelMappings() {
|
|
// Panel gag - similar to tape but slightly more sound
|
|
Map<Character, String[]> consonants = new HashMap<>();
|
|
consonants.put('b', new String[] { "m", "mph" });
|
|
consonants.put('c', new String[] { "n", "m" });
|
|
consonants.put('d', new String[] { "n", "nd" });
|
|
consonants.put('f', new String[] { "m", "f" });
|
|
consonants.put('g', new String[] { "n", "ng" });
|
|
consonants.put('h', new String[] { "hm", "m" });
|
|
consonants.put('j', new String[] { "n", "m" });
|
|
consonants.put('k', new String[] { "n", "m" });
|
|
consonants.put('l', new String[] { "n", "m" });
|
|
consonants.put('m', new String[] { "m", "mm" });
|
|
consonants.put('n', new String[] { "n", "nn" });
|
|
consonants.put('p', new String[] { "m", "mph" });
|
|
consonants.put('q', new String[] { "n", "m" });
|
|
consonants.put('r', new String[] { "n", "m" });
|
|
consonants.put('s', new String[] { "s", "m" });
|
|
consonants.put('t', new String[] { "n", "m" });
|
|
consonants.put('v', new String[] { "m", "f" });
|
|
consonants.put('w', new String[] { "m", "u" });
|
|
consonants.put('x', new String[] { "n", "m" });
|
|
consonants.put('z', new String[] { "n", "m" });
|
|
CONSONANT_MAPS.put(GagMaterial.PANEL, consonants);
|
|
|
|
Map<Character, String[]> vowels = new HashMap<>();
|
|
vowels.put('a', new String[] { "m", "ah" });
|
|
vowels.put('e', new String[] { "n", "m" });
|
|
vowels.put('i', new String[] { "n", "m" });
|
|
vowels.put('o', new String[] { "m", "oh" });
|
|
vowels.put('u', new String[] { "m", "u" });
|
|
vowels.put('y', new String[] { "n", "m" });
|
|
VOWEL_MAPS.put(GagMaterial.PANEL, vowels);
|
|
}
|
|
|
|
private static void initializeLatexMappings() {
|
|
Map<Character, String[]> consonants = new HashMap<>();
|
|
// Latex - tight seal, rubber sounds
|
|
consonants.put('b', new String[] { "m", "mm" });
|
|
consonants.put('c', new String[] { "n", "m" });
|
|
consonants.put('d', new String[] { "n", "nn" });
|
|
consonants.put('f', new String[] { "h", "f" });
|
|
consonants.put('g', new String[] { "ng", "m" });
|
|
consonants.put('h', new String[] { "h", "hh" });
|
|
consonants.put('j', new String[] { "n", "m" });
|
|
consonants.put('k', new String[] { "n", "m" });
|
|
consonants.put('l', new String[] { "n", "m" });
|
|
consonants.put('m', new String[] { "m", "mm" });
|
|
consonants.put('n', new String[] { "n", "nn" });
|
|
consonants.put('p', new String[] { "m", "mm" });
|
|
consonants.put('q', new String[] { "n", "m" });
|
|
consonants.put('r', new String[] { "n", "m" });
|
|
consonants.put('s', new String[] { "s", "h" });
|
|
consonants.put('t', new String[] { "n", "nn" });
|
|
consonants.put('v', new String[] { "f", "m" });
|
|
consonants.put('w', new String[] { "m", "u" });
|
|
consonants.put('x', new String[] { "n", "m" });
|
|
consonants.put('z', new String[] { "s", "m" });
|
|
CONSONANT_MAPS.put(GagMaterial.LATEX, consonants);
|
|
|
|
Map<Character, String[]> vowels = new HashMap<>();
|
|
vowels.put('a', new String[] { "u", "a" });
|
|
vowels.put('e', new String[] { "u", "e" });
|
|
vowels.put('i', new String[] { "u", "i" });
|
|
vowels.put('o', new String[] { "u", "o" });
|
|
vowels.put('u', new String[] { "u", "uu" });
|
|
vowels.put('y', new String[] { "u", "i" });
|
|
VOWEL_MAPS.put(GagMaterial.LATEX, vowels);
|
|
}
|
|
|
|
private static void initializeRingMappings() {
|
|
Map<Character, String[]> consonants = new HashMap<>();
|
|
// Ring gag - mouth forced open, tongue partially free
|
|
consonants.put('b', new String[] { "b", "bh" });
|
|
consonants.put('c', new String[] { "k", "kh" });
|
|
consonants.put('d', new String[] { "d", "dh" });
|
|
consonants.put('f', new String[] { "f", "fh" });
|
|
consonants.put('g', new String[] { "g", "gh" });
|
|
consonants.put('h', new String[] { "h", "ah" });
|
|
consonants.put('j', new String[] { "j", "zh" });
|
|
consonants.put('k', new String[] { "k", "kh" });
|
|
consonants.put('l', new String[] { "l", "lh" });
|
|
consonants.put('m', new String[] { "m", "mh" });
|
|
consonants.put('n', new String[] { "n", "nh" });
|
|
consonants.put('p', new String[] { "p", "ph" });
|
|
consonants.put('q', new String[] { "k", "kh" });
|
|
consonants.put('r', new String[] { "r", "rh" });
|
|
consonants.put('s', new String[] { "s", "sh" });
|
|
consonants.put('t', new String[] { "t", "th" });
|
|
consonants.put('v', new String[] { "v", "vh" });
|
|
consonants.put('w', new String[] { "w", "wh" });
|
|
consonants.put('x', new String[] { "ks", "kh" });
|
|
consonants.put('z', new String[] { "z", "zh" });
|
|
CONSONANT_MAPS.put(GagMaterial.RING, consonants);
|
|
|
|
Map<Character, String[]> vowels = new HashMap<>();
|
|
// Ring forces mouth open - vowels become "a/ah"
|
|
vowels.put('a', new String[] { "a", "ah" });
|
|
vowels.put('e', new String[] { "eh", "a" });
|
|
vowels.put('i', new String[] { "ih", "a" });
|
|
vowels.put('o', new String[] { "oh", "a" });
|
|
vowels.put('u', new String[] { "uh", "a" });
|
|
vowels.put('y', new String[] { "ih", "a" });
|
|
VOWEL_MAPS.put(GagMaterial.RING, vowels);
|
|
}
|
|
|
|
private static void initializeBiteMappings() {
|
|
Map<Character, String[]> consonants = new HashMap<>();
|
|
// Bite gag - teeth clenched on bar
|
|
consonants.put('b', new String[] { "bh", "ph" });
|
|
consonants.put('c', new String[] { "kh", "gh" });
|
|
consonants.put('d', new String[] { "dh", "th" });
|
|
consonants.put('f', new String[] { "fh", "f" });
|
|
consonants.put('g', new String[] { "gh", "ng" });
|
|
consonants.put('h', new String[] { "h", "hh" });
|
|
consonants.put('j', new String[] { "jh", "zh" });
|
|
consonants.put('k', new String[] { "kh", "gh" });
|
|
consonants.put('l', new String[] { "lh", "hl" });
|
|
consonants.put('m', new String[] { "m", "mh" });
|
|
consonants.put('n', new String[] { "n", "nh" });
|
|
consonants.put('p', new String[] { "ph", "bh" });
|
|
consonants.put('q', new String[] { "kh", "gh" });
|
|
consonants.put('r', new String[] { "rh", "hr" });
|
|
consonants.put('s', new String[] { "sh", "s" });
|
|
consonants.put('t', new String[] { "th", "dh" });
|
|
consonants.put('v', new String[] { "vh", "fh" });
|
|
consonants.put('w', new String[] { "wh", "uh" });
|
|
consonants.put('x', new String[] { "ksh", "kh" });
|
|
consonants.put('z', new String[] { "zh", "sh" });
|
|
CONSONANT_MAPS.put(GagMaterial.BITE, consonants);
|
|
|
|
Map<Character, String[]> vowels = new HashMap<>();
|
|
vowels.put('a', new String[] { "eh", "ah" });
|
|
vowels.put('e', new String[] { "eh", "e" });
|
|
vowels.put('i', new String[] { "ih", "eh" });
|
|
vowels.put('o', new String[] { "oh", "eh" });
|
|
vowels.put('u', new String[] { "uh", "eh" });
|
|
vowels.put('y', new String[] { "ih", "eh" });
|
|
VOWEL_MAPS.put(GagMaterial.BITE, vowels);
|
|
}
|
|
|
|
private static void initializeSpongeMappings() {
|
|
// Sponge - absorbs almost all sound
|
|
Map<Character, String[]> consonants = new HashMap<>();
|
|
for (char c = 'a'; c <= 'z'; c++) {
|
|
if (!VOWELS.contains(c)) {
|
|
consonants.put(c, new String[] { "mm", "" });
|
|
}
|
|
}
|
|
CONSONANT_MAPS.put(GagMaterial.SPONGE, consonants);
|
|
|
|
Map<Character, String[]> vowels = new HashMap<>();
|
|
for (char c : VOWELS) {
|
|
vowels.put(c, new String[] { "mm", "" });
|
|
}
|
|
VOWEL_MAPS.put(GagMaterial.SPONGE, vowels);
|
|
}
|
|
|
|
private static void initializeBaguetteMappings() {
|
|
Map<Character, String[]> consonants = new HashMap<>();
|
|
// Baguette - comedic, food-blocked
|
|
consonants.put('b', new String[] { "bm", "mm" });
|
|
consonants.put('c', new String[] { "km", "gm" });
|
|
consonants.put('d', new String[] { "dm", "nm" });
|
|
consonants.put('f', new String[] { "fm", "hm" });
|
|
consonants.put('g', new String[] { "gm", "ngm" });
|
|
consonants.put('h', new String[] { "hm", "h" });
|
|
consonants.put('j', new String[] { "jm", "zhm" });
|
|
consonants.put('k', new String[] { "km", "gm" });
|
|
consonants.put('l', new String[] { "lm", "mm" });
|
|
consonants.put('m', new String[] { "mm", "m" });
|
|
consonants.put('n', new String[] { "nm", "n" });
|
|
consonants.put('p', new String[] { "pm", "mm" });
|
|
consonants.put('q', new String[] { "km", "gm" });
|
|
consonants.put('r', new String[] { "rm", "mm" });
|
|
consonants.put('s', new String[] { "sm", "shm" });
|
|
consonants.put('t', new String[] { "tm", "nm" });
|
|
consonants.put('v', new String[] { "vm", "fm" });
|
|
consonants.put('w', new String[] { "wm", "um" });
|
|
consonants.put('x', new String[] { "ksm", "km" });
|
|
consonants.put('z', new String[] { "zm", "sm" });
|
|
CONSONANT_MAPS.put(GagMaterial.BAGUETTE, consonants);
|
|
|
|
Map<Character, String[]> vowels = new HashMap<>();
|
|
vowels.put('a', new String[] { "am", "om" });
|
|
vowels.put('e', new String[] { "em", "um" });
|
|
vowels.put('i', new String[] { "im", "um" });
|
|
vowels.put('o', new String[] { "om", "o" });
|
|
vowels.put('u', new String[] { "um", "u" });
|
|
vowels.put('y', new String[] { "im", "um" });
|
|
VOWEL_MAPS.put(GagMaterial.BAGUETTE, vowels);
|
|
}
|
|
|
|
/**
|
|
* Map a single phoneme to its muffled equivalent.
|
|
*
|
|
* @param c The original character
|
|
* @param material The gag material
|
|
* @param bleedChance Chance (0-1) that the original sound passes through
|
|
* @return The muffled phoneme
|
|
*/
|
|
public static String mapPhoneme(
|
|
char c,
|
|
GagMaterial material,
|
|
float bleedChance
|
|
) {
|
|
char lower = Character.toLowerCase(c);
|
|
|
|
// Non-alphabetic characters pass through
|
|
if (!Character.isLetter(c)) {
|
|
return String.valueOf(c);
|
|
}
|
|
|
|
// Bleed-through check: original sound passes
|
|
if (RANDOM.nextFloat() < bleedChance) {
|
|
return String.valueOf(c);
|
|
}
|
|
|
|
// Get appropriate map
|
|
Map<Character, String[]> map = isVowel(lower)
|
|
? VOWEL_MAPS.get(material)
|
|
: CONSONANT_MAPS.get(material);
|
|
|
|
if (map == null) {
|
|
return String.valueOf(c);
|
|
}
|
|
|
|
String[] options = map.get(lower);
|
|
if (options == null || options.length == 0) {
|
|
// Default fallback
|
|
return isVowel(lower) ? "mm" : "nn";
|
|
}
|
|
|
|
// Pick a random option
|
|
String result = options[RANDOM.nextInt(options.length)];
|
|
|
|
// Preserve case for first character
|
|
if (Character.isUpperCase(c) && !result.isEmpty()) {
|
|
return (
|
|
Character.toUpperCase(result.charAt(0)) + result.substring(1)
|
|
);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check if a character is a vowel.
|
|
*/
|
|
public static boolean isVowel(char c) {
|
|
return VOWELS.contains(Character.toLowerCase(c));
|
|
}
|
|
|
|
/**
|
|
* Check if a character is a plosive consonant.
|
|
*/
|
|
public static boolean isPlosive(char c) {
|
|
return PLOSIVES.contains(Character.toLowerCase(c));
|
|
}
|
|
|
|
/**
|
|
* Check if a character is a nasal consonant.
|
|
*/
|
|
public static boolean isNasal(char c) {
|
|
return NASALS.contains(Character.toLowerCase(c));
|
|
}
|
|
|
|
/**
|
|
* Check if a character is a fricative consonant.
|
|
*/
|
|
public static boolean isFricative(char c) {
|
|
return FRICATIVES.contains(Character.toLowerCase(c));
|
|
}
|
|
|
|
/**
|
|
* Check if a character is a liquid consonant.
|
|
*/
|
|
public static boolean isLiquid(char c) {
|
|
return LIQUIDS.contains(Character.toLowerCase(c));
|
|
}
|
|
|
|
/**
|
|
* Check if a character can potentially bleed through for a given material.
|
|
* Nasals have higher bleed-through, plosives have lower.
|
|
*/
|
|
public static float getBleedModifier(char c, GagMaterial material) {
|
|
char lower = Character.toLowerCase(c);
|
|
|
|
// Nasals almost always pass through
|
|
if (isNasal(lower)) {
|
|
return 2.0f;
|
|
}
|
|
|
|
// Plosives are harder to pronounce with most gags
|
|
if (isPlosive(lower)) {
|
|
return material == GagMaterial.RING ? 1.5f : 0.3f;
|
|
}
|
|
|
|
// Fricatives depend on whether air can escape
|
|
if (isFricative(lower)) {
|
|
return (
|
|
material == GagMaterial.TAPE ||
|
|
material == GagMaterial.STUFFED
|
|
)
|
|
? 0.1f
|
|
: 0.8f;
|
|
}
|
|
|
|
// Liquids depend on tongue freedom
|
|
if (isLiquid(lower)) {
|
|
return (
|
|
material == GagMaterial.RING || material == GagMaterial.BITE
|
|
)
|
|
? 1.2f
|
|
: 0.2f;
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
}
|