Mercurial > hg > webaudioevaluationtool
view js/resample.js @ 2642:77204f78386a
#3: Added resampler from JS-xtract into resampler.js. Automatically use to match sample rates
author | Nicholas Jillings <nicholas.jillings@mail.bcu.ac.uk> |
---|---|
date | Thu, 19 Jan 2017 14:48:02 +0000 |
parents | |
children |
line wrap: on
line source
// Taken for JS-Xtract // https://github.com/nickjillings/js-xtract function xtract_resample(data, p, q, n) { // Same function call as matlab: // data is the array // p is the target sample rate // q is the source sample rate // n is the desired filter order, n==1024 if undefined function filter(N, c) { var c_b, Re, Im, b; c_b = Math.floor(c * N); Re = new Float64Array(N); Im = new Float64Array(N); var i, j; for (i = 0; i < c_b; i++) { Re[i] = 1; } for (i = N - c_b + 1; i < N; i++) { Re[i] = 1; } inverseTransform(Re, Im); // Scale and shift into Im for (i = 0; i < N; i++) { j = (i + (N >> 1)) % N; Im[i] = Re[j] / N; // Apply compute blackman-harris to Im var rad = (Math.PI * i) / (N); Im[i] *= 0.35875 - 0.48829 * Math.cos(2 * rad) + 0.14128 * Math.cos(4 * rad) - 0.01168 * Math.cos(6 * rad); } return Im; } function polyn(data, K) { var N = data.length; var x = [0, data[0], data[1]]; var dst = new Float64Array(K); var ratio = K / N; var tinc = 1 / ratio; var n = 0, t = 0, k; for (k = 0; k < K; k++) { if (t == n) { // Points lie on same time dst[k] = data[n]; } else { var y = (t - n - 1) * (t - n) * x[0] - 2 * (t - n - 1) * (t - n + 1) * x[1] + (t - n) * (t - n + 1) * x[2]; dst[k] = y / 2; } t += tinc; if (t >= n + 1) { n = Math.floor(t); x[0] = data[n - 1]; x[1] = data[n]; if (n + 1 < N) { x[2] = data[n + 1]; } else { x[2] = 0.0; } } } return dst; } function zp(a) { var b = new Float64Array(a.length * 2); for (var n = 0; n < a.length; n++) { b[n] = a[n]; } return b; } function overlap(X, b) { var i, f; var Y = new Float64Array(X.length); var N = b.length; var N2 = 2 * N; var B = { real: zp(b), imag: new Float64Array(N * 2) } transform(B.real, B.imag); var Xi = X.xtract_get_data_frames(N, N, false); var Yi = Y.xtract_get_data_frames(N, N, false); var x_last = new Float64Array(N); var y_last = new Float64Array(N); var w = new Float64Array(N2); for (i = 0; i < N2; i++) { var rad = (Math.PI * i) / (N2); w[i] = 0.35875 - 0.48829 * Math.cos(2 * rad) + 0.14128 * Math.cos(4 * rad) - 0.01168 * Math.cos(6 * rad); } var xF = { real: new Float64Array(N2), imag: new Float64Array(N2) } var yF = { real: new Float64Array(N2), imag: new Float64Array(N2) } for (f = 0; f < Xi.length; f++) { for (i = 0; i < N; i++) { xF.real[i] = x_last[i] * w[i]; xF.real[i + N] = Xi[f][i] * w[i + N]; x_last[i] = Xi[f][i]; xF.imag[i] = 0; xF.imag[i + N] = 0; } transform(xF.real, xF.imag); for (i = 0; i < N2; i++) { yF.real[i] = xF.real[i] * B.real[i] - xF.imag[i] * B.imag[i]; yF.imag[i] = xF.imag[i] * B.real[i] + xF.real[i] * B.imag[i]; } transform(yF.imag, yF.real); // Perform fft_shift and scale for (i = 0; i < N; i++) { var h = yF.real[i + N] / N; yF.real[i + N] = yF.real[i] / N; yF.real[i] = h; } for (i = 0; i < N; i++) { Yi[f][i] = (yF.real[i] + y_last[i]); y_last[i] = yF.real[i + N]; } } return Y; } // Determine which way to go var b, N = data.length; if (typeof n != "number" || n <= 0) { n = 512; } if (p == q) { return data; } var ratio = (p / q); var K = Math.floor(N * ratio); var dst; if (p > q) { // Upsampling // 1. Expand Data range dst = polyn(data, K); // 2. Filter out spurious energy above q var b = filter(n, 1 / ratio); overlap(data, b); } else { // Downsampling // 1. Filter out energy above p var b = filter(n, ratio / 2); var ds1 = overlap(data, b); // 2. Decrease data range dst = polyn(ds1, K); } return dst; }