Skip to content

Commit 07d124a

Browse files
feat: adding basic TTS
This adds the basic TTS feature without many bells or whistles. You can configure the output device, the voice, and the volume through the settings menu. closes #17
1 parent c27f0cb commit 07d124a

39 files changed

+1832
-124
lines changed

src/TwitchStreamingTools/.editorconfig

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ end_of_line = lf
2525
insert_final_newline = false
2626

2727
#### .NET Coding Conventions ####
28-
[*.{cs,vb}]
2928

3029
# Organize usings
3130
dotnet_separate_import_directive_groups = true
@@ -78,7 +77,6 @@ dotnet_code_quality_unused_parameters = all:suggestion
7877
dotnet_remove_unnecessary_suppression_exclusions = none
7978

8079
#### C# Coding Conventions ####
81-
[*.cs]
8280

8381
# var preferences
8482
csharp_style_var_elsewhere = false:silent
@@ -175,7 +173,6 @@ csharp_preserve_single_line_blocks = true
175173
csharp_preserve_single_line_statements = true
176174

177175
#### Naming styles ####
178-
[*.{cs,vb}]
179176

180177
# Naming rules
181178

@@ -229,19 +226,19 @@ dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelca
229226

230227
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
231228
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
232-
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
229+
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = all_upper
233230

234231
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
235232
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
236-
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
233+
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = all_upper
237234

238235
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
239236
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
240-
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
237+
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = all_upper
241238

242239
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
243240
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
244-
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
241+
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = all_upper
245242

246243
dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
247244
dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
@@ -365,3 +362,7 @@ dotnet_naming_style.s_camelcase.required_suffix =
365362
dotnet_naming_style.s_camelcase.word_separator =
366363
dotnet_naming_style.s_camelcase.capitalization = camel_case
367364

365+
dotnet_naming_style.all_upper.required_prefix =
366+
dotnet_naming_style.all_upper.required_suffix =
367+
dotnet_naming_style.all_upper.word_separator = _
368+
dotnet_naming_style.all_upper.capitalization = all_upper

