{"id":16525,"date":"2025-07-04T16:21:50","date_gmt":"2025-07-04T14:21:50","guid":{"rendered":"https:\/\/www.beseit.net\/?p=16525"},"modified":"2025-07-04T17:11:33","modified_gmt":"2025-07-04T15:11:33","slug":"projecte-veus-amb-copilot","status":"publish","type":"post","link":"https:\/\/www.beseit.net\/?p=16525","title":{"rendered":"Projecte Veus amb Copilot"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">? Estructura del projecte<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\\n\u251c\u2500\u2500 tts\\\n\u2502   \u251c\u2500\u2500 tts-to-wav.ps1       \u2190 Script PowerShell per generar l\u2019\u00e0udio\n\u2502   \u2514\u2500\u2500 list-voices.ps1      \u2190 Script PowerShell per llistar veus\n\u2514\u2500\u2500 AppServ\\\n    \u2514\u2500\u2500 www\\\n        \u2514\u2500\u2500 tts\\\n            \u251c\u2500\u2500 index.php    \u2190 Interf\u00edcie web\n            \u2514\u2500\u2500 tts.wav      \u2190 Fitxer d\u2019\u00e0udio generat\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">? Qu\u00e8 \u00e9s un fitxer <code>.ps1<\/code>?<\/h2>\n\n\n\n<p>Un fitxer <code>.ps1<\/code> \u00e9s un script de <strong>PowerShell<\/strong>, el llenguatge de l\u00ednia de comandes i automatitzaci\u00f3 de Windows. S\u2019utilitza per executar ordres, scripts i automatitzar tasques del sistema.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">? Documentaci\u00f3 dels components<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\">1. <code>list-voices.ps1<\/code> \u2014 Llista de veus SAPI5<\/h2>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<pre class=\"wp-block-code\"><code>Add-Type -AssemblyName System.Speech\n$synth = New-Object System.Speech.Synthesis.SpeechSynthesizer\n\n$synth.GetInstalledVoices() | ForEach-Object {\n    $info = $_.VoiceInfo\n    $label = \"SAPI5|$($info.Name)\"\n    Write-Output $label\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>? Funci\u00f3: Detecta totes les veus SAPI5 instal\u00b7lades i les imprimeix amb el prefix <code>SAPI5|<\/code>.<\/strong><\/h3>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2. <code>tts-to-wav.ps1<\/code> \u2014 Generaci\u00f3 d\u2019\u00e0udio<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>param (\n    &#91;string]$text = \"Hola m\u00f3n\",\n    &#91;string]$voiceName,\n    &#91;string]$outputPath = \"C:\\tts\\tts.wav\"\n)\n\ntry {\n    if ($voiceName -like \"SAPI5|*\") {\n        $realVoice = $voiceName.Substring(6)\n        Add-Type -AssemblyName System.Speech\n        $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer\n        $synth.SelectVoice($realVoice)\n        $synth.SetOutputToWaveFile($outputPath)\n        $synth.Speak($text)\n    } else {\n        Write-Error \"Format de veu desconegut: $voiceName\"\n        exit 1\n    }\n}\ncatch {\n    Write-Error \"Error inesperat: $_\"\n    exit 1\n}\n<\/code><\/pre>\n\n\n\n<p>? <strong>Funci\u00f3<\/strong>: Rep un text i una veu, i genera un fitxer <code>.wav<\/code> amb la veu seleccionada.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. <code>index.php<\/code> \u2014 Interf\u00edcie web<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\/\/ Obtenim la llista de veus\n$voiceListCmd = \"C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe -ExecutionPolicy Bypass -File C:\\\\tts\\\\list-voices.ps1\";\nexec($voiceListCmd, $voices, $voiceStatus);\n\n\/\/ Depuraci\u00f3: mostra les veus detectades\necho \"&lt;pre>&lt;strong>Veus detectades:&lt;\/strong>\\n\";\nprint_r($voices);\necho \"&lt;\/pre>\";\n\n\/\/ Si falla, usem una llista per defecte\nif ($voiceStatus !== 0 || empty($voices)) {\n    $voices = &#91;\n        \"SAPI5|Microsoft Helena Desktop\",\n        \"SAPI5|Microsoft Hazel Desktop\",\n        \"SAPI5|Microsoft Zira Desktop\"\n    ];\n}\n?>\n\n&lt;!DOCTYPE html>\n&lt;html lang=\"ca\">\n&lt;head>\n    &lt;meta charset=\"UTF-8\">\n    &lt;title>Conversi\u00f3 TTS&lt;\/title>\n&lt;\/head>\n&lt;body>\n    &lt;h2>? Conversi\u00f3 de text a veu (SAPI5)&lt;\/h2>\n    \n  \n    &lt;form method=\"get\">\n        &lt;label for=\"text\">Text a llegir:&lt;\/label>&lt;br>\n        &lt;textarea name=\"text\" rows=\"4\" cols=\"60\">&lt;?= htmlspecialchars($_GET&#91;'text'] ?? '') ?>&lt;\/textarea>&lt;br>&lt;br>\n\n        &lt;label for=\"voice\">Selecciona una veu:&lt;\/label>&lt;br>\n        &lt;select name=\"voice\">\n            &lt;?php foreach ($voices as $v): ?>\n                &lt;option value=\"&lt;?= htmlspecialchars($v) ?>\" &lt;?= ($v === ($_GET&#91;'voice'] ?? '')) ? 'selected' : '' ?>>\n                    &lt;?= htmlspecialchars($v) ?>\n                &lt;\/option>\n            &lt;?php endforeach; ?>\n        &lt;\/select>&lt;br>&lt;br>\n\n        &lt;button type=\"submit\">\u25b6\ufe0f Generar \u00e0udio&lt;\/button>\n    &lt;\/form>\n\n    &lt;?php\n    $text = $_GET&#91;'text'] ?? '';\n    $voice = $_GET&#91;'voice'] ?? '';\n    $audioFile = '\/tts\/tts.wav';\n\n    if ($text &amp;&amp; $voice) {\n        $escapedText = escapeshellarg($text);\n        $escapedVoice = escapeshellarg($voice);\n        $escapedOutput = escapeshellarg(__DIR__ . DIRECTORY_SEPARATOR . 'tts.wav');\n\n        $cmd = \"C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe -ExecutionPolicy Bypass -File C:\\\\tts\\\\tts-to-wav.ps1 -text $escapedText -voiceName $escapedVoice -outputPath $escapedOutput\";\n\n        \/\/ ? DEBUG: Mostra la comanda que s'executar\u00e0\n        echo \"&lt;pre>&lt;strong>Comanda PowerShell:&lt;\/strong>\\n$cmd\\n&lt;\/pre>\";\n\n        exec($cmd, $outputLines, $status);\n\n        if ($status === 0 &amp;&amp; file_exists(__DIR__ . '\/tts.wav')) {\n            echo \"&lt;h3>\u2705 \u00c0udio generat:&lt;\/h3>\";\n            echo \"&lt;audio controls autoplay style='margin-top:10px;'>\n                    &lt;source src=\\\"$audioFile\\\" type=\\\"audio\/wav\\\">\n                    El teu navegador no suporta \u00e0udio.\n                  &lt;\/audio>\";\n        } else {\n            echo \"&lt;p style='color:red;'>\u274c Error generant l\u2019\u00e0udio.&lt;\/p>\";\n            echo \"&lt;pre>&lt;strong>Sortida PowerShell:&lt;\/strong>\\n\";\n            print_r($outputLines);\n            echo \"\\nCodi de sortida: $status&lt;\/pre>\";\n        }\n    }\n    ?>\n&lt;\/body>\n&lt;\/html>\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>? Estructura del projecte ? Qu\u00e8 \u00e9s un fitxer .ps1? Un fitxer .ps1 \u00e9s un script de PowerShell, el llenguatge de l\u00ednia de comandes i automatitzaci\u00f3 de Windows. S\u2019utilitza per executar ordres, scripts i automatitzar tasques del sistema. ? Documentaci\u00f3 &hellip; <a href=\"https:\/\/www.beseit.net\/?p=16525\">Continua llegint <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":8179,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[185],"tags":[],"class_list":["post-16525","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-veus"],"_links":{"self":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts\/16525","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=16525"}],"version-history":[{"count":9,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts\/16525\/revisions"}],"predecessor-version":[{"id":16540,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts\/16525\/revisions\/16540"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/media\/8179"}],"wp:attachment":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=16525"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=16525"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=16525"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}