Files
2025-09-29 00:52:08 +02:00

404 lines
16 KiB
Python
Executable File

""" This module provides definitions for phonemes, phoneme lists, and word lists
static variables:
PHONEME_REGISTRY -- A PhonemeRegistry object that has been initialized
classes:
PhonemeType -- Information about a specific type of phoneme
PhonemeRegistry -- Provides a mapping from phonemeID or facefxCoding to
PhonemeType objects
PhonemeMapEntry -- A single entry in a PhonemeMap, maps a phoneme to a target by
an amount
PhonemeMap -- The actor's mapping from phonemes to targets
Phoneme -- A single phoneme in the PhonemeList
PhonemeList -- A sorted list of the phonemes in an animation
Word -- A single word in the PhonemeWordList
PhonemeWordList -- A sorted list of the phonemes and words in an animation
Owner: Jamie Redmond
Copyright (c) 2002-2011 OC3 Entertainment, Inc.
"""
import FxStudio
class PhonemeType:
""" Provides information about a specific type of phoneme.
instance variables:
phonemeId -- the int id assigned to the phoneme
facefxCoding -- the FaceFX coding of the phoneme
ipaCoding -- the IPA coding of the phoneme
sampaCoding -- the X-SAMPA coding of the phoneme
durationMean -- the mean duration of the phoneme
durationStddev -- the standard devaition of the duration of the phoneme.
"""
def __init__(self, phonemeTypeTupleFromStudio):
""" Initializes the object with the tuple from Studio. """
self.phonemeId = phonemeTypeTupleFromStudio[0]
self.facefxCoding = phonemeTypeTupleFromStudio[1]
self.ipaCoding = phonemeTypeTupleFromStudio[2]
self.sampaCoding = phonemeTypeTupleFromStudio[3]
self.durationMean = phonemeTypeTupleFromStudio[4]
self.durationStddev = phonemeTypeTupleFromStudio[5]
def __str__(self):
""" Returns the string representation of the PhonemeType """
return 'id={0} facefxCoding={1} ipaCoding={2} sampaCoding={3} durationMean={4} durationStddev={5}'.\
format(self.phonemeId, self.facefxCoding, self.ipaCoding,
self.sampaCoding, self.durationMean, self.durationStddev)
def __repr__(self):
""" Hackish repr to make it look nice in the interpreter. """
return '[{0} {1}]'.format(self.phonemeId, self.facefxCoding)
def getPhonemeClass(self):
""" Returns the class of the PhonemeType as a string.
Valid returns are 'vowel', 'diphthong', 'fricative', 'nasal',
'approximate', 'stop', 'trill', 'silence', and 'unknown'
"""
return FxStudio.getPhonemeClass(self.phonemeId)
def getSampleWords(self):
""" Returns a string containing sample words that use the phoneme. """
return FxStudio.getPhonemeSampleWords(self.phonemeId)
def getClassification(self):
""" Returns the classification string for the phoneme. """
return FxStudio.getPhonemeClassification(self.phonemeId)
def isVowel(self):
""" Returns True if the class is a vowel or a diphthong. """
return self.getPhonemeClass() == 'vowel' or\
self.getPhonemeClass() == 'diphthong'
def isDiphthong(self):
""" Returns True if the class is a diphthong. """
return self.getPhonemeClass() == 'diphthong'
def isFricative(self):
""" Returns True if the class is a fricative. """
return self.getPhonemeClass() == 'fricative'
def isNasal(self):
""" Returns True if the class is a nasal. """
return self.getPhonemeClass() == 'nasal'
def isApproximate(self):
""" Returns True if the class is an approximate. """
return self.getPhonemeClass() == 'approximate'
def isStop(self):
""" Returns True if the class is a stop. """
return self.getPhonemeClass() == 'stop'
def isTrill(self):
""" Returns True if the class is a trill. """
return self.getPhonemeClass() == 'trill'
def isSilence(self):
""" Returns True if the class is silence. """
return self.getPhonemeClass() == 'silence'
class PhonemeRegistry:
""" Provides a mapping from phonemeID to PhonemeType.
When FaceFX Studio starts up, a global phoneme registry is created as
FxPhonemes.PHONEME_REGISTRY. The PHONEME_REGISTRY may be indexed by
PhonemeType.phonemeId or PhonemeType.facefxCoding.
instance variables:
entries - a list of phoneme types sorted by id
"""
def __init__(self):
""" Initializes the object by pulling the data from FaceFX Studio """
phonemeTypeTuplesFromStudio = FxStudio.getPhonemeRegistry()
self.entries = [PhonemeType(entry)
for entry in phonemeTypeTuplesFromStudio]
# Use a dictionary to help map phonemeIDs and facefx codings to indices
self._redirect = dict()
for e in self.entries:
self._redirect[e.phonemeId] = e.phonemeId
self._redirect[e.facefxCoding] = e.phonemeId
def __str__(self):
""" Returns the string representation of the object. """
return '\n'.join([str(x) for x in self.entries])
def __getitem__(self, item):
""" Returns the entry specified by item, either phonemeID or
facefxCoding
"""
return self.entries[self._redirect[item]]
def getNumEntries(self):
""" Returns the number of entries in the registry. """
return len(self.entries)
def findPhonemeTypeByFaceFXCoding(self, facefxCoding):
""" Returns the PhonemeType with the specified facefxCoding, or None """
for entry in self.entries:
if entry.facefxCoding == facefxCoding:
return entry
print 'Error: Unregistered facefx phoneme coding detected [ ' + facefxCoding + ' ]!'
return None
def findPhonemeTypeByIPACoding(self, ipaCoding):
""" Returns the PhonemeType with the specified ipaCoding, or None """
for entry in self.entries:
if entry.ipaCoding == ipaCoding:
return entry
print 'Error: Unregistered ipa phoneme coding detected [ ' + ipaCoding + ' ]!'
return None
def findPhonemeTypeBySampaCoding(self, sampaCoding):
""" Returns the PhonemeType with the specified sampaCoding, or None """
for entry in self.entries:
if entry.sampaCoding == sampaCoding:
return entry
print 'Error: Unregistered sampa phoneme coding detected [ ' + sampaCoding + ' ]!'
return None
class PhonemeMapEntry:
""" Represents a single mapping entry.
instance variables:
phonemeId -- the integer ID of the phoneme
targetName -- the name of the target to drive
mappingAmount -- a float value indicated the value of the target
"""
def __init__(self, phonemeMapTupleFromStudio):
""" Initialize the PhonemeMapEntry with the tuple from Studio. """
self.phonemeId = phonemeMapTupleFromStudio[0]
self.targetName = phonemeMapTupleFromStudio[1]
self.mappingAmount = phonemeMapTupleFromStudio[2]
def __str__(self):
""" Returns a string representation of the mapping entry. """
return 'phoneme={0} => targetName={1} mappingAmount={2}'.format(
PHONEME_REGISTRY[self.phonemeId].facefxCoding, self.targetName,
self.mappingAmount)
def __repr__(self):
""" Returns a hackish representation of the mapping entry """
return 'PhonemeMapEntry(({0}, {1}, {2}))'.format(
PHONEME_REGISTRY[self.phonemeId].facefxCoding, self.targetName,
self.mappingAmount)
class PhonemeMap:
""" Represents an actor's phoneme map.
instance variables:
entries -- a list of non-zero PhonemeMapEntry
"""
def __init__(self):
""" Initialize the phoneme map by pulling the data from Studio. """
phonemeMapTuplesFromStudio = FxStudio.getPhonemeMap()
self.entries = [PhonemeMapEntry(e) for e in phonemeMapTuplesFromStudio]
# Sort the map entries in ascending order by phonemeId using a
# simple lambda function passed to sort().
self.entries.sort(lambda this, next: this.phonemeId - next.phonemeId)
def __str__(self):
""" Returns the string representation of the phoneme map. """
return '\n'.join([str(entry) for entry in self.entries])
def getNumEntries(self):
""" Returns the number of entries in the phoneme map. """
return len(self.entries)
def getMappingAmount(self, phonemeId, targetName):
""" Returns the amount that phonemeId is mapped to targetName.
If there is no explicit entry between phonemeId and targetName, it is
assumed that the mapping amount is 0.0.
keyword arguments:
phonemeId -- the integer identifier of the phoneme to search for
targetName -- the name of the target to search for
"""
for entry in self.entries:
if entry.phonemeId == phonemeId and entry.targetName == targetName:
return entry.mappingAmount
return 0.0
def getTargetNamesUsedInMapping(self):
""" Returns a tuple of targetNames that are involved in the mapping.
The tuple returned contains no duplicates. An empty tuple is returned
if there is no mapping.
"""
return tuple(set([entry.targetName for entry in self.entries]))
def getTargetsMappedToPhonemeId(self, phonemeId):
""" Returns a list of tuples that are mapped to the phonemeId.
The list is in the format [(targetName, mappingAmount), *], or an empty
list if no targets are explicitly mapped to the given phonemeId. In that
case it should be assumed that the mapping value is 0.0 for each target.
keyword arguments:
phonemeId -- the integer identifier of the phoneme to search for
"""
return [(e.targetName, e.mappingAmount) for e in self.entries if
e.phonemeId == phonemeId]
def getPhonemeIdsMappedToTarget(self, targetName):
""" Returns a list of tuples that are mapped to the targetName.
The list is in the format [(phonemeId, mappingAmount), *], or an empty
list if no phonemeIds are explicitly mapped to the given targetName. In
that case it should be assumed that the mapped value is 0.0 for each
phonemeId.
keyword arguments:
targetName -- the name of the target to search for.
"""
return [(e.phonemeId, e.mappingAmount) for e in self.entries if
entry.targetName == targetName]
class Phoneme:
""" Rrepresents a single phoneme in a list.
instance variables:
phonemeId -- the integer identifier of the phoneme
startTime -- the time the phoneme starts, in seconds
endTime -- the time the phoneme ends, in seconds
"""
def __init__(self, phonemeTupleFromStudio):
""" Initialize the object with the tuple from Studio. """
self.phonemeId = phonemeTupleFromStudio[0]
self.startTime = phonemeTupleFromStudio[1]
self.endTime = phonemeTupleFromStudio[2]
def __str__(self):
""" Returns a string representation of the phoneme. """
return 'phoneme={0} startTime={1} endTime={2}'.format(
PHONEME_REGISTRY[self.phonemeId].facefxCoding, self.startTime,
self.endTime)
def __repr__(self):
""" Returns a hackish representation of the phoneme. """
return '[{0} ({1}, {2})]'.format(
PHONEME_REGISTRY[self.phonemeId].facefxCoding, self.startTime,
self.endTime)
def getDuration(self):
""" Returns the duration of the phoneme in seconds. """
return self.endTime - self.startTime
def getFacefxCoding(self):
""" Returns the FaceFX coding of the phoneme. """
return PHONEME_REGISTRY[self.phonemeId].facefxCoding
class PhonemeList:
""" Represents a chronologically sorted list of phonemes in an animation.
instance variables:
phonemes -- a list of Phoneme objects
"""
def __init__(self, animGroupName, animName):
""" Initializes the object by grabbing the data from Studio. """
phonemeTuplesFromStudio = FxStudio.getPhonemeList(animGroupName,
animName)
self.phonemes = [Phoneme(p) for p in phonemeTuplesFromStudio]
def __str__(self):
""" Returns a string representation of the phoneme list. """
return ' '.join([p.getFacefxCoding() for p in self.phonemes])
def __len__(self):
""" Returns the number of phonemes in the list. """
return len(self.phonemes)
def getNumPhonemes(self):
""" Returns the number of phonemes in the list. """
return len(self)
class Word:
""" Represents a word in a list.
instance variables:
word -- a string containing the word's text
startTime -- the time the word starts, in seconds
endTime -- the time the word ends, in seconds
phonemes -- a list of the Phoneme objects comprising the word
"""
def __init__(self, wordTupleFromStudio, phonemes = []):
""" Initializes the word with a tuple from Studio and the list of the
phonemes in the word.
"""
self.word = wordTupleFromStudio[0]
self.startTime = wordTupleFromStudio[1]
self.endTime = wordTupleFromStudio[2]
self.phonemes = [Phoneme(p) for p in phonemes]
def __str__(self):
""" Returns the string representation of the word. """
return 'word={0} startTime={1} endTime={2}'.format(self.word,
self.startTime, self.endTime)
def getDuration(self):
""" Returns the duration of the word, in seconds. """
return self.endTime - self.startTime
# This class represents a list of phonemes and a list of words and
# is inherited from the PhonemeList class.
class PhonemeWordList(PhonemeList):
""" Represents a chronologically sorted list of phonemes and words in the
animation.
instance variables:
phonemes -- a list of Phoneme objects
words -- a list of Word objects
"""
def __init__(self, animGroupName, animName):
""" Initialize the object by pulling the data from Studio. """
PhonemeList.__init__(self, animGroupName, animName)
wordTuplesFromStudio = FxStudio.getWordList(animGroupName, animName)
self.words = [
Word(w, FxStudio.getPhonemesInWord(animGroupName, animName, i))
for i, w in enumerate(wordTuplesFromStudio)]
def __str__(self):
""" Returns a string representation of the PhonemeWordList. """
r = ' '.join(word.word for word in self.words)
r += '\n'
r += PhonemeList.__str__(self)
return r
def getNumWords(self):
""" Returns the number of words in the PhonemeWordList. """
return len(self.words)