diff options
Diffstat (limited to 'src/sms')
37 files changed, 9059 insertions, 0 deletions
diff --git a/src/sms/OOURA.c b/src/sms/OOURA.c new file mode 100644 index 0000000..d88d20d --- /dev/null +++ b/src/sms/OOURA.c @@ -0,0 +1,638 @@ +/* -_-_-_-_-_-_-_-_-_-_-_-_-_-_- Ooura Real DFT -_-_-_-_-_-_-_-_-_-_-_-_-_-_ */ +/* Copyright notice: + This code comes from the "General Purpose FFT Package" that I obtained at + the following website http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html + It is exactly copied from the file fft4g.c, but I have changed the doubles to sfloats. + Here is the copyright notice included in the package: + + Copyright(C) 1996-2001 Takuya OOURA + email: ooura@mmm.t.u-tokyo.ac.jp + download: http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html + You may use, copy, modify this code for any purpose and + without fee. You may distribute this ORIGINAL package. + + The following is documentation of the algorithm included with the source code: + + -------- Real DFT / Inverse of Real DFT -------- + [definition] + <case1> RDFT + R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 + I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0<k<n/2 + <case2> IRDFT (excluding scale) + a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + + sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + + sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k<n + [usage] + <case1> + ip[0] = 0; // first time only + rdft(n, 1, a, ip, w); + <case2> + ip[0] = 0; // first time only + rdft(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (sfloat *) + <case1> + output data + a[2*k] = R[k], 0<=k<n/2 + a[2*k+1] = I[k], 0<k<n/2 + a[1] = R[n/2] + <case2> + input data + a[2*j] = R[j], 0<=j<n/2 + a[2*j+1] = I[j], 0<j<n/2 + a[1] = R[n/2] + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (sfloat *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + rdft(n, 1, a, ip, w); + is + rdft(n, -1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . +*/ + +#include "OOURA.h" +#include "math.h" /*! \todo (optimize) replace math.h trig functions (table lookup?) */ + +/* ! \brief OOURA Real / Inverse DFT algoriithm + * + * The source code contains documentation from + * the original author. + */ +void rdft(int n, int isgn, sfloat *a, int *ip, sfloat *w) +{ + int nw, nc; + sfloat xi; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 2)) { + nc = n >> 2; + makect(nc, ip, w + nw); + } + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, w); + } + xi = a[0] - a[1]; + a[0] += a[1]; + a[1] = xi; + } else { + a[1] = 0.5 * (a[0] - a[1]); + a[0] -= a[1]; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } else if (n == 4) { + cftfsub(n, a, w); + } + } +} + + +void makewt(int nw, int *ip, sfloat *w) +{ + int j, nwh; + sfloat delta, x, y; + + ip[0] = nw; + ip[1] = 1; + if (nw > 2) { + nwh = nw >> 1; + delta = atan(1.0) / nwh; + w[0] = 1; + w[1] = 0; + w[nwh] = cos(delta * nwh); + w[nwh + 1] = w[nwh]; + if (nwh > 2) { + for (j = 2; j < nwh; j += 2) { + x = cos(delta * j); + y = sin(delta * j); + w[j] = x; + w[j + 1] = y; + w[nw - j] = y; + w[nw - j + 1] = x; + } + bitrv2(nw, ip + 2, w); + } + } +} + +void makect(int nc, int *ip, sfloat *c) +{ + int j, nch; + sfloat delta; + + ip[1] = nc; + if (nc > 1) { + nch = nc >> 1; + delta = atan(1.0) / nch; + c[0] = cos(delta * nch); + c[nch] = 0.5 * c[0]; + for (j = 1; j < nch; j++) { + c[j] = 0.5 * cos(delta * j); + c[nc - j] = 0.5 * sin(delta * j); + } + } +} + +void bitrv2(int n, int *ip, sfloat *a) +{ + int j, j1, k, k1, l, m, m2; + sfloat xr, xi, yr, yi; + + ip[0] = 0; + l = n; + m = 1; + while ((m << 3) < l) { + l >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + l; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == l) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + j1 = 2 * k + m2 + ip[k]; + k1 = j1 + m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } else { + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } + } +} + +void cftfsub(int n, sfloat *a, sfloat *w) +{ + int j, j1, j2, j3, l; + sfloat x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n > 8) { + cft1st(n, a, w); + l = 8; + while ((l << 2) < n) { + cftmdl(n, l, a, w); + l <<= 2; + } + } + if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + } else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = a[j + 1] - a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] += a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + + +void cftbsub(int n, sfloat *a, sfloat *w) +{ + int j, j1, j2, j3, l; + sfloat x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n > 8) { + cft1st(n, a, w); + l = 8; + while ((l << 2) < n) { + cftmdl(n, l, a, w); + l <<= 2; + } + } + if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = -a[j + 1] - a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = -a[j + 1] + a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i - x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i + x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i - x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i + x3r; + } + } else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = -a[j + 1] + a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] = -a[j + 1] - a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + + +void cft1st(int n, sfloat *a, sfloat *w) +{ + int j, k1, k2; + sfloat wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + sfloat x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = a[0] + a[2]; + x0i = a[1] + a[3]; + x1r = a[0] - a[2]; + x1i = a[1] - a[3]; + x2r = a[4] + a[6]; + x2i = a[5] + a[7]; + x3r = a[4] - a[6]; + x3i = a[5] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[2] = x1r - x3i; + a[3] = x1i + x3r; + a[6] = x1r + x3i; + a[7] = x1i - x3r; + wk1r = w[2]; + x0r = a[8] + a[10]; + x0i = a[9] + a[11]; + x1r = a[8] - a[10]; + x1i = a[9] - a[11]; + x2r = a[12] + a[14]; + x2i = a[13] + a[15]; + x3r = a[12] - a[14]; + x3i = a[13] - a[15]; + a[8] = x0r + x2r; + a[9] = x0i + x2i; + a[12] = x2i - x0i; + a[13] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[10] = wk1r * (x0r - x0i); + a[11] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[14] = wk1r * (x0i - x0r); + a[15] = wk1r * (x0i + x0r); + k1 = 0; + for (j = 16; j < n; j += 16) { + k1 += 2; + k2 = 2 * k1; + wk2r = w[k1]; + wk2i = w[k1 + 1]; + wk1r = w[k2]; + wk1i = w[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + x0r = a[j] + a[j + 2]; + x0i = a[j + 1] + a[j + 3]; + x1r = a[j] - a[j + 2]; + x1i = a[j + 1] - a[j + 3]; + x2r = a[j + 4] + a[j + 6]; + x2i = a[j + 5] + a[j + 7]; + x3r = a[j + 4] - a[j + 6]; + x3i = a[j + 5] - a[j + 7]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 4] = wk2r * x0r - wk2i * x0i; + a[j + 5] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 2] = wk1r * x0r - wk1i * x0i; + a[j + 3] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 6] = wk3r * x0r - wk3i * x0i; + a[j + 7] = wk3r * x0i + wk3i * x0r; + wk1r = w[k2 + 2]; + wk1i = w[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + x0r = a[j + 8] + a[j + 10]; + x0i = a[j + 9] + a[j + 11]; + x1r = a[j + 8] - a[j + 10]; + x1i = a[j + 9] - a[j + 11]; + x2r = a[j + 12] + a[j + 14]; + x2i = a[j + 13] + a[j + 15]; + x3r = a[j + 12] - a[j + 14]; + x3i = a[j + 13] - a[j + 15]; + a[j + 8] = x0r + x2r; + a[j + 9] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 12] = -wk2i * x0r - wk2r * x0i; + a[j + 13] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 10] = wk1r * x0r - wk1i * x0i; + a[j + 11] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 14] = wk3r * x0r - wk3i * x0i; + a[j + 15] = wk3r * x0i + wk3i * x0r; + } +} + + +void cftmdl(int n, int l, sfloat *a, sfloat *w) +{ + int j, j1, j2, j3, k, k1, k2, m, m2; + sfloat wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + sfloat x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + m = l << 2; + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + wk1r = w[2]; + for (j = m; j < l + m; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x2i - x0i; + a[j2 + 1] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * (x0r - x0i); + a[j1 + 1] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[j3] = wk1r * (x0i - x0r); + a[j3 + 1] = wk1r * (x0i + x0r); + } + k1 = 0; + m2 = 2 * m; + for (k = m2; k < n; k += m2) { + k1 += 2; + k2 = 2 * k1; + wk2r = w[k1]; + wk2i = w[k1 + 1]; + wk1r = w[k2]; + wk1i = w[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + for (j = k; j < l + k; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2] = wk2r * x0r - wk2i * x0i; + a[j2 + 1] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + wk1r = w[k2 + 2]; + wk1i = w[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + for (j = k + m; j < l + (k + m); j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2] = -wk2i * x0r - wk2r * x0i; + a[j2 + 1] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + } +} + + +void rftfsub(int n, sfloat *a, int nc, sfloat *c) +{ + int j, k, kk, ks, m; + sfloat wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr - wki * xi; + yi = wkr * xi + wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + +void rftbsub(int n, sfloat *a, int nc, sfloat *c) +{ + int j, k, kk, ks, m; + sfloat wkr, wki, xr, xi, yr, yi; + + a[1] = -a[1]; + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr + wki * xi; + yi = wkr * xi - wki * xr; + a[j] -= yr; + a[j + 1] = yi - a[j + 1]; + a[k] += yr; + a[k + 1] = yi - a[k + 1]; + } + a[m + 1] = -a[m + 1]; +} diff --git a/src/sms/OOURA.h b/src/sms/OOURA.h new file mode 100644 index 0000000..d7c60ad --- /dev/null +++ b/src/sms/OOURA.h @@ -0,0 +1,23 @@ +#ifndef _OOURA_H +#define _OOURA_H + +#define sfloat double +/*#define sfloat float*/ + +#define NMAX 8192 +#define NMAXSQRT 64 + +void rdft(int n, int isgn, sfloat *a, int *ip, sfloat *w); + +void makewt(int nw, int *ip, sfloat *w); +void makect(int nc, int *ip, sfloat *c); +void bitrv2(int n, int *ip, sfloat *a); +void cftfsub(int n, sfloat *a, sfloat *w); +void cftbsub(int n, sfloat *a, sfloat *w); +void rftfsub(int n, sfloat *a, int nc, sfloat *c); +void rftbsub(int n, sfloat *a, int nc, sfloat *c); + +void cft1st(int n, sfloat *a, sfloat *w); +void cftmdl(int n, int l, sfloat *a, sfloat *w); + +#endif /* _OURA_H */ diff --git a/src/sms/SFMT.c b/src/sms/SFMT.c new file mode 100644 index 0000000..7b84db7 --- /dev/null +++ b/src/sms/SFMT.c @@ -0,0 +1,621 @@ +/* + * @file SFMT.c + * @brief SIMD oriented Fast Mersenne Twister(SFMT) + * + * @author Mutsuo Saito (Hiroshima University) + * @author Makoto Matsumoto (Hiroshima University) + * + * Copyright (C) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima + * University. All rights reserved. + * + * The new BSD License is applied to this software, see LICENSE.txt + */ + +#include <string.h> +#include <assert.h> +#include "SFMT.h" +#include "SFMT/SFMT-params.h" + +#if defined(__BIG_ENDIAN__) && !defined(__amd64) && !defined(BIG_ENDIAN64) +#define BIG_ENDIAN64 1 +#endif +#if defined(HAVE_ALTIVEC) && !defined(BIG_ENDIAN64) +#define BIG_ENDIAN64 1 +#endif +#if defined(ONLY64) && !defined(BIG_ENDIAN64) + #if defined(__GNUC__) + #error "-DONLY64 must be specified with -DBIG_ENDIAN64" + #endif +#undef ONLY64 +#endif +/*------------------------------------------------------ + 128-bit SIMD data type for Altivec, SSE2 or standard C + ------------------------------------------------------*/ +#if defined(HAVE_ALTIVEC) + #if !defined(__APPLE__) + #include <altivec.h> + #endif +/** 128-bit data structure */ +union W128_T { + vector unsigned int s; + uint32_t u[4]; +}; +/** 128-bit data type */ +typedef union W128_T w128_t; + +#elif defined(HAVE_SSE2) + #include <emmintrin.h> + +/** 128-bit data structure */ +union W128_T { + __m128i si; + uint32_t u[4]; +}; +/** 128-bit data type */ +typedef union W128_T w128_t; + +#else + +/** 128-bit data structure */ +struct W128_T { + uint32_t u[4]; +}; +/** 128-bit data type */ +typedef struct W128_T w128_t; + +#endif + +/*-------------------------------------- + FILE GLOBAL VARIABLES + internal state, index counter and flag + --------------------------------------*/ +/** the 128-bit internal state array */ +static w128_t sfmt[N]; +/** the 32bit integer pointer to the 128-bit internal state array */ +static uint32_t *psfmt32 = &sfmt[0].u[0]; +#if !defined(BIG_ENDIAN64) || defined(ONLY64) +/** the 64bit integer pointer to the 128-bit internal state array */ +static uint64_t *psfmt64 = (uint64_t *)&sfmt[0].u[0]; +#endif +/** index counter to the 32-bit internal state array */ +static int idx; +/** a flag: it is 0 if and only if the internal state is not yet + * initialized. */ +static int initialized = 0; +/** a parity check vector which certificate the period of 2^{MEXP} */ +static uint32_t parity[4] = {PARITY1, PARITY2, PARITY3, PARITY4}; + +/*---------------- + STATIC FUNCTIONS + ----------------*/ +inline static int idxof(int i); +inline static void rshift128(w128_t *out, w128_t const *in, int shift); +inline static void lshift128(w128_t *out, w128_t const *in, int shift); +inline static void gen_rand_all(void); +inline static void gen_rand_array(w128_t *array, int size); +inline static uint32_t func1(uint32_t x); +inline static uint32_t func2(uint32_t x); +static void period_certification(void); +#if defined(BIG_ENDIAN64) && !defined(ONLY64) +inline static void swap(w128_t *array, int size); +#endif + +#if defined(HAVE_ALTIVEC) + #include "SFMT-alti.h" +#elif defined(HAVE_SSE2) + #include "SFMT-sse2.h" +#endif + +/** + * This function simulate a 64-bit index of LITTLE ENDIAN + * in BIG ENDIAN machine. + */ +#ifdef ONLY64 +inline static int idxof(int i) { + return i ^ 1; +} +#else +inline static int idxof(int i) { + return i; +} +#endif +/** + * This function simulates SIMD 128-bit right shift by the standard C. + * The 128-bit integer given in in is shifted by (shift * 8) bits. + * This function simulates the LITTLE ENDIAN SIMD. + * @param out the output of this function + * @param in the 128-bit data to be shifted + * @param shift the shift value + */ +#ifdef ONLY64 +inline static void rshift128(w128_t *out, w128_t const *in, int shift) { + uint64_t th, tl, oh, ol; + + th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]); + tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]); + + oh = th >> (shift * 8); + ol = tl >> (shift * 8); + ol |= th << (64 - shift * 8); + out->u[0] = (uint32_t)(ol >> 32); + out->u[1] = (uint32_t)ol; + out->u[2] = (uint32_t)(oh >> 32); + out->u[3] = (uint32_t)oh; +} +#else +inline static void rshift128(w128_t *out, w128_t const *in, int shift) { + uint64_t th, tl, oh, ol; + + th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]); + tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]); + + oh = th >> (shift * 8); + ol = tl >> (shift * 8); + ol |= th << (64 - shift * 8); + out->u[1] = (uint32_t)(ol >> 32); + out->u[0] = (uint32_t)ol; + out->u[3] = (uint32_t)(oh >> 32); + out->u[2] = (uint32_t)oh; +} +#endif +/** + * This function simulates SIMD 128-bit left shift by the standard C. + * The 128-bit integer given in in is shifted by (shift * 8) bits. + * This function simulates the LITTLE ENDIAN SIMD. + * @param out the output of this function + * @param in the 128-bit data to be shifted + * @param shift the shift value + */ +#ifdef ONLY64 +inline static void lshift128(w128_t *out, w128_t const *in, int shift) { + uint64_t th, tl, oh, ol; + + th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]); + tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]); + + oh = th << (shift * 8); + ol = tl << (shift * 8); + oh |= tl >> (64 - shift * 8); + out->u[0] = (uint32_t)(ol >> 32); + out->u[1] = (uint32_t)ol; + out->u[2] = (uint32_t)(oh >> 32); + out->u[3] = (uint32_t)oh; +} +#else +inline static void lshift128(w128_t *out, w128_t const *in, int shift) { + uint64_t th, tl, oh, ol; + + th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]); + tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]); + + oh = th << (shift * 8); + ol = tl << (shift * 8); + oh |= tl >> (64 - shift * 8); + out->u[1] = (uint32_t)(ol >> 32); + out->u[0] = (uint32_t)ol; + out->u[3] = (uint32_t)(oh >> 32); + out->u[2] = (uint32_t)oh; +} +#endif + +/** + * This function represents the recursion formula. + * @param r output + * @param a a 128-bit part of the internal state array + * @param b a 128-bit part of the internal state array + * @param c a 128-bit part of the internal state array + * @param d a 128-bit part of the internal state array + */ +#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2)) +#ifdef ONLY64 +inline static void do_recursion(w128_t *r, w128_t *a, w128_t *b, w128_t *c, + w128_t *d) { + w128_t x; + w128_t y; + + lshift128(&x, a, SL2); + rshift128(&y, c, SR2); + r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK2) ^ y.u[0] + ^ (d->u[0] << SL1); + r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK1) ^ y.u[1] + ^ (d->u[1] << SL1); + r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK4) ^ y.u[2] + ^ (d->u[2] << SL1); + r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK3) ^ y.u[3] + ^ (d->u[3] << SL1); +} +#else +inline static void do_recursion(w128_t *r, w128_t *a, w128_t *b, w128_t *c, + w128_t *d) { + w128_t x; + w128_t y; + + lshift128(&x, a, SL2); + rshift128(&y, c, SR2); + r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK1) ^ y.u[0] + ^ (d->u[0] << SL1); + r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK2) ^ y.u[1] + ^ (d->u[1] << SL1); + r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK3) ^ y.u[2] + ^ (d->u[2] << SL1); + r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK4) ^ y.u[3] + ^ (d->u[3] << SL1); +} +#endif +#endif + +#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2)) +/** + * This function fills the internal state array with pseudorandom + * integers. + */ +inline static void gen_rand_all(void) { + int i; + w128_t *r1, *r2; + + r1 = &sfmt[N - 2]; + r2 = &sfmt[N - 1]; + for (i = 0; i < N - POS1; i++) { + do_recursion(&sfmt[i], &sfmt[i], &sfmt[i + POS1], r1, r2); + r1 = r2; + r2 = &sfmt[i]; + } + for (; i < N; i++) { + do_recursion(&sfmt[i], &sfmt[i], &sfmt[i + POS1 - N], r1, r2); + r1 = r2; + r2 = &sfmt[i]; + } +} + +/** + * This function fills the user-specified array with pseudorandom + * integers. + * + * @param array an 128-bit array to be filled by pseudorandom numbers. + * @param size number of 128-bit pseudorandom numbers to be generated. + */ +inline static void gen_rand_array(w128_t *array, int size) { + int i, j; + w128_t *r1, *r2; + + r1 = &sfmt[N - 2]; + r2 = &sfmt[N - 1]; + for (i = 0; i < N - POS1; i++) { + do_recursion(&array[i], &sfmt[i], &sfmt[i + POS1], r1, r2); + r1 = r2; + r2 = &array[i]; + } + for (; i < N; i++) { + do_recursion(&array[i], &sfmt[i], &array[i + POS1 - N], r1, r2); + r1 = r2; + r2 = &array[i]; + } + for (; i < size - N; i++) { + do_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2); + r1 = r2; + r2 = &array[i]; + } + for (j = 0; j < 2 * N - size; j++) { + sfmt[j] = array[j + size - N]; + } + for (; i < size; i++, j++) { + do_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2); + r1 = r2; + r2 = &array[i]; + sfmt[j] = array[i]; + } +} +#endif + +#if defined(BIG_ENDIAN64) && !defined(ONLY64) && !defined(HAVE_ALTIVEC) +inline static void swap(w128_t *array, int size) { + int i; + uint32_t x, y; + + for (i = 0; i < size; i++) { + x = array[i].u[0]; + y = array[i].u[2]; + array[i].u[0] = array[i].u[1]; + array[i].u[2] = array[i].u[3]; + array[i].u[1] = x; + array[i].u[3] = y; + } +} +#endif +/** + * This function represents a function used in the initialization + * by init_by_array + * @param x 32-bit integer + * @return 32-bit integer + */ +static uint32_t func1(uint32_t x) { + return (x ^ (x >> 27)) * (uint32_t)1664525UL; +} + +/** + * This function represents a function used in the initialization + * by init_by_array + * @param x 32-bit integer + * @return 32-bit integer + */ +static uint32_t func2(uint32_t x) { + return (x ^ (x >> 27)) * (uint32_t)1566083941UL; +} + +/** + * This function certificate the period of 2^{MEXP} + */ +static void period_certification(void) { + int inner = 0; + int i, j; + uint32_t work; + + for (i = 0; i < 4; i++) + inner ^= psfmt32[idxof(i)] & parity[i]; + for (i = 16; i > 0; i >>= 1) + inner ^= inner >> i; + inner &= 1; + /* check OK */ + if (inner == 1) { + return; + } + /* check NG, and modification */ + for (i = 0; i < 4; i++) { + work = 1; + for (j = 0; j < 32; j++) { + if ((work & parity[i]) != 0) { + psfmt32[idxof(i)] ^= work; + return; + } + work = work << 1; + } + } +} + +/*---------------- + PUBLIC FUNCTIONS + ----------------*/ +/** + * This function returns the identification string. + * The string shows the word size, the Mersenne exponent, + * and all parameters of this generator. + */ +const char *get_idstring(void) { + return IDSTR; +} + +/** + * This function returns the minimum size of array used for \b + * fill_array32() function. + * @return minimum size of array used for fill_array32() function. + */ +int get_min_array_size32(void) { + return N32; +} + +/** + * This function returns the minimum size of array used for \b + * fill_array64() function. + * @return minimum size of array used for fill_array64() function. + */ +int get_min_array_size64(void) { + return N64; +} + +#ifndef ONLY64 +/** + * This function generates and returns 32-bit pseudorandom number. + * init_gen_rand or init_by_array must be called before this function. + * @return 32-bit pseudorandom number + */ +uint32_t gen_rand32(void) { + uint32_t r; + + assert(initialized); + if (idx >= N32) { + gen_rand_all(); + idx = 0; + } + r = psfmt32[idx++]; + return r; +} +#endif +/** + * This function generates and returns 64-bit pseudorandom number. + * init_gen_rand or init_by_array must be called before this function. + * The function gen_rand64 should not be called after gen_rand32, + * unless an initialization is again executed. + * @return 64-bit pseudorandom number + */ +uint64_t gen_rand64(void) { +#if defined(BIG_ENDIAN64) && !defined(ONLY64) + uint32_t r1, r2; +#else + uint64_t r; +#endif + + assert(initialized); + assert(idx % 2 == 0); + + if (idx >= N32) { + gen_rand_all(); + idx = 0; + } +#if defined(BIG_ENDIAN64) && !defined(ONLY64) + r1 = psfmt32[idx]; + r2 = psfmt32[idx + 1]; + idx += 2; + return ((uint64_t)r2 << 32) | r1; +#else + r = psfmt64[idx / 2]; + idx += 2; + return r; +#endif +} + +#ifndef ONLY64 +/** + * This function generates pseudorandom 32-bit integers in the + * specified array[] by one call. The number of pseudorandom integers + * is specified by the argument size, which must be at least 624 and a + * multiple of four. The generation by this function is much faster + * than the following gen_rand function. + * + * For initialization, init_gen_rand or init_by_array must be called + * before the first call of this function. This function can not be + * used after calling gen_rand function, without initialization. + * + * @param array an array where pseudorandom 32-bit integers are filled + * by this function. The pointer to the array must be \b "aligned" + * (namely, must be a multiple of 16) in the SIMD version, since it + * refers to the address of a 128-bit integer. In the standard C + * version, the pointer is arbitrary. + * + * @param size the number of 32-bit pseudorandom integers to be + * generated. size must be a multiple of 4, and greater than or equal + * to (MEXP / 128 + 1) * 4. + * + * @note \b memalign or \b posix_memalign is available to get aligned + * memory. Mac OSX doesn't have these functions, but \b malloc of OSX + * returns the pointer to the aligned memory block. + */ +void fill_array32(uint32_t *array, int size) { + assert(initialized); + assert(idx == N32); + assert(size % 4 == 0); + assert(size >= N32); + + gen_rand_array((w128_t *)array, size / 4); + idx = N32; +} +#endif + +/** + * This function generates pseudorandom 64-bit integers in the + * specified array[] by one call. The number of pseudorandom integers + * is specified by the argument size, which must be at least 312 and a + * multiple of two. The generation by this function is much faster + * than the following gen_rand function. + * + * For initialization, init_gen_rand or init_by_array must be called + * before the first call of this function. This function can not be + * used after calling gen_rand function, without initialization. + * + * @param array an array where pseudorandom 64-bit integers are filled + * by this function. The pointer to the array must be "aligned" + * (namely, must be a multiple of 16) in the SIMD version, since it + * refers to the address of a 128-bit integer. In the standard C + * version, the pointer is arbitrary. + * + * @param size the number of 64-bit pseudorandom integers to be + * generated. size must be a multiple of 2, and greater than or equal + * to (MEXP / 128 + 1) * 2 + * + * @note \b memalign or \b posix_memalign is available to get aligned + * memory. Mac OSX doesn't have these functions, but \b malloc of OSX + * returns the pointer to the aligned memory block. + */ +void fill_array64(uint64_t *array, int size) { + assert(initialized); + assert(idx == N32); + assert(size % 2 == 0); + assert(size >= N64); + + gen_rand_array((w128_t *)array, size / 2); + idx = N32; + +#if defined(BIG_ENDIAN64) && !defined(ONLY64) + swap((w128_t *)array, size /2); +#endif +} + +/** + * This function initializes the internal state array with a 32-bit + * integer seed. + * + * @param seed a 32-bit integer used as the seed. + */ +void init_gen_rand(uint32_t seed) { + int i; + + psfmt32[idxof(0)] = seed; + for (i = 1; i < N32; i++) { + psfmt32[idxof(i)] = 1812433253UL * (psfmt32[idxof(i - 1)] + ^ (psfmt32[idxof(i - 1)] >> 30)) + + i; + } + idx = N32; + period_certification(); + initialized = 1; +} + +/** + * This function initializes the internal state array, + * with an array of 32-bit integers used as the seeds + * @param init_key the array of 32-bit integers, used as a seed. + * @param key_length the length of init_key. + */ +void init_by_array(uint32_t *init_key, int key_length) { + int i, j, count; + uint32_t r; + int lag; + int mid; + int size = N * 4; + + if (size >= 623) { + lag = 11; + } else if (size >= 68) { + lag = 7; + } else if (size >= 39) { + lag = 5; + } else { + lag = 3; + } + mid = (size - lag) / 2; + + memset(sfmt, 0x8b, sizeof(sfmt)); + if (key_length + 1 > N32) { + count = key_length + 1; + } else { + count = N32; + } + r = func1(psfmt32[idxof(0)] ^ psfmt32[idxof(mid)] + ^ psfmt32[idxof(N32 - 1)]); + psfmt32[idxof(mid)] += r; + r += key_length; + psfmt32[idxof(mid + lag)] += r; + psfmt32[idxof(0)] = r; + + count--; + for (i = 1, j = 0; (j < count) && (j < key_length); j++) { + r = func1(psfmt32[idxof(i)] ^ psfmt32[idxof((i + mid) % N32)] + ^ psfmt32[idxof((i + N32 - 1) % N32)]); + psfmt32[idxof((i + mid) % N32)] += r; + r += init_key[j] + i; + psfmt32[idxof((i + mid + lag) % N32)] += r; + psfmt32[idxof(i)] = r; + i = (i + 1) % N32; + } + for (; j < count; j++) { + r = func1(psfmt32[idxof(i)] ^ psfmt32[idxof((i + mid) % N32)] + ^ psfmt32[idxof((i + N32 - 1) % N32)]); + psfmt32[idxof((i + mid) % N32)] += r; + r += i; + psfmt32[idxof((i + mid + lag) % N32)] += r; + psfmt32[idxof(i)] = r; + i = (i + 1) % N32; + } + for (j = 0; j < N32; j++) { + r = func2(psfmt32[idxof(i)] + psfmt32[idxof((i + mid) % N32)] + + psfmt32[idxof((i + N32 - 1) % N32)]); + psfmt32[idxof((i + mid) % N32)] ^= r; + r -= i; + psfmt32[idxof((i + mid + lag) % N32)] ^= r; + psfmt32[idxof(i)] = r; + i = (i + 1) % N32; + } + + idx = N32; + period_certification(); + initialized = 1; +} diff --git a/src/sms/SFMT.h b/src/sms/SFMT.h new file mode 100644 index 0000000..7c8b35e --- /dev/null +++ b/src/sms/SFMT.h @@ -0,0 +1,157 @@ +/** + * @file SFMT.h + * + * @brief SIMD oriented Fast Mersenne Twister(SFMT) pseudorandom + * number generator + * + * @author Mutsuo Saito (Hiroshima University) + * @author Makoto Matsumoto (Hiroshima University) + * + * Copyright (C) 2006, 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima + * University. All rights reserved. + * + * The new BSD License is applied to this software. + * see LICENSE.txt + * + * @note We assume that your system has inttypes.h. If your system + * doesn't have inttypes.h, you have to typedef uint32_t and uint64_t, + * and you have to define PRIu64 and PRIx64 in this file as follows: + * @verbatim + typedef unsigned int uint32_t + typedef unsigned long long uint64_t + #define PRIu64 "llu" + #define PRIx64 "llx" +@endverbatim + * uint32_t must be exactly 32-bit unsigned integer type (no more, no + * less), and uint64_t must be exactly 64-bit unsigned integer type. + * PRIu64 and PRIx64 are used for printf function to print 64-bit + * unsigned int and 64-bit unsigned int in hexadecimal format. + */ + +#ifndef SFMT_H +#define SFMT_H + +#include <stdio.h> + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + #include <inttypes.h> +#elif defined(_MSC_VER) || defined(__BORLANDC__) + typedef unsigned int uint32_t; + typedef unsigned __int64 uint64_t; + #define inline __inline +#else + #include <inttypes.h> + #if defined(__GNUC__) + #define inline __inline__ + #endif +#endif + +#ifndef PRIu64 + #if defined(_MSC_VER) || defined(__BORLANDC__) + #define PRIu64 "I64u" + #define PRIx64 "I64x" + #else + #define PRIu64 "llu" + #define PRIx64 "llx" + #endif +#endif + +#if defined(__GNUC__) +#define ALWAYSINLINE __attribute__((always_inline)) +#else +#define ALWAYSINLINE +#endif + +#if defined(_MSC_VER) + #if _MSC_VER >= 1200 + #define PRE_ALWAYS __forceinline + #else + #define PRE_ALWAYS inline + #endif +#else + #define PRE_ALWAYS inline +#endif + +uint32_t gen_rand32(void); +uint64_t gen_rand64(void); +void fill_array32(uint32_t *array, int size); +void fill_array64(uint64_t *array, int size); +void init_gen_rand(uint32_t seed); +void init_by_array(uint32_t *init_key, int key_length); +const char *get_idstring(void); +int get_min_array_size32(void); +int get_min_array_size64(void); + +/* These real versions are due to Isaku Wada */ +/** generates a random number on [0,1]-real-interval */ +inline static double to_real1(uint32_t v) +{ + return v * (1.0/4294967295.0); + /* divided by 2^32-1 */ +} + +/** generates a random number on [0,1]-real-interval */ +inline static double genrand_real1(void) +{ + return to_real1(gen_rand32()); +} + +/** generates a random number on [0,1)-real-interval */ +inline static double to_real2(uint32_t v) +{ + return v * (1.0/4294967296.0); + /* divided by 2^32 */ +} + +/** generates a random number on [0,1)-real-interval */ +inline static double genrand_real2(void) +{ + return to_real2(gen_rand32()); +} + +/** generates a random number on (0,1)-real-interval */ +inline static double to_real3(uint32_t v) +{ + return (((double)v) + 0.5)*(1.0/4294967296.0); + /* divided by 2^32 */ +} + +/** generates a random number on (0,1)-real-interval */ +inline static double genrand_real3(void) +{ + return to_real3(gen_rand32()); +} +/** These real versions are due to Isaku Wada */ + +/** generates a random number on [0,1) with 53-bit resolution*/ +inline static double to_res53(uint64_t v) +{ + return v * (1.0/18446744073709551616.0L); +} + +/** generates a random number on [0,1) with 53-bit resolution from two + * 32 bit integers */ +inline static double to_res53_mix(uint32_t x, uint32_t y) +{ + return to_res53(x | ((uint64_t)y << 32)); +} + +/** generates a random number on [0,1) with 53-bit resolution + */ +inline static double genrand_res53(void) +{ + return to_res53(gen_rand64()); +} + +/** generates a random number on [0,1) with 53-bit resolution + using 32bit integer. + */ +inline static double genrand_res53_mix(void) +{ + uint32_t x, y; + + x = gen_rand32(); + y = gen_rand32(); + return to_res53_mix(x, y); +} +#endif diff --git a/src/sms/SFMT/SFMT-params.h b/src/sms/SFMT/SFMT-params.h new file mode 100644 index 0000000..661bbf2 --- /dev/null +++ b/src/sms/SFMT/SFMT-params.h @@ -0,0 +1,97 @@ +#ifndef SFMT_PARAMS_H +#define SFMT_PARAMS_H + +#if !defined(MEXP) +#ifdef __GNUC__ + #warning "MEXP is not defined. I assume MEXP is 19937." +#endif + #define MEXP 19937 +#endif +/*----------------- + BASIC DEFINITIONS + -----------------*/ +/** Mersenne Exponent. The period of the sequence + * is a multiple of 2^MEXP-1. + * #define MEXP 19937 */ +/** SFMT generator has an internal state array of 128-bit integers, + * and N is its size. */ +#define N (MEXP / 128 + 1) +/** N32 is the size of internal state array when regarded as an array + * of 32-bit integers.*/ +#define N32 (N * 4) +/** N64 is the size of internal state array when regarded as an array + * of 64-bit integers.*/ +#define N64 (N * 2) + +/*---------------------- + the parameters of SFMT + following definitions are in paramsXXXX.h file. + ----------------------*/ +/** the pick up position of the array. +#define POS1 122 +*/ + +/** the parameter of shift left as four 32-bit registers. +#define SL1 18 + */ + +/** the parameter of shift left as one 128-bit register. + * The 128-bit integer is shifted by (SL2 * 8) bits. +#define SL2 1 +*/ + +/** the parameter of shift right as four 32-bit registers. +#define SR1 11 +*/ + +/** the parameter of shift right as one 128-bit register. + * The 128-bit integer is shifted by (SL2 * 8) bits. +#define SR2 1 +*/ + +/** A bitmask, used in the recursion. These parameters are introduced + * to break symmetry of SIMD. +#define MSK1 0xdfffffefU +#define MSK2 0xddfecb7fU +#define MSK3 0xbffaffffU +#define MSK4 0xbffffff6U +*/ + +/** These definitions are part of a 128-bit period certification vector. +#define PARITY1 0x00000001U +#define PARITY2 0x00000000U +#define PARITY3 0x00000000U +#define PARITY4 0xc98e126aU +*/ + +#if MEXP == 607 + #include "SFMT-params607.h" +#elif MEXP == 1279 + #include "SFMT-params1279.h" +#elif MEXP == 2281 + #include "SFMT-params2281.h" +#elif MEXP == 4253 + #include "SFMT-params4253.h" +#elif MEXP == 11213 + #include "SFMT-params11213.h" +#elif MEXP == 19937 + #include "SFMT-params19937.h" +#elif MEXP == 44497 + #include "SFMT-params44497.h" +#elif MEXP == 86243 + #include "SFMT-params86243.h" +#elif MEXP == 132049 + #include "SFMT-params132049.h" +#elif MEXP == 216091 + #include "SFMT-params216091.h" +#else +#ifdef __GNUC__ + #error "MEXP is not valid." + #undef MEXP +#else + #undef MEXP +#endif + +#endif + +#endif /* SFMT_PARAMS_H */ diff --git a/src/sms/SFMT/SFMT-params11213.h b/src/sms/SFMT/SFMT-params11213.h new file mode 100644 index 0000000..244d313 --- /dev/null +++ b/src/sms/SFMT/SFMT-params11213.h @@ -0,0 +1,46 @@ +#ifndef SFMT_PARAMS11213_H +#define SFMT_PARAMS11213_H + +#define POS1 68 +#define SL1 14 +#define SL2 3 +#define SR1 7 +#define SR2 3 +#define MSK1 0xeffff7fbU +#define MSK2 0xffffffefU +#define MSK3 0xdfdfbfffU +#define MSK4 0x7fffdbfdU +#define PARITY1 0x00000001U +#define PARITY2 0x00000000U +#define PARITY3 0xe8148000U +#define PARITY4 0xd0c7afa3U + + +/* PARAMETERS FOR ALTIVEC */ +#if defined(__APPLE__) /* For OSX */ + #define ALTI_SL1 (vector unsigned int)(SL1, SL1, SL1, SL1) + #define ALTI_SR1 (vector unsigned int)(SR1, SR1, SR1, SR1) + #define ALTI_MSK (vector unsigned int)(MSK1, MSK2, MSK3, MSK4) + #define ALTI_MSK64 \ + (vector unsigned int)(MSK2, MSK1, MSK4, MSK3) + #define ALTI_SL2_PERM \ + (vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10) + #define ALTI_SL2_PERM64 \ + (vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2) + #define ALTI_SR2_PERM \ + (vector unsigned char)(5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12) + #define ALTI_SR2_PERM64 \ + (vector unsigned char)(13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12) +#else /* For OTHER OSs(Linux?) */ + #define ALTI_SL1 {SL1, SL1, SL1, SL1} + #define ALTI_SR1 {SR1, SR1, SR1, SR1} + #define ALTI_MSK {MSK1, MSK2, MSK3, MSK4} + #define ALTI_MSK64 {MSK2, MSK1, MSK4, MSK3} + #define ALTI_SL2_PERM {3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10} + #define ALTI_SL2_PERM64 {3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2} + #define ALTI_SR2_PERM {5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12} + #define ALTI_SR2_PERM64 {13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12} +#endif /* For OSX */ +#define IDSTR "SFMT-11213:68-14-3-7-3:effff7fb-ffffffef-dfdfbfff-7fffdbfd" + +#endif /* SFMT_PARAMS11213_H */ diff --git a/src/sms/SFMT/SFMT-params1279.h b/src/sms/SFMT/SFMT-params1279.h new file mode 100644 index 0000000..df538df --- /dev/null +++ b/src/sms/SFMT/SFMT-params1279.h @@ -0,0 +1,46 @@ +#ifndef SFMT_PARAMS1279_H +#define SFMT_PARAMS1279_H + +#define POS1 7 +#define SL1 14 +#define SL2 3 +#define SR1 5 +#define SR2 1 +#define MSK1 0xf7fefffdU +#define MSK2 0x7fefcfffU +#define MSK3 0xaff3ef3fU +#define MSK4 0xb5ffff7fU +#define PARITY1 0x00000001U +#define PARITY2 0x00000000U +#define PARITY3 0x00000000U +#define PARITY4 0x20000000U + + +/* PARAMETERS FOR ALTIVEC */ +#if defined(__APPLE__) /* For OSX */ + #define ALTI_SL1 (vector unsigned int)(SL1, SL1, SL1, SL1) + #define ALTI_SR1 (vector unsigned int)(SR1, SR1, SR1, SR1) + #define ALTI_MSK (vector unsigned int)(MSK1, MSK2, MSK3, MSK4) + #define ALTI_MSK64 \ + (vector unsigned int)(MSK2, MSK1, MSK4, MSK3) + #define ALTI_SL2_PERM \ + (vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10) + #define ALTI_SL2_PERM64 \ + (vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2) + #define ALTI_SR2_PERM \ + (vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14) + #define ALTI_SR2_PERM64 \ + (vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14) +#else /* For OTHER OSs(Linux?) */ + #define ALTI_SL1 {SL1, SL1, SL1, SL1} + #define ALTI_SR1 {SR1, SR1, SR1, SR1} + #define ALTI_MSK {MSK1, MSK2, MSK3, MSK4} + #define ALTI_MSK64 {MSK2, MSK1, MSK4, MSK3} + #define ALTI_SL2_PERM {3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10} + #define ALTI_SL2_PERM64 {3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2} + #define ALTI_SR2_PERM {7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14} + #define ALTI_SR2_PERM64 {15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14} +#endif /* For OSX */ +#define IDSTR "SFMT-1279:7-14-3-5-1:f7fefffd-7fefcfff-aff3ef3f-b5ffff7f" + +#endif /* SFMT_PARAMS1279_H */ diff --git a/src/sms/SFMT/SFMT-params132049.h b/src/sms/SFMT/SFMT-params132049.h new file mode 100644 index 0000000..94e297e --- /dev/null +++ b/src/sms/SFMT/SFMT-params132049.h @@ -0,0 +1,46 @@ +#ifndef SFMT_PARAMS132049_H +#define SFMT_PARAMS132049_H + +#define POS1 110 +#define SL1 19 +#define SL2 1 +#define SR1 21 +#define SR2 1 +#define MSK1 0xffffbb5fU +#define MSK2 0xfb6ebf95U +#define MSK3 0xfffefffaU +#define MSK4 0xcff77fffU +#define PARITY1 0x00000001U +#define PARITY2 0x00000000U +#define PARITY3 0xcb520000U +#define PARITY4 0xc7e91c7dU + + +/* PARAMETERS FOR ALTIVEC */ +#if defined(__APPLE__) /* For OSX */ + #define ALTI_SL1 (vector unsigned int)(SL1, SL1, SL1, SL1) + #define ALTI_SR1 (vector unsigned int)(SR1, SR1, SR1, SR1) + #define ALTI_MSK (vector unsigned int)(MSK1, MSK2, MSK3, MSK4) + #define ALTI_MSK64 \ + (vector unsigned int)(MSK2, MSK1, MSK4, MSK3) + #define ALTI_SL2_PERM \ + (vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8) + #define ALTI_SL2_PERM64 \ + (vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0) + #define ALTI_SR2_PERM \ + (vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14) + #define ALTI_SR2_PERM64 \ + (vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14) +#else /* For OTHER OSs(Linux?) */ + #define ALTI_SL1 {SL1, SL1, SL1, SL1} + #define ALTI_SR1 {SR1, SR1, SR1, SR1} + #define ALTI_MSK {MSK1, MSK2, MSK3, MSK4} + #define ALTI_MSK64 {MSK2, MSK1, MSK4, MSK3} + #define ALTI_SL2_PERM {1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8} + #define ALTI_SL2_PERM64 {1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0} + #define ALTI_SR2_PERM {7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14} + #define ALTI_SR2_PERM64 {15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14} +#endif /* For OSX */ +#define IDSTR "SFMT-132049:110-19-1-21-1:ffffbb5f-fb6ebf95-fffefffa-cff77fff" + +#endif /* SFMT_PARAMS132049_H */ diff --git a/src/sms/SFMT/SFMT-params19937.h b/src/sms/SFMT/SFMT-params19937.h new file mode 100644 index 0000000..04708cd --- /dev/null +++ b/src/sms/SFMT/SFMT-params19937.h @@ -0,0 +1,46 @@ +#ifndef SFMT_PARAMS19937_H +#define SFMT_PARAMS19937_H + +#define POS1 122 +#define SL1 18 +#define SL2 1 +#define SR1 11 +#define SR2 1 +#define MSK1 0xdfffffefU +#define MSK2 0xddfecb7fU +#define MSK3 0xbffaffffU +#define MSK4 0xbffffff6U +#define PARITY1 0x00000001U +#define PARITY2 0x00000000U +#define PARITY3 0x00000000U +#define PARITY4 0x13c9e684U + + +/* PARAMETERS FOR ALTIVEC */ +#if defined(__APPLE__) /* For OSX */ + #define ALTI_SL1 (vector unsigned int)(SL1, SL1, SL1, SL1) + #define ALTI_SR1 (vector unsigned int)(SR1, SR1, SR1, SR1) + #define ALTI_MSK (vector unsigned int)(MSK1, MSK2, MSK3, MSK4) + #define ALTI_MSK64 \ + (vector unsigned int)(MSK2, MSK1, MSK4, MSK3) + #define ALTI_SL2_PERM \ + (vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8) + #define ALTI_SL2_PERM64 \ + (vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0) + #define ALTI_SR2_PERM \ + (vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14) + #define ALTI_SR2_PERM64 \ + (vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14) +#else /* For OTHER OSs(Linux?) */ + #define ALTI_SL1 {SL1, SL1, SL1, SL1} + #define ALTI_SR1 {SR1, SR1, SR1, SR1} + #define ALTI_MSK {MSK1, MSK2, MSK3, MSK4} + #define ALTI_MSK64 {MSK2, MSK1, MSK4, MSK3} + #define ALTI_SL2_PERM {1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8} + #define ALTI_SL2_PERM64 {1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0} + #define ALTI_SR2_PERM {7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14} + #define ALTI_SR2_PERM64 {15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14} +#endif /* For OSX */ +#define IDSTR "SFMT-19937:122-18-1-11-1:dfffffef-ddfecb7f-bffaffff-bffffff6" + +#endif /* SFMT_PARAMS19937_H */ diff --git a/src/sms/SFMT/SFMT-params216091.h b/src/sms/SFMT/SFMT-params216091.h new file mode 100644 index 0000000..46c7303 --- /dev/null +++ b/src/sms/SFMT/SFMT-params216091.h @@ -0,0 +1,46 @@ +#ifndef SFMT_PARAMS216091_H +#define SFMT_PARAMS216091_H + +#define POS1 627 +#define SL1 11 +#define SL2 3 +#define SR1 10 +#define SR2 1 +#define MSK1 0xbff7bff7U +#define MSK2 0xbfffffffU +#define MSK3 0xbffffa7fU +#define MSK4 0xffddfbfbU +#define PARITY1 0xf8000001U +#define PARITY2 0x89e80709U +#define PARITY3 0x3bd2b64bU +#define PARITY4 0x0c64b1e4U + + +/* PARAMETERS FOR ALTIVEC */ +#if defined(__APPLE__) /* For OSX */ + #define ALTI_SL1 (vector unsigned int)(SL1, SL1, SL1, SL1) + #define ALTI_SR1 (vector unsigned int)(SR1, SR1, SR1, SR1) + #define ALTI_MSK (vector unsigned int)(MSK1, MSK2, MSK3, MSK4) + #define ALTI_MSK64 \ + (vector unsigned int)(MSK2, MSK1, MSK4, MSK3) + #define ALTI_SL2_PERM \ + (vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10) + #define ALTI_SL2_PERM64 \ + (vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2) + #define ALTI_SR2_PERM \ + (vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14) + #define ALTI_SR2_PERM64 \ + (vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14) +#else /* For OTHER OSs(Linux?) */ + #define ALTI_SL1 {SL1, SL1, SL1, SL1} + #define ALTI_SR1 {SR1, SR1, SR1, SR1} + #define ALTI_MSK {MSK1, MSK2, MSK3, MSK4} + #define ALTI_MSK64 {MSK2, MSK1, MSK4, MSK3} + #define ALTI_SL2_PERM {3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10} + #define ALTI_SL2_PERM64 {3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2} + #define ALTI_SR2_PERM {7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14} + #define ALTI_SR2_PERM64 {15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14} +#endif /* For OSX */ +#define IDSTR "SFMT-216091:627-11-3-10-1:bff7bff7-bfffffff-bffffa7f-ffddfbfb" + +#endif /* SFMT_PARAMS216091_H */ diff --git a/src/sms/SFMT/SFMT-params2281.h b/src/sms/SFMT/SFMT-params2281.h new file mode 100644 index 0000000..ee2ebdf --- /dev/null +++ b/src/sms/SFMT/SFMT-params2281.h @@ -0,0 +1,46 @@ +#ifndef SFMT_PARAMS2281_H +#define SFMT_PARAMS2281_H + +#define POS1 12 +#define SL1 19 +#define SL2 1 +#define SR1 5 +#define SR2 1 +#define MSK1 0xbff7ffbfU +#define MSK2 0xfdfffffeU +#define MSK3 0xf7ffef7fU +#define MSK4 0xf2f7cbbfU +#define PARITY1 0x00000001U +#define PARITY2 0x00000000U +#define PARITY3 0x00000000U +#define PARITY4 0x41dfa600U + + +/* PARAMETERS FOR ALTIVEC */ +#if defined(__APPLE__) /* For OSX */ + #define ALTI_SL1 (vector unsigned int)(SL1, SL1, SL1, SL1) + #define ALTI_SR1 (vector unsigned int)(SR1, SR1, SR1, SR1) + #define ALTI_MSK (vector unsigned int)(MSK1, MSK2, MSK3, MSK4) + #define ALTI_MSK64 \ + (vector unsigned int)(MSK2, MSK1, MSK4, MSK3) + #define ALTI_SL2_PERM \ + (vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8) + #define ALTI_SL2_PERM64 \ + (vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0) + #define ALTI_SR2_PERM \ + (vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14) + #define ALTI_SR2_PERM64 \ + (vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14) +#else /* For OTHER OSs(Linux?) */ + #define ALTI_SL1 {SL1, SL1, SL1, SL1} + #define ALTI_SR1 {SR1, SR1, SR1, SR1} + #define ALTI_MSK {MSK1, MSK2, MSK3, MSK4} + #define ALTI_MSK64 {MSK2, MSK1, MSK4, MSK3} + #define ALTI_SL2_PERM {1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8} + #define ALTI_SL2_PERM64 {1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0} + #define ALTI_SR2_PERM {7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14} + #define ALTI_SR2_PERM64 {15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14} +#endif /* For OSX */ +#define IDSTR "SFMT-2281:12-19-1-5-1:bff7ffbf-fdfffffe-f7ffef7f-f2f7cbbf" + +#endif /* SFMT_PARAMS2281_H */ diff --git a/src/sms/SFMT/SFMT-params4253.h b/src/sms/SFMT/SFMT-params4253.h new file mode 100644 index 0000000..f391a70 --- /dev/null +++ b/src/sms/SFMT/SFMT-params4253.h @@ -0,0 +1,46 @@ +#ifndef SFMT_PARAMS4253_H +#define SFMT_PARAMS4253_H + +#define POS1 17 +#define SL1 20 +#define SL2 1 +#define SR1 7 +#define SR2 1 +#define MSK1 0x9f7bffffU +#define MSK2 0x9fffff5fU +#define MSK3 0x3efffffbU +#define MSK4 0xfffff7bbU +#define PARITY1 0xa8000001U +#define PARITY2 0xaf5390a3U +#define PARITY3 0xb740b3f8U +#define PARITY4 0x6c11486dU + + +/* PARAMETERS FOR ALTIVEC */ +#if defined(__APPLE__) /* For OSX */ + #define ALTI_SL1 (vector unsigned int)(SL1, SL1, SL1, SL1) + #define ALTI_SR1 (vector unsigned int)(SR1, SR1, SR1, SR1) + #define ALTI_MSK (vector unsigned int)(MSK1, MSK2, MSK3, MSK4) + #define ALTI_MSK64 \ + (vector unsigned int)(MSK2, MSK1, MSK4, MSK3) + #define ALTI_SL2_PERM \ + (vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8) + #define ALTI_SL2_PERM64 \ + (vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0) + #define ALTI_SR2_PERM \ + (vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14) + #define ALTI_SR2_PERM64 \ + (vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14) +#else /* For OTHER OSs(Linux?) */ + #define ALTI_SL1 {SL1, SL1, SL1, SL1} + #define ALTI_SR1 {SR1, SR1, SR1, SR1} + #define ALTI_MSK {MSK1, MSK2, MSK3, MSK4} + #define ALTI_MSK64 {MSK2, MSK1, MSK4, MSK3} + #define ALTI_SL2_PERM {1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8} + #define ALTI_SL2_PERM64 {1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0} + #define ALTI_SR2_PERM {7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14} + #define ALTI_SR2_PERM64 {15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14} +#endif /* For OSX */ +#define IDSTR "SFMT-4253:17-20-1-7-1:9f7bffff-9fffff5f-3efffffb-fffff7bb" + +#endif /* SFMT_PARAMS4253_H */ diff --git a/src/sms/SFMT/SFMT-params44497.h b/src/sms/SFMT/SFMT-params44497.h new file mode 100644 index 0000000..ddef00d --- /dev/null +++ b/src/sms/SFMT/SFMT-params44497.h @@ -0,0 +1,46 @@ +#ifndef SFMT_PARAMS44497_H +#define SFMT_PARAMS44497_H + +#define POS1 330 +#define SL1 5 +#define SL2 3 +#define SR1 9 +#define SR2 3 +#define MSK1 0xeffffffbU +#define MSK2 0xdfbebfffU +#define MSK3 0xbfbf7befU +#define MSK4 0x9ffd7bffU +#define PARITY1 0x00000001U +#define PARITY2 0x00000000U +#define PARITY3 0xa3ac4000U +#define PARITY4 0xecc1327aU + + +/* PARAMETERS FOR ALTIVEC */ +#if defined(__APPLE__) /* For OSX */ + #define ALTI_SL1 (vector unsigned int)(SL1, SL1, SL1, SL1) + #define ALTI_SR1 (vector unsigned int)(SR1, SR1, SR1, SR1) + #define ALTI_MSK (vector unsigned int)(MSK1, MSK2, MSK3, MSK4) + #define ALTI_MSK64 \ + (vector unsigned int)(MSK2, MSK1, MSK4, MSK3) + #define ALTI_SL2_PERM \ + (vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10) + #define ALTI_SL2_PERM64 \ + (vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2) + #define ALTI_SR2_PERM \ + (vector unsigned char)(5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12) + #define ALTI_SR2_PERM64 \ + (vector unsigned char)(13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12) +#else /* For OTHER OSs(Linux?) */ + #define ALTI_SL1 {SL1, SL1, SL1, SL1} + #define ALTI_SR1 {SR1, SR1, SR1, SR1} + #define ALTI_MSK {MSK1, MSK2, MSK3, MSK4} + #define ALTI_MSK64 {MSK2, MSK1, MSK4, MSK3} + #define ALTI_SL2_PERM {3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10} + #define ALTI_SL2_PERM64 {3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2} + #define ALTI_SR2_PERM {5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12} + #define ALTI_SR2_PERM64 {13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12} +#endif /* For OSX */ +#define IDSTR "SFMT-44497:330-5-3-9-3:effffffb-dfbebfff-bfbf7bef-9ffd7bff" + +#endif /* SFMT_PARAMS44497_H */ diff --git a/src/sms/SFMT/SFMT-params607.h b/src/sms/SFMT/SFMT-params607.h new file mode 100644 index 0000000..fc2be6d --- /dev/null +++ b/src/sms/SFMT/SFMT-params607.h @@ -0,0 +1,46 @@ +#ifndef SFMT_PARAMS607_H +#define SFMT_PARAMS607_H + +#define POS1 2 +#define SL1 15 +#define SL2 3 +#define SR1 13 +#define SR2 3 +#define MSK1 0xfdff37ffU +#define MSK2 0xef7f3f7dU +#define MSK3 0xff777b7dU +#define MSK4 0x7ff7fb2fU +#define PARITY1 0x00000001U +#define PARITY2 0x00000000U +#define PARITY3 0x00000000U +#define PARITY4 0x5986f054U + + +/* PARAMETERS FOR ALTIVEC */ +#if defined(__APPLE__) /* For OSX */ + #define ALTI_SL1 (vector unsigned int)(SL1, SL1, SL1, SL1) + #define ALTI_SR1 (vector unsigned int)(SR1, SR1, SR1, SR1) + #define ALTI_MSK (vector unsigned int)(MSK1, MSK2, MSK3, MSK4) + #define ALTI_MSK64 \ + (vector unsigned int)(MSK2, MSK1, MSK4, MSK3) + #define ALTI_SL2_PERM \ + (vector unsigned char)(3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10) + #define ALTI_SL2_PERM64 \ + (vector unsigned char)(3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2) + #define ALTI_SR2_PERM \ + (vector unsigned char)(5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12) + #define ALTI_SR2_PERM64 \ + (vector unsigned char)(13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12) +#else /* For OTHER OSs(Linux?) */ + #define ALTI_SL1 {SL1, SL1, SL1, SL1} + #define ALTI_SR1 {SR1, SR1, SR1, SR1} + #define ALTI_MSK {MSK1, MSK2, MSK3, MSK4} + #define ALTI_MSK64 {MSK2, MSK1, MSK4, MSK3} + #define ALTI_SL2_PERM {3,21,21,21,7,0,1,2,11,4,5,6,15,8,9,10} + #define ALTI_SL2_PERM64 {3,4,5,6,7,29,29,29,11,12,13,14,15,0,1,2} + #define ALTI_SR2_PERM {5,6,7,0,9,10,11,4,13,14,15,8,19,19,19,12} + #define ALTI_SR2_PERM64 {13,14,15,0,1,2,3,4,19,19,19,8,9,10,11,12} +#endif /* For OSX */ +#define IDSTR "SFMT-607:2-15-3-13-3:fdff37ff-ef7f3f7d-ff777b7d-7ff7fb2f" + +#endif /* SFMT_PARAMS607_H */ diff --git a/src/sms/SFMT/SFMT-params86243.h b/src/sms/SFMT/SFMT-params86243.h new file mode 100644 index 0000000..3b52b76 --- /dev/null +++ b/src/sms/SFMT/SFMT-params86243.h @@ -0,0 +1,46 @@ +#ifndef SFMT_PARAMS86243_H +#define SFMT_PARAMS86243_H + +#define POS1 366 +#define SL1 6 +#define SL2 7 +#define SR1 19 +#define SR2 1 +#define MSK1 0xfdbffbffU +#define MSK2 0xbff7ff3fU +#define MSK3 0xfd77efffU +#define MSK4 0xbf9ff3ffU +#define PARITY1 0x00000001U +#define PARITY2 0x00000000U +#define PARITY3 0x00000000U +#define PARITY4 0xe9528d85U + + +/* PARAMETERS FOR ALTIVEC */ +#if defined(__APPLE__) /* For OSX */ + #define ALTI_SL1 (vector unsigned int)(SL1, SL1, SL1, SL1) + #define ALTI_SR1 (vector unsigned int)(SR1, SR1, SR1, SR1) + #define ALTI_MSK (vector unsigned int)(MSK1, MSK2, MSK3, MSK4) + #define ALTI_MSK64 \ + (vector unsigned int)(MSK2, MSK1, MSK4, MSK3) + #define ALTI_SL2_PERM \ + (vector unsigned char)(25,25,25,25,3,25,25,25,7,0,1,2,11,4,5,6) + #define ALTI_SL2_PERM64 \ + (vector unsigned char)(7,25,25,25,25,25,25,25,15,0,1,2,3,4,5,6) + #define ALTI_SR2_PERM \ + (vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14) + #define ALTI_SR2_PERM64 \ + (vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14) +#else /* For OTHER OSs(Linux?) */ + #define ALTI_SL1 {SL1, SL1, SL1, SL1} + #define ALTI_SR1 {SR1, SR1, SR1, SR1} + #define ALTI_MSK {MSK1, MSK2, MSK3, MSK4} + #define ALTI_MSK64 {MSK2, MSK1, MSK4, MSK3} + #define ALTI_SL2_PERM {25,25,25,25,3,25,25,25,7,0,1,2,11,4,5,6} + #define ALTI_SL2_PERM64 {7,25,25,25,25,25,25,25,15,0,1,2,3,4,5,6} + #define ALTI_SR2_PERM {7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14} + #define ALTI_SR2_PERM64 {15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14} +#endif /* For OSX */ +#define IDSTR "SFMT-86243:366-6-7-19-1:fdbffbff-bff7ff3f-fd77efff-bf9ff3ff" + +#endif /* SFMT_PARAMS86243_H */ diff --git a/src/sms/analysis.c b/src/sms/analysis.c new file mode 100644 index 0000000..89d4b4a --- /dev/null +++ b/src/sms/analysis.c @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/*! \file analysis.c + * \brief main sms analysis routines + * + * the analysis routine here calls all necessary functions to perform the complete + * SMS analysis, once the desired analysis parameters are set in SMS_AnalParams. + */ + +#include "sms.h" + +/*! \brief maximum size for magnitude spectrum */ +#define SMS_MAX_SPEC 8192 + +/*! \brief compute spectrum, find peaks, and fundamental of one frame + * + * This is the main core of analysis calls + * + * \param iCurrentFrame frame number to be computed + * \param pAnalParams structure of analysis parameters + * \param fRefFundamental reference fundamental + */ +void sms_analyzeFrame(int iCurrentFrame, SMS_AnalParams *pAnalParams, sfloat fRefFundamental) +{ + int i, iFrame; + SMS_AnalFrame *pCurrentFrame = pAnalParams->ppFrames[iCurrentFrame]; + int iSoundLoc = pCurrentFrame->iFrameSample -((pCurrentFrame->iFrameSize + 1) >> 1) + 1; + sfloat *pFData = &(pAnalParams->soundBuffer.pFBuffer[iSoundLoc - pAnalParams->soundBuffer.iMarker]); + + /* TODO: this doesn't have to be done every time */ + int sizeWindow = pCurrentFrame->iFrameSize; + int sizeMag = sms_power2(sizeWindow); + sms_getWindow(sizeWindow, pAnalParams->spectrumWindow, pAnalParams->iWindowType); + sms_scaleWindow(sizeWindow, pAnalParams->spectrumWindow); + + /* compute the magnitude and (zero-windowed) phase spectra */ + sms_spectrum(sizeWindow, pFData, pAnalParams->spectrumWindow, sizeMag, + pAnalParams->magSpectrum, pAnalParams->phaseSpectrum, + pAnalParams->fftBuffer); + + /* convert magnitude spectra to dB */ + sms_arrayMagToDB(sizeMag, pAnalParams->magSpectrum); + + /* find the prominent peaks */ + pCurrentFrame->nPeaks = sms_detectPeaks(sizeMag, + pAnalParams->magSpectrum, + pAnalParams->phaseSpectrum, + pCurrentFrame->pSpectralPeaks, + pAnalParams); + + /* find a reference harmonic */ + if(pCurrentFrame->nPeaks > 0 && + (pAnalParams->iFormat == SMS_FORMAT_H || pAnalParams->iFormat == SMS_FORMAT_HP)) + pCurrentFrame->fFundamental = sms_harmDetection(pAnalParams->maxPeaks, pCurrentFrame->pSpectralPeaks, + fRefFundamental, pAnalParams->iRefHarmonic, + pAnalParams->fLowestFundamental, pAnalParams->fHighestFundamental, + pAnalParams->iSoundType, pAnalParams->fMinRefHarmMag, + pAnalParams->fRefHarmMagDiffFromMax); +} + +/*! \brief re-analyze the previous frames if necessary + * + * \todo explain when this is necessary + * + * \param iCurrentFrame current frame number + * \param pAnalParams structure with analysis parameters + * \return 1 if frames are good, -1 if analysis is necessary + * \todo is the return value info correct? Why isn't it used in sms_analyze? + */ +static int ReAnalyzeFrame(int iCurrentFrame, SMS_AnalParams *pAnalParams) +{ + sfloat fFund, fLastFund, fDev; + int iNewFrameSize, i; + sfloat fAvgDeviation = sms_fundDeviation(pAnalParams, iCurrentFrame); + int iFirstFrame = iCurrentFrame - pAnalParams->minGoodFrames; + + /*! \todo make this a < 0 check, but first make sure sms_fundDeviation does not + return values below zero */ + if(fAvgDeviation == -1) + return -1; + + /* if the last SMS_MIN_GOOD_FRAMES are stable look before them */ + /* and recompute the frames that are not stable */ + if (fAvgDeviation <= pAnalParams->maxDeviation) + { + for(i = 0; i < pAnalParams->analDelay; i++) + { + if(pAnalParams->ppFrames[iFirstFrame - i]->iFrameNum <= 0 || + pAnalParams->ppFrames[iFirstFrame - i]->iStatus == SMS_FRAME_RECOMPUTED) + return -1; + fFund = pAnalParams->ppFrames[iFirstFrame - i]->fFundamental; + fLastFund = pAnalParams->ppFrames[iFirstFrame - i + 1]->fFundamental; + fDev = fabs (fFund - fLastFund) / fLastFund; + iNewFrameSize = ((pAnalParams->iSamplingRate / fLastFund) * + pAnalParams->fSizeWindow/2) * 2 + 1; + + if(fFund <= 0 || fDev > .2 || + fabs((double)(pAnalParams->ppFrames[iFirstFrame - i]->iFrameSize - + iNewFrameSize)) / iNewFrameSize >= .2) + { + pAnalParams->ppFrames[iFirstFrame - i]->iFrameSize = iNewFrameSize; + pAnalParams->ppFrames[iFirstFrame - i]->iStatus = SMS_FRAME_READY; + + /* recompute frame */ + sms_analyzeFrame(iFirstFrame - i, pAnalParams, fLastFund); + pAnalParams->ppFrames[iFirstFrame - i]->iStatus = SMS_FRAME_RECOMPUTED; + + if(fabs(pAnalParams->ppFrames[iFirstFrame - i]->fFundamental - fLastFund) / + fLastFund >= .2) + return -1; + } + } + } + return 1; +} + +int sms_findPeaks(int sizeWaveform, sfloat *pWaveform, SMS_AnalParams *pAnalParams, SMS_SpectralPeaks *pSpectralPeaks) +{ + int iCurrentFrame = pAnalParams->iMaxDelayFrames - 1; /* frame # of current frame */ + sfloat fRefFundamental = 0; /* reference fundamental for current frame */ + int i, iError, iExtraSamples; /* samples used for next analysis frame */ + SMS_AnalFrame *pTmpAnalFrame; + + /* set initial analysis-window size */ + if(pAnalParams->windowSize == 0) + pAnalParams->windowSize = pAnalParams->iDefaultSizeWindow; + + /* fill sound buffer and perform pre-emphasis */ + if(sizeWaveform > 0) + sms_fillSoundBuffer(sizeWaveform, pWaveform, pAnalParams); + + /* move analysis data one frame back */ + pTmpAnalFrame = pAnalParams->ppFrames[0]; + for(i = 1; i < pAnalParams->iMaxDelayFrames; i++) + pAnalParams->ppFrames[i-1] = pAnalParams->ppFrames[i]; + pAnalParams->ppFrames[pAnalParams->iMaxDelayFrames-1] = pTmpAnalFrame; + + /* initialize the current frame */ + sms_initFrame(iCurrentFrame, pAnalParams, pAnalParams->windowSize); + + if(pAnalParams->ppFrames[iCurrentFrame]->iStatus == SMS_FRAME_READY) + { + sfloat fAvgDev = sms_fundDeviation(pAnalParams, iCurrentFrame - 1); + + /* if single note use the default fundamental as reference */ + if(pAnalParams->iSoundType == SMS_SOUND_TYPE_NOTE) + fRefFundamental = pAnalParams->fDefaultFundamental; + /* if sound is stable use the last fundamental as a reference */ + else if(fAvgDev != -1 && fAvgDev <= pAnalParams->maxDeviation) + fRefFundamental = pAnalParams->ppFrames[iCurrentFrame - 1]->fFundamental; + else + fRefFundamental = 0; + + /* compute spectrum, find peaks, and find fundamental of frame */ + sms_analyzeFrame(iCurrentFrame, pAnalParams, fRefFundamental); + + /* set the size of the next analysis window */ + if(pAnalParams->ppFrames[iCurrentFrame]->fFundamental > 0 && + pAnalParams->iSoundType != SMS_SOUND_TYPE_NOTE) + pAnalParams->windowSize = sms_sizeNextWindow(iCurrentFrame, pAnalParams); + + /* figure out how much needs to be read next time + * how many processed - sample no. of end of next frame + * = no. samples that we haven't processed yet from whenever, if sizeNextRead was 0 + */ + iExtraSamples = (pAnalParams->soundBuffer.iMarker + pAnalParams->soundBuffer.sizeBuffer) - + (pAnalParams->ppFrames[iCurrentFrame]->iFrameSample + pAnalParams->sizeHop); + + pAnalParams->sizeNextRead = MAX(0, (pAnalParams->windowSize+1)/2 - iExtraSamples); + ReAnalyzeFrame(iCurrentFrame, pAnalParams); + + /* save peaks */ + pSpectralPeaks->nPeaksFound = pAnalParams->ppFrames[iCurrentFrame]->nPeaks; + pSpectralPeaks->nPeaks = pAnalParams->maxPeaks; + + for(i = 0; i < pSpectralPeaks->nPeaks; i++) + { + if(i < pSpectralPeaks->nPeaksFound) + { + pSpectralPeaks->pSpectralPeaks[i].fMag = + sms_dBToMag(pAnalParams->ppFrames[iCurrentFrame]->pSpectralPeaks[i].fMag); + pSpectralPeaks->pSpectralPeaks[i].fFreq = + pAnalParams->ppFrames[iCurrentFrame]->pSpectralPeaks[i].fFreq; + pSpectralPeaks->pSpectralPeaks[i].fPhase = + pAnalParams->ppFrames[iCurrentFrame]->pSpectralPeaks[i].fPhase; + } + else + { + pSpectralPeaks->pSpectralPeaks[i].fMag = 0.0; + pSpectralPeaks->pSpectralPeaks[i].fFreq = 0.0; + pSpectralPeaks->pSpectralPeaks[i].fPhase = 0.0; + } + } + return pSpectralPeaks->nPeaks; + } + else + { + return 0; + } +} + +void sms_setPeaks(SMS_AnalParams *pAnalParams, int numamps, sfloat* amps, + int numfreqs, sfloat* freqs, int numphases, sfloat* phases) +{ + int i; + SMS_AnalFrame *tempFrame; + int currentFrame = pAnalParams->iMaxDelayFrames - 1; /* frame # of current frame */ + + /* move analysis data one frame back */ + tempFrame = pAnalParams->ppFrames[0]; + for(i = 1; i < pAnalParams->iMaxDelayFrames; i++) + pAnalParams->ppFrames[i-1] = pAnalParams->ppFrames[i]; + pAnalParams->ppFrames[pAnalParams->iMaxDelayFrames-1] = tempFrame; + + /* initialize the current frame */ + SMS_AnalFrame *frame = pAnalParams->ppFrames[currentFrame]; + sms_initFrame(currentFrame, pAnalParams, 0); + if(sms_errorCheck()) + { + printf("Error in init frame: %s \n", sms_errorString()); + return; + } + + for(i = 0; i < numamps; i++) + { + /* copy current peaks data */ + frame->pSpectralPeaks[i].fMag = sms_magToDB(amps[i]); + frame->pSpectralPeaks[i].fFreq = freqs[i]; + frame->pSpectralPeaks[i].fPhase = phases[i]; + } + frame->nPeaks = numamps; + frame->iStatus = SMS_FRAME_READY; + + /* harmonic detection */ + if(frame->nPeaks > 0 && + (pAnalParams->iFormat == SMS_FORMAT_H || pAnalParams->iFormat == SMS_FORMAT_HP)) + { + /* get a reference fundamental */ + sfloat refFundamental = 0; + sfloat avgDeviation = sms_fundDeviation(pAnalParams, currentFrame-1); + if(pAnalParams->iSoundType == SMS_SOUND_TYPE_NOTE) + refFundamental = pAnalParams->fDefaultFundamental; + /* if sound is stable use the last fundamental as a reference */ + else if(avgDeviation != -1 && avgDeviation <= pAnalParams->maxDeviation) + refFundamental = pAnalParams->ppFrames[currentFrame-1]->fFundamental; + else + refFundamental = 0; + + frame->fFundamental = sms_harmDetection(frame->nPeaks, frame->pSpectralPeaks, + refFundamental, pAnalParams->iRefHarmonic, + pAnalParams->fLowestFundamental, pAnalParams->fHighestFundamental, + pAnalParams->iSoundType, pAnalParams->fMinRefHarmMag, + pAnalParams->fRefHarmMagDiffFromMax); + } +} + +int sms_findPartials(SMS_Data *pSmsData, SMS_AnalParams *pAnalParams) +{ + int currentFrame = pAnalParams->iMaxDelayFrames - 1; + + /* set the frame delay, checking that it does not exceed the given maximum + * + * TODO: check for good values of pAnalParams->minGoodFrames and + * pAnalParams->analDelay here too? Or figure out why sms_crashes if + * pAnalParamx->iMaxDelayFrames is changed without changing the other + * two variables. + */ + int delayFrames = pAnalParams->minGoodFrames + pAnalParams->analDelay; + if(delayFrames > (pAnalParams->iMaxDelayFrames - 1)) + delayFrames = pAnalParams->iMaxDelayFrames - 1; + + /* clear SMS output */ + sms_clearFrame(pSmsData); + + /* incorporate the peaks into the corresponding tracks */ + if(pAnalParams->ppFrames[currentFrame-delayFrames]->fFundamental > 0 || + ((pAnalParams->iFormat == SMS_FORMAT_IH || pAnalParams->iFormat == SMS_FORMAT_IHP) && + pAnalParams->ppFrames[currentFrame-delayFrames]->nPeaks > 0)) + { + sms_peakContinuation(currentFrame-delayFrames, pAnalParams); + } + + /* fill gaps and delete short tracks */ + if(pAnalParams->iCleanTracks > 0) + { + sms_cleanTracks(currentFrame-delayFrames, pAnalParams); + } + + /* output data */ + int length = sizeof(sfloat) * pSmsData->nTracks; + memcpy((char *) pSmsData->pFSinFreq, (char *) + pAnalParams->ppFrames[0]->deterministic.pFSinFreq, length); + memcpy((char *) pSmsData->pFSinAmp, (char *) + pAnalParams->ppFrames[0]->deterministic.pFSinAmp, length); + + /* convert mags back to linear */ + sms_arrayDBToMag(pSmsData->nTracks, pSmsData->pFSinAmp); + + if(pAnalParams->iFormat == SMS_FORMAT_HP || + pAnalParams->iFormat == SMS_FORMAT_IHP) + memcpy((char *) pSmsData->pFSinPha, (char *) + pAnalParams->ppFrames[0]->deterministic.pFSinPha, length); + + return 1; +} + +int sms_findResidual(int sizeSynthesis, sfloat* pSynthesis, + int sizeOriginal, sfloat* pOriginal, + SMS_ResidualParams *residualParams) +{ + if(residualParams->hopSize < sizeOriginal) + { + sms_error("Residual signal length is smaller than the original signal length"); + return -1; + } + + sms_residual(residualParams->hopSize, pSynthesis, pOriginal, residualParams); + sms_filterHighPass(residualParams->hopSize, + residualParams->residual, + residualParams->samplingRate); + return 0; +} + +int sms_analyze(int sizeWaveform, sfloat *pWaveform, SMS_Data *pSmsData, SMS_AnalParams *pAnalParams) +{ + int iCurrentFrame = pAnalParams->iMaxDelayFrames - 1; /* frame # of current frame */ + int i, iError, iExtraSamples; /* samples used for next analysis frame */ + sfloat fRefFundamental = 0; /* reference fundamental for current frame */ + SMS_AnalFrame *pTmpAnalFrame; + + /* set the frame delay, checking that it does not exceed the given maximum + * + * TODO: check for good values of pAnalParams->minGoodFrames and + * pAnalParams->analDelay here too? Or figure out why sms_crashes if + * pAnalParamx->iMaxDelayFrames is changed without changing the other + * two variables. + */ + int delayFrames = pAnalParams->minGoodFrames + pAnalParams->analDelay; + if(delayFrames > (pAnalParams->iMaxDelayFrames - 1)) + delayFrames = pAnalParams->iMaxDelayFrames - 1; + + /* clear SMS output */ + sms_clearFrame(pSmsData); + + /* set initial analysis-window size */ + if(pAnalParams->windowSize == 0) + pAnalParams->windowSize = pAnalParams->iDefaultSizeWindow; + + /* fill the input sound buffer and perform pre-emphasis */ + if(sizeWaveform > 0) + sms_fillSoundBuffer(sizeWaveform, pWaveform, pAnalParams); + + /* move analysis data one frame back */ + pTmpAnalFrame = pAnalParams->ppFrames[0]; + for(i = 1; i < pAnalParams->iMaxDelayFrames; i++) + pAnalParams->ppFrames[i-1] = pAnalParams->ppFrames[i]; + pAnalParams->ppFrames[pAnalParams->iMaxDelayFrames-1] = pTmpAnalFrame; + + /* initialize the current frame */ + sms_initFrame(iCurrentFrame, pAnalParams, pAnalParams->windowSize); + if(sms_errorCheck()) + { + printf("error in init frame: %s \n", sms_errorString()); + return -1; + } + + /* if right data in the sound buffer do analysis */ + if(pAnalParams->ppFrames[iCurrentFrame]->iStatus == SMS_FRAME_READY) + { + sfloat fAvgDev = sms_fundDeviation(pAnalParams, iCurrentFrame - 1); + + /* if single note use the default fundamental as reference */ + if(pAnalParams->iSoundType == SMS_SOUND_TYPE_NOTE) + fRefFundamental = pAnalParams->fDefaultFundamental; + /* if sound is stable use the last fundamental as a reference */ + else if(fAvgDev != -1 && fAvgDev <= pAnalParams->maxDeviation) + fRefFundamental = pAnalParams->ppFrames[iCurrentFrame - 1]->fFundamental; + else + fRefFundamental = 0; + + /* compute spectrum, find peaks, and find fundamental of frame */ + sms_analyzeFrame(iCurrentFrame, pAnalParams, fRefFundamental); + + /* set the size of the next analysis window */ + if(pAnalParams->ppFrames[iCurrentFrame]->fFundamental > 0 && + pAnalParams->iSoundType != SMS_SOUND_TYPE_NOTE) + pAnalParams->windowSize = sms_sizeNextWindow (iCurrentFrame, pAnalParams); + + /* figure out how much needs to be read next time */ + iExtraSamples = + (pAnalParams->soundBuffer.iMarker + pAnalParams->soundBuffer.sizeBuffer) - + (pAnalParams->ppFrames[iCurrentFrame]->iFrameSample + pAnalParams->sizeHop); + + pAnalParams->sizeNextRead = MAX(0, (pAnalParams->windowSize+1)/2 - iExtraSamples); + + /* check again the previous frames and recompute if necessary */ + ReAnalyzeFrame(iCurrentFrame, pAnalParams); + } + + /* incorporate the peaks into the corresponding tracks */ + /* This is done after a pAnalParams->iMaxDelayFrames delay */ + if(pAnalParams->ppFrames[iCurrentFrame - delayFrames]->fFundamental > 0 || + ((pAnalParams->iFormat == SMS_FORMAT_IH || pAnalParams->iFormat == SMS_FORMAT_IHP) && + pAnalParams->ppFrames[iCurrentFrame - delayFrames]->nPeaks > 0)) + sms_peakContinuation(iCurrentFrame - delayFrames, pAnalParams); + + /* fill gaps and delete short tracks */ + if(pAnalParams->iCleanTracks > 0 && + pAnalParams->ppFrames[iCurrentFrame - delayFrames]->iStatus != SMS_FRAME_EMPTY) + sms_cleanTracks(iCurrentFrame - delayFrames, pAnalParams); + + /* do stochastic analysis */ + if(pAnalParams->iStochasticType != SMS_STOC_NONE) + { + /* synthesize deterministic signal */ + if(pAnalParams->ppFrames[1]->iStatus != SMS_FRAME_EMPTY && + pAnalParams->ppFrames[1]->iStatus != SMS_FRAME_END) + { + /* shift synthesis buffer */ + memcpy(pAnalParams->synthBuffer.pFBuffer, + pAnalParams->synthBuffer.pFBuffer+pAnalParams->sizeHop, + sizeof(sfloat) * pAnalParams->sizeHop); + memset(pAnalParams->synthBuffer.pFBuffer+pAnalParams->sizeHop, + 0, sizeof(sfloat) * pAnalParams->sizeHop); + + /* get deterministic signal with phase */ + sms_sineSynthFrame(&pAnalParams->ppFrames[1]->deterministic, + pAnalParams->synthBuffer.pFBuffer+pAnalParams->sizeHop, + pAnalParams->sizeHop, &pAnalParams->prevFrame, + pAnalParams->iSamplingRate); + } + + /* perform stochastic analysis after 1 frame of the */ + /* deterministic synthesis because it needs two frames */ + if(pAnalParams->ppFrames[0]->iStatus != SMS_FRAME_EMPTY && + pAnalParams->ppFrames[0]->iStatus != SMS_FRAME_END) + { + int iSoundLoc = pAnalParams->ppFrames[0]->iFrameSample - pAnalParams->sizeHop; + sfloat *pOriginal = &(pAnalParams->soundBuffer.pFBuffer[iSoundLoc - + pAnalParams->soundBuffer.iMarker]); + + int sizeData = MIN(pAnalParams->soundBuffer.sizeBuffer - + (iSoundLoc - pAnalParams->soundBuffer.iMarker), + pAnalParams->residualParams.residualSize); + if(sizeData > pAnalParams->residualParams.residualSize) + { + sms_error("Residual size larger than expected."); + return -1; + } + else if(sizeData < pAnalParams->residualParams.residualSize) + { + /* should only happen if we're at the end of a sound, unless hop size changes */ + /* TODO: should the window type be set to pAnalParams->iWindowType? */ + sms_getWindow(sizeData, pAnalParams->residualParams.fftWindow, SMS_WIN_HAMMING); + sms_scaleWindow(sizeData, pAnalParams->residualParams.fftWindow); + } + + /* obtain residual sound from original and synthesized sounds. accumulate the residual percentage.*/ + pAnalParams->fResidualAccumPerc += sms_residual(sizeData, + pAnalParams->synthBuffer.pFBuffer, + pOriginal, + &pAnalParams->residualParams); + + if(pAnalParams->iStochasticType == SMS_STOC_APPROX) + { + /* filter residual with a high pass filter (it solves some problems) */ + sms_filterHighPass(sizeData, pAnalParams->residualParams.residual, pAnalParams->iSamplingRate); + + /* approximate residual */ + sms_stocAnalysis(sizeData, pAnalParams->residualParams.residual, pAnalParams->residualParams.fftWindow, + pSmsData, pAnalParams); + } + else if(pAnalParams->iStochasticType == SMS_STOC_IFFT) + { + int sizeMag = sms_power2(sizeData >> 1); + sms_spectrum(sizeData, pAnalParams->residualParams.residual, pAnalParams->residualParams.fftWindow, + sizeMag, pSmsData->pFStocCoeff, pSmsData->pResPhase, + pAnalParams->fftBuffer); + } + + /* get sharper transitions in deterministic representation */ + sms_scaleDet(pAnalParams->synthBuffer.pFBuffer, pOriginal, + pAnalParams->ppFrames[0]->deterministic.pFSinAmp, + pAnalParams, pSmsData->nTracks); + + pAnalParams->ppFrames[0]->iStatus = SMS_FRAME_DONE; + } + } + else if(pAnalParams->ppFrames[0]->iStatus != SMS_FRAME_EMPTY && + pAnalParams->ppFrames[0]->iStatus != SMS_FRAME_END) + pAnalParams->ppFrames[0]->iStatus = SMS_FRAME_DONE; + + /* get the result */ + if(pAnalParams->ppFrames[0]->iStatus == SMS_FRAME_EMPTY) + { + /* no partials yet, so output the current peaks for testing */ + int numPeaks = pAnalParams->ppFrames[iCurrentFrame]->nPeaks; + int numTracks = pSmsData->nTracks; + numTracks = MIN(numPeaks, numTracks); + for(i = 0; i < numTracks; i++) + { + pSmsData->pFSinFreq[i] = pAnalParams->ppFrames[iCurrentFrame]->pSpectralPeaks[i].fFreq; + pSmsData->pFSinAmp[i] = sms_dBToMag(pAnalParams->ppFrames[iCurrentFrame]->pSpectralPeaks[i].fMag); + if(pAnalParams->iFormat == SMS_FORMAT_HP || pAnalParams->iFormat == SMS_FORMAT_IHP) + { + pSmsData->pFSinPha[i] = pAnalParams->ppFrames[iCurrentFrame]->pSpectralPeaks[i].fPhase; + } + } + pSmsData->nTracks = numTracks; + return 0; + } + /* return analysis data */ + else if(pAnalParams->ppFrames[0]->iStatus == SMS_FRAME_DONE) + { + /* put data into output */ + int length = sizeof(sfloat) * pSmsData->nTracks; + memcpy((char *) pSmsData->pFSinFreq, (char *) + pAnalParams->ppFrames[0]->deterministic.pFSinFreq, length); + memcpy((char *) pSmsData->pFSinAmp, (char *) + pAnalParams->ppFrames[0]->deterministic.pFSinAmp, length); + + /* convert mags back to linear */ + sms_arrayDBToMag(pSmsData->nTracks, pSmsData->pFSinAmp); + if(pAnalParams->iFormat == SMS_FORMAT_HP || pAnalParams->iFormat == SMS_FORMAT_IHP) + memcpy((char *) pSmsData->pFSinPha, (char *) + pAnalParams->ppFrames[0]->deterministic.pFSinPha, length); + + /* do post-processing (for now, spectral envelope calculation and storage) */ + if(pAnalParams->specEnvParams.iType != SMS_ENV_NONE) + { + sms_spectralEnvelope(pSmsData, &pAnalParams->specEnvParams); + } + return 1; + } + /* done, end of sound */ + else if(pAnalParams->ppFrames[0]->iStatus == SMS_FRAME_END) + return -1; + else + { + sms_error("sms_analyze error: wrong status of frame."); + return -1; + } + return 1; +} diff --git a/src/sms/cepstrum.c b/src/sms/cepstrum.c new file mode 100644 index 0000000..8a56787 --- /dev/null +++ b/src/sms/cepstrum.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file cepstrum.c + * \brief routines for different Fast Fourier Transform Algorithms + * + */ + +#include "sms.h" +#include <gsl/gsl_matrix.h> +#include <gsl/gsl_linalg.h> +#include <gsl/gsl_blas.h> + +#define COEF ( 8 * powf(PI, 2)) +#define CHOLESKY 1 + +typedef struct +{ + int nPoints; + int nCoeff; + gsl_matrix *pM; + gsl_matrix *pMt; + gsl_matrix *pR; + gsl_matrix *pMtMR; + gsl_vector *pXk; + gsl_vector *pMtXk; + gsl_vector *pC; + gsl_permutation *pPerm; +} CepstrumMatrices; + +void FreeDCepstrum(CepstrumMatrices *m) +{ + gsl_matrix_free(m->pM); + gsl_matrix_free(m->pMt); + gsl_matrix_free(m->pR); + gsl_matrix_free(m->pMtMR); + gsl_vector_free(m->pXk); + gsl_vector_free(m->pMtXk); + gsl_vector_free(m->pC); + gsl_permutation_free (m->pPerm); + +} + +void AllocateDCepstrum(int nPoints, int nCoeff, CepstrumMatrices *m) +{ + if(m->nPoints != 0 || m->nCoeff != 0) + FreeDCepstrum(m); + m->nPoints = nPoints; + m->nCoeff = nCoeff; + m->pM = gsl_matrix_alloc(nPoints, nCoeff); + m->pMt = gsl_matrix_alloc(nCoeff, nPoints); + m->pR = gsl_matrix_calloc(nCoeff, nCoeff); + m->pMtMR = gsl_matrix_alloc(nCoeff, nCoeff); + m->pXk = gsl_vector_alloc(nPoints); + m->pMtXk = gsl_vector_alloc(nCoeff); + m->pC = gsl_vector_alloc(nCoeff); + m->pPerm = gsl_permutation_alloc (nCoeff); +} + +/*! \brief Discrete Cepstrum Transform + * + * method for computing cepstrum aenalysis from a discrete + * set of partial peaks (frequency and amplitude) + * + * This implementation is owed to the help of Jordi Janer (thanks!) from the MTG, + * along with the following paper: + * "Regularization Techniques for Discrete Cepstrum Estimation" + * Olivier Cappe and Eric Moulines, IEEE Signal Processing Letters, Vol. 3 + * No.4, April 1996 + * + * \todo add anchor point add at frequency = 0 with the same magnitude as the first + * peak in pMag. This does not change the size of the cepstrum, only helps to smoothen it + * at the very beginning. + * + * \param sizeCepstrum order+1 of the discrete cepstrum + * \param pCepstrum pointer to output array of cepstrum coefficients + * \param sizeFreq number of partials peaks (the size of pFreq should be the same as pMag + * \param pFreq pointer to partial peak frequencies (hertz) + * \param pMag pointer to partial peak magnitudes (linear) + * \param fLambda regularization factor + * \param iMaxFreq maximum frequency of cepstrum + */ +void sms_dCepstrum( int sizeCepstrum, sfloat *pCepstrum, int sizeFreq, sfloat *pFreq, sfloat *pMag, + sfloat fLambda, int iMaxFreq) +{ + int i, k; + sfloat factor; + sfloat fNorm = PI / (sfloat)iMaxFreq; /* value to normalize frequencies to 0:0.5 */ + //static sizeCepstrumStatic + static CepstrumMatrices m; + //printf("nPoints: %d, nCoeff: %d \n", m.nPoints, m.nCoeff); + if(m.nPoints != sizeCepstrum || m.nCoeff != sizeFreq) + AllocateDCepstrum(sizeFreq, sizeCepstrum, &m); + int s; /* signum: "(-1)^n, where n is the number of interchanges in the permutation." */ + /* compute matrix M (eq. 4)*/ + for (i=0; i<sizeFreq; i++) + { + gsl_matrix_set (m.pM, i, 0, 1.); // first colum is all 1 + for (k=1; k <sizeCepstrum; k++) + gsl_matrix_set (m.pM, i, k , 2.*sms_sine(PI_2 + fNorm * k * pFreq[i]) ); + } + + /* compute transpose of M */ + gsl_matrix_transpose_memcpy (m.pMt, m.pM); + + /* compute R diagonal matrix (for eq. 7)*/ + factor = COEF * (fLambda / (1.-fLambda)); /* \todo why is this divided like this again? */ + for (k=0; k<sizeCepstrum; k++) + gsl_matrix_set(m.pR, k, k, factor * powf((sfloat) k,2.)); + + /* MtM = Mt * M, later will add R */ + gsl_blas_dgemm (CblasNoTrans, CblasNoTrans, 1., m.pMt, m.pM, 0.0, m.pMtMR); + /* add R to make MtMR */ + gsl_matrix_add (m.pMtMR, m.pR); + + /* set pMag in X and multiply with Mt to get pMtXk */ + for(k = 0; k <sizeFreq; k++) + gsl_vector_set(m.pXk, k, log(pMag[k])); + gsl_blas_dgemv (CblasNoTrans, 1., m.pMt, m.pXk, 0., m.pMtXk); + + /* solve x (the cepstrum) in Ax = b, where A=MtMR and b=pMtXk */ + + /* ==== the Cholesky Decomposition way ==== */ + /* MtM is 'symmetric and positive definite?' */ + //gsl_linalg_cholesky_decomp (m.pMtMR); + //gsl_linalg_cholesky_solve (m.pMtMR, m.pMtXk, m.pC); + + /* ==== the LU decomposition way ==== */ + gsl_linalg_LU_decomp (m.pMtMR, m.pPerm, &s); + gsl_linalg_LU_solve (m.pMtMR, m.pPerm, m.pMtXk, m.pC); + + + /* copy pC to pCepstrum */ + for(i = 0; i < sizeCepstrum; i++) + pCepstrum[i] = gsl_vector_get (m.pC, i); +} + +/*! \brief Spectrum Envelope from Cepstrum + * + * from a set of cepstrum coefficients, compute the spectrum envelope + * + * \param sizeCepstrum order + 1 of the cepstrum + * \param pCepstrum pointer to array of cepstrum coefficients + * \param sizeEnv size of spectrum envelope (max frequency in bins) \todo does this have to be a pow2 + * \param pEnv pointer to output spectrum envelope (real part only) + */ +void sms_dCepstrumEnvelope(int sizeCepstrum, sfloat *pCepstrum, int sizeEnv, sfloat *pEnv) +{ + + static sfloat *pFftBuffer; + static int sizeFftArray = 0; + int sizeFft = sizeEnv << 1; + int i; + if(sizeFftArray != sizeFft) + { + if(sizeFftArray != 0) free(pFftBuffer); + sizeFftArray = sms_power2(sizeFft); + if(sizeFftArray != sizeFft) + { + sms_error("bad fft size, incremented to power of 2"); + } + if ((pFftBuffer = (sfloat *) malloc(sizeFftArray * sizeof(sfloat))) == NULL) + { + sms_error("could not allocate memory for fft array"); + return; + } + } + memset(pFftBuffer, 0, sizeFftArray * sizeof(sfloat)); + + pFftBuffer[0] = pCepstrum[0] * 0.5; + for (i = 1; i < sizeCepstrum-1; i++) + pFftBuffer[i] = pCepstrum[i]; + + + sms_fft(sizeFftArray, pFftBuffer); + + for (i = 0; i < sizeEnv; i++) + pEnv[i] = powf(EXP, 2. * pFftBuffer[i*2]); +} + +/*! \brief main function for computing spectral envelope from sinusoidal peaks + * + * Magnitudes should already be in linear for this function. + * If pSmsData->iEnvelope == SMS_ENV_CEP, will return cepstrum coefficeints + * If pSmsData->iEnvelope == SMS_ENV_FBINS, will return linear magnitude spectrum + * + * \param pSmsData pointer to SMS_Data structure with all the arrays necessary + * \param pSpecEnvParams pointer to a structure of parameters for spectral enveloping + */ +void sms_spectralEnvelope( SMS_Data *pSmsData, SMS_SEnvParams *pSpecEnvParams) +{ + int i, k; + int sizeCepstrum = pSpecEnvParams->iOrder+1; + //int nPeaks = 0; + static sfloat pFreqBuff[1000], pMagBuff[1000]; + + /* \todo see if this memset is even necessary, once working */ + //memset(pSmsData->pSpecEnv, 0, pSpecEnvParams->nCoeff * sizeof(sfloat)); + + /* try to store cepstrum coefficients in pSmsData->nEnvCoeff always. + if cepstrum is what is wanted, memset the rest. otherwise, hand this array 2x to dCepstrumEnvelope */ + if(pSpecEnvParams->iOrder + 1> pSmsData->nEnvCoeff) + { + sms_error("cepstrum order is larger than the size of the spectral envelope"); + return; + } + + /* find out how many tracks were actually found... many are zero + \todo is this necessary? */ + for(i = 0, k=0; i < pSmsData->nTracks; i++) + { + if(pSmsData->pFSinFreq[i] > 0.00001) + { + if(pSpecEnvParams->iAnchor != 0) + { + if(k == 0) /* add anchor at beginning */ + + { + pFreqBuff[k] = 0.0; + pMagBuff[k] = pSmsData->pFSinAmp[i]; + k++; + } + } + pFreqBuff[k] = pSmsData->pFSinFreq[i]; + pMagBuff[k] = pSmsData->pFSinAmp[i]; + k++; + } + } + /* \todo see if adding an anchor at the max freq helps */ + + + if(k < 1) // how few can this be? try out a few in python + return; + sms_dCepstrum(sizeCepstrum, pSmsData->pSpecEnv, k, pFreqBuff, pMagBuff, + pSpecEnvParams->fLambda, pSpecEnvParams->iMaxFreq); + + if(pSpecEnvParams->iType == SMS_ENV_FBINS) + { + sms_dCepstrumEnvelope(sizeCepstrum, pSmsData->pSpecEnv, + pSpecEnvParams->nCoeff, pSmsData->pSpecEnv); + } +} diff --git a/src/sms/fileIO.c b/src/sms/fileIO.c new file mode 100644 index 0000000..e870c5a --- /dev/null +++ b/src/sms/fileIO.c @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file fileIO.c + * \brief SMS file input and output + */ + +#include "sms.h" + +/*! \brief file identification constant + * + * constant number that is first within SMS_Header, in order to correctly + * identify an SMS file when read. + */ +#define SMS_MAGIC 767 + +static char pChTextString[1000]; /*!< string to store analysis parameters in sms header */ + +/*! \brief initialize the header structure of an SMS file + * + * \param pSmsHeader header for SMS file + */ +void sms_initHeader(SMS_Header *pSmsHeader) +{ + pSmsHeader->iSmsMagic = SMS_MAGIC; + pSmsHeader->iHeadBSize = sizeof(SMS_Header); + pSmsHeader->nFrames = 0; + pSmsHeader->iFrameBSize = 0; + pSmsHeader->iFormat = SMS_FORMAT_H; + pSmsHeader->iFrameRate = 0; + pSmsHeader->iStochasticType = SMS_STOC_APPROX; + pSmsHeader->nTracks = 0; + pSmsHeader->nStochasticCoeff = 0; + pSmsHeader->nEnvCoeff = 0; + pSmsHeader->iMaxFreq = 0; + pSmsHeader->fResidualPerc = 0; +} + +/*! \brief fill an SMS header with necessary information for storage + * + * copies parameters from SMS_AnalParams, along with other values + * so an SMS file can be stored and correctly synthesized at a later + * time. This is somewhat of a convenience function. + * + * sms_initAnal() should be done first to properly set everything. + * + * \param pSmsHeader header for SMS file (to be stored) + * \param pAnalParams structure of analysis parameters + * \param pProgramString pointer to a string containing the name of the program that made the analysis data + */ +void sms_fillHeader(SMS_Header *pSmsHeader, SMS_AnalParams *pAnalParams, char *pProgramString) +{ + sms_initHeader(pSmsHeader); + pSmsHeader->nFrames = pAnalParams->nFrames; + pSmsHeader->iFormat = pAnalParams->iFormat; + pSmsHeader->iFrameRate = pAnalParams->iFrameRate; + pSmsHeader->iStochasticType = pAnalParams->iStochasticType; + pSmsHeader->nTracks = pAnalParams->nTracks; + pSmsHeader->iSamplingRate = pAnalParams->iSamplingRate; + if(pAnalParams->iStochasticType == SMS_STOC_NONE) + pSmsHeader->nStochasticCoeff = 0; + else + pSmsHeader->nStochasticCoeff = pAnalParams->nStochasticCoeff; + pSmsHeader->iEnvType = pAnalParams->specEnvParams.iType; + pSmsHeader->nEnvCoeff = pAnalParams->specEnvParams.nCoeff; + pSmsHeader->iMaxFreq = (int)pAnalParams->fHighestFreq; + pSmsHeader->iFrameBSize = sms_frameSizeB(pSmsHeader); +} + +/*! \brief write SMS header to file + * + * \param pChFileName file name for SMS file + * \param pSmsHeader header for SMS file + * \param ppSmsFile (double pointer to) file to be created + * \return error code \see SMS_WRERR in SMS_ERRORS + */ +int sms_writeHeader(char *pChFileName, SMS_Header *pSmsHeader, FILE **ppSmsFile) +{ + int iVariableSize = 0; + + if(pSmsHeader->iSmsMagic != SMS_MAGIC) + { + sms_error("not an SMS file"); + return -1; + } + if((*ppSmsFile = fopen (pChFileName, "w+")) == NULL) + { + sms_error("cannot open file for writing"); + return -1; + } + + pSmsHeader->iHeadBSize = sizeof(SMS_Header); + + /* write header */ + if(fwrite((void *)pSmsHeader, (size_t)1, (size_t)sizeof(SMS_Header), + *ppSmsFile) < (size_t)sizeof(SMS_Header)) + { + sms_error("cannot write output file"); + return(-1); + } + return 0; +} + +/*! \brief rewrite SMS header and close file + * + * \param pSmsFile pointer to SMS file + * \param pSmsHeader pointer to header for SMS file + * \return error code \see SMS_WRERR in SMS_ERRORS + */ +int sms_writeFile(FILE *pSmsFile, SMS_Header *pSmsHeader) +{ + int iVariableSize; + rewind(pSmsFile); + + pSmsHeader->iHeadBSize = sizeof(SMS_Header); + + /* write header */ + if(fwrite((void *)pSmsHeader, (size_t)1, (size_t)sizeof(SMS_Header), + pSmsFile) < (size_t)sizeof(SMS_Header)) + { + sms_error("cannot write output file (header)"); + return -1; + } + + fclose(pSmsFile); + return 0; +} + +/*! \brief write SMS frame + * + * \param pSmsFile pointer to SMS file + * \param pSmsHeader pointer to SMS header + * \param pSmsFrame pointer to SMS data frame + * \return 0 on success, -1 on failure + */ +int sms_writeFrame(FILE *pSmsFile, SMS_Header *pSmsHeader, SMS_Data *pSmsFrame) +{ + if (fwrite((void *)pSmsFrame->pSmsData, 1, pSmsHeader->iFrameBSize, + pSmsFile) < (unsigned int) pSmsHeader->iFrameBSize) + { + sms_error("cannot write frame to output file"); + return -1; + } + return 0; +} + +/*! \brief get the size in bytes of the frame in a SMS file + * + * \param pSmsHeader pointer to SMS header + * \return the size in bytes of the frame + */ +int sms_frameSizeB(SMS_Header *pSmsHeader) +{ + int iSize, nDet; + + if(pSmsHeader->iFormat == SMS_FORMAT_H || + pSmsHeader->iFormat == SMS_FORMAT_IH) + nDet = 2;/* freq, mag */ + else + nDet = 3; /* freq, mag, phase */ + + iSize = sizeof (sfloat) * (nDet * pSmsHeader->nTracks); + + if(pSmsHeader->iStochasticType == SMS_STOC_APPROX) + { /* stocCoeff + 1 (gain) */ + iSize += sizeof(sfloat) * (pSmsHeader->nStochasticCoeff + 1); + } + else if(pSmsHeader->iStochasticType == SMS_STOC_IFFT) + { + /* sizeFFT*2 + 1 (gain) */ + iSize += sizeof(sfloat) * (pSmsHeader->nStochasticCoeff * 2 + 1); + } + iSize += sizeof(sfloat) * pSmsHeader->nEnvCoeff; + return iSize; +} + +/*! \brief function to read SMS header + * + * \param pChFileName file name for SMS file + * \param ppSmsHeader (double pointer to) SMS header + * \param ppSmsFile (double pointer to) inputfile + * \return error code \see SMS_ERRORS + */ +int sms_getHeader(char *pChFileName, SMS_Header **ppSmsHeader, FILE **ppSmsFile) +{ + int iHeadBSize, iFrameBSize, nFrames; + int iMagicNumber; + + /* open file for reading */ + if((*ppSmsFile = fopen (pChFileName, "r")) == NULL) + { + sms_error("could not open SMS header"); + return -1; + } + /* read magic number */ + if(fread((void *) &iMagicNumber, (size_t) sizeof(int), (size_t)1, + *ppSmsFile) < (size_t)1) + { + sms_error("could not read SMS header"); + return -1; + } + + if(iMagicNumber != SMS_MAGIC) + { + sms_error("not an SMS file"); + return -1; + } + + /* read size of of header */ + if(fread((void *) &iHeadBSize, (size_t) sizeof(int), (size_t)1, + *ppSmsFile) < (size_t)1) + { + sms_error("could not read SMS header (iHeadBSize)"); + return -1; + } + + if(iHeadBSize <= 0) + { + sms_error("bad SMS header size"); + return -1; + } + + /* read number of data Frames */ + if(fread((void *) &nFrames, (size_t) sizeof(int), (size_t)1, + *ppSmsFile) < (size_t)1) + { + sms_error("could not read SMS number of frames"); + return -1; + } + + if(nFrames <= 0) + { + sms_error("number of frames <= 0"); + return -1; + } + + /* read size of data Frames */ + if(fread((void *) &iFrameBSize, (size_t) sizeof(int), (size_t)1, + *ppSmsFile) < (size_t)1) + { + sms_error("could not read size of SMS data"); + return -1; + } + + if(iFrameBSize <= 0) + { + sms_error("size bytes of frames <= 0"); + return -1; + } + + /* allocate memory for header */ + if(((*ppSmsHeader) = (SMS_Header *)malloc (iHeadBSize)) == NULL) + { + sms_error("cannot allocate memory for header"); + return -1; + } + + /* read header */ + rewind(*ppSmsFile); + if(fread ((void *) (*ppSmsHeader), 1, iHeadBSize, *ppSmsFile) < (unsigned int) iHeadBSize) + { + sms_error("cannot read header of SMS file"); + return -1; + } + + return 0; +} + +/*! \brief read an SMS data frame + * + * \param pSmsFile pointer to SMS file + * \param pSmsHeader pointer to SMS header + * \param iFrame frame number + * \param pSmsFrame pointer to SMS frame + * \return 0 on sucess, -1 on error + */ +int sms_getFrame(FILE *pSmsFile, SMS_Header *pSmsHeader, int iFrame, SMS_Data *pSmsFrame) +{ + if(fseek(pSmsFile, pSmsHeader->iHeadBSize + iFrame * + pSmsHeader->iFrameBSize, SEEK_SET) < 0) + { + sms_error("cannot seek to the SMS frame"); + return -1; + } + if((pSmsHeader->iFrameBSize = + fread((void *)pSmsFrame->pSmsData, (size_t)1, + (size_t)pSmsHeader->iFrameBSize, pSmsFile)) + != pSmsHeader->iFrameBSize) + { + sms_error("cannot read SMS frame"); + return -1; + } + return 0; +} + +/*! \brief allocate memory for a frame of SMS data + * + * \param pSmsFrame pointer to a frame of SMS data + * \param nTracks number of sinusoidal tracks in frame + * \param nStochCoeff number of stochastic coefficients in frame + * \param iPhase whether phase information is in the frame + * \param stochType stochastic resynthesis type + * \param nStochCoeff number of envelope coefficients in frame + * \param nEnvCoeff number of envelope coefficients in frame + * \return 0 on success, -1 on error + */ +int sms_allocFrame(SMS_Data *pSmsFrame, int nTracks, int nStochCoeff, int iPhase, + int stochType, int nEnvCoeff) +{ + sfloat *dataPos; /* a marker to locate specific data witin smsData */ + + /* calculate size of frame */ + int sizeData = sizeof(sfloat); /* nSamples */ + /* frequencies and magnitudes */ + sizeData += 2 * nTracks * sizeof(sfloat); + /* phases */ + if(iPhase > 0) + sizeData += nTracks * sizeof(sfloat); + /* stochastic coefficients */ + if(stochType == SMS_STOC_APPROX) + sizeData += (nStochCoeff + 1) * sizeof(sfloat); + else if(stochType == SMS_STOC_IFFT) + sizeData += ((2*nStochCoeff) + 1) * sizeof(sfloat); + /* spectral envelope */ + sizeData += nEnvCoeff * sizeof(sfloat); /* add in number of envelope coefficients (cep or fbins) if any */ + + /* allocate memory for data */ + pSmsFrame->pSmsData = (sfloat *)malloc(sizeData); + if(pSmsFrame->pSmsData == NULL) + { + sms_error("cannot allocate memory for SMS frame data"); + return -1; + } + memset(pSmsFrame->pSmsData, 0, sizeData); + + /* set the variables in the structure */ + /* \todo why not set these in init functions, then allocate with them?? */ + pSmsFrame->sizeData = sizeData; + pSmsFrame->nTracks = nTracks; + pSmsFrame->nCoeff = nStochCoeff; + pSmsFrame->nEnvCoeff = nEnvCoeff; + + /* set pointers to data types within smsData array */ + pSmsFrame->pFSinFreq = pSmsFrame->pSmsData; + dataPos = (sfloat *)(pSmsFrame->pFSinFreq + nTracks); + + pSmsFrame->pFSinAmp = dataPos; + dataPos = (sfloat *)(pSmsFrame->pFSinAmp + nTracks); + + if(iPhase > 0) + { + pSmsFrame->pFSinPha = dataPos; + dataPos = (sfloat *)(pSmsFrame->pFSinPha + nTracks); + } + else + pSmsFrame->pFSinPha = NULL; + + if(stochType == SMS_STOC_APPROX) + { + pSmsFrame->pFStocCoeff = dataPos; + dataPos = (sfloat *)(pSmsFrame->pFStocCoeff + nStochCoeff); + + pSmsFrame->pFStocGain = dataPos; + dataPos = (sfloat *)(pSmsFrame->pFStocGain + 1); + } + else if(stochType == SMS_STOC_IFFT) + { + pSmsFrame->pFStocCoeff = dataPos; + dataPos = (sfloat *)(pSmsFrame->pFStocCoeff + nStochCoeff); + pSmsFrame->pResPhase = dataPos; + dataPos = (sfloat *)(pSmsFrame->pResPhase + nStochCoeff); + pSmsFrame->pFStocGain = dataPos; + dataPos = (sfloat *)(pSmsFrame->pFStocGain + 1); + } + else + { + pSmsFrame->pFStocCoeff = NULL; + pSmsFrame->pResPhase = NULL; + pSmsFrame->pFStocGain = NULL; + } + + if(nEnvCoeff > 0) + pSmsFrame->pSpecEnv = dataPos; + else + pSmsFrame->pSpecEnv = NULL; + + return 0; +} + +/*! \brief function to allocate an SMS data frame using an SMS_Header + * + * this one is used when you have only read the header, such as after + * opening a file. + * + * \param pSmsHeader pointer to SMS header + * \param pSmsFrame pointer to SMS frame + * \return 0 on success, -1 on error + */ +int sms_allocFrameH(SMS_Header *pSmsHeader, SMS_Data *pSmsFrame) +{ + int iPhase = (pSmsHeader->iFormat == SMS_FORMAT_HP || + pSmsHeader->iFormat == SMS_FORMAT_IHP) ? 1 : 0; + return sms_allocFrame(pSmsFrame, pSmsHeader->nTracks, + pSmsHeader->nStochasticCoeff, iPhase, + pSmsHeader->iStochasticType, + pSmsHeader->nEnvCoeff); +} + +/*! \brief free the SMS data structure + * + * \param pSmsFrame pointer to frame of SMS data + */ +void sms_freeFrame(SMS_Data *pSmsFrame) +{ + if(!pSmsFrame) + return; + + if(pSmsFrame->pSmsData) + free(pSmsFrame->pSmsData); + + pSmsFrame->nTracks = 0; + pSmsFrame->nCoeff = 0; + pSmsFrame->sizeData = 0; + pSmsFrame->pFSinFreq = NULL; + pSmsFrame->pFSinAmp = NULL; + pSmsFrame->pFStocCoeff = NULL; + pSmsFrame->pResPhase = NULL; + pSmsFrame->pFStocGain = NULL; +} + +/*! \brief clear the SMS data structure + * + * \param pSmsFrame pointer to frame of SMS data + */ +void sms_clearFrame(SMS_Data *pSmsFrame) +{ + memset(pSmsFrame->pSmsData, 0, pSmsFrame->sizeData); +} + +/*! \brief copy a frame of SMS_Data + * + * \param pCopySmsData copy of frame + * \param pOriginalSmsData original frame + * + */ +void sms_copyFrame(SMS_Data *pCopySmsData, SMS_Data *pOriginalSmsData) +{ + /* if the two frames are the same size just copy data */ + if(pCopySmsData->sizeData == pOriginalSmsData->sizeData && + pCopySmsData->nTracks == pOriginalSmsData->nTracks) + { + memcpy((char *)pCopySmsData->pSmsData, + (char *)pOriginalSmsData->pSmsData, + pCopySmsData->sizeData); + } + /* if frames is different size copy the smallest */ + else + { + int nTracks = MIN(pCopySmsData->nTracks, pOriginalSmsData->nTracks); + int nCoeff = MIN(pCopySmsData->nCoeff, pOriginalSmsData->nCoeff); + + pCopySmsData->nTracks = nTracks; + pCopySmsData->nCoeff = nCoeff; + memcpy((char *)pCopySmsData->pFSinFreq, + (char *)pOriginalSmsData->pFSinFreq, + sizeof(sfloat) * nTracks); + memcpy((char *)pCopySmsData->pFSinAmp, + (char *)pOriginalSmsData->pFSinAmp, + sizeof(sfloat) * nTracks); + if(pOriginalSmsData->pFSinPha != NULL && + pCopySmsData->pFSinPha != NULL) + memcpy((char *)pCopySmsData->pFSinPha, + (char *)pOriginalSmsData->pFSinPha, + sizeof(sfloat) * nTracks); + if(pOriginalSmsData->pFStocCoeff != NULL && + pCopySmsData->pFStocCoeff != NULL) + { + if(pOriginalSmsData->pResPhase != NULL && + pCopySmsData->pResPhase != NULL) + memcpy((char *)pCopySmsData->pResPhase, + (char *)pOriginalSmsData->pResPhase, + sizeof(sfloat) * nCoeff); + } + if(pOriginalSmsData->pFStocGain != NULL && + pCopySmsData->pFStocGain != NULL) + memcpy((char *)pCopySmsData->pFStocGain, + (char *)pOriginalSmsData->pFStocGain, + sizeof(sfloat)); + } +} + +/*! \brief function to interpolate two SMS frames + * + * this assumes that the two frames are of the same size + * + * \param pSmsFrame1 sms frame 1 + * \param pSmsFrame2 sms frame 2 + * \param pSmsFrameOut sms output frame + * \param fInterpFactor interpolation factor + */ +void sms_interpolateFrames(SMS_Data *pSmsFrame1, SMS_Data *pSmsFrame2, + SMS_Data *pSmsFrameOut, sfloat fInterpFactor) +{ + int i; + sfloat fFreq1, fFreq2; + + /* interpolate the deterministic part */ + for(i = 0; i < pSmsFrame1->nTracks; i++) + { + fFreq1 = pSmsFrame1->pFSinFreq[i]; + fFreq2 = pSmsFrame2->pFSinFreq[i]; + if(fFreq1 == 0) + fFreq1 = fFreq2; + if(fFreq2 == 0) + fFreq2 = fFreq1; + pSmsFrameOut->pFSinFreq[i] = fFreq1 + fInterpFactor * (fFreq2 - fFreq1); + pSmsFrameOut->pFSinAmp[i] = + pSmsFrame1->pFSinAmp[i] + fInterpFactor * + (pSmsFrame2->pFSinAmp[i] - pSmsFrame1->pFSinAmp[i]); + } + + /* interpolate the stochastic part. The pointer is non-null when the frame contains + stochastic coefficients */ + if(pSmsFrameOut->pFStocGain) + { + *(pSmsFrameOut->pFStocGain) = + *(pSmsFrame1->pFStocGain) + fInterpFactor * + (*(pSmsFrame2->pFStocGain) - *(pSmsFrame1->pFStocGain)); + } + /*! \todo how to interpolate residual phase spectrum */ + for(i = 0; i < pSmsFrame1->nCoeff; i++) + pSmsFrameOut->pFStocCoeff[i] = + pSmsFrame1->pFStocCoeff[i] + fInterpFactor * + (pSmsFrame2->pFStocCoeff[i] - pSmsFrame1->pFStocCoeff[i]); + + /* DO NEXT: interpolate spec env here if fbins */ + for(i = 0; i < pSmsFrame1->nEnvCoeff; i++) + pSmsFrameOut->pSpecEnv[i] = + pSmsFrame1->pSpecEnv[i] + fInterpFactor * + (pSmsFrame2->pSpecEnv[i] - pSmsFrame1->pSpecEnv[i]); +} + diff --git a/src/sms/filters.c b/src/sms/filters.c new file mode 100644 index 0000000..7f31317 --- /dev/null +++ b/src/sms/filters.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file filters.c + * \brief various filters + */ + +#include "sms.h" + +/*! \brief coefficient for pre_emphasis filter */ +#define SMS_EMPH_COEF .9 + +/* pre-emphasis filter function, it returns the filtered value + * + * sfloat fInput; sound sample + */ +sfloat sms_preEmphasis(sfloat fInput, SMS_AnalParams *pAnalParams) +{ + if(pAnalParams->preEmphasis) + { + sfloat fOutput = fInput - SMS_EMPH_COEF * pAnalParams->preEmphasisLastValue; + pAnalParams->preEmphasisLastValue = fOutput; + return fOutput; + } + return fInput; +} + +/* de-emphasis filter function, it returns the filtered value + * + * sfloat fInput; sound input + */ +sfloat sms_deEmphasis(sfloat fInput, SMS_SynthParams *pSynthParams) +{ + if(pSynthParams->deEmphasis) + { + sfloat fOutput = fInput + SMS_EMPH_COEF * pSynthParams->deEmphasisLastValue; + pSynthParams->deEmphasisLastValue = fInput; + return fOutput; + } + return fInput; +} + +/*! \brief function to implement a zero-pole filter + * + * \todo will forgetting to reset pD to zero at the beginning of a new analysis + * (when there are multiple analyses within the life of one program) + * cause problems? + * + * \param pFa pointer to numerator coefficients + * \param pFb pointer to denominator coefficients + * \param nCoeff number of coefficients + * \param fInput input sample + * \return value is the filtered sample + */ +static sfloat ZeroPoleFilter(sfloat *pFa, sfloat *pFb, int nCoeff, sfloat fInput ) +{ + double fOut = 0; + int iSection; + static sfloat pD[5] = {0, 0, 0, 0, 0}; + + pD[0] = fInput; + for (iSection = nCoeff-1; iSection > 0; iSection--) + { + fOut = fOut + pFa[iSection] * pD[iSection]; + pD[0] = pD[0] - pFb[iSection] * pD[iSection]; + pD[iSection] = pD [iSection-1]; + } + fOut = fOut + pFa[0] * pD[0]; + return (sfloat) fOut; +} + +/*! \brief function to filter a waveform with a high-pass filter + * + * cutoff =1500 Hz + * + * \todo this filter only works on sample rates up to 48k? + * + * \param sizeResidual size of signal + * \param pResidual pointer to residual signal + * \param iSamplingRate sampling rate of signal + */ +void sms_filterHighPass(int sizeResidual, sfloat *pResidual, int iSamplingRate) +{ + /* cutoff 800Hz */ + static sfloat pFCoeff32k[10] = {0.814255, -3.25702, 4.88553, -3.25702, + 0.814255, 1, -3.58973, 4.85128, -2.92405, 0.66301}; + static sfloat pFCoeff36k[10] = {0.833098, -3.33239, 4.99859, -3.33239, + 0.833098, 1, -3.63528, 4.97089, -3.02934,0.694052}; + static sfloat pFCoeff40k[10] = {0.848475, -3.3939, 5.09085, -3.3939, + 0.848475, 1, -3.67173, 5.068, -3.11597, 0.71991}; + static sfloat pFCoeff441k[10] = {0.861554, -3.44622, 5.16932, -3.44622, + 0.861554, 1, -3.70223, 5.15023, -3.19013, 0.742275}; + static sfloat pFCoeff48k[10] = {0.872061, -3.48824, 5.23236, -3.48824, + 0.872061, 1, -3.72641, 5.21605, -3.25002, 0.76049}; + sfloat *pFCoeff, fSample = 0; + int i; + + if(iSamplingRate <= 32000) + pFCoeff = pFCoeff32k; + else if(iSamplingRate <= 36000) + pFCoeff = pFCoeff36k; + else if(iSamplingRate <= 40000) + pFCoeff = pFCoeff40k; + else if(iSamplingRate <= 44100) + pFCoeff = pFCoeff441k; + else + pFCoeff = pFCoeff48k; + + for(i = 0; i < sizeResidual; i++) + { + /* try to avoid underflow when there is nothing to filter */ + if(i > 0 && fSample == 0 && pResidual[i] == 0) + return; + + fSample = pResidual[i]; + pResidual[i] = ZeroPoleFilter (&pFCoeff[0], &pFCoeff[5], 5, fSample); + } +} + +/*! \brief a spectral filter + * + * filter each point of the current array by the surounding + * points using a triangular window + * + * \param pFArray two dimensional input array + * \param size1 vertical size of pFArray + * \param size2 horizontal size of pFArray + * \param pFOutArray output array of size size1 + */ +void sms_filterArray(sfloat *pFArray, int size1, int size2, sfloat *pFOutArray) +{ + int i, j, iPoint, iFrame, size2_2 = size2-2, size2_1 = size2-1; + sfloat *pFCurrentArray = pFArray + (size2_1) * size1; + sfloat fVal, fWeighting, fTotalWeighting, fTmpVal; + + /* find the filtered envelope */ + for(i = 0; i < size1; i++) + { + fVal = pFCurrentArray[i]; + fTotalWeighting = 1; + /* filter by the surrounding points */ + for(j = 1; j < (size2_2); j++) + { + fWeighting = (sfloat) size2 / (1+ j); + /* filter on the vertical dimension */ + /* consider the lower points */ + iPoint = i - (size2_1) + j; + if(iPoint >= 0) + { + fVal += pFCurrentArray[iPoint] * fWeighting; + fTotalWeighting += fWeighting; + } + /* consider the higher points */ + iPoint = i + (size2_1) - j; + if(iPoint < size1) + { + fVal += pFCurrentArray[iPoint] * fWeighting; + fTotalWeighting += fWeighting; + } + /*filter on the horizontal dimension */ + /* consider the previous points */ + iFrame = j; + fTmpVal = pFArray[iFrame*size1 + i]; + if(fTmpVal) + { + fVal += fTmpVal * fWeighting; + fTotalWeighting += fWeighting; + } + } + /* scale value by weighting */ + pFOutArray[i] = fVal / fTotalWeighting; + } +} diff --git a/src/sms/fixTracks.c b/src/sms/fixTracks.c new file mode 100644 index 0000000..109d854 --- /dev/null +++ b/src/sms/fixTracks.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file fixTracks.c + * \brief functions for making smoothly evolving tracks (partial frequencies) + * + * Tries to fix gaps and short tracks + */ + +#include "sms.h" + +/*! \brief fill a gap in a given track + * + * \param iCurrentFrame currrent frame number + * \param iTrack track to be filled + * \param pIState pointer to the state of tracks + * \param pAnalParams pointer to analysis parameters + */ +static void FillGap(int iCurrentFrame, int iTrack, int *pIState, + SMS_AnalParams *pAnalParams) +{ + int iFrame, iLastFrame = - (pIState[iTrack] - 1); + sfloat fConstant = TWO_PI / pAnalParams->iSamplingRate; + sfloat fFirstMag, fFirstFreq, fLastMag, fLastFreq, fIncrMag, fIncrFreq, + fMag, fTmpPha, fFreq; + + if(iCurrentFrame - iLastFrame < 0) + return; + + /* if firstMag is 0 it means that there is no Gap, just the begining of a track */ + if(pAnalParams->ppFrames[iCurrentFrame - + iLastFrame]->deterministic.pFSinAmp[iTrack] == 0) + { + pIState[iTrack] = 1; + return; + } + + fFirstMag = + pAnalParams->ppFrames[iCurrentFrame - iLastFrame]->deterministic.pFSinAmp[iTrack]; + fFirstFreq = + pAnalParams->ppFrames[iCurrentFrame - iLastFrame]->deterministic.pFSinFreq[iTrack]; + fLastMag = pAnalParams->ppFrames[iCurrentFrame]->deterministic.pFSinAmp[iTrack]; + fLastFreq = pAnalParams->ppFrames[iCurrentFrame]->deterministic.pFSinFreq[iTrack]; + fIncrMag = (fLastMag - fFirstMag) / iLastFrame; + fIncrFreq = (fLastFreq - fFirstFreq) / iLastFrame; + + /* if inharmonic format and the two extremes are very different */ + /* do not interpolate, it means that they are different tracks */ + if((pAnalParams->iFormat == SMS_FORMAT_IH || + pAnalParams->iFormat == SMS_FORMAT_IHP) && + (MIN (fFirstFreq, fLastFreq) * .5 * pAnalParams->fFreqDeviation < + fabs(fLastFreq - fFirstFreq))) + { + pIState[iTrack] = 1; + return; + } + + fMag = fFirstMag; + fFreq = fFirstFreq; + /* fill the gap by interpolating values */ + /* if the gap is too long it should consider the lower partials */ + for(iFrame = iCurrentFrame - iLastFrame + 1; iFrame < iCurrentFrame; iFrame++) + { + /* interpolate magnitude */ + fMag += fIncrMag; + pAnalParams->ppFrames[iFrame]->deterministic.pFSinAmp[iTrack] = fMag; + /* interpolate frequency */ + fFreq += fIncrFreq; + pAnalParams->ppFrames[iFrame]->deterministic.pFSinFreq[iTrack] = fFreq; + /*interpolate phase (this may not be the right way) */ + fTmpPha = + pAnalParams->ppFrames[iFrame-1]->deterministic.pFSinPha[iTrack] - + (pAnalParams->ppFrames[iFrame-1]->deterministic.pFSinFreq[iTrack] * + fConstant) * pAnalParams->sizeHop; + pAnalParams->ppFrames[iFrame]->deterministic.pFSinPha[iTrack] = + fTmpPha - floor(fTmpPha/ TWO_PI) * TWO_PI; + } + + if(pAnalParams->iDebugMode == SMS_DBG_CLEAN_TRAJ || + pAnalParams->iDebugMode == SMS_DBG_ALL) + { + fprintf (stdout, "fillGap: track %d, frames %d to %d filled\n", + iTrack, pAnalParams->ppFrames[iCurrentFrame-iLastFrame + 1]->iFrameNum, + pAnalParams->ppFrames[iCurrentFrame-1]->iFrameNum); + fprintf (stdout, "firstFreq %f lastFreq %f, firstMag %f lastMag %f\n", + fFirstFreq, fLastFreq, fFirstMag, fLastMag); + + } + + /* reset status */ + pIState[iTrack] = pAnalParams->iMinTrackLength; +} + + +/*! \brief delete a short track + * + * this function is not exported to sms.h + * + * \param iCurrentFrame current frame + * \param iTrack track to be deleted + * \param pIState pointer to the state of tracks + * \param pAnalParams pointer to analysis parameters + */ +static void DeleteShortTrack(int iCurrentFrame, int iTrack, int *pIState, + SMS_AnalParams *pAnalParams) +{ + int iFrame, frame; + + for(iFrame = 1; iFrame <= pIState[iTrack]; iFrame++) + { + frame = iCurrentFrame - iFrame; + + if(frame <= 0) + return; + + pAnalParams->ppFrames[frame]->deterministic.pFSinAmp[iTrack] = 0; + pAnalParams->ppFrames[frame]->deterministic.pFSinFreq[iTrack] = 0; + pAnalParams->ppFrames[frame]->deterministic.pFSinPha[iTrack] = 0; + } + + if(pAnalParams->iDebugMode == SMS_DBG_CLEAN_TRAJ || + pAnalParams->iDebugMode == SMS_DBG_ALL) + fprintf(stdout, "deleteShortTrack: track %d, frames %d to %d deleted\n", + iTrack, pAnalParams->ppFrames[iCurrentFrame - pIState[iTrack]]->iFrameNum, + pAnalParams->ppFrames[iCurrentFrame-1]->iFrameNum); + + /* reset state */ + pIState[iTrack] = -pAnalParams->iMaxSleepingTime; +} + +/*! \brief fill gaps and delete short tracks + * + * \param iCurrentFrame current frame number + * \param pAnalParams pointer to analysis parameters + */ +void sms_cleanTracks(int iCurrentFrame, SMS_AnalParams *pAnalParams) +{ + int iTrack, iLength, iFrame; + + /* if fundamental and first partial are short, delete everything */ + if((pAnalParams->iFormat == SMS_FORMAT_H || pAnalParams->iFormat == SMS_FORMAT_HP) && + pAnalParams->ppFrames[iCurrentFrame]->deterministic.pFSinAmp[0] == 0 && + pAnalParams->guideStates[0] > 0 && + pAnalParams->guideStates[0] < pAnalParams->iMinTrackLength && + pAnalParams->ppFrames[iCurrentFrame]->deterministic.pFSinAmp[1] == 0 && + pAnalParams->guideStates[1] > 0 && + pAnalParams->guideStates[1] < pAnalParams->iMinTrackLength) + { + iLength = pAnalParams->guideStates[0]; + for(iTrack = 0; iTrack < pAnalParams->nGuides; iTrack++) + { + for(iFrame = 1; iFrame <= iLength; iFrame++) + { + if((iCurrentFrame - iFrame) >= 0) + { + pAnalParams->ppFrames[iCurrentFrame - + iFrame]->deterministic.pFSinAmp[iTrack] = 0; + pAnalParams->ppFrames[iCurrentFrame - + iFrame]->deterministic.pFSinFreq[iTrack] = 0; + pAnalParams->ppFrames[iCurrentFrame - + iFrame]->deterministic.pFSinPha[iTrack] = 0; + } + } + pAnalParams->guideStates[iTrack] = -pAnalParams->iMaxSleepingTime; + } + if(pAnalParams->iDebugMode == SMS_DBG_CLEAN_TRAJ || + pAnalParams->iDebugMode == SMS_DBG_ALL) + { + fprintf(stdout, "cleanTrack: frame %d to frame %d deleted\n", + pAnalParams->ppFrames[iCurrentFrame-iLength]->iFrameNum, + pAnalParams->ppFrames[iCurrentFrame-1]->iFrameNum); + } + + return; + } + + /* check every partial individually */ + for(iTrack = 0; iTrack < pAnalParams->nGuides; iTrack++) + { + /* track after gap */ + if(pAnalParams->ppFrames[iCurrentFrame]->deterministic.pFSinAmp[iTrack] != 0) + { + if(pAnalParams->guideStates[iTrack] < 0 && + pAnalParams->guideStates[iTrack] > -pAnalParams->iMaxSleepingTime) + FillGap (iCurrentFrame, iTrack, pAnalParams->guideStates, pAnalParams); + else + pAnalParams->guideStates[iTrack] = + (pAnalParams->guideStates[iTrack]<0) ? 1 : pAnalParams->guideStates[iTrack]+1; + } + /* gap after track */ + else + { + if(pAnalParams->guideStates[iTrack] > 0 && + pAnalParams->guideStates[iTrack] < pAnalParams->iMinTrackLength) + DeleteShortTrack (iCurrentFrame, iTrack, pAnalParams->guideStates, pAnalParams); + else + pAnalParams->guideStates[iTrack] = + (pAnalParams->guideStates[iTrack]>0) ? -1 : pAnalParams->guideStates[iTrack]-1; + } + } + return; +} + +/*! \brief scale deterministic magnitude if synthesis is larger than original + * + * \param pFSynthBuffer synthesis buffer + * \param pFOriginalBuffer original sound + * \param pFSinAmp magnitudes to be scaled + * \param pAnalParams pointer to analysis parameters + * \param nTrack number of tracks + */ +void sms_scaleDet(sfloat *pFSynthBuffer, sfloat *pFOriginalBuffer, + sfloat *pFSinAmp, SMS_AnalParams *pAnalParams, int nTrack) +{ + sfloat fOriginalMag = 0, fSynthesisMag = 0; + sfloat fCosScaleFactor; + int iTrack, i; + + /* get sound energy */ + for(i = 0; i < pAnalParams->sizeHop; i++) + { + fOriginalMag += fabs(pFOriginalBuffer[i]); + fSynthesisMag += fabs(pFSynthBuffer[i]); + } + + /* if total energy of deterministic sound is larger than original, + scale deterministic representation */ + if(fSynthesisMag > (1.5 * fOriginalMag)) + { + fCosScaleFactor = fOriginalMag / fSynthesisMag; + + if(pAnalParams->iDebugMode == SMS_DBG_CLEAN_TRAJ || + pAnalParams->iDebugMode == SMS_DBG_ALL) + fprintf(stdout, "Frame %d: magnitude scaled by %f\n", + pAnalParams->ppFrames[0]->iFrameNum, fCosScaleFactor); + + for(iTrack = 0; iTrack < nTrack; iTrack++) + if(pFSinAmp[iTrack] > 0) + pFSinAmp[iTrack] = sms_magToDB(sms_dBToMag(pFSinAmp[iTrack]) * fCosScaleFactor); + } +} + diff --git a/src/sms/harmDetection.c b/src/sms/harmDetection.c new file mode 100644 index 0000000..bf99729 --- /dev/null +++ b/src/sms/harmDetection.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file harmDetection.c + * \brief Detection of a given harmonic + */ + +#include "sms.h" + +#define N_FUND_HARM 6 /*!< number of harmonics to use for fundamental detection */ +#define N_HARM_PEAKS 4 /*!< number of peaks to check as possible ref harmonics */ +#define FREQ_DEV_THRES .07 /*!< threshold for deviation from perfect harmonics */ +#define MAG_PERC_THRES .6 /*!< threshold for magnitude of harmonics + with respect to the total magnitude */ +#define HARM_RATIO_THRES .8 /*!< threshold for percentage of harmonics found */ + +/*! \brief get closest peak to a given harmonic of the possible fundamental + * + * \param iPeakCandidate peak number of possible fundamental + * \param nHarm number of harmonic + * \param pSpectralPeaks pointer to all the peaks + * \param pICurrentPeak pointer to the last peak taken + * \param iRefHarmonic reference harmonic number + * \return the number of the closest peak or -1 if not found + */ +static int GetClosestPeak(int iPeakCandidate, int nHarm, SMS_Peak *pSpectralPeaks, + int *pICurrentPeak, int iRefHarmonic, int maxPeaks) +{ + int iBestPeak = *pICurrentPeak + 1; + int iNextPeak = iBestPeak + 1; + + if((iBestPeak >= maxPeaks) || (iNextPeak >= maxPeaks)) + return -1; + + sfloat fBestPeakFreq = pSpectralPeaks[iBestPeak].fFreq, + fHarmFreq = (1 + nHarm) * pSpectralPeaks[iPeakCandidate].fFreq / iRefHarmonic, + fMinDistance = fabs(fHarmFreq - fBestPeakFreq), + fMaxPeakDev = .5 * fHarmFreq / (nHarm + 1), + fDistance = 0.0; + + fDistance = fabs(fHarmFreq - pSpectralPeaks[iNextPeak].fFreq); + while((fDistance < fMinDistance) && (iNextPeak < maxPeaks - 1)) + { + iBestPeak = iNextPeak; + fMinDistance = fDistance; + iNextPeak++; + fDistance = fabs(fHarmFreq - pSpectralPeaks[iNextPeak].fFreq); + } + + /* make sure the chosen peak is good */ + fBestPeakFreq = pSpectralPeaks[iBestPeak].fFreq; + + /* if best peak is not in the range */ + if(fabs(fBestPeakFreq - fHarmFreq) > fMaxPeakDev) + return -1; + + *pICurrentPeak = iBestPeak; + return iBestPeak; +} + +/*! \brief checks if peak is substantial + * + * check if peak is larger enough to be considered a fundamental + * without any further testing or too small to be considered + * + + * \param fRefHarmMag magnitude of possible fundamental + * \param pSpectralPeaks all the peaks + * \param nCand number of existing candidates + * \param fRefHarmMagDiffFromMax value to judge the peak based on the difference of its magnitude compared to the reference + * \return 1 if big peak, -1 if too small , otherwise return 0 + */ +static int ComparePeak(sfloat fRefHarmMag, SMS_Peak *pSpectralPeaks, int nCand, + sfloat fRefHarmMagDiffFromMax, int maxPeaks) +{ + int iPeak; + sfloat fMag = 0; + + /* if peak is very large take it as possible fundamental */ + if(nCand == 0 && fRefHarmMag > 80.) + return 1; + + /* compare the peak with the first N_FUND_HARM peaks */ + /* if too small forget it */ + for(iPeak = 0; (iPeak < N_FUND_HARM) && (iPeak < maxPeaks); iPeak++) + { + if(pSpectralPeaks[iPeak].fMag > 0 && + fRefHarmMag - pSpectralPeaks[iPeak].fMag < - fRefHarmMagDiffFromMax) + return -1; + } + + /* if it is much bigger than rest take it */ + for(iPeak = 0; (iPeak < N_FUND_HARM) && (iPeak < maxPeaks); iPeak++) + { + fMag = pSpectralPeaks[iPeak].fMag; + if(fMag <= 0 || + ((fMag != fRefHarmMag) && (nCand > 0) && (fRefHarmMag - fMag < 30.0)) || + ((nCand == 0) && (fRefHarmMag - fMag < 15.0))) + return 0; + } + return 1; +} + + +/*! \brief check if the current peak is a harmonic of one of the candidates + * + * \param fFundFreq frequency of peak to be tested + * \param pCHarmonic all candidates accepted + * \param nCand location of las candidate + * \return 1 if it is a harmonic, 0 if it is not + */ +static int CheckIfHarmonic(sfloat fFundFreq, SMS_HarmCandidate *pCHarmonic, int nCand) +{ + int iPeak; + + /* go through all the candidates checking if they are fundamentals + * of the peak to be considered */ + for(iPeak = 0; iPeak < nCand; iPeak++) + { + if(fabs(floor((double)(fFundFreq / pCHarmonic[iPeak].fFreq) + .5) - + (fFundFreq / pCHarmonic[iPeak].fFreq)) <= .1) + return 1; + } + return 0; +} + + +/*! \brief consider a peak as a possible candidate and give it a weight value, + * + * \param iPeak iPeak number to be considered + * \param pSpectralPeaks all the peaks + * \param pCHarmonic all the candidates + * \param nCand candidate number that is to be filled + * \param pPeakParams analysis parameters + * \param fRefFundamental previous fundamental + * \return -1 if not good enough for a candidate, return 0 if reached + * the top frequency boundary, return -2 if stop checking because it + * found a really good one, return 1 if the peak is a good candidate + */ + +static int GoodCandidate(int iPeak, int maxPeaks, SMS_Peak *pSpectralPeaks, + SMS_HarmCandidate *pCHarmonic, int nCand, int soundType, sfloat fRefFundamental, + sfloat minRefHarmMag, sfloat refHarmMagDiffFromMax, sfloat refHarmonic) +{ + sfloat fHarmFreq = 0.0, + fRefHarmFreq = 0.0, + fRefHarmMag = 0.0, + fTotalMag = 0.0, + fTotalDev = 0.0, + fTotalMaxMag = 0.0, + fAvgMag = 0.0, + fAvgDev = 0.0, + fHarmRatio = 0.0; + int iHarm = 0, + iChosenPeak = 0, + iPeakComp = 0, + iCurrentPeak = 0, + nGoodHarm = 0, + i = 0; + + fRefHarmFreq = fHarmFreq = pSpectralPeaks[iPeak].fFreq; + + fTotalDev = 0; + fRefHarmMag = pSpectralPeaks[iPeak].fMag; + fTotalMag = fRefHarmMag; + + /* check if magnitude is big enough */ + /*! \bug sfloat comparison to 0 */ + if(((fRefFundamental > 0) && (fRefHarmMag < minRefHarmMag - 10)) || + ((fRefFundamental <= 0) && (fRefHarmMag < minRefHarmMag))) + return -1; + + /* check that it is not a harmonic of a previous candidate */ + if(nCand > 0 && + CheckIfHarmonic(fRefHarmFreq / refHarmonic, pCHarmonic, nCand)) + return -1; + + /* check if it is very big or very small */ + iPeakComp = ComparePeak(fRefHarmMag, pSpectralPeaks, nCand, refHarmMagDiffFromMax, maxPeaks); + + /* too small */ + if(iPeakComp == -1) + return -1; + /* very big */ + else if(iPeakComp == 1) + { + pCHarmonic[nCand].fFreq = fRefHarmFreq; + pCHarmonic[nCand].fMag = fRefHarmMag; + pCHarmonic[nCand].fMagPerc = 1; + pCHarmonic[nCand].fFreqDev = 0; + pCHarmonic[nCand].fHarmRatio = 1; + return -2; + } + + /* get a weight on the peak by comparing its harmonic series */ + /* with the existing peaks */ + if(soundType != SMS_SOUND_TYPE_NOTE) + { + fHarmFreq = fRefHarmFreq; + iCurrentPeak = iPeak; + nGoodHarm = 0; + for(iHarm = refHarmonic; (iHarm < N_FUND_HARM) && (iHarm < maxPeaks); iHarm++) + { + fHarmFreq += fRefHarmFreq / refHarmonic; + iChosenPeak = GetClosestPeak(iPeak, iHarm, pSpectralPeaks, + &iCurrentPeak, refHarmonic, + maxPeaks); + if(iChosenPeak > 0) + { + fTotalDev += fabs(fHarmFreq - pSpectralPeaks[iChosenPeak].fFreq) / + fHarmFreq; + fTotalMag += pSpectralPeaks[iChosenPeak].fMag; + nGoodHarm++; + } + } + + for(i = 0; i <= iCurrentPeak; i++) + fTotalMaxMag += pSpectralPeaks[i].fMag; + + fAvgDev = fTotalDev / (iHarm + 1); + fAvgMag = fTotalMag / fTotalMaxMag; + fHarmRatio = (sfloat)nGoodHarm / (N_FUND_HARM - 1); + + if(fRefFundamental > 0) + { + if(fAvgDev > FREQ_DEV_THRES || fAvgMag < MAG_PERC_THRES - .1 || + fHarmRatio < HARM_RATIO_THRES - .1) + return -1; + } + else + { + if(fAvgDev > FREQ_DEV_THRES || fAvgMag < MAG_PERC_THRES || + fHarmRatio < HARM_RATIO_THRES) + return -1; + } + } + + pCHarmonic[nCand].fFreq = fRefHarmFreq; + pCHarmonic[nCand].fMag = fRefHarmMag; + pCHarmonic[nCand].fMagPerc = fAvgMag; + pCHarmonic[nCand].fFreqDev = fAvgDev; + pCHarmonic[nCand].fHarmRatio = fHarmRatio; + + return 1; +} + +/*! \brief choose the best fundamental out of all the candidates + * + * \param pCHarmonic array of candidates + * \param iRefHarmonic reference harmonic number + * \param nGoodPeaks number of candiates + * \param fPrevFund reference fundamental + * \return the integer number of the best candidate + */ +static int GetBestCandidate(SMS_HarmCandidate *pCHarmonic, + int iRefHarmonic, int nGoodPeaks, sfloat fPrevFund) +{ + int iBestCandidate = 0, iPeak; + sfloat fBestFreq, fHarmFreq, fDev; + + /* if a fundamental existed in previous frame take the closest candidate */ + if(fPrevFund > 0) + { + for(iPeak = 1; iPeak < nGoodPeaks; iPeak++) + { + if(fabs(fPrevFund - pCHarmonic[iPeak].fFreq / iRefHarmonic) < + fabs(fPrevFund - pCHarmonic[iBestCandidate].fFreq / iRefHarmonic)) + iBestCandidate = iPeak; + } + } + else + { + /* try to find the best candidate */ + for(iPeak = 1; iPeak < nGoodPeaks; iPeak++) + { + fBestFreq = pCHarmonic[iBestCandidate].fFreq / iRefHarmonic; + fHarmFreq = fBestFreq * floor(.5 + + (pCHarmonic[iPeak].fFreq / iRefHarmonic) / + fBestFreq); + fDev = fabs(fHarmFreq - (pCHarmonic[iPeak].fFreq / iRefHarmonic)) / fHarmFreq; + + /* if candidate is far from harmonic from best candidate and + * bigger, take it */ + if(fDev > .2 && + pCHarmonic[iPeak].fMag > pCHarmonic[iBestCandidate].fMag) + iBestCandidate = iPeak; + /* if frequency deviation is much smaller, take it */ + else if(pCHarmonic[iPeak].fFreqDev < .2 * pCHarmonic[iBestCandidate].fFreqDev) + iBestCandidate = iPeak; + /* if freq. deviation is smaller and bigger amplitude, take it */ + else if(pCHarmonic[iPeak].fFreqDev < pCHarmonic[iBestCandidate].fFreqDev && + pCHarmonic[iPeak].fMagPerc > pCHarmonic[iBestCandidate].fMagPerc && + pCHarmonic[iPeak].fMag > pCHarmonic[iBestCandidate].fMag) + iBestCandidate = iPeak; + } + } + return iBestCandidate; +} + +/*! \brief main harmonic detection function + * + * find a given harmonic peak from a set of spectral peaks, + * put the frequency of the fundamental in the current frame + * + * \param pFrame pointer to current frame + * \param fRefFundamental frequency of previous frame + * \param pPeakParams pointer to analysis parameters + * \todo is it possible to use pSpectralPeaks instead of SMS_AnalFrame? + * \todo move pCHarmonic array to SMS_AnalFrame structure + - this will allow for analysis of effectiveness from outside this file + * This really should only be for sms_analyzeFrame + */ +sfloat sms_harmDetection(int numPeaks, SMS_Peak* spectralPeaks, sfloat refFundamental, + sfloat refHarmonic, sfloat lowestFreq, sfloat highestFreq, + int soundType, sfloat minRefHarmMag, sfloat refHarmMagDiffFromMax) +{ + int iPeak = -1, nGoodPeaks = 0, iCandidate, iBestCandidate; + sfloat peakFreq=0; + SMS_HarmCandidate pCHarmonic[N_HARM_PEAKS]; + + /* find all possible candidates to use as harmonic reference */ + lowestFreq = lowestFreq * refHarmonic; + highestFreq = highestFreq * refHarmonic; + + while((peakFreq < highestFreq) && (iPeak < numPeaks - 1)) + { + iPeak++; + peakFreq = spectralPeaks[iPeak].fFreq; + if(peakFreq > highestFreq) + break; + + /* no more peaks */ + if(spectralPeaks[iPeak].fMag <= 0) /*!< \bug sfloat comparison to zero */ + break; + + /* peak too low */ + if(peakFreq < lowestFreq) + continue; + + /* if previous fundamental look only around it */ + if(refFundamental > 0 && + fabs(peakFreq - (refHarmonic * refFundamental)) / refFundamental > .5) + continue; + + iCandidate = GoodCandidate(iPeak, numPeaks, spectralPeaks, pCHarmonic, + nGoodPeaks, soundType, refFundamental, + minRefHarmMag, refHarmMagDiffFromMax, refHarmonic); + + /* good candiate found */ + if(iCandidate == 1) + nGoodPeaks++; + + /* a perfect candiate found */ + else if(iCandidate == -2) + { + nGoodPeaks++; + break; + } + } + + /* if no candidate for fundamental, continue */ + if(nGoodPeaks == 0) + return -1; + /* if only 1 candidate for fundamental take it */ + else if(nGoodPeaks == 1) + return pCHarmonic[0].fFreq / refHarmonic; + /* if more than one candidate choose the best one */ + else + { + iBestCandidate = GetBestCandidate(pCHarmonic, refHarmonic, nGoodPeaks, refFundamental); + return pCHarmonic[iBestCandidate].fFreq / refHarmonic; + } +} diff --git a/src/sms/modify.c b/src/sms/modify.c new file mode 100644 index 0000000..1afdf82 --- /dev/null +++ b/src/sms/modify.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2009 John Glover, National University of Ireland, Maynooth + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/*! \file modify.c + * \brief modify sms data + */ + +#include "sms.h" + +/*! \brief initialize a modifications structure based on an SMS_Header + * + * \param params pointer to parameter structure + * \param header pointer to sms header + */ +void sms_initModify(SMS_Header *header, SMS_ModifyParams *params) +{ + static int sizeEnvArray = 0; + params->maxFreq = header->iMaxFreq; + params->sizeSinEnv = header->nEnvCoeff; + + if(sizeEnvArray < params->sizeSinEnv) + { + if(sizeEnvArray != 0) free(params->sinEnv); + if ((params->sinEnv = (sfloat *) malloc(params->sizeSinEnv * sizeof(sfloat))) == NULL) + { + sms_error("could not allocate memory for envelope array"); + return; + } + sizeEnvArray = params->sizeSinEnv; + } + params->ready = 1; +} + +/*! \brief initialize modification parameters + * + * \todo call this from sms_initSynth()? some other mod params are updated there + * + * \param params pointer to parameters structure + */ +void sms_initModifyParams(SMS_ModifyParams *params) +{ + params->ready = 0; + params->doResGain = 0; + params->resGain = 1.; + params->doTranspose = 0; + params->transpose = 0; + params->doSinEnv = 0; + params->sinEnvInterp = 0.; + params->sizeSinEnv = 0; + params->doResEnv = 0; + params->resEnvInterp = 0.; + params->sizeResEnv = 0; +} + +/*! \brief free memory allocated during initialization + * + * \param params pointer to parameter structure + */ +void sms_freeModify(SMS_ModifyParams *params) +{ +} + +/*! \brief linear interpolation between 2 spectral envelopes. + * + * The values in env2 are overwritten by the new interpolated envelope values. + */ +void sms_interpEnvelopes(int sizeEnv, sfloat *env1, sfloat *env2, sfloat interpFactor) +{ + if(sizeEnv <= 0) + { + return; + } + + int i; + sfloat amp1, amp2; + + for(i = 0; i < sizeEnv; i++) + { + amp1 = env1[i]; + amp2 = env2[i]; + if(amp1 <= 0) amp1 = amp2; + if(amp2 <= 0) amp2 = amp1; + env2[i] = amp1 + (interpFactor * (amp2 - amp1)); + } +} + +/*! \brief apply the spectral envelope of 1 sound to another + * + * Changes the amplitude of spectral peaks in a target sound (pFreqs, pMags) to match those + * in the envelope (pCepEnvFreqs, pCepEnvMags) of another, up to a maximum frequency of maxFreq. + */ +void sms_applyEnvelope(int numPeaks, sfloat *pFreqs, sfloat *pMags, int sizeEnv, sfloat *pEnvMags, int maxFreq) +{ + if(sizeEnv <= 0 || maxFreq <= 0) + { + return; + } + + int i, envPos; + sfloat frac, binSize = (sfloat)maxFreq / (sfloat)sizeEnv; + + for(i = 0; i < numPeaks; i++) + { + /* convert peak freqs into bin positions for quicker envelope lookup */ + /* \todo try to remove so many pFreq lookups and get rid of divide */ + pFreqs[i] /= binSize; + + /* if current peak is within envelope range, set its mag to the envelope mag */ + if(pFreqs[i] < (sizeEnv-1) && pFreqs[i] > 0) + { + envPos = (int)pFreqs[i]; + frac = pFreqs[i] - envPos; + if(envPos < sizeEnv - 1) + { + pMags[i] = ((1.0 - frac) * pEnvMags[envPos]) + (frac * pEnvMags[envPos+1]); + } + else + { + pMags[i] = pEnvMags[sizeEnv-1]; + } + } + else + { + pMags[i] = 0; + } + + /* convert back to frequency values */ + pFreqs[i] *= binSize; + } + +} + +/*! \brief scale the residual gain factor + * + * \param frame pointer to sms data + * \param gain factor to scale the residual + */ +void sms_resGain(SMS_Data *frame, sfloat gain) +{ + int i; + for( i = 0; i < frame->nCoeff; i++) + frame->pFStocCoeff[i] *= gain; +} + + +/*! \brief basic transposition + * Multiply the frequencies of the deterministic component by a constant + */ +void sms_transpose(SMS_Data *frame, sfloat transpositionFactor) +{ + int i; + for(i = 0; i < frame->nTracks; i++) + { + frame->pFSinFreq[i] *= sms_scalarTempered(transpositionFactor); + } +} + + +/*! \brief transposition maintaining spectral envelope + * + * Multiply the frequencies of the deterministic component by a constant, then change + * their amplitudes so that the original spectral envelope is maintained + */ +void sms_transposeKeepEnv(SMS_Data *frame, sfloat transpositionFactor, int maxFreq) +{ + sms_transpose(frame, transpositionFactor); + sms_applyEnvelope(frame->nTracks, frame->pFSinFreq, frame->pFSinAmp, frame->nEnvCoeff, frame->pSpecEnv, maxFreq); +} + +/*! \brief modify a frame (SMS_Data object) + * + * Performs a modification on a SMS_Data object. The type of modification and any additional + * parameters are specified in the given SMS_ModifyParams structure. + */ +void sms_modify(SMS_Data *frame, SMS_ModifyParams *params) +{ + if(params->doResGain) + sms_resGain(frame, params->resGain); + + if(params->doTranspose) + sms_transpose(frame, params->transpose); + + if(params->doSinEnv) + { + if(params->sinEnvInterp < .00001) /* maintain original */ + sms_applyEnvelope(frame->nTracks, frame->pFSinFreq, frame->pFSinAmp, + frame->nEnvCoeff, frame->pSpecEnv, params->maxFreq); + else + { + if(params->sinEnvInterp > .00001 && params->sinEnvInterp < .99999) + sms_interpEnvelopes(params->sizeSinEnv, frame->pSpecEnv, params->sinEnv, params->sinEnvInterp); + + sms_applyEnvelope(frame->nTracks, frame->pFSinFreq, frame->pFSinAmp, + params->sizeSinEnv, params->sinEnv, params->maxFreq); + + } + } +} + diff --git a/src/sms/peakContinuation.c b/src/sms/peakContinuation.c new file mode 100644 index 0000000..b61d14e --- /dev/null +++ b/src/sms/peakContinuation.c @@ -0,0 +1,481 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file peakContinuation.c + * \brief peak continuation algorithm and functions + */ + +#include "sms.h" + +/*! guide states */ +#define GUIDE_BEG -2 +#define GUIDE_DEAD -1 +#define GUIDE_ACTIVE 0 + +/*!< maximum number of peak continuation candidates */ +#define MAX_CONT_CANDIDATES 5 + +/*! \brief function to get the next closest peak from a guide + * + * \param fGuideFreq guide's frequency + * \param pFFreqDistance distance of last best peak from guide + * \param pSpectralPeaks array of peaks + * \param pAnalParams analysis parameters + * \param fFreqDev maximum deviation from guide + * \return peak number or -1 if nothing is good + */ +static int GetNextClosestPeak(sfloat fGuideFreq, sfloat *pFFreqDistance, + SMS_Peak *pSpectralPeaks, SMS_AnalParams *pAnalParams, + sfloat fFreqDev) +{ + int iInitialPeak = SMS_MAX_NPEAKS * fGuideFreq / (pAnalParams->iSamplingRate * .5); + int iLowPeak, iHighPeak, iChosenPeak = -1; + sfloat fLowDistance, fHighDistance, fFreq; + + if(iInitialPeak >= pAnalParams->maxPeaks) + iInitialPeak = 0; + else if(pSpectralPeaks[iInitialPeak].fFreq <= 0) + iInitialPeak = 0; + + /* find a low peak to start */ + fLowDistance = fGuideFreq - pSpectralPeaks[iInitialPeak].fFreq; + if(floor(fLowDistance) < floor(*pFFreqDistance)) + { + while(floor(fLowDistance) <= floor(*pFFreqDistance) && + iInitialPeak > 0) + { + iInitialPeak--; + fLowDistance = fGuideFreq - pSpectralPeaks[iInitialPeak].fFreq; + } + } + else + { + while(floor(fLowDistance) >= floor(*pFFreqDistance) && + iInitialPeak < (pAnalParams->maxPeaks-1)) + { + iInitialPeak++; + if((fFreq = pSpectralPeaks[iInitialPeak].fFreq) == 0) + return -1; + fLowDistance = fGuideFreq - fFreq; + } + if(iInitialPeak > 0) + iInitialPeak--; + fLowDistance = fGuideFreq - pSpectralPeaks[iInitialPeak].fFreq; + } + + if(floor(fLowDistance) <= floor(*pFFreqDistance) || + fLowDistance > fFreqDev) + iLowPeak = -1; + else + iLowPeak = iInitialPeak; + + /* find a high peak to finish */ + iHighPeak = iInitialPeak; + fHighDistance = fGuideFreq - pSpectralPeaks[iHighPeak].fFreq; + while(floor(fHighDistance) >= floor(-*pFFreqDistance) && + iHighPeak < (pAnalParams->maxPeaks - 1)) + { + iHighPeak++; + if((fFreq = pSpectralPeaks[iHighPeak].fFreq) == 0) + { + iHighPeak = -1; + break; + } + fHighDistance = fGuideFreq - fFreq; + } + if(fHighDistance > 0 || fabs(fHighDistance) > fFreqDev || + floor(fabs(fHighDistance)) <= floor(*pFFreqDistance)) + iHighPeak = -1; + + /* chose between the two extrema */ + if(iHighPeak >= 0 && iLowPeak >= 0) + { + if(fabs(fHighDistance) > fLowDistance) + iChosenPeak = iLowPeak; + else + iChosenPeak = iHighPeak; + } + else if(iHighPeak < 0 && iLowPeak >= 0) + iChosenPeak = iLowPeak; + else if(iHighPeak >= 0 && iLowPeak < 0) + iChosenPeak = iHighPeak; + else + return -1; + + *pFFreqDistance = fabs(fGuideFreq - pSpectralPeaks[iChosenPeak].fFreq); + return iChosenPeak; +} + +/*! \brief choose the best candidate out of all + * + * \param pCandidate pointer to all the continuation candidates + * \param nCandidates number of candidates + * \param fFreqDev maximum frequency deviation allowed + * \return the peak number of the best candidate + */ +static int ChooseBestCand(SMS_ContCandidate *pCandidate, int nCandidates, sfloat fFreqDev) +{ + int i, iHighestCand, iClosestCand, iBestCand = 0; + sfloat fMaxMag, fClosestFreq; + + /* intial guess */ + iClosestCand = 0; + fClosestFreq = pCandidate[iClosestCand].fFreqDev; + iHighestCand = 0; + fMaxMag = pCandidate[iHighestCand].fMagDev; + + /* get the best candidate */ + for (i = 1; i < nCandidates; i++) + { + /* look for the one with highest magnitude */ + if (pCandidate[i].fMagDev > fMaxMag) + { + fMaxMag = pCandidate[i].fMagDev; + iHighestCand = i; + } + /* look for the closest one to the guide */ + if (pCandidate[i].fFreqDev < fClosestFreq) + { + fClosestFreq = pCandidate[i].fFreqDev; + iClosestCand = i; + } + } + iBestCand = iHighestCand; + + /* reconcile the two results */ + if(iBestCand != iClosestCand && + fabs(pCandidate[iHighestCand].fFreqDev - fClosestFreq) > fFreqDev / 2) + iBestCand = iClosestCand; + + return pCandidate[iBestCand].iPeak; +} + +/*! \brief check for one guide that has choosen iBestPeak + * + * \param iBestPeak choosen peak for a guide + * \param pGuides array of guides + * \param nGuides total number of guides + * \return number of guide that chose the peak, or -1 if none + */ +static int CheckForConflict(int iBestPeak, SMS_Guide *pGuides, int nGuides) +{ + int iGuide; + + for (iGuide = 0; iGuide < nGuides; iGuide++) + if (pGuides[iGuide].iPeakChosen == iBestPeak) + return iGuide; + + return -1; +} + +/*! \brief chose the best of the two guides for the conflicting peak + * + * \param iConflictingGuide conflicting guide number + * \param iGuide guide number + * \param pGuides array of guides + * \param pSpectralPeaks array of peaks + * \return number of guide + */ +static int BestGuide(int iConflictingGuide, int iGuide, SMS_Guide *pGuides, + SMS_Peak *pSpectralPeaks) +{ + int iConflictingPeak = pGuides[iConflictingGuide].iPeakChosen; + sfloat fGuideDistance = fabs(pSpectralPeaks[iConflictingPeak].fFreq - + pGuides[iGuide].fFreq); + sfloat fConfGuideDistance = fabs(pSpectralPeaks[iConflictingPeak].fFreq - + pGuides[iConflictingGuide].fFreq); + + if(fGuideDistance > fConfGuideDistance) + return iConflictingGuide; + else + return iGuide; +} + +/*! \brief function to find the best continuation peak for a given guide + * \param pGuides guide attributes + * \param iGuide number of guide + * \param pSpectralPeaks peak values at the current frame + * \param pAnalParams analysis parameters + * \param fFreqDev frequency deviation allowed + * \return the peak number + */ +int GetBestPeak(SMS_Guide *pGuides, int iGuide, SMS_Peak *pSpectralPeaks, + SMS_AnalParams *pAnalParams, sfloat fFreqDev) +{ + int iCand = 0, iPeak, iBestPeak, iConflictingGuide, iWinnerGuide; + sfloat fGuideFreq = pGuides[iGuide].fFreq, + fGuideMag = pGuides[iGuide].fMag, + fMagDistance = 0; + sfloat fFreqDistance = -1; + SMS_ContCandidate pCandidate[MAX_CONT_CANDIDATES]; + + /* find all possible candidates */ + while (iCand < MAX_CONT_CANDIDATES) + { + /* find the next best peak */ + if((iPeak = GetNextClosestPeak(fGuideFreq, &fFreqDistance, + pSpectralPeaks, pAnalParams, fFreqDev)) < 0) + break; + + /* if the peak's magnitude is not too small accept it as */ + /* possible candidate */ + if ((fMagDistance = pSpectralPeaks[iPeak].fMag - fGuideMag) > -20.0) + { + pCandidate[iCand].fFreqDev = fabs(fFreqDistance); + pCandidate[iCand].fMagDev = fMagDistance; + pCandidate[iCand].iPeak = iPeak; + + if(pAnalParams->iDebugMode == SMS_DBG_PEAK_CONT || + pAnalParams->iDebugMode == SMS_DBG_ALL) + fprintf(stdout, "candidate %d: freq %f mag %f\n", + iCand, pSpectralPeaks[iPeak].fFreq, + pSpectralPeaks[iPeak].fMag); + iCand++; + } + } + /* get best candidate */ + if(iCand < 1) + return 0; + else if (iCand == 1) + iBestPeak = pCandidate[0].iPeak; + else + iBestPeak = ChooseBestCand (pCandidate, iCand, + pAnalParams->fFreqDeviation); + + if(pAnalParams->iDebugMode == SMS_DBG_PEAK_CONT || + pAnalParams->iDebugMode == SMS_DBG_ALL) + fprintf (stdout, "BestCandidate: freq %f\n", + pSpectralPeaks[iBestPeak].fFreq); + + /* if peak taken by another guide resolve conflict */ + if ((iConflictingGuide = CheckForConflict (iBestPeak, pGuides, + pAnalParams->nGuides)) >= 0) + { + iWinnerGuide = BestGuide (iConflictingGuide, iGuide, pGuides, + pSpectralPeaks); + if(pAnalParams->iDebugMode == SMS_DBG_PEAK_CONT || + pAnalParams->iDebugMode == SMS_DBG_ALL) + fprintf (stdout, + "Conflict: guide: %d (%f), and guide: %d (%f). best: %d\n", + iGuide, pGuides[iGuide].fFreq, + iConflictingGuide, pGuides[iConflictingGuide].fFreq, + iWinnerGuide); + + if (iGuide == iWinnerGuide) + { + pGuides[iGuide].iPeakChosen = iBestPeak; + pGuides[iConflictingGuide].iPeakChosen = -1; + } + } + else + pGuides[iGuide].iPeakChosen = iBestPeak; + + return iBestPeak; +} + +/*! \brief function to get the next maximum (magnitude) peak + * \param pSpectralPeaks array of peaks + * \param pFCurrentMax last peak maximum + * \return the number of the maximum peak + */ +static int GetNextMax(SMS_Peak *pSpectralPeaks, SMS_AnalParams *pAnalParams, + sfloat *pFCurrentMax) +{ + sfloat fPeakMag; + sfloat fMaxMag = 0.; + int iPeak, iMaxPeak = -1; + + for (iPeak = 0; iPeak < pAnalParams->maxPeaks; iPeak++) + { + fPeakMag = pSpectralPeaks[iPeak].fMag; + + if (fPeakMag == 0) + break; + + if (fPeakMag > fMaxMag && fPeakMag < *pFCurrentMax) + { + iMaxPeak = iPeak; + fMaxMag = fPeakMag; + } + } + *pFCurrentMax = fMaxMag; + return (iMaxPeak); +} + +/*! \brief function to get a good starting peak for a track + * + * \param iGuide current guide + * \param pGuides array of guides + * \param nGuides total number of guides + * \param pSpectralPeaks array of peaks + * \param pFCurrentMax current peak maximum + * \return \todo should this return something? + */ +static int GetStartingPeak(int iGuide, SMS_Guide *pGuides, int nGuides, + SMS_Peak *pSpectralPeaks, SMS_AnalParams *pAnalParams, + sfloat *pFCurrentMax) +{ + int iPeak = -1; + short peakNotFound = 1; + + while (peakNotFound == 1 && *pFCurrentMax > 0) + { + /* \todo I don't think this ever returns -1, but check */ + if ((iPeak = GetNextMax(pSpectralPeaks, pAnalParams, pFCurrentMax)) < 0) + return (-1); + + if (CheckForConflict (iPeak, pGuides, nGuides) < 0) + { + pGuides[iGuide].iPeakChosen = iPeak; + pGuides[iGuide].iStatus = GUIDE_BEG; + pGuides[iGuide].fFreq = pSpectralPeaks[iPeak].fFreq; + peakNotFound = 0; + } + } + return (1); +} + +/*! \brief function to advance the guides through the next frame + * + * the output is the frequency, magnitude, and phase tracks + * + * \param iFrame current frame number + * \param pAnalParams analysis parameters + * \return error code \see SMS_ERRORS + */ +int sms_peakContinuation(int iFrame, SMS_AnalParams *pAnalParams) +{ + int iGuide, iCurrentPeak = -1, iGoodPeak = -1; + sfloat fFund = pAnalParams->ppFrames[iFrame]->fFundamental, + fFreqDev = fFund * pAnalParams->fFreqDeviation, fCurrentMax = 1000; + + /* update guides with fundamental contribution */ + if(fFund > 0 && (pAnalParams->iFormat == SMS_FORMAT_H || + pAnalParams->iFormat == SMS_FORMAT_HP)) + for(iGuide = 0; iGuide < pAnalParams->nGuides; iGuide++) + pAnalParams->guides[iGuide].fFreq = + (1 - pAnalParams->fFundContToGuide) * pAnalParams->guides[iGuide].fFreq + + pAnalParams->fFundContToGuide * fFund * (iGuide + 1); + + if(pAnalParams->iDebugMode == SMS_DBG_PEAK_CONT || + pAnalParams->iDebugMode == SMS_DBG_ALL) + fprintf(stdout, "Frame %d Peak Continuation: \n", + pAnalParams->ppFrames[iFrame]->iFrameNum); + + /* continue all guides */ + for(iGuide = 0; iGuide < pAnalParams->nGuides; iGuide++) + { + sfloat fPreviousFreq = pAnalParams->ppFrames[iFrame-1]->deterministic.pFSinFreq[iGuide]; + + /* get the guide value by upgrading the previous guide */ + if(fPreviousFreq > 0) + pAnalParams->guides[iGuide].fFreq = + (1 - pAnalParams->fPeakContToGuide) * pAnalParams->guides[iGuide].fFreq + + pAnalParams->fPeakContToGuide * fPreviousFreq; + + if(pAnalParams->iDebugMode == SMS_DBG_PEAK_CONT || + pAnalParams->iDebugMode == SMS_DBG_ALL) + fprintf(stdout, "Guide %d: freq %f, mag %f\n", + iGuide, pAnalParams->guides[iGuide].fFreq, pAnalParams->guides[iGuide].fMag); + + if(pAnalParams->guides[iGuide].fFreq <= 0.0 || + pAnalParams->guides[iGuide].fFreq > pAnalParams->fHighestFreq) + { + pAnalParams->guides[iGuide].iStatus = GUIDE_DEAD; + pAnalParams->guides[iGuide].fFreq = 0; + continue; + } + + pAnalParams->guides[iGuide].iPeakChosen = -1; + + if(pAnalParams->iFormat == SMS_FORMAT_IH || + pAnalParams->iFormat == SMS_FORMAT_IHP) + fFreqDev = pAnalParams->guides[iGuide].fFreq * pAnalParams->fFreqDeviation; + + /* get the best peak for the guide */ + iGoodPeak = + GetBestPeak(pAnalParams->guides, iGuide, pAnalParams->ppFrames[iFrame]->pSpectralPeaks, + pAnalParams, fFreqDev); + } + + /* try to find good peaks for the GUIDE_DEAD guides */ + if(pAnalParams->iFormat == SMS_FORMAT_IH || + pAnalParams->iFormat == SMS_FORMAT_IHP) + for(iGuide = 0; iGuide < pAnalParams->nGuides; iGuide++) + { + if(pAnalParams->guides[iGuide].iStatus != GUIDE_DEAD) + continue; + + if(GetStartingPeak(iGuide, pAnalParams->guides, pAnalParams->nGuides, + pAnalParams->ppFrames[iFrame]->pSpectralPeaks, + pAnalParams, &fCurrentMax) == -1) + break; + } + + /* save all the continuation values, + * assume output tracks are already clear */ + for(iGuide = 0; iGuide < pAnalParams->nGuides; iGuide++) + { + if(pAnalParams->guides[iGuide].iStatus == GUIDE_DEAD) + continue; + + if(pAnalParams->iFormat == SMS_FORMAT_IH || + pAnalParams->iFormat == SMS_FORMAT_IHP) + { + if(pAnalParams->guides[iGuide].iStatus > 0 && + pAnalParams->guides[iGuide].iPeakChosen == -1) + { + if(pAnalParams->guides[iGuide].iStatus++ > pAnalParams->iMaxSleepingTime) + { + pAnalParams->guides[iGuide].iStatus = GUIDE_DEAD; + pAnalParams->guides[iGuide].fFreq = 0; + pAnalParams->guides[iGuide].fMag = 0; + pAnalParams->guides[iGuide].iPeakChosen = -1; + } + else + pAnalParams->guides[iGuide].iStatus++; + continue; + } + + if(pAnalParams->guides[iGuide].iStatus == GUIDE_ACTIVE && + pAnalParams->guides[iGuide].iPeakChosen == -1) + { + pAnalParams->guides[iGuide].iStatus = 1; + continue; + } + } + + /* if good continuation peak found, save it */ + if((iCurrentPeak = pAnalParams->guides[iGuide].iPeakChosen) >= 0) + { + pAnalParams->ppFrames[iFrame]->deterministic.pFSinFreq[iGuide] = + pAnalParams->ppFrames[iFrame]->pSpectralPeaks[iCurrentPeak].fFreq; + pAnalParams->ppFrames[iFrame]->deterministic.pFSinAmp[iGuide] = + pAnalParams->ppFrames[iFrame]->pSpectralPeaks[iCurrentPeak].fMag; + pAnalParams->ppFrames[iFrame]->deterministic.pFSinPha[iGuide] = + pAnalParams->ppFrames[iFrame]->pSpectralPeaks[iCurrentPeak].fPhase; + + pAnalParams->guides[iGuide].iStatus = GUIDE_ACTIVE; + pAnalParams->guides[iGuide].iPeakChosen = -1; + } + } + return SMS_OK; +} + diff --git a/src/sms/peakDetection.c b/src/sms/peakDetection.c new file mode 100644 index 0000000..4015c27 --- /dev/null +++ b/src/sms/peakDetection.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file peakDetection.c + * \brief peak detection algorithm and functions + */ + +#include "sms.h" + +/*! \brief function used for the parabolic interpolation of the spectral peaks + * + * it performs the interpolation in a log scale and + * stores the location in pFDiffFromMax and + * + * \param fMaxVal value of max bin + * \param fLeftBinVal value for left bin + * \param fRightBinVal value for right bin + * \param pFDiffFromMax location of the tip as the difference from the top bin + * \return the peak height + */ +static sfloat PeakInterpolation(sfloat fMaxVal, sfloat fLeftBinVal, + sfloat fRightBinVal, sfloat *pFDiffFromMax) +{ + /* get the location of the tip of the parabola */ + *pFDiffFromMax = (.5 * (fLeftBinVal - fRightBinVal) / + (fLeftBinVal - (2*fMaxVal) + fRightBinVal)); + /* return the value at the tip */ + return fMaxVal - (.25 * (fLeftBinVal - fRightBinVal) * *pFDiffFromMax); +} + +/*! \brief detect the next local maximum in the spectrum + * + * stores the value in pFMaxVal + * + * \todo export this to sms.h and wrap in pysms + * + * \param pFMagSpectrum magnitude spectrum + * \param iHighBinBound highest bin to search + * \param pICurrentLoc current bin location + * \param pFMaxVal value of the maximum found + * \param fMinPeakMag minimum magnitude to accept a peak + * \return the bin location of the maximum + */ +static int FindNextMax(sfloat *pFMagSpectrum, int iHighBinBound, + int *pICurrentLoc, sfloat *pFMaxVal, sfloat fMinPeakMag) +{ + int iCurrentBin = *pICurrentLoc; + sfloat fPrevVal = pFMagSpectrum[iCurrentBin - 1]; + sfloat fCurrentVal = pFMagSpectrum[iCurrentBin]; + sfloat fNextVal = (iCurrentBin >= iHighBinBound) ? 0 : pFMagSpectrum[iCurrentBin + 1]; + + /* try to find a local maximum */ + while(iCurrentBin <= iHighBinBound) + { + if(fCurrentVal > fMinPeakMag && + fCurrentVal >= fPrevVal && + fCurrentVal >= fNextVal) + break; + iCurrentBin++; + fPrevVal = fCurrentVal; + fCurrentVal = fNextVal; + fNextVal = pFMagSpectrum[1+iCurrentBin]; + } + + /* save the current location, value of maximum and return */ + /* location of max */ + *pICurrentLoc = iCurrentBin + 1; + *pFMaxVal = fCurrentVal; + return iCurrentBin; +} + +/*! \brief function to detect the next spectral peak + * + * \param pFMagSpectrum magnitude spectrum + * \param iHighestBin highest bin to search + * \param pICurrentLoc current bin location + * \param pFPeakMag magnitude value of peak + * \param pFPeakLoc location of peak + * \param fMinPeakMag minimum magnitude to accept a peak + * \return 1 if found, 0 if not + */ +static int FindNextPeak(sfloat *pFMagSpectrum, int iHighestBin, + int *pICurrentLoc, sfloat *pFPeakMag, + sfloat *pFPeakLoc, sfloat fMinPeakMag) +{ + int iPeakBin = 0; /* location of the local peak */ + sfloat fPeakMag = 0; /* value of local peak */ + + /* keep trying to find a good peak while inside the freq range */ + while((iPeakBin = FindNextMax(pFMagSpectrum, iHighestBin, + pICurrentLoc, &fPeakMag, fMinPeakMag)) + <= iHighestBin) + { + /* get the neighboring samples */ + sfloat fDiffFromMax = 0; + sfloat fLeftBinVal = pFMagSpectrum[iPeakBin - 1]; + sfloat fRightBinVal = pFMagSpectrum[iPeakBin + 1]; + if(fLeftBinVal <= 0 || fRightBinVal <= 0) //ahah! there you are! + continue; + /* interpolate the spectral samples to obtain + a more accurate magnitude and freq */ + *pFPeakMag = PeakInterpolation(fPeakMag, fLeftBinVal, + fRightBinVal, &fDiffFromMax); + *pFPeakLoc = iPeakBin + fDiffFromMax; + return 1; + } + /* if it does not find a peak return 0 */ + return 0; +} + +/*! \brief get the corresponding phase value for a given peak + * + * performs linear interpolation for a more accurate phase + + * \param pPhaseSpectrum phase spectrum + * \param fPeakLoc location of peak + * \return the phase value + */ +static sfloat GetPhaseVal(sfloat *pPhaseSpectrum, sfloat fPeakLoc) +{ + int bin = (int)fPeakLoc; + sfloat fFraction = fPeakLoc - bin, + fLeftPha = pPhaseSpectrum[bin], + fRightPha = pPhaseSpectrum[bin+1]; + + /* check for phase wrapping */ + if(fLeftPha - fRightPha > 1.5 * PI) + fRightPha += TWO_PI; + else if(fRightPha - fLeftPha > 1.5 * PI) + fLeftPha += TWO_PI; + + /* return interpolated phase */ + return fLeftPha + fFraction * (fRightPha - fLeftPha); +} + +/*! \brief find the prominent spectral peaks + * + * uses a dB spectrum + * + * \param sizeSpec size of magnitude spectrum + * \param pMag pointer to power spectrum + * \param pPhase pointer to phase spectrum + * \param pSpectralPeaks pointer to array of peaks + * \param pAnalParams peak detection parameters + * \return the number of peaks found + */ +int sms_detectPeaks(int sizeSpec, sfloat *pMag, sfloat *pPhase, + SMS_Peak *pSpectralPeaks, SMS_AnalParams *pAnalParams) +{ + int sizeFft = sizeSpec << 1; + sfloat fInvSizeFft = 1.0 / sizeFft; + int iFirstBin = MAX(1, sizeFft * pAnalParams->fLowestFreq / pAnalParams->iSamplingRate); + int iHighestBin = MIN(sizeSpec-1, + sizeFft * pAnalParams->fHighestFreq / pAnalParams->iSamplingRate); + int iPeak = 0; + + /* clear peak structure */ + for(iPeak = 0; iPeak < pAnalParams->maxPeaks; iPeak++) + { + pSpectralPeaks[iPeak].fFreq = 0.0; + pSpectralPeaks[iPeak].fMag = 0.0; + pSpectralPeaks[iPeak].fPhase = 0.0; + } + + /* set starting search values */ + int iCurrentLoc = iFirstBin; + sfloat fPeakMag = 0.0; /* magnitude of peak */ + sfloat fPeakLoc = 0.0; /* location of peak */ + + /* find peaks */ + iPeak = 0; + while((iPeak < pAnalParams->maxPeaks) && + (FindNextPeak(pMag, iHighestBin, &iCurrentLoc, &fPeakMag, + &fPeakLoc, pAnalParams->fMinPeakMag) == 1)) + { + /* store peak values */ + pSpectralPeaks[iPeak].fFreq = pAnalParams->iSamplingRate * fPeakLoc * fInvSizeFft; + pSpectralPeaks[iPeak].fMag = fPeakMag; + pSpectralPeaks[iPeak].fPhase = GetPhaseVal(pPhase, fPeakLoc); + iPeak++; + } + + /* return the number of peaks found */ + return iPeak; +} diff --git a/src/sms/residual.c b/src/sms/residual.c new file mode 100644 index 0000000..0c720f0 --- /dev/null +++ b/src/sms/residual.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file residual.c + * \brief main sms_residual() function + */ + +#include "sms.h" + +/*! \brief get the residual waveform + * + * \param sizeWindow size of buffers + * \param pSynthesis pointer to deterministic component + * \param pOriginal pointer to original waveform + * \param pResidual pointer to output residual waveform + * \param pWindow pointer to windowing array + * \return residual percentage (0 if residual was not large enough) + */ +int sms_residual(int sizeWindow, sfloat *pSynthesis, sfloat *pOriginal, + SMS_ResidualParams* residualParams) +{ + sfloat fScale = 1.; + sfloat fCurrentResidualMag = 0.; + sfloat fCurrentOriginalMag = 0.; + int i; + + /* get residual */ + for(i = 0; i < sizeWindow; i++) + residualParams->residual[i] = pOriginal[i] - pSynthesis[i]; + + /* get energy of residual */ + for(i = 0; i < sizeWindow; i++) + fCurrentResidualMag += (residualParams->residual[i] * residualParams->residual[i]); + + /* if residual is big enough compute coefficients */ + if(fCurrentResidualMag) + { + /* get energy of original */ + for(i = 0; i < sizeWindow; i++) + fCurrentOriginalMag += (pOriginal[i] * pOriginal[i]); + + residualParams->originalMag = + .5 * (fCurrentOriginalMag/sizeWindow + residualParams->originalMag); + residualParams->residualMag = + .5 * (fCurrentResidualMag/sizeWindow + residualParams->residualMag); + + /* scale residual if need to be */ + if(residualParams->residualMag > residualParams->originalMag) + { + fScale = residualParams->originalMag / residualParams->residualMag; + for(i = 0; i < sizeWindow; i++) + residualParams->residual[i] *= fScale; + } + + return fCurrentResidualMag / fCurrentOriginalMag; + } + return 0; +} + diff --git a/src/sms/sineSynth.c b/src/sms/sineSynth.c new file mode 100644 index 0000000..9882062 --- /dev/null +++ b/src/sms/sineSynth.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file sineSynth.c + * \brief functions for synthesizing evolving sinusoids + */ + +#include "sms.h" + +/*! \brief generate a sinusoid given two peaks, current and last + * + * it interpolation between phase values and magnitudes + * + * \param fFreq current frequency + * \param fMag current magnitude + * \param fPhase current phase + * \param pLastFrame stucture with values from last frame + * \param pFWaveform pointer to output waveform + * \param sizeBuffer size of the synthesis buffer + * \param iTrack current track + */ +static void SinePhaSynth(sfloat fFreq, sfloat fMag, sfloat fPhase, + SMS_Data *pLastFrame, sfloat *pFWaveform, + int sizeBuffer, int iTrack) +{ + sfloat fMagIncr, fInstMag, fInstPhase, fTmp; + int iM, i; + sfloat fAlpha, fBeta, fTmp1, fTmp2; + + /* if no mag in last frame copy freq from current and make phase */ + if(pLastFrame->pFSinAmp[iTrack] <= 0) + { + pLastFrame->pFSinFreq[iTrack] = fFreq; + fTmp = fPhase - (fFreq * sizeBuffer); + pLastFrame->pFSinPha[iTrack] = fTmp - floor(fTmp / TWO_PI) * TWO_PI; + } + /* and the other way */ + else if(fMag <= 0) + { + fFreq = pLastFrame->pFSinFreq[iTrack]; + fTmp = pLastFrame->pFSinPha[iTrack] + + (pLastFrame->pFSinFreq[iTrack] * sizeBuffer); + fPhase = fTmp - floor(fTmp / TWO_PI) * TWO_PI; + } + + /* caculate the instantaneous amplitude */ + fMagIncr = (fMag - pLastFrame->pFSinAmp[iTrack]) / sizeBuffer; + fInstMag = pLastFrame->pFSinAmp[iTrack]; + + /* create instantaneous phase from freq. and phase values */ + fTmp1 = fFreq - pLastFrame->pFSinFreq[iTrack]; + fTmp2 = ((pLastFrame->pFSinPha[iTrack] + + pLastFrame->pFSinFreq[iTrack] * sizeBuffer - fPhase) + + fTmp1 * sizeBuffer / 2.0) / TWO_PI; + iM = (int)(fTmp2 + .5); + fTmp2 = fPhase - pLastFrame->pFSinPha[iTrack] - + pLastFrame->pFSinFreq[iTrack] * sizeBuffer + TWO_PI * iM; + fAlpha = (3.0 / (sfloat)(sizeBuffer * sizeBuffer)) * + fTmp2 - fTmp1 / sizeBuffer; + fBeta = (-2.0 / ((sfloat) (sizeBuffer * sizeBuffer * sizeBuffer))) * + fTmp2 + fTmp1 / ((sfloat) (sizeBuffer * sizeBuffer)); + + for(i=0; i<sizeBuffer; i++) + { + fInstMag += fMagIncr; + fInstPhase = pLastFrame->pFSinPha[iTrack] + + pLastFrame->pFSinFreq[iTrack] * i + + fAlpha * i * i + fBeta * i * i * i; + + /*pFWaveform[i] += sms_dBToMag(fInstMag) * sms_sine(fInstPhase + PI_2);*/ + pFWaveform[i] += sms_dBToMag(fInstMag) * sinf(fInstPhase + PI_2); + } + + /* save current values into buffer */ + pLastFrame->pFSinFreq[iTrack] = fFreq; + pLastFrame->pFSinAmp[iTrack] = fMag; + pLastFrame->pFSinPha[iTrack] = fPhase; +} + +/*! \brief generate a sinusoid given two frames, current and last + * + * \param fFreq current frequency + * \param fMag current magnitude + * \param pLastFrame stucture with values from last frame + * \param pFBuffer pointer to output waveform + * \param sizeBuffer size of the synthesis buffer + * \param iTrack current track + */ +static void SineSynth(sfloat fFreq, sfloat fMag, SMS_Data *pLastFrame, + sfloat *pFBuffer, int sizeBuffer, int iTrack) +{ + sfloat fMagIncr, fInstMag, fFreqIncr, fInstPhase, fInstFreq; + int i; + + /* if no mag in last frame copy freq from current */ + if(pLastFrame->pFSinAmp[iTrack] <= 0) + { + pLastFrame->pFSinFreq[iTrack] = fFreq; + pLastFrame->pFSinPha[iTrack] = TWO_PI * sms_random(); + } + /* and the other way */ + else if(fMag <= 0) + fFreq = pLastFrame->pFSinFreq[iTrack]; + + /* calculate the instantaneous amplitude */ + fMagIncr = (fMag - pLastFrame->pFSinAmp[iTrack]) / sizeBuffer; + fInstMag = pLastFrame->pFSinAmp[iTrack]; + /* calculate instantaneous frequency */ + fFreqIncr = (fFreq - pLastFrame->pFSinFreq[iTrack]) / sizeBuffer; + fInstFreq = pLastFrame->pFSinFreq[iTrack]; + fInstPhase = pLastFrame->pFSinPha[iTrack]; + + /* generate all the samples */ + for(i = 0; i < sizeBuffer; i++) + { + fInstMag += fMagIncr; + fInstFreq += fFreqIncr; + fInstPhase += fInstFreq; + pFBuffer[i] += sms_dBToMag(fInstMag) * sms_sine(fInstPhase); + } + + /* save current values into last values */ + pLastFrame->pFSinFreq[iTrack] = fFreq; + pLastFrame->pFSinAmp[iTrack] = fMag; + pLastFrame->pFSinPha[iTrack] = fInstPhase - floor(fInstPhase / TWO_PI) * TWO_PI; +} + +/*! \brief generate all the sinusoids for a given frame + * + * \param pSmsData SMS data for current frame + * \param pFBuffer pointer to output waveform + * \param sizeBuffer size of the synthesis buffer + * \param pLastFrame SMS data from last frame + * \param iSamplingRate sampling rate to synthesize for + */ +void sms_sineSynthFrame(SMS_Data *pSmsData, sfloat *pFBuffer, + int sizeBuffer, SMS_Data *pLastFrame, + int iSamplingRate) +{ + sfloat fMag, fFreq; + int i; + int nTracks = pSmsData->nTracks; + int iHalfSamplingRate = iSamplingRate >> 1; + + /* go through all the tracks */ + for(i = 0; i < nTracks; i++) + { + fMag = pSmsData->pFSinAmp[i]; + fFreq = pSmsData->pFSinFreq[i]; + + /* make that sure transposed frequencies don't alias */ + if(fFreq > iHalfSamplingRate || fFreq < 0) + fMag = 0; + + /* generate sines if there are magnitude values */ + if((fMag > 0) || (pLastFrame->pFSinAmp[i] > 0)) + { + /* frequency from Hz to radians */ + fFreq = (fFreq == 0) ? 0 : TWO_PI * fFreq / iSamplingRate; + + /* \todo make seperate function for SineSynth /wo phase */ + if(pSmsData->pFSinPha == NULL) + { + SineSynth(fFreq, fMag, pLastFrame, pFBuffer, sizeBuffer, i); + } + else + { + SinePhaSynth(fFreq, fMag, pSmsData->pFSinPha[i], pLastFrame, + pFBuffer, sizeBuffer, i); + } + } + } +} diff --git a/src/sms/sms.c b/src/sms/sms.c new file mode 100644 index 0000000..8501d4d --- /dev/null +++ b/src/sms/sms.c @@ -0,0 +1,1132 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file sms.c + * \brief initialization, free, and debug functions + */ + +#include "sms.h" +#include "SFMT.h" /*!< mersenne twister random number genorator */ + +char *pChDebugFile = "debug.txt"; /*!< debug text file */ +FILE *pDebug; /*!< pointer to debug file */ + +static char error_message[256]; +static int error_status = 0; +static sfloat mag_thresh = .00001; /*!< magnitude threshold for db conversion (-100db)*/ +static sfloat inv_mag_thresh = 100000.; /*!< inv(.00001) */ +static int initIsDone = 0; /* \todo is this variable necessary? */ + +#define SIZE_TABLES 4096 +#define HALF_MAX 1073741823.5 /*!< half the max of a 32-bit word */ +#define INV_HALF_MAX (1.0 / HALF_MAX) +#define TWENTY_OVER_LOG10 (20. / LOG10) + +/*! \brief initialize global data + * + * Currently, just generating the sine and sinc tables. + * This is necessary before both analysis and synthesis. + * + * If using the Mersenne Twister algorithm for random number + * generation, initialize (seed) it. + * + * \return error code \see SMS_MALLOC or SMS_OK in SMS_ERRORS + */ +int sms_init(void) +{ + if (!initIsDone) + { + initIsDone = 1; + if(sms_prepSine(SIZE_TABLES)) + { + sms_error("cannot allocate memory for sine table"); + return -1; + } + if(sms_prepSinc(SIZE_TABLES)) + { + sms_error("cannot allocate memory for sinc table"); + return -1; + } + +#ifdef MERSENNE_TWISTER + init_gen_rand(1234); +#endif + } + + return 0; +} + +/*! \brief free global data + * + * deallocates memory allocated to global arrays (windows and tables) + */ +void sms_free() +{ + initIsDone = 0; + sms_clearSine(); + sms_clearSinc(); +} + +/*! \brief give default values to an SMS_AnalParams struct + * + * This will initialize an SMS_AnalParams with values that work + * for common analyses. It is useful to start with and then + * adjust the parameters manually to fit a particular sound + * + * Certain things are hard coded in here that will have to + * be updated later (i.e. samplerate), so it is best to call this + * function first, then fill whatever parameters need to be + * adjusted. + * + * \param pAnalParams pointer to analysis data structure + */ +void sms_initAnalParams(SMS_AnalParams *pAnalParams) +{ + int i; + pAnalParams->iDebugMode = 0; + pAnalParams->iFormat = SMS_FORMAT_H; + pAnalParams->iSoundType = SMS_SOUND_TYPE_MELODY; + pAnalParams->iStochasticType =SMS_STOC_APPROX; + pAnalParams->iFrameRate = 300; + pAnalParams->nStochasticCoeff = 128; + pAnalParams->fLowestFundamental = 50; + pAnalParams->fHighestFundamental = 1000; + pAnalParams->fDefaultFundamental = 100; + pAnalParams->fPeakContToGuide = .4; + pAnalParams->fFundContToGuide = .5; + pAnalParams->fFreqDeviation = .45; + pAnalParams->iSamplingRate = 44100; /* should be set to the real samplingrate with sms_initAnalysis */ + pAnalParams->iDefaultSizeWindow = 1001; + pAnalParams->windowSize = 0; + pAnalParams->sizeHop = 110; + pAnalParams->fSizeWindow = 3.5; + pAnalParams->nTracks = 60; + pAnalParams->maxPeaks = 60; + pAnalParams->nGuides = 100; + pAnalParams->iCleanTracks = 1; + pAnalParams->fMinRefHarmMag = 30; + pAnalParams->fRefHarmMagDiffFromMax = 30; + pAnalParams->iRefHarmonic = 1; + pAnalParams->iMinTrackLength = 40; /*!< depends on iFrameRate normally */ + pAnalParams->iMaxSleepingTime = 40; /*!< depends on iFrameRate normally */ + pAnalParams->fLowestFreq = 50.0; + pAnalParams->fHighestFreq = 12000.; + pAnalParams->fMinPeakMag = 0.; + pAnalParams->iAnalysisDirection = SMS_DIR_FWD; + pAnalParams->iWindowType = SMS_WIN_BH_70; + pAnalParams->iSizeSound = 0; /*!< no sound yet */ + pAnalParams->nFrames = 0; /*!< no frames yet */ + pAnalParams->minGoodFrames = 3; + pAnalParams->maxDeviation = 0.01; + pAnalParams->analDelay = 100; + pAnalParams->iMaxDelayFrames = MAX(pAnalParams->iMinTrackLength, pAnalParams->iMaxSleepingTime) + 2 + + (pAnalParams->minGoodFrames + pAnalParams->analDelay); + pAnalParams->fResidualAccumPerc = 0.; + pAnalParams->preEmphasis = 1; /*!< perform pre-emphasis by default */ + pAnalParams->preEmphasisLastValue = 0.; + /* spectral envelope params */ + pAnalParams->specEnvParams.iType = SMS_ENV_NONE; /* turn off enveloping */ + pAnalParams->specEnvParams.iOrder = 25; /* ... but set default params anyway */ + pAnalParams->specEnvParams.fLambda = 0.00001; + pAnalParams->specEnvParams.iMaxFreq = 0; + pAnalParams->specEnvParams.nCoeff = 0; + pAnalParams->specEnvParams.iAnchor = 0; /* not yet implemented */ + pAnalParams->pFrames = NULL; + /* fft */ + for(i = 0; i < SMS_MAX_SPEC; i++) + { + pAnalParams->magSpectrum[i] = 0.0; + pAnalParams->phaseSpectrum[i] = 0.0; + pAnalParams->spectrumWindow[i] = 0.0; + pAnalParams->fftBuffer[i] = 0.0; + pAnalParams->fftBuffer[i+SMS_MAX_SPEC] = 0.0; + } + /* analysis frames */ + pAnalParams->pFrames = NULL; + pAnalParams->ppFrames = NULL; + /* residual */ + sms_initResidualParams(&pAnalParams->residualParams); + /* peak continuation */ + pAnalParams->guideStates = NULL; + pAnalParams->guides = NULL; + /* audio input frame */ + for(i = 0; i < SMS_MAX_FRAME_SIZE; i++) + pAnalParams->inputBuffer[i] = 0.0; + /* stochastic analysis */ + pAnalParams->stocMagSpectrum = NULL; + pAnalParams->approxEnvelope = NULL; + pAnalParams->ppFrames = NULL; +} + +/*! \brief initialize analysis data structure's arrays + * + * based on the SMS_AnalParams current settings, this function will + * initialize the sound, synth, and fft arrays. It is necessary before analysis. + * there can be multple SMS_AnalParams at the same time + * + * \param pAnalParams pointer to analysis paramaters + * \param pSoundHeader pointer to sound header + * \return 0 on success, -1 on error + */ +int sms_initAnalysis(SMS_AnalParams *pAnalParams) +{ + int i; + SMS_SndBuffer *pSynthBuf = &pAnalParams->synthBuffer; + SMS_SndBuffer *pSoundBuf = &pAnalParams->soundBuffer; + + /* define the hopsize for each record */ + pAnalParams->sizeHop = (int)(pAnalParams->iSamplingRate / + (sfloat) pAnalParams->iFrameRate); + + /* set the default size window to an odd length */ + pAnalParams->iDefaultSizeWindow = + (int)((pAnalParams->iSamplingRate / pAnalParams->fDefaultFundamental) * + pAnalParams->fSizeWindow / 2) * 2 + 1; + + int sizeBuffer = (pAnalParams->iMaxDelayFrames * pAnalParams->sizeHop) + SMS_MAX_WINDOW; + + /* if storing residual phases, restrict number of stochastic coefficients to the size of the spectrum (sizeHop = 1/2 sizeFft)*/ + if(pAnalParams->iStochasticType == SMS_STOC_IFFT) + pAnalParams->nStochasticCoeff = sms_power2(pAnalParams->sizeHop); + + /* do the same if spectral envelope is to be stored in frequency bins */ + if(pAnalParams->specEnvParams.iType == SMS_ENV_FBINS) + pAnalParams->specEnvParams.nCoeff = sms_power2(pAnalParams->specEnvParams.iOrder * 2); + else if(pAnalParams->specEnvParams.iType == SMS_ENV_CEP) + pAnalParams->specEnvParams.nCoeff = pAnalParams->specEnvParams.iOrder+1; + /* if specEnvParams.iMaxFreq is still 0, set it to the same as fHighestFreq (normally what you want)*/ + if(pAnalParams->specEnvParams.iMaxFreq == 0) + pAnalParams->specEnvParams.iMaxFreq = pAnalParams->fHighestFreq; + + /*\todo this probably doesn't need env coefficients - they aren't getting used */ + if(sms_allocFrame(&pAnalParams->prevFrame, pAnalParams->nGuides, + pAnalParams->nStochasticCoeff, 1, pAnalParams->iStochasticType, 0) + == -1) + { + sms_error("Could not allocate memory for prevFrame"); + return -1; + } + + pAnalParams->sizeNextRead = (pAnalParams->iDefaultSizeWindow + 1) * 0.5; + + /* sound buffer */ + if((pSoundBuf->pFBuffer = (sfloat *) calloc(sizeBuffer, sizeof(sfloat))) == NULL) + { + sms_error("Could not allocate memory for sound buffer"); + return -1; + } + pSoundBuf->iMarker = -sizeBuffer; + pSoundBuf->iFirstGood = sizeBuffer; + pSoundBuf->sizeBuffer = sizeBuffer; + + /* check default fundamental */ + if (pAnalParams->fDefaultFundamental < pAnalParams->fLowestFundamental) + { + pAnalParams->fDefaultFundamental = pAnalParams->fLowestFundamental; + } + if (pAnalParams->fDefaultFundamental > pAnalParams->fHighestFundamental) + { + pAnalParams->fDefaultFundamental = pAnalParams->fHighestFundamental; + } + + /* deterministic synthesis buffer */ + pSynthBuf->sizeBuffer = pAnalParams->sizeHop << 1; + pSynthBuf->pFBuffer = calloc(pSynthBuf->sizeBuffer, sizeof(sfloat)); + if(pSynthBuf->pFBuffer == NULL) + { + sms_error("could not allocate memory"); + return -1; + } + pSynthBuf->iMarker = pSynthBuf->sizeBuffer; + /* buffer of analysis frames */ + pAnalParams->pFrames = (SMS_AnalFrame *)malloc(pAnalParams->iMaxDelayFrames * sizeof(SMS_AnalFrame)); + if(pAnalParams->pFrames == NULL) + { + sms_error("could not allocate memory for delay frames"); + return -1; + } + pAnalParams->ppFrames = (SMS_AnalFrame **)malloc(pAnalParams->iMaxDelayFrames * sizeof(SMS_AnalFrame *)); + if(pAnalParams->ppFrames == NULL) + { + sms_error("could not allocate memory for pointers to delay frames"); + return -1; + } + + /* initialize the frame pointers and allocate memory */ + for(i = 0; i < pAnalParams->iMaxDelayFrames; i++) + { + pAnalParams->pFrames[i].iStatus = SMS_FRAME_EMPTY; + pAnalParams->pFrames[i].iFrameSample = 0; + pAnalParams->pFrames[i].iFrameSize = 0; + pAnalParams->pFrames[i].iFrameNum = 0; + pAnalParams->pFrames[i].pSpectralPeaks = + (SMS_Peak *)malloc(pAnalParams->maxPeaks * sizeof(SMS_Peak)); + if((pAnalParams->pFrames[i]).pSpectralPeaks == NULL) + { + sms_error("could not allocate memory for spectral peaks"); + return -1; + } + (pAnalParams->pFrames[i].deterministic).nTracks = pAnalParams->nGuides; + + (pAnalParams->pFrames[i].deterministic).pFSinFreq = + (sfloat *)calloc(pAnalParams->nGuides, sizeof(sfloat)); + if((pAnalParams->pFrames[i].deterministic).pFSinFreq == NULL) + { + sms_error("could not allocate memory"); + return -1; + } + + (pAnalParams->pFrames[i].deterministic).pFSinAmp = + (sfloat *)calloc(pAnalParams->nGuides, sizeof(sfloat)); + if((pAnalParams->pFrames[i].deterministic).pFSinAmp == NULL) + { + sms_error("could not allocate memory"); + return -1; + } + + (pAnalParams->pFrames[i].deterministic).pFSinPha = + (sfloat *)calloc(pAnalParams->nGuides, sizeof(sfloat)); + if((pAnalParams->pFrames[i].deterministic).pFSinPha == NULL) + { + sms_error("could not allocate memory"); + return -1; + } + pAnalParams->ppFrames[i] = &pAnalParams->pFrames[i]; + + /* set initial values */ + if(sms_clearAnalysisFrame(i, pAnalParams) < 0) + { + sms_error("could not set initial values for analysis frames"); + return -1; + } + } + + /* memory for residual */ + pAnalParams->residualParams.hopSize = pAnalParams->sizeHop; + sms_initResidual(&pAnalParams->residualParams); + + /* memory for guide states */ + pAnalParams->guideStates = (int *)calloc(pAnalParams->nGuides, sizeof(int)); + if(pAnalParams->guideStates == NULL) + { + sms_error("Could not allocate memory for guide states"); + return -1; + } + + /* memory for guides */ + pAnalParams->guides = (SMS_Guide *)malloc(pAnalParams->nGuides * sizeof(SMS_Guide)); + if(pAnalParams->guides == NULL) + { + sms_error("Could not allocate memory for guides"); + return -1; + } + + /* initial guide values */ + for (i = 0; i < pAnalParams->nGuides; i++) + { + if(pAnalParams->iFormat == SMS_FORMAT_H || pAnalParams->iFormat == SMS_FORMAT_HP) + { + pAnalParams->guides[i].fFreq = pAnalParams->fDefaultFundamental * (i + 1); + } + else + { + pAnalParams->guides[i].fFreq = 0.0; + } + pAnalParams->guides[i].fMag = 0.0; + pAnalParams->guides[i].iPeakChosen = -1; + pAnalParams->guides[i].iStatus = 0; + } + + /* stochastic analysis */ + pAnalParams->sizeStocMagSpectrum = sms_power2(pAnalParams->residualParams.residualSize) >> 1; + pAnalParams->stocMagSpectrum = (sfloat *)calloc(pAnalParams->sizeStocMagSpectrum, sizeof(sfloat)); + if(pAnalParams->stocMagSpectrum == NULL) + { + sms_error("Could not allocate memory for stochastic magnitude spectrum"); + return -1; + } + pAnalParams->approxEnvelope = (sfloat *)calloc(pAnalParams->nStochasticCoeff, sizeof(sfloat)); + if(pAnalParams->approxEnvelope == NULL) + { + sms_error("Could not allocate memory for spectral approximation envelope"); + return -1; + } + + return 0; +} + +/*! \brief give default values to an SMS_SynthParams struct + * + * This will initialize an SMS_SynthParams with values that work + * for common analyses. It is useful to start with and then + * adjust the parameters manually to fit a particular sound + * + * \param synthParams pointer to synthesis parameters data structure + */ +void sms_initSynthParams(SMS_SynthParams *synthParams) +{ + synthParams->iSamplingRate = 44100; + synthParams->iOriginalSRate = 44100; + synthParams->iSynthesisType = SMS_STYPE_ALL; + synthParams->iDetSynthType = SMS_DET_IFFT; + synthParams->sizeHop = SMS_MIN_SIZE_FRAME; + synthParams->origSizeHop = SMS_MIN_SIZE_FRAME; + synthParams->nTracks = 60; + synthParams->iStochasticType = SMS_STOC_APPROX; + synthParams->nStochasticCoeff = 128; + synthParams->pFDetWindow = NULL; + synthParams->pFStocWindow = NULL; + synthParams->pSynthBuff = NULL; + synthParams->pMagBuff = NULL; + synthParams->pPhaseBuff = NULL; + synthParams->pSpectra = NULL; + synthParams->approxEnvelope = NULL; + synthParams->deEmphasis = 1; /*!< perform de-emphasis by default */ + synthParams->deEmphasisLastValue = 0; +} + +/*! \brief initialize synthesis data structure's arrays + * + * Initialize the synthesis and fft arrays. It is necessary before synthesis. + * there can be multple SMS_SynthParams at the same time + * This function also sets some initial values that will create a sane synthesis + * environment. + * + * This function requires an SMS_Header because it may be called to synthesize + * a stored .sms file, which contains a header with necessary information. + * + * \param pSmsHeader pointer to SMS_Header + * \param pSynthParams pointer to synthesis paramaters + * \return 0 on success, -1 on error + */ +int sms_initSynth(SMS_SynthParams *pSynthParams) +{ + int sizeHop, sizeFft; + + /* make sure sizeHop is something to the power of 2 */ + sizeHop = sms_power2(pSynthParams->sizeHop); + if(sizeHop != pSynthParams->sizeHop) + { + printf("Warning: Synthesis hop size (%d) was not a power of two.\n", + pSynthParams->sizeHop); + printf(" Changed to %d.\n", sizeHop); + pSynthParams->sizeHop = sizeHop; + } + sizeFft = sizeHop * 2; + + /* TODO: check memory allocation */ + pSynthParams->pFStocWindow = (sfloat *)calloc(sizeFft, sizeof(sfloat)); + sms_getWindow(sizeFft, pSynthParams->pFStocWindow, SMS_WIN_HANNING); + pSynthParams->pFDetWindow = (sfloat *)calloc(sizeFft, sizeof(sfloat)); + sms_getWindow(sizeFft, pSynthParams->pFDetWindow, SMS_WIN_IFFT); + + /* allocate memory for analysis data - size of original hopsize + * previous frame to interpolate from */ + /* \todo why is stoch coeff + 1? */ + sms_allocFrame(&pSynthParams->prevFrame, pSynthParams->nTracks, + pSynthParams->nStochasticCoeff + 1, 1, + pSynthParams->iStochasticType, 0); + + pSynthParams->pSynthBuff = (sfloat *)calloc(sizeFft, sizeof(sfloat)); + pSynthParams->pMagBuff = (sfloat *)calloc(sizeHop, sizeof(sfloat)); + pSynthParams->pPhaseBuff = (sfloat *)calloc(sizeHop, sizeof(sfloat)); + pSynthParams->pSpectra = (sfloat *)calloc(sizeFft, sizeof(sfloat)); + + /* approximation envelope */ + pSynthParams->approxEnvelope = (sfloat *)calloc(pSynthParams->nStochasticCoeff, sizeof(sfloat)); + if(pSynthParams->approxEnvelope == NULL) + { + sms_error("Could not allocate memory for spectral approximation envelope"); + return -1; + } + + return SMS_OK; +} + +/*! \brief give default values to an SMS_ResidualParams struct + * + * \param residualParams pointer to residual data structure + */ +void sms_initResidualParams(SMS_ResidualParams *residualParams) +{ + residualParams->samplingRate = 44100; + residualParams->hopSize = 256; + residualParams->residualSize = 0; + residualParams->residual = NULL; + residualParams->fftWindow = NULL; + residualParams->ifftWindow = NULL; + residualParams->windowScale = 0.0; + residualParams->residualMag = 0.0; + residualParams->originalMag = 0.0; + residualParams->nCoeffs = 128; + residualParams->stocCoeffs = NULL; + residualParams->sizeStocMagSpectrum = 0; + residualParams->stocMagSpectrum = NULL; + residualParams->stocPhaseSpectrum = NULL; + residualParams->approx = NULL; + residualParams->approxEnvelope = NULL; + int i; + for(i = 0; i < SMS_MAX_SPEC; i++) + { + residualParams->fftBuffer[i] = 0.0; + residualParams->fftBuffer[i+SMS_MAX_SPEC] = 0.0; + } +} + +/*! \brief initialize residual data structure + * + * \param residualParams pointer to synthesis paramaters + * \return 0 on success, -1 on error + */ +int sms_initResidual(SMS_ResidualParams *residualParams) +{ + if(residualParams->hopSize <= 0) + { + sms_error("Residual hop size must be a positive integer"); + return -1; + } + + /* residual signal */ + residualParams->residualSize = residualParams->hopSize * 2; + residualParams->residual = (sfloat *)calloc(residualParams->residualSize, sizeof(sfloat)); + if(residualParams->residual == NULL) + { + sms_error("Could not allocate memory for residual"); + return -1; + } + + /* residual fft/ifft windows */ + residualParams->fftWindow = (sfloat *)calloc(residualParams->residualSize, sizeof(sfloat)); + if(residualParams->fftWindow == NULL) + { + sms_error("Could not allocate memory for residual FFT window"); + return -1; + } + sms_getWindow(residualParams->residualSize, residualParams->fftWindow, SMS_WIN_BH_70); + sms_scaleWindow(residualParams->residualSize, residualParams->fftWindow); + + residualParams->ifftWindow = (sfloat *)calloc(residualParams->residualSize, sizeof(sfloat)); + if(residualParams->ifftWindow == NULL) + { + sms_error("Could not allocate memory for residual IFFT window"); + return -1; + } + sms_getWindow(residualParams->residualSize, residualParams->ifftWindow, SMS_WIN_HANNING); + /* compute IFFT window scaling: + * windows per hop = hop size / window size = 0.5 + * overlap = 50% => 1 window total in each hop/frame + * => windowScale = window size / sum(window samples) = 1.85 + * for a 1024 sized hamming window + */ + int i; + sfloat sum = 0.0; + for(i = 0; i < residualParams->residualSize; i++) + sum += residualParams->ifftWindow[i]; + residualParams->windowScale = (sfloat)residualParams->residualSize / sum; + + /* stochastic analysis */ + residualParams->stocCoeffs = (sfloat *)calloc(residualParams->nCoeffs, sizeof(sfloat)); + if(residualParams->stocCoeffs == NULL) + { + sms_error("Could not allocate memory for stochastic coefficients"); + return -1; + } + + residualParams->sizeStocMagSpectrum = sms_power2(residualParams->residualSize) >> 1; + residualParams->stocMagSpectrum = (sfloat *)calloc(residualParams->sizeStocMagSpectrum, sizeof(sfloat)); + if(residualParams->stocMagSpectrum == NULL) + { + sms_error("Could not allocate memory for stochastic magnitude spectrum"); + return -1; + } + residualParams->stocPhaseSpectrum = (sfloat *)calloc(residualParams->sizeStocMagSpectrum, sizeof(sfloat)); + if(residualParams->stocPhaseSpectrum == NULL) + { + sms_error("Could not allocate memory for stochastic magnitude spectrum"); + return -1; + } + + residualParams->approx = (sfloat *)calloc(residualParams->residualSize, sizeof(sfloat)); + if(residualParams->approx == NULL) + { + sms_error("Could not allocate memory for spectral approximation"); + return -1; + } + residualParams->approxEnvelope = (sfloat *)calloc(residualParams->nCoeffs, sizeof(sfloat)); + if(residualParams->approxEnvelope == NULL) + { + sms_error("Could not allocate memory for spectral approximation envelope"); + return -1; + } + + return 0; +} + +/*! \brief free residual data + * + * frees all the memory allocated to an SMS_ResidualParams by + * sms_initResidual + * + * \param residualParams pointer to residual data structure + */ +void sms_freeResidual(SMS_ResidualParams *residualParams) +{ + if(residualParams->residual) + free(residualParams->residual); + if(residualParams->fftWindow) + free(residualParams->fftWindow); + if(residualParams->ifftWindow) + free(residualParams->ifftWindow); + if(residualParams->stocCoeffs) + free(residualParams->stocCoeffs); + if(residualParams->stocMagSpectrum) + free(residualParams->stocMagSpectrum); + if(residualParams->stocPhaseSpectrum) + free(residualParams->stocPhaseSpectrum); + if(residualParams->approx) + free(residualParams->approx); + if(residualParams->approxEnvelope) + free(residualParams->approxEnvelope); + + residualParams->residual = NULL; + residualParams->fftWindow = NULL; + residualParams->ifftWindow = NULL; + residualParams->stocCoeffs = NULL; + residualParams->stocMagSpectrum = NULL; + residualParams->stocPhaseSpectrum = NULL; + residualParams->approx = NULL; + residualParams->approxEnvelope = NULL; +} + +/*! \brief free analysis data + * + * frees all the memory allocated to an SMS_AnalParams by + * sms_initAnalysis + * + * \param pAnalParams pointer to analysis data structure + */ +void sms_freeAnalysis(SMS_AnalParams *pAnalParams) +{ + if(pAnalParams->pFrames) + { + int i; + for(i = 0; i < pAnalParams->iMaxDelayFrames; i++) + { + if((pAnalParams->pFrames[i]).pSpectralPeaks) + free((pAnalParams->pFrames[i]).pSpectralPeaks); + if((pAnalParams->pFrames[i].deterministic).pFSinFreq) + free((pAnalParams->pFrames[i].deterministic).pFSinFreq); + if((pAnalParams->pFrames[i].deterministic).pFSinAmp) + free((pAnalParams->pFrames[i].deterministic).pFSinAmp); + if((pAnalParams->pFrames[i].deterministic).pFSinPha) + free((pAnalParams->pFrames[i].deterministic).pFSinPha); + } + free(pAnalParams->pFrames); + } + + sms_freeFrame(&pAnalParams->prevFrame); + sms_freeResidual(&pAnalParams->residualParams); + + if(pAnalParams->soundBuffer.pFBuffer) + free(pAnalParams->soundBuffer.pFBuffer); + if((pAnalParams->synthBuffer).pFBuffer) + free((pAnalParams->synthBuffer).pFBuffer); + if(pAnalParams->ppFrames) + free(pAnalParams->ppFrames); + if(pAnalParams->guideStates) + free(pAnalParams->guideStates); + if(pAnalParams->guides) + free(pAnalParams->guides); + if(pAnalParams->stocMagSpectrum) + free(pAnalParams->stocMagSpectrum); + if(pAnalParams->approxEnvelope) + free(pAnalParams->approxEnvelope); + + pAnalParams->pFrames = NULL; + pAnalParams->ppFrames = NULL; + pAnalParams->soundBuffer.pFBuffer = NULL; + pAnalParams->synthBuffer.pFBuffer = NULL; + pAnalParams->guideStates = NULL; + pAnalParams->guides = NULL; + pAnalParams->stocMagSpectrum = NULL; + pAnalParams->approxEnvelope = NULL; +} + +/*! \brief free analysis data + * + * frees all the memory allocated to an SMS_SynthParams by + * sms_initSynthesis + * + * \todo is there a way to make sure the plan has been made + * already? as it is, it crashes if this is called without one + * \param pSynthParams pointer to synthesis data structure + */ +void sms_freeSynth(SMS_SynthParams *pSynthParams) +{ + if(pSynthParams->pFStocWindow) + free(pSynthParams->pFStocWindow); + if(pSynthParams->pFDetWindow) + free(pSynthParams->pFDetWindow); + if(pSynthParams->pSynthBuff) + free(pSynthParams->pSynthBuff); + if(pSynthParams->pSpectra) + free(pSynthParams->pSpectra); + if(pSynthParams->pMagBuff) + free(pSynthParams->pMagBuff); + if(pSynthParams->pPhaseBuff) + free(pSynthParams->pPhaseBuff); + if(pSynthParams->approxEnvelope) + free(pSynthParams->approxEnvelope); + + sms_freeFrame(&pSynthParams->prevFrame); +} + +/*! \brief Allocate memory for an array of spectral peaks + * + * Creates memory and sets default values. + * + * \param peaks the spectral peaks + * \param n number of peaks + * \return 0 on success, -1 on error + */ +int sms_initSpectralPeaks(SMS_SpectralPeaks* peaks, int n) +{ + peaks->nPeaks = n; + peaks->nPeaksFound = 0; + + peaks->pSpectralPeaks = (SMS_Peak *)malloc(n * sizeof(SMS_Peak)); + if(peaks->pSpectralPeaks == NULL) + { + sms_error("could not allocate memory for spectral peaks"); + return -1; + } + return 0; +} + +/*! \brief Deallocate memory for an array of spectral peaks + * + * \param peaks the spectral peaks + */ +void sms_freeSpectralPeaks(SMS_SpectralPeaks* peaks) +{ + if(!peaks) + return; + + if(peaks->pSpectralPeaks) + free(peaks->pSpectralPeaks); + peaks->nPeaks = 0; + peaks->nPeaksFound = 0; +} + +/*! \brief set window size for next frame + * + * adjusts the next window size to fit the currently detected fundamental + * frequency, or resets to a default window size if unstable. + * + * \param iCurrentFrame number of current frame + * \param pAnalParams analysis parameters + * \return the size of the next window in samples + */ +int sms_sizeNextWindow(int iCurrentFrame, SMS_AnalParams *pAnalParams) +{ + sfloat fFund = pAnalParams->ppFrames[iCurrentFrame]->fFundamental; + sfloat fPrevFund = pAnalParams->ppFrames[iCurrentFrame-1]->fFundamental; + int sizeWindow; + + /* if the previous fundamental was stable use it to set the window size */ + if(fPrevFund > 0 && fabs(fPrevFund - fFund) / fFund <= .2) + sizeWindow = (int)((pAnalParams->iSamplingRate / fFund) * + pAnalParams->fSizeWindow * .5) * 2 + 1; + /* otherwise use the default size window */ + else + sizeWindow = pAnalParams->iDefaultSizeWindow; + + if(sizeWindow > SMS_MAX_WINDOW) + { + fprintf(stderr, "sms_sizeNextWindow error: sizeWindow (%d) too big, set to %d\n", sizeWindow, + SMS_MAX_WINDOW); + sizeWindow = SMS_MAX_WINDOW; + } + + return sizeWindow; +} + +/*! \brief set default values for analysis frame variables + * \param iCurrentFrame frame number of the current frame + * \param pAnalParams analysis parameters + * \return 0 on success, -1 on error + */ +int sms_clearAnalysisFrame(int iCurrentFrame, SMS_AnalParams *pAnalParams) +{ + int i; + SMS_AnalFrame *currentFrame = pAnalParams->ppFrames[iCurrentFrame]; + + /* clear deterministic data */ + for(i = 0; i < pAnalParams->nGuides; i++) + { + currentFrame->deterministic.pFSinFreq[i] = 0.0; + currentFrame->deterministic.pFSinAmp[i] = 0.0; + currentFrame->deterministic.pFSinPha[i] = 0.0; + } + + /* clear peaks */ + for(i = 0; i < pAnalParams->maxPeaks; i++) + { + currentFrame->pSpectralPeaks[i].fFreq = 0.0; + currentFrame->pSpectralPeaks[i].fMag = 0.0; + currentFrame->pSpectralPeaks[i].fPhase = 0.0; + } + + currentFrame->nPeaks = 0; + currentFrame->fFundamental = 0; + currentFrame->iFrameNum = 0; + currentFrame->iFrameSize = 0; + currentFrame->iFrameSample = 0; + currentFrame->iStatus = SMS_FRAME_EMPTY; + + return 0; +} + +/*! \brief initialize the current frame + * + * initializes arrays to zero and sets the correct sample position. + * Special care is taken at the end the sample source (if there is + * not enough samples for an entire frame. + * + * \param iCurrentFrame frame number of current frame in buffer + * \param pAnalParams analysis parameters + * \param sizeWindow size of analysis window + * \return -1 on error \todo make this return void + */ +int sms_initFrame(int iCurrentFrame, SMS_AnalParams *pAnalParams, int sizeWindow) +{ + /* clear deterministic data */ + memset((sfloat *)pAnalParams->ppFrames[iCurrentFrame]->deterministic.pFSinFreq, 0, + sizeof(sfloat) * pAnalParams->nGuides); + memset((sfloat *)pAnalParams->ppFrames[iCurrentFrame]->deterministic.pFSinAmp, 0, + sizeof(sfloat) * pAnalParams->nGuides); + memset((sfloat *)pAnalParams->ppFrames[iCurrentFrame]->deterministic.pFSinPha, 0, + sizeof(sfloat) * pAnalParams->nGuides); + + /* clear peaks */ + int i; + for(i = 0; i < pAnalParams->maxPeaks; i++) + { + pAnalParams->ppFrames[iCurrentFrame]->pSpectralPeaks[i].fFreq = 0.0; + pAnalParams->ppFrames[iCurrentFrame]->pSpectralPeaks[i].fMag = 0.0; + pAnalParams->ppFrames[iCurrentFrame]->pSpectralPeaks[i].fPhase = 0.0; + } + + pAnalParams->ppFrames[iCurrentFrame]->nPeaks = 0; + pAnalParams->ppFrames[iCurrentFrame]->fFundamental = 0; + + pAnalParams->ppFrames[iCurrentFrame]->iFrameNum = + pAnalParams->ppFrames[iCurrentFrame - 1]->iFrameNum + 1; + pAnalParams->ppFrames[iCurrentFrame]->iFrameSize = sizeWindow; + + /* if first frame set center of data around 0 */ + if(pAnalParams->ppFrames[iCurrentFrame]->iFrameNum == 1) + pAnalParams->ppFrames[iCurrentFrame]->iFrameSample = 0; + /* if not, increment center of data by sizeHop */ + else + pAnalParams->ppFrames[iCurrentFrame]->iFrameSample = + pAnalParams->ppFrames[iCurrentFrame-1]->iFrameSample + pAnalParams->sizeHop; + + /* check for end of sound */ + if((pAnalParams->ppFrames[iCurrentFrame]->iFrameSample + (sizeWindow+1)/2) >= pAnalParams->iSizeSound + && pAnalParams->iSizeSound > 0) + { + pAnalParams->ppFrames[iCurrentFrame]->iFrameNum = -1; + pAnalParams->ppFrames[iCurrentFrame]->iFrameSize = 0; + pAnalParams->ppFrames[iCurrentFrame]->iStatus = SMS_FRAME_END; + } + else + { + /* good status, ready to start computing */ + pAnalParams->ppFrames[iCurrentFrame]->iStatus = SMS_FRAME_READY; + } + return SMS_OK; +} + +/*! \brief get deviation from average fundamental + *\ + * \param pAnalParams pointer to analysis params + * \param iCurrentFrame number of current frame + * \return deviation value or -1 if really off + */ +sfloat sms_fundDeviation(SMS_AnalParams *pAnalParams, int iCurrentFrame) +{ + sfloat fFund, fSum = 0, fAverage, fDeviation = 0; + int i; + + if(pAnalParams->minGoodFrames < 1) + return -1; + + /* get the sum of the past few fundamentals */ + for(i = 0; (i < pAnalParams->minGoodFrames) && (iCurrentFrame-i >= 0); i++) + { + fFund = pAnalParams->ppFrames[iCurrentFrame-i]->fFundamental; + if(fFund <= 0) + return -1; + else + fSum += fFund; + } + + /* find the average */ + fAverage = fSum / pAnalParams->minGoodFrames; + + /* get the deviation from the average */ + for(i = 0; (i < pAnalParams->minGoodFrames) && (iCurrentFrame-i >= 0); i++) + fDeviation += fabs(pAnalParams->ppFrames[iCurrentFrame-i]->fFundamental - fAverage); + + /* return the deviation from the average */ + return fDeviation / (pAnalParams->minGoodFrames * fAverage); +} + + +/*! \brief function to create the debug file + * + * \param pAnalParams pointer to analysis params + * \return error value \see SMS_ERRORS + */ +int sms_createDebugFile(SMS_AnalParams *pAnalParams) +{ + if((pDebug = fopen(pChDebugFile, "w+")) == NULL) + { + fprintf(stderr, "Cannot open debugfile: %s\n", pChDebugFile); + return SMS_WRERR; + } + return SMS_OK; +} + +/*! \brief function to write to the debug file + * + * writes three arrays of equal size to a debug text + * file ("./debug.txt"). There are three arrays for the + * frequency, magnitude, phase sets. + * + * \param pFBuffer1 pointer to array 1 + * \param pFBuffer2 pointer to array 2 + * \param pFBuffer3 pointer to array 3 + * \param sizeBuffer the size of the buffers + */ +void sms_writeDebugData(sfloat *pFBuffer1, sfloat *pFBuffer2, + sfloat *pFBuffer3, int sizeBuffer) +{ + int i; + static int counter = 0; + + for(i = 0; i < sizeBuffer; i++) + fprintf(pDebug, "%d %d %d %d\n", counter++, (int)pFBuffer1[i], + (int)pFBuffer2[i], (int)pFBuffer3[i]); +} + +/*! \brief function to write the residual sound file to disk + * + * writes the "debug.txt" file to disk and closes the file. + */ +void sms_writeDebugFile () +{ + fclose(pDebug); +} + +/*! \brief convert from magnitude to decibel + * + * \param x magnitude (0:1) + * \return decibel (0: -100) + */ +sfloat sms_magToDB(sfloat x) +{ + if(x < mag_thresh) + return 0.0; + else + //return(20. * log10(x * inv_mag_thresh)); + return TWENTY_OVER_LOG10 * log(x * inv_mag_thresh); + /*return(TWENTY_OVER_LOG10 * log(x));*/ +} + +/*! \brief convert from decibel to magnitude + * + * \param x decibel (0-100) + * \return magnitude (0-1) + */ +sfloat sms_dBToMag(sfloat x) +{ + if(x < 0.00001) + return 0.0; + else + return mag_thresh * pow(10., x*0.05); + /*return pow(10.0, x*0.05);*/ +} + +/*! \brief convert an array from magnitude to decibel + * + * Depends on a linear threshold that indicates the bottom end + * of the dB scale (magnutdes at this value will convert to zero). + * \see sms_setMagThresh + * + * \param sizeArray size of array + * \param pArray pointer to array + */ +void sms_arrayMagToDB(int sizeArray, sfloat *pArray) +{ + int i; + for(i = 0; i < sizeArray; i++) + pArray[i] = sms_magToDB(pArray[i]); +} + +/*! \brief convert and array from decibel (0-100) to magnitude (0-1) + * + * depends on the magnitude threshold + * \see sms_setMagThresh + * + * \param sizeArray size of array + * \param pArray pointer to array + */ +void sms_arrayDBToMag(int sizeArray, sfloat *pArray) +{ + int i; + for(i = 0; i < sizeArray; i++) + pArray[i] = sms_dBToMag(pArray[i]); +} +/*! \brief set the linear magnitude threshold + * + * magnitudes below this will go to zero when converted to db. + * it is limited to 0.00001 (-100db) + * + * \param x threshold value + */ +void sms_setMagThresh(sfloat x) +{ + /* limit threshold to -100db */ + if(x < 0.00001) + mag_thresh = 0.00001; + else + mag_thresh = x; + inv_mag_thresh = 1. / mag_thresh; +} + +/*! \brief get a string containing information about the error code + * + * \param pErrorMessage pointer to error message string + */ +void sms_error(char *pErrorMessage) +{ + strncpy(error_message, pErrorMessage, 256); + error_status = -1; +} + +/*! \brief check if an error has been reported + * + * \return -1 if there is an error, 0 if ok + */ +int sms_errorCheck() +{ + return error_status; +} + +/*! \brief get a string containing information about the last error + * + * \return pointer to a char string, or NULL if no error + */ +char* sms_errorString() +{ + if (error_status) + { + error_status = 0; + return error_message; + } + return NULL; +} + +/*! \brief random number genorator + * + * \return random number between -1 and 1 + */ +sfloat sms_random() +{ +#ifdef MERSENNE_TWISTER + return genrand_real1(); +#else + return (sfloat)(random() * 2 * INV_HALF_MAX); +#endif +} + +/*! \brief Root Mean Squared of an array + * + * \return RMS energy + */ +sfloat sms_rms(int sizeArray, sfloat *pArray) +{ + int i; + sfloat mean_squared = 0.; + for(i = 0; i < sizeArray; i++) + mean_squared += pArray[i] * pArray[i]; + + return sqrtf(mean_squared / sizeArray); +} + +/*! \brief make sure a number is a power of 2 + * + * \return a power of two integer >= input value + */ +int sms_power2(int n) +{ + int p = -1; + int N = n; + while(n) + { + n >>= 1; + p++; + } + + if(1<<p == N) /* n was a power of 2 */ + { + return N; + } + else /* make the new value larger than n */ + { + p++; + return 1<<p; + } +} + +/*! \brief compute a value for scaling frequency based on the well-tempered scale + * + * \param x linear frequency value + * \return (1.059...)^x, where 1.059 is the 12th root of 2 precomputed + */ +sfloat sms_scalarTempered(sfloat x) +{ + return powf(1.0594630943592953, x); +} + +/*! \brief scale an array of linear frequencies to the well-tempered scale + * + * \param sizeArray size of the array + * \param pArray pointer to array of frequencies + */ +void sms_arrayScalarTempered(int sizeArray, sfloat *pArray) +{ + int i; + for(i = 0; i < sizeArray; i++) + pArray[i] = sms_scalarTempered(pArray[i]); +} + diff --git a/src/sms/sms.h b/src/sms/sms.h new file mode 100644 index 0000000..0613ab3 --- /dev/null +++ b/src/sms/sms.h @@ -0,0 +1,723 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file sms.h + * \brief header file to be included in all SMS application + */ +#ifndef _SMS_H +#define _SMS_H + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <memory.h> +#include <strings.h> + +#define SMS_VERSION 1.15 /*!< \brief version control number */ + +#define SMS_MAX_NPEAKS 400 /*!< \brief maximum number of peaks */ +#define SMS_MAX_FRAME_SIZE 10000 /* maximum size of input frame in samples */ +#define SMS_MAX_SPEC 8192 /*! \brief maximum size for magnitude spectrum */ + +#define sfloat double + +/*! \struct SMS_Header + * \brief structure for the header of an SMS file + * + * This header contains all the information necessary to read an SMS + * file, prepare memory and synthesizer parameters. + * + * The header also contains variable components for additional information + * that may be stored along with the analysis, such as descriptors or text. + * + * The first four members of the Header are necessary in this order to correctly + * open the .sms files created by this library. + * + * iSampleRate contains the samplerate of the analysis signal because it is + * necessary to know this information to recreate the residual spectrum. + * + * In the first release, the descriptors are not used, but are here because they + * were implemented in previous versions of this code (in the 90's). With time, + * the documentation will be updated to reflect which members of the header + * are useful in manipulations, and what functions to use for these manipulatinos + */ +typedef struct +{ + int iSmsMagic; /*!< identification constant */ + int iHeadBSize; /*!< size in bytes of header */ + int nFrames; /*!< number of data frames */ + int iFrameBSize; /*!< size in bytes of each data frame */ + int iSamplingRate; /*!< samplerate of analysis signal (necessary to recreate residual spectrum */ + int iFormat; /*!< type of data format \see SMS_Format */ + int nTracks; /*!< number of sinusoidal tracks per frame */ + int iFrameRate; /*!< rate in Hz of data frames */ + int iStochasticType; /*!< type stochastic representation */ + int nStochasticCoeff; /*!< number of stochastic coefficients per frame */ + int iEnvType; /*!< type of envelope representation */ + int nEnvCoeff; /*!< number of cepstral coefficents per frame */ + int iMaxFreq; /*!< maximum frequency of peaks (also corresponds to the last bin of the specEnv */ + sfloat fResidualPerc; /*!< percentage of the residual to original */ +} SMS_Header; + +/*! \struct SMS_Data + * \brief structure with SMS data + * + * Here is where all the analysis data ends up. Once data is in here, it is ready + * for synthesis. + * + * It is in one contigous block (pSmsData), the other pointer members point + * specifically to each component in the block. + * + * pFSinPha is optional in the final output, but it is always used to construct the + * residual signal. + */ +typedef struct +{ + sfloat *pSmsData; /*!< pointer to all SMS data */ + int sizeData; /*!< size of all the data */ + sfloat *pFSinFreq; /*!< frequency of sinusoids */ + sfloat *pFSinAmp; /*!< magnitude of sinusoids (stored in dB) */ + sfloat *pFSinPha; /*!< phase of sinusoids */ + int nTracks; /*!< number of sinusoidal tracks in frame */ + sfloat *pFStocGain; /*!< gain of stochastic component */ + int nCoeff; /*!< number of filter coefficients */ + sfloat *pFStocCoeff; /*!< filter coefficients for stochastic component */ + sfloat *pResPhase; /*!< residual phase spectrum */ + int nEnvCoeff; /*!< number of spectral envelope coefficients */ + sfloat *pSpecEnv; +} SMS_Data; + + +/*! \struct SMS_SndBuffer + * \brief buffer for sound data + * + * This structure is used for holding a buffer of audio data. iMarker is a + * sample number of the sound source that corresponds to the first sample + * in the buffer. + * + */ +typedef struct +{ + sfloat *pFBuffer; /*!< buffer for sound data*/ + int sizeBuffer; /*!< size of buffer */ + int iMarker; /*!< sample marker relating to sound source */ + int iFirstGood; /*!< first sample in buffer that is a good one */ +} SMS_SndBuffer; + +/*! \struct SMS_Peak + * \brief structure for sinusodial peak + */ +typedef struct +{ + sfloat fFreq; /*!< frequency of peak */ + sfloat fMag; /*!< magnitude of peak */ + sfloat fPhase; /*!< phase of peak */ +} SMS_Peak; + +/* a collection of spectral peaks */ +typedef struct +{ + SMS_Peak *pSpectralPeaks; + int nPeaks; + int nPeaksFound; +} SMS_SpectralPeaks; + +/*! \struct SMS_AnalFrame + * \brief structure to hold an analysis frame + * + * This structure has extra information for continuing the analysis, + * which can be disregarded once the analysis is complete. + */ +typedef struct +{ + int iFrameSample; /*!< sample number of the middle of the frame */ + int iFrameSize; /*!< number of samples used in the frame */ + int iFrameNum; /*!< frame number */ + SMS_Peak *pSpectralPeaks; /*!< spectral peaks found in frame */ + int nPeaks; /*!< number of peaks found */ + sfloat fFundamental; /*!< fundamental frequency in frame */ + SMS_Data deterministic; /*!< deterministic data */ + int iStatus; /*!< status of frame enumerated by SMS_FRAME_STATUS \see SMS_FRAME_STATUS */ +} SMS_AnalFrame; + +/*! \struct SMS_SEnvParams; + * \brief structure information and data for spectral enveloping + * + */ +typedef struct +{ + int iType; /*!< envelope type \see SMS_SpecEnvType */ + int iOrder; /*!< ceptrum order */ + int iMaxFreq; /*!< maximum frequency covered by the envelope */ + sfloat fLambda; /*!< regularization factor */ + int nCoeff; /*!< number of coefficients (bins) in the envelope */ + int iAnchor; /*!< whether to make anchor points at DC / Nyquist or not */ +} SMS_SEnvParams; + +/*! \struct SMS_Guide + * \brief information attached to a guide + * + * This structure is used to organize the detected peaks into time-varying + * trajectories, or sinusoidal tracks. As the analysis progresses, previous + * guides may be updated according to new information in the peak continuation + * of new frames (two-way mismatch). + */ +typedef struct +{ + sfloat fFreq; /*!< frequency of guide */ + sfloat fMag; /*!< magnitude of guide */ + int iStatus; /*!< status of guide: DEAD, SLEEPING, ACTIVE */ + int iPeakChosen; /*!< peak number chosen by the guide */ +} SMS_Guide; + +/*! \struct SMS_ResidualParams + * \brief structure with information for residual functions + * + * This structure contains all the necessary settings and memory for residual synthesis. + * + */ +typedef struct +{ + int samplingRate; + int hopSize; + int residualSize; + sfloat *residual; + sfloat *fftWindow; + sfloat *ifftWindow; + sfloat windowScale; + sfloat residualMag; + sfloat originalMag; + int nCoeffs; + sfloat *stocCoeffs; + int sizeStocMagSpectrum; + sfloat *stocMagSpectrum; + sfloat *stocPhaseSpectrum; + sfloat *approx; + sfloat *approxEnvelope; + sfloat fftBuffer[SMS_MAX_SPEC * 2]; +} SMS_ResidualParams; + +/*! \struct SMS_AnalParams + * \brief structure with useful information for analysis functions + * + * Each analysis needs one of these, which contains all settings, + * sound data, deterministic synthesis data, and every other + * piece of data that needs to be shared between functions. + * + * There is an array of already analyzed frames (hardcoded to 50 right now - + * \todo make it variable) that are accumulated for good harmonic detection + * and partial tracking. For instance, once the fundamental frequency of a + * harmonic signal is located (after a few frames), the harmonic analysis + * and peak detection/continuation process can be re-computed with more accuracy. + * + */ +typedef struct +{ + int iDebugMode; /*!< debug codes enumerated by SMS_DBG \see SMS_DBG */ + int iFormat; /*!< analysis format code defined by SMS_Format \see SMS_Format */ + int iSoundType; /*!< type of sound to be analyzed \see SMS_SOUND_TYPE */ + int iStochasticType; /*!< type of stochastic model defined by SMS_StocSynthType \see SMS_StocSynthType */ + int iFrameRate; /*!< rate in Hz of data frames */ + int nStochasticCoeff; /*!< number of stochastic coefficients per frame */ + sfloat fLowestFundamental; /*!< lowest fundamental frequency in Hz */ + sfloat fHighestFundamental; /*!< highest fundamental frequency in Hz */ + sfloat fDefaultFundamental; /*!< default fundamental in Hz */ + sfloat fPeakContToGuide; /*!< contribution of previous peak to current guide (between 0 and 1) */ + sfloat fFundContToGuide; /*!< contribution of current fundamental to current guide (between 0 and 1) */ + sfloat fFreqDeviation; /*!< maximum deviation from peak to peak */ + int iSamplingRate; /*! sampling rate of sound to be analyzed */ + int iDefaultSizeWindow; /*!< default size of analysis window in samples */ + int windowSize; /*!< the current window size */ + int sizeHop; /*!< hop size of analysis window in samples */ + sfloat fSizeWindow; /*!< size of analysis window in number of periods */ + int nTracks; /*!< number of sinusoidal tracks in frame */ + int maxPeaks; /*!< maximum number of peaks in a frame */ + int nGuides; /*!< number of guides used for peak detection and continuation \see SMS_Guide */ + int iCleanTracks; /*!< whether or not to clean sinusoidal tracks */ + sfloat fMinRefHarmMag; /*!< minimum magnitude in dB for reference peak */ + sfloat fRefHarmMagDiffFromMax; /*!< maximum magnitude difference from reference peak to highest peak */ + int iRefHarmonic; /*!< reference harmonic to use in the fundamental detection */ + int iMinTrackLength; /*!< minimum length in samples of a given track */ + int iMaxSleepingTime; /*!< maximum sleeping time for a track */ + sfloat fLowestFreq; /*!< lowest frequency to be searched */ + sfloat fHighestFreq; /*!< highest frequency to be searched */ + sfloat fMinPeakMag; /*!< minimum magnitude in dB for a good peak */ + int iAnalysisDirection; /*!< analysis direction, direct or reverse */ + int iSizeSound; /*!< total size of sound to be analyzed in samples */ + int nFrames; /*!< total number of frames that will be analyzed */ + int iWindowType; /*!< type of FFT analysis window \see SMS_WINDOWS */ + int iMaxDelayFrames; /*!< maximum number of frames to delay before peak continuation */ + int minGoodFrames; /*!< minimum number of stable frames for backward search */ + sfloat maxDeviation; /*!< maximum deviation allowed */ + int analDelay; /*! number of frames in the past to be looked in possible re-analyze */ + sfloat fResidualAccumPerc; /*!< accumalitive residual percentage */ + int sizeNextRead; /*!< size of samples to read from sound file next analysis */ + int preEmphasis; /*!< whether or not to perform pre-emphasis */ + sfloat preEmphasisLastValue; + SMS_Data prevFrame; /*!< the previous analysis frame */ + SMS_SEnvParams specEnvParams; /*!< all data for spectral enveloping */ + SMS_SndBuffer soundBuffer; /*!< signal to be analyzed */ + SMS_SndBuffer synthBuffer; /*!< resynthesized signal used to create the residual */ + SMS_AnalFrame *pFrames; /*!< an array of frames that have already been analyzed */ + sfloat magSpectrum[SMS_MAX_SPEC]; + sfloat phaseSpectrum[SMS_MAX_SPEC]; + sfloat spectrumWindow[SMS_MAX_SPEC]; + sfloat fftBuffer[SMS_MAX_SPEC * 2]; + SMS_ResidualParams residualParams; + int *guideStates; + SMS_Guide* guides; + sfloat inputBuffer[SMS_MAX_FRAME_SIZE]; + int sizeStocMagSpectrum; + sfloat *stocMagSpectrum; + sfloat *approxEnvelope; /*!< spectral approximation envelope */ + SMS_AnalFrame **ppFrames; /*!< pointers to the frames analyzed (it is circular-shifted once the array is full */ +} SMS_AnalParams; + +/*! \struct SMS_ModifyParams + * + * \brief structure with parameters and data that will be used to modify an SMS_Data frame + */ +typedef struct +{ + int ready; /*!< a flag to know if the struct has been initialized) */ + int maxFreq; /*!< maximum frequency component */ + int doResGain; /*!< whether or not to scale residual gain */ + sfloat resGain; /*!< residual scale factor */ + int doTranspose; /*!< whether or not to transpose */ + sfloat transpose; /*!< transposition factor */ + int doSinEnv; /*!< whether or not to apply a new spectral envelope to the sin component */ + sfloat sinEnvInterp; /*!< value between 0 (use frame's env) and 1 (use *env). Interpolates inbetween values*/ + int sizeSinEnv; /*!< size of the envelope pointed to by env */ + sfloat *sinEnv; /*!< sinusoidal spectral envelope */ + int doResEnv; /*!< whether or not to apply a new spectral envelope to the residual component */ + sfloat resEnvInterp; /*!< value between 0 (use frame's env) and 1 (use *env). Interpolates inbetween values*/ + int sizeResEnv; /*!< size of the envelope pointed to by resEnv */ + sfloat *resEnv; /*!< residual spectral envelope */ +} SMS_ModifyParams; + +/*! \struct SMS_SynthParams + * \brief structure with information for synthesis functions + * + * This structure contains all the necessary settings for different types of synthesis. + * It also holds arrays for windows and the inverse-FFT, as well as the previously + * synthesized frame. + * + */ +typedef struct +{ + int iStochasticType; /*!< type of stochastic model defined by SMS_StocSynthType + \see SMS_StocSynthType */ + int iSynthesisType; /*!< type of synthesis to perform \see SMS_SynthType */ + int iDetSynthType; /*!< method for synthesizing deterministic component \see SMS_DetSynthType */ + int iOriginalSRate; /*!< samplerate of the sound model source (for stochastic synthesis approximation) */ + int iSamplingRate; /*!< synthesis samplerate */ + int sizeHop; /*!< number of samples to synthesis for each frame */ + int origSizeHop; /*!< original number of samples used to create each analysis frame */ + int nTracks; + int nStochasticCoeff; + int deEmphasis; /*!< whether or not to perform de-emphasis */ + sfloat deEmphasisLastValue; + sfloat *pFDetWindow; /*!< array to hold the window used for deterministic synthesis \see SMS_WIN_IFFT */ + sfloat *pFStocWindow; /*!< array to hold the window used for stochastic synthesis (Hanning) */ + sfloat *pSynthBuff; /*!< an array for keeping samples during overlap-add (2x sizeHop) */ + sfloat *pMagBuff; /*!< an array for keeping magnitude spectrum for stochastic synthesis */ + sfloat *pPhaseBuff; /*!< an array for keeping phase spectrum for stochastic synthesis */ + sfloat *pSpectra; /*!< array for in-place FFT transform */ + SMS_Data prevFrame; /*!< previous data frame, for interpolation between frames */ + SMS_ModifyParams modParams; /*!< modification parameters */ + sfloat *approxEnvelope; /*!< spectral approximation envelope */ +} SMS_SynthParams; + +/*! \struct SMS_HarmCandidate + * \brief structure to hold information about a harmonic candidate + * + * This structure provides storage for accumimlated statistics when + * trying to decide which track is the fundamental frequency, during + * harmonic detection. + */ +typedef struct +{ + sfloat fFreq; /*!< frequency of harmonic */ + sfloat fMag; /*!< magnitude of harmonic */ + sfloat fMagPerc; /*!< percentage of magnitude */ + sfloat fFreqDev; /*!< deviation from perfect harmonic */ + sfloat fHarmRatio; /*!< percentage of harmonics found */ +} SMS_HarmCandidate; + +/*! \struct SMS_ContCandidate + * \brief structure to hold information about a continuation candidate + * + * This structure holds statistics about the guides, which is used to + * decide the status of the guide + */ +typedef struct +{ + sfloat fFreqDev; /*!< frequency deviation from guide */ + sfloat fMagDev; /*!< magnitude deviation from guide */ + int iPeak; /*!< peak number (organized according to frequency)*/ +} SMS_ContCandidate; + +/*! \brief analysis format + * + * Is the signal is known to be harmonic, using format harmonic (with out without + * phase) will give more accuracy to the peak continuation algorithm. If the signal + * is known to be inharmonic, then it is best to use one of the inharmonic settings + * to tell the peak continuation algorithm to just look at the peaks and connect them, + * instead of trying to look for peaks at specific frequencies (harmonic partials). + */ +enum SMS_Format +{ + SMS_FORMAT_H, /*!< 0, format harmonic */ + SMS_FORMAT_IH, /*!< 1, format inharmonic */ + SMS_FORMAT_HP, /*!< 2, format harmonic with phase */ + SMS_FORMAT_IHP /*!< 3, format inharmonic with phase */ +}; + +/*! \brief synthesis types + * + * These values are used to determine whether to synthesize + * both deterministic and stochastic components together, + * the deterministic component alone, or the stochastic + * component alone. + */ +enum SMS_SynthType +{ + SMS_STYPE_ALL, /*!< both components combined */ + SMS_STYPE_DET, /*!< deterministic component alone */ + SMS_STYPE_STOC /*!< stochastic component alone */ +}; + +/*! \brief synthesis method for deterministic component + * + * There are two options for deterministic synthesis available to the + * SMS synthesizer. The Inverse Fast Fourier Transform method + * (IFFT) is more effecient for models with lots of partial tracks, but can + * possibly smear transients. The Sinusoidal Table Lookup (SIN) can + * theoritically support faster moving tracks at a higher fidelity, but + * can consume lots of cpu at varying rates. + */ +enum SMS_DetSynthType +{ + SMS_DET_IFFT, /*!< Inverse Fast Fourier Transform (IFFT) */ + SMS_DET_SIN /*!< Sinusoidal Table Lookup (SIN) */ +}; + +/*! \brief synthesis method for stochastic component + * + * Currently, Stochastic Approximation is the only reasonable choice + * for stochastic synthesis: this method approximates the spectrum of + * the stochastic component by a specified number of coefficients during + * analyses, and then approximates another set of coefficients during + * synthesis in order to fit the specified hopsize. The phases of the + * coefficients are randomly generated, according to the theory that a + * stochastic spectrum consists of random phases. + * + * The Inverse FFT method is not implemented, but is based on the idea of storing + * the exact spectrum and phases of the residual component to file. Synthesis + * could then be an exact reconstruction of the original signal, provided + * interpolation is not necessary. + * + * No stochastic component can also be specified in order to skip the this + * time consuming process altogether. This is especially useful when + * performing multiple analyses to fine tune parameters pertaining to the + * determistic component; once that is achieved, the stochastic component + * will be much better as well. + */ +enum SMS_StocSynthType +{ + SMS_STOC_NONE, /*!< 0, no stochastistic component */ + SMS_STOC_APPROX, /*!< 1, Inverse FFT, magnitude approximation and generated phases */ + SMS_STOC_IFFT /*!< 2, inverse FFT, interpolated spectrum (not used) */ +}; + +/*! \brief synthesis method for deterministic component + * + * There are two options for deterministic synthesis available to the + * SMS synthesizer. The Inverse Fast Fourier Transform method + * (IFFT) is more effecient for models with lots of partial tracks, but can + * possibly smear transients. The Sinusoidal Table Lookup (SIN) can + * theoritically support faster moving tracks at a higher fidelity, but + * can consume lots of cpu at varying rates. + */ +enum SMS_SpecEnvType +{ + SMS_ENV_NONE, /*!< none */ + SMS_ENV_CEP, /*!< cepstral coefficients */ + SMS_ENV_FBINS /*!< frequency bins */ +}; + +/*! \brief Error codes returned by SMS file functions */ +/* \todo remove me */ +enum SMS_ERRORS +{ + SMS_OK, /*!< 0, no error*/ + SMS_NOPEN, /*!< 1, couldn't open file */ + SMS_NSMS , /*!< 2, not a SMS file */ + SMS_MALLOC, /*!< 3, couldn't allocate memory */ + SMS_RDERR, /*!< 4, read error */ + SMS_WRERR, /*!< 5, write error */ + SMS_SNDERR /*!< 6, sound IO error */ +}; + +/*! \brief debug modes + * + * \todo write details about debug files + */ +enum SMS_DBG +{ + SMS_DBG_NONE, /*!< 0, no debugging */ + SMS_DBG_DET, /*!< 1, not yet implemented \todo make this show main information to look at for discovering the correct deterministic parameters*/ + SMS_DBG_PEAK_DET, /*!< 2, peak detection function */ + SMS_DBG_HARM_DET, /*!< 3, harmonic detection function */ + SMS_DBG_PEAK_CONT, /*!< 4, peak continuation function */ + SMS_DBG_CLEAN_TRAJ, /*!< 5, clean tracks function */ + SMS_DBG_SINE_SYNTH, /*!< 6, sine synthesis function */ + SMS_DBG_STOC_ANAL, /*!< 7, stochastic analysis function */ + SMS_DBG_STOC_SYNTH, /*!< 8, stochastic synthesis function */ + SMS_DBG_SMS_ANAL, /*!< 9, top level analysis function */ + SMS_DBG_ALL, /*!< 10, everything */ + SMS_DBG_RESIDUAL, /*!< 11, write residual to file */ + SMS_DBG_SYNC, /*!< 12, write original, synthesis and residual to a text file */ +}; + +#define SMS_MAX_WINDOW 8190 /*!< \brief maximum size for analysis window */ + +/* \brief type of sound to be analyzed + * + * \todo explain the differences between these two + */ +enum SMS_SOUND_TYPE +{ + SMS_SOUND_TYPE_MELODY, /*!< 0, sound composed of several notes */ + SMS_SOUND_TYPE_NOTE /*!< 1, sound composed of a single note */ +}; + +/* \brief direction of analysis + * + * Sometimes a signal can be clearer at the end than at + * the beginning. If the signal is very harmonic at the end then + * doing the analysis in reverse could provide better results. + */ +enum SMS_DIRECTION +{ + SMS_DIR_FWD, /*!< analysis from left to right */ + SMS_DIR_REV /*!< analysis from right to left */ +}; + +/* \brief window selection +*/ +enum SMS_WINDOWS +{ + SMS_WIN_HAMMING, /*!< 0: hamming */ + SMS_WIN_BH_62, /*!< 1: blackman-harris, 62dB cutoff */ + SMS_WIN_BH_70, /*!< 2: blackman-harris, 70dB cutoff */ + SMS_WIN_BH_74, /*!< 3: blackman-harris, 74dB cutoff */ + SMS_WIN_BH_92, /*!< 4: blackman-harris, 92dB cutoff */ + SMS_WIN_HANNING, /*!< 5: hanning */ + SMS_WIN_IFFT /*!< 6: window for deterministic synthesis based on the Inverse-FFT algorithm. + This is a combination of an inverse Blackman-Harris 92dB and a triangular window. */ +}; + +/*! + * \brief frame status + */ +enum SMS_FRAME_STATUS +{ + SMS_FRAME_EMPTY, + SMS_FRAME_READY, + SMS_FRAME_PEAKS_FOUND, + SMS_FRAME_FUND_FOUND, + SMS_FRAME_TRAJ_FOUND, + SMS_FRAME_CLEANED, + SMS_FRAME_RECOMPUTED, + SMS_FRAME_DETER_SYNTH, + SMS_FRAME_STOC_COMPUTED, + SMS_FRAME_DONE, + SMS_FRAME_END +}; + +#define SMS_MIN_SIZE_FRAME 128 /* size of synthesis frame */ + +/*! \defgroup math_macros Math Macros + * \brief mathematical operations and values needed for functions within + * this library + * \{ + */ +#define PI 3.141592653589793238462643 /*!< pi */ +#define TWO_PI 6.28318530717958647692 /*!< pi * 2 */ +#define INV_TWO_PI (1 / TWO_PI) /*!< 1 / ( pi * 2) */ +#define PI_2 1.57079632679489661923 /*!< pi / 2 */ +#define LOG2 0.69314718055994529 /*!< natural logarithm of 2 */ +#define LOG10 2.3025850929940459 /*!< natural logarithm of 10 */ +#define EXP 2.7182818284590451 /*!< Eurler's number */ + +sfloat sms_magToDB(sfloat x); +sfloat sms_dBToMag(sfloat x); +void sms_arrayMagToDB(int sizeArray, sfloat *pArray); +void sms_arrayDBToMag(int sizeArray, sfloat *pArray); +void sms_setMagThresh(sfloat x); +sfloat sms_rms(int sizeArray, sfloat *pArray); +sfloat sms_sine(sfloat fTheta); +sfloat sms_sinc(sfloat fTheta); +sfloat sms_random(void); +int sms_power2(int n); +sfloat sms_scalarTempered(sfloat x); +void sms_arrayScalarTempered(int sizeArray, sfloat *pArray); + +#ifndef MAX +/*! \brief returns the maximum of a and b */ +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +/*! \brief returns the minimum of a and b */ +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +/*! \} */ + +/* function declarations */ +void sms_setPeaks(SMS_AnalParams *pAnalParams, int numamps, sfloat* amps, + int numfreqs, sfloat* freqs, int numphases, sfloat* phases); +int sms_findPeaks(int sizeWaveform, sfloat *pWaveform, + SMS_AnalParams *pAnalParams, SMS_SpectralPeaks *pSpectralPeaks); +int sms_findPartials(SMS_Data *pSmsFrame, SMS_AnalParams *pAnalParams); +int sms_findResidual(int sizeSynthesis, sfloat* pSynthesis, + int sizeOriginal, sfloat* pOriginal, + SMS_ResidualParams *residualParams); +void sms_approxResidual(int sizeResidual, sfloat* residual, + int sizeApprox, sfloat* approx, + SMS_ResidualParams *residualParams); +int sms_analyze(int sizeWaveform, sfloat *pWaveform, SMS_Data *pSmsData, + SMS_AnalParams *pAnalParams); +void sms_analyzeFrame(int iCurrentFrame, SMS_AnalParams *pAnalParams, sfloat fRefFundamental); + +int sms_init(); +void sms_free(); +int sms_initAnalysis(SMS_AnalParams *pAnalParams); +void sms_initAnalParams(SMS_AnalParams *pAnalParams); +void sms_initSynthParams(SMS_SynthParams *synthParams); +int sms_initSynth(SMS_SynthParams *pSynthParams); +void sms_freeAnalysis(SMS_AnalParams *pAnalParams); +void sms_freeSynth(SMS_SynthParams *pSynthParams); +int sms_initSpectralPeaks(SMS_SpectralPeaks* peaks, int n); +void sms_freeSpectralPeaks(SMS_SpectralPeaks* peaks); + +void sms_fillSoundBuffer(int sizeWaveform, sfloat *pWaveform, SMS_AnalParams *pAnalParams); +void sms_windowCentered(int sizeWindow, sfloat *pWaveform, sfloat *pWindow, int sizeFft, sfloat *pFftBuffer); +void sms_getWindow(int sizeWindow, sfloat *pWindow, int iWindowType); +void sms_scaleWindow(int sizeWindow, sfloat *pWindow); +int sms_spectrum(int sizeWindow, sfloat *pWaveform, sfloat *pWindow, int sizeMag, + sfloat *pMag, sfloat *pPhase, sfloat *pFftBuffer); +int sms_spectrumW(int sizeWindow, sfloat *pWaveform, sfloat *pWindow, int sizeMag, + sfloat *pMag, sfloat *pPhase, sfloat *pFftBuffer); +int sms_invSpectrum(int sizeWaveform, sfloat *pWaveform, sfloat *pWindow , + int sizeMag, sfloat *pMag, sfloat *pPhase, sfloat *pFftBuffer); +/* \todo remove this once invSpectrum is completely implemented */ +int sms_invQuickSpectrumW(sfloat *pFMagSpectrum, sfloat *pFPhaseSpectrum, + int sizeFft, sfloat *pFWaveform, int sizeWave, + sfloat *pFWindow, sfloat *pFftBuffer); +int sms_spectralApprox(sfloat *pSpec1, int sizeSpec1, int sizeSpec1Used, + sfloat *pSpec2, int sizeSpec2, int nCoefficients, + sfloat *envelope); +int sms_spectrumMag(int sizeWindow, sfloat *pWaveform, sfloat *pWindow, + int sizeMag, sfloat *pMag, sfloat *pFftBuffer); + +void sms_dCepstrum(int sizeCepstrum, sfloat *pCepstrum, int sizeFreq, sfloat *pFreq, sfloat *pMag, + sfloat fLambda, int iSamplingRate); +void sms_dCepstrumEnvelope(int sizeCepstrum, sfloat *pCepstrum, int sizeEnv, sfloat *pEnv); +void sms_spectralEnvelope(SMS_Data *pSmsData, SMS_SEnvParams *pSpecEnvParams); + +int sms_sizeNextWindow(int iCurrentFrame, SMS_AnalParams *pAnalParams); +sfloat sms_fundDeviation(SMS_AnalParams *pAnalParams, int iCurrentFrame); +int sms_detectPeaks(int sizeSpec, sfloat *pFMag, sfloat *pPhase, + SMS_Peak *pSpectralPeaks, SMS_AnalParams *pAnalParams); +sfloat sms_harmDetection(int numPeaks, SMS_Peak* spectralPeaks, sfloat refFundamental, + sfloat refHarmonic, sfloat lowestFreq, sfloat highestFreq, + int soundType, sfloat minRefHarmMag, sfloat refHarmMagDiffFromMax); +int sms_peakContinuation(int iFrame, SMS_AnalParams *pAnalParams); + +sfloat sms_preEmphasis(sfloat fInput, SMS_AnalParams *pAnalParams); +sfloat sms_deEmphasis(sfloat fInput, SMS_SynthParams *pSynthParams); + +void sms_cleanTracks(int iCurrentFrame, SMS_AnalParams *pAnalParams); +void sms_scaleDet(sfloat *pSynthBuffer, sfloat *pOriginalBuffer, + sfloat *pSinAmp, SMS_AnalParams *pAnalParams, int nTracks); + +int sms_prepSine(int nTableSize); +int sms_prepSinc(int nTableSize); +void sms_clearSine(); +void sms_clearSinc(); + +void sms_synthesize(SMS_Data *pSmsFrame, sfloat*pSynthesis, SMS_SynthParams *pSynthParams); +void sms_sineSynthFrame(SMS_Data *pSmsFrame, sfloat *pBuffer, + int sizeBuffer, SMS_Data *pLastFrame, + int iSamplingRate); + +void sms_initHeader(SMS_Header *pSmsHeader); +int sms_getHeader(char *pChFileName, SMS_Header **ppSmsHeader, FILE **ppInputFile); +void sms_fillHeader(SMS_Header *pSmsHeader, SMS_AnalParams *pAnalParams, char *pProgramString); +int sms_writeHeader(char *pFileName, SMS_Header *pSmsHeader, FILE **ppOutSmsFile); +int sms_writeFile(FILE *pSmsFile, SMS_Header *pSmsHeader); +int sms_initFrame(int iCurrentFrame, SMS_AnalParams *pAnalParams, int sizeWindow); +int sms_clearAnalysisFrame(int iCurrentFrame, SMS_AnalParams *pAnalParams); +int sms_allocFrame(SMS_Data *pSmsFrame, int nTracks, int nCoeff, + int iPhase, int stochType, int nEnvCoeff); +int sms_allocFrameH(SMS_Header *pSmsHeader, SMS_Data *pSmsFrame); +int sms_getFrame(FILE *pInputFile, SMS_Header *pSmsHeader, int iFrame, SMS_Data *pSmsFrame); +int sms_writeFrame(FILE *pSmsFile, SMS_Header *pSmsHeader, SMS_Data *pSmsFrame); +void sms_freeFrame(SMS_Data *pSmsFrame); +void sms_clearFrame(SMS_Data *pSmsFrame); +void sms_copyFrame(SMS_Data *pCopySmsFrame, SMS_Data *pOriginalSmsFrame); +int sms_frameSizeB(SMS_Header *pSmsHeader); + +void sms_initResidualParams(SMS_ResidualParams *residualParams); +int sms_initResidual(SMS_ResidualParams *residualParams); +void sms_freeResidual(SMS_ResidualParams *residualParams); +int sms_residual(int sizeWindow, sfloat *pSynthesis, sfloat *pOriginal, + SMS_ResidualParams* residualParams); +void sms_filterHighPass(int sizeResidual, sfloat *pResidual, int iSamplingRate); +int sms_stocAnalysis(int sizeWindow, sfloat *pResidual, sfloat *pWindow, + SMS_Data *pSmsFrame, SMS_AnalParams *pAnalParams); + +void sms_interpolateFrames(SMS_Data *pSmsFrame1, SMS_Data *pSmsFrame2, + SMS_Data *pSmsFrameOut, sfloat fInterpFactor); +void sms_fft(int sizeFft, sfloat *pArray); +void sms_ifft(int sizeFft, sfloat *pArray); +void sms_RectToPolar(int sizeSpec, sfloat *pReal, sfloat *pMag, sfloat *pPhase); +void sms_PolarToRect(int sizeSpec, sfloat *pReal, sfloat *pMag, sfloat *pPhase); +void sms_spectrumRMS(int sizeMag, sfloat *pReal, sfloat *pMag); + +void sms_initModify(SMS_Header *header, SMS_ModifyParams *params); +void sms_initModifyParams(SMS_ModifyParams *params); +void sms_freeModify(SMS_ModifyParams *params); +void sms_modify(SMS_Data *frame, SMS_ModifyParams *params); + +/***********************************************************************************/ +/************* debug functions: ******************************************************/ + +int sms_createDebugFile(SMS_AnalParams *pAnalParams); +void sms_writeDebugData(sfloat *pBuffer1, sfloat *pBuffer2, + sfloat *pBuffer3, int sizeBuffer); +void sms_writeDebugFile(); +void sms_error(char *pErrorMessage ); +int sms_errorCheck(); +char* sms_errorString(); + +#endif /* _SMS_H */ + diff --git a/src/sms/sms.i b/src/sms/sms.i new file mode 100644 index 0000000..0b3ee5a --- /dev/null +++ b/src/sms/sms.i @@ -0,0 +1,540 @@ +%module simplsms +%{ + #include "sms.h" + #define SWIG_FILE_WITH_INIT +%} + +%include "../common/numpy.i" + +%init +%{ + import_array(); +%} + +%exception +{ + $action + if (sms_errorCheck()) + { + PyErr_SetString(PyExc_IndexError,sms_errorString()); + return NULL; + } +} + +/* apply all numpy typemaps to various names in sms.h */ +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeWindow, double* pWindow)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeWaveform, double* pWaveform)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(long sizeSound, double* pSound)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeFft, double* pArray)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeFft, double* pFftBuffer)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeFreq, double* pFreq)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeAmp, double* pAmp)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeMag, double* pMag)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizePhase, double* pPhase)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeCepstrum, double* pCepstrum)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeEnv, double* pEnv)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeTrack, double* pTrack)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeArray, double* pArray)}; +%apply(int DIM1, double* IN_ARRAY1) {(int sizeInArray, double* pInArray)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeOutArray, double* pOutArray)}; +%apply(int DIM1, double* INPLACE_ARRAY1) {(int sizeHop, double* pSynthesis)}; +%apply(int DIM1, double* INPLACE_ARRAY1) +{ + (int sizeResidual, double* residual), + (int sizeApprox, double* approx) +} +%apply(int DIM1, double* IN_ARRAY1) +{ + (int numamps, double* amps), + (int numfreqs, double* freqs), + (int numphases, double* phases) +} +%apply (int DIM1, double* IN_ARRAY1) +{ + (int sizeSynthesis, double* pSynthesis), + (int sizeOriginal, double* pOriginal), + (int sizeResidual, double* pResidual) +} + +%include "sms.h" + +/* overload the functions that will be wrapped to fit numpy typmaps (defined below) + * by renaming the wrapped names back to originals + */ +%rename(sms_detectPeaks) simplsms_detectPeaks; +%rename(sms_spectrum) simplsms_spectrum; +%rename(sms_spectrumMag) simplsms_spectrumMag; +%rename(sms_windowCentered) simplsms_windowCentered; +%rename(sms_invSpectrum) simplsms_invSpectrum; +%rename(sms_dCepstrum) simplsms_dCepstrum; +%rename(sms_synthesize) simplsms_synthesize_wrapper; + +%inline +%{ + typedef struct + { + SMS_Header *header; + SMS_Data *smsData; + int allocated; + } SMS_File; + + void simplsms_dCepstrum(int sizeCepstrum, sfloat *pCepstrum, int sizeFreq, sfloat *pFreq, int sizeMag, sfloat *pMag, + sfloat fLambda, int iSamplingRate) + { + sms_dCepstrum(sizeCepstrum,pCepstrum, sizeFreq, pFreq, pMag, + fLambda, iSamplingRate); + } + int simplsms_detectPeaks(int sizeMag, sfloat *pMag, int sizePhase, sfloat *pPhase, + SMS_SpectralPeaks *pPeakStruct, SMS_AnalParams *pAnalParams) + { + if(sizeMag != sizePhase) + { + sms_error("sizeMag != sizePhase"); + return 0; + } + if(pPeakStruct->nPeaks < pAnalParams->maxPeaks) + { + sms_error("nPeaks in SMS_SpectralPeaks is not large enough (less than SMS_AnalParams.maxPeaks)"); + return 0; + } + pPeakStruct->nPeaksFound = sms_detectPeaks(sizeMag, pMag, pPhase, pPeakStruct->pSpectralPeaks, pAnalParams); + return pPeakStruct->nPeaksFound; + } + int simplsms_spectrum(int sizeWaveform, sfloat *pWaveform, int sizeWindow, sfloat *pWindow, + int sizeMag, sfloat *pMag, int sizePhase, sfloat *pPhase, sfloat *pFftBuffer) + { + return sms_spectrum(sizeWindow, pWaveform, pWindow, sizeMag, pMag, pPhase, pFftBuffer); + } + int simplsms_spectrumMag(int sizeWaveform, sfloat *pWaveform, int sizeWindow, sfloat *pWindow, + int sizeMag, sfloat *pMag, sfloat *pFftBuffer) + { + return sms_spectrumMag(sizeWindow, pWaveform, pWindow, sizeMag, pMag, pFftBuffer); + } + int simplsms_invSpectrum(int sizeWaveform, sfloat *pWaveform, int sizeWindow, sfloat *pWindow, + int sizeMag, sfloat *pMag, int sizePhase, sfloat *pPhase, sfloat *pFftBuffer) + { + return sms_invSpectrum(sizeWaveform, pWaveform, pWindow, sizeMag, pMag, pPhase, pFftBuffer); + } + void simplsms_windowCentered(int sizeWaveform, sfloat *pWaveform, int sizeWindow, + sfloat *pWindow, int sizeFft, sfloat *pFftBuffer) + { + if (sizeWaveform != sizeWindow) + { + sms_error("sizeWaveform != sizeWindow"); + return; + } + sms_windowCentered(sizeWindow, pWaveform, pWindow, sizeFft, pFftBuffer); + } + void simplsms_synthesize_wrapper(SMS_Data *pSmsData, int sizeHop, sfloat *pSynthesis, SMS_SynthParams *pSynthParams) + { + if(sizeHop != pSynthParams->sizeHop) + { + sms_error("sizeHop != pSynthParams->sizeHop"); + return; + } + sms_synthesize(pSmsData, pSynthesis, pSynthParams); + } +%} + +%extend SMS_File +{ + /* load an entire file to an internal numpy array */ + void load(char *pFilename) + { + int i; + FILE *pSmsFile; + $self->allocated = 0; + sms_getHeader (pFilename, &$self->header, &pSmsFile); + if(sms_errorCheck()) return; + + $self->smsData = calloc($self->header->nFrames, sizeof(SMS_Data)); + for(i = 0; i < $self->header->nFrames; i++) + { + sms_allocFrameH ($self->header, &$self->smsData[i]); + if(sms_errorCheck()) + return; + sms_getFrame (pSmsFile, $self->header, i, &$self->smsData[i]); + if(sms_errorCheck()) + return; + } + $self->allocated = 1; + } + void close() /* todo: this should be in the destructor, no? */ + { + int i; + if(!$self->allocated) + { + sms_error("file not yet alloceted"); + return; + } + $self->allocated = 0; + for(i = 0; i < $self->header->nFrames; i++) + sms_freeFrame(&$self->smsData[i]); + free($self->smsData); + } + /* return a pointer to a frame, which can be passed around to other libsms functions */ + void getFrame(int i, SMS_Data *frame) + { + if(i < 0 || i >= $self->header->nFrames) + { + sms_error("index is out of file boundaries"); + return; + } + frame = &$self->smsData[i]; + } + void getTrack(int track, int sizeFreq, sfloat *pFreq, int sizeAmp, sfloat *pAmp) + { + /* fatal error protection first */ + if(!$self->allocated) + { + sms_error("file not yet alloceted"); + return; + } + if(track >= $self->header->nTracks) + { + sms_error("desired track is greater than number of tracks in file"); + return; + } + if(sizeFreq != sizeAmp) + { + sms_error("freq and amp arrays are different in size"); + return; + } + /* make sure arrays are big enough, or return less data */ + int nFrames = MIN(sizeFreq, $self->header->nFrames); + int i; + for(i=0; i < nFrames; i++) + { + pFreq[i] = $self->smsData[i].pFSinFreq[track]; + pAmp[i] = $self->smsData[i].pFSinAmp[track]; + } + } + // TODO turn into getTrackP - and check if phase exists + void getTrack(int track, int sizeFreq, sfloat *pFreq, int sizeAmp, + sfloat *pAmp, int sizePhase, sfloat *pPhase) + { + /* fatal error protection first */ + if(!$self->allocated) + { + sms_error("file not yet alloceted"); + return; + } + if(track >= $self->header->nTracks) + { + sms_error("desired track is greater than number of tracks in file"); + return; + } + if(sizeFreq != sizeAmp) + { + sms_error("freq and amp arrays are different in size"); + return; + } + /* make sure arrays are big enough, or return less data */ + int nFrames = MIN (sizeFreq, $self->header->nFrames); + int i; + for(i=0; i < nFrames; i++) + { + pFreq[i] = $self->smsData[i].pFSinFreq[track]; + pAmp[i] = $self->smsData[i].pFSinFreq[track]; + } + if($self->header->iFormat < SMS_FORMAT_HP) + return; + + if(sizePhase != sizeFreq || sizePhase != sizeAmp) + { + sms_error("phase array and freq/amp arrays are different in size"); + return; + } + for(i=0; i < nFrames; i++) + pPhase[i] = $self->smsData[i].pFSinPha[track]; + } + void getFrameDet(int i, int sizeFreq, sfloat *pFreq, int sizeAmp, sfloat *pAmp) + { + if(!$self->allocated) + { + sms_error("file not yet alloceted"); + return; + } + if(i >= $self->header->nFrames) + { + sms_error("index is greater than number of frames in file"); + return; + } + int nTracks = $self->smsData[i].nTracks; + if(sizeFreq > nTracks) + { + sms_error("index is greater than number of frames in file"); + return; + } + if(sizeFreq != sizeAmp) + { + sms_error("freq and amp arrays are different in size"); + return; + } + memcpy(pFreq, $self->smsData[i].pFSinFreq, sizeof(sfloat) * nTracks); + memcpy(pAmp, $self->smsData[i].pFSinAmp, sizeof(sfloat) * nTracks); + } + void getFrameDetP(int i, int sizeFreq, sfloat *pFreq, int sizeAmp, + sfloat *pAmp, int sizePhase, sfloat *pPhase) + { + if(!$self->allocated) + { + sms_error("file not yet alloceted"); + return; + } + if($self->header->iFormat < SMS_FORMAT_HP) + { + sms_error("file does not contain a phase component in Deterministic (iFormat < SMS_FORMAT_HP)"); + return; + } + if(i >= $self->header->nFrames) + { + sms_error("index is greater than number of frames in file"); + return; + } + int nTracks = $self->smsData[i].nTracks; + if(sizeFreq > nTracks) + { + sms_error("index is greater than number of frames in file"); + return; + } + if(sizeFreq != sizeAmp) + { + sms_error("freq and amp arrays are different in size"); + return; + } + memcpy(pFreq, $self->smsData[i].pFSinFreq, sizeof(sfloat) * nTracks); + memcpy(pAmp, $self->smsData[i].pFSinAmp, sizeof(sfloat) * nTracks); + + if(sizePhase != sizeFreq || sizePhase != sizeAmp) + { + sms_error("phase array and freq/amp arrays are different in size"); + return; + } + memcpy(pPhase, $self->smsData[i].pFSinPha, sizeof(sfloat) * nTracks); + } + void getFrameRes(int i, int sizeRes, sfloat *pRes) + { + if(!$self->allocated) + { + sms_error("file not yet alloceted"); + return; + } + if($self->header->iStochasticType < 1) + { + sms_error("file does not contain a stochastic component"); + return; + } + int nCoeff = sizeRes; + if($self->header->nStochasticCoeff > sizeRes) + nCoeff = $self->header->nStochasticCoeff; // return what you can + + memcpy(pRes, $self->smsData[i].pFStocCoeff, sizeof(sfloat) * nCoeff); + } + void getFrameEnv(int i, int sizeEnv, sfloat *pEnv) + { + if(!$self->allocated) + { + sms_error("file not yet alloceted"); + return; + } + if($self->header->iEnvType < 1) + { + sms_error("file does not contain a spectral envelope"); + return; + } + int nCoeff = sizeEnv; + if($self->header->nStochasticCoeff > sizeEnv) + nCoeff = $self->header->nEnvCoeff; // return what you can + + memcpy(pEnv, $self->smsData[i].pSpecEnv, sizeof(sfloat) * nCoeff); + } +} + +%extend SMS_AnalParams +{ + SMS_AnalParams() + { + SMS_AnalParams *s = (SMS_AnalParams *)malloc(sizeof(SMS_AnalParams)); + sms_initAnalParams(s); + return s; + } +} + +%extend SMS_SynthParams +{ + SMS_SynthParams() + { + SMS_SynthParams *s = (SMS_SynthParams *)malloc(sizeof(SMS_SynthParams)); + sms_initSynthParams(s); + return s; + } +} + +%extend SMS_SpectralPeaks +{ + SMS_SpectralPeaks(int n) + { + SMS_SpectralPeaks *s = (SMS_SpectralPeaks *)malloc(sizeof(SMS_SpectralPeaks)); + if(s == NULL) + { + sms_error("Could not allocate memory for SMS_SpectralPeaks"); + return NULL; + } + if(sms_initSpectralPeaks(s, n) < 0) + { + sms_error("Could not initialise SMS_SpectralPeaks"); + return NULL; + } + return s; + } + void getFreq(int sizeArray, sfloat *pArray ) + { + if(sizeArray < $self->nPeaksFound) + { + sms_error("numpy array not big enough"); + return; + } + int i; + for(i = 0; i < $self->nPeaksFound; i++) + pArray[i] = $self->pSpectralPeaks[i].fFreq; + } + void getMag(int sizeArray, sfloat *pArray ) + { + if(sizeArray < $self->nPeaksFound) + { + sms_error("numpy array not big enough"); + return; + } + int i; + for(i = 0; i < $self->nPeaksFound; i++) + pArray[i] = $self->pSpectralPeaks[i].fMag; + } + void getPhase(int sizeArray, sfloat *pArray ) + { + if(sizeArray < $self->nPeaksFound) + { + sms_error("numpy array not big enough"); + return; + } + int i; + for(i = 0; i < $self->nPeaksFound; i++) + pArray[i] = $self->pSpectralPeaks[i].fPhase; + } +} + +%extend SMS_Data +{ + void getSinAmp(int sizeArray, sfloat *pArray) + { + if(sizeArray < $self->nTracks) + { + sms_error("numpy array not big enough"); + return; + } + int i; + for(i = 0; i < $self->nTracks; i++) + pArray[i] = $self->pFSinAmp[i]; + } + void getSinFreq(int sizeArray, sfloat *pArray) + { + if(sizeArray < $self->nTracks) + { + sms_error("numpy array not big enough"); + return; + } + int i; + for(i = 0; i < $self->nTracks; i++) + pArray[i] = $self->pFSinFreq[i]; + } + void getSinPhase(int sizeArray, sfloat *pArray) + { + if(sizeArray < $self->nTracks) + { + sms_error("numpy array not big enough"); + return; + } + int i; + for(i = 0; i < $self->nTracks; i++) + pArray[i] = $self->pFSinPha[i]; + } + void getSinEnv(int sizeArray, sfloat *pArray) + { + if(sizeArray < $self->nEnvCoeff) + { + sms_error("numpy array not big enough"); + return; + } + int i; + for(i = 0; i < $self->nEnvCoeff; i++) + pArray[i] = $self->pSpecEnv[i]; + } + void setSinAmp(int sizeArray, sfloat *pArray) + { + if(sizeArray < $self->nTracks) + { + sms_error("numpy array not big enough"); + return; + } + int i; + for (i = 0; i < $self->nTracks; i++) + $self->pFSinAmp[i] = pArray[i]; + } + void setSinFreq(int sizeArray, sfloat *pArray) + { + if(sizeArray < $self->nTracks) + { + sms_error("numpy array not big enough"); + return; + } + int i; + for(i = 0; i < $self->nTracks; i++) + $self->pFSinFreq[i] = pArray[i]; + } + void setSinPha(int sizeArray, sfloat *pArray) + { + if(sizeArray < $self->nTracks) + { + sms_error("numpy array not big enough"); + return; + } + int i; + for(i = 0; i < $self->nTracks; i++) + $self->pFSinPha[i] = pArray[i]; + } +} + +%extend SMS_ResidualParams +{ + void getResidual(int sizeArray, sfloat *pArray) + { + if(sizeArray < $self->hopSize) + { + sms_error("numpy array not big enough"); + return; + } + int i; + for(i = 0; i < $self->hopSize; i++) + pArray[i] = $self->residual[i]; + } +} + +%extend SMS_ModifyParams +{ + /* no need to return an error code, if sms_error is called, it will throw an exception in python */ + void setSinEnv(int sizeArray, sfloat *pArray) + { + if(!$self->ready) + { + sms_error("modify parameter structure has not been initialized"); + return; + } + if(sizeArray != $self->sizeSinEnv) + { + sms_error("numpy array is not equal to envelope size"); + return; + } + memcpy($self->sinEnv, pArray, sizeof(sfloat) * $self->sizeSinEnv); + } +} + diff --git a/src/sms/soundIO.c b/src/sms/soundIO.c new file mode 100644 index 0000000..2e2379b --- /dev/null +++ b/src/sms/soundIO.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file soundIO.c + * \brief soundfile input and output. +*/ +#include "sms.h" + +/*! \brief fill the sound buffer + * + * \param sizeWaveform size of input data + * \param pWaveform input data + * \param pAnalParams pointer to structure of analysis parameters + */ +void sms_fillSoundBuffer(int sizeWaveform, sfloat *pWaveform, SMS_AnalParams *pAnalParams) +{ + int i; + long sizeNewData = (long)sizeWaveform; + + /* leave space for new data */ + memcpy(pAnalParams->soundBuffer.pFBuffer, pAnalParams->soundBuffer.pFBuffer+sizeNewData, + sizeof(sfloat) * (pAnalParams->soundBuffer.sizeBuffer - sizeNewData)); + + pAnalParams->soundBuffer.iFirstGood = MAX(0, pAnalParams->soundBuffer.iFirstGood - sizeNewData); + pAnalParams->soundBuffer.iMarker += sizeNewData; + + /* put the new data in, and do some pre-emphasis */ + if(pAnalParams->iAnalysisDirection == SMS_DIR_REV) + for(i=0; i<sizeNewData; i++) + pAnalParams->soundBuffer.pFBuffer[pAnalParams->soundBuffer.sizeBuffer - sizeNewData + i] = + sms_preEmphasis(pWaveform[sizeNewData - (1 + i)], pAnalParams); + else + for(i=0; i<sizeNewData; i++) + pAnalParams->soundBuffer.pFBuffer[pAnalParams->soundBuffer.sizeBuffer - sizeNewData + i] = + sms_preEmphasis(pWaveform[i], pAnalParams); +} + diff --git a/src/sms/spectralApprox.c b/src/sms/spectralApprox.c new file mode 100644 index 0000000..b1f65ef --- /dev/null +++ b/src/sms/spectralApprox.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file spectralApprox.c + * \brief line segment approximation of a magnitude spectrum + */ +#include "sms.h" + +/*! \brief approximate a magnitude spectrum + * First downsampling using local maxima and then upsampling using linear + * interpolation. The output spectrum doesn't have to be the same size as + * the input one. + * + * \param pFSpec1 magnitude spectrum to approximate + * \param sizeSpec1 size of input spectrum + * \param sizeSpec1Used size of the spectrum to use + * \param pFSpec2 output envelope + * \param sizeSpec2 size of output envelope + * \param nCoefficients number of coefficients to use in approximation + * \return error code \see SMS_ERRORS + */ +int sms_spectralApprox(sfloat *pFSpec1, int sizeSpec1, int sizeSpec1Used, + sfloat *pFSpec2, int sizeSpec2, int nCoefficients, + sfloat *envelope) +{ + sfloat fHopSize, fCurrentLoc = 0, fLeft = 0, fRight = 0, fValue = 0, + fLastLocation, fSizeX, fSpec2Acum=0, fNextHop, fDeltaY; + int iFirstGood = 0, iLastSample = 0, i, j; + + /* when number of coefficients is smaller than 2 do not approximate */ + if(nCoefficients < 2) + { + for(i = 0; i < sizeSpec2; i++) + pFSpec2[i] = 1; + return SMS_OK; + } + + /* calculate the hop size */ + if(nCoefficients > sizeSpec1) + nCoefficients = sizeSpec1; + + fHopSize = (sfloat)sizeSpec1Used / nCoefficients; + + /* approximate by linear interpolation */ + if(fHopSize > 1) + { + iFirstGood = 0; + for(i = 0; i < nCoefficients; i++) + { + iLastSample = fLastLocation = fCurrentLoc + fHopSize; + iLastSample = MIN(sizeSpec1-1, iLastSample); + if(iLastSample < sizeSpec1-1) + { + fRight = pFSpec1[iLastSample] + + (pFSpec1[iLastSample+1] - pFSpec1[iLastSample]) * + (fLastLocation - iLastSample); + } + else + { + fRight = pFSpec1[iLastSample]; + } + + fValue = 0; + for(j = iFirstGood; j <= iLastSample; j++) + fValue = MAX (fValue, pFSpec1[j]); + fValue = MAX(fValue, MAX (fRight, fLeft)); + envelope[i] = fValue; + + fLeft = fRight; + fCurrentLoc = fLastLocation; + iFirstGood = (int)(1+ fCurrentLoc); + } + } + else if(fHopSize == 1) + { + for(i = 0; i < nCoefficients; i++) + envelope[i] = pFSpec1[i]; + } + else + { + sms_error("SpectralApprox: sizeSpec1 has too many nCoefficients"); /* \todo need to increase the frequency? */ + return -1; + } + + /* Creates Spec2 from Envelope */ + if(nCoefficients < sizeSpec2) + { + fSizeX = (sfloat) (sizeSpec2-1) / nCoefficients; + + /* the first step */ + fNextHop = fSizeX / 2; + fDeltaY = envelope[0] / fNextHop; + fSpec2Acum=pFSpec2[j=0]=0; + while(++j < fNextHop) + pFSpec2[j] = (fSpec2Acum += fDeltaY); + + /* middle values */ + for(i = 0; i <= nCoefficients-2; ++i) + { + fDeltaY = (envelope[i+1] - envelope[i]) / fSizeX; + /* first point of a segment */ + pFSpec2[j] = (fSpec2Acum = (envelope[i]+(fDeltaY*(j-fNextHop)))); + ++j; + /* remaining points */ + fNextHop += fSizeX; + while(j < fNextHop) + pFSpec2[j++] = (fSpec2Acum += fDeltaY); + } + + /* last step */ + fDeltaY = -envelope[i] * 2 / fSizeX; + /* first point of the last segment */ + pFSpec2[j] = (fSpec2Acum = (envelope[i]+(fDeltaY*(j-fNextHop)))); + ++j; + fNextHop += fSizeX / 2; + while(j < sizeSpec2-1) + pFSpec2[j++]=(fSpec2Acum += fDeltaY); + /* last should be exactly zero */ + pFSpec2[sizeSpec2-1] = .0; + } + else if(nCoefficients == sizeSpec2) + { + for(i = 0; i < nCoefficients; i++) + pFSpec2[i] = envelope[i]; + } + else + { + sms_error("SpectralApprox: sizeSpec2 has too many nCoefficients\n"); + return -1; + } + + return SMS_OK; +} + + diff --git a/src/sms/spectrum.c b/src/sms/spectrum.c new file mode 100644 index 0000000..1b8e053 --- /dev/null +++ b/src/sms/spectrum.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file spectrum.c + * \brief functions to convert between frequency (spectrum) and time (wavefrom) domain + */ +#include "sms.h" + +/*! \brief compute a complex spectrum from a waveform + * + * \param sizeWindow size of analysis window + * \param pWaveform pointer to input waveform + * \param pWindow pointer to input window + * \param sizeMag size of output magnitude and phase spectrums + * \param pMag pointer to output magnitude spectrum + * \param pPhase pointer to output phase spectrum + * \return sizeFft, -1 on error \todo remove this return + */ +int sms_spectrum(int sizeWindow, sfloat *pWaveform, sfloat *pWindow, int sizeMag, + sfloat *pMag, sfloat *pPhase, sfloat *pFftBuffer) +{ + int i, it2; + sfloat fReal, fImag; + + int sizeFft = sizeMag << 1; + memset(pFftBuffer, 0, sizeFft * sizeof(sfloat)); + + /* apply window to waveform and center window around 0 (zero-phase windowing)*/ + sms_windowCentered(sizeWindow, pWaveform, pWindow, sizeFft, pFftBuffer); + sms_fft(sizeFft, pFftBuffer); + + /* convert from rectangular to polar coordinates */ + for(i = 0; i < sizeMag; i++) + { + it2 = i << 1; //even numbers 0-N + fReal = pFftBuffer[it2]; /*odd numbers 1->N+1 */ + fImag = pFftBuffer[it2 + 1]; /*even numbers 2->N+2 */ + pMag[i] = sqrt(fReal * fReal + fImag * fImag); + pPhase[i] = atan2(-fImag, fReal); /* \todo why is fImag negated? */ + /*pPhase[i] = atan2(fImag, fReal);*/ + } + + return sizeFft; +} + +/* sms_spectrum, but without zero-phase windowing, and with phase calculated + * according by arctan(imag/real) instead of arctan2(-imag/real) + */ +int sms_spectrumW(int sizeWindow, sfloat *pWaveform, sfloat *pWindow, int sizeMag, + sfloat *pMag, sfloat *pPhase, sfloat *pFftBuffer) +{ + int i, it2; + sfloat fReal, fImag; + + int sizeFft = sizeMag << 1; + memset(pFftBuffer, 0, sizeFft * sizeof(sfloat)); + + /* apply window to waveform */ + for(i = 0; i < sizeWindow; i++) + pFftBuffer[i] = pWaveform[i] * pWindow[i]; + + sms_fft(sizeFft, pFftBuffer); + + /* convert from rectangular to polar coordinates */ + for(i = 0; i < sizeMag; i++) + { + it2 = i << 1; //even numbers 0-N + fReal = pFftBuffer[it2]; /*odd numbers 1->N+1 */ + fImag = pFftBuffer[it2 + 1]; /*even numbers 2->N+2 */ + pMag[i] = sqrt(fReal * fReal + fImag * fImag); + pPhase[i] = atan2(fImag, fReal); + } + + return sizeFft; +} + +/*! \brief compute the spectrum Magnitude of a waveform + * + * This function windows the waveform with pWindow and + * performs a zero-padded FFT (if sizeMag*2 > sizeWindow). + * The spectra is then converted magnitude (RMS). + * + * \param sizeWindow size of analysis window / input wavefrom + * \param pWaveform pointer to input waveform + * \param pWindow pointer to analysis window + * \param sizeMag size of output magnitude spectrum + * \param pMag pointer to output magnitude spectrum + * \return 0 on success, -1 on error + */ +int sms_spectrumMag(int sizeWindow, sfloat *pWaveform, sfloat *pWindow, + int sizeMag, sfloat *pMag, sfloat *pFftBuffer) +{ + int i,it2; + int sizeFft = sizeMag << 1; + sfloat fReal, fImag; + + /* apply window to waveform, zero the rest of the array */ + for(i = 0; i < sizeWindow; i++) + pFftBuffer[i] = pWaveform[i] * pWindow[i]; + for(i = sizeWindow; i < sizeFft; i++) + pFftBuffer[i] = 0.; + + /* compute real FFT */ + sms_fft(sizeFft, pFftBuffer); + + /* convert from rectangular to polar coordinates */ + for(i = 0; i < sizeMag; i++) + { + it2 = i << 1; + fReal = pFftBuffer[it2]; + fImag = pFftBuffer[it2+1]; + pMag[i] = sqrtf(fReal * fReal + fImag * fImag); + } + + return sizeFft; +} + +/*! \brief function for a quick inverse spectrum, windowed + * + * Not done yet, but this will be a function that is the inverse of + * sms_spectrum above. + * + * function to perform the inverse FFT, windowing the output + * sfloat *pFMagSpectrum input magnitude spectrum + * sfloat *pFPhaseSpectrum input phase spectrum + * int sizeFft size of FFT + * sfloat *pFWaveform output waveform + * int sizeWave size of output waveform + * sfloat *pFWindow synthesis window + */ +int sms_invSpectrum(int sizeWaveform, sfloat *pWaveform, sfloat *pWindow, + int sizeMag, sfloat *pMag, sfloat *pPhase, sfloat *pFftBuffer) +{ + int i; + int sizeFft = sizeMag << 1; + + sms_PolarToRect(sizeMag, pFftBuffer, pMag, pPhase); + sms_ifft(sizeFft, pFftBuffer); + + /* assume that the output array does not need to be cleared */ + /* before, this was multiplied by .5, why? */ + for(i = 0; i < sizeWaveform; i++) + //pWaveform[i] += pFftBuffer[i] * pWindow[i]; + pWaveform[i] = pFftBuffer[i]; + + return sizeFft; +} + +/*! \brief function for a quick inverse spectrum, windowed + * function to perform the inverse FFT, windowing the output + * sfloat *pFMagSpectrum input magnitude spectrum + * sfloat *pFPhaseSpectrum input phase spectrum + * int sizeFft size of FFT + * sfloat *pFWaveform output waveform + * int sizeWave size of output waveform + * sfloat *pFWindow synthesis window + */ +int sms_invQuickSpectrumW(sfloat *pFMagSpectrum, sfloat *pFPhaseSpectrum, + int sizeFft, sfloat *pFWaveform, int sizeWave, + sfloat *pFWindow, sfloat* pFftBuffer) +{ + int i, it2; + int sizeMag = sizeFft >> 1; + + /* convert from polar coordinates to rectangular */ + for(i = 0; i < sizeMag; i++) + { + it2 = i << 1; + pFftBuffer[it2] = pFMagSpectrum[i] * cos(pFPhaseSpectrum[i]); + pFftBuffer[it2+1] = pFMagSpectrum[i] * sin(pFPhaseSpectrum[i]); + } + + /* compute IFFT */ + sms_ifft(sizeFft, pFftBuffer); + + /* assume that the output array does not need to be cleared */ + for(i = 0; i < sizeWave; i++) + pFWaveform[i] += (pFftBuffer[i] * pFWindow[i] * .5); + + return sizeMag; +} + +/*! \brief convert spectrum from Rectangular to Polar form + * + * \param sizeMag size of spectrum (pMag and pPhase arrays) + * \param pRect pointer output spectrum in rectangular form (2x sizeSpec) + * \param pMag pointer to sfloat array of magnitude spectrum + * \param pPhase pointer to sfloat array of phase spectrum + */ +void sms_RectToPolar(int sizeMag, sfloat *pRect, sfloat *pMag, sfloat *pPhase) +{ + int i, it2; + sfloat fReal, fImag; + + for(i=0; i<sizeMag; i++) + { + it2 = i << 1; + fReal = pRect[it2]; + fImag = pRect[it2+1]; + + pMag[i] = sqrtf(fReal * fReal + fImag * fImag); + if(pPhase) + pPhase[i] = atan2f(fImag, fReal); + } +} + +/*! \brief convert spectrum from Rectangular to Polar form + * + * \param sizeSpec size of spectrum (pMag and pPhase arrays) + * \param pRect pointer output spectrum in rectangular form (2x sizeSpec) + * \param pMag pointer to sfloat array of magnitude spectrum + * \param pPhase pointer to sfloat array of phase spectrum + */ +void sms_PolarToRect(int sizeSpec, sfloat *pRect, sfloat *pMag, sfloat *pPhase) +{ + int i, it2; + sfloat fMag; + + for(i = 0; i<sizeSpec; i++) + { + it2 = i << 1; + fMag = pMag[i]; + pRect[it2] = fMag * cos (pPhase[i]); + pRect[it2+1] = fMag * sin (pPhase[i]); + } +} + +/*! \brief compute magnitude spectrum of a DFT in rectangular coordinates + * + * \param sizeMag size of output Magnitude (half of input real FFT) + * \param pInRect pointer to input DFT array (real/imag sfloats) + * \param pOutMag pointer to of magnitude spectrum array + */ +void sms_spectrumRMS(int sizeMag, sfloat *pInRect, sfloat *pOutMag) +{ + int i, it2; + sfloat fReal, fImag; + + for(i=0; i<sizeMag; i++) + { + it2 = i << 1; + fReal = pInRect[it2]; + fImag = pInRect[it2+1]; + pOutMag[i] = sqrtf(fReal * fReal + fImag * fImag); + } +} + diff --git a/src/sms/stocAnalysis.c b/src/sms/stocAnalysis.c new file mode 100644 index 0000000..802e41c --- /dev/null +++ b/src/sms/stocAnalysis.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, M 02111-1307 USA + * + */ +/*! \file stocAnalysis.c + * \brief stochastic analysis using spectral analysis and approximation + */ +#include "sms.h" + +/*! \brief main function for the stochastic analysis + * \param sizeWindow size of buffer + * \param pResidual pointer to residual signal + * \param pWindow pointer to windowing array + * \param pSmsData pointer to output SMS data + * \param pAnalParams point to analysis parameters + * \return 0 on success, -1 on error + */ +int sms_stocAnalysis(int sizeWindow, sfloat *pResidual, sfloat *pWindow, + SMS_Data *pSmsData, SMS_AnalParams* pAnalParams) +{ + int i; + sfloat fMag = 0.0; + + sms_spectrumMag(sizeWindow, pResidual, pWindow, pAnalParams->sizeStocMagSpectrum, + pAnalParams->stocMagSpectrum, pAnalParams->fftBuffer); + + sms_spectralApprox(pAnalParams->stocMagSpectrum, pAnalParams->sizeStocMagSpectrum, + pAnalParams->sizeStocMagSpectrum, pSmsData->pFStocCoeff, + pSmsData->nCoeff, pSmsData->nCoeff, + pAnalParams->approxEnvelope); + + /* get energy of spectrum */ + for(i = 0; i < pAnalParams->sizeStocMagSpectrum; i++) + fMag += (pAnalParams->stocMagSpectrum[i] * pAnalParams->stocMagSpectrum[i]); + + *pSmsData->pFStocGain = fMag / pAnalParams->sizeStocMagSpectrum; + return 0; +} + diff --git a/src/sms/synthesis.c b/src/sms/synthesis.c new file mode 100644 index 0000000..bc739d0 --- /dev/null +++ b/src/sms/synthesis.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file synthesis.c + * \brief main synthesis routines + */ +#include "sms.h" + +/*! \brief synthesis of one frame of the deterministic component using the IFFT + * + * \param pSmsData pointer to SMS data structure frame + * \param pSynthParams pointer to structure of synthesis parameters + */ +static void SineSynthIFFT(SMS_Data *pSmsData, SMS_SynthParams *pSynthParams) +{ + int sizeFft = pSynthParams->sizeHop << 1; + int iHalfSamplingRate = pSynthParams->iSamplingRate >> 1; + int sizeMag = pSynthParams->sizeHop; + int nBins = 8; + int nTracks = pSmsData->nTracks; + int iFirstBin, k, i, l, b; + sfloat fMag=0.0, fFreq=0.0, fPhase=0.0, fLoc, fSin, fCos, fBinRemainder, + fTmp, fNewMag, fIndex; + sfloat fSamplingPeriod = 1.0 / pSynthParams->iSamplingRate; + memset(pSynthParams->pSpectra, 0, sizeFft * sizeof(sfloat)); + for(i = 0; i < nTracks; i++) + { + if(((fMag = pSmsData->pFSinAmp[i]) > 0) && + ((fFreq = (pSmsData->pFSinFreq[i])) < iHalfSamplingRate)) + { + /* \todo maybe this check can be removed if the SynthParams->prevFrame gets random + phases in sms_initSynth? */ + if(pSynthParams->prevFrame.pFSinAmp[i] <= 0) + pSynthParams->prevFrame.pFSinPha[i] = TWO_PI * sms_random(); + + fMag = sms_dBToMag(fMag); + fTmp = pSynthParams->prevFrame.pFSinPha[i] + + TWO_PI * fFreq * fSamplingPeriod * sizeMag; + fPhase = fTmp - floor(fTmp * INV_TWO_PI) * TWO_PI; + fLoc = sizeFft * fFreq * fSamplingPeriod; + iFirstBin = (int) fLoc - 3; + fBinRemainder = fLoc - floor (fLoc); + fSin = sms_sine(fPhase); + fCos = sms_sine(fPhase + PI_2); + for (k = 1, l = iFirstBin; k <= nBins; k++, l++) + { + fIndex = (k - fBinRemainder); + if (fIndex > 7.999) fIndex = 0; + fNewMag = fMag * sms_sinc (fIndex); + if(l > 0 && l < sizeMag) + { + pSynthParams->pSpectra[l*2+1] += fNewMag * fCos; + pSynthParams->pSpectra[l*2] += fNewMag * fSin; + } + else if(l == 0) + { + pSynthParams->pSpectra[0] += 2 * fNewMag * fSin; + } + else if(l < 0) + { + b = abs(l); + pSynthParams->pSpectra[b*2+1] -= fNewMag * fCos; + pSynthParams->pSpectra[b*2] += fNewMag * fSin; + } + else if(l > sizeMag) + { + b = sizeMag - (l - sizeMag); + pSynthParams->pSpectra[b*2+1] -= fNewMag * fCos; + pSynthParams->pSpectra[b*2] += fNewMag * fSin; + } + else if(l == sizeMag) + { + pSynthParams->pSpectra[1] += 2 * fNewMag * fSin; + } + } + } + pSynthParams->prevFrame.pFSinAmp[i] = fMag; + pSynthParams->prevFrame.pFSinPha[i] = fPhase; + pSynthParams->prevFrame.pFSinFreq[i] = fFreq; + } + + sms_ifft(sizeFft, pSynthParams->pSpectra); + + for(i = 0, k = sizeMag; i < sizeMag; i++, k++) + pSynthParams->pSynthBuff[i] += pSynthParams->pSpectra[k] * pSynthParams->pFDetWindow[i]; + for(i= sizeMag, k = 0; i < sizeFft; i++, k++) + pSynthParams->pSynthBuff[i] += pSynthParams->pSpectra[k] * pSynthParams->pFDetWindow[i]; +} + +/*! \brief synthesis of one frame of the stochastic component by apprimating phases + * + * computes a linearly interpolated spectral envelope to fit the correct number of output + * audio samples. Phases are generated randomly. + * + * \param pSmsData pointer to the current SMS frame + * \param pSynthParams pointer to a strucure of synthesis parameters + * \return + * \todo cleanup returns and various constant multipliers. check that approximation is ok + */ +static int StocSynthApprox(SMS_Data *pSmsData, SMS_SynthParams *pSynthParams) +{ + int i, sizeSpec1Used; + int sizeSpec1 = pSmsData->nCoeff; + int sizeSpec2 = pSynthParams->sizeHop; + int sizeFft = pSynthParams->sizeHop << 1; /* 50% overlap, so sizeFft is 2x sizeHop */ + + /* if no gain or no coefficients return */ + if(*(pSmsData->pFStocGain) <= 0) + return 0; + + sizeSpec1Used = sizeSpec1 * pSynthParams->iSamplingRate / pSynthParams->iOriginalSRate; + + /* sizeSpec1Used cannot be more than what is available \todo check by graph */ + if(sizeSpec1Used > sizeSpec1) sizeSpec1Used = sizeSpec1; + + sms_spectralApprox(pSmsData->pFStocCoeff, sizeSpec1, sizeSpec1Used, + pSynthParams->pMagBuff, sizeSpec2, sizeSpec1Used, + pSynthParams->approxEnvelope); + + /* generate random phases */ + for(i = 0; i < sizeSpec2; i++) + pSynthParams->pPhaseBuff[i] = TWO_PI * sms_random(); + + sms_invQuickSpectrumW(pSynthParams->pMagBuff, pSynthParams->pPhaseBuff, + sizeFft, pSynthParams->pSynthBuff, sizeFft, + pSynthParams->pFStocWindow, pSynthParams->pSpectra); + return 1; +} + +/*! \brief synthesizes one frame of the residual signal + * + * \param residualParams Parameters and memory for residual synthesis + */ +void sms_approxResidual(int sizeResidual, sfloat* residual, + int sizeApprox, sfloat* approx, + SMS_ResidualParams *residualParams) +{ + int i; + + /* shift buffers */ + memcpy(residualParams->residual, + residualParams->residual + residualParams->hopSize, + sizeof(sfloat) * residualParams->hopSize); + memcpy(residualParams->residual + residualParams->hopSize, residual, + sizeof(sfloat) * residualParams->hopSize); + + memcpy(residualParams->approx, + residualParams->approx + residualParams->hopSize, + sizeof(sfloat) * residualParams->hopSize); + memset(residualParams->approx + residualParams->hopSize, 0, + sizeof(sfloat) * residualParams->hopSize); + + sms_spectrumMag(residualParams->residualSize, + residualParams->residual, + residualParams->fftWindow, + residualParams->sizeStocMagSpectrum, + residualParams->stocMagSpectrum, + residualParams->fftBuffer); + + if(residualParams->sizeStocMagSpectrum != residualParams->nCoeffs) + { + sms_spectralApprox(residualParams->stocMagSpectrum, + residualParams->sizeStocMagSpectrum, + residualParams->sizeStocMagSpectrum, + residualParams->stocCoeffs, + residualParams->nCoeffs, + residualParams->nCoeffs, + residualParams->approxEnvelope); + + sms_spectralApprox(residualParams->stocCoeffs, + residualParams->nCoeffs, + residualParams->nCoeffs, + residualParams->stocMagSpectrum, + residualParams->sizeStocMagSpectrum, + residualParams->sizeStocMagSpectrum, + residualParams->approxEnvelope); + } + + /* generate random phases */ + for(i = 0; i < residualParams->sizeStocMagSpectrum; i++) + residualParams->stocPhaseSpectrum[i] = TWO_PI * sms_random(); + + /* IFFT with 50% overlap */ + sms_invQuickSpectrumW(residualParams->stocMagSpectrum, + residualParams->stocPhaseSpectrum, + residualParams->sizeStocMagSpectrum*2, + residualParams->approx, + residualParams->residualSize, + residualParams->ifftWindow, + residualParams->fftBuffer); + + /* output */ + for(i = 0; i < sizeApprox; i++) + approx[i] = residualParams->approx[i] * residualParams->windowScale; +} + +/*! \brief synthesizes one frame of SMS data + * + * \param pSmsData input SMS data + * \param pFSynthesis output sound buffer + * \param pSynthParams synthesis parameters + */ +void sms_synthesize(SMS_Data *pSmsData, sfloat *pFSynthesis, SMS_SynthParams *pSynthParams) +{ + int i; + int sizeHop = pSynthParams->sizeHop; + + memcpy(pSynthParams->pSynthBuff, (sfloat *)(pSynthParams->pSynthBuff+sizeHop), + sizeof(sfloat) * sizeHop); + memset(pSynthParams->pSynthBuff+sizeHop, 0, sizeof(sfloat) * sizeHop); + + /* convert mags from linear to db */ + sms_arrayMagToDB(pSmsData->nTracks, pSmsData->pFSinAmp); + + /* decide which combo of synthesis methods to use */ + if(pSynthParams->iSynthesisType == SMS_STYPE_ALL) + { + if(pSynthParams->iDetSynthType == SMS_DET_IFFT) + SineSynthIFFT(pSmsData, pSynthParams); + else /*pSynthParams->iDetSynthType == SMS_DET_SIN*/ + { + sms_sineSynthFrame(pSmsData, pSynthParams->pSynthBuff, pSynthParams->sizeHop, + &(pSynthParams->prevFrame), pSynthParams->iSamplingRate); + } + StocSynthApprox(pSmsData, pSynthParams); + } + else if(pSynthParams->iSynthesisType == SMS_STYPE_DET) + { + if(pSynthParams->iDetSynthType == SMS_DET_IFFT) + SineSynthIFFT(pSmsData, pSynthParams); + else /*pSynthParams->iDetSynthType == SMS_DET_SIN*/ + { + sms_sineSynthFrame(pSmsData, pSynthParams->pSynthBuff, pSynthParams->sizeHop, + &(pSynthParams->prevFrame), pSynthParams->iSamplingRate); + } + } + else /* pSynthParams->iSynthesisType == SMS_STYPE_STOC */ + StocSynthApprox(pSmsData, pSynthParams); + + /* de-emphasize the sound and normalize*/ + for(i = 0; i < sizeHop; i++) + pFSynthesis[i] = sms_deEmphasis(pSynthParams->pSynthBuff[i], pSynthParams); +} diff --git a/src/sms/tables.c b/src/sms/tables.c new file mode 100644 index 0000000..85d21cb --- /dev/null +++ b/src/sms/tables.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file tables.c + * \brief sin and sinc tables. + * + * contains functions for creating and indexing the tables + */ +#include "sms.h" + +/*! \brief value to scale the sine-table-lookup phase */ +static sfloat fSineScale; +/*! \brief inverse of fSineScale - turns a division into multiplication */ +static sfloat fSineIncr; +/*! \brief value to scale the sinc-table-lookup phase */ +static sfloat fSincScale; +/*! \brief global pointer to the sine table */ +static sfloat *sms_tab_sine; +/*! \brief global pointer to the sinc table */ +static sfloat *sms_tab_sinc; + +/*! \brief prepares the sine table + * \param nTableSize size of table + * \return error code \see SMS_MALLOC in SMS_ERRORS + */ +int sms_prepSine(int nTableSize) +{ + register int i; + sfloat fTheta; + + sms_tab_sine = (sfloat *)malloc(nTableSize * sizeof(sfloat)); + if(sms_tab_sine == NULL) + { + sms_error("Could not allocate memory for sine table"); + return SMS_MALLOC; + } + fSineScale = (sfloat)(TWO_PI) / (sfloat)(nTableSize - 1); + fSineIncr = 1.0 / fSineScale; + fTheta = 0.0; + for(i = 0; i < nTableSize; i++) + { + fTheta = fSineScale * (sfloat)i; + sms_tab_sine[i] = sin(fTheta); + } + return SMS_OK; +} +/*! \brief clear sine table */ +void sms_clearSine() +{ + if(sms_tab_sine) + free(sms_tab_sine); + sms_tab_sine = NULL; +} + +/*! \brief table-lookup sine method + * \param fTheta angle in radians + * \return approximately sin(fTheta) + */ +sfloat sms_sine(sfloat fTheta) +{ + int i; + fTheta = fTheta - floor(fTheta * INV_TWO_PI) * TWO_PI; + + if(fTheta < 0) + { + i = .5 - (fTheta * fSineIncr); + return -(sms_tab_sine[i]); + } + else + { + i = fTheta * fSineIncr + .5; + return sms_tab_sine[i]; + } +} + +/*! \brief Sinc method to generate the lookup table + */ +static sfloat Sinc(sfloat x, sfloat N) +{ + return sinf((N/2) * x) / sinf(x/2); +} + +/*! \brief prepare the Sinc table + * + * used for the main lobe of a frequency domain + * BlackmanHarris92 window + * + * \param nTableSize size of table + * \return error code \see SMS_MALLOC in SMS_ERRORS + */ +int sms_prepSinc(int nTableSize) +{ + int i, m; + sfloat N = 512.0; + sfloat fA[4] = {.35875, .48829, .14128, .01168}; + sfloat fMax = 0; + sfloat fTheta = -4.0 * TWO_PI / N; + sfloat fThetaIncr = (8.0 * TWO_PI / N) / (nTableSize); + + sms_tab_sinc = (sfloat *)calloc(nTableSize, sizeof(sfloat)); + if(sms_tab_sinc == NULL) + { + sms_error("Could not allocate memory for sinc table"); + return (SMS_MALLOC); + } + + for(i = 0; i < nTableSize; i++) + { + for (m = 0; m < 4; m++) + sms_tab_sinc[i] += -1 * (fA[m]/2) * + (Sinc (fTheta - m * TWO_PI/N, N) + + Sinc (fTheta + m * TWO_PI/N, N)); + fTheta += fThetaIncr; + } + + fMax = sms_tab_sinc[(int) nTableSize / 2]; + for (i = 0; i < nTableSize; i++) + sms_tab_sinc[i] = sms_tab_sinc[i] / fMax; + + fSincScale = (sfloat) nTableSize / 8.0; + return SMS_OK; +} + +/*! \brief clear sine table */ +void sms_clearSinc() +{ + if(sms_tab_sinc) + free(sms_tab_sinc); + sms_tab_sinc = 0; +} + +/*! \brief global sinc table-lookup method + * + * fTheta has to be from 0 to 8 + * + * \param fTheta angle in radians + * \return approximately sinc(fTheta) + */ +sfloat sms_sinc(sfloat fTheta) +{ + int index = (int) (.5 + fSincScale * fTheta); + return sms_tab_sinc[index]; +} + + diff --git a/src/sms/transforms.c b/src/sms/transforms.c new file mode 100644 index 0000000..04ab492 --- /dev/null +++ b/src/sms/transforms.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/*! \file transforms.c + * \brief routines for different Fast Fourier Transform Algorithms + * + */ + +#include "sms.h" +#include "OOURA.h" + +static int ip[NMAXSQRT +2]; +static sfloat w[NMAX * 5 / 4]; + +/*! \brief Forward Fast Fourier Transform + * + * function to call the OOURA routines to calculate + * the forward FFT. Operation is in place. + * \todo if sizeFft != power of 2, there is a silent crash.. cuidado! + * + * \param sizeFft size of the FFT in samples (must be a power of 2 >= 2) + * \param pArray pointer to real array (n >= 2, n = power of 2) + */ +void sms_fft(int sizeFft, sfloat *pArray) +{ + rdft(sizeFft, 1, pArray, ip, w); +} + +/*! \brief Inverse Forward Fast Fourier Transform + * + * function to call the OOURA routines to calculate + * the Inverse FFT. Operation is in place. + * + * \param sizeFft size of the FFT in samples (must be a power of 2 >= 2) + * \param pArray pointer to real array (n >= 2, n = power of 2) + */ +void sms_ifft(int sizeFft, sfloat *pArray) +{ + rdft(sizeFft, -1, pArray, ip, w); +} diff --git a/src/sms/windows.c b/src/sms/windows.c new file mode 100644 index 0000000..46ca5be --- /dev/null +++ b/src/sms/windows.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2008 MUSIC TECHNOLOGY GROUP (MTG) + * UNIVERSITAT POMPEU FABRA + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/*! \file windows.c + * \brief functions for creating various windows + * + * Use sms_getWindow() for selecting which window will be made + */ +#include "sms.h" + +/* \brief scale a window by its integral (numeric quadrature) + * + * In order to get a normalized magnitude spectrum (ex. Fourier analysis + * of a sinusoid with linear magnitude 1 gives one peak of magnitude 1 in + * the frequency domain), the spectrum windowing function should be + * normalized by its area under the curve. + * + * \param sizeWindow the size of the window + * \param pWindow pointer to an array that will hold the window + */ +void sms_scaleWindow(int sizeWindow, sfloat *pWindow) +{ + int i; + sfloat fSum = 0; + sfloat fScale; + + for(i = 0; i < sizeWindow; i++) + fSum += pWindow[i]; + + fScale = 2. / fSum; + + for(i = 0; i < sizeWindow; i++) + pWindow[i] *= fScale; +} + +/*! \brief window to be used in the IFFT synthesis + * + * contains both an inverse Blackman-Harris and triangular window. + * + * \todo read X. Rodet, Ph. Depalle, "Spectral Envelopes and Inverse FFT + * Synthesis." Proc. 93rd AES Convention, October 1992 + * \param sizeWindow the size of the window + * \param pFWindow pointer to an array that will hold the window + */ +void IFFTwindow(int sizeWindow, sfloat *pFWindow) +{ + int i; + sfloat a0 = .35875, a1 = .48829, a2 = .14128, a3 = .01168; + double fConst = TWO_PI / sizeWindow, fIncr = 2.0 /sizeWindow, fVal = 0; + + /* compute inverse of Blackman-Harris 92dB window */ + for(i = 0; i < sizeWindow; i++) + { + pFWindow[i] = 1 / (a0 - a1 * cos(fConst * i) + + a2 * cos(fConst * 2 * i) - a3 * cos(fConst * 3 * i)); + } + + /* scale function by a triangular */ + for(i = 0; i < sizeWindow / 2; i++) + { + pFWindow[i] = fVal * pFWindow[i] / 2.787457; + fVal += fIncr; + } + for(i = sizeWindow / 2; i < sizeWindow; i++) + { + pFWindow[i] = fVal * pFWindow[i] / 2.787457; + fVal -= fIncr; + } +} + +/*! \brief BlackmanHarris window with 62dB rolloff + * + * \todo where did these come from? + * \param sizeWindow the size of the window + * \param pFWindow pointer to an array that will hold the window + */ +void BlackmanHarris62(int sizeWindow, sfloat *pFWindow) +{ + int i; + double fSum = 0; + /* for 3 term -62.05 */ + sfloat a0 = .44959, a1 = .49364, a2 = .05677; + double fConst = TWO_PI / sizeWindow; + + /* compute window */ + for(i = 0; i < sizeWindow; i++) + { + fSum += pFWindow[i] = a0 - a1 * cos(fConst * i) + + a2 * cos(fConst * 2 * i); + } +} + +/*! \brief BlackmanHarris window with 70dB rolloff + * + * \param sizeWindow the size of the window + * \param pFWindow pointer to an array that will hold the window + */ +void BlackmanHarris70(int sizeWindow, sfloat *pFWindow) +{ + int i; + double fSum = 0; + /* for 3 term -70.83 */ + sfloat a0 = .42323, a1 = .49755, a2 = .07922; + double fConst = TWO_PI / sizeWindow; + + /* compute window */ + for(i = 0; i < sizeWindow; i++) + { + fSum += pFWindow[i] = a0 - a1 * cos(fConst * i) + + a2 * cos(fConst * 2 * i); + } +} + +/*! \brief BlackmanHarris window with 74dB rolloff + * + * \param sizeWindow the size of the window + * \param pFWindow pointer to an array that will hold the window + */ +void BlackmanHarris74(int sizeWindow, sfloat *pFWindow) +{ + int i; + double fSum = 0; + /* for -74dB from the Nuttall paper */ + sfloat a0 = .40217, a1 = .49703, a2 = .09892, a3 = .00188; + double fConst = TWO_PI / sizeWindow; + + /* compute window */ + for(i = 0; i < sizeWindow; i++) + { + fSum += pFWindow[i] = a0 - a1 * cos(fConst * i) + + a2 * cos(fConst * 2 * i) + a3 * cos(fConst * 3 * i); + } +} + +/*! \brief BlackmanHarris window with 92dB rolloff + * + * \param sizeWindow the size of the window + * \param pFWindow pointer to an array that will hold the window + */ +void BlackmanHarris92(int sizeWindow, sfloat *pFWindow) +{ + int i; + double fSum = 0; + /* for -92dB */ + sfloat a0 = .35875, a1 = .48829, a2 = .14128, a3 = .01168; + double fConst = TWO_PI / sizeWindow; + + /* compute window */ + for(i = 0; i < sizeWindow; i++) + { + fSum += pFWindow[i] = a0 - a1 * cos(fConst * i) + + a2 * cos(fConst * 2 * i) + a3 * cos(fConst * 3 * i); + } +} + +/*! \brief default BlackmanHarris window (70dB rolloff) + * + * \param sizeWindow the size of the window + * \param pFWindow pointer to an array that will hold the window + */ +void BlackmanHarris(int sizeWindow, sfloat *pFWindow) +{ + BlackmanHarris70(sizeWindow, pFWindow); +} + +/*! \brief Hamming window + * + * \param sizeWindow window size + * \param pWindow window array + */ +void Hamming(int sizeWindow, sfloat *pWindow) +{ + int i; + sfloat fSum = 0; + + for(i = 0; i < sizeWindow; i++) + { + fSum += pWindow[i] = 0.53836 - 0.46164*cos(TWO_PI*i/(sizeWindow-1)); + } +} + +/*! \brief Hanning window + * + * \param sizeWindow window size + * \param pWindow window array + */ +void Hanning(int sizeWindow, sfloat *pWindow) +{ + int i; + for(i = 0; i < sizeWindow; i++) + pWindow[i] = (sin(PI*i/(sizeWindow-1)))*(sin(PI*i/(sizeWindow-1))); +} + +/*! \brief main function for getting various windows + * + * \todo note on window scales + * + * \see SMS_WINDOWS for the different window types available + * \param sizeWindow window size + * \param pFWindow window array + * \param iWindowType the desired window type defined by #SMS_WINDOWS + */ +void sms_getWindow(int sizeWindow, sfloat *pFWindow, int iWindowType) +{ + switch(iWindowType) + { + case SMS_WIN_BH_62: + BlackmanHarris62(sizeWindow, pFWindow); + break; + case SMS_WIN_BH_70: + BlackmanHarris70(sizeWindow, pFWindow); + break; + case SMS_WIN_BH_74: + BlackmanHarris74(sizeWindow, pFWindow); + break; + case SMS_WIN_BH_92: + BlackmanHarris92(sizeWindow, pFWindow); + break; + case SMS_WIN_HAMMING: + Hamming(sizeWindow, pFWindow); + break; + case SMS_WIN_HANNING: + Hanning(sizeWindow, pFWindow); + break; + case SMS_WIN_IFFT: + IFFTwindow(sizeWindow, pFWindow); + break; + default: + BlackmanHarris(sizeWindow, pFWindow); + } +} + +/*! \brief apply a window and center around sample 0 + * + * function to center a waveform around sample 0, also known + * as 'zero-phase windowing'. Half the samples are at the beginning, + * half at the end, with the remaining samples (sizeFft-sizeWindow) + * in the middle (zero-padding for an interpolated spectrum). + * + * \todo do I need to garuntee that sizeWindow is odd-lengthed? + * + * \param sizeWindow size of waveform/waveform + * \param pWaveform pointer to waveform + * \param pWindow pointer to window + * \param sizeFft size of FFT + * \param pFftBuffer pointer to FFT buffer + */ +void sms_windowCentered(int sizeWindow, sfloat *pWaveform, sfloat *pWindow, + int sizeFft, sfloat *pFftBuffer) +{ + int iMiddleWindow = (sizeWindow+1) >> 1; + int iOffset = sizeFft - (iMiddleWindow - 1); + int i; + + for(i=0; i<iMiddleWindow-1; i++) + pFftBuffer[iOffset + i] = pWindow[i] * pWaveform[i]; + + iOffset = iMiddleWindow - 1; + + for(i=0; i<iMiddleWindow; i++) + pFftBuffer[i] = pWindow[iOffset + i] * pWaveform[iOffset + i]; +} |