Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/Agent/AgentManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,26 @@ public void Speak(string text)
}
}

/// <summary>
/// Makes the character speak text sentence by sentence
/// </summary>
public void SpeakSentences(List<string> sentences)
{
EnsureLoaded();
if (sentences == null || sentences.Count == 0)
return;

// MS Agent Speak queues the text, so we can just call Speak for each sentence
// and it will display them sequentially in separate speech bubbles.
foreach (var sentence in sentences)
{
if (!string.IsNullOrEmpty(sentence))
{
_character.Speak(sentence, null);
}
}
}

/// <summary>
/// Makes the character think the specified text (shows in thought balloon)
/// </summary>
Expand Down
43 changes: 37 additions & 6 deletions src/Config/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text.RegularExpressions;
using Newtonsoft.Json;

namespace MSAgentAI.Config
Expand Down Expand Up @@ -153,6 +154,9 @@ public class AppSettings
// Agent size (100 = normal, 50 = half, 200 = double)
public int AgentSize { get; set; } = 100;

// Speech truncation (sentence by sentence)
public bool TruncateSpeech { get; set; } = false;

// Idle animation spacing (in idle timer ticks - higher = less frequent)
public int IdleAnimationSpacing { get; set; } = 5;

Expand Down Expand Up @@ -249,15 +253,15 @@ public string ProcessText(string text)
{
// Use word boundaries (\b) to match WHOLE words only, not substrings
// This prevents "AI" from matching inside "Entertaining"
string pattern = @"\b" + System.Text.RegularExpressions.Regex.Escape(entry.Key) + @"\b";
string pattern = @"\b" + Regex.Escape(entry.Key) + @"\b";

// The \map\ command REPLACES the word with the pronunciation
// Format: \map="anim-ay"="anime"\ (replaces the word entirely)
text = System.Text.RegularExpressions.Regex.Replace(
text = Regex.Replace(
text,
pattern,
match => $"\\map=\"{entry.Value}\"=\"{match.Value}\"\\",
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
RegexOptions.IgnoreCase);
}
}
}
Expand Down Expand Up @@ -285,18 +289,45 @@ public static (string text, List<string> animations) ExtractAnimationTriggers(st
if (string.IsNullOrEmpty(text))
return (text, animations);

var matches = System.Text.RegularExpressions.Regex.Matches(text, @"&&(\w+)");
foreach (System.Text.RegularExpressions.Match match in matches)
var matches = Regex.Matches(text, @"&&(\w+)");
foreach (Match match in matches)
{
animations.Add(match.Groups[1].Value);
}

// Remove animation triggers from text
text = System.Text.RegularExpressions.Regex.Replace(text, @"&&\w+\s*", "").Trim();
text = Regex.Replace(text, @"&&\w+\s*", "").Trim();

return (text, animations);
}

/// <summary>
/// Splits text into sentences for sentence-by-sentence speech
/// </summary>
public static List<string> SplitIntoSentences(string text)
{
var sentences = new List<string>();
if (string.IsNullOrEmpty(text))
return sentences;

// Split by common sentence endings: period, exclamation, question mark
// Also handle ellipsis (...) as a sentence boundary
// Note: This is a simple implementation that may not handle all edge cases
// (e.g., abbreviations like "Dr." or decimal numbers like "3.14")
var parts = Regex.Split(text, @"(?<=[.!?])\s+|(?<=\.\.\.)\s*");

foreach (var part in parts)
{
var trimmed = part.Trim();
if (!string.IsNullOrEmpty(trimmed))
{
sentences.Add(trimmed);
}
}

return sentences;
}

/// <summary>
/// Gets a random line with text processing applied
/// </summary>
Expand Down
12 changes: 10 additions & 2 deletions src/UI/ChatForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,16 @@ private void SpeakWithAnimations(string text, string defaultAnimation = null)
_agentManager.PlayAnimation(defaultAnimation);
}

// Speak the processed text
_agentManager.Speak(cleanText);
// Speak the processed text - check if truncation is enabled
if (_settings.TruncateSpeech)
{
var sentences = AppSettings.SplitIntoSentences(cleanText);
_agentManager.SpeakSentences(sentences);
}
else
{
_agentManager.Speak(cleanText);
}
}

private void AppendToHistory(string speaker, string message, Color color)
Expand Down
12 changes: 10 additions & 2 deletions src/UI/MainForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,16 @@ private void SpeakWithAnimations(string text, string defaultAnimation = null)
_agentManager.PlayAnimation(defaultAnimation);
}

// Speak the processed text
_agentManager.Speak(cleanText);
// Speak the processed text - check if truncation is enabled
if (_settings.TruncateSpeech)
{
var sentences = AppSettings.SplitIntoSentences(cleanText);
_agentManager.SpeakSentences(sentences);
}
else
{
_agentManager.Speak(cleanText);
}
}

/// <summary>
Expand Down
35 changes: 27 additions & 8 deletions src/UI/SettingsForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public class SettingsForm : Form
private Label _confidenceValueLabel;
private TrackBar _silenceTrackBar;
private Label _silenceValueLabel;
private CheckBox _truncateSpeechCheckBox;

// Ollama controls
private TextBox _ollamaUrlTextBox;
Expand Down Expand Up @@ -178,15 +179,15 @@ private void InitializeComponent()
this.Text = "MSAgent AI Settings";
this.Size = new Size(650, 550);
this.StartPosition = FormStartPosition.CenterScreen;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.FormBorderStyle = FormBorderStyle.Sizable;
this.MinimumSize = new Size(650, 550);

// Create main tab control
_tabControl = new TabControl
{
Location = new Point(10, 10),
Size = new Size(615, 450)
Size = new Size(615, 450),
Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right
};

// Create tabs
Expand All @@ -205,7 +206,8 @@ private void InitializeComponent()
Text = "OK",
Location = new Point(365, 470),
Size = new Size(80, 30),
DialogResult = DialogResult.OK
DialogResult = DialogResult.OK,
Anchor = AnchorStyles.Bottom | AnchorStyles.Right
};
_okButton.Click += OnOkClick;

Expand All @@ -214,14 +216,16 @@ private void InitializeComponent()
Text = "Cancel",
Location = new Point(455, 470),
Size = new Size(80, 30),
DialogResult = DialogResult.Cancel
DialogResult = DialogResult.Cancel,
Anchor = AnchorStyles.Bottom | AnchorStyles.Right
};

_applyButton = new Button
{
Text = "Apply",
Location = new Point(545, 470),
Size = new Size(80, 30)
Size = new Size(80, 30),
Anchor = AnchorStyles.Bottom | AnchorStyles.Right
};
_applyButton.Click += OnApplyClick;

Expand Down Expand Up @@ -645,6 +649,14 @@ private void CreateVoiceTab()
ForeColor = Color.Gray,
Font = new Font(this.Font.FontFamily, 7.5f)
};

// Truncate speech checkbox
_truncateSpeechCheckBox = new CheckBox
{
Text = "Sentence-by-sentence speech (truncate long speeches into separate bubbles)",
Location = new Point(15, 370),
Size = new Size(550, 25)
};

_voiceTab.Controls.AddRange(new Control[]
{
Expand All @@ -656,7 +668,8 @@ private void CreateVoiceTab()
callModeLabel,
micLabel, _microphoneComboBox,
confidenceLabel, _confidenceTrackBar, _confidenceValueLabel, confidenceHint,
silenceLabel, _silenceTrackBar, _silenceValueLabel, silenceHint
silenceLabel, _silenceTrackBar, _silenceValueLabel, silenceHint,
_truncateSpeechCheckBox
});
}

Expand Down Expand Up @@ -1334,6 +1347,9 @@ private void LoadSettings()
// Agent size
_agentSizeTrackBar.Value = Math.Max(_agentSizeTrackBar.Minimum, Math.Min(_agentSizeTrackBar.Maximum, _settings.AgentSize));
_agentSizeValueLabel.Text = _agentSizeTrackBar.Value.ToString() + "%";

// Truncate speech
_truncateSpeechCheckBox.Checked = _settings.TruncateSpeech;

// Ollama settings
_ollamaUrlTextBox.Text = _settings.OllamaUrl;
Expand Down Expand Up @@ -1431,6 +1447,9 @@ private void SaveSettings()

// Agent size
_settings.AgentSize = _agentSizeTrackBar.Value;

// Truncate speech
_settings.TruncateSpeech = _truncateSpeechCheckBox.Checked;

// Ollama settings
_settings.OllamaUrl = _ollamaUrlTextBox.Text;
Expand Down