578 lines
18 KiB
C
578 lines
18 KiB
C
/* utilities.c
|
|
* atsa: ATS analysis implementation
|
|
* Oscar Pablo Di Liscia / Pete Moss / Juan Pampin
|
|
*/
|
|
|
|
#include "atsa.h"
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* 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(tmp<num) tmp = tmp << 1;
|
|
return(tmp);
|
|
}
|
|
|
|
/* optimize_sound
|
|
* ==============
|
|
* optimizes an ATS_SOUND in memory before saving
|
|
* anargs: pointer to analysis parameters
|
|
* sound: pointer to ATS_SOUND structure
|
|
*/
|
|
void optimize_sound(ANARGS *anargs, ATS_SOUND *sound)
|
|
{
|
|
double ampmax = 0.0, frqmax = 0.0;
|
|
int frame, partial;
|
|
|
|
for(frame=0; frame<sound->frames; frame++)
|
|
for(partial=0; partial<sound->partials; 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; k<partials; k++) {
|
|
sound->time[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; k<sound->partials; 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; k<new_max_partials; k++) {
|
|
sound->time[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 ; k<ATSA_CRITICAL_BANDS ; k++){
|
|
freez(sound->band_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; part<sound->partials; 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);
|
|
}
|