Blame platform-demos/gl/guitar-tuner.c.page

Packit 1470ea
Packit 1470ea
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" type="topic" id="guitar-tuner.c" xml:lang="gl">
Packit 1470ea
Packit 1470ea
  <info>
Packit 1470ea
    <title type="text">Guitar tuner (C)</title>
Packit 1470ea
    <link type="guide" xref="c#examples"/>
Packit 1470ea
Packit 1470ea
    <desc>Use GTK+ and GStreamer to build a simple guitar tuner application for GNOME. Shows off how to use the interface designer.</desc>
Packit 1470ea
Packit 1470ea
    <revision pkgversion="0.1" version="0.1" date="2010-12-02" status="review"/>
Packit 1470ea
    <credit type="author">
Packit 1470ea
      <name>Proxecto de documentación de GNOME</name>
Packit 1470ea
      <email its:translate="no">gnome-doc-list@gnome.org</email>
Packit 1470ea
    </credit>
Packit 1470ea
    <credit type="author">
Packit 1470ea
      <name>Johannes Schmid</name>
Packit 1470ea
      <email its:translate="no">jhs@gnome.org</email>
Packit 1470ea
    </credit>
Packit 1470ea
    <credit type="editor">
Packit 1470ea
      <name>Marta Maria Casetti</name>
Packit 1470ea
      <email its:translate="no">mmcasetti@gmail.com</email>
Packit 1470ea
      <years>2013</years>
Packit 1470ea
    </credit>
Packit 1470ea
  
Packit 1470ea
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
Packit 1470ea
      <mal:name>Fran Dieguez</mal:name>
Packit 1470ea
      <mal:email>frandieguez@gnome.org</mal:email>
Packit 1470ea
      <mal:years>2012-2013.</mal:years>
Packit 1470ea
    </mal:credit>
Packit 1470ea
  </info>
Packit 1470ea
Packit 1470ea
<title>Guitar tuner</title>
Packit 1470ea
Packit 1470ea
<synopsis>
Packit 1470ea
  

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

Packit 1470ea
  <list>
Packit 1470ea
    <item>

Configurar un proxecto básico en Anjuta

</item>
Packit 1470ea
    <item>

Crear unha GUI sinxela co deseñador de UI de Anjuta

</item>
Packit 1470ea
    <item>

Usar GStreamer para reproducir sons.

</item>
Packit 1470ea
  </list>
Packit 1470ea
  

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

Packit 1470ea
  <list>
Packit 1470ea
    <item>

An installed copy of the <link xref="getting-ready">Anjuta IDE</link>

</item>
Packit 1470ea
    <item>

Coñecemento básico da linguaxe de programación C

</item>
Packit 1470ea
  </list>
Packit 1470ea
</synopsis>
Packit 1470ea
Packit 1470ea
<media type="image" mime="image/png" src="media/guitar-tuner.png"/>
Packit 1470ea
Packit 1470ea
<section id="anjuta">
Packit 1470ea
  <title>Cree un proxecto de Anjuta</title>
Packit 1470ea
  

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.

Packit 1470ea
  <steps>
Packit 1470ea
    <item>
Packit 1470ea
    

Inicie Anjuta e prema <guiseq><gui>Ficheiro</gui><gui>Novo</gui><gui>Proxecto</gui></guiseq> para abrir o asistente de proxectos.

Packit 1470ea
    </item>
Packit 1470ea
    <item>
Packit 1470ea
    

Choose <gui>GTK+ (Simple)</gui> from the <gui>C</gui> tab, click <gui>Continue</gui>, and fill out your details on the next few pages. Use <file>guitar-tuner</file> as project name and directory.

Packit 1470ea
   	</item>
Packit 1470ea
    <item>
Packit 1470ea
    

Make sure that <gui>Configure external packages</gui> is switched <gui>ON</gui>. On the next page, select

Packit 1470ea
       gstreamer-0.10 from the list to include the GStreamer library in your project.

Packit 1470ea
    </item>
Packit 1470ea
    <item>
Packit 1470ea
    

Prema <gui>Aplicar</gui> para crear o proxecto. Abra <file>src/main.c</file> desde as lapelas <gui>Proxecto</gui> ou <gui>Ficheiro</gui>. Debería ver algún código que comeza coas liñas:

Packit 1470ea
    
Packit 1470ea
#include <config.h>
Packit 1470ea
#include <gtk/gtk.h>]]>
Packit 1470ea
    </item>