src/TwitchStreamingTools/App.axaml.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public override void OnFrameworkInitializationCompleted() {
4040

4141
// Creates a ServiceProvider containing services from the provided IServiceCollection
4242
ServiceProvider services = collection.BuildServiceProvider();
43+
services.StartupServices();
4344
var vm = services.GetRequiredService<MainWindowViewModel>();
4445
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
4546
desktop.MainWindow = new MainWindow {

src/TwitchStreamingTools/Assets/icons.axaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<StreamGeometry x:Key="InprivateAccountRegular">M21 16C22.3062 16 23.4175 16.8348 23.8293 18H14V17.5H7C6.2203 17.5 5.57955 18.0949 5.50687 18.8555L5.5 19V19.7146C5.5 22.389 8.88879 24.5 14 24.5V24H21.4712C19.6819 25.2427 17.0617 26 14 26C8.21053 26 4 23.4333 4 19.7146V19C4 17.3431 5.34315 16 7 16H21Z M24 19V19.7146C24 19.9815 23.9766 20.2436 23.9308 20.5H14V19H24Z M23.6286 21.5H14V23H22.6339C23.0614 22.5387 23.397 22.0359 23.6286 21.5Z M19.9536 7.25C19.9842 7.4957 20 7.746 20 8C20 8.254 19.9842 8.5043 19.9536 8.75H14V7.25H19.9536Z M19.7408 6.25C19.5777 5.71438 19.3417 5.21053 19.0444 4.75H14V6.25H19.7408Z M14 2C15.6531 2 17.1501 2.66854 18.2353 3.75H14V3.5C11.5147 3.5 9.5 5.51472 9.5 8C9.5 10.4853 11.5147 12.5 14 12.5V12.25H18.2353C17.1501 13.3315 15.6531 14 14 14C10.6863 14 8 11.3137 8 8C8 4.68629 10.6863 2 14 2Z M19.0444 11.25H14V9.75H19.7408C19.5777 10.2856 19.3417 10.7895 19.0444 11.25Z</StreamGeometry>
77
<StreamGeometry x:Key="LineHorizontal3Regular">M2 4.5C2 4.22386 2.22386 4 2.5 4H17.5C17.7761 4 18 4.22386 18 4.5C18 4.77614 17.7761 5 17.5 5H2.5C2.22386 5 2 4.77614 2 4.5Z M2 9.5C2 9.22386 2.22386 9 2.5 9H17.5C17.7761 9 18 9.22386 18 9.5C18 9.77614 17.7761 10 17.5 10H2.5C2.22386 10 2 9.77614 2 9.5Z M2.5 14C2.22386 14 2 14.2239 2 14.5C2 14.7761 2.22386 15 2.5 15H17.5C17.7761 15 18 14.7761 18 14.5C18 14.2239 17.7761 14 17.5 14H2.5Z</StreamGeometry>
88
<StreamGeometry x:Key="ChatRegular">M14.0038862,2.5 C20.3551608,2.5 25.5038862,7.64872538 25.5038862,14 C25.5038862,20.3512746 20.3551608,25.5 14.0038862,25.5 C12.0828262,25.5 10.2289934,25.0277557 8.57453454,24.1400553 L4.06949813,25.4927799 C3.40830889,25.6913248 2.71135661,25.3162775 2.51281166,24.6550883 C2.44236748,24.4204969 2.44239769,24.1703786 2.5128929,23.935823 L3.86597493,19.4333464 C2.97690068,17.777898 2.50388618,15.9226229 2.50388618,14 C2.50388618,7.64872538 7.65261155,2.5 14.0038862,2.5 Z M14.0038862,4 C8.48103868,4 4.00388618,8.4771525 4.00388618,14 C4.00388618,15.7703119 4.46384891,17.4718347 5.32571954,18.9725127 L5.48656853,19.2525809 L4.08316234,23.9225148 L8.75512584,22.5196672 L9.03501121,22.6802549 C10.5348218,23.5407899 12.2350195,24 14.0038862,24 C19.5267337,24 24.0038862,19.5228475 24.0038862,14 C24.0038862,8.4771525 19.5267337,4 14.0038862,4 Z M10.2538862,15.5 L14.7521489,15.5 C15.1663625,15.5 15.5021489,15.8357864 15.5021489,16.25 C15.5021489,16.6296958 15.219995,16.943491 14.8539195,16.9931534 L14.7521489,17 L10.2538862,17 C9.83967261,17 9.50388618,16.6642136 9.50388618,16.25 C9.50388618,15.8703042 9.78604006,15.556509 10.1521156,15.5068466 L10.2538862,15.5 L14.7521489,15.5 L10.2538862,15.5 Z M10.2538862,11 L17.7583662,11 C18.1725798,11 18.5083662,11.3357864 18.5083662,11.75 C18.5083662,12.1296958 18.2262123,12.443491 17.8601368,12.4931534 L17.7583662,12.5 L10.2538862,12.5 C9.83967261,12.5 9.50388618,12.1642136 9.50388618,11.75 C9.50388618,11.3703042 9.78604006,11.056509 10.1521156,11.0068466 L10.2538862,11 L17.7583662,11 L10.2538862,11 Z</StreamGeometry>
9+
<StreamGeometry x:Key="SettingsRegular">M14 9.50006C11.5147 9.50006 9.5 11.5148 9.5 14.0001C9.5 16.4853 11.5147 18.5001 14 18.5001C15.3488 18.5001 16.559 17.9066 17.3838 16.9666C18.0787 16.1746 18.5 15.1365 18.5 14.0001C18.5 13.5401 18.431 13.0963 18.3028 12.6784C17.7382 10.8381 16.0253 9.50006 14 9.50006ZM11 14.0001C11 12.3432 12.3431 11.0001 14 11.0001C15.6569 11.0001 17 12.3432 17 14.0001C17 15.6569 15.6569 17.0001 14 17.0001C12.3431 17.0001 11 15.6569 11 14.0001Z M21.7093 22.3948L19.9818 21.6364C19.4876 21.4197 18.9071 21.4515 18.44 21.7219C17.9729 21.9924 17.675 22.4693 17.6157 23.0066L17.408 24.8855C17.3651 25.273 17.084 25.5917 16.7055 25.682C14.9263 26.1061 13.0725 26.1061 11.2933 25.682C10.9148 25.5917 10.6336 25.273 10.5908 24.8855L10.3834 23.0093C10.3225 22.4731 10.0112 21.9976 9.54452 21.7281C9.07783 21.4586 8.51117 21.4269 8.01859 21.6424L6.29071 22.4009C5.93281 22.558 5.51493 22.4718 5.24806 22.1859C4.00474 20.8536 3.07924 19.2561 2.54122 17.5137C2.42533 17.1384 2.55922 16.7307 2.8749 16.4977L4.40219 15.3703C4.83721 15.0501 5.09414 14.5415 5.09414 14.0007C5.09414 13.4598 4.83721 12.9512 4.40162 12.6306L2.87529 11.5051C2.55914 11.272 2.42513 10.8638 2.54142 10.4882C3.08038 8.74734 4.00637 7.15163 5.24971 5.82114C5.51684 5.53528 5.93492 5.44941 6.29276 5.60691L8.01296 6.36404C8.50793 6.58168 9.07696 6.54881 9.54617 6.27415C10.0133 6.00264 10.3244 5.52527 10.3844 4.98794L10.5933 3.11017C10.637 2.71803 10.9245 2.39704 11.3089 2.31138C12.19 2.11504 13.0891 2.01071 14.0131 2.00006C14.9147 2.01047 15.8128 2.11485 16.6928 2.31149C17.077 2.39734 17.3643 2.71823 17.4079 3.11017L17.617 4.98937C17.7116 5.85221 18.4387 6.50572 19.3055 6.50663C19.5385 6.507 19.769 6.45838 19.9843 6.36294L21.7048 5.60568C22.0626 5.44818 22.4807 5.53405 22.7478 5.81991C23.9912 7.1504 24.9172 8.74611 25.4561 10.487C25.5723 10.8623 25.4386 11.2703 25.1228 11.5035L23.5978 12.6297C23.1628 12.95 22.9 13.4586 22.9 13.9994C22.9 14.5403 23.1628 15.0489 23.5988 15.3698L25.1251 16.4965C25.441 16.7296 25.5748 17.1376 25.4586 17.5131C24.9198 19.2536 23.9944 20.8492 22.7517 22.1799C22.4849 22.4657 22.0671 22.5518 21.7093 22.3948ZM16.263 22.1966C16.4982 21.4685 16.9889 20.8288 17.6884 20.4238C18.5702 19.9132 19.6536 19.8547 20.5841 20.2627L21.9281 20.8526C22.791 19.8538 23.4593 18.7013 23.8981 17.4552L22.7095 16.5778L22.7086 16.5771C21.898 15.98 21.4 15.0277 21.4 13.9994C21.4 12.9719 21.8974 12.0195 22.7073 11.4227L22.7085 11.4218L23.8957 10.545C23.4567 9.2988 22.7881 8.14636 21.9248 7.1477L20.5922 7.73425L20.5899 7.73527C20.1844 7.91463 19.7472 8.00722 19.3039 8.00663C17.6715 8.00453 16.3046 6.77431 16.1261 5.15465L16.1259 5.15291L15.9635 3.69304C15.3202 3.57328 14.6677 3.50872 14.013 3.50017C13.3389 3.50891 12.6821 3.57367 12.0377 3.69328L11.8751 5.15452C11.7625 6.16272 11.1793 7.05909 10.3019 7.56986C9.41937 8.0856 8.34453 8.14844 7.40869 7.73694L6.07273 7.14893C5.20949 8.14751 4.54092 9.29983 4.10196 10.5459L5.29181 11.4233C6.11115 12.0269 6.59414 12.9837 6.59414 14.0007C6.59414 15.0173 6.11142 15.9742 5.29237 16.5776L4.10161 17.4566C4.54002 18.7044 5.2085 19.8585 6.07205 20.8587L7.41742 20.2682C8.34745 19.8613 9.41573 19.9215 10.2947 20.4292C11.174 20.937 11.7593 21.832 11.8738 22.84L11.8744 22.8445L12.0362 24.3088C13.3326 24.5638 14.6662 24.5638 15.9626 24.3088L16.1247 22.8418C16.1491 22.6217 16.1955 22.4055 16.263 22.1966Z</StreamGeometry>
910
</Style.Resources>
1011
</Style>
1112
</Styles>

src/TwitchStreamingTools/Constants.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,39 @@ public static class Constants {
1212
/// <summary>
1313
/// The twitch app client id.
1414
/// </summary>
15-
public static string TWITCH_CLIENT_ID = "cvipqhi9y6ri8yhv0w8ryxokxh0ebd";
15+
public const string TWITCH_CLIENT_ID = "cvipqhi9y6ri8yhv0w8ryxokxh0ebd";
1616

1717
/// <summary>
1818
/// The domain that the api service is hosted at.
1919
/// </summary>
20-
public static string API_SITE_DOMAIN = "http://localhost:5036";
20+
public const string API_SITE_DOMAIN = "http://localhost:5036";
2121
#else
2222
/// <summary>
2323
/// The twitch app client id.
2424
/// </summary>
25-
public static string TWITCH_CLIENT_ID = "gi1eu8xu9tl6vkjqz4tjqkdzfmcq5h";
25+
public const string TWITCH_CLIENT_ID = "gi1eu8xu9tl6vkjqz4tjqkdzfmcq5h";
2626

2727
/// <summary>
2828
/// The domain that the api service is hosted at.
2929
/// </summary>
30-
public static string API_SITE_DOMAIN = "https://nullinside.com";
30+
public const string API_SITE_DOMAIN = "https://nullinside.com";
3131
#endif
3232

33+
/// <summary>
34+
/// A regular expression for identifying a link.
35+
/// </summary>
36+
public const string REGEX_URL = @"(https?:\/\/(www\.)?)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=,]*)";
37+
3338
/// <summary>
3439
/// The twitch app redirect link.
3540
/// </summary>
36-
public static string TWITCH_CLIENT_REDIRECT = $"{API_SITE_DOMAIN}/api/v1/user/twitch-login/twitch-streaming-tools";
41+
public const string TWITCH_CLIENT_REDIRECT = $"{API_SITE_DOMAIN}/api/v1/user/twitch-login/twitch-streaming-tools";
3742

3843
/// <summary>
3944
/// The reference to the clipboard API.
4045
/// </summary>
4146
/// <remarks>This is a hack because it's hard to get to.</remarks>
42-
public static IClipboard? CLIPBOARD;
47+
public static IClipboard? Clipboard;
4348

4449
/// <summary>
4550
/// The twitch permissions to request.

src/TwitchStreamingTools/Models/Configuration.cs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace TwitchStreamingTools.Models;
1212
/// The configuration of the application.
1313
/// </summary>
1414
public class Configuration {
15-
private static readonly string s_configLocation =
15+
private static readonly string CONFIG_LOCATION =
1616
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "nullinside",
1717
"twitch-streaming-tools", "config.json");
1818

@@ -49,15 +49,25 @@ public static Configuration Instance {
4949
/// <summary>
5050
/// The collection of twitch chats we should read from.
5151
/// </summary>
52-
public IEnumerable<string>? TwitchChats { get; set; }
52+
public IEnumerable<TwitchChatConfiguration>? TwitchChats { get; set; }
53+
54+
/// <summary>
55+
/// The collection of usernames to skip reading the messages of.
56+
/// </summary>
57+
public IEnumerable<string>? TtsUsernamesToSkip { get; set; }
58+
59+
/// <summary>
60+
/// The collection of phonetic pronunciations of words.
61+
/// </summary>
62+
public IDictionary<string, string>? TtsPhoneticUsernames { get; set; }
5363

5464
/// <summary>
5565
/// Reads the configuration from disk.
5666
/// </summary>
5767
/// <returns>The configuration if successful, null otherwise.</returns>
5868
private static Configuration? ReadConfiguration() {
5969
try {
60-
string json = File.ReadAllText(s_configLocation);
70+
string json = File.ReadAllText(CONFIG_LOCATION);
6171
return JsonConvert.DeserializeObject<Configuration>(json);
6272
}
6373
catch { return null; }
@@ -69,14 +79,44 @@ public static Configuration Instance {
6979
/// <returns>True if successful, false otherwise.</returns>
7080
public bool WriteConfiguration() {
7181
try {
72-
Directory.CreateDirectory(Path.GetDirectoryName(s_configLocation)!);
82+
Directory.CreateDirectory(Path.GetDirectoryName(CONFIG_LOCATION)!);
7383

7484
string json = JsonConvert.SerializeObject(this);
75-
File.WriteAllText(s_configLocation, json);
85+
File.WriteAllText(CONFIG_LOCATION, json);
7686
return true;
7787
}
7888
catch {
7989
return false;
8090
}
8191
}
92+
93+
/// <summary>
94+
/// Represents a single connection to a twitch chat by a single user.
95+
/// </summary>
96+
public class TwitchChatConfiguration {
97+
/// <summary>
98+
/// Gets or sets the output device to send audio to.
99+
/// </summary>
100+
public string? OutputDevice { get; set; }
101+
102+
/// <summary>
103+
/// Gets or sets a value indicating whether text to speech is on.
104+
/// </summary>
105+
public bool TtsOn { get; set; }
106+
107+
/// <summary>
108+
/// Gets or sets the selected Microsoft Text to Speech voice.
109+
/// </summary>
110+
public string? TtsVoice { get; set; }
111+
112+
/// <summary>
113+
/// Gets or sets the volume of the text to speech voice.
114+
/// </summary>
115+
public uint TtsVolume { get; set; }
116+
117+
/// <summary>
118+
/// Gets or sets the twitch channel to read chat from.
119+
/// </summary>
120+
public string? TwitchChannel { get; set; }
121+
}
82122
}

src/TwitchStreamingTools/Models/OAuthResponse.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
using Newtonsoft.Json;
44

5+
namespace TwitchStreamingTools.Models;
6+
57
/// <summary>
68
/// A JSON representation of an OAuth token.
79
/// </summary>

src/TwitchStreamingTools/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
using System;
2+
using System.Runtime.Versioning;
23

34
using Avalonia;
45
using Avalonia.ReactiveUI;
56

7+
[assembly: SupportedOSPlatform("windows")]
8+
69
namespace TwitchStreamingTools;
710

811
internal sealed class Program {

src/TwitchStreamingTools/Services/IAccountManager.cs renamed to src/TwitchStreamingTools/Services/ITwitchAccountService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace TwitchStreamingTools.Services;
88
/// <summary>
99
/// The contract for management credentials in the application.
1010
/// </summary>
11-
public interface IAccountManager {
11+
public interface ITwitchAccountService {
1212
/// <summary>
1313
/// The current OAuth's twitch username.
1414
/// </summary>

src/TwitchStreamingTools/Services/ServiceCollectionExtensions.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using Microsoft.Extensions.DependencyInjection;
1+
using System;
2+
3+
using Microsoft.Extensions.DependencyInjection;
24

35
using Nullinside.Api.Common.Twitch;
46

@@ -16,12 +18,23 @@ public static class ServiceCollectionExtensions {
1618
/// </summary>
1719
/// <param name="collection">The services collection to initialize.</param>
1820
public static void AddCommonServices(this IServiceCollection collection) {
19-
collection.AddSingleton<IAccountManager, AccountManager>();
21+
collection.AddSingleton<ITwitchAccountService, TwitchAccountService>();
2022
collection.AddSingleton<ITwitchClientProxy, TwitchClientProxy>(_ => TwitchClientProxy.Instance);
23+
collection.AddSingleton<TwitchTtsService>();
2124

2225
collection.AddTransient<MainWindowViewModel>();
2326
collection.AddTransient<AccountViewModel>();
2427
collection.AddTransient<ChatViewModel>();
2528
collection.AddTransient<NewVersionWindowViewModel>();
29+
collection.AddTransient<SettingsViewModel>();
30+
}
31+
32+
/// <summary>
33+
/// Buffers the services for the top level services that live in the dependency injection framework.
34+
/// </summary>
35+
/// <param name="provider">The service provider.</param>
36+
public static void StartupServices(this IServiceProvider provider) {
37+
provider.GetRequiredService<ITwitchAccountService>();
38+
provider.GetRequiredService<TwitchTtsService>();
2639
}
2740
}

src/TwitchStreamingTools/Services/AccountManager.cs renamed to src/TwitchStreamingTools/Services/TwitchAccountService.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
namespace TwitchStreamingTools.Services;
1212

1313
/// <summary>
14-
/// Manages the credentials in the application.
14+
/// Manages the credentials in the application, ensuring credentials are kept up-to-date.
1515
/// </summary>
16-
public class AccountManager : IAccountManager {
16+
public class TwitchAccountService : ITwitchAccountService {
1717
/// <summary>
1818
/// The timer used to check the twitch OAuth token against the API.
1919
/// </summary>
@@ -25,10 +25,10 @@ public class AccountManager : IAccountManager {
2525
private readonly ITwitchClientProxy _twitchClient;
2626

2727
/// <summary>
28-
/// Initializes a new instance of the <see cref="AccountManager" /> class.
28+
/// Initializes a new instance of the <see cref="TwitchAccountService" /> class.
2929
/// </summary>
3030
/// <param name="twitchClient">The twitch chat client.</param>
31-
public AccountManager(ITwitchClientProxy twitchClient) {
31+
public TwitchAccountService(ITwitchClientProxy twitchClient) {
3232
_twitchClient = twitchClient;
3333
_timer = new DispatcherTimer {
3434
Interval = TimeSpan.FromSeconds(5)

0 commit comments

Comments
 (0)