{"id":42650,"date":"2025-01-24T10:51:35","date_gmt":"2025-01-24T02:51:35","guid":{"rendered":"https:\/\/www.junyiacademy.org\/event\/?page_id=42650"},"modified":"2025-02-15T23:52:00","modified_gmt":"2025-02-15T15:52:00","slug":"sayit","status":"publish","type":"page","link":"https:\/\/www.junyiacademy.org\/event\/sayit\/","title":{"rendered":"SayIt"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"42650\" class=\"elementor elementor-42650\">\n\t\t\t\t\t\t<div class=\"elementor-inner\">\n\t\t\t\t<div class=\"elementor-section-wrap\">\n\t\t\t\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-689c616 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"689c616\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t\t\t<div class=\"elementor-row\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-eb01136\" data-id=\"eb01136\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-column-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t<div class=\"elementor-widget-wrap\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-4dbcb6d elementor-widget elementor-widget-html\" data-id=\"4dbcb6d\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Text to Speech Demo<\/title>\n  <style>\n    body {\n      font-family: Arial, sans-serif;\n      margin: 10px;\n    }\n    #loadingPlacholder {\n      display: block;\n    }\n    #userIsLogin {\n      display: none;\n    }\n    #userIsNotLogin {\n      display: none;\n    }\n    .container {\n      max-width: 400px;\n      margin: auto;\n    }\n    .inline-group {\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      margin-bottom: 10px;\n      flex-wrap: wrap;\n      gap: 5px;\n    }\n    .radio-group {\n      display: flex;\n      gap: 0;\n      border: 2px solid #007BFF;\n      border-radius: 25px;\n      overflow: hidden;\n      background-color: #f9f9f9;\n    }\n    .radio-group input[type=\"radio\"] {\n      display: none;\n    }\n    .radio-group label {\n      flex: 1;\n      text-align: center;\n      padding: 10px 15px;\n      cursor: pointer;\n      font-size: 16px;\n      transition: background-color 0.3s, color 0.3s;\n    }\n    .radio-group input[type=\"radio\"]:checked + label {\n      background-color: #007BFF;\n      color: #fff;\n    }\n    .radio-group label:not(:last-child) {\n      border-right: 1px solid #007BFF;\n    }\n    button {\n      display: block;\n      margin: 5px 0;\n      padding: 15px;\n      width: 100%;\n      font-size: 16px;\n      background-color: #007BFF;\n      color: #fff;\n      border: 2px solid #007BFF;\n      border-radius: 25px;\n      cursor: pointer;\n      text-align: center;\n      transition: background-color 0.3s, border-color 0.3s;\n    }\n    button:hover {\n      background-color: #0056b3;\n      border-color: #0056b3;\n    }\n    audio {\n      margin-top: 10px;\n      width: 100%;\n    }\n    .toggle-container {\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      margin-bottom: 10px;\n    }\n    .toggle-container .toggle-switch {\n      position: relative;\n      display: inline-block;\n      width: 40px;\n      height: 20px;\n    }\n    .toggle-container .toggle-switch input {\n      opacity: 0;\n      width: 0;\n      height: 0;\n    }\n    .toggle-container .slider {\n      position: absolute;\n      cursor: pointer;\n      top: 0;\n      left: 0;\n      right: 0;\n      bottom: 0;\n      background-color: #ccc;\n      transition: 0.4s;\n      border-radius: 20px;\n    }\n    .toggle-container .slider:before {\n      position: absolute;\n      content: \"\";\n      height: 14px;\n      width: 14px;\n      left: 3px;\n      bottom: 3px;\n      background-color: white;\n      transition: 0.4s;\n      border-radius: 50%;\n    }\n    .toggle-container input:checked + .slider {\n      background-color: #007BFF;\n    }\n    .toggle-container input:checked + .slider:before {\n      transform: translateX(20px);\n    }\n    textarea {\n      width: 100%;\n      resize: none;\n      display: none;\n      font-size: 14px;\n      padding: 5px;\n      box-sizing: border-box;\n    }\n    #loading_indicator {\n      display: none;\n      text-align: center;\n      margin: 10px 0;\n      font-size: 14px;\n      color: #007BFF;\n    }\n    #loading_indicator.error {\n      color: #FF0000;\n    }\n  <\/style>\n  <script>\n    document.addEventListener(\"DOMContentLoaded\", () => {\n      document.getElementById(\"loadingPlacholder\").style.display = \"none\";\n      \n      \/\/ Declare xsrfToken in a scope accessible to generateAudio\n      let xsrfToken = null;\n      \n      jQuery.ajax({\n        url: 'https:\/\/www.junyiacademy.org\/',\n        method: 'GET',\n        async: false,\n        cache: false, \/\/ disable cache to prevent error with browser history-back\n      }).done(function () { \n        xsrfToken = \/fkey=([^;]*)\/.exec(document.cookie) ? \/fkey=([^;]*)\/.exec(document.cookie)[1] : null;\n      });\n\n      \/\/ Check User logged-Status\n      var isLogin;\n      jQuery.ajax({\n        url: 'https:\/\/www.junyiacademy.org\/api\/v2\/user\/tracking-identity',\n        method: 'GET',\n        async: false,\n        cache: false,\n        xhrFields: { withCredentials: true },\n        headers: { 'X-KA-FKey': xsrfToken },\n      }).done(function (data) {\n        data = data[\"data\"];\n        isLogin = data[\"isLoggedIn\"];\n      });\n      \n      if (isLogin) {\n        document.getElementById(\"userIsLogin\").style.display = \"block\";\n        document.getElementById(\"userIsNotLogin\").style.display = \"none\";\n      } else {\n        document.getElementById(\"userIsLogin\").style.display = \"none\";\n        document.getElementById(\"userIsNotLogin\").style.display = \"block\";\n      }\n    \n      const textArea = document.getElementById(\"input_msg\");\n      const toggleSwitch = document.getElementById(\"toggle_textarea_switch\");\n      const audioElement = document.getElementById(\"audio_output\");\n      const playButton = document.getElementById(\"play_button\");\n      const areaInputs = document.querySelectorAll(\"input[name='para_area']\");\n      const genderInputs = document.querySelectorAll(\"input[name='para_gender']\");\n      const loadingIndicator = document.getElementById(\"loading_indicator\");\n\n      const voices = {\n        \"\u7f8e_\u7537\": \"en-US-AndrewMultilingualNeural\",\n        \"\u7f8e_\u5973\": \"en-US-AvaMultilingualNeural\",\n        \"\u82f1_\u7537\": \"en-GB-RyanNeural\",\n        \"\u82f1_\u5973\": \"en-GB-LibbyNeural\",\n        \"\u6fb3_\u7537\": \"en-AU-WilliamNeural\",\n        \"\u6fb3_\u5973\": \"en-AU-NatashaNeural\",\n        \"\u52a0_\u7537\": \"en-CA-LiamNeural\",\n        \"\u52a0_\u5973\": \"en-CA-ClaraNeural\",\n      };\n\n      \/\/ Global cache object to store generated audio by text and voice combination\n      const ttsCache = {};\n\n      const showLoading = (message = \"\u6b63\u5728\u751f\u6210\u8a9e\u97f3...\", isError = false) => {\n        loadingIndicator.textContent = message;\n        loadingIndicator.style.display = \"block\";\n        loadingIndicator.classList.toggle(\"error\", isError);\n      };\n\n      const hideLoading = () => {\n        loadingIndicator.style.display = \"none\";\n      };\n\n      const updateTextAreaFromURL = () => {\n        const urlParams = new URLSearchParams(window.location.search);\n        const message = urlParams.get(\"message\");\n        if (message) {\n          textArea.value = decodeURIComponent(message);\n          textArea.style.height = \"auto\";\n          textArea.style.height = textArea.scrollHeight + \"px\";\n        }\n        textArea.readOnly = true; \/\/ Prevent direct editing\n      };\n\n      const generateAudio = async () => {\n        const selectedArea = document.querySelector(\"input[name='para_area']:checked\").value;\n        const selectedGender = document.querySelector(\"input[name='para_gender']:checked\").value;\n        const voiceKey = `${selectedArea}_${selectedGender}`;\n        const voiceName = voices[voiceKey];\n\n        if (!voiceName) {\n          console.error(\"Voice not found\");\n          return;\n        }\n\n        const currentText = textArea.value;\n        \/\/ Create a unique cache key from text and voice\n        const cacheKey = `${currentText}_${voiceName}`;\n\n        \/\/ If audio has been generated before for this combination, use cached version.\n        if (ttsCache[cacheKey]) {\n          console.log(\"Using cached audio.\");\n          audioElement.src = ttsCache[cacheKey];\n          audioElement.style.display = \"block\";\n          audioElement.load();\n          audioElement.play();\n          return;\n        }\n\n        if (!audioElement.paused) {\n          audioElement.pause();\n          audioElement.currentTime = 0;\n        }                \n\n        try {\n          showLoading();\n          audioElement.disabled = true;\n          const response = await fetch(\"\/api\/v2\/jutor\/tts\", {\n            method: \"POST\",\n            headers: {\n              \"Content-Type\": \"application\/json\",\n              \"X-KA-FKey\": xsrfToken \/\/ system-required, do not remove\n            },\n            body: JSON.stringify({\n              \"text\": currentText,\n              \"lang\": voiceName.substring(0, 5),\n              \"voice_name\": voiceName,\n            }),\n          });\n  \n          const data = await response.json();\n          hideLoading();\n  \n          if (data.data && data.data.audio_data) {\n            const audioSrc = \"data:audio\/wav;base64,\" + data.data.audio_data;\n            \/\/ Cache the generated audio data\n            ttsCache[cacheKey] = audioSrc;\n  \n            audioElement.src = audioSrc;\n            audioElement.style.display = \"block\";\n            audioElement.load();\n            audioElement.disabled = false;\n            audioElement.play();\n          } else {\n            showLoading(\"\u751f\u6210\u8a9e\u97f3\u5931\u6557\uff0c\u8acb\u7a0d\u5f8c\u518d\u8a66\uff01\", true);\n            audioElement.disabled = false;\n          }\n        } catch (error) {\n          console.error(\"Error in Azure TTS:\", error);\n          showLoading(\"\u751f\u6210\u8a9e\u97f3\u6642\u51fa\u932f\uff0c\u8acb\u7a0d\u5f8c\u518d\u8a66\uff01\", true);\n          audioElement.disabled = false;\n        }\n      };\n\n      const autoGenerateAudio = () => {\n        generateAudio();\n      };\n\n      playButton.addEventListener(\"click\", generateAudio);\n      areaInputs.forEach(input => input.addEventListener(\"change\", autoGenerateAudio));\n      genderInputs.forEach(input => input.addEventListener(\"change\", autoGenerateAudio));\n\n      toggleSwitch.addEventListener(\"change\", () => {\n        textArea.style.display = toggleSwitch.checked ? \"block\" : \"none\";\n        if (toggleSwitch.checked) {\n          textArea.style.height = \"auto\";\n          textArea.style.height = textArea.scrollHeight + \"px\";\n        }\n      });\n\n      updateTextAreaFromURL();\n    });\n  <\/script>\n<\/head>\n<body>\n  <div id=\"loadingPlacholder\">loading...<\/div>\n  <div id=\"userIsLogin\" class=\"container\">\n    <div class=\"inline-group\">\n      <div class=\"radio-group\">\n        <input type=\"radio\" id=\"area_us\" name=\"para_area\" value=\"\u7f8e\" checked>\n        <label for=\"area_us\">\u7f8e<\/label>\n        <input type=\"radio\" id=\"area_uk\" name=\"para_area\" value=\"\u82f1\">\n        <label for=\"area_uk\">\u82f1<\/label>\n        <input type=\"radio\" id=\"area_au\" name=\"para_area\" value=\"\u6fb3\">\n        <label for=\"area_au\">\u6fb3<\/label>\n        <input type=\"radio\" id=\"area_ca\" name=\"para_area\" value=\"\u52a0\">\n        <label for=\"area_ca\">\u52a0<\/label>\n      <\/div>\n\n      <div class=\"radio-group\">\n        <input type=\"radio\" id=\"gender_male\" name=\"para_gender\" value=\"\u7537\" checked>\n        <label for=\"gender_male\">\u7537<\/label>\n        <input type=\"radio\" id=\"gender_female\" name=\"para_gender\" value=\"\u5973\">\n        <label for=\"gender_female\">\u5973<\/label>\n      <\/div>\n    <\/div>\n\n    <div class=\"toggle-container\">\n      <button id=\"play_button\">\u751f\u6210 AI \u8a9e\u97f3<\/button>\n      <label class=\"toggle-switch\">\n        <input type=\"checkbox\" id=\"toggle_textarea_switch\">\n        <span class=\"slider\"><\/span>\n      <\/label>\n    <\/div>\n    <audio id=\"audio_output\" controls style=\"display: none;\"><\/audio>\n    <div id=\"loading_indicator\"><\/div>\n    <textarea id=\"input_msg\" placeholder=\"Text will appear here based on URL parameters\"><\/textarea>\n  <\/div>\n  <div id=\"userIsNotLogin\" class=\"container\">\n    \u8acb\u5148\u8a3b\u518a\/\u767b\u5165\u5747\u4e00\u5e33\u865f\uff0c\u4f86\u4f7f\u7528 AI \u8a9e\u97f3\u5408\u6210\u529f\u80fd\u3002\n  <\/div>\n<\/body>\n<\/html>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Text to Speech Demo loading&#8230; \u7f8e \u82f1 \u6fb3 \u52a0 \u7537 \u5973 \u751f\u6210 AI \u8a9e\u97f3 [&hellip;]<\/p>\n","protected":false},"author":15,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.6.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>SayIt - \u5747\u4e00\u6559\u80b2\u5e73\u53f0<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.junyiacademy.org\/event\/sayit\/\" \/>\n<meta property=\"og:locale\" content=\"zh_TW\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"SayIt - \u5747\u4e00\u6559\u80b2\u5e73\u53f0\" \/>\n<meta property=\"og:description\" content=\"Text to Speech Demo loading&#8230; \u7f8e \u82f1 \u6fb3 \u52a0 \u7537 \u5973 \u751f\u6210 AI \u8a9e\u97f3 [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.junyiacademy.org\/event\/sayit\/\" \/>\n<meta property=\"og:site_name\" content=\"\u5747\u4e00\u6559\u80b2\u5e73\u53f0\" \/>\n<meta property=\"article:modified_time\" content=\"2025-02-15T15:52:00+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"4 \u5206\u9418\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.junyiacademy.org\/event\/sayit\/\",\"url\":\"https:\/\/www.junyiacademy.org\/event\/sayit\/\",\"name\":\"SayIt - \u5747\u4e00\u6559\u80b2\u5e73\u53f0\",\"isPartOf\":{\"@id\":\"https:\/\/www.junyiacademy.org\/event\/#website\"},\"datePublished\":\"2025-01-24T02:51:35+00:00\",\"dateModified\":\"2025-02-15T15:52:00+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.junyiacademy.org\/event\/sayit\/#breadcrumb\"},\"inLanguage\":\"zh-TW\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.junyiacademy.org\/event\/sayit\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.junyiacademy.org\/event\/sayit\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"SayIt\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.junyiacademy.org\/event\/#website\",\"url\":\"https:\/\/www.junyiacademy.org\/event\/\",\"name\":\"\u5747\u4e00\u6559\u80b2\u5e73\u53f0\",\"description\":\"\u8b93\u6bcf\u4e00\u4f4d\u5b69\u5b50\u90fd\u6709\u6a5f\u6703\u6210\u70ba\u7d42\u8eab\u5b78\u7fd2\u8005\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.junyiacademy.org\/event\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"zh-TW\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"SayIt - \u5747\u4e00\u6559\u80b2\u5e73\u53f0","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.junyiacademy.org\/event\/sayit\/","og_locale":"zh_TW","og_type":"article","og_title":"SayIt - \u5747\u4e00\u6559\u80b2\u5e73\u53f0","og_description":"Text to Speech Demo loading&#8230; \u7f8e \u82f1 \u6fb3 \u52a0 \u7537 \u5973 \u751f\u6210 AI \u8a9e\u97f3 [&hellip;]","og_url":"https:\/\/www.junyiacademy.org\/event\/sayit\/","og_site_name":"\u5747\u4e00\u6559\u80b2\u5e73\u53f0","article_modified_time":"2025-02-15T15:52:00+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"4 \u5206\u9418"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.junyiacademy.org\/event\/sayit\/","url":"https:\/\/www.junyiacademy.org\/event\/sayit\/","name":"SayIt - \u5747\u4e00\u6559\u80b2\u5e73\u53f0","isPartOf":{"@id":"https:\/\/www.junyiacademy.org\/event\/#website"},"datePublished":"2025-01-24T02:51:35+00:00","dateModified":"2025-02-15T15:52:00+00:00","breadcrumb":{"@id":"https:\/\/www.junyiacademy.org\/event\/sayit\/#breadcrumb"},"inLanguage":"zh-TW","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.junyiacademy.org\/event\/sayit\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.junyiacademy.org\/event\/sayit\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/"},{"@type":"ListItem","position":2,"name":"SayIt"}]},{"@type":"WebSite","@id":"https:\/\/www.junyiacademy.org\/event\/#website","url":"https:\/\/www.junyiacademy.org\/event\/","name":"\u5747\u4e00\u6559\u80b2\u5e73\u53f0","description":"\u8b93\u6bcf\u4e00\u4f4d\u5b69\u5b50\u90fd\u6709\u6a5f\u6703\u6210\u70ba\u7d42\u8eab\u5b78\u7fd2\u8005","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.junyiacademy.org\/event\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"zh-TW"}]}},"_links":{"self":[{"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/pages\/42650"}],"collection":[{"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/users\/15"}],"replies":[{"embeddable":true,"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/comments?post=42650"}],"version-history":[{"count":84,"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/pages\/42650\/revisions"}],"predecessor-version":[{"id":42771,"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/pages\/42650\/revisions\/42771"}],"wp:attachment":[{"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/media?parent=42650"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}