Använd GTK+ och GStreamer för att bygga ett enkelt gitarrstämmarprogram för GNOME. Visar hur gränssnittsdesignern används.
Dokumentationsprojekt för GNOME
gnome-doc-list@gnome.org
Johannes Schmid
jhs@gnome.org
Tiffany Antopolski
tiffany.antopolski@gmail.com
Marta Maria Casetti
mmcasetti@gmail.com 2013
Sebastian Rasmussen
sebras@gmail.com 2019
Anders Jonsson
anders.jonsson@norsjovallen.se 2021

# Gitarrstämmare

      In this tutorial you will create an application which plays tones that you can use to tune a guitar. You will learn how to:

        Konfigurerar ett grundläggande projekt med den integrerade utvecklingsmiljön Anjuta.
        Skapar ett enkelt grafiskt användargränssnitt med Anjutas användargränssnittsdesigner.
        Använder GStreamer-biblioteket för att spela upp ljud.

      Du behöver följande för att kunna följa denna handledning:

        Grundläggande kunskap i programmeringsspråket Vala.
        En installerad kopia av Anjuta.

# Skapa ett projekt i Anjuta

Innan du börjar koda kommer du behöva konfigurera ett nytt projekt i Anjuta. Detta kommer skapa alla filer som du behöver för att bygga och köra koden senare. Det är också användbart för att hålla allting samlat.

Starta Anjuta och klicka på Skapa ett nytt projekt eller Arkiv Ny Projekt för att öppna projektguiden.

Klicka på fliken Vala och välj GTK+ (enkel). Klicka på Fortsätt och fyll i dina detaljer på de nästkommande sidorna. Använd guitar-tuner som projektnamn och katalog.

Säkerställ att Konfigurera externa paket är i läget PÅ. På nästa sida, välj gstreamer-0.10 från listan för att inkludera GStreamer-biblioteket i ditt projekt. Klicka på Fortsätt

Klicka på Verkställ så kommer projektet skapas åt dig. Öppna src/guitar_tuner.vala från flikarna Projekt eller Filer genom att dubbelklicka på den. Du bör se kod som börjar med raderna:

using GLib; using Gtk;

# Bygg koden för första gången

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

De två `using`-raderna importerar namnrymder så vi inte behöver namnge dem explicit.

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. This 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.

Att ansluta signaler är hur du definierar vad som händer när du trycker på en knapp, eller när någon annan händelse inträffar. Här anropas funktionen `on_destroy` (och avslutar programmet) när du stänger fönstret.

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). When you do this, a dialog will appear. Change the Configuration to Default and then click Execute to configure the build directory. You only need to do this once, for the first build.

# Skapa användargränssnittet

A description of the user interface (UI) is contained in the GtkBuilder file src/guitar_tuner.ui defined at the top of the class. To edit the user interface, open src/guitar_tuner.ui by double-clicking on it in the Project or Files section. This will switch to the interface designer. The design window is in the center; Widgets and the widget properties are on the right, and the Palette of available widgets is on the left.

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.

In the Palette tab, from the Containers section, select a Button Box (GtkButtonBox) by clicking on the icon. Then click on the design window in the center to place it into the window. A dialog will display where you can set the Number of items to 6. Then click Create.

You can also change the Number of elements and the Orientation in the General tab on the right.

Now, from the Control and Display section of the Palette choose a Button (GtkButton) by clicking on it. Place it into the first section of the GtkButtonBox by clicking in the first section.

While the button is still selected, scroll down in the General tab on the right to the Label property, and change it to E. This will be the low E guitar string.

The General tab is located in the Widgets section on the right.

Click on the Signals tab in the Widgets section on the right, 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 the Enter.

Repeat the above steps for the other buttons, adding the next 5 strings with the names A, D, G, B, and e.

Save the UI design (by clicking File Save) and keep it open.

# GStreamer-rörledningar

This section will show you how to create the code to produce sounds. GStreamer is GNOME's multimedia framework — you can use it for playing, recording, and processing video, audio, webcam streams and the like. Here, we'll be using it to produce single-frequency tones.

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.

Exempel på en GStreamer-rörledning.

# Konfigurera rörledningen

In this 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 args);

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.NULL); return false; }); time.attach(null); }

The first three 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 in 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.

# Skapa signalhanteraren

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, in the user interface file (guitar_tuner.ui) select one of the buttons by clicking on it, then open guitar_tuner.vala (by clicking on the tab in the center). Switch to the Signals tab on the right, which you used to set the signal name. Now take the row where you set the clicked signal and drag and drop it into to the source file at the beginning of the class. The following code will be added to your source file:

public void on_button_clicked (Gtk.Button sender) {

}

You can also just type the code at the beginning of the class instead of using the drag and drop.

Denna signalhanterare har endast ett argument: den `Gtk.Widget` som anropade funktionen (i vårt fall alltid en `Gtk.Button`).

# Definiera signalhanteraren

We want to play the correct sound when the user clicks a button. For this, we flesh out the signal handler which we defined above, `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 (329.63); 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!

# Bygg och kör programmet

All of the code should now be ready to go. Click Build Build Project to build everything again, and then Run Execute 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!

# Referensimplementation

Om du stöter på problem med handledningen kan du jämföra din kod med denna referenskod.

# Vidare läsning

För att lära dig mer om programmeringsspråket Vala kan du vilja ta en titt på Vala-handledningen och Valas API-dokumentation

# Nästa steg

Här är några idéer på hur du kan utöka denna enkla demonstration:

Have the program automatically cycle through the notes.

Make the program play recordings of real guitar strings being plucked.

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.

Automatically analyze notes that the user plays.

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?