Subscribe to Linux Format magazine - the number 1 source for Linux tutorials, reviews and news!

Don't miss: Get a lifetime of Linux learning for under $110

Code Project: create a media player

Code

Amarok is a great music player for KDE when judged by both its capabilities and its size. But it's hardly a quick point and click music player - it takes several clicks and some careful GUI navigation to listen to your music collection and that takes a toll on both your CPU and your head. We're going to offer an alternative by building the most simple and straightforward music player we could think of.

We're going to take some inspiration from Apple's new iPod Shuffle, and only offer the bare minimum of controls. One button for selecting your music, another button for play and pause, and a third button for skipping to the next track. For most people, and most uses, these are the only controls you need, and it makes a refreshing contrast to the bloated frippery of players like Amarok.

The build environment

Before we can get started with any programming, we have to satisfy several requirements for our player. The first is Qt 4.5, and this needs to be installed along with its associated development library. The development library contains the header files we import into our own projects so we can use Qt functionality outside of our own application.

If your distribution recent enough, such as Ubuntu Jaunty, then you'll find Qt 4.5 is already included. If not, you're going to have to try it to track it down for yourself. Nokia provides packages which are easy enough to install, and these can sit alongside your current version of Qt, or you could try updating your distribution.

Beginners will also need a working development environment. You can test whether one is installed or not by typing 'make' on the command line. If the only output you get is a complaint that the command can't be found, you'll need to install the GNU series of build tools through your distribution's package manager. If you're using Ubuntu, for instance, just install the build-essentials package. This includes fundamental programming tools such as the GCC compiler suite, the 'make' build system and the GDB debugger. Most other distributions should include the build environment with a default installation, but you should be able to find a similarly named meta-package if not.

Qt 4.5 includes the Creator IDE, but Kubuntu users need to install this manually as a separate package. Audio playback on Linux, and most other operating systems for that matter, can be a minefield as the programmer tries to guess the hardware and software layers being used. Fortunately, Qt has one of the best solutions we've seen and that's to integrate the fantastic Phonon framework from KDE. Phonon sits on top of whatever audio drivers and routing layer you're using, and provides an interface between your programming and the complicated part of guess the configuration. It makes playing audio files relatively easy, and completely cross platform.

You will need to install Phonon and its development library. Along with several other dependencies, it will probably also require GStreamer, which is the backend of choice for most Linux distributions. Phonon will talk to GStreamer directly, while we'll only talk to Phonon.

Qt Creator is usually part of the standard Qt 4.5 installation, but Ubuntu users will need to grab the package manually

Qt Creator is usually part of the standard Qt 4.5 installation, but Ubuntu users will need to grab the package manually.

Getting started

Now that you've got all the software installed, the next step is to get stuck in. Launch Creator, using either the launch menu or by typing 'qtcreator' on the command line. The application starts with a helpful wizard screen from which you can normal load previous projects or find out more about Qt. To start a new project, click on File > New from the menu.

This will launch the new project wizard, and your first tasks is to select the kind of project you want to create. Under the 'Projects' title, look for 'Qt4 GUI Application' and click on OK. You now need to enter a name for the project and choose a folder where the project directory will be created.

The next window is the module selection page. This is where you select which additional components you want to include in your project. The two core modules which are used with every Qt project are already selected, QtCore and QtGUI, and you need to select any non-core components you're likely to use. If you're developing a browser, for example, you'd want to include the WebKit module. We need just need to select Phonon, as we're using this to interact with the audio layer of the operating system.

After selecting Phonon, the final page will list the default names for files and classes in your project, and you might as well keep these at their default values. Click on 'Finish' to end the wizard, create the folder and the file templates, all of which will be loaded into Creator automatically.

If you've used any form of IDE before, Qt Creator is going to feel familiar. There are several different modes of operation. The one you'll be spending the most time in initially is the GUI designer, and this is launched automatically when you click on a file ending with 'ui' - a User Interface designer file. One of these files has already been added to your project, and you can see this along with four other files in the projects panel to the left of the main display.