Packit 1470ea
  </steps>
Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="build">
Packit 1470ea
  <title>Construír o código por primeira vez</title>
Packit 1470ea
  

C is a rather verbose language, so don't be surprised that the file contains quite a lot of code. Most of it is template code. It 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:

Packit 1470ea
Packit 1470ea
  <list>
Packit 1470ea
  <item>
Packit 1470ea
    

The three #include lines at the top include the config (useful autoconf build defines), gtk (user interface) and gi18n (internationalization) libraries. Functions from these libraries are used in the rest of the code.

Packit 1470ea
   </item>
Packit 1470ea
   <item>
Packit 1470ea
    

The create_window function creates a new window by opening a GtkBuilder file (<file>src/guitar-tuner.ui</file>, 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.

Packit 1470ea
    

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.

Packit 1470ea
   </item>
Packit 1470ea
   <item>
Packit 1470ea
    

The main function is run by default when you start a C application. It calls a few functions which 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).

Packit 1470ea
   </item>
Packit 1470ea
   <item>
Packit 1470ea
    

The ENABLE_NLS conditional definition sets up gettext, which is a framework for translating applications. These functions specify how translation tools should handle your app when you run them.

Packit 1470ea
   </item>
Packit 1470ea
  </list>
Packit 1470ea
Packit 1470ea
  

This code is ready to be used, so you can compile it by clicking <guiseq><gui>Build</gui><gui>Build Project</gui></guiseq> (or press <keyseq><key>Shift</key><key>F7</key></keyseq>).

Packit 1470ea
  

Prema <gui>Executar</gui> na seguinte xanela que aparece para configurar a compilación de depuración. Só precisa facer isto unha vez para a primeira compilación.

Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="ui">
Packit 1470ea
  <title>Crear a interface de usuario</title>
Packit 1470ea
  

A description of the user interface (UI) is contained in the GtkBuilder file. To edit the user interface, open <file>src/guitar_tuner.ui</file>. 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.

Packit 1470ea
  

Packit 1470ea
  

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

Packit 1470ea
Packit 1470ea
<media type="image" mime="image/png" src="media/guitar-tuner-glade.png"/>
Packit 1470ea
Packit 1470ea
  <steps>
Packit 1470ea
   <item>
Packit 1470ea
   

Select a <gui>GtkButtonBox</gui> from the <gui>Container</gui> section of the <gui>Palette</gui> on the right and put it into the window. In the <gui>Properties</gui> pane, set the number of elements to 6 (for the

Packit 1470ea
six strings) and the orientation to vertical.

Packit 1470ea
   </item>
Packit 1470ea
   <item>
Packit 1470ea
    

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

Packit 1470ea
   </item>
Packit 1470ea
   <item>
Packit 1470ea
    

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

Packit 1470ea
    </item>
Packit 1470ea
    <item>
Packit 1470ea
     

Switch to the <gui>Signals</gui> tab (inside the <gui>Widgets</gui> 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 on_button_clicked in the <gui>Handler</gui> column and press <key>Return</key>.

Packit 1470ea
    </item>
Packit 1470ea
    <item>
Packit 1470ea
    

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

Packit 1470ea
    </item>
Packit 1470ea
    <item>
Packit 1470ea
    

Garde o deseño da IU (premendo <guiseq><gui>Ficheiro</gui><gui>Gardar</gui></guiseq>) e déixeo aberto.

Packit 1470ea
    </item>
Packit 1470ea
  </steps>
Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="signal">
Packit 1470ea
  <title>Crear o manexador de sinais</title>
Packit 1470ea
  

In the UI designer, you made it so that all of the buttons will call the same function, <gui>on_button_clicked</gui>, when they are clicked. We need to add that function in the source file.

Packit 1470ea
  

To do this, open <file>main.c</file> while the user interface file is still open. Switch to the <gui>Signals</gui> tab, which you already used to set the signal name. Now take the row where you set the

Packit 1470ea
<gui>clicked</gui> signal and drag it into to the source file at a
Packit 1470ea
position that is outside any function. The following code will be added to your source file:

Packit 1470ea
Packit 1470ea
void on_button_clicked (GtkWidget* button, gpointer user_data)
Packit 1470ea
{
Packit 1470ea
Packit 1470ea
}]]>
Packit 1470ea
  

