{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# VMC2026 Track 2 — Chuẩn bị data (gộp ESD + DailyTalk) trên Kaggle\n", "\n", "Gói Track 2 thiếu **1.417 mẫu giọng thật** (license tách ra):\n", "- **sys006** = ESD (1.379 file) · **sys001** = DailyTalk (38 file)\n", "\n", "Notebook này: cài **SoX** + build **sv56** → gom đúng utterance từ ESD/DailyTalk →\n", "**chuẩn hóa âm lượng** (giống mẫu TTS) → ráp vào `wav/` đủ **15.477 file**.\n", "\n", "### Cách dùng\n", "1. Settings → **Internet = On** (cần tải/biên dịch sv56). GPU không bắt buộc.\n", "2. **+ Add Input** 3 dataset:\n", " - Gói Track 2 (`vmc2026_track2_..._v3.tar.gz` — Kaggle tự giải nén ra `vmc2026-track2/`).\n", " - ESD: `Emotional Speech Dataset (ESD).zip` (Kaggle tự giải nén ra `Emotion Speech Dataset/`).\n", " - DailyTalk: `dailytalk.zip` (giải nén ra `dailytalk/data/...`).\n", "3. **Run All**. Xong → **Save Version** (Commit) để lưu `wav/` ra output.\n", "4. Từ output đó → **Create Dataset** → dùng làm input cho notebook train/baseline.\n", "\n", "> Notebook tự dò vị trí ESD/DailyTalk dù Kaggle giải nén ra thư mục tên gì." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 0. Tìm gói Track 2 + copy ra thư mục ghi được" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import os, glob, shutil, subprocess\n", "\n", "# Tự dò thư mục vmc2026-track2 trong mọi input đã add.\n", "_cands = glob.glob(\"/kaggle/input/*/vmc2026-track2\") + glob.glob(\"/kaggle/input/**/vmc2026-track2\", recursive=True)\n", "TRACK2_SRC = _cands[0] if _cands else None\n", "assert TRACK2_SRC, \"Không thấy thư mục vmc2026-track2 — đã Add Input gói Track 2 chưa?\"\n", "\n", "WORK = \"/kaggle/working/vmc2026-track2\" # bản ghi được (input là read-only)\n", "print(\"Track2 source :\", TRACK2_SRC)\n", "\n", "# Copy toàn bộ gói ra working (gồm wav/ 14060 file + scripts + csv). Mất vài phút.\n", "if not os.path.exists(WORK):\n", " print(\"Đang copy gói Track 2 ra working (vài phút)...\")\n", " shutil.copytree(TRACK2_SRC, WORK)\n", "print(\"Số wav hiện có:\", len(os.listdir(f\"{WORK}/wav\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Cài SoX + build sv56 (cần Internet = On)\n", "sv56 = công cụ chuẩn hóa âm lượng của ITU-T, build từ source openitu/STL." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def sh(cmd):\n", " print(\"$\", cmd)\n", " print(subprocess.run(cmd, shell=True, capture_output=True, text=True).stdout[-2000:])\n", "\n", "# SoX + trình biên dịch (để build sv56)\n", "sh(\"apt-get -qq update && apt-get -qq install -y sox make gcc\")\n", "sh(\"which sox && sox --version\")\n", "\n", "# sv56demo\n", "SV56_DIR = \"/kaggle/working/STL-2009\"\n", "SV56_BIN_DIR = f\"{SV56_DIR}/src/sv56\"\n", "if not os.path.exists(f\"{SV56_BIN_DIR}/sv56demo\"):\n", " sh(\"cd /kaggle/working && wget -q https://github.com/openitu/STL/archive/refs/tags/v2009.tar.gz\")\n", " sh(\"cd /kaggle/working && tar -xf v2009.tar.gz\")\n", " sh(f\"cd {SV56_BIN_DIR} && make -f makefile.unx\")\n", "assert os.path.exists(f\"{SV56_BIN_DIR}/sv56demo\"), \"Build sv56 thất bại — kiểm tra Internet=On + log make.\"\n", "\n", "# Đưa cả sox và sv56demo vào PATH cho các script .sh dùng được\n", "os.environ[\"PATH\"] = SV56_BIN_DIR + \":\" + os.environ[\"PATH\"]\n", "sh(\"which sv56demo\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Dò vị trí ESD + DailyTalk (tự tìm dù tên thư mục khác nhau)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def find_root(rel_path):\n", " \"\"\"Tìm thư mục ROOT trong /kaggle/input sao cho ROOT/rel_path tồn tại.\"\"\"\n", " base = os.path.basename(rel_path)\n", " for hit in glob.glob(f\"/kaggle/input/**/{base}\", recursive=True):\n", " if hit.endswith(rel_path.replace(\"/\", os.sep)) or hit.endswith(rel_path):\n", " return hit[: -len(rel_path)].rstrip(\"/\")\n", " return None\n", "\n", "# ESD: dòng CSV \"0014/Angry/000381.wav\" → file thật là \"0014/Angry/0014_000381.wav\"\n", "_esd_first = open(f\"{WORK}/ESD_utts_train_dev.csv\").readline().strip().split(\",\")[0]\n", "_p = _esd_first.split(\"/\") # [spk, emo, uttID.wav]\n", "ESD_REL = f\"{_p[0]}/{_p[1]}/{_p[0]}_{_p[2]}\"\n", "ESD_ROOT = find_root(ESD_REL)\n", "\n", "# DailyTalk: dòng CSV \"1020/0_1_d1020.wav\" → file thật \".../data/1020/0_1_d1020.wav\"\n", "_dt_first = open(f\"{WORK}/DT_utts_train_dev.csv\").readline().strip().split(\",\")[0]\n", "DT_ROOT = find_root(_dt_first) # ROOT sao cho ROOT/1020/0_1_d1020.wav tồn tại\n", "\n", "print(\"ESD_REL :\", ESD_REL)\n", "print(\"ESD_ROOT :\", ESD_ROOT)\n", "print(\"DT_ROOT :\", DT_ROOT)\n", "assert ESD_ROOT, \"Không thấy ESD — đã Add Input 'Emotional Speech Dataset (ESD).zip' chưa?\"\n", "assert DT_ROOT, \"Không thấy DailyTalk — đã Add Input 'dailytalk.zip' chưa?\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Gom các utterance cần dùng → thư mục gathered/" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "GATHERED = f\"{WORK}/gathered\"\n", "os.makedirs(GATHERED, exist_ok=True)\n", "\n", "# ESD: copy ROOT/spk/emo/spk_uttID → gathered/\n", "n_esd = 0\n", "for line in open(f\"{WORK}/ESD_utts_train_dev.csv\"):\n", " src_rel, dst = line.strip().split(\",\")\n", " p = src_rel.split(\"/\")\n", " src = f\"{ESD_ROOT}/{p[0]}/{p[1]}/{p[0]}_{p[2]}\"\n", " if os.path.exists(src):\n", " shutil.copy(src, f\"{GATHERED}/{dst}\")\n", " n_esd += 1\n", " else:\n", " print(\"ESD thiếu:\", src)\n", "\n", "# DailyTalk: copy ROOT/parts[0] → gathered/\n", "n_dt = 0\n", "for line in open(f\"{WORK}/DT_utts_train_dev.csv\"):\n", " src_rel, dst = line.strip().split(\",\")\n", " src = f\"{DT_ROOT}/{src_rel}\"\n", " if os.path.exists(src):\n", " shutil.copy(src, f\"{GATHERED}/{dst}\")\n", " n_dt += 1\n", " else:\n", " print(\"DailyTalk thiếu:\", src)\n", "\n", "print(f\"Đã gom: ESD {n_esd}/1379 · DailyTalk {n_dt}/38 · tổng {len(os.listdir(GATHERED))}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Chuẩn hóa âm lượng bằng sv56 (mức -26 dB, giữ nguyên sample rate)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Dùng chính script gốc trong gói: batch_normRMSE.sh → tạo *_norm.wav trong gathered/\n", "sh(f\"bash {WORK}/sv56scripts/batch_normRMSE.sh {GATHERED}\")\n", "\n", "# Move các file đã chuẩn hóa vào wav/ với đúng tên (bỏ hậu tố _norm)\n", "moved = 0\n", "for n in os.listdir(GATHERED):\n", " if n.endswith(\"_norm.wav\"):\n", " final = \"_\".join(n.split(\"_\")[:-1]) + \".wav\" # bỏ \"_norm\"\n", " shutil.move(f\"{GATHERED}/{n}\", f\"{WORK}/wav/{final}\")\n", " moved += 1\n", "print(\"Đã chuẩn hóa & move vào wav/:\", moved, \"file\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. Kiểm tra đủ 15.477 + dọn rác" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "total = len(glob.glob(f\"{WORK}/wav/*.wav\"))\n", "print(\"Tổng wav trong wav/:\", total)\n", "if total == 15477:\n", " shutil.rmtree(GATHERED, ignore_errors=True)\n", " # Dọn artifact build để dataset lưu ra GỌN (chỉ giữ vmc2026-track2/)\n", " shutil.rmtree(SV56_DIR, ignore_errors=True)\n", " for f in glob.glob(\"/kaggle/working/v2009.tar.gz\"):\n", " os.remove(f)\n", " print(\"✅ ĐỦ 15.477 file. Sẵn sàng Save Version → tạo Dataset.\")\n", " print(\" Dùng dataset này cho notebook baseline: DATA_ROOT = '/kaggle/input//vmc2026-track2'\")\n", "else:\n", " print(f\"⚠️ Chưa đủ (đang {total}). Kiểm tra log bước 3-4 xem ESD/DailyTalk có thiếu file nào.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Ghi chú\n", "- Output nặng (~2-3GB do 15.477 wav). `wav/` đã gồm cả train+dev nên dùng được cho cả fine-tune lẫn inference.\n", "- sv56 chuẩn hóa để mẫu ESD/DailyTalk cùng mức âm lượng với mẫu TTS → tránh model bị nhiễu bởi độ to.\n", "- Nếu Internet Off: SoX có thể có sẵn nhưng KHÔNG build được sv56 → bắt buộc bật Internet." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }