diff --git a/assets/audio/br_003.mp3 b/assets/audio/br_003.mp3
new file mode 100644
index 0000000..6581228
Binary files /dev/null and b/assets/audio/br_003.mp3 differ
diff --git a/assets/audio/br_004.mp3 b/assets/audio/br_004.mp3
new file mode 100644
index 0000000..d1acf0b
Binary files /dev/null and b/assets/audio/br_004.mp3 differ
diff --git a/assets/audio/br_005.mp3 b/assets/audio/br_005.mp3
new file mode 100644
index 0000000..df3fe38
Binary files /dev/null and b/assets/audio/br_005.mp3 differ
diff --git a/assets/audio/de_001.mp3 b/assets/audio/de_001.mp3
new file mode 100644
index 0000000..7f41ad2
Binary files /dev/null and b/assets/audio/de_001.mp3 differ
diff --git a/assets/audio/de_002.mp3 b/assets/audio/de_002.mp3
new file mode 100644
index 0000000..ed9aa0d
Binary files /dev/null and b/assets/audio/de_002.mp3 differ
diff --git a/assets/audio/en_au_001.mp3 b/assets/audio/en_au_001.mp3
new file mode 100644
index 0000000..1679af3
Binary files /dev/null and b/assets/audio/en_au_001.mp3 differ
diff --git a/assets/audio/en_au_002.mp3 b/assets/audio/en_au_002.mp3
new file mode 100644
index 0000000..18018d6
Binary files /dev/null and b/assets/audio/en_au_002.mp3 differ
diff --git a/assets/audio/en_female_betty.mp3 b/assets/audio/en_female_betty.mp3
new file mode 100644
index 0000000..abe2677
Binary files /dev/null and b/assets/audio/en_female_betty.mp3 differ
diff --git a/assets/audio/en_female_emotional.mp3 b/assets/audio/en_female_emotional.mp3
new file mode 100644
index 0000000..ed30eea
Binary files /dev/null and b/assets/audio/en_female_emotional.mp3 differ
diff --git a/assets/audio/en_female_f08_salut_damour.mp3 b/assets/audio/en_female_f08_salut_damour.mp3
new file mode 100644
index 0000000..f007045
Binary files /dev/null and b/assets/audio/en_female_f08_salut_damour.mp3 differ
diff --git a/assets/audio/en_female_f08_twinkle.mp3 b/assets/audio/en_female_f08_twinkle.mp3
new file mode 100644
index 0000000..47fb768
Binary files /dev/null and b/assets/audio/en_female_f08_twinkle.mp3 differ
diff --git a/assets/audio/en_female_f08_warmy_breeze.mp3 b/assets/audio/en_female_f08_warmy_breeze.mp3
new file mode 100644
index 0000000..c8d0576
Binary files /dev/null and b/assets/audio/en_female_f08_warmy_breeze.mp3 differ
diff --git a/assets/audio/en_female_grandma.mp3 b/assets/audio/en_female_grandma.mp3
new file mode 100644
index 0000000..8490bd0
Binary files /dev/null and b/assets/audio/en_female_grandma.mp3 differ
diff --git a/assets/audio/en_female_ht_f08_glorious.mp3 b/assets/audio/en_female_ht_f08_glorious.mp3
new file mode 100644
index 0000000..b1edd23
Binary files /dev/null and b/assets/audio/en_female_ht_f08_glorious.mp3 differ
diff --git a/assets/audio/en_female_ht_f08_halloween.mp3 b/assets/audio/en_female_ht_f08_halloween.mp3
new file mode 100644
index 0000000..0275509
Binary files /dev/null and b/assets/audio/en_female_ht_f08_halloween.mp3 differ
diff --git a/assets/audio/en_female_ht_f08_newyear.mp3 b/assets/audio/en_female_ht_f08_newyear.mp3
new file mode 100644
index 0000000..f962ecb
Binary files /dev/null and b/assets/audio/en_female_ht_f08_newyear.mp3 differ
diff --git a/assets/audio/en_female_ht_f08_wonderful_world.mp3 b/assets/audio/en_female_ht_f08_wonderful_world.mp3
new file mode 100644
index 0000000..207ec7f
Binary files /dev/null and b/assets/audio/en_female_ht_f08_wonderful_world.mp3 differ
diff --git a/assets/audio/en_female_madam_leota.mp3 b/assets/audio/en_female_madam_leota.mp3
new file mode 100644
index 0000000..4c4c0fe
Binary files /dev/null and b/assets/audio/en_female_madam_leota.mp3 differ
diff --git a/assets/audio/en_female_makeup.mp3 b/assets/audio/en_female_makeup.mp3
new file mode 100644
index 0000000..8affb11
Binary files /dev/null and b/assets/audio/en_female_makeup.mp3 differ
diff --git a/assets/audio/en_female_pansino.mp3 b/assets/audio/en_female_pansino.mp3
new file mode 100644
index 0000000..0b0bd25
Binary files /dev/null and b/assets/audio/en_female_pansino.mp3 differ
diff --git a/assets/audio/en_female_richgirl.mp3 b/assets/audio/en_female_richgirl.mp3
new file mode 100644
index 0000000..128f6a3
Binary files /dev/null and b/assets/audio/en_female_richgirl.mp3 differ
diff --git a/assets/audio/en_female_samc.mp3 b/assets/audio/en_female_samc.mp3
new file mode 100644
index 0000000..d83a861
Binary files /dev/null and b/assets/audio/en_female_samc.mp3 differ
diff --git a/assets/audio/en_female_shenna.mp3 b/assets/audio/en_female_shenna.mp3
new file mode 100644
index 0000000..1364d56
Binary files /dev/null and b/assets/audio/en_female_shenna.mp3 differ
diff --git a/assets/audio/en_male_cody.mp3 b/assets/audio/en_male_cody.mp3
new file mode 100644
index 0000000..c94de53
Binary files /dev/null and b/assets/audio/en_male_cody.mp3 differ
diff --git a/assets/audio/en_male_cupid.mp3 b/assets/audio/en_male_cupid.mp3
new file mode 100644
index 0000000..0f3f0ee
Binary files /dev/null and b/assets/audio/en_male_cupid.mp3 differ
diff --git a/assets/audio/en_male_deadpool.mp3 b/assets/audio/en_male_deadpool.mp3
new file mode 100644
index 0000000..ebdc09d
Binary files /dev/null and b/assets/audio/en_male_deadpool.mp3 differ
diff --git a/assets/audio/en_male_funny.mp3 b/assets/audio/en_male_funny.mp3
new file mode 100644
index 0000000..aabf850
Binary files /dev/null and b/assets/audio/en_male_funny.mp3 differ
diff --git a/assets/audio/en_male_ghosthost.mp3 b/assets/audio/en_male_ghosthost.mp3
new file mode 100644
index 0000000..983cdfc
Binary files /dev/null and b/assets/audio/en_male_ghosthost.mp3 differ
diff --git a/assets/audio/en_male_grinch.mp3 b/assets/audio/en_male_grinch.mp3
new file mode 100644
index 0000000..6d69ee5
Binary files /dev/null and b/assets/audio/en_male_grinch.mp3 differ
diff --git a/assets/audio/en_male_jarvis.mp3 b/assets/audio/en_male_jarvis.mp3
new file mode 100644
index 0000000..ef70450
Binary files /dev/null and b/assets/audio/en_male_jarvis.mp3 differ
diff --git a/assets/audio/en_male_m03_classical.mp3 b/assets/audio/en_male_m03_classical.mp3
new file mode 100644
index 0000000..d58adae
Binary files /dev/null and b/assets/audio/en_male_m03_classical.mp3 differ
diff --git a/assets/audio/en_male_m03_lobby.mp3 b/assets/audio/en_male_m03_lobby.mp3
new file mode 100644
index 0000000..8b3c204
Binary files /dev/null and b/assets/audio/en_male_m03_lobby.mp3 differ
diff --git a/assets/audio/en_male_m03_sunshine_soon.mp3 b/assets/audio/en_male_m03_sunshine_soon.mp3
new file mode 100644
index 0000000..e19e97f
Binary files /dev/null and b/assets/audio/en_male_m03_sunshine_soon.mp3 differ
diff --git a/assets/audio/en_male_m2_xhxs_m03_christmas.mp3 b/assets/audio/en_male_m2_xhxs_m03_christmas.mp3
new file mode 100644
index 0000000..a503de7
Binary files /dev/null and b/assets/audio/en_male_m2_xhxs_m03_christmas.mp3 differ
diff --git a/assets/audio/en_male_m2_xhxs_m03_silly.mp3 b/assets/audio/en_male_m2_xhxs_m03_silly.mp3
new file mode 100644
index 0000000..4951520
Binary files /dev/null and b/assets/audio/en_male_m2_xhxs_m03_silly.mp3 differ
diff --git a/assets/audio/en_male_narration.mp3 b/assets/audio/en_male_narration.mp3
new file mode 100644
index 0000000..ba6f3a0
Binary files /dev/null and b/assets/audio/en_male_narration.mp3 differ
diff --git a/assets/audio/en_male_pirate.mp3 b/assets/audio/en_male_pirate.mp3
new file mode 100644
index 0000000..7668797
Binary files /dev/null and b/assets/audio/en_male_pirate.mp3 differ
diff --git a/assets/audio/en_male_santa.mp3 b/assets/audio/en_male_santa.mp3
new file mode 100644
index 0000000..10889fd
Binary files /dev/null and b/assets/audio/en_male_santa.mp3 differ
diff --git a/assets/audio/en_male_santa_effect.mp3 b/assets/audio/en_male_santa_effect.mp3
new file mode 100644
index 0000000..5a24c6a
Binary files /dev/null and b/assets/audio/en_male_santa_effect.mp3 differ
diff --git a/assets/audio/en_male_santa_narration.mp3 b/assets/audio/en_male_santa_narration.mp3
new file mode 100644
index 0000000..dbfa9f3
Binary files /dev/null and b/assets/audio/en_male_santa_narration.mp3 differ
diff --git a/assets/audio/en_male_sing_deep_jingle.mp3 b/assets/audio/en_male_sing_deep_jingle.mp3
new file mode 100644
index 0000000..710b27d
Binary files /dev/null and b/assets/audio/en_male_sing_deep_jingle.mp3 differ
diff --git a/assets/audio/en_male_sing_funny_it_goes_up.mp3 b/assets/audio/en_male_sing_funny_it_goes_up.mp3
new file mode 100644
index 0000000..d73ecca
Binary files /dev/null and b/assets/audio/en_male_sing_funny_it_goes_up.mp3 differ
diff --git a/assets/audio/en_male_sing_funny_thanksgiving.mp3 b/assets/audio/en_male_sing_funny_thanksgiving.mp3
new file mode 100644
index 0000000..8a2b68e
Binary files /dev/null and b/assets/audio/en_male_sing_funny_thanksgiving.mp3 differ
diff --git a/assets/audio/en_male_trevor.mp3 b/assets/audio/en_male_trevor.mp3
new file mode 100644
index 0000000..67c9d40
Binary files /dev/null and b/assets/audio/en_male_trevor.mp3 differ
diff --git a/assets/audio/en_male_ukbutler.mp3 b/assets/audio/en_male_ukbutler.mp3
new file mode 100644
index 0000000..b0a938d
Binary files /dev/null and b/assets/audio/en_male_ukbutler.mp3 differ
diff --git a/assets/audio/en_male_ukneighbor.mp3 b/assets/audio/en_male_ukneighbor.mp3
new file mode 100644
index 0000000..05a2db5
Binary files /dev/null and b/assets/audio/en_male_ukneighbor.mp3 differ
diff --git a/assets/audio/en_male_wizard.mp3 b/assets/audio/en_male_wizard.mp3
new file mode 100644
index 0000000..4b622f6
Binary files /dev/null and b/assets/audio/en_male_wizard.mp3 differ
diff --git a/assets/audio/en_uk_001.mp3 b/assets/audio/en_uk_001.mp3
new file mode 100644
index 0000000..61f70a3
Binary files /dev/null and b/assets/audio/en_uk_001.mp3 differ
diff --git a/assets/audio/en_uk_003.mp3 b/assets/audio/en_uk_003.mp3
new file mode 100644
index 0000000..e69c5ff
Binary files /dev/null and b/assets/audio/en_uk_003.mp3 differ
diff --git a/assets/audio/en_us_002.mp3 b/assets/audio/en_us_002.mp3
new file mode 100644
index 0000000..6daefa5
Binary files /dev/null and b/assets/audio/en_us_002.mp3 differ
diff --git a/assets/audio/en_us_006.mp3 b/assets/audio/en_us_006.mp3
new file mode 100644
index 0000000..8002cc4
Binary files /dev/null and b/assets/audio/en_us_006.mp3 differ
diff --git a/assets/audio/en_us_007.mp3 b/assets/audio/en_us_007.mp3
new file mode 100644
index 0000000..d944b0d
Binary files /dev/null and b/assets/audio/en_us_007.mp3 differ
diff --git a/assets/audio/en_us_009.mp3 b/assets/audio/en_us_009.mp3
new file mode 100644
index 0000000..a77e7be
Binary files /dev/null and b/assets/audio/en_us_009.mp3 differ
diff --git a/assets/audio/en_us_010.mp3 b/assets/audio/en_us_010.mp3
new file mode 100644
index 0000000..6707841
Binary files /dev/null and b/assets/audio/en_us_010.mp3 differ
diff --git a/assets/audio/en_us_c3po.mp3 b/assets/audio/en_us_c3po.mp3
new file mode 100644
index 0000000..52834d4
Binary files /dev/null and b/assets/audio/en_us_c3po.mp3 differ
diff --git a/assets/audio/en_us_chewbacca.mp3 b/assets/audio/en_us_chewbacca.mp3
new file mode 100644
index 0000000..bcd0a6b
Binary files /dev/null and b/assets/audio/en_us_chewbacca.mp3 differ
diff --git a/assets/audio/en_us_ghostface.mp3 b/assets/audio/en_us_ghostface.mp3
new file mode 100644
index 0000000..4a070d8
Binary files /dev/null and b/assets/audio/en_us_ghostface.mp3 differ
diff --git a/assets/audio/en_us_rocket.mp3 b/assets/audio/en_us_rocket.mp3
new file mode 100644
index 0000000..77a19ca
Binary files /dev/null and b/assets/audio/en_us_rocket.mp3 differ
diff --git a/assets/audio/en_us_stitch.mp3 b/assets/audio/en_us_stitch.mp3
new file mode 100644
index 0000000..e373ea4
Binary files /dev/null and b/assets/audio/en_us_stitch.mp3 differ
diff --git a/assets/audio/en_us_stormtrooper.mp3 b/assets/audio/en_us_stormtrooper.mp3
new file mode 100644
index 0000000..cd65cba
Binary files /dev/null and b/assets/audio/en_us_stormtrooper.mp3 differ
diff --git a/assets/audio/es_002.mp3 b/assets/audio/es_002.mp3
new file mode 100644
index 0000000..58120eb
Binary files /dev/null and b/assets/audio/es_002.mp3 differ
diff --git a/assets/audio/es_female_f6.mp3 b/assets/audio/es_female_f6.mp3
new file mode 100644
index 0000000..563fb07
Binary files /dev/null and b/assets/audio/es_female_f6.mp3 differ
diff --git a/assets/audio/es_female_fp1.mp3 b/assets/audio/es_female_fp1.mp3
new file mode 100644
index 0000000..05f3f15
Binary files /dev/null and b/assets/audio/es_female_fp1.mp3 differ
diff --git a/assets/audio/es_male_m3.mp3 b/assets/audio/es_male_m3.mp3
new file mode 100644
index 0000000..fccf1cd
Binary files /dev/null and b/assets/audio/es_male_m3.mp3 differ
diff --git a/assets/audio/es_mx_002.mp3 b/assets/audio/es_mx_002.mp3
new file mode 100644
index 0000000..5030be0
Binary files /dev/null and b/assets/audio/es_mx_002.mp3 differ
diff --git a/assets/audio/es_mx_female_supermom.mp3 b/assets/audio/es_mx_female_supermom.mp3
new file mode 100644
index 0000000..1e413d1
Binary files /dev/null and b/assets/audio/es_mx_female_supermom.mp3 differ
diff --git a/assets/audio/es_mx_male_transformer.mp3 b/assets/audio/es_mx_male_transformer.mp3
new file mode 100644
index 0000000..e69de29
diff --git a/assets/audio/fr_001.mp3 b/assets/audio/fr_001.mp3
new file mode 100644
index 0000000..d72ea36
Binary files /dev/null and b/assets/audio/fr_001.mp3 differ
diff --git a/assets/audio/fr_002.mp3 b/assets/audio/fr_002.mp3
new file mode 100644
index 0000000..fe40092
Binary files /dev/null and b/assets/audio/fr_002.mp3 differ
diff --git a/assets/audio/id_female_icha.mp3 b/assets/audio/id_female_icha.mp3
new file mode 100644
index 0000000..eb7ee91
Binary files /dev/null and b/assets/audio/id_female_icha.mp3 differ
diff --git a/assets/audio/id_female_noor.mp3 b/assets/audio/id_female_noor.mp3
new file mode 100644
index 0000000..5290816
Binary files /dev/null and b/assets/audio/id_female_noor.mp3 differ
diff --git a/assets/audio/id_male_darma.mp3 b/assets/audio/id_male_darma.mp3
new file mode 100644
index 0000000..fbd97c1
Binary files /dev/null and b/assets/audio/id_male_darma.mp3 differ
diff --git a/assets/audio/id_male_putra.mp3 b/assets/audio/id_male_putra.mp3
new file mode 100644
index 0000000..3fd8c84
Binary files /dev/null and b/assets/audio/id_male_putra.mp3 differ
diff --git a/assets/audio/it_male_m18.mp3 b/assets/audio/it_male_m18.mp3
new file mode 100644
index 0000000..55928be
Binary files /dev/null and b/assets/audio/it_male_m18.mp3 differ
diff --git a/assets/audio/jp_001.mp3 b/assets/audio/jp_001.mp3
new file mode 100644
index 0000000..c4e77b0
Binary files /dev/null and b/assets/audio/jp_001.mp3 differ
diff --git a/assets/audio/jp_003.mp3 b/assets/audio/jp_003.mp3
new file mode 100644
index 0000000..81393b2
Binary files /dev/null and b/assets/audio/jp_003.mp3 differ
diff --git a/assets/audio/jp_005.mp3 b/assets/audio/jp_005.mp3
new file mode 100644
index 0000000..6c0ddf6
Binary files /dev/null and b/assets/audio/jp_005.mp3 differ
diff --git a/assets/audio/jp_006.mp3 b/assets/audio/jp_006.mp3
new file mode 100644
index 0000000..08e5dfe
Binary files /dev/null and b/assets/audio/jp_006.mp3 differ
diff --git a/assets/audio/jp_female_machikoriiita.mp3 b/assets/audio/jp_female_machikoriiita.mp3
new file mode 100644
index 0000000..7cacc87
Binary files /dev/null and b/assets/audio/jp_female_machikoriiita.mp3 differ
diff --git a/assets/audio/jp_female_rei.mp3 b/assets/audio/jp_female_rei.mp3
new file mode 100644
index 0000000..6705201
Binary files /dev/null and b/assets/audio/jp_female_rei.mp3 differ
diff --git a/assets/audio/jp_female_yagishaki.mp3 b/assets/audio/jp_female_yagishaki.mp3
new file mode 100644
index 0000000..b3dc46d
Binary files /dev/null and b/assets/audio/jp_female_yagishaki.mp3 differ
diff --git a/assets/audio/jp_male_hikakin.mp3 b/assets/audio/jp_male_hikakin.mp3
new file mode 100644
index 0000000..dea2dae
Binary files /dev/null and b/assets/audio/jp_male_hikakin.mp3 differ
diff --git a/assets/audio/jp_male_matsudake.mp3 b/assets/audio/jp_male_matsudake.mp3
new file mode 100644
index 0000000..46eca0f
Binary files /dev/null and b/assets/audio/jp_male_matsudake.mp3 differ
diff --git a/assets/audio/jp_male_matsuo.mp3 b/assets/audio/jp_male_matsuo.mp3
new file mode 100644
index 0000000..0e7d1b3
Binary files /dev/null and b/assets/audio/jp_male_matsuo.mp3 differ
diff --git a/assets/audio/jp_male_osada.mp3 b/assets/audio/jp_male_osada.mp3
new file mode 100644
index 0000000..34935c7
Binary files /dev/null and b/assets/audio/jp_male_osada.mp3 differ
diff --git a/assets/audio/jp_male_shuichiro.mp3 b/assets/audio/jp_male_shuichiro.mp3
new file mode 100644
index 0000000..14cd124
Binary files /dev/null and b/assets/audio/jp_male_shuichiro.mp3 differ
diff --git a/assets/audio/kr_002.mp3 b/assets/audio/kr_002.mp3
new file mode 100644
index 0000000..3646d7a
Binary files /dev/null and b/assets/audio/kr_002.mp3 differ
diff --git a/assets/audio/kr_003.mp3 b/assets/audio/kr_003.mp3
new file mode 100644
index 0000000..3eb6e2e
Binary files /dev/null and b/assets/audio/kr_003.mp3 differ
diff --git a/assets/audio/kr_004.mp3 b/assets/audio/kr_004.mp3
new file mode 100644
index 0000000..c2d03d8
Binary files /dev/null and b/assets/audio/kr_004.mp3 differ
diff --git a/assets/audio/pt_female_laizza.mp3 b/assets/audio/pt_female_laizza.mp3
new file mode 100644
index 0000000..416f89d
Binary files /dev/null and b/assets/audio/pt_female_laizza.mp3 differ
diff --git a/assets/audio/pt_female_lhays.mp3 b/assets/audio/pt_female_lhays.mp3
new file mode 100644
index 0000000..1c6aeb2
Binary files /dev/null and b/assets/audio/pt_female_lhays.mp3 differ
diff --git a/assets/audio/pt_male_transformer.mp3 b/assets/audio/pt_male_transformer.mp3
new file mode 100644
index 0000000..e69de29
diff --git a/assets/control.html b/assets/control.html
index ada5424..2a91818 100644
--- a/assets/control.html
+++ b/assets/control.html
@@ -106,16 +106,42 @@
class="provider-voice-settings"
style="display: none"
>
-
-
-
-
-
+
+
+
+
+
+
+
+ Selected Voices: 0
+
+
+
+
+
+
+
+
+
+
Language
+
Voice
+
Gender
+
Sample
+
+
+
+
+
+
+
diff --git a/assets/css/control.css b/assets/css/control.css
index c1e98b7..fb39fb3 100644
--- a/assets/css/control.css
+++ b/assets/css/control.css
@@ -833,4 +833,134 @@ button:not(:disabled):hover {
/* Provider-specific styles */
.provider-voice-settings:not(:first-child) {
margin-top: var(--spacing-lg);
+}
+
+/* Voice table styles */
+.voice-filter {
+ display: flex;
+ gap: var(--spacing-md);
+ margin-bottom: var(--spacing-lg);
+}
+
+.tts-input {
+ flex: 2;
+ padding: var(--spacing-md) var(--spacing-lg);
+ border-radius: var(--border-radius-md);
+ border: 1px solid var(--border-color);
+ font-size: var(--font-sm);
+}
+
+.voice-table-container {
+ max-height: 400px;
+ overflow-y: auto;
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius-md);
+}
+
+.voice-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.voice-table th,
+.voice-table td {
+ padding: var(--spacing-md);
+ text-align: left;
+ border-bottom: 1px solid var(--border-color);
+}
+
+.voice-table th {
+ background: var(--color-secondary);
+ position: sticky;
+ top: 0;
+ z-index: 1;
+}
+
+.voice-table tr:hover {
+ background: var(--color-secondary);
+}
+
+.voice-table .play-sample {
+ background: none;
+ border: none;
+ color: var(--color-primary);
+ cursor: pointer;
+ padding: var(--spacing-sm);
+}
+
+.voice-table .play-sample:hover {
+ color: var(--color-primary-dark);
+}
+
+/* Style the audio player */
+.voice-sample {
+ height: 30px;
+ width: 150px;
+}
+
+/* Make the sample column wide enough */
+.voice-table td:last-child {
+ min-width: 160px;
+}
+
+.voice-filter .tts-select {
+ flex: 1;
+}
+
+.voice-counter {
+ margin-bottom: var(--spacing-md);
+ font-size: var(--font-sm);
+ color: var(--text-secondary);
+ font-weight: 500;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.voice-counter span {
+ color: var(--color-primary);
+ font-weight: 600;
+}
+
+.clear-voices-btn {
+ border: none;
+ background: none;
+ color: var(--color-danger);
+ font-size: var(--font-xs);
+ cursor: pointer;
+ padding: var(--spacing-xs) var(--spacing-sm);
+ border-radius: var(--border-radius-sm);
+ transition: var(--transition-default);
+}
+
+.clear-voices-btn:hover {
+ background: var(--color-danger);
+ color: white;
+}
+
+.voice-table tr:hover {
+ background: var(--color-secondary);
+}
+
+.voice-row {
+ cursor: pointer;
+}
+
+.voice-row .sample-cell {
+ cursor: default;
+}
+
+/* Prevent text selection when clicking rows */
+.voice-row td:not(.sample-cell) {
+ user-select: none;
+}
+
+/* Make all cells except sample cell clickable */
+.voice-row td:not(.sample-cell) {
+ cursor: pointer;
+ user-select: none;
+}
+
+.voice-row .sample-cell {
+ cursor: default;
}
\ No newline at end of file
diff --git a/assets/data/tiktok_voice_ids.csv b/assets/data/tiktok_voice_ids.csv
index 3adf0a3..12342c2 100644
--- a/assets/data/tiktok_voice_ids.csv
+++ b/assets/data/tiktok_voice_ids.csv
@@ -1,94 +1,93 @@
-lang_label,voice_name,voice_id
-English,Narrator (Chris),en_uk_001
-English,UK Male 2,en_uk_003
-English,Peaceful,en_female_emotional
-English,Metro (Eddie),en_au_001
-English,Smooth (Alex),en_au_002
-English,Jessie,en_us_002
-English,Joey,en_us_006
-English,Professor,en_us_007
-English,Scientist,en_us_009
-English,Confidence,en_us_010
-English,Empathetic,en_female_samc
-English,Serious,en_male_cody
-English,Story Teller,en_male_narration
-English,Wacky,en_male_funny
-English,Alfred,en_male_jarvis
-English,Author,en_male_santa_narration
-English,Bae,en_female_betty
-English,Beauty Guru,en_female_makeup
-English,Bestie,en_female_richgirl
-English,Cupid,en_male_cupid
-English,Debutante,en_female_shenna
-English,Ghost Host,en_male_ghosthost
-English,Grandma,en_female_grandma
-English,Lord Cringe,en_male_ukneighbor
-English,Magician,en_male_wizard
-English,Marty,en_male_trevor
-English,Mr. GoodGuy (Deadpool),en_male_deadpool
-English,Mr. Meticulous,en_male_ukbutler
-English,Optimus Prime,en_male_petercullen
-English,Pirate,en_male_pirate
-English,Santa,en_male_santa
-English,Santa (w/ effect),en_male_santa_effect
-English,Varsity,en_female_pansino
-English,Trickster (Grinch),en_male_grinch
-English,Ghostface (Scream),en_us_ghostface
-English,Chewbacca (Star Wars),en_us_chewbacca
-English,C-3PO (Star Wars),en_us_c3po
-English,Stormtrooper (Star Wars),en_us_stormtrooper
-English,Stitch (Lilo & Stitch),en_us_stitch
-English,Rocket (Guardians of the Galaxy),en_us_rocket
-English,Madame Leota (Haunted Mansion),en_female_madam_leota
-English,Song: Caroler,en_male_sing_deep_jingle
-English,Song: Classic Electric,en_male_m03_classical
-English,Song: Cottagecore (Salut d'Amour),en_female_f08_salut_damour
-English,Song: Cozy,en_male_m2_xhxs_m03_christmas
-English,Song: Open Mic (Warmy Breeze),en_female_f08_warmy_breeze
-English,Song: Opera (Halloween),en_female_ht_f08_halloween
-English,Song: Euphoric (Glorious),en_female_ht_f08_glorious
-English,Song: Hypetrain (It Goes Up),en_male_sing_funny_it_goes_up
-English,Song: Jingle (Lobby),en_male_m03_lobby
-English,Song: Melodrama (Wonderful World),en_female_ht_f08_wonderful_world
-English,Song: NYE 2023,en_female_ht_f08_newyear
-English,Song: Thanksgiving,en_male_sing_funny_thanksgiving
-English,Song: Toon Beat (Sunshine Soon),en_male_m03_sunshine_soon
-English,Song: Pop Lullaby,en_female_f08_twinkle
-English,Song: Quirky Time,en_male_m2_xhxs_m03_silly
-French,French Male 1,fr_001
-French,French Male 2,fr_002
-German,German Female,de_001
-German,German Male,de_002
-Indonesian,Darma,id_male_darma
-Indonesian,Icha,id_female_icha
-Indonesian,Noor,id_female_noor
-Indonesian,Putra,id_male_putra
-Italian,Italian Male,it_male_m18
-Japanese,Miho,jp_001
-Japanese,Keiko,jp_003
-Japanese,Sakura,jp_005
-Japanese,Naoki,jp_006
-Japanese,Morisuke,jp_male_osada
-Japanese,Matsuo,jp_male_matsuo
-Japanese,Machikoriiita,jp_female_machikoriiita
-Japanese,Matsudake,jp_male_matsudake
-Japanese,Shuichiro,jp_male_shuichiro
-Japanese,Maruyama Rei,jp_female_rei
-Japanese,Hikakin,jp_male_hikakin
-Japanese,Yagi Saki,jp_female_yagishaki
-Korean,Korean Male 1,kr_002
-Korean,Korean Male 2,kr_004
-Korean,Korean Female,kr_003
-Portuguese,Julia,br_003
-Portuguese,Ana,br_004
-Portuguese,Lucas,br_005
-Portuguese,Lhays Macedo,pt_female_lhays
-Portuguese,Laizza,pt_female_laizza
-Portuguese,Optimus Prime (Portuguese),pt_male_transformer
-Spanish,Spanish Male,es_002
-Spanish,Julio,es_male_m3
-Spanish,Alejandra,es_female_f6
-Spanish,Mariana,es_female_fp1
-Spanish,Álex (Warm),es_mx_002
-Spanish,Optimus Prime (Mexican),es_mx_male_transformer
-Spanish,Super Mamá,es_mx_female_supermom
+lang_label,voice_name,voice_gender,voice_id,sample_sentence,sample_voice
+English,Narrator (Chris),neutral,en_uk_001,This is an example sentence in English.,/audio/en_uk_001.mp3
+English,UK Male 2,male,en_uk_003,This is an example sentence in English.,/audio/en_uk_003.mp3
+English,Peaceful,female,en_female_emotional,This is an example sentence in English.,/audio/en_female_emotional.mp3
+English,Metro (Eddie),neutral,en_au_001,This is an example sentence in English.,/audio/en_au_001.mp3
+English,Smooth (Alex),neutral,en_au_002,This is an example sentence in English.,/audio/en_au_002.mp3
+English,Jessie,neutral,en_us_002,This is an example sentence in English.,/audio/en_us_002.mp3
+English,Joey,neutral,en_us_006,This is an example sentence in English.,/audio/en_us_006.mp3
+English,Professor,neutral,en_us_007,This is an example sentence in English.,/audio/en_us_007.mp3
+English,Scientist,neutral,en_us_009,This is an example sentence in English.,/audio/en_us_009.mp3
+English,Confidence,neutral,en_us_010,This is an example sentence in English.,/audio/en_us_010.mp3
+English,Empathetic,female,en_female_samc,This is an example sentence in English.,/audio/en_female_samc.mp3
+English,Serious,male,en_male_cody,This is an example sentence in English.,/audio/en_male_cody.mp3
+English,Story Teller,male,en_male_narration,This is an example sentence in English.,/audio/en_male_narration.mp3
+English,Wacky,male,en_male_funny,This is an example sentence in English.,/audio/en_male_funny.mp3
+English,Alfred,male,en_male_jarvis,This is an example sentence in English.,/audio/en_male_jarvis.mp3
+English,Author,male,en_male_santa_narration,This is an example sentence in English.,/audio/en_male_santa_narration.mp3
+English,Bae,female,en_female_betty,This is an example sentence in English.,/audio/en_female_betty.mp3
+English,Beauty Guru,female,en_female_makeup,This is an example sentence in English.,/audio/en_female_makeup.mp3
+English,Bestie,female,en_female_richgirl,This is an example sentence in English.,/audio/en_female_richgirl.mp3
+English,Cupid,male,en_male_cupid,This is an example sentence in English.,/audio/en_male_cupid.mp3
+English,Debutante,female,en_female_shenna,This is an example sentence in English.,/audio/en_female_shenna.mp3
+English,Ghost Host,male,en_male_ghosthost,This is an example sentence in English.,/audio/en_male_ghosthost.mp3
+English,Grandma,female,en_female_grandma,This is an example sentence in English.,/audio/en_female_grandma.mp3
+English,Lord Cringe,male,en_male_ukneighbor,This is an example sentence in English.,/audio/en_male_ukneighbor.mp3
+English,Magician,male,en_male_wizard,This is an example sentence in English.,/audio/en_male_wizard.mp3
+English,Marty,male,en_male_trevor,This is an example sentence in English.,/audio/en_male_trevor.mp3
+English,Mr. GoodGuy (Deadpool),male,en_male_deadpool,This is an example sentence in English.,/audio/en_male_deadpool.mp3
+English,Mr. Meticulous,male,en_male_ukbutler,This is an example sentence in English.,/audio/en_male_ukbutler.mp3
+English,Pirate,male,en_male_pirate,This is an example sentence in English.,/audio/en_male_pirate.mp3
+English,Santa,male,en_male_santa,This is an example sentence in English.,/audio/en_male_santa.mp3
+English,Santa (w/ effect),male,en_male_santa_effect,This is an example sentence in English.,/audio/en_male_santa_effect.mp3
+English,Varsity,female,en_female_pansino,This is an example sentence in English.,/audio/en_female_pansino.mp3
+English,Trickster (Grinch),male,en_male_grinch,This is an example sentence in English.,/audio/en_male_grinch.mp3
+English,Ghostface (Scream),neutral,en_us_ghostface,This is an example sentence in English.,/audio/en_us_ghostface.mp3
+English,Chewbacca (Star Wars),neutral,en_us_chewbacca,This is an example sentence in English.,/audio/en_us_chewbacca.mp3
+English,C-3PO (Star Wars),neutral,en_us_c3po,This is an example sentence in English.,/audio/en_us_c3po.mp3
+English,Stormtrooper (Star Wars),neutral,en_us_stormtrooper,This is an example sentence in English.,/audio/en_us_stormtrooper.mp3
+English,Stitch (Lilo & Stitch),neutral,en_us_stitch,This is an example sentence in English.,/audio/en_us_stitch.mp3
+English,Rocket (Guardians of the Galaxy),neutral,en_us_rocket,This is an example sentence in English.,/audio/en_us_rocket.mp3
+English,Madame Leota (Haunted Mansion),female,en_female_madam_leota,This is an example sentence in English.,/audio/en_female_madam_leota.mp3
+Song,Song: Caroler,male,en_male_sing_deep_jingle,This is an example sentence in English.,/audio/en_male_sing_deep_jingle.mp3
+Song,Song: Classic Electric,male,en_male_m03_classical,This is an example sentence in English.,/audio/en_male_m03_classical.mp3
+Song,Song: Cottagecore (Salut d'Amour),female,en_female_f08_salut_damour,This is an example sentence in English.,/audio/en_female_f08_salut_damour.mp3
+Song,Song: Cozy,male,en_male_m2_xhxs_m03_christmas,This is an example sentence in English.,/audio/en_male_m2_xhxs_m03_christmas.mp3
+Song,Song: Open Mic (Warmy Breeze),female,en_female_f08_warmy_breeze,This is an example sentence in English.,/audio/en_female_f08_warmy_breeze.mp3
+Song,Song: Opera (Halloween),female,en_female_ht_f08_halloween,This is an example sentence in English.,/audio/en_female_ht_f08_halloween.mp3
+Song,Song: Euphoric (Glorious),female,en_female_ht_f08_glorious,This is an example sentence in English.,/audio/en_female_ht_f08_glorious.mp3
+Song,Song: Hypetrain (It Goes Up),male,en_male_sing_funny_it_goes_up,This is an example sentence in English.,/audio/en_male_sing_funny_it_goes_up.mp3
+Song,Song: Jingle (Lobby),male,en_male_m03_lobby,This is an example sentence in English.,/audio/en_male_m03_lobby.mp3
+Song,Song: Melodrama (Wonderful World),female,en_female_ht_f08_wonderful_world,This is an example sentence in English.,/audio/en_female_ht_f08_wonderful_world.mp3
+Song,Song: NYE 2023,female,en_female_ht_f08_newyear,This is an example sentence in English.,/audio/en_female_ht_f08_newyear.mp3
+Song,Song: Thanksgiving,male,en_male_sing_funny_thanksgiving,This is an example sentence in English.,/audio/en_male_sing_funny_thanksgiving.mp3
+Song,Song: Toon Beat (Sunshine Soon),male,en_male_m03_sunshine_soon,This is an example sentence in English.,/audio/en_male_m03_sunshine_soon.mp3
+Song,Song: Pop Lullaby,female,en_female_f08_twinkle,This is an example sentence in English.,/audio/en_female_f08_twinkle.mp3
+Song,Song: Quirky Time,male,en_male_m2_xhxs_m03_silly,This is an example sentence in English.,/audio/en_male_m2_xhxs_m03_silly.mp3
+French,French Male 1,male,fr_001,Ceci est une phrase exemple en français.,/audio/fr_001.mp3
+French,French Male 2,male,fr_002,Ceci est une phrase exemple en français.,/audio/fr_002.mp3
+German,German Female,female,de_001,Dies ist ein Beispielsatz auf Deutsch.,/audio/de_001.mp3
+German,German Male,male,de_002,Dies ist ein Beispielsatz auf Deutsch.,/audio/de_002.mp3
+Indonesian,Darma,male,id_male_darma,Ini adalah kalimat contoh dalam bahasa Indonesia.,/audio/id_male_darma.mp3
+Indonesian,Icha,female,id_female_icha,Ini adalah kalimat contoh dalam bahasa Indonesia.,/audio/id_female_icha.mp3
+Indonesian,Noor,female,id_female_noor,Ini adalah kalimat contoh dalam bahasa Indonesia.,/audio/id_female_noor.mp3
+Indonesian,Putra,male,id_male_putra,Ini adalah kalimat contoh dalam bahasa Indonesia.,/audio/id_male_putra.mp3
+Italian,Italian Male,male,it_male_m18,Questa è una frase di esempio in italiano.,/audio/it_male_m18.mp3
+Japanese,Miho,neutral,jp_001,これは日本語の例文です。,/audio/jp_001.mp3
+Japanese,Keiko,neutral,jp_003,これは日本語の例文です。,/audio/jp_003.mp3
+Japanese,Sakura,neutral,jp_005,これは日本語の例文です。,/audio/jp_005.mp3
+Japanese,Naoki,neutral,jp_006,これは日本語の例文です。,/audio/jp_006.mp3
+Japanese,Morisuke,male,jp_male_osada,これは日本語の例文です。,/audio/jp_male_osada.mp3
+Japanese,Matsuo,male,jp_male_matsuo,これは日本語の例文です。,/audio/jp_male_matsuo.mp3
+Japanese,Machikoriiita,female,jp_female_machikoriiita,これは日本語の例文です。,/audio/jp_female_machikoriiita.mp3
+Japanese,Matsudake,male,jp_male_matsudake,これは日本語の例文です。,/audio/jp_male_matsudake.mp3
+Japanese,Shuichiro,male,jp_male_shuichiro,これは日本語の例文です。,/audio/jp_male_shuichiro.mp3
+Japanese,Maruyama Rei,female,jp_female_rei,これは日本語の例文です。,/audio/jp_female_rei.mp3
+Japanese,Hikakin,male,jp_male_hikakin,これは日本語の例文です。,/audio/jp_male_hikakin.mp3
+Japanese,Yagi Saki,female,jp_female_yagishaki,これは日本語の例文です。,/audio/jp_female_yagishaki.mp3
+Korean,Korean Male 1,male,kr_002,이것은 한국어 예문입니다.,/audio/kr_002.mp3
+Korean,Korean Male 2,male,kr_004,이것은 한국어 예문입니다.,/audio/kr_004.mp3
+Korean,Korean Female,female,kr_003,이것은 한국어 예문입니다.,/audio/kr_003.mp3
+Portuguese,Julia,neutral,br_003,Esta é uma frase de exemplo em português.,/audio/br_003.mp3
+Portuguese,Ana,neutral,br_004,Esta é uma frase de exemplo em português.,/audio/br_004.mp3
+Portuguese,Lucas,neutral,br_005,Esta é uma frase de exemplo em português.,/audio/br_005.mp3
+Portuguese,Lhays Macedo,female,pt_female_lhays,Esta é uma frase de exemplo em português.,/audio/pt_female_lhays.mp3
+Portuguese,Laizza,female,pt_female_laizza,Esta é uma frase de exemplo em português.,/audio/pt_female_laizza.mp3
+Portuguese,Optimus Prime (Portuguese),male,pt_male_transformer,Esta é uma frase de exemplo em português.,/audio/pt_male_transformer.mp3
+Spanish,Spanish Male,male,es_002,Esta es una oración de ejemplo en español.,/audio/es_002.mp3
+Spanish,Julio,male,es_male_m3,Esta es una oración de ejemplo en español.,/audio/es_male_m3.mp3
+Spanish,Alejandra,female,es_female_f6,Esta es una oración de ejemplo en español.,/audio/es_female_f6.mp3
+Spanish,Mariana,female,es_female_fp1,Esta es una oración de ejemplo en español.,/audio/es_female_fp1.mp3
+Spanish,Álex (Warm),neutral,es_mx_002,Esta es una oración de ejemplo en español.,/audio/es_mx_002.mp3
+Spanish,Optimus Prime (Mexican),male,es_mx_male_transformer,Esta es una oración de ejemplo en español.,/audio/es_mx_male_transformer.mp3
+Spanish,Super Mamá,female,es_mx_female_supermom,Esta es una oración de ejemplo en español.,/audio/es_mx_female_supermom.mp3
diff --git a/assets/js/modules/VoiceManager.js b/assets/js/modules/VoiceManager.js
index 8db8102..9cbde65 100644
--- a/assets/js/modules/VoiceManager.js
+++ b/assets/js/modules/VoiceManager.js
@@ -1,11 +1,17 @@
export class VoiceManager {
constructor() {
this.voiceData = null;
+ this.selectedVoices = new Set();
this.providerSelect = document.getElementById('tts-provider');
this.googleSelect = document.getElementById('google-voice-select');
this.tiktokSelects = document.getElementById('tiktok-voice-selects');
- this.tiktokLangSelect = document.getElementById('tiktok-language');
- this.tiktokVoiceSelect = document.getElementById('tiktok-voice');
+ this.voiceSearch = document.getElementById('voice-search');
+ this.languageFilter = document.getElementById('language-filter');
+ this.genderFilter = document.getElementById('gender-filter');
+ this.voiceList = document.getElementById('voice-list');
+ this.selectAllCheckbox = document.getElementById('select-all-voices');
+ this.selectedVoiceCount = document.getElementById('selected-voice-count');
+ this.clearVoicesBtn = document.getElementById('clear-voices');
this.init();
}
@@ -21,7 +27,8 @@ export class VoiceManager {
const response = await fetch('/data/tiktok_voice_ids.csv');
const text = await response.text();
this.voiceData = this.parseCSV(text);
- this.populateLanguageSelect();
+ this.populateLanguageFilter();
+ this.populateVoiceList();
} catch (error) {
console.error('Failed to load voice data:', error);
}
@@ -31,37 +38,78 @@ export class VoiceManager {
const lines = text.split('\n');
const headers = lines[0].split(',');
- return lines.slice(1)
+ const parsed = lines.slice(1)
.filter(line => line.trim())
.map(line => {
- const values = line.split(',');
- return {
+ // Handle CSV values that might contain commas within quotes
+ const values = line.match(/(?:^|,)("(?:[^"]|"")*"|[^,]*)/g)
+ .map(value => {
+ // Remove leading comma and quotes, handle escaped quotes
+ value = value.replace(/^,/, '');
+ if (value.startsWith('"') && value.endsWith('"')) {
+ value = value.slice(1, -1).replace(/""/g, '"');
+ }
+ return value.trim();
+ });
+
+ const result = {
lang_label: values[0],
voice_name: values[1],
- voice_id: values[2].trim()
+ voice_gender: values[2],
+ voice_id: values[3],
+ sample_sentence: values[4] || '',
+ sample_voice: values[5] ? values[5].replace(/\\n/g, '\n').replace(/\\"/g, '"') : ''
};
+ console.log('Parsed voice entry:', result);
+ return result;
});
+ return parsed;
}
- populateLanguageSelect() {
+ populateLanguageFilter() {
// Get unique languages
const languages = [...new Set(this.voiceData.map(v => v.lang_label))];
- this.tiktokLangSelect.innerHTML = languages
+ this.languageFilter.innerHTML = '' + languages
.map(lang => ``)
.join('');
-
- // Trigger voice population for initial language
- this.populateVoiceSelect(languages[0]);
}
- populateVoiceSelect(language) {
- const voices = this.voiceData.filter(v => v.lang_label === language);
-
- this.tiktokVoiceSelect.innerHTML =
- '' +
- voices.map(v => ``)
- .join('');
+ populateVoiceList() {
+ this.voiceList.innerHTML = this.voiceData
+ .map(voice => `
+