The two files ending with '.cpp' hold the source code for our project, although 'main.cpp' is really just used to launch the application, so we'll only be editing 'mainwindow.cpp'. This has a corresponding header file, which is used to describe the object and development files we using in mainwindow.cpp. Finally, there's the '.pro' file, which shouldn't really be visible, as this is the project file used by Qt and Creator to build your project.

If you forget to include the modules you need from Creator's startup wizard, you can add them yourself to your project's '.pro' file.

If you forget to include the modules you need from Creator's startup wizard, you can add them yourself to your project's '.pro' file.

GUI design

Double click on the 'ui' file in the project pane. The embedded designer view appears. If you've done any Qt programming in the past, you'll recognise the layout from the old Qt Designer application, which has been embedded into Creator more or less in-tact. No one would ever accuse Designer of being easy to use, but it is powerful and allows you to create dynamic and expansive Qt GUI applications easier than using a pure programming approach.

Thanks to its inclusion in Creator, you can now switch back and forth between GUI modifications and programming with ease, and this can make a dramatic improvement to development speed.

Our application has only a very simple GUI. Scroll down the list of available widgets and drag three separate 'Push Buttons' from the Buttons section onto the grey application canvas. Alternatively, drag a single button and copy/paste two more. These buttons are the only control we'll have over our application. One will be used to select the files to play, another will be used to play and pause the music and the final button will be used to skip to the next track. That's the complete opposite of Amarok's 20 plus buttons.

We now need to give the user some indication of what each button does. We can do this by either double-clicking on each button and using a word to describe the function, or using an icon in its place. For the icon, select the button and look for the value panel in the lower left quarter of Creator's screen. This holds most properties for the currently selected widget in your GUI. Scroll down until you see 'icon' in the list.

Select the item, and click on the '...' button to the right to open a file requester. You can now select and use any image file you want for the button, but with a standard KDE installation, you might want to take a look at the '/usr/share/icons/oxygen/32x32/actions' directory. This contains icons for most common functions, and the 'media-skip-forward', 'media-skip-backward' and 'media-playback-start' are particularly well suited to the functions we're going to implement.

We now need to arrange these three buttons vertically, and restrict the size of the window. Hold down the left Ctrl key, and select each button individually. They should all now have the scale border highlighted. Now click on the 'Lay Out Vertically' button in the toolbar (or the right click menu). This will tie the three buttons together, and align them on top of one another.

Now click on the application background and select 'Layout in a Grid' . This will stretch the three buttons to fill all the available space, and this should happen regardless of the window size. But we also want to make the window small. Either drag the bottom left anchor point of the background until you've created a small rectangle, or use the 'Geometry' section of the property panel to type in the size manually. We ended up with a window 115 pixels wide and 162 pixels tall. You can choose to fix the size of the window too, by setting the maximum Horizontal and Vertical parameters to the same numbers used by the minimum size.

Building a GUI using Creator can take a little getting used to, but you essentially just drag widgets from the left and lock them into place with the layout engine

Building a GUI using Creator can take a little getting used to, but you essentially just drag widgets from the left and lock them into place with the layout engine.

Wiring it up

The great thing about the Designer view in Creator is that you can also create a chunk of the programme logic without writing a single line of code. This is thanks to Qt's Signals and Slots mechanism. They're basically a form of remote procedure calls, but they're handled automatically by Qt. In a web browser, for example your browser interface might 'emit' a signal to refresh the display when the user clicks on the refresh button.

In our application, we want to emit three signals that correspond to our three buttons - play/pause, skip forward and add files. We need to write the functions to handle each case, but from Designer, we can configuration our application to call a specific function when a specific action occurs. To do this, we need to switch to the 'Signals/Slots' editing mode, either by using the icon in the toolbar or by pressing F4.

