Usar GTK+ y GStreamer para construír un sinxelo afinador de guitarra para GNOME. Mostra como usar o deseñador de interfaces.
Proxecto de documentación de GNOME
gnome-doc-list@gnome.org
Johannes Schmid
jhs@gnome.org

# Afinador de guitarra

      Neste titorial faremos un programa que reproduce tonos que pode usar para afinar unha guitarra. Aprenderá como:

        Configurar un proxecto básico en Anjuta
        Crear unha GUI sinxela co deseñador de UI de Anjuta
        Usar GStreamer para reproducir sons.

      You'll need the following to be able to follow this tutorial:

        An installed copy of the Anjuta IDE
        Coñecemento básico da linguaxe de programación Vala

# Cree un proxecto de Anjuta

Antes de comezar a programar, deberá configurar un proxecto novo en Anjuta. Isto creará todos os ficheiros que precise para construír e executar o código máis adiante. Tamén é útil para manter todo ordenado.

Inicie Anjuta e prema Ficheiro Novo Proxecto para abrir o asistente de proxectos.

Seleccione GTK+ (Simple) desde a lapela Vala, prema Adiante e complete os detalles nas seguintes páxinas. Use guitar-tunner como nome do proxecto e cartafol.

Asegúrese que Configurar paquetes externos está seleccionada. Na seguinte páxina, seleccione gstreamermm-0.10 da lista para incluír a biblioteca GStreamer no seu proxecto.

Prema Aplicar e crearase o proxecto. Abra src/guitar_tuner.vala desde as lapelas Proxecto ou Ficheiro. Debería ver algo de código que comece coas liñas:

using GLib; using Gtk;

# Construír o código por primeira vez

The code loads an (empty) window from the user interface description file and shows it. More details are given below; skip this list if you understand the basics:

The two `using` lines import namespaces so we don't have to name them explicitly.

The constructor of the `Main` class creates a new window by opening a GtkBuilder file (src/guitar-tuner.ui, defined a few lines above), connecting its signals and then displaying it in a window. The GtkBuilder file contains a description of a user interface and all of its elements. You can use Anjuta's editor to design GtkBuilder user interfaces.

Connecting signals is how you define what happens when you push a button, or when some other event happens. Here, the `destroy` function is called (and quits the app) when you close the window.

The static `main` function is run by default when you start a Vala application. It calls a few functions which create the Main class, set up and then run the application. The `Gtk.main` function starts the GTK main loop, which runs the user interface and starts listening for events (like clicks and key presses).

This code is ready to be used, so you can compile it by clicking Build Build Project (or press Shift F7).

Change the Configuration to Default and then press Execute to configure the build directory. You only need to do this once, for the first build.

# Crear a interface de usuario

A description of the user interface (UI) is contained in the GtkBuilder file. To edit the user interface, open src/guitar_tuner.ui. This will switch to the interface designer. The design window is in the center; widgets and widgets' properties are on the left, and the palette of available widgets is on the right.

The layout of every UI in GTK+ is organized using boxes and tables. Let's use a vertical GtkButtonBox here to assign six GtkButtons, one for each of the six guitar strings.

Select a GtkButtonBox from the Container section of the Palette on the right and put it into the window. In the Properties pane, set the number of elements to 6 (for the six strings) and the orientation to vertical.

Now, choose a GtkButton from the palette and put it into the first part of the box.

While the button is still selected, change the Label property in the Widgets tab to E. This will be the low E string.

Switch to the Signals tab (inside the Widgets tab) and look for the `clicked` signal of the button. You can use this to connect a signal handler that will be called when the button is clicked by the user. To do this, click on the signal and type `main_on_button_clicked` in the Handler column and press Return.

Repita os pasos anteriores para o resto dos botóns, engadindo as 5 cordas restantes cos nomes A, D, G, B e e.

Garde o deseño da IU (premendo Ficheiro Gardar) e déixeo aberto.

# Crear o manexador de sinais

In the UI designer, you made it so that all of the buttons will call the same function, on_button_clicked, when they are clicked. Actually we type main_on_button_clicked which tells the UI designer that this method is part of our `Main`. We need to add that function in the source file.

To do this, open guitar_tuner.vala while the user interface file is still open. Switch to the Signals tab, which you already used to set the signal name. Now take the row where you set the clicked signal and drag it into to the source file at the beginning of the class. The following code will be added to your source file:

[CCode (instance_pos=-1)] public void on_button_clicked (Gtk.Button sender) {

}

This signal handler has only one argument: the `Gtk.Widget` that called the function (in our case, always a `Gtk.Button`). The additonal `[CCode (instance_pos=-1)]` tells the compiler that this is a signal handler that needs special treating while linking in order to be found at runtime.

For now, we'll leave the signal handler empty while we work on writing the code to produce sounds.

# Tuberías de GStreamer

GStreamer é un marco de traballo multimedia de GNOME — vostede pode usalo para reproducir, gravar e procesar vídeo, son, fluxos de cámara web e semellantes. Aquí, usarémolo para producir tonos dunha única frecuencia.

Conceptually, GStreamer works as follows: You create a pipeline containing several processing elements going from the source to the sink (output). The source can be an image file, a video, or a music file, for example, and the output could be a widget or the soundcard.

Between source and sink, you can apply various filters and converters to handle effects, format conversions and so on. Each element of the pipeline has properties which can be used to change its behaviour.

Un exemplo de tubería de GStreamer.

# Configurar a tubería

In this simple example we will use a tone generator source called `audiotestsrc` and send the output to the default system sound device, `autoaudiosink`. We only need to configure the frequency of the tone generator; this is accessible through the `freq` property of `audiotestsrc`.

We need to add a line to initialize GStreamer; put the following code on the line above the `Gtk.init` call in the `main` function:

Gst.init (ref argv);

Then, copy the following function into guitar_tuner.vala inside our `Main` class:

Gst.Element sink; Gst.Element source; Gst.Pipeline pipeline;

private void play_sound(double frequency) { pipeline = new Gst.Pipeline ("note"); source = Gst.ElementFactory.make ("audiotestsrc", "source"); sink = Gst.ElementFactory.make ("autoaudiosink", "output");

/* set frequency */ source.set ("freq", frequency);

pipeline.add (source); pipeline.add (sink); source.link (sink);

pipeline.set_state (Gst.State.PLAYING);

/* stop it after 200ms */ var time = new TimeoutSource(200);

time.set_callback(() => { pipeline.set_state (Gst.State.PAUSED); return false; }); time.attach(null); }

The first five lines create source and sink GStreamer elements (`Gst.Element`), and a pipeline element (which will be used as a container for the other two elements). Those are class variables so they are defined outside the method. The pipeline is given the name "note"; the source is named "source" and is set to the `audiotestsrc` source; and the sink is named "output" and set to the `autoaudiosink` sink (default sound card output).

The call to `source.set` sets the `freq` property of the source element to `frequency`, which is passed as an argument to the `play_sound` function. This is just the frequency of the note in Hertz; some useful frequencies will be defined later on.

`pipeline.add` puts the source and sink into the pipeline. The pipeline is a `Gst.Bin`, which is just an element that can contain multiple other GStreamer elements. In general, you can add as many elements as you like to the pipeline by adding more calls to `pipeline.add`.

Next, `sink.link` is used to connect the elements together, so the output of source (a tone) goes into the input of sink (which is then output to the sound card). `pipeline.set_state` is then used to start playback, by setting the state of the pipeline to playing (`Gst.State.PLAYING`).

We don't want to play an annoying tone forever, so the last thing `play_sound` does is to add a `TimeoutSource`. This sets a timeout for stopping the sound; it waits for 200 milliseconds before calling a signal handler defined inline that stops and destroys the pipeline. It returns `false` to remove itself from the timeout, otherwise it would continue to be called every 200 ms.

# Definir o manexador de sinais

We want to play the correct sound when the user clicks a button. For this, we flesh out the signal handler that we defined earlier, `on_button_clicked`. We could have connected every button to a different signal handler, but that would lead to a lot of code duplication. Instead, we can use the label of the button to figure out which button was clicked:

public void on_button_clicked (Gtk.Button sender) { var label = sender.get_child () as Gtk.Label; switch (label.get_label()) { case "E": play_sound (369.23); break; case "A": play_sound (440); break; case "D": play_sound (587.33); break; case "G": play_sound (783.99); break; case "B": play_sound (987.77); break; case "e": play_sound (1318); break; default: break; } }

The `Gtk.Button` that was clicked is passed as an argument (`sender`) to `on_button_clicked`. We can get the label of that button by using `get_child`, and then get the text from that label using `get_label`.

The switch statement compares the label text to the notes that we can play, and `play_sound` is called with the frequency appropriate for that note. This plays the tone; we have a working guitar tuner!

# Construír e executar o aplicativo

All of the code should now be ready to go. Click Build Build Project to build everything again, and then Run Run to start the application.

If you haven't already done so, choose the Debug/src/guitar-tuner application in the dialog that appears. Finally, hit Run and enjoy!

# Implementación de referencia

If you run into problems with the tutorial, compare your code with this reference code.

# Lectura complementaria

Para atopar máis información sobre a linguaxe de programación Vala pode visitar o titorial de Vala.

# Seguintes pasos

Aquí hai algunhas ideas sobre como pode estender esta sinxela demostración:

Facer que o programa reproduza de forma cíclica as notas.

Facer que o programa reproduza gravacións de cordas de guitarras que se están afinando.

To do this, you would need to set up a more complicated GStreamer pipeline which allows you to load and play back music files. You'll have to choose decoder and demuxer GStreamer elements based on the file format of your recorded sounds — MP3s use different elements to Ogg Vorbis files, for example.

You might need to connect the elements in more complicated ways too. This could involve using GStreamer concepts that we didn't cover in this tutorial, such as pads. You may also find the gst-inspect command useful.

Analizar automaticamente as notas que toca o músico.

You could connect a microphone and record sounds from it using an input source. Perhaps some form of spectrum analysis would allow you to figure out what notes are being played?