RuiRuiHigh
Initial Hyperion MT deepfake detector upload
acb3a72
Raw
History Blame Contribute Delete
10.8 kB
"""
Copyright 2024 Johns Hopkins University (Author: Jesus Villalba)
Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
"""
import glob
import logging
from pathlib import Path
from typing import Optional
import numpy as np
import pandas as pd
from jsonargparse import ArgumentParser
from ..utils import ClassInfo, HyperDataset, RecordingSet, SegmentSet
from ..utils.misc import PathLike
from ..utils.scp_list import SCPList
from .data_prep import DataPrep
class LibriSpeechDataPrep(DataPrep):
"""
Prepares the LibriSpeech dataset into structured tables.
This class processes a specified subset of the LibriSpeech corpus, including
speaker and chapter metadata, transcripts, and audio durations. It produces
a dataset suitable for training or evaluating speech and speaker recognition systems.
Attributes:
corpus_dir (PathLike): Path to the base LibriSpeech directory.
subset (str): Subset to process (e.g., "train-clean-100").
output_dir (PathLike): Output directory for the prepared dataset.
use_kaldi_ids (bool): Whether to prepend speaker ID to segment ID.
target_sample_freq (Optional[int]): Optional resampling target frequency.
num_threads (int): Number of threads to use for audio duration extraction.
"""
def __init__(
self,
corpus_dir: PathLike,
subset: str,
output_dir: PathLike,
use_kaldi_ids: bool = False,
target_sample_freq: Optional[int] = None,
num_threads: int = 10,
):
"""Initializes the LibriSpeech data preparation object."""
super().__init__(corpus_dir, output_dir, False, target_sample_freq, num_threads)
self.subset = subset
@staticmethod
def dataset_name() -> str:
"""Returns the dataset identifier name."""
return "librispeech"
@staticmethod
def add_class_args(parser: ArgumentParser) -> None:
"""Adds LibriSpeech-specific arguments to an argument parser."""
DataPrep.add_class_args(parser)
parser.add_argument(
"--subset",
choices=[
"train-clean-100",
"train-clean-360",
"train-other",
"dev-clean",
"dev-other",
"test-clean",
"test-other",
],
help="""if we prepare the data for ["train-clean-100", "train-clean-360", "train-other", "dev-clean", "dev-other", "test-clean", "test-other"]""",
required=True,
)
def _get_docs_dir(self) -> Path:
"""
Resolves the location of the metadata documentation directory.
Returns:
Path: Path to the directory containing documentation files.
"""
docs_dir = self.corpus_dir / "docs"
if docs_dir.is_dir():
return docs_dir
else:
return self.corpus_dir
def _get_spks_metadata(self) -> pd.DataFrame:
"""
Loads speaker metadata from SPEAKERS.TXT.
Returns:
pd.DataFrame: DataFrame with speaker ID and gender.
"""
docs_dir = self._get_docs_dir()
file_path = docs_dir / "SPEAKERS.TXT"
# read in two parts because of bad formating of speakers.txt
df = pd.read_csv(
file_path,
sep="\s*\|\s*",
header=None,
names=["id", "gender", "subset", "min", "name"],
skiprows=12,
engine="python",
dtype={"id": str},
on_bad_lines="skip",
)
df2 = pd.read_csv(
file_path,
sep="\s*\|\s*",
header=None,
names=["id", "gender", "subset", "min", "name", "d1", "d2"],
skiprows=44,
engine="python",
dtype={"id": str},
nrows=1,
)
df2.drop(columns=["d1", "d2"], inplace=True)
df = pd.concat([df, df2], ignore_index=True)
df["id"] = df["id"].apply(lambda x: f"libri-{x}")
df = df.loc[df["subset"] == self.subset]
df["gender"] = df["gender"].apply(lambda x: x.lower())
df.drop(columns=["subset", "min", "name"], inplace=True)
return df
def _get_chapters_metadata(self) -> tuple[pd.DataFrame, pd.DataFrame]:
"""
Loads metadata for books and chapters.
Returns:
Tuple[pd.DataFrame, pd.DataFrame]: Book and chapter metadata tables.
"""
docs_dir = self._get_docs_dir()
file_path = docs_dir / "BOOKS.TXT"
df_books = pd.read_csv(
file_path,
sep="\s*\|\s*",
header=None,
names=["book", "book_title", "dummy"],
engine="python",
dtype={"book": str},
)
df_books.drop(columns=["dummy"], inplace=True)
file_path = docs_dir / "CHAPTERS.TXT"
df_chapters = pd.read_csv(
file_path,
sep="\s*\|\s*",
header=None,
names=[
"chapter",
"speaker",
"mins",
"subset",
"proj",
"book",
"chapter_title",
"proj_title",
],
skiprows=14,
engine="python",
dtype={"chapter": str, "book": str, "speaker": str},
)
df_chapters = df_chapters.merge(df_books, how="left", on="book")
df_chapters["speaker"] = df_chapters["speaker"].apply(lambda x: f"libri-{x}")
df_chapters.drop(columns=["proj", "mins", "proj_title"], inplace=True)
return df_books, df_chapters
def _get_transcripts(self, rec_dir: Path) -> pd.DataFrame:
"""
Loads transcript files and returns a mapping of ID to transcript text.
Args:
rec_dir (Path): Directory where audio and transcript files are located.
Returns:
pd.DataFrame: DataFrame with columns ['id', 'transcript'].
"""
logging.info("searching transcript files in %s", str(rec_dir))
trans_files = list(rec_dir.glob("**/*.trans.txt"))
if not trans_files:
# symlinks? try glob
trans_files = [
Path(f) for f in glob.iglob(f"{rec_dir}/**/.*trans.txt", recursive=True)
]
assert len(trans_files) > 0, "transcript files not found"
dfs = []
for trans_file in trans_files:
scp = SCPList.load(trans_file)
df_i = pd.DataFrame({"id": scp.key, "transcript": scp.file_path})
dfs.append(df_i)
df_trans = pd.concat(dfs)
df_trans["id"] = df_trans["id"].apply(lambda x: f"librispeech-{x}")
return df_trans
def prepare(self) -> None:
"""
Executes the full LibriSpeech preparation pipeline:
- Loads audio files and metadata (speakers, chapters, books, transcripts)
- Extracts audio durations
- Builds segment, recording, and class info tables
- Writes the final dataset to output_dir
"""
logging.info(
"Peparing Libri %s corpus_dir:%s -> data_dir:%s",
self.subset,
self.corpus_dir,
self.output_dir,
)
df_spks = self._get_spks_metadata()
df_books, df_chapters = self._get_chapters_metadata()
rec_dir = self.corpus_dir / self.subset
logging.info("searching audio files in %s", str(rec_dir))
rec_files = list(rec_dir.glob("**/*.flac"))
if not rec_files:
# symlinks? try glob
rec_files = [
Path(f) for f in glob.iglob(f"{rec_dir}/**/*.flac", recursive=True)
]
assert len(rec_files) > 0, "recording files not found"
df_trans = self._get_transcripts(rec_dir)
speakers = ["libri-" + f.parent.parent.name for f in rec_files]
chapters = [f.parent.name for f in rec_files]
rec_ids = ["librispeech-" + f.with_suffix("").name for f in rec_files]
if self.use_kaldi_ids:
rec_ids = [f"{s}-{f}" for f, s in zip(rec_ids, speakers)]
file_paths = [str(r) for r in rec_files]
logging.info("making RecordingSet")
recs = pd.DataFrame({"id": rec_ids, "storage_path": file_paths})
recs = RecordingSet(recs)
recs.sort()
logging.info("getting recording durations")
recs.get_durations(self.num_threads)
if self.target_sample_freq:
recs["target_sample_freq"] = self.target_sample_freq
logging.info("making SegmentsSet")
df_segs = pd.DataFrame(
{"id": rec_ids, "speaker": speakers, "chapter": chapters}
)
df_segs = df_segs.merge(
df_spks, how="left", left_on="speaker", right_on="id", suffixes=(None, "_y")
)
df_segs.drop(columns=["id_y"], inplace=True)
df_segs = df_segs.merge(df_trans, how="left", on="id")
df_segs["duration"] = recs.loc[df_segs["id"], "duration"].values
df_segs = df_segs.merge(df_chapters, how="left", on="chapter")
df_segs.rename(columns={"speaker_x": "speaker"}, inplace=True)
df_segs.drop(columns=["speaker_y"], inplace=True)
df_segs["language"] = "eng"
df_segs["corpusid"] = "librivox"
df_segs["dataset"] = "librispeech"
df_segs["original_bandwidth"] = 8000
segments = SegmentSet(df_segs)
segments.sort()
logging.info("making speaker info file")
df_spks = df_spks.loc[df_spks["id"].isin(df_segs["speaker"].values)]
speakers = ClassInfo(df_spks)
logging.info("making book info file")
df_books.rename(columns={"book": "id"}, inplace=True)
df_books = df_books.loc[df_books["id"].isin(df_segs["book"].values)]
books = ClassInfo(df_books)
logging.info("making chapter info file")
df_chapters.rename(columns={"chapter": "id"}, inplace=True)
df_chapters = df_chapters.loc[df_chapters["id"].isin(df_segs["chapter"].values)]
chapters = ClassInfo(df_chapters)
logging.info("making language info file")
languages = ClassInfo(pd.DataFrame({"id": ["eng"]}))
logging.info("making gender info file")
genders = ClassInfo(pd.DataFrame({"id": ["m", "f"]}))
classes = {
"speaker": speakers,
"book": books,
"chapter": chapters,
"language": languages,
"gender": genders,
}
logging.info("making dataset")
dataset = HyperDataset(
segments,
classes=classes,
recordings=recs,
)
logging.info("saving dataset at %s", self.output_dir)
dataset.save(self.output_dir)
dataset.describe()