The principle behind this mode is that you first select the widget that will emit the signal, and drag the cursor to the widget who's slot will act on the signal. With a web browser, for instance, you'd make a connection between the reload button and the WebKit browser widget. You could then refresh the display without writing a line of code. When there's no specific widget that's going to receive the signal, as in our case, then you drag the connection onto the window background. We need to do this for each of our buttons.

The first time you drop the connection onto the background, a 'Configure Connection' window will appear. Click on the 'Edit' button for the panel on the right. This will open another window listing the signals and slots for the MainWindowClass, which is the background of our application. We need to add three Slots to accept out three different Signals, and these slots will directly correspond to functions we're going to write in the main application.

Press the '+' symbol, and add 'playPause()', 'addFiles()' and 'nextFile()'. Click on OK, and using the Edit Signals/Slots mode, connect the 'clicked()' Signal from each button to the correct Slot we've just created. For the 'Add Files' button, for example, connect 'clicked()' to 'addFiles(). You get an overview of each connection in the editor window, as blue lines and squares are used to indicate where signals from certain widgets are attached to various slots. This is the last piece of GUI editing we need to do, so save your progress and move on to the next step.

Getting your head around signals and slots is the most important aspect to Qt programming

Getting your head around signals and slots is the most important aspect to Qt programming.

Coding: mainwindow.h

We've now created the framework for our application, we just add the functionality. Click on 'mainwindow.h' and add the following lines to the top:

#include <QList>
#include <QFileDialog>
#include <QDesktopServices>
#include <Phonon>

These import the Qt functions we're going to use on our code sections through their header files. We now need to add our slots, as defined by the ui file we were editing earlier. Just below the '~MainWindow();' line in the 'public:' section, add the following:

private slots:
    void playPause();
    void addFiles();
    void nextFile();
    void aboutToFinish();
    void finished();

You can see that these slots correspond to the names we created in the user-interface, and we've also added a couple more slots to deal with internal communication. Finally, add the following variables to the 'private:' section - we'll be using these within the main logic of the applications:

    QList<Phonon::MediaSource> sources;
    Phonon::MediaObject *mediaObject;
    Phonon::AudioOutput *audioOutput;
    Phonon::MediaObject *metaInformationResolver;

Coding: mainwindow.cpp

The first thing we now need to do is initialise Phonon and setup the internal signals and slots. This can be accomplished from the standard initialising method, MainWindow::MainWindow. If you take a look inside this method, you'll see that the GUI for application is launched by the 'ui->setupUi(this);' line. This means that we need to put our pre-launch code just before. We'll start with setting up Phonon:

     audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this);
     mediaObject = new Phonon::MediaObject(this);
     metaInformationResolver = new Phonon::MediaObject(this);
     Phonon::createPath(mediaObject, audioOutput);

The audioOutput object we're creating is known as an audio sink in Phonon terms. It's the part of the layer that communicates with the audio driver directly, and acts as a virtual audio device for the MediaObject, which sits on top of this and adds niceties such as pause, play and rewind. Incidentally, the MusicCategory is not absolutely required, but it could be used for a future developments, such as a KDE equaliser that changes automatically according to what is being listened to.

We'll use the 'metaInformationResolver' to point to the current audio file, while the final line makes the connection between the sink and the media object. Phonon uses something called a 'graph' framework, which means that objects are like nodes on a graph and they need to be connected to create a stream. This is what we do in the final line of the above chunk of code.

Now add the following new function that deals with the playPause() slot:

void MainWindow::playPause()
{
    switch (mediaObject->state()){
        case Phonon::PlayingState:
            mediaObject->pause();
            ui->pushButtonPlay->setChecked(false);
            break;
        case Phonon::PausedState:
            mediaObject->play();
            break;
        case Phonon::StoppedState:
            mediaObject->play();
            break;
        case Phonon::LoadingState:
            ui->pushButtonPlay->setChecked(false);
            break;
    }
}

