pHash 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +12 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +53 -0
- data/audiophash.diff +17 -0
- data/lib/phash.rb +44 -0
- data/lib/phash/all.rb +3 -0
- data/lib/phash/audio.rb +116 -0
- data/lib/phash/image.rb +59 -0
- data/lib/phash/text.rb +100 -0
- data/lib/phash/video.rb +55 -0
- data/pHash.gemspec +20 -0
- data/spec/data/audiophash.cpp-0.9.3.txt +571 -0
- data/spec/data/audiophash.cpp-0.9.4.txt +572 -0
- data/spec/data/audiophash.h-0.9.3.txt +111 -0
- data/spec/data/audiophash.h-0.9.4.txt +108 -0
- data/spec/data/hal9000-m.mp3 +0 -0
- data/spec/data/hal9000-o.mp3 +0 -0
- data/spec/data/jug-0-10.jpg +0 -0
- data/spec/data/jug-0-120.png +0 -0
- data/spec/data/jug-0-50.jpg +0 -0
- data/spec/data/jug-0-70.jpg +0 -0
- data/spec/data/jug-1-10.jpg +0 -0
- data/spec/data/jug-1-120.png +0 -0
- data/spec/data/jug-1-50.jpg +0 -0
- data/spec/data/jug-1-70.jpg +0 -0
- data/spec/data/jug-120.mp4 +0 -0
- data/spec/data/jug-150.mp4 +0 -0
- data/spec/data/jug-180.mp4 +0 -0
- data/spec/data/jug-2-10.jpg +0 -0
- data/spec/data/jug-2-120.png +0 -0
- data/spec/data/jug-2-50.jpg +0 -0
- data/spec/data/jug-2-70.jpg +0 -0
- data/spec/data/mouse-0-10.jpg +0 -0
- data/spec/data/mouse-0-120.png +0 -0
- data/spec/data/mouse-0-50.jpg +0 -0
- data/spec/data/mouse-0-70.jpg +0 -0
- data/spec/data/mouse-1-10.jpg +0 -0
- data/spec/data/mouse-1-120.png +0 -0
- data/spec/data/mouse-1-50.jpg +0 -0
- data/spec/data/mouse-1-70.jpg +0 -0
- data/spec/data/mouse-120.mp4 +0 -0
- data/spec/data/mouse-150.mp4 +0 -0
- data/spec/data/mouse-180.mp4 +0 -0
- data/spec/data/mouse-2-10.jpg +0 -0
- data/spec/data/mouse-2-120.png +0 -0
- data/spec/data/mouse-2-50.jpg +0 -0
- data/spec/data/mouse-2-70.jpg +0 -0
- data/spec/data/scream-m.mp3 +0 -0
- data/spec/data/scream-o.mp3 +0 -0
- data/spec/data/vader-m.mp3 +0 -0
- data/spec/data/vader-o.mp3 +0 -0
- data/spec/phash_spec.rb +43 -0
- data/spec/spec_helper.rb +10 -0
- metadata +186 -0
@@ -0,0 +1,572 @@
|
|
1
|
+
/*
|
2
|
+
|
3
|
+
pHash, the open source perceptual hash library
|
4
|
+
Copyright (C) 2009 Aetilius, Inc.
|
5
|
+
All rights reserved.
|
6
|
+
|
7
|
+
This program is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the GNU General Public License as published by
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
This program is distributed in the hope that it will be useful,
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
GNU General Public License for more details.
|
16
|
+
|
17
|
+
You should have received a copy of the GNU General Public License
|
18
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
Evan Klinger - eklinger@phash.org
|
21
|
+
David Starkweather - dstarkweather@phash.org
|
22
|
+
|
23
|
+
*/
|
24
|
+
|
25
|
+
#include "audiophash.h"
|
26
|
+
#include <sndfile.h>
|
27
|
+
#include <samplerate.h>
|
28
|
+
#include "config.h"
|
29
|
+
|
30
|
+
#ifdef HAVE_LIBMPG123
|
31
|
+
#include <mpg123.h>
|
32
|
+
#endif
|
33
|
+
|
34
|
+
int ph_count_samples(const char *filename, int sr,int channels){
|
35
|
+
|
36
|
+
SF_INFO sf_info;
|
37
|
+
sf_info.format=0;
|
38
|
+
SNDFILE *sndfile = sf_open(filename, SFM_READ, &sf_info);
|
39
|
+
if (sndfile == NULL){
|
40
|
+
return NULL;
|
41
|
+
}
|
42
|
+
int count = sf_info.frames;
|
43
|
+
sf_close(sndfile);
|
44
|
+
return count;
|
45
|
+
}
|
46
|
+
|
47
|
+
#ifdef HAVE_LIBMPG123
|
48
|
+
|
49
|
+
static
|
50
|
+
float* readaudio_mp3(const char *filename,long *sr, const float nbsecs, unsigned int *buflen){
|
51
|
+
mpg123_handle *m;
|
52
|
+
int ret;
|
53
|
+
|
54
|
+
if (mpg123_init() != MPG123_OK || ((m = mpg123_new(NULL,&ret)) == NULL)|| \
|
55
|
+
mpg123_open(m, filename) != MPG123_OK){
|
56
|
+
fprintf(stderr,"unable to init mpg\n");
|
57
|
+
return NULL;
|
58
|
+
}
|
59
|
+
|
60
|
+
/*turn off logging */
|
61
|
+
mpg123_param(m, MPG123_ADD_FLAGS, MPG123_QUIET, 0);
|
62
|
+
|
63
|
+
off_t totalsamples;
|
64
|
+
|
65
|
+
mpg123_scan(m);
|
66
|
+
totalsamples = mpg123_length(m);
|
67
|
+
|
68
|
+
int meta = mpg123_meta_check(m);
|
69
|
+
|
70
|
+
int channels, encoding;
|
71
|
+
|
72
|
+
if (mpg123_getformat(m, sr, &channels, &encoding) != MPG123_OK){
|
73
|
+
fprintf(stderr,"unable to get format\n");
|
74
|
+
return NULL;
|
75
|
+
}
|
76
|
+
|
77
|
+
mpg123_format_none(m);
|
78
|
+
mpg123_format(m, *sr, channels, encoding);
|
79
|
+
|
80
|
+
size_t decbuflen = mpg123_outblock(m);
|
81
|
+
unsigned char *decbuf = (unsigned char*)malloc(decbuflen);
|
82
|
+
if (decbuf == NULL){
|
83
|
+
printf("mem alloc error\n");
|
84
|
+
return NULL;
|
85
|
+
}
|
86
|
+
|
87
|
+
unsigned int nbsamples = (nbsecs <= 0) ? totalsamples : nbsecs*(*sr);
|
88
|
+
nbsamples = (nbsamples < totalsamples) ? nbsamples : totalsamples;
|
89
|
+
|
90
|
+
size_t i, j, index = 0, done;
|
91
|
+
|
92
|
+
|
93
|
+
float *buffer = (float*)malloc(nbsamples*sizeof(float));
|
94
|
+
*buflen = nbsamples;
|
95
|
+
|
96
|
+
do {
|
97
|
+
|
98
|
+
ret = mpg123_read(m, decbuf, decbuflen, &done);
|
99
|
+
switch (encoding) {
|
100
|
+
case MPG123_ENC_SIGNED_16 :
|
101
|
+
for (i = 0; i < done/sizeof(short); i+=channels){
|
102
|
+
buffer[index] = 0.0f;
|
103
|
+
for (j = 0; j < channels ; j++){
|
104
|
+
buffer[index] += (float)(((short*)decbuf)[i+j])/(float)SHRT_MAX;
|
105
|
+
}
|
106
|
+
buffer[index++] /= channels;
|
107
|
+
if (index >= nbsamples) break;
|
108
|
+
}
|
109
|
+
break;
|
110
|
+
case MPG123_ENC_SIGNED_8:
|
111
|
+
for (i = 0; i < done/sizeof(char); i+=channels){
|
112
|
+
buffer[index] = 0.0f;
|
113
|
+
for (j = 0; j < channels ; j++){
|
114
|
+
buffer[index] += (float)(((char*)decbuf)[i+j])/(float)SCHAR_MAX;
|
115
|
+
}
|
116
|
+
buffer[index++] /= channels;
|
117
|
+
if (index >= nbsamples) break;
|
118
|
+
}
|
119
|
+
break;
|
120
|
+
case MPG123_ENC_FLOAT_32:
|
121
|
+
for (i = 0; i < done/sizeof(float); i+=channels){
|
122
|
+
buffer[index] = 0.0f;
|
123
|
+
for (j = 0; j < channels; j++){
|
124
|
+
buffer[index] += ((float*)decbuf)[i+j];
|
125
|
+
}
|
126
|
+
buffer[index++] /= channels;
|
127
|
+
if (index >= nbsamples) break;
|
128
|
+
}
|
129
|
+
break;
|
130
|
+
default:
|
131
|
+
done = 0;
|
132
|
+
}
|
133
|
+
|
134
|
+
} while (ret == MPG123_OK && index < nbsamples);
|
135
|
+
|
136
|
+
free(decbuf);
|
137
|
+
mpg123_close(m);
|
138
|
+
mpg123_delete(m);
|
139
|
+
mpg123_exit();
|
140
|
+
|
141
|
+
return buffer;
|
142
|
+
}
|
143
|
+
|
144
|
+
#endif /*HAVE_LIBMPG123*/
|
145
|
+
|
146
|
+
static
|
147
|
+
float *readaudio_snd(const char *filename, long *sr, const float nbsecs, unsigned int *buflen){
|
148
|
+
|
149
|
+
SF_INFO sf_info;
|
150
|
+
sf_info.format=0;
|
151
|
+
SNDFILE *sndfile = sf_open(filename, SFM_READ, &sf_info);
|
152
|
+
if (sndfile == NULL){
|
153
|
+
return NULL;
|
154
|
+
}
|
155
|
+
|
156
|
+
/* normalize */
|
157
|
+
sf_command(sndfile, SFC_SET_NORM_FLOAT, NULL, SF_TRUE);
|
158
|
+
|
159
|
+
*sr = (long)sf_info.samplerate;
|
160
|
+
|
161
|
+
//allocate input buffer for signal
|
162
|
+
unsigned int src_frames = (nbsecs <= 0) ? sf_info.frames : (nbsecs*sf_info.samplerate);
|
163
|
+
src_frames = (sf_info.frames < src_frames) ? sf_info.frames : src_frames;
|
164
|
+
float *inbuf = (float*)malloc(src_frames*sf_info.channels*sizeof(float));
|
165
|
+
|
166
|
+
/*read frames */
|
167
|
+
sf_count_t cnt_frames = sf_readf_float(sndfile, inbuf, src_frames);
|
168
|
+
|
169
|
+
float *buf = (float*)malloc(cnt_frames*sizeof(float));
|
170
|
+
|
171
|
+
//average across all channels
|
172
|
+
int i,j,indx=0;
|
173
|
+
for (i=0;i<cnt_frames*sf_info.channels;i+=sf_info.channels){
|
174
|
+
buf[indx] = 0;
|
175
|
+
for (j=0;j<sf_info.channels;j++){
|
176
|
+
buf[indx] += inbuf[i+j];
|
177
|
+
}
|
178
|
+
buf[indx++] /= sf_info.channels;
|
179
|
+
}
|
180
|
+
free(inbuf);
|
181
|
+
|
182
|
+
*buflen = indx;
|
183
|
+
return buf;
|
184
|
+
}
|
185
|
+
|
186
|
+
float* ph_readaudio2(const char *filename, int sr, float *sigbuf, int &buflen, const float nbsecs){
|
187
|
+
|
188
|
+
long orig_sr;
|
189
|
+
float *inbuffer = NULL;
|
190
|
+
unsigned int inbufferlength;
|
191
|
+
buflen = 0;
|
192
|
+
|
193
|
+
const char *suffix = strrchr(filename, '.');
|
194
|
+
if (suffix == NULL) return NULL;
|
195
|
+
if (!strcasecmp(suffix+1, "mp3")) {
|
196
|
+
#ifdef HAVE_LIBMPG123
|
197
|
+
inbuffer = readaudio_mp3(filename, &orig_sr, nbsecs, &inbufferlength);
|
198
|
+
#endif /* HAVE_LIBMPG123 */
|
199
|
+
} else {
|
200
|
+
inbuffer = readaudio_snd(filename, &orig_sr, nbsecs, &inbufferlength);
|
201
|
+
}
|
202
|
+
|
203
|
+
if (inbuffer == NULL){
|
204
|
+
return NULL;
|
205
|
+
}
|
206
|
+
|
207
|
+
/* resample float array */
|
208
|
+
/* set desired sr ratio */
|
209
|
+
double sr_ratio = (double)(sr)/(double)orig_sr;
|
210
|
+
if (src_is_valid_ratio(sr_ratio) == 0){
|
211
|
+
free(inbuffer);
|
212
|
+
return NULL;
|
213
|
+
}
|
214
|
+
|
215
|
+
/* allocate output buffer for conversion */
|
216
|
+
unsigned int outbufferlength = sr_ratio*inbufferlength;
|
217
|
+
float *outbuffer = (float*)malloc(outbufferlength*sizeof(float));
|
218
|
+
if (!outbuffer){
|
219
|
+
free(inbuffer);
|
220
|
+
return NULL;
|
221
|
+
}
|
222
|
+
|
223
|
+
int error;
|
224
|
+
SRC_STATE *src_state = src_new(SRC_LINEAR, 1, &error);
|
225
|
+
if (!src_state){
|
226
|
+
free(inbuffer);
|
227
|
+
free(outbuffer);
|
228
|
+
return NULL;
|
229
|
+
}
|
230
|
+
|
231
|
+
SRC_DATA src_data;
|
232
|
+
src_data.data_in = inbuffer;
|
233
|
+
src_data.data_out = outbuffer;
|
234
|
+
src_data.input_frames = inbufferlength;
|
235
|
+
src_data.output_frames = outbufferlength;
|
236
|
+
src_data.end_of_input = SF_TRUE;
|
237
|
+
src_data.src_ratio = sr_ratio;
|
238
|
+
|
239
|
+
/* sample rate conversion */
|
240
|
+
if (error = src_process(src_state, &src_data)){
|
241
|
+
free(inbuffer);
|
242
|
+
free(outbuffer);
|
243
|
+
src_delete(src_state);
|
244
|
+
return NULL;
|
245
|
+
}
|
246
|
+
|
247
|
+
buflen = src_data.output_frames;
|
248
|
+
|
249
|
+
src_delete(src_state);
|
250
|
+
free(inbuffer);
|
251
|
+
|
252
|
+
return outbuffer;
|
253
|
+
}
|
254
|
+
|
255
|
+
|
256
|
+
float* ph_readaudio(const char *filename, int sr, int channels, float *sigbuf, int &buflen,\
|
257
|
+
const float nbsecs){
|
258
|
+
if(!filename || sr <= 0)
|
259
|
+
return NULL;
|
260
|
+
return ph_readaudio2(filename, sr, sigbuf, buflen, nbsecs);
|
261
|
+
}
|
262
|
+
|
263
|
+
uint32_t* ph_audiohash(float *buf, int N, int sr, int &nb_frames){
|
264
|
+
|
265
|
+
int frame_length = 4096;//2^12
|
266
|
+
int nfft = frame_length;
|
267
|
+
int nfft_half = 2048;
|
268
|
+
int start = 0;
|
269
|
+
int end = start + frame_length - 1;
|
270
|
+
int overlap = (int)(31*frame_length/32);
|
271
|
+
int advance = frame_length - overlap;
|
272
|
+
int index = 0;
|
273
|
+
nb_frames = (int)(floor(N/advance) - floor(frame_length/advance) + 1);
|
274
|
+
double window[frame_length];
|
275
|
+
for (int i = 0;i<frame_length;i++){
|
276
|
+
//hamming window
|
277
|
+
window[i] = 0.54 - 0.46*cos(2*M_PI*i/(frame_length-1));
|
278
|
+
}
|
279
|
+
|
280
|
+
double frame[frame_length];
|
281
|
+
//fftw_complex *pF;
|
282
|
+
//fftw_plan p;
|
283
|
+
//pF = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*nfft);
|
284
|
+
complex double *pF = (complex double*)malloc(sizeof(complex double)*nfft);
|
285
|
+
|
286
|
+
double magnF[nfft_half];
|
287
|
+
double maxF = 0.0;
|
288
|
+
double maxB = 0.0;
|
289
|
+
|
290
|
+
double minfreq = 300;
|
291
|
+
double maxfreq = 3000;
|
292
|
+
double minbark = 6*asinh(minfreq/600.0);
|
293
|
+
double maxbark = 6*asinh(maxfreq/600.0);
|
294
|
+
double nyqbark = maxbark - minbark;
|
295
|
+
int nfilts = 33;
|
296
|
+
double stepbarks = nyqbark/(nfilts - 1);
|
297
|
+
int nb_barks = (int)(floor(nfft_half/2 + 1));
|
298
|
+
double barkwidth = 1.06;
|
299
|
+
|
300
|
+
double freqs[nb_barks];
|
301
|
+
double binbarks[nb_barks];
|
302
|
+
double curr_bark[nfilts];
|
303
|
+
double prev_bark[nfilts];
|
304
|
+
for (int i=0;i< nfilts;i++){
|
305
|
+
prev_bark[i] = 0.0;
|
306
|
+
}
|
307
|
+
uint32_t *hash = (uint32_t*)malloc(nb_frames*sizeof(uint32_t));
|
308
|
+
double lof,hif;
|
309
|
+
|
310
|
+
for (int i=0; i < nb_barks;i++){
|
311
|
+
binbarks[i] = 6*asinh(i*sr/nfft_half/600.0);
|
312
|
+
freqs[i] = i*sr/nfft_half;
|
313
|
+
}
|
314
|
+
double **wts = new double*[nfilts];
|
315
|
+
for (int i=0;i<nfilts;i++){
|
316
|
+
wts[i] = new double[nfft_half];
|
317
|
+
}
|
318
|
+
for (int i=0;i<nfilts;i++){
|
319
|
+
for (int j=0;j<nfft_half;j++){
|
320
|
+
wts[i][j] = 0.0;
|
321
|
+
}
|
322
|
+
}
|
323
|
+
|
324
|
+
//calculate wts for each filter
|
325
|
+
for (int i=0;i<nfilts;i++){
|
326
|
+
double f_bark_mid = minbark + i*stepbarks;
|
327
|
+
for (int j=0;j<nb_barks;j++){
|
328
|
+
double barkdiff = binbarks[j] - f_bark_mid;
|
329
|
+
lof = -2.5*(barkdiff/barkwidth - 0.5);
|
330
|
+
hif = barkdiff/barkwidth + 0.5;
|
331
|
+
double m = std::min(lof,hif);
|
332
|
+
m = std::min(0.0,m);
|
333
|
+
m = pow(10,m);
|
334
|
+
wts[i][j] = m;
|
335
|
+
}
|
336
|
+
}
|
337
|
+
|
338
|
+
//p = fftw_plan_dft_r2c_1d(frame_length,frame,pF,FFTW_ESTIMATE);
|
339
|
+
|
340
|
+
while (end < N){
|
341
|
+
maxF = 0.0;
|
342
|
+
maxB = 0.0;
|
343
|
+
for (int i = 0;i<frame_length;i++){
|
344
|
+
frame[i] = window[i]*buf[start+i];
|
345
|
+
}
|
346
|
+
//fftw_execute(p);
|
347
|
+
if (fft(frame, frame_length, pF) < 0){
|
348
|
+
return NULL;
|
349
|
+
}
|
350
|
+
for (int i=0; i < nfft_half;i++){
|
351
|
+
//magnF[i] = sqrt(pF[i][0]*pF[i][0] + pF[i][1]*pF[i][1] );
|
352
|
+
magnF[i] = cabs(pF[i]);
|
353
|
+
if (magnF[i] > maxF){
|
354
|
+
maxF = magnF[i];
|
355
|
+
}
|
356
|
+
}
|
357
|
+
|
358
|
+
for (int i=0;i<nfilts;i++){
|
359
|
+
curr_bark[i] = 0;
|
360
|
+
for (int j=0;j < nfft_half;j++){
|
361
|
+
curr_bark[i] += wts[i][j]*magnF[j];
|
362
|
+
}
|
363
|
+
if (curr_bark[i] > maxB)
|
364
|
+
maxB = curr_bark[i];
|
365
|
+
}
|
366
|
+
|
367
|
+
uint32_t curr_hash = 0x00000000u;
|
368
|
+
for (int m=0;m<nfilts-1;m++){
|
369
|
+
double H = curr_bark[m] - curr_bark[m+1] - (prev_bark[m] - prev_bark[m+1]);
|
370
|
+
curr_hash = curr_hash << 1;
|
371
|
+
if (H > 0)
|
372
|
+
curr_hash |= 0x00000001;
|
373
|
+
}
|
374
|
+
|
375
|
+
|
376
|
+
hash[index] = curr_hash;
|
377
|
+
for (int i=0;i<nfilts;i++){
|
378
|
+
prev_bark[i] = curr_bark[i];
|
379
|
+
}
|
380
|
+
index += 1;
|
381
|
+
start += advance;
|
382
|
+
end += advance;
|
383
|
+
}
|
384
|
+
|
385
|
+
|
386
|
+
|
387
|
+
//fftw_destroy_plan(p);
|
388
|
+
//fftw_free(pF);
|
389
|
+
free(pF);
|
390
|
+
for (int i=0;i<nfilts;i++){
|
391
|
+
delete [] wts[i];
|
392
|
+
}
|
393
|
+
delete [] wts;
|
394
|
+
return hash;
|
395
|
+
}
|
396
|
+
|
397
|
+
|
398
|
+
int ph_bitcount(uint32_t n){
|
399
|
+
|
400
|
+
//parallel bit count
|
401
|
+
#define MASK_01010101 (((uint32_t)(-1))/3)
|
402
|
+
#define MASK_00110011 (((uint32_t)(-1))/5)
|
403
|
+
#define MASK_00001111 (((uint32_t)(-1))/17)
|
404
|
+
|
405
|
+
n = (n & MASK_01010101) + ((n >> 1) & MASK_01010101) ;
|
406
|
+
n = (n & MASK_00110011) + ((n >> 2) & MASK_00110011) ;
|
407
|
+
n = (n & MASK_00001111) + ((n >> 4) & MASK_00001111) ;
|
408
|
+
return n % 255;
|
409
|
+
|
410
|
+
}
|
411
|
+
|
412
|
+
double ph_compare_blocks(const uint32_t *ptr_blockA,const uint32_t *ptr_blockB, const int block_size){
|
413
|
+
double result = 0;
|
414
|
+
for (int i=0;i<block_size;i++){
|
415
|
+
uint32_t xordhash = ptr_blockA[i]^ptr_blockB[i];
|
416
|
+
result += ph_bitcount(xordhash);
|
417
|
+
}
|
418
|
+
result = result/(32*block_size);
|
419
|
+
return result;
|
420
|
+
}
|
421
|
+
double* ph_audio_distance_ber(uint32_t *hash_a , const int Na, uint32_t *hash_b, const int Nb, const float threshold, const int block_size, int &Nc){
|
422
|
+
|
423
|
+
uint32_t *ptrA, *ptrB;
|
424
|
+
int N1, N2;
|
425
|
+
if (Na <= Nb){
|
426
|
+
ptrA = hash_a;
|
427
|
+
ptrB = hash_b;
|
428
|
+
Nc = Nb - Na + 1;
|
429
|
+
N1 = Na;
|
430
|
+
N2 = Nb;
|
431
|
+
} else {
|
432
|
+
ptrB = hash_a;
|
433
|
+
ptrA = hash_b;
|
434
|
+
Nc = Na - Nb + 1;
|
435
|
+
N1 = Nb;
|
436
|
+
N2 = Na;
|
437
|
+
}
|
438
|
+
|
439
|
+
double *pC = new double[Nc];
|
440
|
+
if (!pC)
|
441
|
+
return NULL;
|
442
|
+
int k,M,nb_above, nb_below, hash1_index,hash2_index;
|
443
|
+
double sum_above, sum_below,above_factor, below_factor;
|
444
|
+
|
445
|
+
uint32_t *pha,*phb;
|
446
|
+
double *dist = NULL;
|
447
|
+
|
448
|
+
for (int i=0; i < Nc;i++){
|
449
|
+
|
450
|
+
M = (int)floor(std::min(N1,N2-i)/block_size);
|
451
|
+
|
452
|
+
pha = ptrA;
|
453
|
+
phb = ptrB + i;
|
454
|
+
|
455
|
+
double *tmp_dist = (double*)realloc(dist, M*sizeof(double));
|
456
|
+
if (!tmp_dist){
|
457
|
+
return NULL;
|
458
|
+
}
|
459
|
+
dist = tmp_dist;
|
460
|
+
dist[0] = ph_compare_blocks(pha,phb,block_size);
|
461
|
+
|
462
|
+
k = 1;
|
463
|
+
|
464
|
+
pha += block_size;
|
465
|
+
phb += block_size;
|
466
|
+
|
467
|
+
hash1_index = block_size;
|
468
|
+
hash2_index = i + block_size;
|
469
|
+
|
470
|
+
while ((hash1_index < N1 - block_size) && (hash2_index < N2 - block_size)){
|
471
|
+
dist[k++] = ph_compare_blocks(pha,phb,block_size);
|
472
|
+
hash1_index += block_size;
|
473
|
+
hash2_index += block_size;
|
474
|
+
pha += block_size;
|
475
|
+
phb += block_size;
|
476
|
+
}
|
477
|
+
sum_above = 0;
|
478
|
+
sum_below = 0;
|
479
|
+
nb_above = 0;
|
480
|
+
nb_below = 0;
|
481
|
+
for (int n = 0; n < M; n++){
|
482
|
+
|
483
|
+
if (dist[n] <= threshold){
|
484
|
+
sum_below += 1-dist[n];
|
485
|
+
nb_below++;
|
486
|
+
} else {
|
487
|
+
sum_above += 1-dist[n];
|
488
|
+
nb_above++;
|
489
|
+
}
|
490
|
+
}
|
491
|
+
above_factor = sum_above/M;
|
492
|
+
below_factor = sum_below/M;
|
493
|
+
pC[i] = 0.5*(1 + below_factor - above_factor);
|
494
|
+
}
|
495
|
+
|
496
|
+
free(dist);
|
497
|
+
return pC;
|
498
|
+
}
|
499
|
+
#ifdef HAVE_PTHREAD
|
500
|
+
|
501
|
+
void *ph_audio_thread(void *p)
|
502
|
+
{
|
503
|
+
slice *s = (slice *)p;
|
504
|
+
for(int i = 0; i < s->n; ++i)
|
505
|
+
{
|
506
|
+
DP *dp = (DP *)s->hash_p[i];
|
507
|
+
int N, count;
|
508
|
+
pair<int,int> *p = (pair<int,int> *)s->hash_params;
|
509
|
+
float *buf = ph_readaudio(dp->id, p->first, p->second, NULL, N);
|
510
|
+
uint32_t *hash = ph_audiohash(buf, N, p->first, count);
|
511
|
+
free(buf);
|
512
|
+
buf = NULL;
|
513
|
+
dp->hash = hash;
|
514
|
+
dp->hash_length = count;
|
515
|
+
}
|
516
|
+
}
|
517
|
+
|
518
|
+
DP** ph_audio_hashes(char *files[], int count, int sr, int channels, int threads)
|
519
|
+
{
|
520
|
+
if(!files || count == 0)
|
521
|
+
return NULL;
|
522
|
+
|
523
|
+
int num_threads;
|
524
|
+
if(threads > count)
|
525
|
+
{
|
526
|
+
num_threads = count;
|
527
|
+
}
|
528
|
+
else if(threads > 0)
|
529
|
+
{
|
530
|
+
num_threads = threads;
|
531
|
+
}
|
532
|
+
else
|
533
|
+
{
|
534
|
+
num_threads = ph_num_threads();
|
535
|
+
}
|
536
|
+
|
537
|
+
DP **hashes = (DP**)malloc(count*sizeof(DP*));
|
538
|
+
|
539
|
+
for(int i = 0; i < count; ++i)
|
540
|
+
{
|
541
|
+
hashes[i] = (DP *)malloc(sizeof(DP));
|
542
|
+
hashes[i]->id = strdup(files[i]);
|
543
|
+
}
|
544
|
+
|
545
|
+
pthread_t thds[num_threads];
|
546
|
+
|
547
|
+
int rem = count % num_threads;
|
548
|
+
int start = 0;
|
549
|
+
int off = 0;
|
550
|
+
slice *s = new slice[num_threads];
|
551
|
+
for(int n = 0; n < num_threads; ++n)
|
552
|
+
{
|
553
|
+
off = (int)floor((count/(float)num_threads) + (rem>0?num_threads-(count % num_threads):0));
|
554
|
+
|
555
|
+
s[n].hash_p = &hashes[start];
|
556
|
+
s[n].n = off;
|
557
|
+
s[n].hash_params = new pair<int,int>(sr,channels);
|
558
|
+
start += off;
|
559
|
+
--rem;
|
560
|
+
pthread_create(&thds[n], NULL, ph_audio_thread, &s[n]);
|
561
|
+
}
|
562
|
+
for(int i = 0; i < num_threads; ++i)
|
563
|
+
{
|
564
|
+
pthread_join(thds[i], NULL);
|
565
|
+
delete (pair<int,int>*)s[i].hash_params;
|
566
|
+
}
|
567
|
+
delete[] s;
|
568
|
+
|
569
|
+
return hashes;
|
570
|
+
|
571
|
+
}
|
572
|
+
#endif
|