Skip to content

Commit

Permalink
Merge pull request #51 from timmahrt/praatio_v6.1
Browse files Browse the repository at this point in the history
Praatio 6.1
  • Loading branch information
timmahrt authored Nov 4, 2023
2 parents 0f0544e + 0f219e6 commit 5ff7c78
Show file tree
Hide file tree
Showing 39 changed files with 7,387 additions and 5,475 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

*Praatio uses semantic versioning (Major.Minor.Patch)*

Ver 6.1 (Nov 4, 2023)
- Add `TextgridTier.dejitter()` method for improving consistency between tiers

Ver 6.0 (Feb 4, 2023)
- Refactored 'audio.py' for maintainability (see [UPGRADING.md](https://github.com/timmahrt/praatIO/blob/main/UPGRADING.md) for details)
- Added unit tests for 'audio.py'
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ Please view [CHANGELOG.md](https://github.com/timmahrt/praatIO/blob/main/CHANGEL

Python module `https://pypi.org/project/typing-extensions/`. It should be installed automatically with praatio but you can install it manually if you have any problems.

``Python 3.7.*`` or above

[Click here to visit travis-ci and see the specific versions of python that praatIO is currently tested under](<https://travis-ci.org/timmahrt/praatIO>)
``Python 3.7.*`` or above (CI tested with python `3.7` through `3.12`).

If you are using ``Python 2.x`` or ``Python < 3.7``, you can use `PraatIO 4.x`.

Expand Down
32 changes: 16 additions & 16 deletions docs/praatio.html

Large diffs are not rendered by default.

294 changes: 282 additions & 12 deletions docs/praatio/audio.html

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions docs/praatio/data_classes.html

Large diffs are not rendered by default.

80 changes: 72 additions & 8 deletions docs/praatio/data_classes/data_point.html

Large diffs are not rendered by default.

4,224 changes: 2,230 additions & 1,994 deletions docs/praatio/data_classes/interval_tier.html

Large diffs are not rendered by default.

614 changes: 347 additions & 267 deletions docs/praatio/data_classes/klattgrid.html

Large diffs are not rendered by default.

2,355 changes: 1,280 additions & 1,075 deletions docs/praatio/data_classes/point_tier.html

Large diffs are not rendered by default.

82 changes: 69 additions & 13 deletions docs/praatio/data_classes/textgrid.html

Large diffs are not rendered by default.

1,164 changes: 665 additions & 499 deletions docs/praatio/data_classes/textgrid_tier.html

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions docs/praatio/data_points.html

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions docs/praatio/klattgrid.html

Large diffs are not rendered by default.

42 changes: 36 additions & 6 deletions docs/praatio/pitch_and_intensity.html

Large diffs are not rendered by default.

42 changes: 36 additions & 6 deletions docs/praatio/praat_scripts.html

Large diffs are not rendered by default.

315 changes: 144 additions & 171 deletions docs/praatio/praatio_scripts.html

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions docs/praatio/textgrid.html

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions docs/praatio/utilities.html

Large diffs are not rendered by default.

528 changes: 474 additions & 54 deletions docs/praatio/utilities/constants.html

Large diffs are not rendered by default.

162 changes: 156 additions & 6 deletions docs/praatio/utilities/errors.html

Large diffs are not rendered by default.

672 changes: 349 additions & 323 deletions docs/praatio/utilities/my_math.html

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions docs/praatio/utilities/textgrid_io.html

Large diffs are not rendered by default.

28 changes: 22 additions & 6 deletions docs/praatio/utilities/timit.html

Large diffs are not rendered by default.

1,800 changes: 900 additions & 900 deletions docs/praatio/utilities/utils.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/search.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/get_pitch_and_formants.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
450,
forceRegenerate=False,
)
tg = textgrid.openTextgrid(join(tgPath, "mary.TextGrid"))
tg = textgrid.openTextgrid(join(tgPath, "mary.TextGrid"), False)
tier = tg.getTier("phone")
filteredData = tier.getValuesInIntervals(maryPitchData)

Expand Down
54 changes: 54 additions & 0 deletions praatio/data_classes/interval_tier.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from praatio.utilities import errors
from praatio.utilities import utils
from praatio.utilities import my_math
from praatio.utilities import constants

from praatio.data_classes import textgrid_tier
Expand Down Expand Up @@ -100,6 +101,23 @@ def _validate(self):
f"({nextEntry.start}, {nextEntry.end}, {nextEntry.label})"
)

@property
def timestamps(self) -> List[float]:
"""All unique timestamps used in this tier"""
tmpTimestamps = [
time
for start, stop, _ in self.entries
for time in [
start,
stop,
]
]

uniqueTimestamps = list(set(tmpTimestamps))
uniqueTimestamps.sort()

return uniqueTimestamps

def crop(
self,
cropStart: float,
Expand Down Expand Up @@ -156,6 +174,42 @@ def crop(

return croppedTier

def dejitter(
self,
referenceTier: textgrid_tier.TextgridTier,
maxDifference: float = 0.001,
) -> textgrid_tier.TextgridTier:
"""
Set timestamps in this tier to be the same as values in the reference tier
Timestamps will only be moved if they are less than maxDifference away from the
reference time.
This can be used to correct minor alignment errors between tiers, as made when
annotating files manually, etc.
Args:
referenceTier: the IntervalTier or PointTier to use as a reference
maxDifference: the maximum amount to allow timestamps to be moved by
Returns:
the modified version of the current tier
"""
referenceTimestamps = referenceTier.timestamps

newEntries = []
for start, stop, label in self.entries:
startCompare = min(referenceTimestamps, key=lambda x: abs(x - start))
stopCompare = min(referenceTimestamps, key=lambda x: abs(x - stop))

if my_math.lessThanOrEqual(abs(start - startCompare), maxDifference):
start = startCompare
if my_math.lessThanOrEqual(abs(stop - stopCompare), maxDifference):
stop = stopCompare
newEntries.append((start, stop, label))

return self.new(entries=newEntries)

def deleteEntry(self, entry: Interval) -> None:
"""Removes an entry from the entries"""
self._entries.pop(self._entries.index(entry))
Expand Down
9 changes: 8 additions & 1 deletion praatio/data_classes/klattgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ def __init__(
def crop(self):
raise NotImplementedError

def dejitter(self):
raise NotImplementedError

def deleteEntry(self, entry):
raise NotImplementedError

Expand All @@ -153,6 +156,10 @@ def insertEntry(self):
def insertSpace(self):
raise NotImplementedError

@property
def timestamps(self):
raise NotImplementedError

def validate(self):
raise NotImplementedError

Expand All @@ -161,7 +168,7 @@ def modifyValues(self, modFunc: Callable[[float], bool]) -> None:
(timestamp, modFunc(float(value))) for timestamp, value in self.entries
]

self.entries = newEntries
self._entries = newEntries

def getAsText(self) -> str:
outputList = []
Expand Down
42 changes: 42 additions & 0 deletions praatio/data_classes/point_tier.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from praatio.utilities import constants
from praatio.utilities import errors
from praatio.utilities import utils
from praatio.utilities import my_math

from praatio.data_classes import textgrid_tier

Expand Down Expand Up @@ -72,6 +73,16 @@ def __init__(

super(PointTier, self).__init__(name, entries, calculatedMinT, calculatedMaxT)

@property
def timestamps(self) -> List[float]:
"""All unique timestamps used in this tier"""
tmpTimestamps = [time for time, _ in self.entries]

uniqueTimestamps = list(set(tmpTimestamps))
uniqueTimestamps.sort()

return uniqueTimestamps

def crop(
self,
cropStart: float,
Expand Down Expand Up @@ -121,6 +132,37 @@ def deleteEntry(self, entry: Point) -> None:
"""Removes an entry from the entries"""
self._entries.pop(self._entries.index(entry))

def dejitter(
self, referenceTier: textgrid_tier.TextgridTier, maxDifference: float = 0.001
) -> "PointTier":
"""
Set timestamps in this tier to be the same as values in the reference tier
Timestamps will only be moved if they are less than maxDifference away from the
reference time.
This can be used to correct minor alignment errors between tiers, as made when
annotating files manually, etc.
Args:
referenceTier: the IntervalTier or PointTier to use as a reference
maxDifference: the maximum amount to allow timestamps to be moved by
Returns:
the modified version of the current tier
"""
referenceTimestamps = referenceTier.timestamps

newEntries = []
for time, label in self.entries:
timeCompare = min(referenceTimestamps, key=lambda x: abs(x - time))

if my_math.lessThanOrEqual(abs(time - timeCompare), maxDifference):
time = timeCompare
newEntries.append((time, label))

return self.new(entries=newEntries)

def editTimestamps(
self,
offset: float,
Expand Down
13 changes: 13 additions & 0 deletions praatio/data_classes/textgrid_tier.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ def __eq__(self, other):
def entries(self):
return tuple(self._entries)

@property
@abstractmethod
def timestamps(self) -> List[float]:
pass

def appendTier(self, tier: "TextgridTier") -> "TextgridTier":
"""Append a tier to the end of this one.
Expand Down Expand Up @@ -209,6 +214,14 @@ def insertEntry(
) -> None: # pragma: no cover
pass

@abstractmethod
def dejitter(
self,
referenceTier: "TextgridTier",
maxDifference: float = 0.001,
) -> "TextgridTier": # pragma: no cover
pass

@abstractmethod
def eraseRegion(
self,
Expand Down
46 changes: 4 additions & 42 deletions praatio/praatio_scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,8 @@ def getValue(myBool) -> Literal["strict", "lax", "truncated"]:
return outputFNList


# TODO: Remove this method in the next major version
# Migrate to using the new Textgridtier.dejitter()
def alignBoundariesAcrossTiers(
tg: textgrid.Textgrid, tierName: str, maxDifference: float = 0.005
) -> textgrid.Textgrid:
Expand Down Expand Up @@ -464,7 +466,7 @@ def alignBoundariesAcrossTiers(
In such a case, choose a smaller maxDifference.
"""
referenceTier = tg.getTier(tierName)
times = _getTimestampsFromTier(referenceTier)
times = referenceTier.timestamps

for time, nextTime in zip(times[1::], times[2::]):
if nextTime - time < maxDifference:
Expand All @@ -480,47 +482,7 @@ def alignBoundariesAcrossTiers(
if tier.name == tierName:
continue

newEntries: list = []
if tier.entryType == constants.Interval:
for start, stop, label in tier.entries:
startCompare = min(times, key=lambda x: abs(x - start))
stopCompare = min(times, key=lambda x: abs(x - stop))

if abs(start - startCompare) <= maxDifference:
start = startCompare
if abs(stop - stopCompare) <= maxDifference:
stop = stopCompare
newEntries.append((start, stop, label))
elif tier.entryType == constants.Point:
for time, label in tier.entries:
timeCompare = min(times, key=lambda x: abs(x - time))

if abs(time - timeCompare) <= maxDifference:
time = timeCompare
newEntries.append((time, label))

tier = tier.new(entries=newEntries)
tier = tier.dejitter(referenceTier, maxDifference)
tg.replaceTier(tier.name, tier)

return tg


def _getTimestampsFromTier(tier: textgrid_tier.TextgridTier) -> List[float]:
"""Get all timestamps used in a tier"""
timestamps = []
if tier.entryType == constants.Interval:
timestamps = [
time
for start, stop, _ in tier.entries
for time in [
start,
stop,
]
]
elif tier.entryType == constants.Point:
timestamps = [time for time, _ in tier.entries]

timestamps = list(set(timestamps))
timestamps.sort()

return timestamps
4 changes: 4 additions & 0 deletions praatio/utilities/my_math.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ def isclose(a: float, b: float, rel_tol: float = 1e-14, abs_tol: float = 0.0) ->
return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)


def lessThanOrEqual(a: float, b: float):
return isclose(a, b) or a < b


def filterTimeSeriesData(
filterFunc: Callable[[List[float], int, bool], List[float]],
featureTimeList: List[list],
Expand Down
15 changes: 8 additions & 7 deletions praatio/utilities/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import subprocess
import itertools
import wave
from pkg_resources import resource_filename
from importlib import resources
from typing_extensions import Literal
from typing import Any, Iterator, List, Tuple, NoReturn, Type, Optional

Expand All @@ -15,11 +15,13 @@

Interval = constants.Interval

# Get the folder one level above the current folder
scriptsPath = resource_filename(
"praatio",
"praatScripts",
)
# New in python 3.9
if hasattr(resources, "files"):
scriptsPath = resources.files("praatio") / "praatScripts"
# Deprecated in python 3.11
else:
with resources.path("praatio", "praatScripts") as path:
scriptsPath = path


def find(list, value, reverse) -> Optional[int]:
Expand Down Expand Up @@ -386,7 +388,6 @@ def findAll(txt: str, subStr: str) -> List[int]:
def runPraatScript(
praatEXE: str, scriptFN: str, argList: List[Any], cwd: str = None
) -> None:

# Popen gives a not-very-transparent error
if not os.path.exists(praatEXE):
raise errors.FileNotFound(praatEXE)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
setup(
name="praatio",
python_requires=">3.6.0",
version="6.0.1",
version="6.1.0",
author="Tim Mahrt",
author_email="timmahrt@gmail.com",
url="https://github.com/timmahrt/praatIO",
Expand Down
Loading

0 comments on commit 5ff7c78

Please sign in to comment.