The above chunk of code should be straightforward enough to understand. We're using a Case statement to check the current state of playback. Phonon provides this for us, through the mediaObject. It's really nothing more than an integer, but each value represents a certain state. The LoadingState, for example, is returned if the application has first started and no files have been added to the playlist. Play and pause are handled by the media object, which will automatically continue from the current position after the 'mediaObject->pause();' command.

Use the auto-complete facility to find the functions you require while typing up the code in Creator

Use the auto-complete facility to find the functions you require while typing up the code in Creator.

We also try and give the user some feedback on the current state of playback. If the music is playing, then we indent the 'Play' button to make it look like it's pressed down. If the music is in any other state, then we'll lift the indentation to make it look like the button has popped out. For this to work, your play button needs to be called 'pushButtonPlay'. You can change its name from the Designer view by selecting the button and changing the 'objectName' value.

 void MainWindow::addFiles()
{
    QStringList files = QFileDialog::getOpenFileNames(this, tr("Select Music Files"),
        QDesktopServices::storageLocation(QDesktopServices::MusicLocation));

    ui->pushButtonPlay->setChecked(false);
    if (files.isEmpty())
        return;
    int index = sources.size();
    foreach (QString string, files) {
            Phonon::MediaSource source(string);
        	sources.append(source);
    }
    if (!sources.isEmpty()){
        metaInformationResolver->setCurrentSource(sources.at(index));
        mediaObject->setCurrentSource(metaInformationResolver->currentSource());

    }
}

Now we get to the Adding files section. Creating a file requester is almost automatic, and we drop the results into a list of strings. The 'QdesktopServices::MusicLocation' returns an operating system specific location where music files are typically held, and we use this as the default start point for the requester. We then append each music file that has been selected to the 'metaInformationResolver' we created earlier, and use this to tell the mediaObject which file to play next.

In between all this action, we do a little house keeping to make sure there are files in the queue, and pop up the play button if it happens to be in a playing state. Adding files will automatically stop playback.

void MainWindow::nextFile()
{

    int index = sources.indexOf(mediaObject->currentSource()) + 1;

    if (sources.size() > index) {
         mediaObject->stop();
         mediaObject->setCurrentSource(sources.at(index));
         mediaObject->play();
     }
}

void MainWindow::aboutToFinish()
{

    int index = sources.indexOf(mediaObject->currentSource()) + 1;

     if (sources.size() > index) {
         mediaObject->enqueue(sources.at(index));
     } else {
         ui->pushButtonPlay->setChecked(false);
     }

}

void MainWindow::finished()
{

         ui->pushButtonPlay->setChecked(false);

}
It might not look like much, but our music application bundles most of the facilities you're ever going to need from a music player

It might not look like much, but our music application bundles most of the facilities you're ever going to need from a music player.

Brought to you by the nice folks at Linux Format

Brought to you by the nice folks at Linux Format magazine

You should follow us on Identi.ca or Twitter


Your comments

Wow, good stuff!

Almost like you were responding to requests via Twitter? ;)

Nokia have a fantastic community site with plenty of Qt examples; here's a start:

http://www.forum.nokia.com/Tools_Docs_and_Code/Code_Examples/Qt.xhtml

(albeit biased towards Symbian but you get the idea, I think all are cross-platform)

The development environment is truly excellent too, plenty of support via the forums.

@spaceyjase

You want to see the huge pile of commissions we've sent out based around all the ideas folks submitted to the LXF blog...

Oh no!

Just what we need... ANOTHER 500 media players. Well, at least it's good practise.

*cough* Phonon audio sucks *cough*

And what's with the sudden move to <tt>-like text?

Something for the week.....

Will give this Hey oh go!!!!

Detailed begining and missing end...

Hi,

I followed the instructions and did the project.

So far problems:
- missing the article end, so actually how do you start/build/debug/solve problems???
-By default the QPushButton is not checkable, so it have to be enabled for the pushButtonPlay.
-It would have been nice to implement: if after one song is finished then it would not stop but continue automatically with the next one.
-Any explanation for the warnings?:

