Click here to Skip to main content
Click here to Skip to main content
Go to top

Talking Clipboard

, 14 Jan 2008
Rate this:
Please Sign up or sign in to vote.
An application that automatically speaks the contents of the Windows clipboard

Introduction

TalkingClipboard is a little .NET app that speaks text that has been copied to the Windows clipboard, or that is manually typed into the application. The application demonstrates how to listen for Windows clipboard events and how to use the SpeechSynthesizer class that's new in .NET 3.0. This article was inspired in part by this Lounge post. I hope you have as much fun using TalkingClipboard as I had writing it!

The Talking Clipboard

How to Use TalkingClipboard

TalkingClipboard is easy to use - just start it and copy some text to the Windows clipboard. The application displays the contents of the clipboard and starts speaking it. If you copy new text into the clipboard, TalkingClipboard will interrupt its monolog and respond with the clipboard's new contents.

To stop the current speech, click "Stop". You can also directly type into the text box and click "Speak" to have text read aloud to you. Click "Pause/Resume" to pause and resume speaking. Check the "Stop monitoring clipboard" box to temporarily prevent TalkingClipboard from grabbing the clipboard's text. To save the speech to a .WAV file, click the "Save As .WAV File..." button.

If you have more than one voice installed on your system, you can select a voice by clicking the "Voice" selector. The "Speed" trackbar allows you to select a speed between -10 and +10, zero being the default.

How It Works

There are two aspects to TalkingClipboard's operation:

  • listening to the Windows clipboard
  • the TTS (text to speech) engine
Listening to the Windows Clipboard

Listening to the Windows clipboard is done using a few Win32 APIs:

  • SetClipboardViewer()
    This API registers the application's main window with the Windows clipboard. Once registered, Windows sends our main window a WM_DRAWCLIPBOARD message when the contents of the clipboard changes.
  • ChangeClipboardChain()
    This API is used to set the clipboard viewer chain (i.e. the list of windows registered with the Windows clipboard) back to the state before our application became a viewer. This is done when our application exits.
  • We also use the ubiquitous SendMessage() API to delegate the handling of changes to the clipboard viewer chain (sent as WM_CHANGECBCHAIN notifications) to other windows.

These APIs are made available to us via .NET's interop services located in the System.Runtime.InteropServices namespace. Perhaps the most important (and commonly used) functionality provided by this namespace is the DllImport attribute that lets us define a managed equivalent of a Win32 API. Invoking a Windows platform API is usually referred to as P/Invoke (for "platform invoke"). The site www.pinvoke.net contains a large collection of predefined P/Invoke signatures. For our purposes, we use the following declarations:

[DllImport("user32.dll")]
static extern IntPtr SetClipboardViewer (IntPtr hWndNewViewer);

[DllImport("user32.dll")]
static extern bool ChangeClipboardChain (IntPtr hWndRemove, IntPtr hWndNewNext);

