"""Hugging Face dataset export: audio layout, metadata, card, optional push.""" from __future__ import annotations import csv import json import shutil from pathlib import Path from typing import Dict, List, Optional from .builder import BuildResult from .config import DatasetConfig def _dataset_card(config: DatasetConfig, result: BuildResult, repo_id: str) -> str: labels_table = "\n".join( f"| `{label}` | {count} |" for label, count in sorted(result.label_counts.items()) ) return f"""--- license: cc-by-4.0 pretty_name: {config.dataset_name} wake word synthetic speech dataset task_categories: - audio-classification tags: - audio - speech - wake-word - keyword-spotting - text-to-speech - synthetic-data - edge-impulse - tinyml size_categories: - n<1K --- # {config.dataset_name} — Wake Word Synthetic Speech Dataset Synthetic, augmented audio for training a small wake-word / keyword-spotting model. Generated with **{result.backend_source}** and local audio augmentation. ## Classes | Label | Samples | |---|---| {labels_table} - `{config.wake_label}` — the target wake phrase and close variants. - `{config.unknown_label}` — near-miss and unrelated short phrases. - `{config.noise_label}` — synthetic background noise. ## Audio Specification | Property | Value | |---|---| | Format | WAV | | Channels | Mono | | Sample rate | {config.sample_rate_hz} Hz | | Clip length | {config.duration_seconds} seconds | ## Layout ```text audio/ train/ {config.wake_label}..wav ... test/ {config.wake_label}..wav ... metadata.csv hf_metadata.csv selected_voices.csv dataset_summary.json ``` ## Loading ```python from datasets import load_dataset, Audio ds = load_dataset("{repo_id}") ds = ds.cast_column("audio", Audio(sampling_rate={config.sample_rate_hz})) print(ds) ``` ## Edge Impulse Filenames follow the Edge Impulse label-prefix convention (`{config.wake_label}..wav`) so they upload directly: ```bash edge-impulse-uploader --category training audio/train/*.wav edge-impulse-uploader --category testing audio/test/*.wav ``` ## Limitations Synthetic TTS is a bootstrap, not a production benchmark. Add real device and environment recordings before deploying a wake-word product. ## License CC BY 4.0. Verify that your use of the generated synthetic speech complies with the terms of the voice models and tools used to create it. """ def export_hf_dataset( config: DatasetConfig, result: BuildResult, hf_dir: str, repo_id: str = "your-username/your-dataset", ) -> str: """Build a Hugging Face-ready folder from a completed build. Returns its path.""" source_dir = Path(result.out_dir) hf_path = Path(hf_dir).resolve() if hf_path.exists(): shutil.rmtree(hf_path) (hf_path / "audio" / "train").mkdir(parents=True, exist_ok=True) (hf_path / "audio" / "test").mkdir(parents=True, exist_ok=True) for src_split, hf_split in (("training", "train"), ("testing", "test")): src = source_dir / "edge_impulse_upload" / src_split dst = hf_path / "audio" / hf_split for wav in sorted(src.glob("*.wav")): shutil.copy2(wav, dst / wav.name) for name in ("metadata.csv", "selected_voices.csv", "dataset_summary.json"): src = source_dir / name if src.exists(): shutil.copy2(src, hf_path / name) # HF metadata.csv-style index (audio path + label). hf_rows: List[Dict[str, str]] = [] for split in ("train", "test"): for wav in sorted((hf_path / "audio" / split).glob("*.wav")): hf_rows.append( { "audio": str(wav.relative_to(hf_path)), "label": wav.name.split(".")[0], "split": split, "filename": wav.name, } ) with (hf_path / "hf_metadata.csv").open("w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=["audio", "label", "split", "filename"]) writer.writeheader() writer.writerows(hf_rows) (hf_path / "README.md").write_text( _dataset_card(config, result, repo_id), encoding="utf-8" ) (hf_path / "hf_dataset_summary.json").write_text( json.dumps({"total_wavs": len(hf_rows), "repo_id": repo_id}, indent=2), encoding="utf-8", ) return str(hf_path) def push_to_hub(hf_dir: str, repo_id: str, token: str, private: bool = False) -> str: """Upload the HF dataset folder to the Hub. Returns the dataset URL.""" from huggingface_hub import HfApi api = HfApi(token=token) api.create_repo(repo_id=repo_id, repo_type="dataset", exist_ok=True, private=private) api.upload_folder( folder_path=hf_dir, repo_id=repo_id, repo_type="dataset", ) return f"https://huggingface.co/datasets/{repo_id}"