Cosas que aprendí de... GaGa
febrero 01, 2015

GaGa es un pequeño reproductor de radios online para Windows, escrito en C#. Es parecido a RadioTray para Linux, en el sentido en que se integra con el área de notificación del sistema.

Tiene esta pinta:

GaGa

Arquitectura

Con unas 3000 líneas de código en 18 archivos, GaGa es bastante más grande que la mayoría de proyectos que tengo en Github (MultiHash por ejemplo tiene unas 400 líneas). ¿Cómo organizarlo?.

Las pautas que seguí fueron las siguientes:

Y este fue el resultado:

GaGa

Finalmente, GaGa (GaGa.cs y GaGaSettings.cs) solo implementa el menú y la lógica de los eventos que se derivan de él, como reproducir una radio al hacer click en el stream correspondiente. Sirve para unir todo lo anterior.

Curiosidades del diseño:

Diría que la lección más importante en este punto es: organiza el programa de modo que necesites tener en tu cabeza el mínimo contenido posible para poder analizar su funcionamiento.

mINI y LowKey

mINI es una pequeña librería que sirve para leer archivos INI. Es curioso que aunque el formato INI sea predominante en Windows, .NET no tenga nada en la librería standard para usarlo.

Lo más interesante de mINI es que no guarda dato alguno del archivo que está leyendo. Es una clase abstracta con métodos como OnSection(String section) que las subclases implementan para decidir qué hacer con los datos. Este diseño es el que permite recargar el menú de GaGa con la mínima latencia posible, dado que no hay estructuras de datos intermedias.

Escribí mINI en un fin de semana y la verdad es que estoy contento con el resultado. Es simple, práctico, funciona.

LowKey es mucho más complicado. Es una librería que responde a pulsaciones de teclas desde cualquier aplicación utilizando hooks a bajo nivel. Aunque su código es relativamente corto, hay partes en las que es fácil cometer un error.

Dos ejemplos:

.NET Framework

C# es, en general, un lenguaje excelente. Un buen ejemplo es el modo en el que GaGa hace la animación en su icono de "buffering", que tiene esta pinta:

Buffering

Implementado así (fragmento):

// iconos que vamos a usar:
private readonly Icon[] bufferingIcons;

// temporizador para cambiar de icono:
private readonly DispatcherTimer bufferingIconTimer;

// índice del icono actual en el array bufferingIcons:
private Int32 currentBufferingIcon;

public Player(NotifyIcon icon)
{
    ...

    // 300 milisegundos entre icono e icono:
    bufferingIconTimer = new DispatcherTimer(DispatcherPriority.Background);
    bufferingIconTimer.Interval = TimeSpan.FromMilliseconds(300);
    bufferingIconTimer.Tick += OnBufferingIconTimerTick;
    currentBufferingIcon = 0;
}

private void OnBufferingIconTimerTick(Object sender, EventArgs e)
{
    // cambiar icono:
    notifyIcon.Icon = bufferingIcons[currentBufferingIcon];

    // ir al siguiente icono o volver el primero si es necesario:
    currentBufferingIcon++;
    if (currentBufferingIcon == bufferingIcons.Length)
    {
        currentBufferingIcon = 0;
    }
}

Un simple DispatcherTimer que se ejecuta en el thread actual con prioridad lo más baja posible (dado que no es algo que requiera precisión). Ojalá todos los lenguajes tuviesen algo similar.

Por desgracia, no puedo decir lo mismo de Windows Forms o de otras partes de la librería standard. Aunque sobre el papel es genial, en la práctica hay comportamientos extraños y bugs que hacen su uso incómodo y que llevan fácilmente a errores por parte del programador.

Algunos ejemplos (no exhaustivo):

Conclusiones

Estoy muy satisfecho con GaGa.

Casi todos mis programas son utilidades de consola diseñadas para usuarios avanzados (o para programadores), pero GaGa es útil para cualquier persona con un ordenador. Ha sido un reto interesante.

Además, una vez conseguí escuchar Radio GaGa de Queen en el propio GaGa (en la radio KissFM), lo que no tiene precio.

Repositorios en Github: GaGa - LowKey - mINI