File size: 4,901 Bytes
6da1c6c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
!pip install onnx

import json
import os
import onnx
import urllib.request
from urllib.error import URLError, HTTPError
from google.colab import files

def download_file(url, save_path):
    """
    Helper function to download a file from a URL.
    Returns True if successful, False otherwise.
    """
    if not url:
        return False
        
    try:
        print(f"Downloading from {url} ...")
        urllib.request.urlretrieve(url, save_path)
        print(f"Successfully downloaded to Colab: {save_path}")
        return True
    except HTTPError as e:
        print(f"HTTP Error failed to download {url}: {e.code}")
        return False
    except URLError as e:
        print(f"URL Error failed to download {url}: {e.reason}")
        return False
    except Exception as e:
        print(f"An unexpected error occurred during download: {e}")
        return False

def process_piper_model_from_url(onnx_url, json_url, output_dir):
    # Guard Clauses: Verify if required inputs are provided
    if not onnx_url or not json_url:
        print("Error: Both ONNX URL and JSON URL must be provided.")
        return

    # Create output directory if it does not exist
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # Extract filenames from URLs or assign default names
    onnx_filename = onnx_url.split('/')[-1] if '/' in onnx_url else "model.onnx"
    json_filename = json_url.split('/')[-1] if '/' in json_url else "config.json"
    
    # Define local file paths
    onnx_path = os.path.join(output_dir, onnx_filename)
    json_path = os.path.join(output_dir, json_filename)
    tokens_path = os.path.join(output_dir, "tokens.txt")

    # Download required files to Colab environment
    if not download_file(onnx_url, onnx_path):
        print("Failed to download ONNX file. Aborting process.")
        return
        
    if not download_file(json_url, json_path):
        print("Failed to download JSON config. Aborting process.")
        return

    print("Files fetched successfully. Starting processing...")

    # Read JSON Configuration File
    try:
        with open(json_path, "r", encoding="utf-8") as f:
            config = json.load(f)
    except Exception as e:
        print(f"Error reading JSON file: {e}")
        return

    # Guard Clause: Verify required keys exist in JSON
    if "language" not in config or "espeak" not in config or "audio" not in config:
        print("Error: JSON config is missing required Piper metadata keys.")
        return

    # Step 1: Embed/Add Metadata into the ONNX File
    print("Step 1: Adding metadata to the ONNX file...")
    try:
        model = onnx.load(onnx_path)
    except Exception as e:
        print(f"Error loading ONNX model: {e}")
        return

    meta_data = {
        "model_type": "vits",
        "comment": "piper",
        "language": config["language"]["code"],
        "voice": config["espeak"]["voice"],
        "has_espeak": 1,
        "n_speakers": config["num_speakers"],
        "sample_rate": config["audio"]["sample_rate"],
    }

    for key, value in meta_data.items():
        meta = model.metadata_props.add()
        meta.key = key
        meta.value = str(value)

    try:
        onnx.save(model, onnx_path)
        print(f"-> Metadata successfully saved into '{onnx_filename}'!")
    except Exception as e:
        print(f"Error saving modified ONNX model: {e}")
        return

    # Step 2: Generate tokens.txt (Phoneme Map) file for Sherpa-ONNX
    print("Step 2: Generating tokens.txt file for Sherpa-ONNX...")
    if "phoneme_id_map" not in config:
        print("Error: 'phoneme_id_map' not found in JSON config.")
        return
        
    id_map = config["phoneme_id_map"]

    try:
        with open(tokens_path, "w", encoding="utf-8") as f_tokens:
            for s, i in id_map.items():
                f_tokens.write(f"{s} {i[0]}\n")
        
        print(f"-> tokens.txt successfully generated! Total tokens: {len(id_map)}")
    except Exception as e:
        print(f"Error writing tokens.txt: {e}")
        return

    # Step 3: Trigger Automatic Browser Downloads
    print("\n[SUCCESS] Conversion complete! Starting automatic browser downloads...")
    try:
        print(f"Downloading {onnx_filename} to your device...")
        files.download(onnx_path)
        
        print("Downloading tokens.txt to your device...")
        files.download(tokens_path)
    except Exception as e:
        print(f"Browser download failed or not running in Colab environment: {e}")

# ==========================================
# MAIN LOGIC - Users only need to edit below
# ==========================================

ONNX_FILE_URL = "YOUR_ONNX_FILE_URL_HERE"
JSON_FILE_URL = "YOUR_JSON_FILE_URL_HERE"
OUTPUT_DIRECTORY = "./sherpa_piper_model"

# Execute the function directly
process_piper_model_from_url(ONNX_FILE_URL, JSON_FILE_URL, OUTPUT_DIRECTORY)