domingo, 9 de enero de 2011

Clipboard To Speech. Sintetizar el texto del portapapeles en C#

Clipboard 2 Speech es una pequeña aplicación en C# que observa el contenido del clipboard (o portapapeles) y cuando detecta cualquier texto en él, lo "sintetiza vocalmente" (Text-To-Speech).

Utiliza la clase SpeechSynthesizer del namespace System.Speech.Synthesis de .NET.
No es mucha la documentación que se puede encontrar de esta clase en MSDN, pero su uso es extremadamente sencillo; con sólo dos líneas de código, el sintetizador estará hablando:

SpeechSynthesizer ss = new SpeechSynthesizer();
ss.Speak("Hello, this is a test");
Para el monitoreo del contenido del clipboard, se creó la clase ClipboardWatcher (basada en un ejemplo de Tom Archer) que utiliza la función SetClipboardViewer de la API de windows, con el fin de suscribirnos al sistema operativo para que nos envíe un mensaje cada vez que el contenido de clipboard se modifique.

Esta es la forma correcta de enlazarnos a la cadena del clipboard:
private IntPtr nextClipboardViewer;
const int WM_DRAWCLIPBOARD = 0x308;
const int WM_CHANGECBCHAIN = 0x030D;
[DllImport("User32.dll")]
protected static extern int SetClipboardViewer(int hWndNewViewer);
[DllImport("User32.dll")]
public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
public ClipboardWatcher() // Constructor
{
    nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);
}
protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case WM_DRAWCLIPBOARD:
            OnClipboardData();
            SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
            break;
        case WM_CHANGECBCHAIN:
            if (m.WParam == nextClipboardViewer)
                nextClipboardViewer = m.LParam;
            else
                SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
            break;
        default:
            base.WndProc(ref m);
            break;
    }
}
protected override void Dispose(bool disposing)
{
    ChangeClipboardChain(this.Handle, nextClipboardViewer);
}
Este código debe estar dentro de un control de windows forms que implemente la función WndProc, (por ejemplo un Form o un UserControl).

Hasta aquí sólo obtenemos un aviso cada vez que el contenido del clipboard se modifica, por eso en el método OnClipboardData debemos obtener el nuevo contenido mediante una llamada al método estático GetDataObject de la clase Clipboard (de System.Windows.Form), y a partir de esto se dispara un evento con algunos datos del contenido del clipboard:

private void OnClipboardData()
{
    ClipboardDataArgs args = new ClipboardDataArgs();
    try
    {
        args.Data = Clipboard.GetDataObject();
        if (args.Data.GetDataPresent(DataFormats.Text))
        {
            args.Text = (string)args.Data.GetData(DataFormats.Text);
        }
        else if (args.Data.GetDataPresent(DataFormats.Bitmap))
        {
            args.Image = (Bitmap)args.Data.GetData(DataFormats.Bitmap);
        }
        OnRaiseClipboardData(args);
    }
    catch (Exception e)
    {
        MessageBox.Show(e.ToString());
    }
}
public class ClipboardDataArgs : EventArgs
{
    public string Text { get; set; }
    public Bitmap Image { get; set; }
    public IDataObject Data { get; set; }
}

Simple...

Puedes descargar el código completo desde aquí.

Nota:
La aplicación acepta ciertos "comandos" desde el clipboard. Por ejemplo, puedes copiar la cadena "/pause" o "/stop" para que el sintetizador se detenga, o la cadena "/start" para que reanude la síntesis.

No hay comentarios: