promo.morph_utils.modify_pitch_accent API documentation

# promo.morph_utils.modify_pitch_accent

Created on Jan 26, 2017

This convenience code that makes it easy to manipulate characteristics of a pitch accent. It is flexible in how it is used (on a stylized pitch contour or raw values and as a single step or in a piecewise manner).

See examples/modify_pitch_accent_example.py for example usage.

NOTE: By pitch accent, I mean a single hill or peak, surrounded by less intense values to the left and right. Some of the functions make reference to the max value in the dataset, so if you have multiple peaks or a downward slope, etc. you may get strange results.

@author: Tim

View Source

      1"""
      2Created on Jan 26, 2017
      3
      4This convenience code that makes it easy to manipulate
      5characteristics of a pitch accent.  It is flexible in how
      6it is used (on a stylized pitch contour or raw values and
      7as a single step or in a piecewise manner).
      8
      9See examples/modify_pitch_accent_example.py for example usage.
     10
     11NOTE: By pitch accent, I mean a single hill or peak, surrounded
     12by less intense values to the left and right.  Some of the
     13functions make reference to the max value in the dataset, so if you
     14have multiple peaks or a downward slope, etc. you may get strange
     15results.
     16
     17@author: Tim
     18"""
     19import copy
     20
     21
     22def _deletePoints(f0List, start, end):
     23    return [(timeV, f0V) for timeV, f0V in f0List if timeV < start or timeV > end]
     24
     25
     26class PitchAccent(object):
     27    def __init__(self, pointList):
     28
     29        self.pointList = copy.deepcopy(pointList)
     30
     31        self.netLeftShift = 0
     32        self.netRightShift = 0
     33
     34        pitchList = [f0V for _, f0V in self.pointList]
     35        minV = min(pitchList)
     36        maxV = max(pitchList)
     37
     38        self.peakI = pitchList.index(maxV)
     39
     40        timeList = [timeV for timeV, _ in self.pointList]
     41        self.minT = min(timeList)
     42        self.maxT = max(timeList)
     43
     44    def adjustPeakHeight(self, heightAmount):
     45        """
     46        Adjust peak height
     47
     48        The foot of the accent is left unchanged and intermediate
     49        values are linearly scaled
     50        """
     51        if heightAmount == 0:
     52            return
     53
     54        pitchList = [f0V for _, f0V in self.pointList]
     55        minV = min(pitchList)
     56        maxV = max(pitchList)
     57        scale = lambda x, y: x + y * (x - minV) / float(maxV - minV)
     58
     59        self.pointList = [
     60            (timeV, scale(f0V, heightAmount)) for timeV, f0V in self.pointList
     61        ]
     62
     63    def addPlateau(self, plateauAmount, pitchSampFreq=None):
     64        """
     65        Add a plateau
     66
     67        A negative plateauAmount will move the peak backwards.
     68        A positive plateauAmount will move the peak forwards.
     69
     70        All points on the side of the peak growth will also get moved.
     71        i.e. the slope of the peak does not change.  The accent gets
     72        wider instead.
     73
     74        If pitchSampFreq=None, the plateau will only be specified by
     75        the start and end points of the plateau
     76        """
     77        if plateauAmount == 0:
     78            return
     79
     80        maxPoint = self.pointList[self.peakI]
     81
     82        # Define the plateau
     83        if pitchSampFreq is not None:
     84            numSteps = abs(int(plateauAmount / pitchSampFreq))
     85            timeChangeList = [stepV * pitchSampFreq for stepV in range(0, numSteps + 1)]
     86        else:
     87            timeChangeList = [
     88                plateauAmount,
     89            ]
     90
     91        # Shift the side being pushed by the plateau
     92        if plateauAmount < 0:  # Plateau moves left of the peak
     93            leftSide = self.pointList[: self.peakI]
     94            rightSide = self.pointList[self.peakI :]
     95
     96            plateauPoints = [
     97                (maxPoint[0] + timeChange, maxPoint[1]) for timeChange in timeChangeList
     98            ]
     99            leftSide = [(timeV + plateauAmount, f0V) for timeV, f0V in leftSide]
    100            self.netLeftShift += plateauAmount
    101
    102        elif plateauAmount > 0:  # Plateau moves right of the peak
    103            leftSide = self.pointList[: self.peakI + 1]
    104            rightSide = self.pointList[self.peakI + 1 :]
    105
    106            plateauPoints = [
    107                (maxPoint[0] + timeChange, maxPoint[1]) for timeChange in timeChangeList
    108            ]
    109            rightSide = [(timeV + plateauAmount, f0V) for timeV, f0V in rightSide]
    110            self.netRightShift += plateauAmount
    111
    112        self.pointList = leftSide + plateauPoints + rightSide
    113
    114    def shiftAccent(self, shiftAmount):
    115        """
    116        Move the whole accent earlier or later
    117        """
    118        if shiftAmount == 0:
    119            return
    120
    121        self.pointList = [(time + shiftAmount, pitch) for time, pitch in self.pointList]
    122
    123        # Update shift amounts
    124        if shiftAmount < 0:
    125            self.netLeftShift += shiftAmount
    126        elif shiftAmount >= 0:
    127            self.netRightShift += shiftAmount
    128
    129    def deleteOverlapping(self, targetList):
    130        """
    131        Erase points from another list that overlap with points in this list
    132        """
    133        start = self.pointList[0][0]
    134        stop = self.pointList[-1][0]
    135
    136        if self.netLeftShift < 0:
    137            start += self.netLeftShift
    138
    139        if self.netRightShift > 0:
    140            stop += self.netRightShift
    141
    142        targetList = _deletePoints(targetList, start, stop)
    143
    144        return targetList
    145
    146    def reintegrate(self, fullPointList):
    147        """
    148        Integrates the pitch values of the accent into a larger pitch contour
    149        """
    150        # Erase the original region of the accent
    151        fullPointList = _deletePoints(fullPointList, self.minT, self.maxT)
    152
    153        # Erase the new region of the accent
    154        fullPointList = self.deleteOverlapping(fullPointList)
    155
    156        # Add the accent into the full pitch list
    157        outputPointList = fullPointList + self.pointList
    158        outputPointList.sort()
    159
    160        return outputPointList

class PitchAccent: View Source

     27class PitchAccent(object):
     28    def __init__(self, pointList):
     29
     30        self.pointList = copy.deepcopy(pointList)
     31
     32        self.netLeftShift = 0
     33        self.netRightShift = 0
     34
     35        pitchList = [f0V for _, f0V in self.pointList]
     36        minV = min(pitchList)
     37        maxV = max(pitchList)
     38
     39        self.peakI = pitchList.index(maxV)
     40
     41        timeList = [timeV for timeV, _ in self.pointList]
     42        self.minT = min(timeList)
     43        self.maxT = max(timeList)
     44
     45    def adjustPeakHeight(self, heightAmount):
     46        """
     47        Adjust peak height
     48
     49        The foot of the accent is left unchanged and intermediate
     50        values are linearly scaled
     51        """
     52        if heightAmount == 0:
     53            return
     54
     55        pitchList = [f0V for _, f0V in self.pointList]
     56        minV = min(pitchList)
     57        maxV = max(pitchList)
     58        scale = lambda x, y: x + y * (x - minV) / float(maxV - minV)
     59
     60        self.pointList = [
     61            (timeV, scale(f0V, heightAmount)) for timeV, f0V in self.pointList
     62        ]
     63
     64    def addPlateau(self, plateauAmount, pitchSampFreq=None):
     65        """
     66        Add a plateau
     67
     68        A negative plateauAmount will move the peak backwards.
     69        A positive plateauAmount will move the peak forwards.
     70
     71        All points on the side of the peak growth will also get moved.
     72        i.e. the slope of the peak does not change.  The accent gets
     73        wider instead.
     74
     75        If pitchSampFreq=None, the plateau will only be specified by
     76        the start and end points of the plateau
     77        """
     78        if plateauAmount == 0:
     79            return
     80
     81        maxPoint = self.pointList[self.peakI]
     82
     83        # Define the plateau
     84        if pitchSampFreq is not None:
     85            numSteps = abs(int(plateauAmount / pitchSampFreq))
     86            timeChangeList = [stepV * pitchSampFreq for stepV in range(0, numSteps + 1)]
     87        else:
     88            timeChangeList = [
     89                plateauAmount,
     90            ]
     91
     92        # Shift the side being pushed by the plateau
     93        if plateauAmount < 0:  # Plateau moves left of the peak
     94            leftSide = self.pointList[: self.peakI]
     95            rightSide = self.pointList[self.peakI :]
     96
     97            plateauPoints = [
     98                (maxPoint[0] + timeChange, maxPoint[1]) for timeChange in timeChangeList
     99            ]
    100            leftSide = [(timeV + plateauAmount, f0V) for timeV, f0V in leftSide]
    101            self.netLeftShift += plateauAmount
    102
    103        elif plateauAmount > 0:  # Plateau moves right of the peak
    104            leftSide = self.pointList[: self.peakI + 1]
    105            rightSide = self.pointList[self.peakI + 1 :]
    106
    107            plateauPoints = [
    108                (maxPoint[0] + timeChange, maxPoint[1]) for timeChange in timeChangeList
    109            ]
    110            rightSide = [(timeV + plateauAmount, f0V) for timeV, f0V in rightSide]
    111            self.netRightShift += plateauAmount
    112
    113        self.pointList = leftSide + plateauPoints + rightSide
    114
    115    def shiftAccent(self, shiftAmount):
    116        """
    117        Move the whole accent earlier or later
    118        """
    119        if shiftAmount == 0:
    120            return
    121
    122        self.pointList = [(time + shiftAmount, pitch) for time, pitch in self.pointList]
    123
    124        # Update shift amounts
    125        if shiftAmount < 0:
    126            self.netLeftShift += shiftAmount
    127        elif shiftAmount >= 0:
    128            self.netRightShift += shiftAmount
    129
    130    def deleteOverlapping(self, targetList):
    131        """
    132        Erase points from another list that overlap with points in this list
    133        """
    134        start = self.pointList[0][0]
    135        stop = self.pointList[-1][0]
    136
    137        if self.netLeftShift < 0:
    138            start += self.netLeftShift
    139
    140        if self.netRightShift > 0:
    141            stop += self.netRightShift
    142
    143        targetList = _deletePoints(targetList, start, stop)
    144
    145        return targetList
    146
    147    def reintegrate(self, fullPointList):
    148        """
    149        Integrates the pitch values of the accent into a larger pitch contour
    150        """
    151        # Erase the original region of the accent
    152        fullPointList = _deletePoints(fullPointList, self.minT, self.maxT)
    153
    154        # Erase the new region of the accent
    155        fullPointList = self.deleteOverlapping(fullPointList)
    156
    157        # Add the accent into the full pitch list
    158        outputPointList = fullPointList + self.pointList
    159        outputPointList.sort()
    160
    161        return outputPointList

