Qt – Playing Audio using the BASS library

The BASS library is an audio library which provides a lot of functions, most importantly the playback of various types of audio files. The BASS library website (www.un4seen.com) contains a lot of code samples for various functions. In this tutorial, a very basic and efficient way of playing audio files is presented using a BASS Channel running on a separate thread and a basic explanation of the BASS library functions used for playing the audio.

As a general rule, all CPU intensive tasks other than the UI updates are performed on separate threads. The AudioThread class derived from QThread handles the task of creating a BASS channel and playing the audio.

In this tutorial, we will learn how to create a BASS audio channel, and then load and play audio files in it.

We start off by creating a new QtGui project and add a new class AudioThread with QThread as its base class. Include the header file bass.h. Create slots for Play, Pause, Resume, Stop and more as can be seen in the code below. Also we need to create a template for a function syncFunc which will be used to ascertain the end of the audio file. The code for the AudioThread class along with its header is given below.

We need to add the path of the header files as well as the library path in the project (*.pro) file.

INCLUDEPATH += "C:\QtProjects\audio_tutorial\bass24\c"

LIBS += -L"C:\QtProjects\audio_tutorial\bass24\c" \
        -lbass

After building the program in Windows, we could place the bass.dll in the executable folder or copy the bass.dll to the system32 folder or even add the path to bass.dll to the PATH environment variable. Any one will do but for the time being we will place the dll in the executable folder right beside the exe file.

All the detailed description about the BASS library functions have been very nicely explained in the BASS lib documentation. We’ll still look at some of the basic and tricky functions as it’ll help in understanding the code quickly. The BASS_Init function initializes an output device (in this case the default output device as the first parameter is 0) with a sampling rate of 44100 Hz (second parameter). The other parameters are all set to default values.The BASS_StreamCreateFile creates a sample stream from audio files like MP3 or WAV. The filepath (second parameter) is passed and all the other parameters are set to the default values. This function returns the handle to the audio stream. The BASS_STREAM_AUTOFREE essentially frees the stream when the audio playback ends. If the stream creation does not occur and the file is a MOD file, then the BASS_MusicLoad tries to load the MOD file and return the handle to the music. The BASS_ChannelPlay starts the playback of the audio. The BASS_ChannelSetSync sets up a synchronizer on a stream or MOD music. Here it is used to effectively identify the end of the audio playback using the BASS_SYNC_END flag and to trigger the syncFunc at the end of audio playback. All the other BASS lib functions are more or less self explanatory.

After this, we set up two signals to update the UI thread regarding the status of the audio being played, i.e., if it has reached the end of playback and to intimate the UI thread of the current track position of the audio file.

Finally, all that is required is a nice looking GUI (go crazy) and signal/slot connections to and from the AudioThread class.

Qt code for the AudioThread class

// audiothread.h

#ifndef AUDIOTHREAD_H
#define AUDIOTHREAD_H

#include <QThread>
#include <QDebug>
#include <QTimer>
#include "bass24/c/bass.h"

void __stdcall syncFunc(HSYNC handle, DWORD channel, DWORD data, void *user);

class AudioThread : public QThread
{
    Q_OBJECT
public:
    explicit AudioThread(QObject *parent = 0);
    bool playing;
    void run();
private:
    unsigned long chan;
    QTimer *t;
signals:
    void endOfPlayback();
    void curPos(double Position, double Total);
public slots:
    void play(QString filepath);
    void pause();
    void resume();
    void stop();
    void signalUpdate();
    void changePosition(int position);
};
#endif // AUDIOTHREAD_H
// audiothread.cpp

#include "audiothread.h"

bool endOfMusic;

void __stdcall syncFunc(HSYNC handle, DWORD channel, DWORD data, void *user)
{
    BASS_ChannelRemoveSync(channel, handle);
    BASS_ChannelStop(channel);
    qDebug() << "End of playback!";
    endOfMusic = true;
}

AudioThread::AudioThread(QObject *parent) :
    QThread(parent)
{
    if (!BASS_Init(-1, 44100, 0, NULL, NULL))
        qDebug() << "Cannot initialize device";
    t = new QTimer(this);
    connect(t, SIGNAL(timeout()), this, SLOT(signalUpdate()));
    endOfMusic = true;
}

void AudioThread::play(QString filename)
{
    BASS_ChannelStop(chan);
    if (!(chan = BASS_StreamCreateFile(false, filename.toLatin1(), 0, 0, BASS_SAMPLE_LOOP))
        && !(chan = BASS_MusicLoad(false, filename.toLatin1(), 0, 0, BASS_MUSIC_RAMP | BASS_SAMPLE_LOOP, 1)))
            qDebug() << "Can't play file";
    else
    {
        endOfMusic = false;
        BASS_ChannelPlay(chan, true);
        t->start(100);
        BASS_ChannelSetSync(chan, BASS_SYNC_END, 0, &syncFunc, 0);
        playing = true;
    }
}

void AudioThread::pause()
{
    BASS_ChannelPause(chan);
    t->stop();
    playing = false;
}

void AudioThread::resume()
{
    if (!BASS_ChannelPlay(chan, false))
        qDebug() << "Error resuming";
    else
    {
        t->start(98);
        playing = true;
    }
}

void AudioThread::stop()
{
    BASS_ChannelStop(chan);
    t->stop();
    playing = false;
}

void AudioThread::signalUpdate()
{
    if (endOfMusic == false)
    {
        emit curPos(BASS_ChannelBytes2Seconds(chan, BASS_ChannelGetPosition(chan, BASS_POS_BYTE)),
                    BASS_ChannelBytes2Seconds(chan, BASS_ChannelGetLength(chan, BASS_POS_BYTE)));
    }
    else
    {
        emit endOfPlayback();
        playing = false;
    }
}

void AudioThread::changePosition(int position)
{
    BASS_ChannelSetPosition(chan, BASS_ChannelSeconds2Bytes(chan, position), BASS_POS_BYTE);
}

void AudioThread::run()
{
    while (1);
}

Leave a Reply

Your email address will not be published. Required fields are marked *