Skip to content

Commit

Permalink
Merge pull request #183 from vocalpy/add-aud-txt
Browse files Browse the repository at this point in the history
Add 'aud-txt' format that reads Audacity Labeltrack files exported to standard/default .txt format
  • Loading branch information
NickleDave committed May 21, 2022
2 parents 0f9eb2f + 5a490d7 commit 3c623a1
Show file tree
Hide file tree
Showing 471 changed files with 23,762 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
0.0 0.7698181682 SIL
0.7698181682 1.1312740106 call
1.1312740106 2.2184007427 SIL
2.2184007427 2.555023741 call
2.555023741 3.0903094924 SIL
3.0903094924 3.6945753663 Z
3.6945753663 3.8132211772 SIL
3.8132211772 4.0560312087 Ci
4.0560312087 4.8617190407 C
4.8617190407 4.8755150652 SIL
4.8755150652 5.5349650372 H
5.5239282176 6.5558708516 E
6.5558708516 6.5944997203 SIL
6.5944997203 7.0883973981 R
7.0883973981 7.1877287746 SIL
7.1877287746 7.234635258 J1
7.234635258 7.9437509183 J1
7.9437509183 9.0198408308 J2
9.0198408308 9.0695065191 J2
9.0695065191 9.1826339201 SIL
9.1826339201 10.0628202845 B1
10.0628202845 10.076616309 SIL
10.076616309 10.9705986979 B2
10.9705986979 10.9871539273 SIL
10.9871539273 11.8066377837 Q
11.8066377837 11.8645810867 SIL
11.8645810867 12.2039632899 H
12.1901672654 13.2414243338 E
13.2414243338 13.2772939975 SIL
13.2772939975 14.496862565 R
14.496862565 14.6072307612 SIL
14.6072307612 15.2280518646 O
15.2280518646 15.3108280117 SIL
15.3108280117 16.22136563 J1
16.22136563 17.2560674689 J2
17.2560674689 18.1666050872 L
18.1666050872 18.202474751 SIL
18.202474751 19.6538165304 N
19.6538165304 19.7559071119 SIL
19.7559071119 20.7105920086 A
20.7105920086 20.785090541 SIL
20.785090541 20.9671980647 O
20.9671980647 21.0251413676 SIL
21.0251413676 21.3562459561 P
21.3562459561 21.4500589228 SIL
21.4500589228 21.6652769053 K
21.6652769053 21.6735545201 SIL
21.6735545201 22.738607613 V
22.738607613 22.8296613748 SIL
22.8296613748 23.6353492067 J1
23.6353492067 24.5983117182 J2
24.5983117182 24.603830128 SIL
24.603830128 24.6728102506 J2
24.6728102506 24.7721416271 SIL
24.7721416271 25.6826792454 B1
25.6826792454 25.7075120895 SIL
25.7075120895 26.6594377814 B2
26.6594377814 27.7410461037 Q
27.7410461037 27.760360538 SIL
27.760360538 28.3591080022 H
28.3453119777 29.1013341213 E
Empty file.
3 changes: 3 additions & 0 deletions src/crowsetta/data/audtxt/citation.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Giraudon, Juliette, Trouvain, Nathan, Cazala, Aurore, Del Negro, Catherine, & Hinaut, Xavier. (2021).
Labeled songs of domestic canary M1-2016-spring (Serinus canaria) (0.0.2) [Data set].
Zenodo. https://doi.org/10.5281/zenodo.6521932
2 changes: 2 additions & 0 deletions src/crowsetta/data/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class ExampleAnnotFile:


DATA = {
'aud-txt': FormatPathArgs(package='crowsetta.data.audtxt',
resource='405_marron1_June_14_2016_69640887.audacity.txt'),
'birdsong-recognition-dataset': FormatPathArgs(package='crowsetta.data.birdsongrec',
resource='Annotation.xml'),
'generic-seq': FormatPathArgs(package='crowsetta.data.generic',
Expand Down
2 changes: 2 additions & 0 deletions src/crowsetta/formats/seq/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .audtxt import AudTxt
from .birdsongrec import BirdsongRec
from .generic import GenericSeq
from .notmat import NotMat
Expand All @@ -7,6 +8,7 @@
from .yarden import SongAnnotationGUI

__all__ = [
'AudTxt',
'BirdsongRec',
'GenericSeq',
'NotMat',
Expand Down
224 changes: 224 additions & 0 deletions src/crowsetta/formats/seq/audtxt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
"""module for Audacity LabelTrack
in standard/default format exported to .txt files
https://manual.audacityteam.org/man/importing_and_exporting_labels.html#Standard_.28default.29_format
"""
import pathlib
from typing import ClassVar, Optional

import attr
import numpy as np
import pandas as pd
import pandera
from pandera.typing import Series

import crowsetta
from crowsetta.typing import PathLike


class AudTxtSchema(pandera.SchemaModel):
"""A ``pandera.SchemaModel`` that validates ``pandas`` dataframes
loaded from Audacity Labeltrack annotations
exported to .txt files in the standard format
https://manual.audacityteam.org/man/importing_and_exporting_labels.html#Standard_.28default.29_format
"""
start_time: Optional[Series[float]] = pandera.Field()
end_time: Optional[Series[float]] = pandera.Field()
label: Series[pd.StringDtype] = pandera.Field(coerce=True)

class Config:
ordered = True
strict = True


@crowsetta.interface.SeqLike.register
@attr.define
class AudTxt:
"""Class meant to represent
Audacity Labeltrack annotations
exported to .txt files in the standard format
https://manual.audacityteam.org/man/importing_and_exporting_labels.html#Standard_.28default.29_format
The .txt file will have 3 tab-separated columns
that represent the start time, end time, and labels
of annotated regions.
Attributes
----------
name: str
Shorthand name for annotation format: ``'aud-txt'``.
ext: str
Extension of files in annotation format:
``'.txt'``
start_times : numpy.ndarray
Vector of integer sample numbers corresponding
to beginning of segments, i.e. onsets
end_times : numpy.ndarray
Vector of integer sample numbers corresponding
to ends of segments, i.e. offsets
labels : numpy.ndarray
Vector of string labels for segments;
each element is either a single word,
or a single phonetic transcription code.
annot_path : str, pathlib.Path
Path to file from which annotations were loaded.
notated_path : str. pathlib.Path
path to file that ``annot_path`` annotates.
E.g., an audio file, or an array file
that contains a spectrogram generated from audio.
Optional, default is None.
"""
name: ClassVar[str] = 'aud-txt'
ext: ClassVar[str] = '.txt'

start_times: np.ndarray = attr.field(eq=attr.cmp_using(eq=np.array_equal))
end_times: np.ndarray = attr.field(eq=attr.cmp_using(eq=np.array_equal))
labels: np.ndarray = attr.field(eq=attr.cmp_using(eq=np.array_equal))
annot_path: pathlib.Path
notated_path: Optional[pathlib.Path] = attr.field(default=None,
converter=attr.converters.optional(pathlib.Path))

@classmethod
def from_file(cls,
annot_path: PathLike,
notated_path: Optional[PathLike] = None,
) -> 'Self':
"""Load annotations from a file
Parameters
----------
annot_path : str, pathlib.Path
Path to an annotation file,
with one of the extensions {'.csv', '.txt'}.
notated_path : str, pathlib.Path
path to file that ``annot_path`` annotates.
E.g., an audio file, or an array file
that contains a spectrogram generated from audio.
Optional, default is None.
Examples
--------
>>> example = crowsetta.data.get('aud-txt')
>>> with example.annot_path as annot_path:
... simple = crowsetta.formats.seq.AudTxt.from_file(annot_path)
"""
annot_path = pathlib.Path(annot_path)
crowsetta.validation.validate_ext(annot_path, extension=cls.ext)
df = pd.read_csv(annot_path, sep='\t', header=None)
df.columns = ['start_time', 'end_time', 'label']
df = AudTxtSchema.validate(df)

return cls(
start_times=df['start_time'].values,
end_times=df['end_time'].values,
labels=df['label'].values,
annot_path=annot_path,
notated_path=notated_path,
)

def to_seq(self,
round_times: bool = True,
decimals: int = 3) -> crowsetta.Sequence:
"""Convert this annotation to a ``crowsetta.Sequence``.
Parameters
----------
round_times : bool
If True, round onsets_s and offsets_s.
Default is True.
decimals : int
Number of decimals places to round floating point numbers to.
Only meaningful if round_times is True.
Default is 3, so that times are rounded to milliseconds.
Returns
-------
seq : crowsetta.Sequence
Examples
--------
>>> example = crowsetta.data.get('aud-txt')
>>> with example.annot_path as annot_path:
... simple = crowsetta.formats.seq.AudTxt.from_file(annot_path)
>>> seq = simple.to_seq()
Notes
-----
The ``round_times`` and ``decimals`` arguments are provided
to reduce differences across platforms
due to floating point error, e.g. when loading annotation files
and then sending them to a csv file,
the result should be the same on Windows and Linux.
"""
if round_times:
onsets_s = np.around(self.start_times, decimals=decimals)
offsets_s = np.around(self.end_times, decimals=decimals)
else:
onsets_s = self.start_times
offsets_s = self.end_times

seq = crowsetta.Sequence.from_keyword(labels=self.labels,
onsets_s=onsets_s,
offsets_s=offsets_s)
return seq

def to_annot(self,
round_times: bool = True,
decimals: int = 3) -> crowsetta.Annotation:
"""Convert this annotation to a ``crowsetta.Annotation``.
Parameters
----------
round_times : bool
If True, round onsets_s and offsets_s.
Default is True.
decimals : int
Number of decimals places to round floating point numbers to.
Only meaningful if round_times is True.
Default is 3, so that times are rounded to milliseconds.
Returns
-------
annot : crowsetta.Annotation
Examples
--------
>>> example = crowsetta.data.get('aud-txt')
>>> with example.annot_path as annot_path:
... simple = crowsetta.formats.seq.AudTxt.from_file(annot_path)
>>> annot = simple.to_annot()
Notes
-----
The ``round_times`` and ``decimals`` arguments are provided
to reduce differences across platforms
due to floating point error, e.g. when loading annotation files
and then sending them to a csv file,
the result should be the same on Windows and Linux.
"""
seq = self.to_seq(round_times, decimals)
return crowsetta.Annotation(annot_path=self.annot_path, notated_path=self.notated_path, seq=seq)

def to_file(self,
annot_path: PathLike) -> None:
"""save this 'aud-txt' annotation to a .txt file
in the standard/default Audacity LabelTrack format
Parameters
----------
annot_path : str, pathlib.Path
path with filename of .csv file that should be saved
"""
df = pd.DataFrame.from_records(
{'start_time': self.start_times,
'end_time': self.end_times,
'label': self.labels}
)
df = df[['start_time', 'end_time', 'label']] # put in correct order
try:
df = AudTxtSchema.validate(df)
except pandera.errors.SchemaError as e:
raise ValueError(
f'Annotations produced an invalid dataframe, '
f'cannot convert to Audacity LabelTrack .txt file:\n{df}'
) from e
df.to_csv(annot_path, sep='\t', header=False, index=False)
11 changes: 11 additions & 0 deletions tests/data_for_tests/aud-txt/giraudon-et-al-2022/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Audacity standard/default LabelTrack format

From the dataset:
Giraudon, Juliette, Trouvain, Nathan, Cazala, Aurore, Del Negro, Catherine, & Hinaut, Xavier. (2021).
Labeled songs of domestic canary M1-2016-spring (Serinus canaria) (0.0.2) [Data set].
Zenodo. https://doi.org/10.5281/zenodo.6521932

Associated with the paper:
N. Trouvain, X. Hinaut (2021)
Canary Song Decoder: Transduction and Implicit Segmentation with ESNs and LTSMs.
HAL preprint ⟨hal-03203374⟩. https://hal.inria.fr/hal-03203374

0 comments on commit 3c623a1

Please sign in to comment.