using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json.Serialization; using LibVLCSharp.Shared; using TwitchLib.Client.Models; namespace TTSMe; public static class TTSClient { private static readonly HttpClient httpClient = new() { DefaultRequestHeaders = { Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("OPENAI_API_KEY")), }, BaseAddress = new Uri("https://api.openai.com") }; public const string AudioBasePath = @"C:\data\twitch\tts"; public static async Task GenerateSpeech(ChatMessage chatMessage) { Console.WriteLine($"Generating speech as {chatMessage.GetAudioFilePath()} for {chatMessage.Username}: {chatMessage.Message}"); HttpResponseMessage response = await httpClient.PostAsJsonAsync("v1/audio/speech", chatMessage.ToTTSRequest()); await File.WriteAllBytesAsync(chatMessage.GetAudioFilePath(), await response.Content.ReadAsByteArrayAsync()); } public static async Task PlaySpeech(string path) { try { await Task.Run(() => { TaskCompletionSource taskCompletionSource = new(); using LibVLC libVLC = new(); if (TtsMeCommand.VERBOSE) libVLC.Log += (_, e) => Console.WriteLine($"LibVLC [{e.Level}] {e.Module}:{e.Message}"); using Media media = new(libVLC, path); using MediaPlayer player = new(media); player.EndReached += OnEndReached; player.Play(); player.EndReached += (_, _) => taskCompletionSource.TrySetResult(true); taskCompletionSource.Task.Wait(); player.Stop(); }); } catch (Exception ex) { Console.WriteLine($"An error occurred during playback: {ex.Message}"); } } private static void OnEndReached(object? sender, EventArgs _) { if (sender is MediaPlayer mediaPlayer) mediaPlayer.Stop(); } } internal record TextToSpeechRequest( [property: JsonPropertyName("input")] string Input, [property: JsonPropertyName("voice")] string Voice, [property: JsonPropertyName("model")] string Model = "tts-1", [property: JsonPropertyName("response_format")] string ResponseFormat = "mp3", [property: JsonPropertyName("speed")] double Speed = 1.0 );