/* utilities.c * atsa: ATS analysis implementation * Oscar Pablo Di Liscia / Pete Moss / Juan Pampin */ #include "atsa.h" #include #include #include #include /* various usefull macros */ #define freez(p) if(p){free(p); p=0;} #define swap(type, a, b) { type tmp = (a); (a) = (b); (b) = tmp; } /* private function prototypes */ int find_next_val_arr(double* arr, int beg, int size); int find_next_zero_arr(double* arr, int beg, int size); int find_prev_val_arr(double* arr, int beg); void fill_sound_gaps(ATS_SOUND *sound, int min_gap_len); void trim_partials(ATS_SOUND *sound, int min_seg_len, double min_seg_smr); void set_av(ATS_SOUND *sound); /* various conversion functions * to deal with dB and dB SPL * they take and return double doubles */ double amp2db(double amp) { return (20* log10(amp)); } double db2amp(double db) { return (pow(10, db/20)); } double amp2db_spl(double amp) { return(amp2db(amp) + ATSA_MAX_DB_SPL); } double db2amp_spl(double db_spl) { return(db2amp(db_spl - ATSA_MAX_DB_SPL)); } /* ppp2 * ==== * returns the closest power of two * greater than num */ unsigned int ppp2(unsigned int num) { unsigned int tmp = 2; while(tmpframes; frame++) for(partial=0; partialpartials; partial++) { if(ampmax < sound->amp[partial][frame]) ampmax = sound->amp[partial][frame]; if(frqmax < sound->frq[partial][frame]) frqmax = sound->frq[partial][frame]; } sound->ampmax = ampmax; sound->frqmax = frqmax; fill_sound_gaps(sound, anargs->min_gap_len); trim_partials(sound, anargs->min_seg_len, anargs->min_seg_SMR); set_av(sound); /* finally set slot to 1 */ sound->optimized = 1; } /* fill_sound_gaps * =============== * fills gaps in ATS_SOUND partials by interpolation * sound: pointer to ATS_SOUND * min_gap_len: minimum gap length, gaps shorter or equal to this * value will be filled in by interpolation */ void fill_sound_gaps(ATS_SOUND *sound, int min_gap_len) { int i, j, k, next_val, next_zero, prev_val, gap_size; double f_inc, a_inc, s_inc, mag; mag = TWOPI / (double)sound->srate; fprintf(stderr, "Filling sound gaps...\n"); for(i = 0 ; i < sound->partials ; i++){ /* first we fix the freq gap before attack */ next_val = find_next_val_arr(sound->frq[i], 0, sound->frames); if( next_val > 0 ){ for( j = 0 ; j < next_val ; j++ ){ sound->frq[i][j] = sound->frq[i][next_val]; } } /* fix the freq gap at end */ prev_val = find_prev_val_arr(sound->frq[i], sound->frames-1); if( prev_val != NIL && prev_val < sound->frames-1 ){ for( j = prev_val ; j < sound->frames ; j++ ){ sound->frq[i][j] = sound->frq[i][prev_val]; } } /* now we fix inner gaps of frq, pha, and amp */ k = find_next_val_arr(sound->amp[i], 0, sound->frames); while( k < sound->frames && k != NIL){ /* find next gap: we consider gaps in amplitude, we fix frequency and phase in parallel */ next_zero = find_next_zero_arr(sound->amp[i], k, sound->frames); if( next_zero != NIL){ prev_val = next_zero - 1; next_val = find_next_val_arr(sound->amp[i], next_zero, sound->frames); /* check if we didn't get to end of array */ if( next_val == NIL) break; gap_size = next_val - prev_val; // fprintf(stderr, "par: %d prev_val: %d next_val: %d gap_size %d\n", // i, prev_val, next_val, gap_size); /* check validity of found gap */ if( gap_size <= min_gap_len){ // fprintf(stderr, "Filling gap of par: %d\n", i); f_inc = (sound->frq[i][next_val] - sound->frq[i][prev_val]) / gap_size; a_inc = (sound->amp[i][next_val] - sound->amp[i][prev_val]) / gap_size; s_inc = (sound->smr[i][next_val] - sound->smr[i][prev_val]) / gap_size; for( j = next_zero ; j < next_val ; j++){ sound->frq[i][j] = sound->frq[i][j-1] + f_inc; sound->amp[i][j] = sound->amp[i][j-1] + a_inc; sound->smr[i][j] = sound->smr[i][j-1] + s_inc; sound->pha[i][j] = sound->pha[i][j-1] - (sound->frq[i][j] * sound->frame_size * mag); /* wrap phase */ while(sound->pha[i][j] > PI){ sound->pha[i][j] -= TWOPI; } while(sound->pha[i][j] < (PI * -1.0)){ sound->pha[i][j] += TWOPI; } } /* gap fixed, find next gap */ k = next_val; } else { /* gap not valid, move to next one */ // fprintf(stderr, "jumping to next_val: %d\n", next_val); k = next_val; } } else { break; } } } } /* trim_partials * ============= * trim short segments from ATS_SOUND partials * sound: pointer to ATS_SOUND * min_seg_len: minimum segment length, segments shorter or equal * to this value will be candidates for trimming * min_seg_smr: minimum segment average SMR, segment candidates * should have an average SMR below this value to be trimmed */ void trim_partials(ATS_SOUND *sound, int min_seg_len, double min_seg_smr) { int i, j, k, seg_beg, seg_end, seg_size, count=0; double val=0.0, smr_av=0.0; fprintf(stderr, "Trimming short partials...\n"); for(i = 0 ; i < sound->partials ; i++){ k = 0; while( k < sound->frames ){ /* find next segment */ seg_beg = find_next_val_arr(sound->amp[i], k, sound->frames); if( seg_beg != NIL){ seg_end = find_next_zero_arr(sound->amp[i], seg_beg, sound->frames); /* check if we didn't get to end of array */ if( seg_end == NIL) seg_end = sound->frames; seg_size = seg_end - seg_beg; // fprintf(stderr, "par: %d seg_beg: %d seg_end: %d seg_size %d\n", // i, seg_beg, seg_end, seg_size); if( seg_size <= min_seg_len ){ for( j = seg_beg ; j < seg_end ; j++){ if( sound->smr[i][j] > 0.0 ){ val += sound->smr[i][j]; count++; } } if(count > 0) smr_av = val/(double)count; if( smr_av < min_seg_smr ){ /* trim segment, only amplitude and SMR data */ // fprintf(stderr, "Trimming par: %d\n", i); for( j = seg_beg ; j < seg_end ; j++){ sound->amp[i][j] = (double)0.0; sound->smr[i][j] = (double)0.0; } } k = seg_end; } else { /* segment not valid, move to the next one */ // fprintf(stderr, "jumping to seg_end: %d\n", seg_end); k = seg_end; } } else { break; } } } } /* auxiliary functions to fill_sound_gaps and trim_partials */ int find_next_val_arr(double* arr, int beg, int size) { int j, next_val=NIL; for( j = beg ; j < size ; j++){ if( arr[j] > 0.0 ){ next_val = j; break; } } return(next_val); } int find_next_zero_arr(double* arr, int beg, int size) { int j, next_zero=NIL; for( j = beg ; j < size ; j++){ if( arr[j] == 0.0 ){ next_zero = j; break; } } return(next_zero); } int find_prev_val_arr(double* arr, int beg) { int j, prev_val=NIL; for( j = beg ; j >= 0 ; j--){ if( arr[j] != 0.0 ){ prev_val = j; break; } } return(prev_val); } /* set_av * ====== * sets the av structure slot of an ATS_SOUND, * it computes the average ampl., freq. and SMR for each partial * sound: pointer to ATS_SOUND structure */ void set_av(ATS_SOUND *sound) { int i, j, count; double val; fprintf(stderr,"Computing averages..\n"); for( i = 0 ; i < sound->partials ; i++){ /* amp */ val=0.0; count = 0; for(j = 0 ; j < sound->frames ; j++){ if(sound->amp[i][j] > 0.0){ val += sound->amp[i][j]; count++; } } if(count > 0){ sound->av[i].amp = val/(double)count; } else { sound->av[i].amp = (double)0.0; } // fprintf(stderr,"par: %d amp_av: %f\n", i, (double)sound->av[i].amp); /* smr */ val=0.0; count = 0; for(j = 0 ; j < sound->frames ; j++){ if(sound->smr[i][j] > 0.0){ val += sound->smr[i][j]; count++; } } if(count > 0){ sound->av[i].smr = val/(double)count; } else { sound->av[i].smr = (double)0.0; } // fprintf(stderr,"par: %d smr_av: %f\n", i, (double)sound->av[i].smr); /* frq */ val=0.0; count = 0; for(j = 0 ; j < sound->frames ; j++){ if(sound->frq[i][j] > 0.0){ val += sound->frq[i][j]; count++; } } if(count > 0){ sound->av[i].frq = val/(double)count; } else { sound->av[i].frq = (double)0.0; } /* set track# */ sound->av[i].track = i; } /* sort partials by increasing frequency */ qsort(sound->av, sound->partials, sizeof(ATS_PEAK), peak_frq_inc); } /* init_sound * ========== * initializes a new sound allocating memory (WARNING: sound pointer must be allocated first) */ void init_sound(ATS_SOUND *sound, int sampling_rate, int frame_size, int window_size, int frames, double duration, int partials, int with_noise) { int i, j, k; sound->srate = sampling_rate; sound->frame_size = frame_size; sound->window_size = window_size; sound->frames = frames; sound->dur = duration; sound->partials = partials; sound->max_partials = partials; sound->optimized = NIL; sound->ampmax = NIL; sound->frqmax = NIL; sound->av = (ATS_PEAK *)malloc(partials * sizeof(ATS_PEAK)); sound->time = (double **)malloc(partials * sizeof(double *)); sound->frq = (double **)malloc(partials * sizeof(double *)); sound->amp = (double **)malloc(partials * sizeof(double *)); sound->pha = (double **)malloc(partials * sizeof(double *)); sound->smr = (double **)malloc(partials * sizeof(double *)); sound->res = (double **)malloc(partials * sizeof(double *)); /* allocate memory for time, amp, frq, smr, and res data */ for(k=0; ktime[k] = (double *)malloc(frames * sizeof(double)); sound->amp[k] = (double *)malloc(frames * sizeof(double)); sound->frq[k] = (double *)malloc(frames * sizeof(double)); sound->pha[k] = (double *)malloc(frames * sizeof(double)); sound->smr[k] = (double *)malloc(frames * sizeof(double)); sound->res[k] = (double *)malloc(frames * sizeof(double)); } /* init all array values with 0.0 */ for(i = 0 ; i < partials ; i++){ for(j = 0 ; j < frames ; j++){ sound->amp[i][j] = (double)0.0; sound->frq[i][j] = (double)0.0; sound->pha[i][j] = (double)0.0; sound->smr[i][j] = (double)0.0; sound->res[i][j] = (double)0.0; } } /* init critical bands noise array if necessary */ if(with_noise) { sound->band_energy = (double **)malloc(ATSA_CRITICAL_BANDS * sizeof(double *)); for (i = 0; i < ATSA_CRITICAL_BANDS; i++) sound->band_energy[i] = (double *)malloc(frames * sizeof(double)); } else sound->band_energy = 0; } /* copy_sound * ========== * make a full copy of an existing sound and return a pointer to it */ ATS_SOUND *copy_sound(ATS_SOUND *sound) { int i, k; ATS_SOUND* sound_copy = (ATS_SOUND*)malloc(sizeof(ATS_SOUND)); /* Initialize sound copy (alloc memory) */ init_sound(sound_copy, sound->srate, sound->frame_size, sound->window_size, sound->frames, sound->dur, sound->partials, sound->band_energy != 0); /* Copy data from source sound */ sound_copy->optimized = sound->optimized; sound_copy->ampmax = sound->ampmax; sound_copy->frqmax = sound->frqmax; memcpy(sound_copy->av, sound->av, sound->partials * sizeof(ATS_PEAK)); for(k=0; kpartials; k++) { memcpy(sound_copy->time[k], sound->time[k], sound->frames * sizeof(double)); memcpy(sound_copy->amp[k], sound->amp[k], sound->frames * sizeof(double)); memcpy(sound_copy->frq[k], sound->frq[k], sound->frames * sizeof(double)); memcpy(sound_copy->pha[k], sound->pha[k], sound->frames * sizeof(double)); memcpy(sound_copy->smr[k], sound->smr[k], sound->frames * sizeof(double)); memcpy(sound_copy->res[k], sound->res[k], sound->frames * sizeof(double)); } if(sound->band_energy) for (i = 0; i < ATSA_CRITICAL_BANDS; i++) memcpy(sound_copy->band_energy[i], sound->band_energy[i], sound->frames * sizeof(double)); return sound_copy; } /* increase_max_partials * ===================== * increases by a given offset the max supported number of partials * by reallocating and copying associated partials data arrays * (WARNING: sound must have been initialized first) */ void increase_max_partials(ATS_SOUND *sound, unsigned int offset) { int k; int new_max_partials = sound->max_partials + offset; /* Reallocate each partial data array, and keep old data unchanged */ sound->av = (ATS_PEAK *)realloc(sound->av, new_max_partials * sizeof(ATS_PEAK)); sound->time = (double **)realloc(sound->time, new_max_partials * sizeof(double *)); sound->frq = (double **)realloc(sound->frq, new_max_partials * sizeof(double *)); sound->amp = (double **)realloc(sound->amp, new_max_partials * sizeof(double *)); sound->pha = (double **)realloc(sound->pha, new_max_partials * sizeof(double *)); sound->smr = (double **)realloc(sound->smr, new_max_partials * sizeof(double *)); sound->res = (double **)realloc(sound->res, new_max_partials * sizeof(double *)); /* allocate memory for time, amp, frq, smr, and res data for new possible partials */ for(k=sound->max_partials; ktime[k] = (double *)malloc(sound->frames * sizeof(double)); sound->amp[k] = (double *)malloc(sound->frames * sizeof(double)); sound->frq[k] = (double *)malloc(sound->frames * sizeof(double)); sound->pha[k] = (double *)malloc(sound->frames * sizeof(double)); sound->smr[k] = (double *)malloc(sound->frames * sizeof(double)); sound->res[k] = (double *)malloc(sound->frames * sizeof(double)); } /* save new max number of partials */ sound->max_partials = new_max_partials; } /* free_sound * ========== * frees sound's memory (WARNING: sound pointer is not deallocated) */ void free_sound(ATS_SOUND *sound) { int k; freez(sound->av); /* data */ for(k=0; k < sound->max_partials; k++) { freez(sound->time[k]); freez(sound->amp[k]); freez(sound->frq[k]); freez(sound->pha[k]); freez(sound->smr[k]); freez(sound->res[k]); } /* pointers to data */ freez(sound->time); freez(sound->frq); freez(sound->amp); freez(sound->pha); freez(sound->smr); freez(sound->res); /* check if we have residual data and free its memory up */ if(sound->band_energy){ for(k=0 ; kband_energy[k]); } freez(sound->band_energy); } } /* add_partial * ============ * creates a new partial that is the copy of the one with given source index if valid ; * invalid source indexes (<0 or >=nb partials) give a new partial with null values * (reallocates partials data arrays as necessary, by an offset of ATSA_INCROFFSET, * recomputes the partial average data and maintains the partial sorting by increasing freq.) */ void add_partial(ATS_SOUND *sound, int src_index) { int tgt_index = sound->partials; /* reallocate ATS_SOUND if not enough room for the new partial */ if (sound->partials == sound->max_partials) increase_max_partials(sound, ATSA_INCROFFSET); /* copy source partial at the end of the list if its index is valid */ if (src_index >= 0 && src_index < sound->partials) { memcpy(sound->time[tgt_index], sound->time[src_index], sound->frames * sizeof(double)); memcpy(sound->frq[tgt_index], sound->frq[src_index], sound->frames * sizeof(double)); memcpy(sound->amp[tgt_index], sound->amp[src_index], sound->frames * sizeof(double)); memcpy(sound->pha[tgt_index], sound->pha[src_index], sound->frames * sizeof(double)); memcpy(sound->smr[tgt_index], sound->smr[src_index], sound->frames * sizeof(double)); memcpy(sound->res[tgt_index], sound->res[src_index], sound->frames * sizeof(double)); } else { /* other wise, set it to 0 everywhere */ memset(sound->time[tgt_index], 0, sound->frames * sizeof(double)); memset(sound->frq[tgt_index], 0, sound->frames * sizeof(double)); memset(sound->amp[tgt_index], 0, sound->frames * sizeof(double)); memset(sound->pha[tgt_index], 0, sound->frames * sizeof(double)); memset(sound->smr[tgt_index], 0, sound->frames * sizeof(double)); memset(sound->res[tgt_index], 0, sound->frames * sizeof(double)); } /* update the number of partials */ sound->partials++; /* update average data and resort partials by increasing frequency */ set_av(sound); } /* remove_partials * =============== * removes the partials of given indexes * (recompute the partial average data and maintain the partial sorting by increasing freq). */ void remove_partials(ATS_SOUND *sound, int* rem_indexes, int nb_rem_indexes) { int part, kept, ind; int* kept_indexes; int nb_kept; /* if nothing to do, do nothing */ if (!rem_indexes || nb_rem_indexes <= 0) return; /* determine the indexes of the partials to keep, sorted by increasing index */ kept_indexes = (int*)malloc(sound->partials*sizeof(int)); nb_kept = 0; for (part=0; partpartials; part++) { for (ind = 0; ind < nb_rem_indexes && rem_indexes[ind] != part; ind++); if (ind == nb_rem_indexes) /* Is this partial to be kept ? */ kept_indexes[nb_kept++] = part; } /* for each partial to keep : */ part = 0; for (ind = 0; ind < nb_kept; ind++) { /* if the partial index changes, move the partial */ kept = kept_indexes[ind]; if (kept != part) { swap(double*, sound->time[part], sound->time[kept]); swap(double*, sound->frq[part], sound->frq[kept]); swap(double*, sound->amp[part], sound->amp[kept]); swap(double*, sound->pha[part], sound->pha[kept]); swap(double*, sound->smr[part], sound->smr[kept]); swap(double*, sound->res[part], sound->res[kept]); } /* increment kept partials insertion index */ part++; } /* free now useless kept partials index array */ freez(kept_indexes); /* update the number of partials */ sound->partials = nb_kept; /* update average data and resort partials by increasing frequency */ set_av(sound); }