Skip to content

Instantly share code, notes, and snippets.

@ecsplendid
Created December 13, 2022 18:06
Show Gist options
  • Save ecsplendid/8c1b70cdc55d1e1b7bb573f1eacfe7bf to your computer and use it in GitHub Desktop.
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 :)
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