[DllImport("user32.dll")]
public static extern int SendMessage 
	(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

In order to process notifications sent by Windows, we override Form.WndProc() and handle the appropriate messages. The notifications that interest us are WM_DRAWCLIPBOARD and WM_CHANGECBCHAIN.

const int WM_DRAWCLIPBOARD = 0x0308;
const int WM_CHANGECBCHAIN = 0x030D;

protected override void WndProc
    (ref Message m)
{
  base.WndProc (ref m);
  
  switch (m.Msg) {
    case WM_DRAWCLIPBOARD:
      ...
      break;

    case WM_CHANGECBCHAIN:
      ...
      break;
  }
}

When Windows sends us a WM_DRAWCLIPBOARD message to inform us that the contents of the clipboard have changed, we first check if the new contents are a blob of text. If so, we obtain the text and place it in the text box and request the TTS engine to speak it, as shown below:

IDataObject dataObj = Clipboard.GetDataObject();
if (dataObj.GetDataPresent (DataFormats.Text)) {
    string clipboardText = dataObj.GetData (DataFormats.Text) as string;
    _editText.Text = clipboardText;
    if (_synth.State == SynthesizerState.Speaking)
        _synth.SpeakAsyncCancelAll();
    _btnSpeak.PerformClick();
}

The application receives the WM_CHANGECBCHAIN notification when the chain of clipboard listeners has changed. If the next window in the clipboard chain has changed, we keep track of the fact. For other cases of WM_CHANGECBCHAIN, we delegate the handling to the next window in the chain by forwarding it the message.

if (m.WParam == _chainedWnd)
    _chainedWnd = m.LParam;
else
    SendMessage (_chainedWnd, m.Msg, m.WParam, m.LParam);
The TTS (Text to Speech) Engine

Among the new bits in .NET 3.0 is the SpeechSynthesizer class that exposes SAPI's functionality to managed code without having to resort to P/Invoke. SpeechSynthesizer is located in the System.Speech.Synthesis namespace. To speak text, we simply call Speak() or SpeakAsync() with a string containing the text to be spoken. To terminate the speech, we call SpeakAsyncCancelAll().

SpeechSynthesizer ss = new SpeechSynthesizer();
ss.SpeakAsync ("Hello, world");
...
ss.SpeakAsyncCancelAll();

The speech is spoken using the object's current properties of Voice, Rate and Volume. Voice is the name of an installed voice, Rate is an integer between -10 and 10, and Volume is an integer between 0 and 100 and represents the percentage of the audio device's main volume. The list of installed voices is obtained by calling GetInstalledVoices() as shown below:

ReadOnlyCollection<InstalledVoice> voices = ss.GetInstalledVoices();
if (voices.Count > 0)
    ss.Voice = voices[0];

Saving the spoken text as a .WAV file is easily done by directing the synthesizer's output to the file, speaking the text, and redirecting the output to the default audio device.

// Save speech as .WAV file
ss.SetOutputToWaveFile ("C:\\hello.wav");
ss.Speak ("Hello, world");
ss.SetOutputToDefaultAudioDevice();

Conclusion

There you have it - a simple yet useful little app that was fun to write and hopefully fun to learn from.

Revision History

  • 14 Jan 2008
    • Added checkbox to suspend monitoring the clipboard
    • Added Pause/Resume speech functionality
  • 14 Jan 2008
    • Initial version

License

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

Share

About the Author

Ravi Bhavnani
Technical Lead
Canada Canada
Ravi Bhavnani is an ardent fan of Microsoft technologies who loves building Windows apps, especially PIMs, system utilities, and things that go bump on the Internet. During his career, Ravi has developed expert systems, desktop imaging apps, marketing automation software, EDA tools, a platform to help people find, analyze and understand information, trading software for institutional investors and advanced data visualization solutions. He currently works for a company that provides enterprise workforce management solutions to large clients.
 
His interests include the .NET framework, reasoning systems, financial analysis and algorithmic trading, NLP, HCI and UI design. Ravi holds a BS in Physics and Math and an MS in Computer Science and was a Microsoft MVP (C++ and C# in 2006 and 2007). He is also the co-inventor of 2 patents on software security and generating data visualization dashboards. His claim to fame is that he crafted CodeProject's "joke" forum post icon.
 
Ravi's biggest fear is that one day he might actually get a life, although the chances of that happening seem extremely remote.
Follow on   Google+   LinkedIn

Comments and Discussions

 
Generalvideo in clipboard Pinmembersalam ellayan2-Nov-08 23:10 
Generalsyntax highlighting Pinmemberalgarnims31-Aug-08 23:08 
GeneralRe: syntax highlighting PinmemberRavi Bhavnani1-Sep-08 6:03 
GeneralRe: syntax highlighting Pinmemberalgarnims2-Sep-08 2:49 
GeneralRe: syntax highlighting PinmemberRavi Bhavnani2-Sep-08 3:02 
GeneralRe: syntax highlighting Pinmemberalgarnims2-Sep-08 3:00 
GeneralVery Interesting! Pinmembergeorani28-Jan-08 3:50 
GeneralRe: Very Interesting! PinmemberRavi Bhavnani28-Jan-08 7:00 
Questioncan not run? PinmemberhinetAccount24-Jan-08 20:44 
just happened to come by and intereted by the title talking clipboard> however, I downloaded the fdemo and unxip it and run, error message pop up
 
I used vs 2005 to debug the following messages appear:
 
""""
 
'TalkingClipboard.exe' (Managed): Loaded 'C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll', No symbols loaded.
'TalkingClipboard.exe' (Managed): Loaded 'C:\unzipped\TalkingClipboard_demo\TalkingClipboard.exe', No symbols loaded.
'TalkingClipboard.exe' (Managed): Loaded 'C:\WINDOWS\assembly\GAC_MSIL\System.Windows.Forms\2.0.0.0__b77a5c561934e089\System.Windows.Forms.dll', No symbols loaded.
'TalkingClipboard.exe' (Managed): Loaded 'C:\WINDOWS\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll', No symbols loaded.
'TalkingClipboard.exe' (Managed): Loaded 'C:\WINDOWS\assembly\GAC_MSIL\System.Drawing\2.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll', No symbols loaded.
An unhandled exception of type 'System.IO.FileNotFoundException' occurred in TalkingClipboard.exe
 
Additional information: Could not load file or assembly 'System.Speech, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies.
 
The program '[2560] TalkingClipboard.exe: Managed' has exited with code -532459699 (0xe0434f4d).
""""
 
any suggestion?
 
tf
AnswerRe: can not run? PinmemberRavi Bhavnani24-Jan-08 23:59 
GeneralCool! PinprotectorMarc Clifton14-Jan-08 2:56 
GeneralRe: Cool! PinmemberRavi Bhavnani14-Jan-08 5:48 
AnswerRe: Cool! PinmemberRavi Bhavnani14-Jan-08 6:36 
GeneralRe: Cool! PinmemberBassam Abdul-Baki22-Jan-08 5:33 
Generalnice Stuff PinmemberAbhijit Jana14-Jan-08 0:06 
GeneralRe: nice Stuff PinmemberRavi Bhavnani14-Jan-08 6:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 14 Jan 2008
Article Copyright 2008 by Ravi Bhavnani
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid