# Afinador de guitarra (C)

Usar GTK+ y GStreamer para construir un sencillo afinador de guitarra para GNOME. Muestra cómo usar el diseñador de interfaces.
Proyecto de documentación de GNOME
gnome-doc-list@gnome.org
Johannes Schmid
jhs@gnome.org
Marta Maria Casetti
mmcasetti@gmail.com 2013
Daniel Mustieles
daniel.mustieles@gmail.com 2011 - 2020
Nicolás Satragno
nsatragno@gmail.com 2012 - 2013
Jorge González
jorgegonz@svn.gnome.org 2011

# Afinador de guitarra

      En este tutorial se va a hacer un programa que reproduce tonos que puede usar para afinar su guitarra. Aprenderá a:

        Configurar un proyecto básico en Anjuta
        Crear una IGU sencilla con el diseñador IU de Anjuta
        Usar GStreamer para reproducir sonidos

      Necesitará lo siguiente para poder seguir este tutorial:

        Una copia instalada del EID Anjuta
        Conocimiento básico del lenguaje de programación C

# Crear un proyecto en Anjuta

Antes de empezar a programar, deberá configurar un proyecto nuevo en Anjuta. Esto creará todos los archivos que necesite para construir y ejecutar el código más adelante. También es útil para mantener todo ordenado.

Inicie Anjuta y pulse ArchivoNuevoProyecto para abrir el asistente de proyectos.

Seleccione GTK+ (simple) en la pestaña C, pulse Continuar, y rellene sus detalles en las siguientes páginas. Use afinador-guitarra como nombre del proyecto y de la carpeta.

Asegúrese de que Configurar paquetes externos está activada. En la siguiente página, seleccione gstreamer-0.10 de la lista para incluir la biblioteca GStreamer en su proyecto.

Pulse Aplicar y se creará el proyecto. Abra src/main.c desde las pestañas Proyecto o Archivo. Debería ver algo de código que comience con las líneas:

#include <config.h> #include <gtk/gtk.h>

# Construir el código por primera vez

C es un lenguaje más detallado, por lo que no se sorprenda de que el archivo contiene un gran cantidad de código. La mayor parte es código de plantilla. Carga una ventana (vacía) desde el archivo de descripción de la interfaz de usuario y la muestra. A continuación se ofrecen más detalles; omita esta lista si entiende los conceptos básicos:

Las tres líneas `#include` en la parte superior incluyen las bibliotecas `config` (útil para definiciones de construcción de autoconf), `gtk` (interfaz de usuario) y `gi18n` (internacionalización). Las funciones de estas bibliotecas se usan en el resto del código.

La función `create_window` crea una ventana nueva abriendo un archivo de GtkBuilder (src/guitar-tuner.ui, definido unas pocas líneas más arriba), conectando sus señales y mostrándolo en una ventana. El archivo de GtkBuilder contiene una descripción de una interfaz de usuario y de todos sus elementos. Puede usar el editor Anjuta para diseñar interfaces de usuario con GtkBuilder.

Conectar señales es como se define lo que pasa cuando pulsa un botón, o cuando ocurre algún otro evento. Aquí, se llama a la función `destroy` (y se sale de la aplicación) cuando cierra la ventana.

La función `main` se ejecuta de manera predeterminada cuando inicia una aplicación en C. Llama a unas pocas funciones que configuran y ejecutan la aplicación. La función `gtk_main` inicia el bucle principal de GTK+, que ejecuta la interfaz de usuario y comienza a escuchar eventos (como pulsaciones del ratón y del teclado).

La definición condicional `ENABLE_NLS` configura `gettext`, que es un entorno de trabajo para traducir aplicaciones. Estas funciones especifican cómo deben manejar su aplicación las herramientas de traducción cuando las ejecuta.

Este código está listo para usarse, por lo que puede compilarlo pulsando ConstruirConstruir proyecto (o pulsando MayúsF7).

Pulse Ejecutar en la siguiente ventana que aparece para configurar una construcción de depuración. Esto sólo necesita hacer una vez para la primera construcción.

# Crear la interfaz de usuario

El archivo de GtkBuilder contiene una descripción de la interfaz de usuario (IU). Para editar la interfaz de usuario, abra src/guitar_tuner.ui. Esto cambiará al diseñador de interfaces. La ventana de diseño está en el centro; los widgets y sus propiedades están a la izquierda, y la paleta de los widgets disponibles está a la derecha.

La distribución de cualquier IU en GTK+ se organiza usando cajas y tablas. Aquí se usará una GtkButtonBox vertical para asignar seis GtkButtons, uno para cada una de las cuerdas de la guitarra.

Seleccione una GtkButtonBox de la sección Contenedor de la Paleta de la derecha y póngalo en la ventana. En el panel de Propiedades, establezca el número de elementos a «6» (para las seis cuerdas) y la orientación a «vertical».

Ahora, elija un GtkButton de la paleta y póngalo en la primera parte de la caja.

Mientras el botón esté seleccionado, cambie la propiedad Etiqueta en la pestaña Widgets a E. Esta será la cuerda Mi grave.

Cambie a la pestaña Señales (dentro de la pestaña Widgets) y busque la señal `clicked` del botón. Puede usar esto para conectar un gestor de señal al que se llamará cuando el usuario pulse el botón. Para hacer esto, pulse sobre la señal, escriba `on_button_clicked` en la columna Gestor y pulse Intro.

Repita los pasos anteriores para el resto de botones, añadiendo las 5 cuerdas restantes con los nombres A, D, G, B, y e.

Guarde el diseño de la IU (pulsando ArchivoGuardar) y déjelo abierto.

# Crear el gestor de señales

El el diseñador de interfaces, se ha hecho que todos los botones llamen a la misma función, on_button_clicked, cuando se pulsan. Se debe añadir esta función al archivo de código fuente.

Para hacer esto, abra main.c mientras el archivo de la interfaz de usuario está abierto. Cambie a la pestaña Señales, que ya ha usado para establecer el nombre de la señal. Ahora vaya a la fila en la que estableció la señal clicked y arrástrela al archivo de código fuente, fuera de cualquier función. Se añadirá el siguiente código a su archivo de código fuente:

void on_button_clicked (GtkWidget* button, gpointer user_data) {

}

El gestor de la señal tiene dos argumentos. un puntero al `GtkWidget` que llamó a la función (en este caso, siempre es un `GtkButton`), y un puntero a ciertos «datos de usuario» que puede definir, pero que aquí no se usan. (Puede establecer los datos de usuario llamando a `gtk_builder_connect_signals`; normalmente se usa para pasar un puntero a una estructura de datos a la que puede necesitar acceder dentro del gestor de la señal.)

Por ahora, se dejará el gestor de la señal vacío mientras se escribe el código para producir sonidos.

# Tuberías de Gstreamer

GStreamer es el entorno multimedia de trabajo de GNOME: puede usarlo para reproducir, grabar y procesar vídeo, sonido, flujos de la cámara web y similares. En este caso, se usará para generar tonos de frecuencia única.

Conceptualmente. GStreamer funciona de la siguiente manera: puede crear una tubería que contenga varios elementos de procesado que van desde la fuente hasta el sumidero (salida). La fuente puede ser, por ejemplo, un archivo de imagen, un vídeo o un archivo de música, y la salida puede ser un widget o la tarjeta de sonido.

Entre la fuente y el sumidero, puede aplicar varios filtros y conversores para manejar efectos, conversiones de formato, etc. Cada elemento de la tubería tiene propiedades que se pueden usar para cambiar este comportamiento.

Un ejemplo de tubería de GStreamer.

# Configurar la tubería

En este sencillo ejemplo se usará un generador de tonos llamado `audiotestsrc` y se enviará la salida al dispositivo de sonido predeterminado del sistema, `autoaudiosink`. Sólo es necesario configurar la frecuencia del generador de tonos; esto es accesible a través de la propiedad `freq` de `audiotestsrc`.

Inserte la siguiente línea en main.c, justo a continuación de la línea `#include <gtk/gtk.h>`:

#include <gst/gst.h>

Esto incluye la bilbioteca GStreamer. También necesita añadir una línea para inicializar GStreamer; ponga la siguiente línea de código antes de la llamada `gtk_init` en la función `main`:

gst_init (&argc, &argv);

Después, copie la siguiente función en main.c encima de la función `on_button_clicked` vacía:

static void play_sound (gdouble frequency) { GstElement *source, *sink; GstElement *pipeline;

pipeline = gst_pipeline_new ("note"); source = gst_element_factory_make ("audiotestsrc", "source"); sink = gst_element_factory_make ("autoaudiosink", "output");

/* set frequency */ g_object_set (source, "freq", frequency, NULL);

gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL); gst_element_link (source, sink);

gst_element_set_state (pipeline, GST_STATE_PLAYING);

/* stop it after 500ms */ g_timeout_add (LENGTH, (GSourceFunc) pipeline_stop, pipeline); }

Las cinco primeras líneas crean los elementos «fuente» y «sumidero» de GStreamer (`GstElement`), y un elemento de tubería (que se usará como contenedor de los otros dos elementos). A la tubería se le asigna el nombre «note»; la fuente se llama «source» y se asocia a la fuente `audiotestsrc` y el sumidero se llama «output» y se asocia con el sumidero `autoaudiosink` (la salida de la tarjeta de sonido predeterminada).

La llamada a `g_object_set` establece la propiedad `freq` del elemento fuente a `frequency`, que se pasa como argumento a la función `play_sound`. Esto sólo es la frecuencia de la nota en Hercios; más adelante se definirán algunas frecuencias útiles.

`gst_bin_add_many` añade la fuente y el sumidero a la tubería. La tubería es un `GstBin`, que es un elemento que puede contener otros muchos elementos de GStreamer. En general, puede añadir tantos elementos como quiera a una tubería añadiendo más argumentos a `gst_bin_add_many`.

Después, se usa `gst_element_link` para conectar los elementos de forma conjunta, de tal forma que la salida de `source` (un tono) va a la entrada de `sink` (que es después la salida de la tarjeta de sonido). `gst_element_set_state` se usa al iniciar la reproducción, configurando el estado de la tubería a reproduciendo (`GST_STATE_PLAYING`).

# Detener la reproducción

No se quiere reproducir un tono molesto para siempre, por lo que lo último que `play_sound` hace es llamar a `g_timeout_add`. Esto establece un tiempo de expiración para detener el sonido; espera `LENGTH` milisegundos antes de llamar a la función `pipeline_stop`, y se queda llamando a `pipeline_stop` hasta que devuelve `FALSE`.

Ahora, se escribirá el código de la función `pipeline_stop`, llamada por `g_timeout_add`. Inserte el código siguiente encima de la función `play_sound`:

#define LENGTH 500 /* Length of playing in ms */

static gboolean pipeline_stop (GstElement* pipeline) { gst_element_set_state (pipeline, GST_STATE_NULL); g_object_unref (pipeline);

return FALSE; }

La llamada a `gst_element_set_state` detiene la reproducción de la tubería y `g_object_unref` desreferencia la tubería, la destruye y libera su memoria.

# Definir los tonos

Se quiere reproducir el sonido correcto cuando un usuario pulsa un botón. En primer lugar, se necesita conocer las frecuencias de las seis cuerdas de la guitarra, que están definidas (al principio de main.c) de la siguiente manera:

/* Frequencies of the strings */ #define NOTE_E 329.63 #define NOTE_A 440 #define NOTE_D 587.33 #define NOTE_G 783.99 #define NOTE_B 987.77 #define NOTE_e 1318.5

Ahora se se profundiza en el gestor de la señal definido anteriormente, `on_button_clicked`. Se podría haber conectado cada botón a un gestor de la señal diferente, pero esto había supuesto duplicar mucho código. En su lugar, se puede usar la etiqueta del botón para saber cuál de ellos se ha pulsado:

/* Callback for the buttons */ void on_button_clicked (GtkButton* button, gpointer user_data) { const gchar* text = gtk_button_get_label (button);

if (g_str_equal (text, _("E"))) play_sound (NOTE_E); else if (g_str_equal (text, _("A"))) play_sound (NOTE_A); else if (g_str_equal (text, _("G"))) play_sound (NOTE_G); else if (g_str_equal (text, _("D"))) play_sound (NOTE_D); else if (g_str_equal (text, _("B"))) play_sound (NOTE_B); else if (g_str_equal (text, _("e"))) play_sound (NOTE_e); }

A `on_button_clicked` se le pasa como argumento (`button`) un puntero al `GtkButton` que se ha pulsado. Se puede obtener el texto de este botón usando `gtk_label_get_label`.

El texto se compara con las notas que se tiene usando `g_str_equal`, y se llama a `play_sound` con la frecuencia correspondiente a cada nota. Esto reproduce el tono; el afinador de guitarra ya está funcionando.

# Construir y ejecutar la aplicación

Todo el código debería estar listo para ejecutarse. Pulse ConstruirConstruir proyecto para construir todo otra vez y pulse EjecutarEjecutar para iniciar la aplicación.

Si todavía no lo ha hecho, elija la aplicación Debug/src/afinador-guitarra en el diálogo que aparece. Finalmente, pulse Ejecutar y disfrute.

# Implementación de referencia

Si tiene problemas con este tutorial, compare su código con este código de referencia.

# Siguientes pasos

Aquí hay algunas ideas sobre cómo puede extender esta sencilla demostración:

Hacer que el programa recorra las notas automáticamente.

Hacer que el programa reproduzca grabaciones de cuerdas de guitarras que se están afinando.

PAra hacer esto, debe configurar una tubería de GStreamer más complicada, que le permite cargar y reproducir archivos de música. Deberá elegir un los elementos decodificador y demultiplexor de GStreamer basándose en el formato del archivo de sus sonidos grabados; los MP3 usan elementos diferentes de los de los archivos Ogg Vorbis, por ejemplo.

Puede querer conectar los elementos de maneras más complicadas. Esto puede implicar usar conceptos de GStreamer que no se han comentado en este tutorial, tales como interfaces. Es posible que encuentre útil el comando gst-inspect.

Analizar automáticamente las notas que toca el músico.

Puede conectar un micrófono y grabar sonidos con él usando una fuente de entrada. ¿Es posible que algún tipo de análisis de espectro le permita saber qué notas se están reproduciendo?