Skip to content

Playing audio files

timijntema edited this page May 7, 2017 · 6 revisions

The next sections gives a detailed description on how to play audio files using a linux or windows. The provided code has been tested. This way of playing audio files dus not support Arduino, only machines with an windows, linux or Mac based os.

Libraries used

To get this to work you will need two libraries. The first one is portaudio. This provides a interface that plays whatever you put inside the buffer. The second one is called libsndfile. This provides the code to load audio files, get their channel count and much more.

Installation

The next section wil explain how to install both libraries on Windows, Mac and Linux. For all three you will first have to download the libraries from the websites mentioned in the previous section.

Windows

Installation instructions still unknown. However there are installation instructions for portaudio in the documentation of the library. For microsoft visual studio you should follow these instructions or alternatively you could use the second set of instructions. those are for ASIO support. The third set of instructions is for using mingw to compile the libraries.

Mac

After downloading the files, open terminal, navigate to the folder you downloaded the files to using cd and type the following commands. Make sure you change the filenames in the commands to the correct filenames. You will need to type your password for some of the commands.

tar xopf pa_stable_v190600_20161030.tar
cd portaudio
./configure && make && sudo make install
cd ..
tar xopf libsndfile-1.0.28.tar
cd libsndfile-1.0.28
./configure --prefix=$HOME/local
make && sudo make install

To use the libraries in a project with an IDE of your choosing. You wil have to add the libraries to the linker settings. This can be done by going into the project settings after creating the project. Finding the linker options. There should be a libraries option where you can add the following two libraries: libportaudio.a and libsndfile.a. After doing this you can get yourself a sound file you want to play and test the example code.

You might need to edit some environment variables if it still dus not work. This can be done by typing another line in the terminal window.

export PATH = $PATH:/usr/local/lib
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/local/lib

The installation instructions for libsndfile where found here.

Linux

For linux the installation procedure can be found here. This installation has not been tested. However it is very similar to the Mac osx installation procedure with a few minor differences.

First download the files. After this has been done a few commands have to be typed into a terminal window again. These commands also sometimes require your password. According to the installation instructions it is advised to run the first command to install a audio controller with better support. However this is not required.

sudo apt-get install libasound-dev

After this is done you should use cd to navigate to the folder you downloaded the files of portaudio and libsndfile to. After this you can run the following commands to install the two libraries. You will have to change the filenames to the names of the files you downloaded.

tar xopf pa_stable_v190600_20161030.tar
cd portaudio
./configure && make && sudo make install
cd ..
tar xopf libsndfile-1.0.28.tar
cd libsndfile-1.0.28
./configure --prefix=$HOME/local
make && sudo make install

After this you should try the example code. If you use the command line to compile the c++ code, you should link the library's as well. An example of this can be found here. The stack overflow page only lists the code for linking libsndfile with the package, but adding -lportaudio to the line of code just after -lsndfile should compile both. This however has not been tested.

If the code still dus not compile, you might need to change the environment variables. This can be done as follows.

export PATH = $PATH:/usr/local/lib
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/local/lib

Example code

The following example loads a sound file named sound.wav using sndfile and then plays it using the port audio library. The code was found here.

#include <iostream>

#include "sndfile.h"
#include "portaudio.h"

SF_INFO fileInfo;

static int outputCallBack( const void *inputBuffer, void *outputBuffer,
                           unsigned long framesPerBuffer,
                           const PaStreamCallbackTimeInfo* timeInfo,
                           PaStreamCallbackFlags statusFlags,
                           void *userData )
{
    SNDFILE * file = (SNDFILE *)userData;

    sf_read_short(file, (short *)outputBuffer, framesPerBuffer*fileInfo.channels);
    return paContinue;
}

void error_check(PaError & err){
    if (err != paNoError){
        std::cerr << "The following error occured: " << Pa_GetErrorText(err) << '\n';
        exit(0);
    }
}

void endCallBack(void * data){
    std::cout << "Finished playing\n";
}

int main(int argc, char **argv){
    SNDFILE * file;

    if (!(file = sf_open("sound.wav", SFM_READ, &fileInfo))){
        std::cerr << "Failed to open file\n";
        exit(0);
    }

    PaStream *stream;
    PaStreamParameters outputParameters;
    PaError err = Pa_Initialize();
    error_check(err);


    outputParameters.device = Pa_GetDefaultOutputDevice();
    if (outputParameters.device == paNoDevice){
        std::cerr << "No output device\n";
        exit(0);
    }

    outputParameters.channelCount = fileInfo.channels;
    outputParameters.sampleFormat = paInt16;
    outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
    outputParameters.hostApiSpecificStreamInfo = NULL;

    /* Open an audio I/O stream. */
    err = Pa_OpenStream(
                        &stream,
                        NULL,
                        &outputParameters,
                        fileInfo.samplerate,
                        paFramesPerBufferUnspecified,
                        paClipOff,
                        outputCallBack,
                        file
                        );
    error_check(err);

    err = Pa_SetStreamFinishedCallback(stream, &endCallBack);
    error_check(err);

    err = Pa_StartStream( stream );
    error_check(err);

    /* Sleep for several seconds. */
    Pa_Sleep(60*1000);

    err = Pa_StopStream( stream );
    error_check(err);

    err = Pa_CloseStream( stream );

    sf_close(file);

    err = Pa_Terminate();
    error_check(err);

	return 0;
}

Callback functions

In this code there is a outputcallback function. This function is used to put information into the buffer so portaudio can play it. In our example we grab a certain amount of information from the audio file and put it in the buffer. This proces can be reversed where you use the input from a microphone and put it in a file. This can be done in the outputcallback function. Most likely you will have to use the sf_write_short() function to write something from the input parameter of the function into a file opened with sndfile. The parameters of the outputcallback function are predetermined and can be found here. There is also a endcallback function. This gets executed after audio playback is finished and could be used to execute something after the file has stopped playing.

Error checking

In the example a lot of error checking has already been done through the error_check function. This function checks the received error code against a paNoError constant. If there is an error, the program gets shut down and a error message will be displayed that corresponds to the error you got.

Delays

The sound you are trying to play, will not play unless you ad a delay with the length of the sound file. In the example the sound should play for about a minute (60 seconds *1000 milliseconds = 60 000 milliseconds). This should be changed to the length of the audio file you want to play.

closing streams and files

Whenever you work with files, you should never forget to close them. In the example this is done by calling the sf_close() function with the object name as a parameter. The audio stream itself should also be closed. This is done using the following three lines:

err = Pa_StopStream( stream );
err = Pa_CloseStream( stream );
err = Pa_Terminate();

Supported formats

So far .wav has been tested using the example code. A more detailed list of supported formats can be found here.

Clone this wiki locally