diff --git a/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/Pages/Index.razor b/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/Pages/Index.razor index 8d0daa6..6fcf1fe 100644 --- a/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/Pages/Index.razor +++ b/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/Pages/Index.razor @@ -214,12 +214,20 @@ return; } - await JSRuntime.InvokeVoidAsync( - "synthesizeSpeech", - spitConfiguration.SpeechApiKey, - spitConfiguration.Region, - spitConfiguration.SpeechEndpoint, - _textToSpeech.Ssml); + try + { + await JSRuntime.InvokeVoidAsync( + "synthesizeSpeech", + spitConfiguration.SpeechApiKey, + spitConfiguration.Region, + spitConfiguration.SpeechEndpoint, + _textToSpeech.Ssml); + } + catch (JSException ex) + { + Log.Error(ex, "Play sound failed."); + toastService.ShowError($"Unable to play sound. {ex.Message}"); + } } public void OpenDirectory(string fileDestination) diff --git a/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/wwwroot/js/SpeechFunctions.js b/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/wwwroot/js/SpeechFunctions.js index 388f615..f5aee3e 100644 --- a/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/wwwroot/js/SpeechFunctions.js +++ b/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/LCU.Webapps.Marketing.SPIT/wwwroot/js/SpeechFunctions.js @@ -1,26 +1,116 @@ function synthesizeSpeech(subscriptionKey, region, endpoint, ssml) { - var SpeechSDK; - if (!!window.SpeechSDK) { - SpeechSDK = window.SpeechSDK; + return new Promise(function (resolve, reject) { + const SpeechSDK = window.SpeechSDK; + + if (!SpeechSDK) { + reject(new Error("Speech SDK is not loaded.")); + return; + } + + let speechConfig; + + try { + speechConfig = createSpeechConfig(SpeechSDK, subscriptionKey, region, endpoint); + } catch (error) { + reject(error); + return; + } + + const audioConfig = SpeechSDK.AudioConfig.fromDefaultSpeakerOutput(); + let synthesizer = new SpeechSDK.SpeechSynthesizer(speechConfig, audioConfig); + + synthesizer.speakSsmlAsync( + ssml, + function (result) { + try { + if (result.reason !== SpeechSDK.ResultReason.SynthesizingAudioCompleted) { + reject(new Error(buildSynthesisErrorMessage(SpeechSDK, result, region, endpoint))); + return; + } + + resolve(); + } finally { + synthesizer.close(); + synthesizer = undefined; + } + }, + function (error) { + try { + reject(new Error(normalizeSpeechError(error, region, endpoint))); + } finally { + synthesizer.close(); + synthesizer = undefined; + } + }); + }); +} + +function createSpeechConfig(SpeechSDK, subscriptionKey, region, endpoint) { + const trimmedRegion = typeof region === "string" ? region.trim() : ""; + const trimmedEndpoint = typeof endpoint === "string" ? endpoint.trim() : ""; + + if (trimmedRegion) { + return SpeechSDK.SpeechConfig.fromSubscription(subscriptionKey, trimmedRegion); } - const speechConfig = endpoint - ? SpeechSDK.SpeechConfig.fromEndpoint(new URL(endpoint), subscriptionKey) - : SpeechSDK.SpeechConfig.fromSubscription(subscriptionKey, region); - const audioConfig = SpeechSDK.AudioConfig.fromDefaultSpeakerOutput(); - let synthesizer = new SpeechSDK.SpeechSynthesizer(speechConfig, audioConfig); + if (trimmedEndpoint) { + return SpeechSDK.SpeechConfig.fromEndpoint(new URL(trimmedEndpoint), subscriptionKey); + } - synthesizer.speakSsmlAsync( - ssml, - function (result) { - window.console.log(result); - synthesizer.close(); - synthesizer = undefined; - }, - function (err) { - window.console.log(err); - - synthesizer.close(); - synthesizer = undefined; - }); + throw new Error("Speech configuration is missing both region and endpoint."); +} + +function buildSynthesisErrorMessage(SpeechSDK, result, region, endpoint) { + const cancellation = SpeechSDK.CancellationDetails.fromResult(result); + const parts = [ + "Speech synthesis failed.", + "Reason: " + result.reason + ]; + + if (cancellation) { + if (cancellation.reason !== undefined) { + parts.push("CancellationReason: " + cancellation.reason); + } + + if (cancellation.errorCode !== undefined) { + parts.push("CancellationErrorCode: " + cancellation.errorCode); + } + + if (cancellation.errorDetails) { + parts.push("ErrorDetails: " + cancellation.errorDetails); + } + } + + appendConnectionDetails(parts, region, endpoint); + return parts.join(" "); +} + +function normalizeSpeechError(error, region, endpoint) { + const parts = ["Speech synthesis connection failed."]; + + if (error instanceof Error && error.message) { + parts.push(error.message); + } else if (typeof error === "string" && error.trim()) { + parts.push(error.trim()); + } else if (error && error.toString) { + parts.push(error.toString()); + } else { + parts.push("No error details were provided."); + } + + appendConnectionDetails(parts, region, endpoint); + return parts.join(" "); +} + +function appendConnectionDetails(parts, region, endpoint) { + const trimmedRegion = typeof region === "string" ? region.trim() : ""; + const trimmedEndpoint = typeof endpoint === "string" ? endpoint.trim() : ""; + + if (trimmedRegion) { + parts.push("Region: " + trimmedRegion); + } + + if (trimmedEndpoint) { + parts.push("Endpoint: " + trimmedEndpoint); + } }