PitchAccent(pointList) View Source

    28    def __init__(self, pointList):
    29
    30        self.pointList = copy.deepcopy(pointList)
    31
    32        self.netLeftShift = 0
    33        self.netRightShift = 0
    34
    35        pitchList = [f0V for _, f0V in self.pointList]
    36        minV = min(pitchList)
    37        maxV = max(pitchList)
    38
    39        self.peakI = pitchList.index(maxV)
    40
    41        timeList = [timeV for timeV, _ in self.pointList]
    42        self.minT = min(timeList)
    43        self.maxT = max(timeList)

pointList
netLeftShift
netRightShift
peakI
minT
maxT
def adjustPeakHeight(self, heightAmount): View Source

    45    def adjustPeakHeight(self, heightAmount):
    46        """
    47        Adjust peak height
    48
    49        The foot of the accent is left unchanged and intermediate
    50        values are linearly scaled
    51        """
    52        if heightAmount == 0:
    53            return
    54
    55        pitchList = [f0V for _, f0V in self.pointList]
    56        minV = min(pitchList)
    57        maxV = max(pitchList)
    58        scale = lambda x, y: x + y * (x - minV) / float(maxV - minV)
    59
    60        self.pointList = [
    61            (timeV, scale(f0V, heightAmount)) for timeV, f0V in self.pointList
    62        ]

Adjust peak height

The foot of the accent is left unchanged and intermediate values are linearly scaled