------------------------
WARNING: Phonon needs QCoreApplication::applicationName to be set to export audio output names through the DBUS interface
Qt Application(8849) Phonon::KdePlatformPlugin::createBackend: using backend: "Xine"
------------------------

Otherwise it is almost a good coding project article.

@Istvan

You're right about the ending. We should have mentioned that the project needed saving and building from within Creator.

As for debugging and solving problems, we had half a mind to cover that in a different project.

The error you describe is because Phonon is expecting the application to have a name defined through the Qt framework. This can be solved by adding:

a.setApplicationName( "Mu" );

to the 'main.cpp' file, just below the 'QApplication a(argc, argv)' line.

HELP11

For the icon, select the button and look for the value panel in the lower left quarter of Creator's screen. This holds most properties for the currently selected widget in your GUI. Scroll down until you see 'icon' in the list.

tHE RPEVIOUS DROVE ME NUTTZ FOR TWO HOURS LAST NIGHT. Please explain or help me figure out where the "value panel is" right or left??where is the list ....

HELP ....

DESPERATE!!!!

@coacharnold

Hello! The panel you’re looking for is actually in the lower right quarter of the Creator screen. It’s the one that lists all the properties for each of the widgets you click on. When you click on one of the buttons, it will reflect all of the values for that button, and you need to scroll down through the list of properties until you find ‘icon’. It should be listed under the QAbstractButton section. You then need to click on the field to the right of ‘icon’ and you should see the file requester button appear. Click on this and select an image.

Hope that helps!

Cant compile - Phonon:no such file or directory

Great tutorial. I cant compile even with the download code examples though since I get an error Phonon:no such file or directory.

I am using Arch linux what else do I need to install other than Qt, qtcreator. Phonon and Gstreamer and Xine are already installed.

thanks

Include

FYI - incase someone else is stuck

For some reason it works when i change #include <Phonon> to

#include <phonon>.

Permission denied

Great tutorial!
When I try to run in IDE, I get /bin/sh: : Permission denied.

It works when I run from command line.

Any ideas?

...You did give it

...You did give it executable rights...right?

Root

I was log in as root, so root should be able to do everything.

Phonon

for some reason I get "no such file or directory" for #include <Phonon> .
I'm using openSUSE 11.2 M7, and phonon-devel is installed..
Even tried changing it to "phonon"

phonon: no such directory or file

i used #include<Phonon> then the error i got Phonon: no such directory or file
then i changer it to #include<phonon> and facing the same problem again. Sombody please help me

C:/Documents and Settings/bhaskar.reddy/My Documents/qtmu/mainwi

hi i am new to QT i have just copied your code and pasted.

finally while building i am getting following errors:

C:/Documents and Settings/bhaskar.reddy/My Documents/qtmu/mainwindow.cpp:20: error: 'class Ui::MainWindow' has no member named 'pushButtonPlay'

C:/Documents and Settings/bhaskar.reddy/My Documents/qtmu/mainwindow.cpp:34: error: 'class Ui::MainWindow' has no member named 'pushButtonPlay'

C:/Documents and Settings/bhaskar.reddy/My Documents/qtmu/mainwindow.cpp:45: warning: enumeration value `BufferingState' not handled in switch

error collect2:

i am getting following error while building:
-1: error: collect2: ld returned 1 exit status

Thanks

Thanks for the great tutorial, it inspired me to write my own media player, see it at http://github.com/pymatio/MPED or http://sourceforge.net/projects/mped/

RE: phonon: no such directory or file

I had the same problem and it seems to be solved now:

I installed libphonon-dev libphonon4.
Probably one of them is enough.
Then I changed to #include <phonon>

Sudent

How were you able to add the background to your player ?? Pls elaborate..! I need to embed widgets over the background ~!

Thanks in advance .!!

No sound

I have implemented this and I get no sound, no errors. I am running ubuntu 9.10, any clues?

Post new comment

CAPTCHA
Fill in this captcha, or you shall be mocked mercilessly.
Username:   Password:
Create Account | About TuxRadar