A fast MATLAB executable (MEX) compilation of the PESQ measure

After having spent a good while looking for a working copy of the Perceptual Evaluation of Speech Quality measure implemented in MATLAB, I ended up using the readily available “wrapper” functions found on the MATLAB file exchange site. While these wrappers work a treat they are a bit slow as they require a call to the system to run the PESQ binary file. The PESQ binary file is compiled from the source code found on the ITU site for the PESQ standard. The source code is written in C which MATLAB supports for its MATLAB executable (MEX) compilations. That was what then prompt me to look for a MEX compiled version of the PESQ measure but to my surprise I couldn’t find any. I managed to get the ITU standards’ source code compiled and working in a MEX function and I will go through the few steps required to do it.

First thing’s first, we must head over to the ITU page for the PESQ standard and download the PESQ source code. In the ZIP file there is a second ZIP file under \Software, we are interested in the contents inside that second ZIP file under \P862_annex_A_2005_CD\source. Extract the source folder to a separate location where you can work on it with MATLAB. We are going to compile it using the MEX compiler function for MATLAB but it needs a few extra things added to it first so that MATLAB knows how to handle the source code and interface it to the MATLAB data types.

Open MATLAB and edit the pesqmain.c file (you can make a copy and rename it if you wish). MEX source code needs an entry point that MATLAB understands so we can’t use main() as the entry point anymore, however, we can still keep it for use as a function. MATLAB looks for an entry point called mexFunction() so we will have to add this to the code and we will do it just before the existing main() function:

#include "mex.h"
#include "matrix.h"
#include <stdlib.h>
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){
     //Entry point code goes here
}

Now, a neat way to make use of the existing code would be to rearrange and convert the MATLAB data types coming in to mexFunction() and then send them off as C data type equivalents to main() in a way it was expecting them. I did this by adding the following code to mexFunction():

    double res = 0.0;
    int i;
    int argc  = nrhs + 1;
    char **argv = (char**) mxMalloc(argc * sizeof(char*));
			
    argv[0] = mxMalloc( sizeof("pesq") );
    strcpy(argv[0], "pesq");
	
    for (i=1; i<argc; i++) {
        argv[i] = mxMalloc( mxGetN(prhs[i-1]) + 1 );
        argv[i] = mxArrayToString(prhs[i-1]);
    }	
		
    res = main(argc,argv);
    nlhs = 1;
    plhs[0] = mxCreateDoubleScalar(res);

	
    for (i=0; i<argc; i++) {
        mxFree(argv[i]);
    }

The only problem with adding this code is that it is requiring a return value from main(). This is because we ideally want to return the PESQ result to the MATLAB workspace (currently I have only implemented this for the MOS-LQO (Mean Opinion Score – Listening Quality Objective)), so a few more additions and we will be set. Replace the main() function definition and its declaration with:

double main(int argc, char **argv)

Now it is capable of returning decimal numbers for the results (negative numbers for errors). We also need to find where the original PESQ code would print out the result and use the same variable to get our return value. The original code should look something like this:

if ( err_info.mode == NB_MODE )
     printf ("\nP.862 Prediction (Raw MOS, MOS-LQO):  = %.3f\t%.3f\n", (double) err_info.pesq_mos, 
          (double) err_info.mapped_mos);
else
     printf ("\nP.862.2 Prediction (MOS-LQO):  = %.3f\n", (double) err_info.mapped_mos);
return 0;

and we want to replace the return value with:

return err_info.mapped_mos;

Now the source code should be ready to compile as a MEX function. If you don’t want anything printed to the screen during the execution of the PESQ algorithm then you will have to comment out the printf() functions riddled throughout the source code (or create a switch).

To compile the source code as a MEX function make sure that in MATLAB you are in the directory specified earlier and run the following at the command window:

mex('pesqmain.c', 'pesqmod.c', 'pesqio.c', 'pesqdsp.c', 'dsp.c')

That’s it, all done. You can now use the pesqmain_mex.mexw64 function in MATLAB with the same arguments you would use with the binary compiled PESQ function, be careful, though, they will need to be entered as separate character arrays in MATLAB. When I ran this MEX function it was approximately 8 times faster than the MATLAB PESQ wrappers on file exchange. Hope it saves someone a little bit (or a lot) of time! =]

Update
If for some reason you cannot build a MEX executable PESQ function then you can download my compiled version here. Additionally, you can use this helper function to call the MEX function from MATLAB. The helper function also includes an optional fourth argument to return either the narrowband or wideband (or both) results.

Update 2
I have written a follow-up post here which shows how to compile the MEX function in a way that allows you to directly send MATLAB vectors to the PESQ algorithm. Check it out!

Like this:
Feel free to share