def addPlateau(self, plateauAmount, pitchSampFreq=None): View Source

     64    def addPlateau(self, plateauAmount, pitchSampFreq=None):
     65        """
     66        Add a plateau
     67
     68        A negative plateauAmount will move the peak backwards.
     69        A positive plateauAmount will move the peak forwards.
     70
     71        All points on the side of the peak growth will also get moved.
     72        i.e. the slope of the peak does not change.  The accent gets
     73        wider instead.
     74
     75        If pitchSampFreq=None, the plateau will only be specified by
     76        the start and end points of the plateau
     77        """
     78        if plateauAmount == 0:
     79            return
     80
     81        maxPoint = self.pointList[self.peakI]
     82
     83        # Define the plateau
     84        if pitchSampFreq is not None:
     85            numSteps = abs(int(plateauAmount / pitchSampFreq))
     86            timeChangeList = [stepV * pitchSampFreq for stepV in range(0, numSteps + 1)]
     87        else:
     88            timeChangeList = [
     89                plateauAmount,
     90            ]
     91
     92        # Shift the side being pushed by the plateau
     93        if plateauAmount < 0:  # Plateau moves left of the peak
     94            leftSide = self.pointList[: self.peakI]
     95            rightSide = self.pointList[self.peakI :]
     96
     97            plateauPoints = [
     98                (maxPoint[0] + timeChange, maxPoint[1]) for timeChange in timeChangeList
     99            ]
    100            leftSide = [(timeV + plateauAmount, f0V) for timeV, f0V in leftSide]
    101            self.netLeftShift += plateauAmount
    102
    103        elif plateauAmount > 0:  # Plateau moves right of the peak
    104            leftSide = self.pointList[: self.peakI + 1]
    105            rightSide = self.pointList[self.peakI + 1 :]
    106
    107            plateauPoints = [
    108                (maxPoint[0] + timeChange, maxPoint[1]) for timeChange in timeChangeList
    109            ]
    110            rightSide = [(timeV + plateauAmount, f0V) for timeV, f0V in rightSide]
    111            self.netRightShift += plateauAmount
    112
    113        self.pointList = leftSide + plateauPoints + rightSide

Add a plateau

A negative plateauAmount will move the peak backwards. A positive plateauAmount will move the peak forwards.

All points on the side of the peak growth will also get moved. i.e. the slope of the peak does not change. The accent gets wider instead.

If pitchSampFreq=None, the plateau will only be specified by the start and end points of the plateau

def shiftAccent(self, shiftAmount): View Source

    115    def shiftAccent(self, shiftAmount):
    116        """
    117        Move the whole accent earlier or later
    118        """
    119        if shiftAmount == 0:
    120            return
    121
    122        self.pointList = [(time + shiftAmount, pitch) for time, pitch in self.pointList]
    123
    124        # Update shift amounts
    125        if shiftAmount < 0:
    126            self.netLeftShift += shiftAmount
    127        elif shiftAmount >= 0:
    128            self.netRightShift += shiftAmount

Move the whole accent earlier or later

def deleteOverlapping(self, targetList): View Source

    130    def deleteOverlapping(self, targetList):
    131        """
    132        Erase points from another list that overlap with points in this list
    133        """
    134        start = self.pointList[0][0]
    135        stop = self.pointList[-1][0]
    136
    137        if self.netLeftShift < 0:
    138            start += self.netLeftShift
    139
    140        if self.netRightShift > 0:
    141            stop += self.netRightShift
    142
    143        targetList = _deletePoints(targetList, start, stop)
    144
    145        return targetList

Erase points from another list that overlap with points in this list

def reintegrate(self, fullPointList): View Source

    147    def reintegrate(self, fullPointList):
    148        """
    149        Integrates the pitch values of the accent into a larger pitch contour
    150        """
    151        # Erase the original region of the accent
    152        fullPointList = _deletePoints(fullPointList, self.minT, self.maxT)
    153
    154        # Erase the new region of the accent
    155        fullPointList = self.deleteOverlapping(fullPointList)
    156
    157        # Add the accent into the full pitch list
    158        outputPointList = fullPointList + self.pointList
    159        outputPointList.sort()
    160
    161        return outputPointList

Integrates the pitch values of the accent into a larger pitch contour