114 lines
2.4 KiB
Go
114 lines
2.4 KiB
Go
|
|
package animation
|
||
|
|
|
||
|
|
// VisemeMapping maps phonemes to visemes
|
||
|
|
var VisemeMapping = map[string]string{
|
||
|
|
// Silence
|
||
|
|
"sil": "sil",
|
||
|
|
"sp": "sil",
|
||
|
|
|
||
|
|
// Vowels
|
||
|
|
"aa": "aa", // "father"
|
||
|
|
"ae": "aa", // "cat"
|
||
|
|
"ah": "aa", // "but"
|
||
|
|
"ao": "oh", // "law"
|
||
|
|
"aw": "ou", // "cow"
|
||
|
|
"ay": "aa", // "hide"
|
||
|
|
"eh": "ee", // "red"
|
||
|
|
"er": "er", // "her"
|
||
|
|
"ey": "ee", // "ate"
|
||
|
|
"ih": "ee", // "it"
|
||
|
|
"iy": "ee", // "eat"
|
||
|
|
"ow": "ou", // "show"
|
||
|
|
"oy": "ou", // "toy"
|
||
|
|
"uh": "ou", // "book"
|
||
|
|
"uw": "ou", // "blue"
|
||
|
|
|
||
|
|
// Consonants
|
||
|
|
"b": "mbp", // "bat"
|
||
|
|
"ch": "ch", // "chair"
|
||
|
|
"d": "td", // "dog"
|
||
|
|
"dh": "th", // "the"
|
||
|
|
"f": "fv", // "fish"
|
||
|
|
"g": "gk", // "go"
|
||
|
|
"hh": "aa", // "hat"
|
||
|
|
"jh": "ch", // "joy"
|
||
|
|
"k": "gk", // "cat"
|
||
|
|
"l": "aa", // "let"
|
||
|
|
"m": "mbp", // "mat"
|
||
|
|
"n": "aa", // "not"
|
||
|
|
"ng": "gk", // "sing"
|
||
|
|
"p": "mbp", // "pat"
|
||
|
|
"r": "aa", // "red"
|
||
|
|
"s": "s", // "sat"
|
||
|
|
"sh": "ch", // "ship"
|
||
|
|
"t": "td", // "top"
|
||
|
|
"th": "th", // "think"
|
||
|
|
"v": "fv", // "vat"
|
||
|
|
"w": "ou", // "wet"
|
||
|
|
"y": "ee", // "yet"
|
||
|
|
"z": "s", // "zoo"
|
||
|
|
"zh": "ch", // "measure"
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetVisemeForPhoneme returns the viseme for a phoneme
|
||
|
|
func GetVisemeForPhoneme(phoneme string) string {
|
||
|
|
if viseme, ok := VisemeMapping[phoneme]; ok {
|
||
|
|
return viseme
|
||
|
|
}
|
||
|
|
return "aa" // Default
|
||
|
|
}
|
||
|
|
|
||
|
|
// PhonemeToVisemeTimeline converts phoneme timings to viseme timeline
|
||
|
|
func PhonemeToVisemeTimeline(phonemes []PhonemeTiming) []VisemeEvent {
|
||
|
|
if len(phonemes) == 0 {
|
||
|
|
return []VisemeEvent{}
|
||
|
|
}
|
||
|
|
|
||
|
|
var visemes []VisemeEvent
|
||
|
|
currentViseme := GetVisemeForPhoneme(phonemes[0].Phoneme)
|
||
|
|
startTime := phonemes[0].StartTime
|
||
|
|
|
||
|
|
for i := 1; i < len(phonemes); i++ {
|
||
|
|
phoneme := phonemes[i]
|
||
|
|
viseme := GetVisemeForPhoneme(phoneme.Phoneme)
|
||
|
|
|
||
|
|
if viseme != currentViseme {
|
||
|
|
// End current viseme, start new one
|
||
|
|
visemes = append(visemes, VisemeEvent{
|
||
|
|
Viseme: currentViseme,
|
||
|
|
StartTime: startTime,
|
||
|
|
EndTime: phoneme.StartTime,
|
||
|
|
})
|
||
|
|
currentViseme = viseme
|
||
|
|
startTime = phoneme.StartTime
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add final viseme
|
||
|
|
if len(phonemes) > 0 {
|
||
|
|
lastPhoneme := phonemes[len(phonemes)-1]
|
||
|
|
visemes = append(visemes, VisemeEvent{
|
||
|
|
Viseme: currentViseme,
|
||
|
|
StartTime: startTime,
|
||
|
|
EndTime: lastPhoneme.EndTime,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
return visemes
|
||
|
|
}
|
||
|
|
|
||
|
|
// PhonemeTiming represents a phoneme with timing
|
||
|
|
type PhonemeTiming struct {
|
||
|
|
Phoneme string
|
||
|
|
StartTime float64
|
||
|
|
EndTime float64
|
||
|
|
}
|
||
|
|
|
||
|
|
// VisemeEvent represents a viseme event
|
||
|
|
type VisemeEvent struct {
|
||
|
|
Viseme string
|
||
|
|
StartTime float64
|
||
|
|
EndTime float64
|
||
|
|
}
|
||
|
|
|