From 506bb16cccae7d4abd80ae6ab8df1e31018b2edf Mon Sep 17 00:00:00 2001 From: Wannaphong Phatthiyaphaibun Date: Wed, 24 Jan 2024 13:46:39 +0700 Subject: [PATCH 01/19] Update use_lunarlist_model.ipynb --- notebook/use_lunarlist_model.ipynb | 1616 ++++++++++++++++++++++++++-- 1 file changed, 1551 insertions(+), 65 deletions(-) diff --git a/notebook/use_lunarlist_model.ipynb b/notebook/use_lunarlist_model.ipynb index 2e4b617..82cceb0 100644 --- a/notebook/use_lunarlist_model.ipynb +++ b/notebook/use_lunarlist_model.ipynb @@ -2,18 +2,33 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { - "id": "RzK2lnjPJ0DV" + "id": "RzK2lnjPJ0DV", + "outputId": "109120f8-0c42-4ea3-a2ea-53c05f1fdf5d", + "colab": { + "base_uri": "https://localhost:8080/" + } }, - "outputs": [], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m6.4/6.4 MB\u001b[0m \u001b[31m18.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m46.0/46.0 kB\u001b[0m \u001b[31m3.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m86.8/86.8 kB\u001b[0m \u001b[31m7.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h" + ] + } + ], "source": [ "!pip install -q pythaitts" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "id": "1yNy8jUhBsPZ" }, @@ -24,18 +39,138 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": { - "id": "rRxhLoMVCxQV" + "id": "rRxhLoMVCxQV", + "outputId": "6cee640f-1a60-444a-c4eb-578caaeabf0a", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 277, + "referenced_widgets": [ + "f29548216be247579a6ef8c6c3b541b1", + "3f85d476f9634589b79d459a93725ed2", + "ad3acac8f15f45398570f8faa508a830", + "bef53bb7447145be9825dbedc64553bc", + "8ea03986a66442ebb2a2644b677fb034", + "eb2f604604a946d888a9c8bdba4e1cff", + "d37c7176732d4867a907a0ac23110b30", + "ed0ce8d7dc134bd59cd837137f623be9", + "ec45adb66db1414ba1eb95152edac653", + "03605bc21bf4440083adeaa1c5e6ed65", + "d11af55234bd4e28868687fd3fb4e5a3", + "3d7e19cc96de47a6a0f2353f8a2ddcbb", + "c84209b8b64e442ab341695ec61f61d8", + "e391d7de3b684a018593e5242c6d48e4", + "2ab12fdf67b140e7b6cd1bc8ead10d05", + "0f04b4b396b44b279c2e9a636a809d4c", + "0cdcedcb208e4a95a6e5c8680e10bcc5", + "8f92d7c7ff8c41928ad548684db8f3e7", + "ae19dec5c2d34a21a7678b5b0a0781b5", + "6f6d20e531144031ab09729c42d9c403", + "ce8596720bdf4761b78d337b6679ee54", + "a358e9a724044300905692c7969023ab", + "19e54ee3ba374df18b594a3877a70793", + "f69a394de2d3433c89f566e268ef2488", + "d889b84de25d4fe4be49bd6f0d59554b", + "3a1f0a997ca04ba7bc04c1c8637071af", + "d2e106b0872d43e7be6d39c025e262d2", + "e92e2d04083a429f8df7542a8469bc21", + "8e1661cd4ea14251bdb6449b2d3679fa", + "ad99e7252212478089e7189142efda34", + "1a71c61bb2a1440bb95d601dc1bdd8e0", + "369503fedf2b4364b0ccff398e65f830", + "7284c0f550bc459a87b9f6cc14486846", + "ff1994286e7546d4a0bd48d23364e76e", + "b0dc802bca6b4699b4fe5cdaee4af3dd", + "bf588e10924e473bad4871fb48f6cd18", + "5413b15f27f1494fb36059a87a8c720a", + "db79b68c7ffe4471ae032126a17e8466", + "ff3f75bc2ff9491dacdf726ae22fd246", + "0a780a68ef6c431aa420ec940a2543bb", + "ac5b23b651184262a3e925220ff5da49", + "fad5b0c23ace43ce912fef46f171aacc", + "b3c3f674be884c1a8c22dbbf94f7a8a7", + "c8c6ccda72614bc4bc6c80fc98ae0b67" + ] + } }, - "outputs": [], + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "/usr/local/lib/python3.10/dist-packages/huggingface_hub/utils/_token.py:88: UserWarning: \n", + "The secret `HF_TOKEN` does not exist in your Colab secrets.\n", + "To authenticate with the Hugging Face Hub, create a token in your settings tab (https://huggingface.co/settings/tokens), set it as secret in your Google Colab and restart your session.\n", + "You will be able to reuse this secret in all of your notebooks.\n", + "Please note that authentication is recommended but still optional to access public models or datasets.\n", + " warnings.warn(\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "tacotron2encoder-th.onnx: 0%| | 0.00/22.5M [00:00" + ], "text/html": [ "\n", " \n", " " - ], - "text/plain": [ - "" ] }, - "execution_count": 5, "metadata": {}, - "output_type": "execute_result" + "execution_count": 6 } ], "source": [ "IPython.display.Audio(file)" ] }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[-0.00015215, -0.00042837, -0.00048692, ..., 0.00796382,\n", - " 0.00965735, 0.01095489]], dtype=float32)" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "wave[0][0]" - ] - }, { "cell_type": "code", "execution_count": 10, @@ -120,36 +234,36 @@ "height": 60 }, "id": "pkoPFBU4rUQX", - "outputId": "f616231a-6ec1-4d22-ecc6-81f561c59c09" + "outputId": "37db86e9-cfa8-42e0-cd7d-0b02eaf6ace7" }, "outputs": [ { + "output_type": "execute_result", "data": { + "text/plain": [ + "" + ], "text/html": [ "\n", " \n", " " - ], - "text/plain": [ - "" ] }, - "execution_count": 10, "metadata": {}, - "output_type": "execute_result" + "execution_count": 10 } ], "source": [ "wave = tts.tts(\"ภาษาไทย ง่าย มาก มาก\",return_type=\"waveform\")\n", - "IPython.display.Audio(wave[0][0, 0, :], rate=22050) # load a NumPy array" + "IPython.display.Audio(wave, rate=22050) # load a NumPy array" ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 11, "metadata": { "id": "xa21tvyvVtFU" }, @@ -160,33 +274,33 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 12, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 60 }, "id": "BNmVNV7WVvLh", - "outputId": "465b508d-42db-4bc5-c39e-64b3eeca963d" + "outputId": "d22a30fe-760f-4c5c-ee35-7fda493e9b7a" }, "outputs": [ { + "output_type": "execute_result", "data": { + "text/plain": [ + "" + ], "text/html": [ "\n", " \n", " " - ], - "text/plain": [ - "" ] }, - "execution_count": 24, "metadata": {}, - "output_type": "execute_result" + "execution_count": 12 } ], "source": [ @@ -195,7 +309,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 13, "metadata": { "id": "I6gdBKvyVwSq" }, @@ -206,33 +320,33 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 14, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 60 }, "id": "HlEm7UngqAeL", - "outputId": "ce2c1e2e-8658-4d4f-d270-f234fa77d0de" + "outputId": "7cc2027e-6f00-497c-9707-8ecf0d5527c1" }, "outputs": [ { + "output_type": "execute_result", "data": { + "text/plain": [ + "" + ], "text/html": [ "\n", " \n", " " - ], - "text/plain": [ - "" ] }, - "execution_count": 26, "metadata": {}, - "output_type": "execute_result" + "execution_count": 14 } ], "source": [ @@ -275,8 +389,1380 @@ "interpreter": { "hash": "a1d6ff38954a1cdba4cf61ffa51e42f4658fc35985cd256cd89123cae8466a39" } + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "f29548216be247579a6ef8c6c3b541b1": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_3f85d476f9634589b79d459a93725ed2", + "IPY_MODEL_ad3acac8f15f45398570f8faa508a830", + "IPY_MODEL_bef53bb7447145be9825dbedc64553bc" + ], + "layout": "IPY_MODEL_8ea03986a66442ebb2a2644b677fb034" + } + }, + "3f85d476f9634589b79d459a93725ed2": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_eb2f604604a946d888a9c8bdba4e1cff", + "placeholder": "​", + "style": "IPY_MODEL_d37c7176732d4867a907a0ac23110b30", + "value": "tacotron2encoder-th.onnx: 100%" + } + }, + "ad3acac8f15f45398570f8faa508a830": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_ed0ce8d7dc134bd59cd837137f623be9", + "max": 22454384, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_ec45adb66db1414ba1eb95152edac653", + "value": 22454384 + } + }, + "bef53bb7447145be9825dbedc64553bc": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_03605bc21bf4440083adeaa1c5e6ed65", + "placeholder": "​", + "style": "IPY_MODEL_d11af55234bd4e28868687fd3fb4e5a3", + "value": " 22.5M/22.5M [00:00<00:00, 86.4MB/s]" + } + }, + "8ea03986a66442ebb2a2644b677fb034": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "eb2f604604a946d888a9c8bdba4e1cff": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d37c7176732d4867a907a0ac23110b30": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "ed0ce8d7dc134bd59cd837137f623be9": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "ec45adb66db1414ba1eb95152edac653": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "03605bc21bf4440083adeaa1c5e6ed65": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d11af55234bd4e28868687fd3fb4e5a3": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "3d7e19cc96de47a6a0f2353f8a2ddcbb": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_c84209b8b64e442ab341695ec61f61d8", + "IPY_MODEL_e391d7de3b684a018593e5242c6d48e4", + "IPY_MODEL_2ab12fdf67b140e7b6cd1bc8ead10d05" + ], + "layout": "IPY_MODEL_0f04b4b396b44b279c2e9a636a809d4c" + } + }, + "c84209b8b64e442ab341695ec61f61d8": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_0cdcedcb208e4a95a6e5c8680e10bcc5", + "placeholder": "​", + "style": "IPY_MODEL_8f92d7c7ff8c41928ad548684db8f3e7", + "value": "tacotron2decoder-th.onnx: 100%" + } + }, + "e391d7de3b684a018593e5242c6d48e4": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_ae19dec5c2d34a21a7678b5b0a0781b5", + "max": 72773451, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_6f6d20e531144031ab09729c42d9c403", + "value": 72773451 + } + }, + "2ab12fdf67b140e7b6cd1bc8ead10d05": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_ce8596720bdf4761b78d337b6679ee54", + "placeholder": "​", + "style": "IPY_MODEL_a358e9a724044300905692c7969023ab", + "value": " 72.8M/72.8M [00:00<00:00, 85.7MB/s]" + } + }, + "0f04b4b396b44b279c2e9a636a809d4c": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0cdcedcb208e4a95a6e5c8680e10bcc5": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8f92d7c7ff8c41928ad548684db8f3e7": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "ae19dec5c2d34a21a7678b5b0a0781b5": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "6f6d20e531144031ab09729c42d9c403": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "ce8596720bdf4761b78d337b6679ee54": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a358e9a724044300905692c7969023ab": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "19e54ee3ba374df18b594a3877a70793": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_f69a394de2d3433c89f566e268ef2488", + "IPY_MODEL_d889b84de25d4fe4be49bd6f0d59554b", + "IPY_MODEL_3a1f0a997ca04ba7bc04c1c8637071af" + ], + "layout": "IPY_MODEL_d2e106b0872d43e7be6d39c025e262d2" + } + }, + "f69a394de2d3433c89f566e268ef2488": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_e92e2d04083a429f8df7542a8469bc21", + "placeholder": "​", + "style": "IPY_MODEL_8e1661cd4ea14251bdb6449b2d3679fa", + "value": "tacotron2postnet-th.onnx: 100%" + } + }, + "d889b84de25d4fe4be49bd6f0d59554b": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_ad99e7252212478089e7189142efda34", + "max": 17377894, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_1a71c61bb2a1440bb95d601dc1bdd8e0", + "value": 17377894 + } + }, + "3a1f0a997ca04ba7bc04c1c8637071af": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_369503fedf2b4364b0ccff398e65f830", + "placeholder": "​", + "style": "IPY_MODEL_7284c0f550bc459a87b9f6cc14486846", + "value": " 17.4M/17.4M [00:00<00:00, 81.0MB/s]" + } + }, + "d2e106b0872d43e7be6d39c025e262d2": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e92e2d04083a429f8df7542a8469bc21": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8e1661cd4ea14251bdb6449b2d3679fa": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "ad99e7252212478089e7189142efda34": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "1a71c61bb2a1440bb95d601dc1bdd8e0": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "369503fedf2b4364b0ccff398e65f830": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "7284c0f550bc459a87b9f6cc14486846": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "ff1994286e7546d4a0bd48d23364e76e": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_b0dc802bca6b4699b4fe5cdaee4af3dd", + "IPY_MODEL_bf588e10924e473bad4871fb48f6cd18", + "IPY_MODEL_5413b15f27f1494fb36059a87a8c720a" + ], + "layout": "IPY_MODEL_db79b68c7ffe4471ae032126a17e8466" + } + }, + "b0dc802bca6b4699b4fe5cdaee4af3dd": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_ff3f75bc2ff9491dacdf726ae22fd246", + "placeholder": "​", + "style": "IPY_MODEL_0a780a68ef6c431aa420ec940a2543bb", + "value": "vocoder.onnx: 100%" + } + }, + "bf588e10924e473bad4871fb48f6cd18": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_ac5b23b651184262a3e925220ff5da49", + "max": 55762937, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_fad5b0c23ace43ce912fef46f171aacc", + "value": 55762937 + } + }, + "5413b15f27f1494fb36059a87a8c720a": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_b3c3f674be884c1a8c22dbbf94f7a8a7", + "placeholder": "​", + "style": "IPY_MODEL_c8c6ccda72614bc4bc6c80fc98ae0b67", + "value": " 55.8M/55.8M [00:01<00:00, 46.1MB/s]" + } + }, + "db79b68c7ffe4471ae032126a17e8466": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "ff3f75bc2ff9491dacdf726ae22fd246": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0a780a68ef6c431aa420ec940a2543bb": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "ac5b23b651184262a3e925220ff5da49": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "fad5b0c23ace43ce912fef46f171aacc": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "b3c3f674be884c1a8c22dbbf94f7a8a7": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "c8c6ccda72614bc4bc6c80fc98ae0b67": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + } + } } }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file From 2dc6f186594f21fc848f37ea481f9ce1e53dca4f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:42:11 +0000 Subject: [PATCH 02/19] Initial plan From de6be0585b800a1d9a9cf326634b1c010a16480d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:42:26 +0000 Subject: [PATCH 03/19] Initial plan From ff059e2ecdb440acfbb11bcc4a45545790141d35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:46:05 +0000 Subject: [PATCH 04/19] Add Dockerfile and demo script for PyThaiTTS Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- .dockerignore | 51 ++++++++++++++++++++++ DOCKER.md | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++ Dockerfile | 29 +++++++++++++ demo.py | 56 +++++++++++++++++++++++++ 4 files changed, 250 insertions(+) create mode 100644 .dockerignore create mode 100644 DOCKER.md create mode 100644 Dockerfile create mode 100644 demo.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..74fe190 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,51 @@ +# Git +.git +.gitignore + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +*.egg +*.egg-info/ +dist/ +build/ +.eggs/ +.pytest_cache/ +.mypy_cache/ +.coverage +htmlcov/ + +# Virtual environments +venv/ +env/ +ENV/ +.venv + +# IDEs +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Docs +docs/ +/site + +# Notebooks +.ipynb_checkpoints +notebook/ + +# GitHub +.github/ + +# Output files +*.wav +*.mp3 +output.* + +# Test files +tests/ +test/ diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000..ee36486 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,114 @@ +# Docker Usage Guide for PyThaiTTS + +This guide explains how to build and run PyThaiTTS using Docker. + +## Building the Docker Image + +To build the Docker image, run the following command from the root directory of the repository: + +```bash +docker build -t pythaitts:latest . +``` + +This will create a Docker image named `pythaitts:latest` with all dependencies installed. + +## Running the Demo + +To run the demo script that demonstrates Thai text-to-speech synthesis: + +```bash +docker run --rm pythaitts:latest +``` + +The demo will: +1. Initialize the PyThaiTTS model (default: lunarlist_onnx) +2. Generate speech from Thai text +3. Save the output to a WAV file +4. Display the waveform information + +## Custom Usage + +### Interactive Shell + +To start an interactive shell inside the container: + +```bash +docker run --rm -it pythaitts:latest /bin/bash +``` + +### Run Custom Python Script + +To run your own Python script: + +```bash +docker run --rm -v $(pwd)/your_script.py:/app/custom.py pythaitts:latest python custom.py +``` + +### Save Output Files + +To save generated audio files to your host machine: + +```bash +docker run --rm -v $(pwd)/output:/app/output pythaitts:latest python -c " +from pythaitts import TTS +tts = TTS() +tts.tts('สวัสดีครับ', filename='output/hello.wav') +" +``` + +This will save the generated `hello.wav` file to the `output` directory on your host machine. + +## Example Usage in Python + +Inside the container, you can use PyThaiTTS as follows: + +```python +from pythaitts import TTS + +# Initialize TTS with default model +tts = TTS() + +# Generate speech and save to file +file_path = tts.tts("ภาษาไทย ง่าย มาก มาก", filename="output.wav") +print(f"Audio saved to: {file_path}") + +# Generate speech and get waveform +waveform = tts.tts("ภาษาไทย ง่าย มาก มาก", return_type="waveform") +print(f"Waveform shape: {waveform.shape}") +``` + +## Available TTS Models + +PyThaiTTS supports multiple models: + +- **lunarlist_onnx** (default): ONNX-optimized model, CPU-only +- **khanomtan**: KhanomTan TTS model +- **lunarlist**: Original Lunarlist model + +To use a different model: + +```python +from pythaitts import TTS + +# Using KhanomTan model +tts = TTS(pretrained="khanomtan", version="1.0") +``` + +## Requirements + +- Docker installed on your system +- At least 2GB of available disk space +- Internet connection for downloading models on first run + +## Troubleshooting + +If you encounter issues with model downloads, ensure: +1. You have a stable internet connection +2. The Hugging Face Hub is accessible from your network +3. You have sufficient disk space for model files + +## Notes + +- The first run will download model files from Hugging Face Hub, which may take some time depending on your internet connection +- Generated audio files are in WAV format +- The default model (lunarlist_onnx) runs on CPU and doesn't require GPU support diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2952254 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +# Use Python 3.9 as base image (compatible with the project requirements) +FROM python:3.9-slim + +# Set working directory +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + git \ + && rm -rf /var/lib/apt/lists/* + +# Copy requirements and setup files +COPY requirements.txt setup.py README.md ./ +COPY pythaitts ./pythaitts + +# Install Python dependencies +RUN pip install --no-cache-dir --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org -r requirements.txt + +# Install the package +RUN pip install --no-cache-dir -e . + +# Copy demo script +COPY demo.py ./ + +# Set environment variable to avoid Python buffering +ENV PYTHONUNBUFFERED=1 + +# Run the demo script by default +CMD ["python", "demo.py"] diff --git a/demo.py b/demo.py new file mode 100644 index 0000000..1bb3b6f --- /dev/null +++ b/demo.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Simple demo script for PyThaiTTS +This script demonstrates Thai text-to-speech synthesis using PyThaiTTS. +""" + +from pythaitts import TTS + +def main(): + print("=" * 60) + print("PyThaiTTS Demo - Thai Text-to-Speech") + print("=" * 60) + print() + + # Initialize TTS with default model (lunarlist_onnx) + print("Initializing TTS model (lunarlist_onnx)...") + try: + tts = TTS() + print("✓ TTS model loaded successfully!") + print() + + # Sample Thai text + text = "สวัสดีครับ ยินดีต้อนรับสู่ PyThaiTTS" + print(f"Input text: {text}") + print() + + # Generate speech and save to file + output_file = "output.wav" + print(f"Generating speech and saving to {output_file}...") + result = tts.tts(text, filename=output_file) + print(f"✓ Speech generated successfully!") + print(f"Output saved to: {result}") + print() + + # Also demonstrate getting waveform + print("Generating waveform...") + waveform = tts.tts(text, return_type="waveform") + print(f"✓ Waveform generated successfully!") + print(f"Waveform shape: {waveform.shape}") + print() + + print("=" * 60) + print("Demo completed successfully!") + print("=" * 60) + + except Exception as e: + print(f"✗ Error occurred: {e}") + import traceback + traceback.print_exc() + return 1 + + return 0 + +if __name__ == "__main__": + exit(main()) From 4a67260f8f70a85a987f2b8ca8aaaef492580df2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:46:22 +0000 Subject: [PATCH 05/19] Add Thai text preprocessing module with number-to-text and mai yamok expansion Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- pythaitts/__init__.py | 10 +- pythaitts/preprocess.py | 258 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 pythaitts/preprocess.py diff --git a/pythaitts/__init__.py b/pythaitts/__init__.py index f32b396..b74caa1 100644 --- a/pythaitts/__init__.py +++ b/pythaitts/__init__.py @@ -4,6 +4,8 @@ """ __version__ = "0.3.0" +from pythaitts.preprocess import preprocess_text, num_to_thai, expand_maiyamok + class TTS: def __init__(self, pretrained="lunarlist_onnx", mode="last_checkpoint", version="1.0", device:str="cpu") -> None: @@ -52,7 +54,7 @@ def load_pretrained(self,version): "PyThaiTTS doesn't support %s pretrained." % self.pretrained ) - def tts(self, text: str, speaker_idx: str = "Linda", language_idx: str = "th-th", return_type: str = "file", filename: str = None): + def tts(self, text: str, speaker_idx: str = "Linda", language_idx: str = "th-th", return_type: str = "file", filename: str = None, preprocess: bool = True): """ speech synthesis @@ -61,7 +63,13 @@ def tts(self, text: str, speaker_idx: str = "Linda", language_idx: str = "th-th" :param str language_idx: language (default is th-th) :param str return_type: return type (default is file) :param str filename: path filename for save wav file if return_type is file. + :param bool preprocess: whether to preprocess text (convert numbers to Thai text and expand ๆ). Default is True. """ + # Preprocess text if requested + if preprocess: + from pythaitts.preprocess import preprocess_text + text = preprocess_text(text) + if self.pretrained == "lunarlist" or self.pretrained == "lunarlist_onnx": return self.model(text=text,return_type=return_type,filename=filename) return self.model( diff --git a/pythaitts/preprocess.py b/pythaitts/preprocess.py new file mode 100644 index 0000000..273614b --- /dev/null +++ b/pythaitts/preprocess.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 -*- +""" +Thai Text Preprocessing for TTS + +This module provides text preprocessing functions for Thai Text-to-Speech, +including number to Thai text conversion and handling of Thai repetition character (ๆ). +""" +import re + + +# Thai number words +THAI_ONES = ["", "หนึ่ง", "สอง", "สาม", "สี่", "ห้า", "หก", "เจ็ด", "แปด", "เก้า"] +THAI_TENS = ["", "สิบ", "ยี่สิบ", "สามสิบ", "สี่สิบ", "ห้าสิบ", "หกสิบ", "เจ็ดสิบ", "แปดสิบ", "เก้าสิบ"] + + +def _num_to_thai_under_hundred(num: int) -> str: + """ + Convert numbers 0-99 to Thai text. + + :param int num: Number to convert (0-99) + :return: Thai text representation + :rtype: str + """ + if num == 0: + return "ศูนย์" + elif num < 10: + return THAI_ONES[num] + elif num < 20: + if num == 10: + return "สิบ" + elif num == 11: + return "สิบเอ็ด" + else: + return "สิบ" + THAI_ONES[num % 10] + elif num < 100: + tens = num // 10 + ones = num % 10 + result = THAI_TENS[tens] + if ones == 1: + result += "เอ็ด" + elif ones > 1: + result += THAI_ONES[ones] + return result + return "" + + +def _num_to_thai_under_thousand(num: int) -> str: + """ + Convert numbers 0-999 to Thai text. + + :param int num: Number to convert (0-999) + :return: Thai text representation + :rtype: str + """ + if num < 100: + return _num_to_thai_under_hundred(num) + + hundreds = num // 100 + remainder = num % 100 + + if hundreds == 1: + result = "หนึ่งร้อย" + elif hundreds == 2: + result = "สองร้อย" + else: + result = THAI_ONES[hundreds] + "ร้อย" + + if remainder > 0: + result += _num_to_thai_under_hundred(remainder) + + return result + + +def num_to_thai(num_str: str) -> str: + """ + Convert number string to Thai text. + Supports integers and decimals. + + :param str num_str: Number string to convert (e.g., "123", "1234", "12.5") + :return: Thai text representation + :rtype: str + + Examples: + >>> num_to_thai("0") + 'ศูนย์' + >>> num_to_thai("123") + 'หนึ่งร้อยยี่สิบสาม' + >>> num_to_thai("1000") + 'หนึ่งพัน' + """ + # Handle decimal numbers + if '.' in num_str: + integer_part, decimal_part = num_str.split('.') + result = num_to_thai(integer_part) + "จุด" + for digit in decimal_part: + result += THAI_ONES[int(digit)] if int(digit) > 0 else "ศูนย์" + return result + + # Convert to integer + try: + num = int(num_str) + except ValueError: + return num_str # Return original if cannot convert + + if num == 0: + return "ศูนย์" + + if num < 0: + return "ลบ" + num_to_thai(str(-num)) + + # Handle numbers by magnitude + if num < 1000: + return _num_to_thai_under_thousand(num) + elif num < 10000: + thousands = num // 1000 + remainder = num % 1000 + result = THAI_ONES[thousands] + "พัน" + if remainder > 0: + result += _num_to_thai_under_thousand(remainder) + return result + elif num < 100000: + ten_thousands = num // 10000 + remainder = num % 10000 + if ten_thousands == 1: + result = "หนึ่งหมื่น" + elif ten_thousands == 2: + result = "สองหมื่น" + else: + result = THAI_ONES[ten_thousands] + "หมื่น" + if remainder > 0: + thousands = remainder // 1000 + if thousands > 0: + result += THAI_ONES[thousands] + "พัน" + remainder = remainder % 1000 + if remainder > 0: + result += _num_to_thai_under_thousand(remainder) + return result + elif num < 1000000: + hundred_thousands = num // 100000 + remainder = num % 100000 + result = THAI_ONES[hundred_thousands] + "แสน" + if remainder > 0: + ten_thousands = remainder // 10000 + if ten_thousands > 0: + result += THAI_ONES[ten_thousands] + "หมื่น" + remainder = remainder % 10000 + thousands = remainder // 1000 + if thousands > 0: + result += THAI_ONES[thousands] + "พัน" + remainder = remainder % 1000 + if remainder > 0: + result += _num_to_thai_under_thousand(remainder) + return result + elif num < 10000000: + millions = num // 1000000 + remainder = num % 1000000 + result = THAI_ONES[millions] + "ล้าน" + if remainder > 0: + # Process hundred thousands and below + if remainder >= 100000: + result += num_to_thai(str(remainder)) + else: + if remainder > 0: + result += num_to_thai(str(remainder)) + return result + else: + # For very large numbers, use a simple approach + millions = num // 1000000 + remainder = num % 1000000 + result = num_to_thai(str(millions)) + "ล้าน" + if remainder > 0: + result += num_to_thai(str(remainder)) + return result + + +def expand_maiyamok(text: str) -> str: + """ + Expand Thai repetition character (ๆ) by repeating the previous word or syllable. + + The mai yamok (ๆ) is a Thai repetition mark that indicates the previous + word or syllable should be repeated. + + :param str text: Text containing ๆ character + :return: Text with ๆ expanded + :rtype: str + + Examples: + >>> expand_maiyamok("ช้าๆ") + 'ช้าช้า' + >>> expand_maiyamok("ดีๆ") + 'ดีดี' + """ + if 'ๆ' not in text: + return text + + result = [] + i = 0 + while i < len(text): + if text[i] == 'ๆ': + # Find the previous word/syllable to repeat + if result: + # Look back to find the word to repeat + # Thai words are typically separated by spaces or are continuous + # We'll repeat the last word or syllable + prev_text = ''.join(result) + + # Find the last word (sequence of Thai characters) + thai_char_pattern = r'[ก-๙]+' + matches = list(re.finditer(thai_char_pattern, prev_text)) + if matches: + last_match = matches[-1] + word_to_repeat = last_match.group() + result.append(word_to_repeat) + else: + # If no Thai characters found, just skip the ๆ + pass + i += 1 + else: + result.append(text[i]) + i += 1 + + return ''.join(result) + + +def preprocess_text(text: str, expand_numbers: bool = True, expand_maiyamok_char: bool = True) -> str: + """ + Preprocess Thai text for TTS by converting numbers to text and expanding ๆ. + + :param str text: Input text to preprocess + :param bool expand_numbers: Whether to convert numbers to Thai text (default: True) + :param bool expand_maiyamok_char: Whether to expand ๆ character (default: True) + :return: Preprocessed text + :rtype: str + + Examples: + >>> preprocess_text("ฉันมี 123 บาท") + 'ฉันมี หนึ่งร้อยยี่สิบสาม บาท' + >>> preprocess_text("ดีๆ") + 'ดีดี' + >>> preprocess_text("มี 5 คนๆ") + 'มี ห้า คนคน' + """ + result = text + + # Expand mai yamok (ๆ) first + if expand_maiyamok_char: + result = expand_maiyamok(result) + + # Convert numbers to Thai text + if expand_numbers: + # Find all numbers in the text and replace them + def replace_number(match): + return num_to_thai(match.group()) + + # Match integers and decimals + result = re.sub(r'\d+\.?\d*', replace_number, result) + + return result From 2e120e9ec829b6be18d67a3a797badd6ed072bb2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:47:50 +0000 Subject: [PATCH 06/19] Add comprehensive unit tests for Thai text preprocessing Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- tests/__init__.py | 4 ++ tests/test_preprocess.py | 125 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/test_preprocess.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..40cf8a7 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +""" +Tests for PyThaiTTS +""" diff --git a/tests/test_preprocess.py b/tests/test_preprocess.py new file mode 100644 index 0000000..356b369 --- /dev/null +++ b/tests/test_preprocess.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +""" +Unit tests for Thai text preprocessing module +""" +import unittest +from pythaitts.preprocess import num_to_thai, expand_maiyamok, preprocess_text + + +class TestNumToThai(unittest.TestCase): + """Test number to Thai text conversion""" + + def test_single_digits(self): + """Test single digit numbers""" + self.assertEqual(num_to_thai("0"), "ศูนย์") + self.assertEqual(num_to_thai("1"), "หนึ่ง") + self.assertEqual(num_to_thai("5"), "ห้า") + self.assertEqual(num_to_thai("9"), "เก้า") + + def test_tens(self): + """Test numbers 10-99""" + self.assertEqual(num_to_thai("10"), "สิบ") + self.assertEqual(num_to_thai("11"), "สิบเอ็ด") + self.assertEqual(num_to_thai("15"), "สิบห้า") + self.assertEqual(num_to_thai("20"), "ยี่สิบ") + self.assertEqual(num_to_thai("21"), "ยี่สิบเอ็ด") + self.assertEqual(num_to_thai("99"), "เก้าสิบเก้า") + + def test_hundreds(self): + """Test numbers 100-999""" + self.assertEqual(num_to_thai("100"), "หนึ่งร้อย") + self.assertEqual(num_to_thai("123"), "หนึ่งร้อยยี่สิบสาม") + self.assertEqual(num_to_thai("200"), "สองร้อย") + self.assertEqual(num_to_thai("999"), "เก้าร้อยเก้าสิบเก้า") + + def test_thousands(self): + """Test numbers 1000-9999""" + self.assertEqual(num_to_thai("1000"), "หนึ่งพัน") + self.assertEqual(num_to_thai("1234"), "หนึ่งพันสองร้อยสามสิบสี่") + self.assertEqual(num_to_thai("5000"), "ห้าพัน") + + def test_ten_thousands(self): + """Test numbers 10000-99999""" + self.assertEqual(num_to_thai("10000"), "หนึ่งหมื่น") + self.assertEqual(num_to_thai("50000"), "ห้าหมื่น") + + def test_negative_numbers(self): + """Test negative numbers""" + self.assertEqual(num_to_thai("-5"), "ลบห้า") + self.assertEqual(num_to_thai("-123"), "ลบหนึ่งร้อยยี่สิบสาม") + + def test_decimal_numbers(self): + """Test decimal numbers""" + result = num_to_thai("12.5") + self.assertIn("จุด", result) + self.assertIn("สิบสอง", result) + self.assertIn("ห้า", result) + + +class TestExpandMaiyamok(unittest.TestCase): + """Test Thai repetition character (ๆ) expansion""" + + def test_basic_maiyamok(self): + """Test basic mai yamok expansion""" + self.assertEqual(expand_maiyamok("ดีๆ"), "ดีดี") + self.assertEqual(expand_maiyamok("ช้าๆ"), "ช้าช้า") + self.assertEqual(expand_maiyamok("คนๆ"), "คนคน") + + def test_no_maiyamok(self): + """Test text without mai yamok""" + self.assertEqual(expand_maiyamok("ภาษาไทย"), "ภาษาไทย") + self.assertEqual(expand_maiyamok("สวัสดี"), "สวัสดี") + + def test_maiyamok_in_sentence(self): + """Test mai yamok in longer sentences""" + result = expand_maiyamok("เดินช้าๆ") + self.assertNotIn("ๆ", result) + self.assertIn("ช้า", result) + + +class TestPreprocessText(unittest.TestCase): + """Test full text preprocessing""" + + def test_number_conversion(self): + """Test number conversion in text""" + result = preprocess_text("ฉันมี 123 บาท") + self.assertNotIn("123", result) + self.assertIn("หนึ่งร้อยยี่สิบสาม", result) + + def test_maiyamok_expansion(self): + """Test mai yamok expansion in text""" + result = preprocess_text("ดีๆ") + self.assertEqual(result, "ดีดี") + + def test_combined_preprocessing(self): + """Test both number conversion and mai yamok expansion""" + result = preprocess_text("มี 5 คนๆ") + self.assertNotIn("5", result) + self.assertNotIn("ๆ", result) + self.assertIn("ห้า", result) + self.assertIn("คนคน", result) + + def test_no_preprocessing_numbers(self): + """Test with number preprocessing disabled""" + result = preprocess_text("มี 5 คน", expand_numbers=False) + self.assertIn("5", result) + + def test_no_preprocessing_maiyamok(self): + """Test with mai yamok preprocessing disabled""" + result = preprocess_text("ดีๆ", expand_maiyamok_char=False) + self.assertIn("ๆ", result) + + def test_empty_text(self): + """Test empty text""" + result = preprocess_text("") + self.assertEqual(result, "") + + def test_text_without_preprocessing_needs(self): + """Test text that doesn't need preprocessing""" + text = "ภาษาไทย ง่าย มาก" + result = preprocess_text(text) + self.assertEqual(result, text) + + +if __name__ == '__main__': + unittest.main() From 0f129f69875019d1b2a525f28b80fbf5b5824f72 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:48:01 +0000 Subject: [PATCH 07/19] Address code review feedback: use Python 3.11 and timestamp filenames Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- Dockerfile | 4 ++-- demo.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2952254..922c983 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ -# Use Python 3.9 as base image (compatible with the project requirements) -FROM python:3.9-slim +# Use Python 3.11 as base image (compatible with the project requirements) +FROM python:3.11-slim # Set working directory WORKDIR /app diff --git a/demo.py b/demo.py index 1bb3b6f..ba08299 100644 --- a/demo.py +++ b/demo.py @@ -26,7 +26,9 @@ def main(): print() # Generate speech and save to file - output_file = "output.wav" + import datetime + timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + output_file = f"output_{timestamp}.wav" print(f"Generating speech and saving to {output_file}...") result = tts.tts(text, filename=output_file) print(f"✓ Speech generated successfully!") From 4619d03598c1111cb1d95c9cb9d662996ea463cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:48:48 +0000 Subject: [PATCH 08/19] Update documentation with Thai text preprocessing examples Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ docs/index.rst | 13 ++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c1dd1f..8cb69bb 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ Install by pip: ## Usage +### Basic Usage + ```python from pythaitts import TTS @@ -22,4 +24,41 @@ file = tts.tts("ภาษาไทย ง่าย มาก มาก", filenam wave = tts.tts("ภาษาไทย ง่าย มาก มาก",return_type="waveform") # It will get waveform. ``` +### Text Preprocessing + +PyThaiTTS includes automatic text preprocessing to improve TTS quality: +- **Number to Thai text conversion**: Converts digits (e.g., "123") to Thai text (e.g., "หนึ่งร้อยยี่สิบสาม") +- **Mai yamok (ๆ) expansion**: Expands the Thai repetition character (e.g., "ดีๆ" becomes "ดีดี") + +Preprocessing is enabled by default: + +```python +from pythaitts import TTS + +tts = TTS() +# Automatic preprocessing: "มี 5 คนๆ" becomes "มี ห้า คนคน" +file = tts.tts("มี 5 คนๆ", filename="output.wav") +``` + +You can disable preprocessing if needed: + +```python +file = tts.tts("มี 5 คนๆ", preprocess=False, filename="output.wav") +``` + +You can also use preprocessing functions directly: + +```python +from pythaitts import num_to_thai, expand_maiyamok, preprocess_text + +# Convert numbers to Thai text +print(num_to_thai("123")) # Output: หนึ่งร้อยยี่สิบสาม + +# Expand mai yamok +print(expand_maiyamok("ดีๆ")) # Output: ดีดี + +# Full preprocessing +print(preprocess_text("มี 5 คนๆ")) # Output: มี ห้า คนคน +``` + You can see more at [https://pythainlp.github.io/PyThaiTTS/](https://pythainlp.github.io/PyThaiTTS/). diff --git a/docs/index.rst b/docs/index.rst index cb51478..9a72f76 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,4 +5,15 @@ PyThaiTTS Open Source Thai Text-to-speech library in Python .. autoclass:: TTS - :members: \ No newline at end of file + :members: + +Text Preprocessing +------------------ + +PyThaiTTS provides text preprocessing functions to improve TTS quality. + +.. autofunction:: preprocess_text + +.. autofunction:: num_to_thai + +.. autofunction:: expand_maiyamok \ No newline at end of file From a30cb333229edd8e292c7e98cbd4307afce8d921 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:50:56 +0000 Subject: [PATCH 09/19] Fix code review issues: simplify redundant logic and improve regex pattern Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- pythaitts/preprocess.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/pythaitts/preprocess.py b/pythaitts/preprocess.py index 273614b..9cfc224 100644 --- a/pythaitts/preprocess.py +++ b/pythaitts/preprocess.py @@ -156,12 +156,7 @@ def num_to_thai(num_str: str) -> str: remainder = num % 1000000 result = THAI_ONES[millions] + "ล้าน" if remainder > 0: - # Process hundred thousands and below - if remainder >= 100000: - result += num_to_thai(str(remainder)) - else: - if remainder > 0: - result += num_to_thai(str(remainder)) + result += num_to_thai(str(remainder)) return result else: # For very large numbers, use a simple approach @@ -252,7 +247,7 @@ def preprocess_text(text: str, expand_numbers: bool = True, expand_maiyamok_char def replace_number(match): return num_to_thai(match.group()) - # Match integers and decimals - result = re.sub(r'\d+\.?\d*', replace_number, result) + # Match integers and decimals (but not numbers ending with just a decimal point) + result = re.sub(r'\d+(?:\.\d+)?', replace_number, result) return result From e6901d3a30d79cbed3f50227f10bc1a7ebe04692 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:52:46 +0000 Subject: [PATCH 10/19] Fix regex to handle negative numbers in preprocessing Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- pythaitts/preprocess.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pythaitts/preprocess.py b/pythaitts/preprocess.py index 9cfc224..7ac1307 100644 --- a/pythaitts/preprocess.py +++ b/pythaitts/preprocess.py @@ -247,7 +247,8 @@ def preprocess_text(text: str, expand_numbers: bool = True, expand_maiyamok_char def replace_number(match): return num_to_thai(match.group()) - # Match integers and decimals (but not numbers ending with just a decimal point) - result = re.sub(r'\d+(?:\.\d+)?', replace_number, result) + # Match integers and decimals, including optional negative sign + # Handles: -5, 123, 123.45 + result = re.sub(r'-?\d+(?:\.\d+)?', replace_number, result) return result From ec3ffdb5ce958ff0d0072497558108880bd4ae97 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:20:38 +0000 Subject: [PATCH 11/19] Initial plan From 5ec1e5459dc918bf2f57001d3313a218f8d9152f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:23:16 +0000 Subject: [PATCH 12/19] Add VachanaTTS2 support with wrapper, documentation, and dependency Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- README.md | 20 ++++++ pythaitts/__init__.py | 15 +++-- pythaitts/pretrained/vachana_tts.py | 95 +++++++++++++++++++++++++++++ requirements.txt | 3 +- 4 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 pythaitts/pretrained/vachana_tts.py diff --git a/README.md b/README.md index 8cb69bb..8ba7cad 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,26 @@ file = tts.tts("ภาษาไทย ง่าย มาก มาก", filenam wave = tts.tts("ภาษาไทย ง่าย มาก มาก",return_type="waveform") # It will get waveform. ``` +### Using Different TTS Models + +PyThaiTTS supports multiple TTS models. You can specify which model to use: + +```python +from pythaitts import TTS + +# Use VachanaTTS (default voices: th_f_1, th_m_1, th_f_2, th_m_2) +tts = TTS(pretrained="vachana") +file = tts.tts("สวัสดีครับ", speaker_idx="th_f_1", filename="output.wav") + +# Use Lunarlist ONNX (default) +tts = TTS(pretrained="lunarlist_onnx") +file = tts.tts("ภาษาไทย ง่าย มาก", filename="output.wav") + +# Use KhanomTan +tts = TTS(pretrained="khanomtan") +file = tts.tts("ภาษาไทย", speaker_idx="Linda", filename="output.wav") +``` + ### Text Preprocessing PyThaiTTS includes automatic text preprocessing to improve TTS quality: diff --git a/pythaitts/__init__.py b/pythaitts/__init__.py index b74caa1..c4481fb 100644 --- a/pythaitts/__init__.py +++ b/pythaitts/__init__.py @@ -10,10 +10,10 @@ class TTS: def __init__(self, pretrained="lunarlist_onnx", mode="last_checkpoint", version="1.0", device:str="cpu") -> None: """ - :param str pretrained: TTS pretrained (lunarlist_onnx, khanomtan, lunarlist) - :param str mode: pretrained mode (lunarlist_onnx don't support) + :param str pretrained: TTS pretrained (lunarlist_onnx, khanomtan, lunarlist, vachana) + :param str mode: pretrained mode (lunarlist_onnx and vachana don't support) :param str version: model version (default is 1.0 or 1.1) - :param str device: device for running model. (lunarlist_onnx support CPU only.) + :param str device: device for running model. (lunarlist_onnx and vachana support CPU only.) **Options for mode** * *last_checkpoint* (default) - last checkpoint of model @@ -28,6 +28,8 @@ def __init__(self, pretrained="lunarlist_onnx", mode="last_checkpoint", version= For lunarlist_onnx tts model, \ You can see more about lunarlist tts at `https://github.com/PyThaiNLP/thaitts-onnx `_ + For vachana tts model, \ + You can see more about vachana tts at `https://github.com/VYNCX/VachanaTTS2 `_ """ @@ -49,6 +51,9 @@ def load_pretrained(self,version): elif self.pretrained == "lunarlist": from pythaitts.pretrained.lunarlist_model import LunarlistModel self.model = LunarlistModel(mode=self.mode, device=self.device) + elif self.pretrained == "vachana": + from pythaitts.pretrained.vachana_tts import VachanaTTS + self.model = VachanaTTS() else: raise NotImplemented( "PyThaiTTS doesn't support %s pretrained." % self.pretrained @@ -59,7 +64,7 @@ def tts(self, text: str, speaker_idx: str = "Linda", language_idx: str = "th-th" speech synthesis :param str text: text - :param str speaker_idx: speaker (default is Linda) + :param str speaker_idx: speaker (default is Linda for khanomtan, th_f_1 for vachana) :param str language_idx: language (default is th-th) :param str return_type: return type (default is file) :param str filename: path filename for save wav file if return_type is file. @@ -72,6 +77,8 @@ def tts(self, text: str, speaker_idx: str = "Linda", language_idx: str = "th-th" if self.pretrained == "lunarlist" or self.pretrained == "lunarlist_onnx": return self.model(text=text,return_type=return_type,filename=filename) + elif self.pretrained == "vachana": + return self.model(text=text,speaker_idx=speaker_idx,return_type=return_type,filename=filename) return self.model( text=text, speaker_idx=speaker_idx, diff --git a/pythaitts/pretrained/vachana_tts.py b/pythaitts/pretrained/vachana_tts.py new file mode 100644 index 0000000..3fc38f7 --- /dev/null +++ b/pythaitts/pretrained/vachana_tts.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +""" +VachanaTTS2 model + +VachanaTTS2 is a Thai text-to-speech model built on VITS architecture. +It supports multiple Thai voices and is optimized for both CPU and GPU usage. + +See more: https://github.com/VYNCX/VachanaTTS2 +""" +import tempfile + + +class VachanaTTS: + def __init__(self) -> None: + """ + Initialize VachanaTTS model. + The model will be automatically downloaded from HuggingFace on first use. + """ + try: + from vachanatts import TTS as VachanaTTS_TTS + self.tts_func = VachanaTTS_TTS + except ImportError: + raise ImportError( + "vachanatts is not installed. Please install it with: pip install vachanatts" + ) + + def __call__(self, text: str, speaker_idx: str = "th_f_1", return_type: str = "file", filename: str = None, **kwargs): + """ + Generate speech from text using VachanaTTS. + + :param str text: Input text to synthesize + :param str speaker_idx: Voice to use (th_f_1, th_m_1, th_f_2, th_m_2). Default is "th_f_1" + :param str return_type: Return type ("file" or "waveform") + :param str filename: Output filename for the generated audio + :param kwargs: Additional parameters (volume, speed, noise_scale, noise_w_scale) + :return: File path if return_type is "file", otherwise audio waveform data + """ + # Extract additional parameters with defaults + volume = kwargs.get('volume', 1.0) + speed = kwargs.get('speed', 1.0) + noise_scale = kwargs.get('noise_scale', 0.667) + noise_w_scale = kwargs.get('noise_w_scale', 0.8) + + if return_type == "waveform": + # For waveform return, we need to generate to a temp file then read it + import wave + import numpy as np + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as fp: + temp_filename = fp.name + + # Generate the audio file + self.tts_func( + text, + voice=speaker_idx, + output=temp_filename, + volume=volume, + speed=speed, + noise_scale=noise_scale, + noise_w_scale=noise_w_scale + ) + + # Read the waveform from the file + with wave.open(temp_filename, 'rb') as wav_file: + n_frames = wav_file.getnframes() + audio_data = wav_file.readframes(n_frames) + # Convert bytes to numpy array + import struct + sample_width = wav_file.getsampwidth() + if sample_width == 2: + waveform = np.frombuffer(audio_data, dtype=np.int16) + else: + waveform = np.frombuffer(audio_data, dtype=np.int8) + + # Clean up temp file + import os + os.unlink(temp_filename) + + return waveform + else: + # File output + if filename is None: + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as fp: + filename = fp.name + + self.tts_func( + text, + voice=speaker_idx, + output=filename, + volume=volume, + speed=speed, + noise_scale=noise_scale, + noise_w_scale=noise_w_scale + ) + + return filename diff --git a/requirements.txt b/requirements.txt index 77ef34a..6501dea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ huggingface_hub numpy>=1.22 -onnxruntime \ No newline at end of file +onnxruntime +vachanatts \ No newline at end of file From 336041d5f463d6c3ef5eb9e41f299c88a77c9db0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:24:51 +0000 Subject: [PATCH 13/19] Add unit tests for VachanaTTS integration Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- tests/test_vachana.py | 72 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/test_vachana.py diff --git a/tests/test_vachana.py b/tests/test_vachana.py new file mode 100644 index 0000000..4a134b1 --- /dev/null +++ b/tests/test_vachana.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +""" +Unit tests for VachanaTTS integration +""" +import unittest +from unittest.mock import Mock, patch, MagicMock +from pythaitts import TTS + + +class TestVachanaIntegration(unittest.TestCase): + """Test VachanaTTS integration""" + + @patch('pythaitts.pretrained.vachana_tts.VachanaTTS') + def test_vachana_model_initialization(self, mock_vachana): + """Test that VachanaTTS model can be initialized""" + # Create TTS instance with vachana model + tts = TTS(pretrained="vachana") + + # Verify model is loaded + self.assertIsNotNone(tts.model) + self.assertEqual(tts.pretrained, "vachana") + + @patch('pythaitts.pretrained.vachana_tts.VachanaTTS') + def test_vachana_tts_call(self, mock_vachana_class): + """Test calling tts method with vachana model""" + # Setup mock + mock_instance = Mock() + mock_instance.return_value = "/tmp/output.wav" + mock_vachana_class.return_value = mock_instance + + # Create TTS instance + tts = TTS(pretrained="vachana") + + # Call tts method + result = tts.tts("สวัสดีครับ", speaker_idx="th_f_1", filename="/tmp/test.wav") + + # Verify the model was called with correct parameters + mock_instance.assert_called_once() + call_args = mock_instance.call_args + self.assertEqual(call_args.kwargs['text'], "สวัสดีครับ") + self.assertEqual(call_args.kwargs['speaker_idx'], "th_f_1") + self.assertEqual(call_args.kwargs['filename'], "/tmp/test.wav") + self.assertEqual(call_args.kwargs['return_type'], "file") + + @patch('pythaitts.pretrained.vachana_tts.VachanaTTS') + def test_vachana_with_preprocessing(self, mock_vachana_class): + """Test that preprocessing works with vachana model""" + # Setup mock + mock_instance = Mock() + mock_instance.return_value = "/tmp/output.wav" + mock_vachana_class.return_value = mock_instance + + # Create TTS instance + tts = TTS(pretrained="vachana") + + # Call tts method with text that needs preprocessing + result = tts.tts("มี 5 คนๆ", speaker_idx="th_f_1", preprocess=True) + + # Verify preprocessing was applied + mock_instance.assert_called_once() + call_args = mock_instance.call_args + processed_text = call_args.kwargs['text'] + + # Text should have numbers converted and ๆ expanded + self.assertNotIn("5", processed_text) + self.assertNotIn("ๆ", processed_text) + self.assertIn("ห้า", processed_text) + self.assertIn("คนคน", processed_text) + + +if __name__ == '__main__': + unittest.main() From 5be47c2521ca4b073a601f5e3012ef6915c6e3c5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:27:52 +0000 Subject: [PATCH 14/19] Address code review feedback: improve error handling, validation, and test coverage Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- pythaitts/__init__.py | 2 +- pythaitts/pretrained/vachana_tts.py | 82 +++++++++++++++++------------ tests/test_vachana.py | 41 +++++++++++++++ 3 files changed, 91 insertions(+), 34 deletions(-) diff --git a/pythaitts/__init__.py b/pythaitts/__init__.py index c4481fb..6243fee 100644 --- a/pythaitts/__init__.py +++ b/pythaitts/__init__.py @@ -55,7 +55,7 @@ def load_pretrained(self,version): from pythaitts.pretrained.vachana_tts import VachanaTTS self.model = VachanaTTS() else: - raise NotImplemented( + raise NotImplementedError( "PyThaiTTS doesn't support %s pretrained." % self.pretrained ) diff --git a/pythaitts/pretrained/vachana_tts.py b/pythaitts/pretrained/vachana_tts.py index 3fc38f7..f7f2dfd 100644 --- a/pythaitts/pretrained/vachana_tts.py +++ b/pythaitts/pretrained/vachana_tts.py @@ -8,9 +8,15 @@ See more: https://github.com/VYNCX/VachanaTTS2 """ import tempfile +import wave +import numpy as np +import os class VachanaTTS: + # Supported voice options + SUPPORTED_VOICES = ["th_f_1", "th_m_1", "th_f_2", "th_m_2"] + def __init__(self) -> None: """ Initialize VachanaTTS model. @@ -35,6 +41,12 @@ def __call__(self, text: str, speaker_idx: str = "th_f_1", return_type: str = "f :param kwargs: Additional parameters (volume, speed, noise_scale, noise_w_scale) :return: File path if return_type is "file", otherwise audio waveform data """ + # Validate speaker_idx + if speaker_idx not in self.SUPPORTED_VOICES: + raise ValueError( + f"Unsupported voice '{speaker_idx}'. Supported voices are: {', '.join(self.SUPPORTED_VOICES)}" + ) + # Extract additional parameters with defaults volume = kwargs.get('volume', 1.0) speed = kwargs.get('speed', 1.0) @@ -43,39 +55,43 @@ def __call__(self, text: str, speaker_idx: str = "th_f_1", return_type: str = "f if return_type == "waveform": # For waveform return, we need to generate to a temp file then read it - import wave - import numpy as np - with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as fp: - temp_filename = fp.name - - # Generate the audio file - self.tts_func( - text, - voice=speaker_idx, - output=temp_filename, - volume=volume, - speed=speed, - noise_scale=noise_scale, - noise_w_scale=noise_w_scale - ) - - # Read the waveform from the file - with wave.open(temp_filename, 'rb') as wav_file: - n_frames = wav_file.getnframes() - audio_data = wav_file.readframes(n_frames) - # Convert bytes to numpy array - import struct - sample_width = wav_file.getsampwidth() - if sample_width == 2: - waveform = np.frombuffer(audio_data, dtype=np.int16) - else: - waveform = np.frombuffer(audio_data, dtype=np.int8) - - # Clean up temp file - import os - os.unlink(temp_filename) - - return waveform + temp_filename = None + try: + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as fp: + temp_filename = fp.name + + # Generate the audio file + self.tts_func( + text, + voice=speaker_idx, + output=temp_filename, + volume=volume, + speed=speed, + noise_scale=noise_scale, + noise_w_scale=noise_w_scale + ) + + # Read the waveform from the file + with wave.open(temp_filename, 'rb') as wav_file: + n_frames = wav_file.getnframes() + audio_data = wav_file.readframes(n_frames) + sample_width = wav_file.getsampwidth() + + # Convert bytes to numpy array based on sample width + if sample_width == 1: + waveform = np.frombuffer(audio_data, dtype=np.int8) + elif sample_width == 2: + waveform = np.frombuffer(audio_data, dtype=np.int16) + elif sample_width == 4: + waveform = np.frombuffer(audio_data, dtype=np.int32) + else: + raise ValueError(f"Unsupported sample width: {sample_width} bytes") + + return waveform + finally: + # Clean up temp file + if temp_filename and os.path.exists(temp_filename): + os.unlink(temp_filename) else: # File output if filename is None: diff --git a/tests/test_vachana.py b/tests/test_vachana.py index 4a134b1..a5a034a 100644 --- a/tests/test_vachana.py +++ b/tests/test_vachana.py @@ -4,6 +4,7 @@ """ import unittest from unittest.mock import Mock, patch, MagicMock +import numpy as np from pythaitts import TTS @@ -67,6 +68,46 @@ def test_vachana_with_preprocessing(self, mock_vachana_class): self.assertIn("ห้า", processed_text) self.assertIn("คนคน", processed_text) + @patch('pythaitts.pretrained.vachana_tts.VachanaTTS') + def test_vachana_all_supported_voices(self, mock_vachana_class): + """Test that all supported voices work correctly""" + # Setup mock + mock_instance = Mock() + mock_instance.return_value = "/tmp/output.wav" + mock_vachana_class.return_value = mock_instance + + # Create TTS instance + tts = TTS(pretrained="vachana") + + # Test all supported voices + supported_voices = ["th_f_1", "th_m_1", "th_f_2", "th_m_2"] + for voice in supported_voices: + mock_instance.reset_mock() + result = tts.tts("สวัสดี", speaker_idx=voice) + + # Verify the voice was passed correctly + call_args = mock_instance.call_args + self.assertEqual(call_args.kwargs['speaker_idx'], voice) + + @patch('pythaitts.pretrained.vachana_tts.VachanaTTS') + def test_vachana_waveform_return(self, mock_vachana_class): + """Test waveform return type functionality""" + # Setup mock + mock_instance = Mock() + mock_waveform = np.array([0.1, 0.2, 0.3, 0.4]) + mock_instance.return_value = mock_waveform + mock_vachana_class.return_value = mock_instance + + # Create TTS instance + tts = TTS(pretrained="vachana") + + # Call tts method with waveform return type + result = tts.tts("สวัสดี", speaker_idx="th_f_1", return_type="waveform") + + # Verify the return type was set correctly + call_args = mock_instance.call_args + self.assertEqual(call_args.kwargs['return_type'], "waveform") + if __name__ == '__main__': unittest.main() From 6aaee7d1add746618b2f796d5241bd21c9836c35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:33:21 +0000 Subject: [PATCH 15/19] Initial plan From aa879ecda34a0666562f6f6e86d7220a5121778b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:35:24 +0000 Subject: [PATCH 16/19] Add GitHub Actions CI workflow for testing Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- .github/workflows/test.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..7fcada6 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,37 @@ +name: CI Tests + +on: + push: + branches: + - main + - dev + pull_request: + branches: + - main + - dev + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + python-version: ['3.8', '3.9', '3.10', '3.11'] + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install -e . + + - name: Run tests + run: | + python -m unittest discover -s tests -p "test_*.py" -v From b4a0372526a6b00cea21b3b44b179b74f4e87922 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:37:00 +0000 Subject: [PATCH 17/19] Update workflow to use latest actions and add improvements Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- .github/workflows/test.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7fcada6..a4a9d64 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,17 +14,19 @@ jobs: test: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [ubuntu-latest] python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + cache: 'pip' - name: Install dependencies run: | From ee1c2de39851525a118976e5439ba9c977fece9e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:38:37 +0000 Subject: [PATCH 18/19] Add explicit permissions to workflow for security Co-authored-by: wannaphong <8536487+wannaphong@users.noreply.github.com> --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a4a9d64..e9fb8e0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,6 +13,8 @@ on: jobs: test: runs-on: ${{ matrix.os }} + permissions: + contents: read strategy: fail-fast: false matrix: From 6da092b56383a50b27e1c49ee4015ec5796e356f Mon Sep 17 00:00:00 2001 From: Wannaphong Phatthiyaphaibun Date: Wed, 28 Jan 2026 21:41:31 +0700 Subject: [PATCH 19/19] Bump version from 0.3.0 to 0.4.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 878d721..4d21e41 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name="PyThaiTTS", - version="0.3.0", + version="0.4.0", description="Open Source Thai Text-to-speech library in Python", long_description=readme, long_description_content_type="text/markdown",