Click here to Skip to main content
15,879,490 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hello Everyone!
BillWoodruff gave me this method to break up the text of a text box into an array, this was to aid in helping me to highlight text in the textbox word by word based on what a TTS operation was speaking. please refer to my previous post on this question.
now i was able to implement his suggestions like so:

this runs right before i start the Speech operation
C#
//this runs right before i start the Speech operation
// split the RTB Text
theWords = TextToSpeak.Split(splitChar, StringSplitOptions.RemoveEmptyEntries);
// number of words
nWords = theWords.Length;

// location pointer
location = 0;

// build the List of word locations
foreach (string word in theWords)
{
    theWordsLocations.Add(new Point(location, word.Length));
    location += word.Length + 1;
}

// location pointer
location = 0;

next whenever a word is spoken when the TTS operation is running, this runs:(this is in the SpeakProgress event method)
C#
private void synth_SpeakProgress(object sender, SpeakProgressEventArgs e)
{
    if (currentWord == 0)
    {
        currentSelection = new Point(location, theWords[currentWord].Length);
        oldlocation = location;
        location = theWords[currentWord + 1].Length;
    }
    else
    {
        if (currentWord + 1 == theWords.Length)
        {
            previousSelection = new Point(oldlocation, theWords[currentWord - 1].Length);
            currentSelection = new Point(location, theWords[currentWord].Length);
            oldlocation = location;
            location = theWords[currentWord].Length;
        }
        else
        {
            previousSelection = new Point(oldlocation, theWords[currentWord - 1].Length);
            currentSelection = new Point(location, theWords[currentWord + 1].Length);
            oldlocation = location;
            location = theWords[currentWord + 1].Length;
        }


    }

    currentWord += 1;
}


now when i want to select something in my text box i just do this(this is being ran every 100 to 1 milliseconds because i am using a timer):
and just for reference the ThreadOperation object is an instance of my "ManualThread" class. if need be i can post this class.
C#
private void ThreadTimer_Tick(object sender, EventArgs e)
{
    if (ThreadOperation.currentSelection == null)
    {

    }
    else
    {
        TB.Select(ThreadOperation.currentSelection.X, ThreadOperation.currentSelection.Y);
        TB.SelectionColor = Color.Black;
        TB.SelectionBackColor = Color.White;

        TB.Select(ThreadOperation.previousSelection.X, ThreadOperation.previousSelection.Y);
        TB.SelectionColor = Color.White;
        TB.SelectionBackColor = Color.FromArgb(255, 51, 153, 255);
    }
    //TB.Text = ThreadOperation.CharPos.ToString();
    txtSpoken.Text = ThreadOperation.LastTextSpoken;

}

now my question is about the synth_SpeakProgress method.
sorry if this seems complicated:
when ever i run this setup the system does't select just the word that the TTS opp(i just abbreviated operation, i am getting tired of writing that word) is speaking, but instead it might select either the space in front of the currently spoken word, the previously spoken word or the space behind the currently spoken word.
i have made thorough use of the Visual Studio debugger and i have traced the problem down to being a math error when i am accessing the "theWords" array.
no matter how many changes i have made to this method(that being the synth_SpeakProgress method) i just can't get it right. can some one help me out here?
thanks for your help in advance,
MasterCodeon
Posted

For everyone who doesn't know how to implement this with a speech synthesizer, do as follows:
Step 1:
add a reference to the System.Speech Name space like this:
Right click on the references folder and click "Add Reference..."
then a window should pop up, click the tab that says ".NET" then scroll down the list until you find the entry named System.Speech. select it and click the "Ok" button

Step 2:
add the following to the the top of your winform app code(we are adding a reference to the System.Speech.Synthesis namespace)
C#
using System.Speech.Synthesis;

Step 3: right above the apps constructer(at the class level) add this:
C#
// Initialize a new instance of the SpeechSynthesizer.
SpeechSynthesizer synth = new SpeechSynthesizer();

//Add The "SpeakProgress" Event Handler
synth.SpeakProgress += new EventHandler<speakprogresseventargs>(synth_SpeakProgress);

//Add The "SpeakCompleted" Event Handler
synth.SpeakCompleted += new EventHandler<speakcompletedeventargs>(SpeakOperationCompleted);
</speakcompletedeventargs></speakprogresseventargs>

Step 4: now add this method:
C#
#region SpeakProgress
private void synth_SpeakProgress(object sender, SpeakProgressEventArgs e)
{
    if (currentWord == theWordsLocations.Count)
    {
        WhateverTheNameOfYourTextBox.SelectionColor = previousSelectionForeColor;
        WhateverTheNameOfYourTextBox.SelectionBackColor = previousSelectionBackColor;
    }
    else
    {
        // get current selection locations
        previousSelection = new Point(TB.SelectionStart, TB.SelectionLength);

        // reset the previous word's state Colors
        if (previousSelection != Point.Empty)
        {
            WhateverTheNameOfYourTextBox.SelectionColor = previousSelectionForeColor;
            WhateverTheNameOfYourTextBox.SelectionBackColor = previousSelectionBackColor;
        }

        // save the current Selection state Colors
        previousSelectionForeColor = TB.SelectionColor;
        previousSelectionBackColor = TB.SelectionBackColor;

        // get the location of the next word
        var location = theWordsLocations[currentWord];

        // select it
        WhateverTheNameOfYourTextBox.Select(location.X, location.Y);

        // highlight it
        WhateverTheNameOfYourTextBox.SelectionColor = HighLightForeColor;
        WhateverTheNameOfYourTextBox.SelectionBackColor = HighLightBackColor;

        // finished ?
        if (currentWord == nWords)
        {
            WhateverTheNameOfYourTextBox.SelectionColor = previousSelectionForeColor;
            WhateverTheNameOfYourTextBox.SelectionBackColor = previousSelectionBackColor;
        }

        // move on
        currentWord++;
    }
}

Step 5: now add the this "SpeakOperationCompleted" method to your code
C#
#region Speak Operation Completed
public void SpeakOperationCompleted(object sender, SpeakCompletedEventArgs e)
{
    WhateverTheNameOfYourTextBox.ReadOnly = false;
    WhateverTheNameOfYourTextBox.SelectAll();
    WhateverTheNameOfYourTextBox.SelectionColor = previousSelectionForeColor;
    WhateverTheNameOfYourTextBox.SelectionBackColor = previousSelectionBackColor;
}
#endregion


Step 5: add this method to your code(this is the method that will be called to actually speak the text you specify):
C#
public void SpeakSynthText(String TextToSpeak)
{
    #region SpeechSynth
    //Set The Audio Output Device To The Defualt Output Device
    synth.SetOutputToDefaultAudioDevice();

    // Select a voice that matches a specific gender.  
    synth.SelectVoiceByHints(VoiceGender.Female);

    // Speak The Text Asynchronously And If Wave Save Is Enable Play Back The Wave File.            
    synth.SpeakAsync(TextToSpeak);
    #endregion
}


Step 6: now add a button to the form, double click on it and paste the following code:
C#
public void SpeakText_Click_1(object sender, EventArgs e)
{
    //Make Sure The User Can't Edit The Text That Is Going To Be Spoken
    TB.ReadOnly = true;

    //Clear The text Selector Variables
    nWords = 0;
    theWords.Clear();
    theWordsLocations.Clear();
    loc = 0;

    //Text Selector Starts Here
    theWords = TB.Text.Split(splitChar, StringSplitOptions.RemoveEmptyEntries).ToList();

    nWords = theWords.Count;

    foreach (var word in theWords)
    {
        theWordsLocations.Add(new Point(loc, word.Length));
        loc += word.Length + 1;
    }

    currentWord = 0;
    //The synth_SpeakProgress Method Will Do The Actual Text Selection
    //End Text Selector            

    //Speak The Contents Of The RichTextBox "WhateverTheNameOfYourTextBox"
    SpeakSynthText(WhateverTheNameOfYourTextBox.Text);
}
#endregion


and there you have it!
just type whatever you want to have the Speech Synth Say and click the button you added to the form and watch the text box as it will highlight the current word being spoken by the TTS engine.
if this doesn't work for you or you have any questions please state the problem or question in the comments of this post.
all credit for the text selection idea goes to BillWoodruff.
Thank you soo much Bill!
MasterCodeon
 
Share this answer
 
v2
Here's the complete code I used to test what I previously posted; I suspect it can be simplified since I wrote it quickly:
Controls used:
    Form Form1
    RichTextBox richTextBox1
    Button btnHighLightWords
    System.Windows.Forms.Timer timer1<
The code:
C#
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace RTFWordByWord
{
    public partial class Form1 : Form
    {
        private readonly List<point> theWordsLocations = new List<point>();

        private List<string> theWords = new List<string>();

        private readonly Color HighLightBackColor = Color.Navy;
        private readonly Color HighLightForeColor = Color.Yellow;
        private Color previousSelectionBackColor;
        private Color previousSelectionForeColor;

        private Point previousSelection;

        private int currentWord;
        private int nWords;

        private readonly char[] splitChar = { ' ', '\n' };

        private string theText;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            theWords = richTextBox1.Text.Split(splitChar, StringSplitOptions.RemoveEmptyEntries).ToList();

            nWords = theWords.Count;

            var location = 0;

            foreach (var word in theWords)
            {
                theWordsLocations.Add(new Point(location, word.Length));
                location += word.Length + 1;
            }
        }

        private void btnHighLightWords_Click(object sender, EventArgs e)
        {
            currentWord = 0;
            timer1.Start();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            // get current selection locations
            previousSelection = new Point(richTextBox1.SelectionStart, richTextBox1.SelectionLength);

            // reset the previous word's state Colors
            if (previousSelection != Point.Empty)
            {
                richTextBox1.SelectionColor = previousSelectionForeColor;
                richTextBox1.SelectionBackColor = previousSelectionBackColor;
            }

            // save the current Selection state Colors
            previousSelectionForeColor = richTextBox1.SelectionColor;
            previousSelectionBackColor = richTextBox1.SelectionBackColor;

            // get the location of the next word
            var location = theWordsLocations[currentWord];

            // select it
            richTextBox1.Select(location.X, location.Y);

            // highlight it
            richTextBox1.SelectionColor = HighLightForeColor;
            richTextBox1.SelectionBackColor = HighLightBackColor;

            // finished ?
            if (currentWord == nWords) timer1.Stop();

            // move on
            currentWord++;
        }
    }
}
 
Share this answer
 
v4
Comments
MasterCodeon 26-Dec-14 14:41pm    
wow this works perfectly(once i did a few modifications)!
what was i doing wrong?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900