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!
how to call this function…
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);
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
Hi Beh,
Double check that the compiler can successfully find the entry point,
main()
. It may be erroneously searching forWinMain()
instead.Hi,
It is not clear to me if the items that I have downloaded from:
(1) http://www.itu.int/rec/T-REC-P.862-200511-I!Amd2/en
and compiled successfully using
(2) the instructions mentioned in the beginning of this page,
are enough to run the code (downloaded from (1)) to test the PSEQ of my own sound files. If so, can you please clarify it?
Silvia
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.Hi Guys,
It seems to be not supported for R2014b. Any solution?
“Error using mex
No supported compiler or SDK was found.”
Best
Silvia
Hi Silvia,
It should be supported fine in R2014b (and newer versions).
The problem you are getting is not to do with MATLAB but instead with the lack of C/C++ compiler installed on your system and not linked to MATLAB. You may have to run
mex -setup
and choose a compatible compiler.You could have a look at these:
What You Need to Build MEX Files
Supported and Compatible Compilers
~Jacob
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?
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.
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
The updated function works. I can get both narrowband and wideband results.
Thank you, Jacob.
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
Hi Pramod,
Just double check that you replaced both the main function definition with
double main(int argc, char **argv)
and also its declaration withdouble 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
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.
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?
Never mind, figured it out 🙂
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
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
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. Thispesq_mex
function is compiled using the MATLABmex
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
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
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.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?
Hi Sam,
You will have to make sure that your Matlab installation can successfully use a compatible source code compiler.
Typing
mex -setup
in the command window may help you get started.You could also have a look at these:
What You Need to Build MEX Files
Supported and Compatible Compilers for R2016a
Hope this helps!
~Jacob