From 5e9355ce1b1b28c2ecf71040b9c4d1cbba4d8a72 Mon Sep 17 00:00:00 2001 From: Butterscotch! Date: Sun, 28 Jan 2024 05:42:56 -0500 Subject: [PATCH] Start basic message queueing --- ButterSTT/MessageSystem/MessageQueue.cs | 107 ++++++++++++++++++ ButterSTT/MessageSystem/MessageWord.cs | 14 +++ ButterSTT/{ => MessageSystem}/OSCHandler.cs | 2 +- ButterSTT/SpeechToTextHandler.cs | 6 + .../TextProcessing/TextParts/Paragraph.cs | 2 + .../TextProcessing/TextParts/Sentence.cs | 2 + 6 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 ButterSTT/MessageSystem/MessageQueue.cs create mode 100644 ButterSTT/MessageSystem/MessageWord.cs rename ButterSTT/{ => MessageSystem}/OSCHandler.cs (95%) diff --git a/ButterSTT/MessageSystem/MessageQueue.cs b/ButterSTT/MessageSystem/MessageQueue.cs new file mode 100644 index 0000000..4561a15 --- /dev/null +++ b/ButterSTT/MessageSystem/MessageQueue.cs @@ -0,0 +1,107 @@ +using ButterSTT.TextProcessing.TextParts; + +namespace ButterSTT.MessageSystem +{ + public class MessageQueue + { + public int MessageLength = 144; + public TimeSpan WordTime = TimeSpan.FromSeconds(3); + + public Paragraph CurParagraph; + private (int sentence, int word) CurIndex; + + private readonly Queue WordQueue = new(); + private readonly Queue MessageWordQueue = new(); + private int CurMessageLength; + + private void InternLimitWordIndex() + { + CurIndex.word = CurParagraph.Sentences[CurIndex.sentence].Words.Length - 1; + } + + public void LimitParagraphIndex() + { + if (CurParagraph.Sentences.Length <= CurIndex.sentence) + { + // Move to the end of the last known position + CurIndex.sentence = CurParagraph.Sentences.Length - 1; + InternLimitWordIndex(); + } + else if (CurParagraph.Sentences[CurIndex.sentence].Length <= CurIndex.word) + { + InternLimitWordIndex(); + } + } + + public void FinishCurrentParagraph() + { + // Limit the index if length has changed since last known + LimitParagraphIndex(); + + // Queue all words after the current displayed ones + for (var s = CurIndex.sentence; s < CurParagraph.Sentences.Length; s++) + { + var wordCount = CurParagraph.Sentences[s].Words.Length; + for (var w = CurIndex.word; w < wordCount; w++) + { + var word = CurParagraph.Sentences[s].Words[w]; + WordQueue.Enqueue( + $"{word.Text}{(w + 1 >= wordCount && !word.Text.EndsWith(' ') ? " " : "")}" + ); + } + // Reset word index to 0 for following sentences + CurIndex.word = 0; + } + + // Reset states + CurParagraph = default; + CurIndex = default; + } + + public string GetCurrentMessage() + { + // Remove expired words if more space is needed + if (WordQueue.Count > 0 || CurParagraph.Length > 0) + { + while ( + MessageWordQueue.TryPeek(out var expiredWord) + && DateTime.Now - expiredWord.DisplayTime > WordTime + ) + { + CurMessageLength -= MessageWordQueue.Dequeue().Text.Length; + } + } + + // Make sure there is enough room to fit a new word in the message and + // allow space for a dash after the current text if there is already more + while ( + WordQueue.TryPeek(out var newWord) + && CurMessageLength + newWord.Length + (WordQueue.Count > 1 ? 1 : 0) < MessageLength + ) + { + var word = WordQueue.Dequeue(); + MessageWordQueue.Enqueue(new MessageWord(word, DateTime.Now)); + CurMessageLength += word.Length; + } + + // If there's no queue and there's new words to display + if (WordQueue.Count <= 0 && CurParagraph.Length > 0) + { + // If there's no message to display besides the one in progress, display what we have now + if (MessageWordQueue.Count <= 0) + { + return CurParagraph + .Sentences.SelectMany(x => x.Words, (x, y) => y.Text) + .Aggregate("", (x, y) => x + y) + .Trim(); + } + } + + var message = MessageWordQueue + .Select(w => w.Text) + .Aggregate("", (x, y) => x + y) + .Trim(); + return $"{message}{(WordQueue.Count > 0 ? "-" : "")}"; + } + } +} diff --git a/ButterSTT/MessageSystem/MessageWord.cs b/ButterSTT/MessageSystem/MessageWord.cs new file mode 100644 index 0000000..739de2b --- /dev/null +++ b/ButterSTT/MessageSystem/MessageWord.cs @@ -0,0 +1,14 @@ +namespace ButterSTT.MessageSystem +{ + public readonly struct MessageWord + { + public readonly string Text; + public readonly DateTime DisplayTime; + + public MessageWord(string text, DateTime displayTime) + { + Text = text; + DisplayTime = displayTime; + } + } +} diff --git a/ButterSTT/OSCHandler.cs b/ButterSTT/MessageSystem/OSCHandler.cs similarity index 95% rename from ButterSTT/OSCHandler.cs rename to ButterSTT/MessageSystem/OSCHandler.cs index 93ce5cb..406fdde 100644 --- a/ButterSTT/OSCHandler.cs +++ b/ButterSTT/MessageSystem/OSCHandler.cs @@ -1,6 +1,6 @@ using CoreOSC; -namespace ButterSTT +namespace ButterSTT.MessageSystem { public class OSCHandler { diff --git a/ButterSTT/SpeechToTextHandler.cs b/ButterSTT/SpeechToTextHandler.cs index c0ad559..09b45f3 100644 --- a/ButterSTT/SpeechToTextHandler.cs +++ b/ButterSTT/SpeechToTextHandler.cs @@ -1,5 +1,6 @@ using System.Text; using AprilAsr; +using ButterSTT.MessageSystem; using ButterSTT.TextProcessing; using CoreOSC; using NAudio.Wave; @@ -22,6 +23,7 @@ public class SpeechToTextHandler : IDisposable // Output private readonly StringBuilder consoleOutput = new(); private readonly StringBuilder aprilOutput = new(); + private readonly MessageQueue messageQueue = new(); private readonly OSCHandler oscHandler = new(); private DateTime lastMessage = DateTime.Now; @@ -145,6 +147,10 @@ private void OnAprilTokens(AprilResultKind result, AprilToken[] tokens) ? EnglishCapitalization.Capitalize(aprilOutput.ToString().Trim()) : ""; + messageQueue.CurParagraph = EnglishTextParser.ParseParagraph(aprilOutputString); + if (result == AprilResultKind.FinalRecognition) + messageQueue.FinishCurrentParagraph(); + try { if (tokens.Length > 0 && !string.IsNullOrWhiteSpace(aprilOutputString)) diff --git a/ButterSTT/TextProcessing/TextParts/Paragraph.cs b/ButterSTT/TextProcessing/TextParts/Paragraph.cs index 61935ca..4bd31c2 100644 --- a/ButterSTT/TextProcessing/TextParts/Paragraph.cs +++ b/ButterSTT/TextProcessing/TextParts/Paragraph.cs @@ -3,10 +3,12 @@ namespace ButterSTT.TextProcessing.TextParts public readonly struct Paragraph { public readonly Sentence[] Sentences; + public readonly int Length; public Paragraph(Sentence[] sentences) { Sentences = sentences; + Length = sentences.Sum(s => s.Length); } } } diff --git a/ButterSTT/TextProcessing/TextParts/Sentence.cs b/ButterSTT/TextProcessing/TextParts/Sentence.cs index cdefa2f..08bbc36 100644 --- a/ButterSTT/TextProcessing/TextParts/Sentence.cs +++ b/ButterSTT/TextProcessing/TextParts/Sentence.cs @@ -3,10 +3,12 @@ namespace ButterSTT.TextProcessing.TextParts public readonly struct Sentence { public readonly Word[] Words; + public readonly int Length; public Sentence(Word[] words) { Words = words; + Length = words.Sum(w => w.Text.Length); } } }