This signal handler has two arguments: a pointer to the GtkWidget that called the function (in our case, always a GtkButton), and a pointer to some "user data" that you can define, but which we won't be using here. (You can set the user data by calling gtk_builder_connect_signals; it is normally used to pass a pointer to a data structure that you might need to access inside the signal handler.)

Packit 1470ea
  

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

Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="gstreamer">
Packit 1470ea
  <title>Tuberías de GStreamer</title>
Packit 1470ea
  

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.

Packit 1470ea
  

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.

Packit 1470ea
  

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.

Packit 1470ea
  <media type="image" mime="image/png" src="media/guitar-tuner-pipeline.png">
Packit 1470ea
    

Un exemplo de tubería de GStreamer.

Packit 1470ea
  </media>
Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="pipeline">
Packit 1470ea
  <title>Configurar a tubería</title>
Packit 1470ea
  

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.

Packit 1470ea
Packit 1470ea
  

Insert the following line into <file>main.c</file>, just below the ]]> line:

Packit 1470ea
  ]]>
Packit 1470ea
  

Isto inclúe a biblioteca GSTreamer. Tamén precisa unha liña para inicializar GStreamer; poña a seguinte liña de código antes da chamada gtk_init na función main:

Packit 1470ea
  
Packit 1470ea
  

Despois, copie a seguinte función en <file>main.c</file> enriba da función on_button_clicked baleira:

Packit 1470ea
  
Packit 1470ea
play_sound (gdouble frequency)
Packit 1470ea
{
Packit 1470ea
	GstElement *source, *sink;
Packit 1470ea
	GstElement *pipeline;
Packit 1470ea
Packit 1470ea
	pipeline = gst_pipeline_new ("note");
Packit 1470ea
	source   = gst_element_factory_make ("audiotestsrc",
Packit 1470ea
	                                     "source");
Packit 1470ea
	sink     = gst_element_factory_make ("autoaudiosink",
Packit 1470ea
	                                     "output");
Packit 1470ea
Packit 1470ea
	/* set frequency */
Packit 1470ea
	g_object_set (source, "freq", frequency, NULL);
Packit 1470ea
Packit 1470ea
	gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
Packit 1470ea
	gst_element_link (source, sink);
Packit 1470ea
Packit 1470ea
	gst_element_set_state (pipeline, GST_STATE_PLAYING);
Packit 1470ea
Packit 1470ea
	/* stop it after 500ms */
Packit 1470ea
	g_timeout_add (LENGTH, (GSourceFunc) pipeline_stop, pipeline);
Packit 1470ea
}]]>
Packit 1470ea
Packit 1470ea
  <steps>
Packit 1470ea
    <item>
Packit 1470ea
    

The first five lines create source and sink GStreamer elements (GstElement), and a pipeline element (which will be used as a container for the other two elements). 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).

Packit 1470ea
    </item>
Packit 1470ea
    <item>
Packit 1470ea
    

A chamada a g_object_set estabelecer a propiedade freq do elemento orixe a frequency, a cal se pasa como un argumento á función play_sound. Isto só é a frecuencia da nota en Hertz, algunhas das frecuencias máis útiles definiranse máis tarde.

Packit 1470ea
    </item>
Packit 1470ea
    <item>
Packit 1470ea
    

gst_bin_add_many puts the source and sink into the pipeline. The pipeline is a GstBin, 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 arguments to gst_bin_add_many.

Packit 1470ea
    </item>
Packit 1470ea
    <item>
Packit 1470ea
    

Next, gst_element_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). gst_element_set_state is then used to start playback, by setting the state of the pipeline to playing (GST_STATE_PLAYING).

Packit 1470ea
    </item>
Packit 1470ea
  </steps>
Packit 1470ea
Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="stop">
Packit 1470ea
  <title>Deter a reprodución</title>
Packit 1470ea
  

We don't want to play an annoying tone forever, so the last thing play_sound does is to call g_timeout_add. This sets a timeout for stopping the sound; it waits for LENGTH milliseconds before calling the function pipeline_stop, and will keep calling it until pipeline_stop returns FALSE.

Packit 1470ea
  

Agora, escríbese o código da función pipeline_stop, chamada por g_timeout_add. Inserte o código seguinte enriba da función play_sound:

Packit 1470ea
  
Packit 1470ea
#define LENGTH 500 /* Length of playing in ms */
Packit 1470ea
Packit 1470ea
static gboolean
Packit 1470ea
pipeline_stop (GstElement* pipeline)
Packit 1470ea
{
Packit 1470ea
	gst_element_set_state (pipeline, GST_STATE_NULL);
Packit 1470ea
	g_object_unref (pipeline);
Packit 1470ea
Packit 1470ea
	return FALSE;
Packit 1470ea
}]]>
Packit 1470ea
  

The call to gst_element_set_state stops the playback of the pipeline and g_object_unref unreferences the pipeline, destroying it and freeing its memory.

Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="tones">
Packit 1470ea
  <title>Definir os tonos</title>
Packit 1470ea
  

Quérese reproducir o son correcto cando un usuario preme un botón. En primeiro lugar, precísase coñecer as frecuencias das seis cordas da guitarra, que están definidas (ao principio de <file>main.c</file>) da seguinte maneira:

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

Now to 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:

Packit 1470ea
  
Packit 1470ea
/* Callback for the buttons */
Packit 1470ea
void on_button_clicked (GtkButton* button,
Packit 1470ea
                        gpointer user_data)
Packit 1470ea
{
Packit 1470ea
	const gchar* text = gtk_button_get_label (button);
Packit 1470ea
Packit 1470ea
	if (g_str_equal (text, _("E")))
Packit 1470ea
	    play_sound (NOTE_E);
Packit 1470ea
	else if (g_str_equal (text, _("A")))
Packit 1470ea
	    play_sound (NOTE_A);
Packit 1470ea
	else if (g_str_equal (text, _("G")))
Packit 1470ea
	    play_sound (NOTE_G);
Packit 1470ea
	else if (g_str_equal (text, _("D")))
Packit 1470ea
	    play_sound (NOTE_D);
Packit 1470ea
	else if (g_str_equal (text, _("B")))
Packit 1470ea
	    play_sound (NOTE_B);
Packit 1470ea
	else if (g_str_equal (text, _("e")))
Packit 1470ea
	    play_sound (NOTE_e);
Packit 1470ea
}
Packit 1470ea
]]>
Packit 1470ea
  

A pointer to the GtkButton that was clicked is passed as an argument (button) to on_button_clicked. We can get the text of that button using gtk_button_get_label.

Packit 1470ea
  

The text is then compared to the notes that we have using g_str_equal, and play_sound is called with the frequency appropriate for that note. This plays the tone; we have a working guitar tuner!

Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="run">
Packit 1470ea
  <title>Construír e executar o aplicativo</title>
Packit 1470ea
  

All of the code should now be ready to go. Click <guiseq><gui>Build</gui><gui>Build Project</gui></guiseq> to build everything again, and then <guiseq><gui>Run</gui><gui>Execute</gui></guiseq> to start the application.

Packit 1470ea
  

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

Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="impl">
Packit 1470ea
 <title>Implementación de referencia</title>
Packit 1470ea
 

Se ten problemas ao executar este titorial compare o seu código con este <link href="guitar-tuner/guitar-tuner.c">código de referencia</link>.

Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
<section id="next">
Packit 1470ea
  <title>Seguintes pasos</title>
Packit 1470ea
  

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

Packit 1470ea
  <list>
Packit 1470ea
   <item>
Packit 1470ea
   

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

Packit 1470ea
   </item>
Packit 1470ea
   <item>
Packit 1470ea
   

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

Packit 1470ea
   

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 <link href="http://gstreamer.freedesktop.org/documentation/plugins.html">decoder and demuxer</link> GStreamer elements based on the file format of your recorded sounds — MP3s use different elements to Ogg Vorbis files, for example.

Packit 1470ea
   

You might need to connect the elements in more complicated ways too. This could involve using <link href="http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/chapter-intro-basics.html">GStreamer concepts</link> that we didn't cover in this tutorial, such as <link href="http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/section-intro-basics-pads.html">pads</link>. You may also find the <cmd>gst-inspect</cmd> command useful.

Packit 1470ea
   </item>
Packit 1470ea
   <item>
Packit 1470ea
   

Analizar automaticamente as notas que toca o músico.

Packit 1470ea
   

You could connect a microphone and record sounds from it using an <link href="http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-good-plugins/html/gst-plugins-good-plugins-autoaudiosrc.html">input source</link>. Perhaps some form of <link href="http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-good-plugins/html/gst-plugins-good-plugins-plugin-spectrum.html">spectrum analysis</link> would allow you to figure out what notes are being played?

Packit 1470ea
   </item>
Packit 1470ea
  </list>
Packit 1470ea
</section>
Packit 1470ea
Packit 1470ea
</page>