/* pHash, the open source perceptual hash library Copyright (C) 2009 Aetilius, Inc. All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Evan Klinger - eklinger@phash.org David Starkweather - dstarkweather@phash.org */ #include "audiophash.h" #include #include #ifdef HAVE_LIBMPG123 #include #endif int ph_count_samples(const char *filename, int sr,int channels){ SF_INFO sf_info; sf_info.format=0; SNDFILE *sndfile = sf_open(filename, SFM_READ, &sf_info); if (sndfile == NULL){ return NULL; } int count = sf_info.frames; sf_close(sndfile); return count; } #ifdef HAVE_LIBMPG123 static float* readaudio_mp3(const char *filename,long *sr, const float nbsecs, unsigned int *buflen){ mpg123_handle *m; int ret; if (mpg123_init() != MPG123_OK || ((m = mpg123_new(NULL,&ret)) == NULL)|| \ mpg123_open(m, filename) != MPG123_OK){ fprintf(stderr,"unable to init mpg\n"); return NULL; } /*turn off logging */ mpg123_param(m, MPG123_ADD_FLAGS, MPG123_QUIET, 0); off_t totalsamples; mpg123_scan(m); totalsamples = mpg123_length(m); int meta = mpg123_meta_check(m); int channels, encoding; if (mpg123_getformat(m, sr, &channels, &encoding) != MPG123_OK){ fprintf(stderr,"unable to get format\n"); return NULL; } mpg123_format_none(m); mpg123_format(m, *sr, channels, encoding); size_t decbuflen = mpg123_outblock(m); unsigned char *decbuf = (unsigned char*)malloc(decbuflen); if (decbuf == NULL){ printf("mem alloc error\n"); return NULL; } unsigned int nbsamples = (nbsecs <= 0) ? totalsamples : nbsecs*(*sr); nbsamples = (nbsamples < totalsamples) ? nbsamples : totalsamples; size_t i, j, index = 0, done; float *buffer = (float*)malloc(nbsamples*sizeof(float)); *buflen = nbsamples; do { ret = mpg123_read(m, decbuf, decbuflen, &done); switch (encoding) { case MPG123_ENC_SIGNED_16 : for (i = 0; i < done/sizeof(short); i+=channels){ buffer[index] = 0.0f; for (j = 0; j < channels ; j++){ buffer[index] += (float)(((short*)decbuf)[i+j])/(float)SHRT_MAX; } buffer[index++] /= channels; if (index >= nbsamples) break; } break; case MPG123_ENC_SIGNED_8: for (i = 0; i < done/sizeof(char); i+=channels){ buffer[index] = 0.0f; for (j = 0; j < channels ; j++){ buffer[index] += (float)(((char*)decbuf)[i+j])/(float)SCHAR_MAX; } buffer[index++] /= channels; if (index >= nbsamples) break; } break; case MPG123_ENC_FLOAT_32: for (i = 0; i < done/sizeof(float); i+=channels){ buffer[index] = 0.0f; for (j = 0; j < channels; j++){ buffer[index] += ((float*)decbuf)[i+j]; } buffer[index++] /= channels; if (index >= nbsamples) break; } break; default: done = 0; } } while (ret == MPG123_OK && index < nbsamples); free(decbuf); mpg123_close(m); mpg123_delete(m); mpg123_exit(); return buffer; } #endif /*HAVE_LIBMPG123*/ static float *readaudio_snd(const char *filename, long *sr, const float nbsecs, unsigned int *buflen){ SF_INFO sf_info; sf_info.format=0; SNDFILE *sndfile = sf_open(filename, SFM_READ, &sf_info); if (sndfile == NULL){ return NULL; } /* normalize */ sf_command(sndfile, SFC_SET_NORM_FLOAT, NULL, SF_TRUE); *sr = (long)sf_info.samplerate; //allocate input buffer for signal unsigned int src_frames = (nbsecs <= 0) ? sf_info.frames : (nbsecs*sf_info.samplerate); src_frames = (sf_info.frames < src_frames) ? sf_info.frames : src_frames; float *inbuf = (float*)malloc(src_frames*sf_info.channels*sizeof(float)); /*read frames */ sf_count_t cnt_frames = sf_readf_float(sndfile, inbuf, src_frames); float *buf = (float*)malloc(cnt_frames*sizeof(float)); //average across all channels int i,j,indx=0; for (i=0;i maxF){ maxF = magnF[i]; } } for (int i=0;i maxB) maxB = curr_bark[i]; } uint32_t curr_hash = 0x00000000u; for (int m=0;m 0) curr_hash |= 0x00000001; } hash[index] = curr_hash; for (int i=0;i> 1) & MASK_01010101) ; n = (n & MASK_00110011) + ((n >> 2) & MASK_00110011) ; n = (n & MASK_00001111) + ((n >> 4) & MASK_00001111) ; return n % 255; } double ph_compare_blocks(const uint32_t *ptr_blockA,const uint32_t *ptr_blockB, const int block_size){ double result = 0; for (int i=0;in; ++i) { DP *dp = (DP *)s->hash_p[i]; int N, count; pair *p = (pair *)s->hash_params; float *buf = ph_readaudio(dp->id, p->first, p->second, NULL, N); uint32_t *hash = ph_audiohash(buf, N, p->first, count); free(buf); buf = NULL; dp->hash = hash; dp->hash_length = count; } } DP** ph_audio_hashes(char *files[], int count, int sr, int channels, int threads) { if(!files || count == 0) return NULL; int num_threads; if(threads > count) { num_threads = count; } else if(threads > 0) { num_threads = threads; } else { num_threads = ph_num_threads(); } DP **hashes = (DP**)malloc(count*sizeof(DP*)); for(int i = 0; i < count; ++i) { hashes[i] = (DP *)malloc(sizeof(DP)); hashes[i]->id = strdup(files[i]); } pthread_t thds[num_threads]; int rem = count % num_threads; int start = 0; int off = 0; slice *s = new slice[num_threads]; for(int n = 0; n < num_threads; ++n) { off = (int)floor((count/(float)num_threads) + (rem>0?num_threads-(count % num_threads):0)); s[n].hash_p = &hashes[start]; s[n].n = off; s[n].hash_params = new pair(sr,channels); start += off; --rem; pthread_create(&thds[n], NULL, ph_audio_thread, &s[n]); } for(int i = 0; i < num_threads; ++i) { pthread_join(thds[i], NULL); delete (pair*)s[i].hash_params; } delete[] s; return hashes; } #endif