25 thoughts on “A fast MATLAB executable (MEX) compilation of the PESQ measure”

  1. Hi, I follow your blog step by step, bug I meet some errors and don’t how to solve it. The error is :
    Wrong use mex:
    warning: implicit declaration of function ‘main’ [-Wimplicit-function-declaration]
    res = main(argc,argv);
    ^
    C:\Users\YetiXie1994\Desktop\T-REC-P.862-200511-I!Amd2!SOFT-ZST-E\P862_annex_A_2005_CD\source\pesqmain.c:
    At top level:
    C:\Users\YetiXie1994\Desktop\T-REC-P.862-200511-I!Amd2!SOFT-ZST-E\P862_annex_A_2005_CD\source\pesqmain.c:139:8:
    error: conflicting types for ‘main’
    double main(int argc, char **argv)
    ^
    C:\Users\YetiXie1994\Desktop\T-REC-P.862-200511-I!Amd2!SOFT-ZST-E\P862_annex_A_2005_CD\source\pesqmain.c:129:11:
    note: previous implicit declaration of ‘main’ was here
    res = main(argc,argv);

  2. The below error displayed! Could you please guide me in this regard?

    Error using mex
    C:/MATLAB/SupportPackages/R2016a/MW_MinGW_4_9/bin/../lib/gcc/x86_64-w64-mingw32/4.9.2/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-crt0_c.o): In function 'main':
    C:/crossdev/src/mingw-w64-v3-git/mingw-w64-crt/crt/crt0_c.c:18: undefined reference to 'WinMain'
    C:/MATLAB/SupportPackages/R2016a/MW_MinGW_4_9/bin/../lib/gcc/x86_64-w64-mingw32/4.9.2/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-crt0_c.o):crt0_c.c:(.rdata$.refptr.__mingw_winmain_hInstance[.refptr.__mingw_winmain_hInstance]+0x0):
    undefined reference to '__mingw_winmain_hInstance'
    C:/MATLAB/SupportPackages/R2016a/MW_MinGW_4_9/bin/../lib/gcc/x86_64-w64-mingw32/4.9.2/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-crt0_c.o):crt0_c.c:(.rdata$.refptr.__mingw_winmain_lpCmdLine[.refptr.__mingw_winmain_lpCmdLine]+0x0):
    undefined reference to '__mingw_winmain_lpCmdLine'
    C:/MATLAB/SupportPackages/R2016a/MW_MinGW_4_9/bin/../lib/gcc/x86_64-w64-mingw32/4.9.2/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-crt0_c.o):crt0_c.c:(.rdata$.refptr.__mingw_winmain_nShowCmd[.refptr.__mingw_winmain_nShowCmd]+0x0):
    undefined reference to `__mingw_winmain_nShowCmd'
    collect2.exe: error: ld returned 1 exit status

    1. Hi Silvia,
      You could try looking at the helper function that I link to at the end of the post. It calls the pesq_mex function inside. You can use the helper function to send audio files from MATLAB to the MEX compiled PESQ algorithm.

  3. Hi Guys,
    It seems to be not supported for R2014b. Any solution?
    “Error using mex
    No supported compiler or SDK was found.”
    Best
    Silvia

  4. Hi Jacob, nice and easy to follow guide. Did you, by any chance, implemented also the changes to pass the narrowband outputs from the main function to the matlab workspace?

    1. I managed to get it to work. I reverted to an int main function and passed the variable that will be storing the NB or WB values as a pointer. If you are interested I can share the code.

      1. Hi Guiliano,
        Thanks for the kind comments. I haven’t implemented changes to return the narrowband and wideband outputs at the same time. I have, however, updated the helper function so that you can pass it an optional argument to specify the PESQ mode of operation as narrowband or wideband or both. Then you can just run the helper function with this optional argument.

        I would also like to rewrite the code so that the mex function can directly read from arrays in the MATLAB workspace. I think this would give another good performance increase for cases where the speech signals are already loaded into memory. This would be a good follow up post to write, I just haven’t found the time.

        ~Jacob

  5. Hello,
    When I replace the main() function and its definition with
    double main(int argc, char **argv),
    I get following warning and error :
    1) warning C4013: ‘main’ undefined; assuming extern
    returning int,
    2) error C2371: ‘main’ : redefinition; different basic
    types

    As I do not know c very well, I am unable to fix it

    1. Hi Pramod,
      Just double check that you replaced both the main function definition with double main(int argc, char **argv) and also its declaration with double main(int argc, char **argv).
      In the “pesqmain.c” file, the function declarations are immediately after the #include‘s and #define.
      Hopefully this helps solve the problem!
      ~Jacob

      1. Hello Jacob,
        I figured out a way before I could check your reply.
        I defined the ‘main’ function before ‘mexFunction’ definition and it worked for me.

  6. Hi Jacob, I’m familiar with MATLAB but not C; sorry for the stupid question.
    How would I call PESQ in MATLAB if I had two sound vectors called A and B?
    What do you mean by entering things to be separate character arrays?

      1. Hi Kenny,
        Glad you worked it out in the end. I added an update to the end of the post, the helper function I link to is an example of how to use the MEX function in MATLAB.
        ~Jacob

        1. Hi Jacob,
          With regard to the “update” section, do you mean to write it as follows:
          pesq_mex(‘pesqmain.c’, ‘pesqmod.c’, ‘pesqio.c’, ‘pesqdsp.c’, ‘dsp.c’)

          Thanks
          Silvia

          1. Hi Silvia,
            The updated part links to an already compiled pesq_mex function which should accept string arguments for the audio file locations and other pesq arguments. This pesq_mex function is compiled using the MATLAB mex function.
            The helper function that I link to accepts numerical arrays, which contain the audio samples, as inputs. It then saves the audio samples to disk and then calls upon the compiled pesq_mex function.
            ~Jacob

          2. Hi Jacob,

            It is working now: “MEX completed successfully”.
            How can I use my inputs (sound files) to test them using the pesq code?

            Best
            Silvia

  7. it doesn’t work, this error is displayed:
    Error: Could not find the compiler “cl” on the DOS path.
    Use mex -setup to configure your environment properly.

    what should i do?

Leave a Reply to Kenny Cancel reply

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


This site uses Akismet to reduce spam. Learn how your comment data is processed.