ref: 98267de439714fcdff94c9588f34fb8a67df70c1
dir: /src/polyphas.c/
/* * July 14, 1998 * Copyright 1998 K. Bradley, Carnegie Mellon University * * This source code is freely redistributable and may be used for * any purpose. This copyright notice must be maintained. * Lance Norskog And Sundry Contributors are not responsible for * the consequences of using this software. */ /* * Sound Tools rate change effect file. */ #include <math.h> #include <stdio.h> #include <stdlib.h> #include "st.h" typedef struct _list { int number; float *data_buffer; struct _list *next; } List; typedef struct polyphase { unsigned long lcmrate; /* least common multiple of rates */ unsigned long inskip, outskip; /* LCM increments for I & O rates */ unsigned long total; unsigned long intot, outtot; /* total samples in terms of LCM rate */ long lastsamp; float **filt_array; float **past_hist; float *input_buffer; int *filt_len; List *l1, *l2; } *poly_t; /* * Process options */ /* Options: -w <nut / ham> : window type -width <short / long> : window width short = 128 samples long = 1024 samples <num> num: explicit number -cutoff <float> : frequency cutoff for base bandwidth. Default = 0.95 = 95% */ static int win_type = 0; static int win_width = 1024; static float cutoff = 0.95; void poly_getopts(effp, n, argv) eff_t effp; int n; char **argv; { /* 0: nuttall 1: hamming */ win_type = 0; /* width: short = 128 long = 1024 (default) */ win_width = 1024; /* cutoff: frequency cutoff of base bandwidth in percentage. */ cutoff = 0.95; while(n >= 2) { /* Window type check */ if(!strcmp(argv[0], "-w")) { if(!strcmp(argv[1], "ham")) win_type = 1; if(!strcmp(argv[1], "nut")) win_type = 0; argv += 2; n -= 2; continue; } /* Window width check */ if(!strcmp(argv[0], "-width")) { if(!strcmp(argv[1], "short")) win_width = 128; else if(!strcmp(argv[1], "long")) win_width = 1024; else win_width = atoi(argv[1]); argv += 2; n -= 2; continue; } /* Cutoff frequency check */ if(!strcmp(argv[0], "-cutoff")) { cutoff = atof(argv[1]); argv += 2; n -= 2; continue; } fail("Polyphase: unknown argument (%s %s)!", argv[0], argv[1]); } } /* * Prepare processing. */ static int primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997 }; #ifndef max #define max(x,y) ((x > y) ? x : y) #endif List *prime(number) int number; { int j; List *element = NULL; if(number == 1) return NULL; for(j=167;j>= 0;j--) { if(number % primes[j] == 0) { element = (List *) malloc(sizeof(List)); element->number = primes[j]; element->data_buffer = NULL; element->next = prime(number / primes[j]); break; } } if(element == NULL) { fail("Number %d too large of a prime.\n",number); } return element; } List *prime_inv(number) int number; { int j; List *element = NULL; if(number == 1) return NULL; for(j=0;j<168;j++) { if(number % primes[j] == 0) { element = (List *) malloc(sizeof(List)); element->number = primes[j]; element->data_buffer = NULL; element->next = prime_inv(number / primes[j]); break; } } if(element == NULL) { fail("Number %d too large of a prime.\n",number); } return element; } #ifndef PI #define PI 3.14159265358979 #endif /* Calculate a Nuttall window of a given length. Buffer must already be allocated to appropriate size. */ void nuttall(buffer, length) float *buffer; int length; { int j; double N; double N1; if(buffer == NULL || length < 0) fail("Illegal buffer %p or length %d to nuttall.\n", buffer, length); /* Initial variable setups. */ N = (double) length - 1.0; N1 = N / 2.0; for(j = 0; j < length; j++) { buffer[j] = 0.36335819 + 0.4891775 * cos(2*PI*1*(j - N1) / N) + 0.1365995 * cos(2*PI*2*(j - N1) / N) + 0.0106411 * cos(2*PI*3*(j - N1) / N); } } /* Calculate a Hamming window of given length. Buffer must already be allocated to appropriate size. */ void hamming(buffer, length) float *buffer; int length; { int j; if(buffer == NULL || length < 0) fail("Illegal buffer %p or length %d to hamming.\n",buffer,length); for(j=0;j<length;j++) buffer[j] = 0.5 - 0.46 * cos(2*PI*j/(length-1)); } /* Calculate the sinc function properly */ float sinc(value) float value; { return(fabs(value) < 1E-50 ? 1.0 : sin(value) / value); } /* Design a low-pass FIR filter using window technique. Length of filter is in length, cutoff frequency in cutoff. 0 < cutoff <= 1.0 (normalized frequency) buffer must already be allocated. */ void fir_design(buffer, length, cutoff) float *buffer; int length; float cutoff; { int j; float sum; float *ham_win; if(buffer == NULL || length < 0 || cutoff < 0 || cutoff > PI) fail("Illegal buffer %p, length %d, or cutoff %f.\n",buffer,length,cutoff); /* Design Hamming window: 43 dB cutoff */ ham_win = (float *)malloc(sizeof(float) * length); /* Use the user-option of window type */ if(win_type == 0) nuttall(ham_win, length); else hamming(ham_win,length); /* Design filter: windowed sinc function */ sum = 0.0; for(j=0;j<length;j++) { buffer[j] = sinc(PI*cutoff*(j-length/2)) * ham_win[j] / (2*cutoff); sum += buffer[j]; } /* Normalize buffer to have gain of 1.0: prevent roundoff error */ for(j=0;j<length;j++) buffer[j] /= sum; free((void *) ham_win); } void poly_start(effp) eff_t effp; { poly_t rate = (poly_t) effp->priv; List *t, *t2; int num_l1, num_l2; int j,k; float f_cutoff; extern long lcm(); rate->lcmrate = lcm((long)effp->ininfo.rate, (long)effp->outinfo.rate); /* Cursory check for LCM overflow. * If both rate are below 65k, there should be no problem. * 16 bits x 16 bits = 32 bits, which we can handle. */ rate->inskip = rate->lcmrate / effp->ininfo.rate; rate->outskip = rate->lcmrate / effp->outinfo.rate; /* Find the prime factors of inskip and outskip */ rate->l1 = prime(rate->inskip); /* If we're going up, order things backwards. */ if(effp->ininfo.rate < effp->outinfo.rate) rate->l2 = prime_inv(rate->outskip); else rate->l2 = prime(rate->outskip); /* Find how many factors there were */ if(rate->l1 == NULL) num_l1 = 0; else for(num_l1=0, t = rate->l1; t != NULL; num_l1++, t=t->next); if(rate->l2 == NULL) num_l2 = 0; else for(num_l2=0, t = rate->l2; t != NULL; num_l2++, t=t->next); k = 0; t = rate->l1; /* Compact the lists to be less than 10 */ while(k < num_l1 - 1) { if(t->number * t->next->number < 10) { t->number = t->number * t->next->number; t2 = t->next; t->next = t->next->next; t2->next = NULL; free((void *) t2); num_l1--; } else { k++; t = t->next; } } k = 0; t = rate->l2; while(k < num_l2 - 1) { if(t->number * t->next->number < 10) { t->number = t->number * t->next->number; t2 = t->next; t->next = t->next->next; t2->next = NULL; free((void *) t2); num_l2--; } else { k++; t = t->next; } } /* l1 and l2 are now lists of the prime factors compacted, meaning that they're the lists of up/down sampling we need */ /* Stretch them to be the same length by padding with 1 (no-op) */ if(num_l1 < num_l2) { t = rate->l1; if(t == NULL) { rate->l1 = (List *)malloc(sizeof(List)); rate->l1->next = NULL; rate->l1->number = 1; rate->l1->data_buffer = NULL; t = rate->l1; num_l1++; } while(t->next != NULL) t = t->next; for(k=0;k<num_l2-num_l1;k++) { t->next = (List *) malloc(sizeof(List)); t->next->number = 1; t->next->data_buffer = NULL; t = t->next; } t->next = NULL; num_l1 = num_l2; } else { t = rate->l2; if(t == NULL) { rate->l2 = (List *)malloc(sizeof(List)); rate->l2->next = NULL; rate->l2->number = 1; rate->l2->data_buffer = NULL; t = rate->l2; num_l2++; } /* while(t->next != NULL) t = t->next; */ for(k=0;k<num_l1-num_l2;k++) { t = rate->l2; rate->l2 = (List *) malloc(sizeof(List)); rate->l2->number = 1; rate->l2->data_buffer = NULL; rate->l2->next = t; } /* t->next = NULL; */ num_l2 = num_l1; } /* l1 and l2 are now the same size. */ rate->total = num_l1; report("Poly: input rate %d, output rate %d. %d stages.",effp->ininfo.rate, effp->outinfo.rate,num_l1); report("Poly: window: %s size: %d cutoff: %f.", (win_type == 0) ? ("nut") : ("ham"), win_width, cutoff); for(k=0, t=rate->l1, t2=rate->l2;k<num_l1;k++,t=t->next,t2=t2->next) report("Poly: stage %d: Up by %d, down by %d.",k+1,t->number,t2->number); /* We'll have an array of filters and past history */ rate->filt_array = (float **) malloc(sizeof(float *) * num_l1); rate->past_hist = (float **) malloc(sizeof(float *) * num_l1); rate->filt_len = (int *) malloc(sizeof(int) * num_l1); for(k = 0, t = rate->l1, t2 = rate->l2; k < num_l1; k++) { rate->filt_len[k] = max(2 * 10 * max(t->number,t2->number), win_width); rate->filt_array[k] = (float *) malloc(sizeof(float) * rate->filt_len[k]); rate->past_hist[k] = (float *) malloc(sizeof(float) * rate->filt_len[k]); t->data_buffer = (float *) malloc(sizeof(float) * 1024 * rate->inskip); for(j = 0; j < rate->filt_len[k]; j++) rate->past_hist[k][j] = 0.0; f_cutoff = (t->number > t2->number) ? (float) t->number : (float) t2->number; fir_design(rate->filt_array[k], rate->filt_len[k]-1, cutoff / f_cutoff); t = t->next; t2 = t2->next; } rate->input_buffer = (float *) malloc(sizeof(float) * 2048); } /* * Processed signed long samples from ibuf to obuf. * Return number of samples processed. */ static float *h; static int M, L, N; void polyphase_init(coef, num_coef, up_rate, down_rate) float *coef; int num_coef; int up_rate; int down_rate; { h = coef; M = down_rate; L = up_rate; N = num_coef; } void polyphase(input, output, past, num_samples_input) float *input; float *output; float *past; int num_samples_input; { int num_output; int m,n; float sum; float inp; int base; int h_base; num_output = num_samples_input * L / M; for(m=0;m<num_output;m++) { sum = 0.0; base = (int) (m*M/L); h_base = (m*M) % L; for(n=0;n<N / L;n++) { if(base - n < 0) inp = past[base - n + N]; else inp = input[base - n]; sum += h[n*L + h_base] * inp; } output[m] = sum * L * 0.95; } } void poly_flow(effp, ibuf, obuf, isamp, osamp) eff_t effp; long *ibuf, *obuf; int *isamp, *osamp; { poly_t rate = (poly_t) effp->priv; float *temp_buf, *temp_buf2; int j,k; List *t1, *t2; int in_size, out_size; /* Sanity check: how much can we tolerate? */ in_size = *isamp; out_size = in_size * rate->inskip / rate->outskip; if(out_size > *osamp) { in_size = *osamp * rate->outskip / rate->inskip; *isamp = in_size; } /* Check to see if we're really draining */ if(ibuf != NULL) { for(k=0;k<*isamp;k++) rate->input_buffer[k] = (float) (ibuf[k] >> 16); } else { for(k=0;k<*isamp;k++) rate->input_buffer[k] = 0.0; } temp_buf = rate->input_buffer; t1 = rate->l1; t2 = rate->l2; for(k=0;k<rate->total;k++,t1=t1->next,t2=t2->next) { polyphase_init(rate->filt_array[k], rate->filt_len[k], t1->number,t2->number); out_size = (in_size) * t1->number / t2->number; temp_buf2 = t1->data_buffer; polyphase(temp_buf, temp_buf2, rate->past_hist[k], in_size); for(j = 0; j < rate->filt_len[k]; j++) rate->past_hist[k][j] = temp_buf[j+in_size - rate->filt_len[k]]; in_size = out_size; temp_buf = temp_buf2; } if(out_size > *osamp) out_size = *osamp; *osamp = out_size; if(ibuf != NULL) { for(k=0;k < out_size;k++) obuf[k] = ((int) temp_buf[k]) << 16; } else { /* Wait for all-zero samples to come through. Should happen eventually with all-zero input */ int found = 0; for(k=0; k < out_size; k++) { obuf[k] = ((int) temp_buf[k] << 16); if(obuf[k] != 0) found = 1; } if(!found) *osamp = 0; } } /* * Process tail of input samples. */ void poly_drain(effp, obuf, osamp) eff_t effp; long *obuf; long *osamp; { long in_size = 1024; /* Call "flow" with NULL input. */ poly_flow(effp, NULL, obuf, &in_size, osamp); } /* * Do anything required when you stop reading samples. * Don't close input file! */ void poly_stop(effp) eff_t effp; { List *t, *t2; poly_t rate = (poly_t) effp->priv; int k; /* Free lists */ for(t = rate->l1; t != NULL; ) { t2 = t->next; t->next = NULL; if(t->data_buffer != NULL) free((void *) t->data_buffer); free((void *) t); t = t2; } for(t = rate->l2; t != NULL; ) { t2 = t->next; t->next = NULL; if(t->data_buffer != NULL) free((void *) t->data_buffer); free((void *) t); t = t2; } for(k = 0; k < rate->total;k++) { free((void *) rate->past_hist[k]); free((void *) rate->filt_array[k]); } free((void *) rate->past_hist); free((void *) rate->filt_array); free((void *) rate->filt_len); }