Created
December 13, 2022 18:06
-
-
Save ecsplendid/8c1b70cdc55d1e1b7bb573f1eacfe7bf to your computer and use it in GitHub Desktop.
To save you the hours of pain and misery, here is some C# code which you can use to call the AWS Polly service, without having to use AWS's bloat-central SDK. You are welcome :)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Net.Http; | |
using System.Security.Cryptography; | |
using System.Text; | |
using System.Threading.Tasks; | |
using Newtonsoft.Json; | |
public class PollyService | |
{ | |
private const string AccessKey = "<key>"; | |
private const string SecretKey = "<key>"; | |
private const string Method = "POST"; | |
private const string Service = "polly"; | |
private const string Api = "/v1/speech"; | |
private const string ContentType = "application/json"; | |
public static async Task<Stream> TextToSpeech(string textToSpeech, string voice="Brian", string region = "eu-west-1") | |
{ | |
var host = Service + "." + region + ".amazonaws.com"; | |
var baseUrl = "https://" + host; | |
var requestParameters = new RequestParameters | |
{ | |
OutputFormat = "pcm", | |
SampleRate = "16000", | |
Text = textToSpeech, | |
TextType = "text", | |
VoiceId = voice | |
}; | |
var t = DateTime.UtcNow; | |
var amzDate = t.ToString("yyyyMMddTHHmmssZ"); | |
var dateStamp = t.ToString("yyyyMMdd"); | |
var canonicalQueryString = ""; | |
var canonicalHeaders = $"content-type:{ContentType}\nhost:{host}\nx-amz-date:{amzDate}\n"; | |
var signedHeaders = "content-type;host;x-amz-date"; | |
var payload = JsonConvert.SerializeObject(requestParameters); | |
var requestParametersBytes = Encoding.UTF8.GetBytes(payload); | |
using var digest = SHA256.Create(); | |
var payloadHash = HashToHexString(digest.ComputeHash(requestParametersBytes)); | |
var canonicalRequest = | |
$"{Method}\n{Api}\n{canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\n{payloadHash}"; | |
var algorithm = "AWS4-HMAC-SHA256"; | |
var credentialScope = $"{dateStamp}/{region}/{Service}/aws4_request"; | |
var canonicalRequestBytes = Encoding.UTF8.GetBytes(canonicalRequest); | |
var stringToSign = | |
$"{algorithm}\n{amzDate}\n{credentialScope}\n{HashToHexString(digest.ComputeHash(canonicalRequestBytes))}"; | |
var key = GetSignatureKey(SecretKey, dateStamp, region, Service); | |
var signature = HashToHexString(HmacSHA256(stringToSign, key)); | |
var authorizationHeader = | |
$"{algorithm} Credential={AccessKey}/{credentialScope},SignedHeaders={signedHeaders},Signature={signature}"; | |
var client = new HttpClient(); | |
var content = new StringContent(payload, Encoding.UTF8, "application/json"); | |
client.BaseAddress = new Uri(baseUrl, UriKind.Absolute); | |
var request = new HttpRequestMessage(HttpMethod.Post, new Uri(Api, UriKind.Relative)); | |
request.Content = content; | |
request.Headers.Host = host; | |
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", authorizationHeader); | |
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", ContentType); | |
client.DefaultRequestHeaders.TryAddWithoutValidation("X-Amz-Date", amzDate); | |
content.Headers.ContentType.CharSet = string.Empty; | |
var response = await client.SendAsync(request); | |
return await response.Content.ReadAsStreamAsync(); | |
} | |
private static string HashToHexString(byte[] bytes) | |
{ | |
var hex = new StringBuilder(bytes.Length * 2); | |
foreach (var b in bytes) | |
{ | |
hex.AppendFormat("{0:x2}", b); | |
} | |
return hex.ToString(); | |
} | |
private static byte[] GetSignatureKey(string secretKey, string dateStamp, | |
string regionName, string serviceName) | |
{ | |
var kSecret = Encoding.UTF8.GetBytes(("AWS4" + secretKey).ToCharArray()); | |
var kDate = HmacSHA256(dateStamp, kSecret); | |
var kRegion = HmacSHA256(regionName, kDate); | |
var kService = HmacSHA256(serviceName, kRegion); | |
var kSigning = HmacSHA256("aws4_request", kService); | |
return kSigning; | |
} | |
private static byte[] HmacSHA256(string data, byte[] key) | |
{ | |
const string algorithm = "HmacSHA256"; | |
var kha = KeyedHashAlgorithm.Create(algorithm); | |
kha.Key = key; | |
return kha.ComputeHash(Encoding.UTF8.GetBytes(data)); | |
} | |
} | |
public class RequestParameters | |
{ | |
public string OutputFormat { get; set; } | |
public string Text { get; set; } | |
public string TextType { get; set; } | |
public string VoiceId { get; set; } | |
public string SampleRate { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment