Commit df8fdb8f authored by Simon Morlat's avatar Simon Morlat

enhance msaudiocmp to work with stereo files

parent 9c57afef
......@@ -84,7 +84,6 @@ struct _MSMediaStreamSessions{
MSZrtpContext *zrtp_context;
MSDtlsSrtpContext *dtls_context;
MSTicker *ticker;
bool_t pad[3];
};
typedef struct _MSMediaStreamSessions MSMediaStreamSessions;
......
......@@ -29,7 +29,7 @@ extern "C"{
typedef void (*MSAudioDiffProgressNotify)(void* user_data, int percentage);
/*utility function to check similarity between two audio wav files*/
MS2_PUBLIC int ms_audio_diff(const char *file1, const char *file2, double *ret, MSAudioDiffProgressNotify func, void *user_data);
MS2_PUBLIC int ms_audio_diff(const char *file1, const char *file2, double *ret, double min_overlap_p, MSAudioDiffProgressNotify func, void *user_data);
#ifdef __cplusplus
}
......
......@@ -26,9 +26,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
typedef struct {
int rate;
int nchannels;
int16_t *buffer;
int nsamples;
double energy;
double energy_r;
double energy_l;
}FileInfo;
static void file_info_destroy(FileInfo *fi){
......@@ -58,15 +60,13 @@ static FileInfo *file_info_new(const char *file){
ms_error("not a wav file");
return NULL;
}
if (wave_header_get_channel(&header)==2){
ms_error("stereo files are not supported");
return NULL;
}
fi=ms_new0(FileInfo,1);
size=stbuf.st_size-hsize;
fi->nsamples=size/2;
fi->buffer=ms_new0(int16_t,size);
fi->rate=wave_header_get_rate(&header);
fi->nchannels=wave_header_get_channel(&header);
fi->nsamples=size/(sizeof(int16_t)*fi->nchannels);
err=read(fd,fi->buffer,size);
if (err==-1){
ms_error("Could not read file: %s",strerror(errno));
......@@ -84,14 +84,15 @@ static FileInfo *file_info_new(const char *file){
/*
* compute cross correlation between two signals. The results should n1+n2 samples.
**/
static int compute_cross_correlation(int16_t *s1, int n1, int16_t *s2, int n2, double *xcorr, int xcorr_nsamples, MSAudioDiffProgressNotify func, void *user_data){
static int compute_cross_correlation(int16_t *s1, int n1, int16_t *s2, int n2, double *xcorr, int xcorr_nsamples, MSAudioDiffProgressNotify func, void *user_data, int min_overlap){
int64_t acc;
int64_t max=0;
int max_index=0;
int i,j,k;
int b1min,b2min;
int b1min,b2min,b1max,b2max;
int completion=0;
int prev_completion=0;
int width, l;
#define STEP 4
#define ACC(s) acc+=(int64_t)( (int)s1[j+s]*(int)s2[k+s]);
......@@ -100,7 +101,13 @@ static int compute_cross_correlation(int16_t *s1, int n1, int16_t *s2, int n2, d
acc=0;
b1min=MAX(n1-i,0);
b2min=MAX(0,i-n1);
for(j=b1min,k=b2min; j<b1min+n1-STEP && k<b2min+n2-STEP; j+=STEP,k+=STEP){
b1max=b1min+n1-STEP;
b2max=b2min+n2-STEP;
width=MIN(b1max,b2max) - MAX(b1min, b2min);
if (width < min_overlap) {
continue;
}
for(j=b1min,k=b2min, l=0; l<width ; j+=STEP,k+=STEP, l+=STEP){
ACC(0);
ACC(1);
ACC(2);
......@@ -118,6 +125,56 @@ static int compute_cross_correlation(int16_t *s1, int n1, int16_t *s2, int n2, d
return max_index;
}
/*
* compute cross correlation between two stereo signals, but for one channel only. The results should n1+n2 samples.
**/
static int compute_cross_correlation_interleaved(int16_t *s1, int n1, int16_t *s2, int n2, double *xcorr, int xcorr_nsamples, MSAudioDiffProgressNotify func, void *user_data, int channel_num, int min_overlap){
int64_t acc;
int64_t max=0;
int max_index=0;
int i,j,k;
int b1min,b2min;
int b1max,b2max;
int completion=0;
int prev_completion=0;
int width;
int l;
s1+=channel_num;
s2+=channel_num;
#define STEP2 8
#define ACC2(s) acc+=(int64_t)( (int)s1[j+s]*(int)s2[k+s]);
for(i=0;i<xcorr_nsamples;++i){
completion=100*(i+(xcorr_nsamples*channel_num))/(xcorr_nsamples*2);
acc=0;
b1min=MAX(n1-i,0);
b2min=MAX(0,i-n1);
b1max=b1min+n1-STEP2;
b2max=b2min+n2-STEP2;
width=MIN(b1max,b2max) - MAX(b1min, b2min);
//ms_message(" width %i overlap %i",width, min_overlap);
if (width < min_overlap) {
continue;
}
for(j=b1min,k=b2min,l=0; l<width; j+=STEP2,k+=STEP2,l+=STEP2){
ACC2(0);
ACC2(2);
ACC2(4);
ACC2(6);
}
xcorr[i]=acc;
if (acc>max) {
max=acc;
max_index=i;
}
if (func && completion>prev_completion)
func(user_data,completion);
prev_completion=completion;
}
return max_index;
}
static double energy(int16_t *s1, int n1){
int i;
int64_t ret=0;
......@@ -127,21 +184,44 @@ static double energy(int16_t *s1, int n1){
return (double)ret;
}
static double energy_interleaved(int16_t *s1, int n1){
int i;
int64_t ret=0;
for(i=0;i<n1;i+=2){
ret+=(int)s1[i]*(int)s1[i];
}
return (double)ret;
}
void file_info_compute_energy(FileInfo *fi){
fi->energy=energy(fi->buffer,fi->nsamples);
ms_message("energy=%g",fi->energy);
if (fi->nchannels==2){
fi->energy_r=energy_interleaved(fi->buffer,fi->nsamples);
fi->energy_l=energy_interleaved(fi->buffer+1,fi->nsamples);
}else{
fi->energy_r=energy(fi->buffer,fi->nsamples);
ms_message("energy=%g",fi->energy_r);
}
}
/**
* Utility that compares two PCM 16 bits audio files and returns a similarity factor between 0 and 1.
* @param file1 a wav file path
* @param file2 a wav file path
* @param ret the similarity factor, set in return
* @param min_overlap_p percentage of minimum overlap between the two signals, used to restrict the cross correlation around t=0.
* @param func a callback called to show progress of the operation
* @param user_data a user data passed to the callback when invoked.
* @return -1 on error, 0 if succesful.
**/
int ms_audio_diff(const char *file1, const char *file2, double *ret, MSAudioDiffProgressNotify func, void *user_data){
int ms_audio_diff(const char *file1, const char *file2, double *ret, double min_overlap_p, MSAudioDiffProgressNotify func, void *user_data){
FileInfo *fi1,*fi2;
double *xcorr;
int xcorr_size;
int max_index;
double max;
int min_overlap;
int max_index_r;
int max_index_l;
double max_r, max_l;
*ret=0;
......@@ -158,22 +238,41 @@ int ms_audio_diff(const char *file1, const char *file2, double *ret, MSAudioDiff
return -1;
}
if (fi1->nchannels!=fi2->nchannels){
ms_error("Comparing files with different number of channels is not supported");
return -1;
}
file_info_compute_energy(fi1);
file_info_compute_energy(fi2);
if (fi1->energy==0 || fi2->energy==0){
if (fi1->energy_r==0 || fi2->energy_r==0){
/*avoid division by zero*/
ms_error("One of the two files is pure silence.");
return -1;
}
xcorr_size=fi1->nsamples+fi2->nsamples;
min_overlap = MIN(fi1->nsamples, fi2->nsamples) * min_overlap_p / 100.0;
xcorr=ms_new0(double,xcorr_size);
max_index=compute_cross_correlation(fi1->buffer,fi1->nsamples,fi2->buffer,fi2->nsamples,xcorr,xcorr_size, func, user_data);
max=xcorr[max_index];
if (fi1->nchannels == 2){
max_index_r=compute_cross_correlation_interleaved(fi1->buffer,fi1->nsamples,fi2->buffer,fi2->nsamples,xcorr,xcorr_size, func, user_data, 0, min_overlap);
max_r=xcorr[max_index_r];
max_r/=(sqrt(fi1->energy_r)*sqrt(fi2->energy_r));
max_index_l=compute_cross_correlation_interleaved(fi1->buffer,fi1->nsamples,fi2->buffer,fi2->nsamples,xcorr,xcorr_size, func, user_data, 1, min_overlap);
max_l=xcorr[max_index_l];
max_l/=(sqrt(fi1->energy_l)*sqrt(fi2->energy_l));
ms_message("Max stereo cross-correlation obtained at position [%i,%i], similarity factor=%g,%g",max_index_r,max_index_l,max_r, max_l);
*ret = 0.5 * (max_r + max_l) * (1 - (double)abs(max_index_r-max_index_l)/(double)xcorr_size);
}else{
max_index_r=compute_cross_correlation(fi1->buffer,fi1->nsamples,fi2->buffer,fi2->nsamples,xcorr,xcorr_size, func, user_data, min_overlap);
max_r=xcorr[max_index_r];
max_r/=(sqrt(fi1->energy_r)*sqrt(fi2->energy_r));
*ret=max_r;
ms_message("Max cross-correlation obtained at position [%i], similarity factor=%g",max_index_r,*ret);
}
ms_free(xcorr);
*ret=max/(sqrt(fi1->energy)*sqrt(fi2->energy));
ms_message("Max cross-correlation obtained at position [%i], similarity factor=%g",max_index,*ret);
file_info_destroy(fi1);
file_info_destroy(fi2);
return 0;
......
......@@ -28,12 +28,16 @@ static void completion_cb(void *user_data, int percentage){
int main(int argc, char *argv[]){
double ret=0;
double overlap=0;
if (argc<3){
fprintf(stderr,"%s: file1 file2\nCompare two wav audio files and display a similarity factor between 0 and 1.\n",argv[0]);
fprintf(stderr,"%s: file1 file2 [overlap-percentage]\nCompare two wav audio files and display a similarity factor between 0 and 1.\n",argv[0]);
return -1;
}
if (argc>3){
overlap=atoi(argv[3]);
}
ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
if (ms_audio_diff(argv[1],argv[2],&ret,completion_cb,NULL)==0){
if (ms_audio_diff(argv[1],argv[2],&ret,overlap,completion_cb,NULL)==0){
fprintf(stdout,"%s and %s are similar with a degree of %g.\n",argv[1],argv[2],ret);
return 0;
}else{
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment