""" Copyright 2024 Johns Hopkins University (Author: Jesus Villalba) Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0) """ import logging import re from pathlib import Path from typing import Any, Dict, Optional, Union # import numpy as np import pandas as pd import soundfile as sf from jsonargparse import ActionYesNo, ArgumentParser from ..utils import ClassInfo, HyperDataset, RecordingSet, SegmentSet from ..utils.misc import PathLike from .hf_dataset import HFDatasetDataPrep class FakeCodecDataPrep(HFDatasetDataPrep): """ Prepares the Fake Codec dataset from Hugging Face into structured metadata tables. Attributes: hf_data_path (str | Path | None): Hugging Face dataset ID. corpus_dir (PathLike): Directory where audio is extracted. output_dir (PathLike): Output directory. use_kaldi_ids (bool): Whether to prepend speaker ID to segment ID. target_sample_freq (Optional[int]): Target sample rate. force_download (bool): Whether to overwrite cached data. """ def __init__( self, hf_data_path: Union[PathLike, None], corpus_dir: PathLike, output_dir: PathLike, use_kaldi_ids: bool = False, target_sample_freq: Optional[int] = None, num_threads: int = 10, force_download: bool = False, cache_dir: Optional[str] = None, ): """ Initialize FakeCodecDataPrep instance. """ super().__init__( hf_data_path, corpus_dir, config=None, split="train", output_dir=output_dir, use_kaldi_ids=False, target_sample_freq=target_sample_freq, num_threads=num_threads, force_download=force_download, cache_dir=cache_dir, ) @staticmethod def dataset_name() -> str: """Returns the dataset identifier name.""" return "fake_codec" @staticmethod def add_class_args(parser: ArgumentParser) -> None: """Adds CLI arguments for FakeCodecDataPrep.""" HFDatasetDataPrep.add_class_args(parser) def extract_hf_item(self, item: Dict[str, Any], extract_dir: PathLike): """ Extracts metadata and saves audio for a single Hugging Face dataset row. Args: item (Dict[str, Any]): A single row from the HF dataset. extract_dir (PathLike): Directory to save the audio. Returns: Dict[str, Any]: Metadata including ID, duration, codec, etc. """ seg_id = Path(item["audio"]["path"]).with_suffix("") audio_dir = extract_dir / "audio" if not audio_dir.is_dir(): audio_dir.mkdir(parents=True) storage_path = str(audio_dir / Path(item["audio"]["path"]).with_suffix(".flac")) storage_path_suffix = re.sub(str(self.corpus_dir), "", storage_path)[1:] spoof_det = "spoof" if item["label"] == "spoofing" else "bonafide" spoof_access = "LA" if item["label"] == "spoofing" else None audio = item["audio"]["array"] fs = item["audio"]["sampling_rate"] duration = len(audio) / fs output_item = { "id": seg_id, "storage_path": storage_path_suffix, "speaker": item["speaker_id"], "codec": item["codec_name"], "sample_freq": fs, "spoof_det": spoof_det, "language": "english", "spoof_access": spoof_access, "duration": duration, } sf.write(storage_path, audio, samplerate=fs) return output_item def _prepare_from_meta(self, df_meta: pd.DataFrame) -> None: """ Converts metadata into HyperDataset format (segments, recordings, class infos). Args: df_meta (pd.DataFrame): DataFrame with metadata extracted from HF. """ logging.info("making SegmentsSet") df_segs = df_meta.drop(["storage_path", "sample_freq"], axis=1) df_segs["dataset"] = self.dataset_name() df_segs["corpusid"] = self.dataset_name() segments = SegmentSet(df_segs) logging.info("making RecordingSet") df_recs = df_meta[["id", "storage_path", "duration", "sample_freq"]] if self.target_sample_freq is not None: df_recs["target_sample_freq"] = self.target_sample_freq df_recs["storage_path"] = df_recs["storage_path"].apply( lambda x: self.corpus_dir / x ) recordings = RecordingSet(df_recs) logging.info("making ClassInfos") df_spks = df_meta[["speaker"]].drop_duplicates().sort_values(by="speaker") df_spks.rename(columns={"speaker": "id"}, inplace=True) speakers = ClassInfo(df_spks) df_langs = df_meta[["language"]].drop_duplicates().sort_values(by="language") df_langs.rename(columns={"language": "id"}, inplace=True) languages = ClassInfo(df_langs) df_codecs = df_meta[["codec"]].drop_duplicates().sort_values(by="codec") df_codecs.rename(columns={"codec": "id"}, inplace=True) codecs = ClassInfo(df_codecs) spoof_det = ClassInfo(pd.DataFrame({"id": ["bonafide", "spoof"]})) spoof_access = ClassInfo(pd.DataFrame({"id": ["LA", "PA"]})) classes = { "speaker": speakers, "language": languages, "spoof_det": spoof_det, "spoof_access": spoof_access, "codec": codecs, } logging.info("making dataset") dataset = HyperDataset( segments, classes=classes, recordings=recordings, ) logging.info("saving dataset at %s", self.output_dir) dataset.save(self.output_dir) logging.info( "datasets containts %d segments, %d speakers %f hours", len(segments), len(speakers), segments["duration"].sum() / 3600, )