/* * This program(except FFT and Bessel function part) is distributed under * LGPL. See LGPL.txt for details. But, if you make a new program with derived * code from this program,I strongly wish that my name and derived code are * indicated explicitly. */ package vavi.sound.pcm.resampling.ssrc; import java.io.*; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.util.Random; import vavi.util.I0Bessel; import vavi.util.SplitRadixFft; /** * Shibatch Sampling Rate Converter. * * @author <a href="shibatch@users.sourceforge.net">Naoki Shibata</a> * @author <a href="mailto:vavivavi@yahoo.co.jp">Naohide Sano</a> (nsano) * @author Maksim Khadkevich (a couple of minor changes) * @version 0.00 060127 nsano port to java version <br> */ public class SSRC { /** */ private ByteOrder byteOrder = ByteOrder.LITTLE_ENDIAN; /** */ private SplitRadixFft fft = new SplitRadixFft(); /** */ private static final String VERSION = "1.30"; /** */ private double AA = 150; /** */ private double DF = 200; /** */ private int FFTFIRLEN = 1; /** */ // private static final int M = 15; /** */ private static final int RANDBUFLEN = 65536; /** */ private int RINT(double x) { return ((x) >= 0 ? ((int) ((x) + 0.5)) : ((int) ((x) - 0.5))); } /** */ private static final int scoeffreq[] = { 0, 48000, 44100, 37800, 32000, 22050, 48000, 44100 }; /** */ private static final int scoeflen[] = { 1, 16, 20, 16, 16, 15, 16, 15 }; /** */ private static final int samp[] = { 8, 18, 27, 8, 8, 8, 10, 9 }; /** */ private static final double[][] shapercoefs = { {-1}, // triangular dither {-2.8720729351043701172, 5.0413231849670410156, -6.2442994117736816406, 5.8483986854553222656, -3.7067542076110839844, 1.0495119094848632812, 1.1830236911773681641, -2.1126792430877685547, 1.9094531536102294922, -0.99913084506988525391, 0.17090806365013122559, 0.32615602016448974609, -0.39127644896507263184, 0.26876461505889892578, -0.097676105797290802002, 0.023473845794796943665, }, // 48k, N=16, amp=18 {-2.6773197650909423828, 4.8308925628662109375, -6.570110321044921875, 7.4572014808654785156, -6.7263274192810058594, 4.8481650352478027344, -2.0412089824676513672, -0.7006359100341796875, 2.9537565708160400391, -4.0800385475158691406, 4.1845216751098632812, -3.3311812877655029297, 2.1179926395416259766, -0.879302978515625, 0.031759146600961685181, 0.42382788658142089844, -0.47882103919982910156, 0.35490813851356506348, -0.17496839165687561035, 0.060908168554306030273, }, // 44.1k, N=20, amp=27 {-1.6335992813110351562, 2.2615492343902587891, -2.4077029228210449219, 2.6341717243194580078, -2.1440362930297851562, 1.8153258562088012695, -1.0816224813461303711, 0.70302653312683105469, -0.15991993248462677002, -0.041549518704414367676, 0.29416576027870178223, -0.2518316805362701416, 0.27766478061676025391, -0.15785403549671173096, 0.10165894031524658203, -0.016833892092108726501, }, // 37.8k, N=16 {-0.82901298999786376953, 0.98922657966613769531, -0.59825712442398071289, 1.0028809309005737305, -0.59938216209411621094, 0.79502451419830322266, -0.42723315954208374023, 0.54492527246475219727, -0.30792605876922607422, 0.36871799826622009277, -0.18792048096656799316, 0.2261127084493637085, -0.10573341697454452515, 0.11435490846633911133, -0.038800679147243499756, 0.040842197835445404053, }, // 32k, N=16 {-0.065229974687099456787, 0.54981261491775512695, 0.40278548002243041992, 0.31783768534660339355, 0.28201797604560852051, 0.16985194385051727295, 0.15433363616466522217, 0.12507140636444091797, 0.08903945237398147583, 0.064410120248794555664, 0.047146003693342208862, 0.032805237919092178345, 0.028495194390416145325, 0.011695005930960178375, 0.011831838637590408325, }, // 22.05k, N=15 {-2.3925774097442626953, 3.4350297451019287109, -3.1853709220886230469, 1.8117271661758422852, 0.20124770700931549072, -1.4759907722473144531, 1.7210904359817504883, -0.97746700048446655273, 0.13790138065814971924, 0.38185903429985046387, -0.27421241998672485352, -0.066584214568138122559, 0.35223302245140075684, -0.37672343850135803223, 0.23964276909828186035, -0.068674825131893157959, }, // 48k, N=16, amp=10 {-2.0833916664123535156, 3.0418450832366943359, -3.2047898769378662109, 2.7571926116943359375, -1.4978630542755126953, 0.3427594602108001709, 0.71733748912811279297, -1.0737057924270629883, 1.0225815773010253906, -0.56649994850158691406, 0.20968692004680633545, 0.065378531813621520996, -0.10322438180446624756, 0.067442022264003753662, 0.00495197344571352005, }, // 44.1k, N=15, amp=9 }; /** */ private double[][] shapebuf; /** */ private int shaper_type, shaper_len, shaper_clipmin, shaper_clipmax; /** */ private double[] randbuf; /** */ private int randptr; /** */ private boolean quiet = false; /** */ private int lastshowed2; /** */ private long starttime, lastshowed; /** */ private static final int POOLSIZE = 97; /** */ public int init_shaper(int freq, int nch, int min, int max, int dtype, int pdf, double noiseamp) { int i; int[] pool = new int[POOLSIZE]; for (i = 1; i < 6; i++) { if (freq == scoeffreq[i]) { break; } } if ((dtype == 3 || dtype == 4) && i == 6) { System.err.printf("Warning: ATH based noise shaping for destination frequency %dHz is not available, using triangular dither\n", freq); } if (dtype == 2 || i == 6) { i = 0; } if (dtype == 4 && (i == 1 || i == 2)) { i += 5; } shaper_type = i; shapebuf = new double[nch][]; shaper_len = scoeflen[shaper_type]; for (i = 0; i < nch; i++) { shapebuf[i] = new double[shaper_len]; } shaper_clipmin = min; shaper_clipmax = max; randbuf = new double[RANDBUFLEN]; Random random = new Random(System.currentTimeMillis()); for (i = 0; i < POOLSIZE; i++) { pool[i] = random.nextInt(); } switch (pdf) { case 0: // rectangular for (i = 0; i < RANDBUFLEN; i++) { int r, p; p = random.nextInt() % POOLSIZE; r = pool[p]; pool[p] = random.nextInt(); randbuf[i] = noiseamp * (((double) r) / Integer.MAX_VALUE - 0.5); } break; case 1: // triangular for (i = 0; i < RANDBUFLEN; i++) { int r1, r2, p; p = random.nextInt() % POOLSIZE; r1 = pool[p]; pool[p] = random.nextInt(); p = random.nextInt() % POOLSIZE; r2 = pool[p]; pool[p] = random.nextInt(); randbuf[i] = noiseamp * ((((double) r1) / Integer.MAX_VALUE) - (((double) r2) / Integer.MAX_VALUE)); } break; case 2: // gaussian { int sw = 0; double t = 0, u = 0; for (i = 0; i < RANDBUFLEN; i++) { double r; int p; if (sw == 0) { sw = 1; p = random.nextInt() % POOLSIZE; r = ((double) pool[p]) / Integer.MAX_VALUE; pool[p] = random.nextInt(); if (r == 1.0) { r = 0.0; } t = Math.sqrt(-2 * Math.log(1 - r)); p = random.nextInt() % POOLSIZE; r = ((double) pool[p]) / Integer.MAX_VALUE; pool[p] = random.nextInt(); u = 2 * Math.PI * r; randbuf[i] = noiseamp * t * Math.cos(u); } else { sw = 0; randbuf[i] = noiseamp * t * Math.sin(u); } } } break; } randptr = 0; if (dtype == 0 || dtype == 1) { return 1; } return samp[shaper_type]; } /** */ public int do_shaping(double s, double[] peak, int dtype, int ch) { double u, h; int i; if (dtype == 1) { s += randbuf[randptr++ & (RANDBUFLEN - 1)]; if (s < shaper_clipmin) { double d = s / shaper_clipmin; peak[0] = peak[0] < d ? d : peak[0]; s = shaper_clipmin; } if (s > shaper_clipmax) { double d = s / shaper_clipmax; peak[0] = peak[0] < d ? d : peak[0]; s = shaper_clipmax; } return RINT(s); } h = 0; for (i = 0; i < shaper_len; i++) { h += shapercoefs[shaper_type][i] * shapebuf[ch][i]; } s += h; u = s; s += randbuf[randptr++ & (RANDBUFLEN - 1)]; for (i = shaper_len - 2; i >= 0; i--) { shapebuf[ch][i + 1] = shapebuf[ch][i]; } if (s < shaper_clipmin) { double d = s / shaper_clipmin; peak[0] = peak[0] < d ? d : peak[0]; s = shaper_clipmin; shapebuf[ch][0] = s - u; if (shapebuf[ch][0] > 1) { shapebuf[ch][0] = 1; } if (shapebuf[ch][0] < -1) { shapebuf[ch][0] = -1; } } else if (s > shaper_clipmax) { double d = s / shaper_clipmax; peak[0] = peak[0] < d ? d : peak[0]; s = shaper_clipmax; shapebuf[ch][0] = s - u; if (shapebuf[ch][0] > 1) { shapebuf[ch][0] = 1; } if (shapebuf[ch][0] < -1) { shapebuf[ch][0] = -1; } } else { s = RINT(s); shapebuf[ch][0] = s - u; } return (int) s; } /** */ private void quit_shaper(int nch) { } /** */ private double alpha(double a) { if (a <= 21) { return 0; } if (a <= 50) { return 0.5842 * Math.pow(a - 21, 0.4) + 0.07886 * (a - 21); } return 0.1102 * (a - 8.7); } /** */ private double win(double n, int len, double alp, double iza) { return I0Bessel.value(alp * Math.sqrt(1 - 4 * n * n / (((double) len - 1) * ((double) len - 1)))) / iza; } /** */ private double sinc(double x) { return x == 0 ? 1 : Math.sin(x) / x; } /** */ private double hn_lpf(int n, double lpf, double fs) { double t = 1 / fs; double omega = 2 * Math.PI * lpf; return 2 * lpf * t * sinc(n * omega * t); } /** */ private void usage() { System.err.printf("http://shibatch.sourceforge.net/\n\n"); System.err.printf("usage: ssrc [<options>] <source wav file> <destination wav file>\n"); System.err.printf("options : --rate <sampling rate> output sample rate\n"); System.err.printf(" --att <attenuation(dB)> attenuate signal\n"); System.err.printf(" --bits <number of bits> output quantization bit length\n"); System.err.printf(" --tmpfile <file name> specify temporal file\n"); System.err.printf(" --twopass two pass processing to avoid clipping\n"); System.err.printf(" --normalize normalize the wave file\n"); System.err.printf(" --quiet nothing displayed except error\n"); System.err.printf(" --dither [<type>] dithering\n"); System.err.printf(" 0 : no dither\n"); System.err.printf(" 1 : no noise shaping\n"); System.err.printf(" 2 : triangular spectral shape\n"); System.err.printf(" 3 : ATH based noise shaping\n"); System.err.printf(" 4 : less dither amplitude than type 3\n"); System.err.printf(" --pdf <type> [<amp>] select p.d.f. of noise\n"); System.err.printf(" 0 : rectangular\n"); System.err.printf(" 1 : triangular\n"); System.err.printf(" 2 : Gaussian\n"); System.err.printf(" --profile <type> specify profile\n"); System.err.printf(" standard : the default quality\n"); System.err.printf(" fast : fast, not so bad quality\n"); } /** */ private void fmterr(int x) { throw new IllegalStateException("unknown error " + x); } /** */ private void setstarttime() { starttime = System.currentTimeMillis(); lastshowed = 0; lastshowed2 = -1; } /** */ private void showprogress(double p) { int eta, pc; long t; if (quiet) { return; } t = System.currentTimeMillis() - starttime; if (p == 0) { eta = 0; } else { eta = (int) (t * (1 - p) / p); } pc = (int) (p * 100); if (pc != lastshowed2 || t != lastshowed) { System.err.printf(" %3d%% processed", pc); lastshowed2 = pc; } if (t != lastshowed) { System.err.printf(", ETA =%4dmsec", eta); lastshowed = t; } System.err.printf("\r"); System.err.flush(); } /** */ private int gcd(int x, int y) { int t; while (y != 0) { t = x % y; x = y; y = t; } return x; } /** * @param fpi * @param fpo * @param nch * @param bps * @param dbps sizeof(double)? * @param sfrq * @param dfrq * @param gain * @param chanklen * @param twopass * @param dither * @return * @throws IOException */ public double upsample(InputStream fpi, OutputStream fpo, int nch, int bps, int dbps, int sfrq, int dfrq, double gain, int chanklen, boolean twopass, int dither) throws IOException { int frqgcd, osf = 0, fs1, fs2; double[][] stage1; double[] stage2; int n1, n1x, n1y, n2, n2b; int filter2len; int[] f1order, f1inc; int[] fft_ip = null; double[] fft_w = null; ByteBuffer rawinbuf, rawoutbuf; double[] inbuf, outbuf; double[][] buf1, buf2; double[] peak = new double[]{0}; int spcount = 0; int i, j; // System.err.println("upsample"); filter2len = FFTFIRLEN; // stage 2 filter length // Make stage 1 filter { double aa = AA; // stop band attenuation(dB) double lpf, d, df, alp, iza; // double delta; double guard = 2; frqgcd = gcd(sfrq, dfrq); fs1 = sfrq / frqgcd * dfrq; if (fs1 / dfrq == 1) { osf = 1; } else if (fs1 / dfrq % 2 == 0) { osf = 2; } else if (fs1 / dfrq % 3 == 0) { osf = 3; } else { throw new IllegalArgumentException( String.format("Resampling from %dHz to %dHz is not supported.\n" + "%d/gcd(%d,%d)=%d must be divided by 2 or 3.\n", sfrq, dfrq, sfrq, sfrq, dfrq, fs1 / dfrq)); } df = (dfrq * osf / 2 - sfrq / 2) * 2 / guard; lpf = sfrq / 2 + (dfrq * osf / 2 - sfrq / 2) / guard; // delta = Math.pow(10, -aa / 20); if (aa <= 21) { d = 0.9222; } else { d = (aa - 7.95) / 14.36; } n1 = (int) (fs1 / df * d + 1); if (n1 % 2 == 0) { n1++; } alp = alpha(aa); iza = I0Bessel.value(alp); // System.err.printf("iza = %g\n",iza); n1y = fs1 / sfrq; n1x = n1 / n1y + 1; f1order = new int[n1y * osf]; for (i = 0; i < n1y * osf; i++) { f1order[i] = fs1 / sfrq - (i * (fs1 / (dfrq * osf))) % (fs1 / sfrq); if (f1order[i] == fs1 / sfrq) { f1order[i] = 0; } } f1inc = new int[n1y * osf]; for (i = 0; i < n1y * osf; i++) { f1inc[i] = f1order[i] < fs1 / (dfrq * osf) ? nch : 0; if (f1order[i] == fs1 / sfrq) { f1order[i] = 0; } } stage1 = new double[n1y][n1x]; for (i = -(n1 / 2); i <= n1 / 2; i++) { stage1[(i + n1 / 2) % n1y][(i + n1 / 2) / n1y] = win(i, n1, alp, iza) * hn_lpf(i, lpf, fs1) * fs1 / sfrq; } } // Make stage 2 filter { double aa = AA; // stop band attenuation(dB) double lpf, d, df, alp, iza; // double delta; int ipsize, wsize; // delta = Math.pow(10, -aa / 20); if (aa <= 21) { d = 0.9222; } else { d = (aa - 7.95) / 14.36; } fs2 = dfrq * osf; for (i = 1; ; i = i * 2) { n2 = filter2len * i; if (n2 % 2 == 0) { n2--; } df = (fs2 * d) / (n2 - 1); lpf = sfrq / 2; if (df < DF) { break; } } alp = alpha(aa); iza = I0Bessel.value(alp); for (n2b = 1; n2b < n2; n2b *= 2) { } n2b *= 2; stage2 = new double[n2b]; for (i = -(n2 / 2); i <= n2 / 2; i++) { stage2[i + n2 / 2] = win(i, n2, alp, iza) * hn_lpf(i, lpf, fs2) / n2b * 2; } ipsize = (int) (2 + Math.sqrt(n2b)); fft_ip = new int[ipsize]; fft_ip[0] = 0; wsize = n2b / 2; fft_w = new double[wsize]; fft.rdft(n2b, 1, stage2, fft_ip, fft_w); } // Apply filters setstarttime(); { int n2b2 = n2b / 2; // inbuffs1Tv??? int rp; // disposesfrqTv? int ds; // ?t@Cinbuf?lvZ stage2 filternTv? int nsmplwrt1; // ?t@Cinbuf?lvZ stage2 filternTv? int nsmplwrt2 = 0; // stage1 filter?oTv?n1y*osf] int s1p; boolean init; boolean ending; int sumread, sumwrite; int osc; int ip, ip_backup; int s1p_backup, osc_backup; int ch, p; int inbuflen; int delay = 0; buf1 = new double[nch][n2b2 / osf + 1]; buf2 = new double[nch][n2b]; rawinbuf = ByteBuffer.allocate(nch * (n2b2 + n1x + 2) * bps); // ,bps rawoutbuf = ByteBuffer.allocate(nch * (n2b2 / osf + 1) * dbps); // ,dbps inbuf = new double[nch * (n2b2 + n1x + 2)]; outbuf = new double[nch * (n2b2 / osf + 1)]; s1p = 0; rp = 0; ds = 0; osc = 0; init = true; ending = false; inbuflen = n1 / 2 / (fs1 / sfrq) + 1; delay = (int) ((double) n2 / 2 / (fs2 / dfrq)); sumread = sumwrite = 0; while (true) { int nsmplread, toberead, toberead2; toberead2 = toberead = (int) (Math.ceil((double) n2b2 * sfrq / (dfrq * osf)) + 1 + n1x - inbuflen); if (toberead + sumread > chanklen) { toberead = chanklen - sumread; } rawinbuf.position(0); rawinbuf.limit(bps * nch * toberead); // rawinbuf.limit(bps * nch * toberead); byte[] tempData = new byte[rawinbuf.limit()]; nsmplread = fpi.read(tempData); if (nsmplread < 0) { nsmplread = 0; } if (nsmplread < rawinbuf.limit()) { chanklen = sumread + nsmplread / bps * nch; } rawinbuf.limit(nsmplread); rawinbuf = ByteBuffer.wrap(tempData); rawinbuf.position(nsmplread); rawinbuf.flip(); nsmplread /= bps * nch; switch (bps) { case 1: for (i = 0; i < nsmplread * nch; i++) inbuf[nch * inbuflen + i] = (1 / (double) 0x7f) * ((double) rawinbuf.get(i) - 128); break; case 2: for (i = 0; i < nsmplread * nch; i++) { int v = rawinbuf.order(byteOrder).asShortBuffer().get(i); inbuf[nch * inbuflen + i] = (1 / (double) 0x7fff) * v; } break; case 3: for (i = 0; i < nsmplread * nch; i++) { inbuf[nch * inbuflen + i] = (1 / (double) 0x7fffff) * ((rawinbuf.get(i * 3) << 0) | (rawinbuf.get(i * 3 + 1) << 8) | (rawinbuf.get(i * 3 + 2) << 16)); } break; case 4: for (i = 0; i < nsmplread * nch; i++) { int v = rawinbuf.order(byteOrder).asIntBuffer().get(i); inbuf[nch * inbuflen + i] = (1 / (double) 0x7fffffff) * v; } break; } for (; i < nch * toberead2; i++) { inbuf[nch * inbuflen + i] = 0; } inbuflen += toberead2; sumread += nsmplread; ending = sumread >= chanklen; // ending = fpi.available() == 0 || sumread >= chanklen; // nsmplwrt1 = ((rp - 1) * sfrq / fs1 + inbuflen - n1x) * dfrq * osf / sfrq; // if (nsmplwrt1 > n2b2) { nsmplwrt1 = n2b2; } nsmplwrt1 = n2b2; // apply stage 1 filter ip = ((sfrq * (rp - 1) + fs1) / fs1) * nch; // inbuf s1p_backup = s1p; ip_backup = ip; osc_backup = osc; for (ch = 0; ch < nch; ch++) { int op = ch; // outbuf // int fdo = fs1 / (dfrq * osf); int no = n1y * osf; s1p = s1p_backup; ip = ip_backup + ch; switch (n1x) { case 7: for (p = 0; p < nsmplwrt1; p++) { int s1o = f1order[s1p]; buf2[ch][p] = stage1[s1o][0] * inbuf[ip + 0 * nch] + stage1[s1o][1] * inbuf[ip + 1 * nch] + stage1[s1o][2] * inbuf[ip + 2 * nch] + stage1[s1o][3] * inbuf[ip + 3 * nch] + stage1[s1o][4] * inbuf[ip + 4 * nch] + stage1[s1o][5] * inbuf[ip + 5 * nch] + stage1[s1o][6] * inbuf[ip + 6 * nch]; ip += f1inc[s1p]; s1p++; if (s1p == no) { s1p = 0; } } break; case 9: for (p = 0; p < nsmplwrt1; p++) { int s1o = f1order[s1p]; buf2[ch][p] = stage1[s1o][0] * inbuf[ip + 0 * nch] + stage1[s1o][1] * inbuf[ip + 1 * nch] + stage1[s1o][2] * inbuf[ip + 2 * nch] + stage1[s1o][3] * inbuf[ip + 3 * nch] + stage1[s1o][4] * inbuf[ip + 4 * nch] + stage1[s1o][5] * inbuf[ip + 5 * nch] + stage1[s1o][6] * inbuf[ip + 6 * nch] + stage1[s1o][7] * inbuf[ip + 7 * nch] + stage1[s1o][8] * inbuf[ip + 8 * nch]; ip += f1inc[s1p]; s1p++; if (s1p == no) { s1p = 0; } } break; default: for (p = 0; p < nsmplwrt1; p++) { double tmp = 0; int ip2 = ip; int s1o = f1order[s1p]; for (i = 0; i < n1x; i++) { tmp += stage1[s1o][i] * inbuf[ip2]; ip2 += nch; } buf2[ch][p] = tmp; ip += f1inc[s1p]; s1p++; if (s1p == no) { s1p = 0; } } break; } osc = osc_backup; // apply stage 2 filter for (p = nsmplwrt1; p < n2b; p++) { buf2[ch][p] = 0; } //for(i=0;i<n2b2;i++) { System.err.printf("%d:%g",i,buf2[ch][i]); } fft.rdft(n2b, 1, buf2[ch], fft_ip, fft_w); buf2[ch][0] = stage2[0] * buf2[ch][0]; buf2[ch][1] = stage2[1] * buf2[ch][1]; for (i = 1; i < n2b / 2; i++) { double re, im; re = stage2[i * 2] * buf2[ch][i * 2] - stage2[i * 2 + 1] * buf2[ch][i * 2 + 1]; im = stage2[i * 2 + 1] * buf2[ch][i * 2] + stage2[i * 2] * buf2[ch][i * 2 + 1]; //System.err.printf("%d : %g %g %g %g %g %g\n",i,stage2[i*2],stage2[i*2+1],buf2[ch][i*2],buf2[ch][i*2+1],re,im); buf2[ch][i * 2] = re; buf2[ch][i * 2 + 1] = im; } fft.rdft(n2b, -1, buf2[ch], fft_ip, fft_w); for (i = osc, j = 0; i < n2b2; i += osf, j++) { double f = (buf1[ch][j] + buf2[ch][i]); outbuf[op + j * nch] = f; } nsmplwrt2 = j; osc = i - n2b2; for (j = 0; i < n2b; i += osf, j++) { buf1[ch][j] = buf2[ch][i]; } } rp += nsmplwrt1 * (sfrq / frqgcd) / osf; rawoutbuf.clear(); if (twopass) { for (i = 0; i < nsmplwrt2 * nch; i++) { double f = outbuf[i] > 0 ? outbuf[i] : -outbuf[i]; peak[0] = peak[0] < f ? f : peak[0]; rawoutbuf.asDoubleBuffer().put(i, outbuf[i]); } } else { switch (dbps) { case 1: { double gain2 = gain * 0x7f; ch = 0; for (i = 0; i < nsmplwrt2 * nch; i++) { int s; if (dither != 0) { s = do_shaping(outbuf[i] * gain2, peak, dither, ch); } else { s = RINT(outbuf[i] * gain2); if (s < -0x80) { double d = (double) s / -0x80; peak[0] = peak[0] < d ? d : peak[0]; s = -0x80; } if (0x7f < s) { double d = (double) s / 0x7f; peak[0] = peak[0] < d ? d : peak[0]; s = 0x7f; } } rawoutbuf.put(i, (byte) (s + 0x80)); ch++; if (ch == nch) { ch = 0; } } } break; case 2: { double gain2 = gain * 0x7fff; ch = 0; for (i = 0; i < nsmplwrt2 * nch; i++) { int s; if (dither != 0) { s = do_shaping(outbuf[i] * gain2, peak, dither, ch); } else { s = RINT(outbuf[i] * gain2); if (s < -0x8000) { double d = (double) s / -0x8000; peak[0] = peak[0] < d ? d : peak[0]; s = -0x8000; } if (0x7fff < s) { double d = (double) s / 0x7fff; peak[0] = peak[0] < d ? d : peak[0]; s = 0x7fff; } } rawoutbuf.order(byteOrder).asShortBuffer().put(i, (short) s); ch++; if (ch == nch) { ch = 0; } } } break; case 3: { double gain2 = gain * 0x7fffff; ch = 0; for (i = 0; i < nsmplwrt2 * nch; i++) { int s; if (dither != 0) { s = do_shaping(outbuf[i] * gain2, peak, dither, ch); } else { s = RINT(outbuf[i] * gain2); if (s < -0x800000) { double d = (double) s / -0x800000; peak[0] = peak[0] < d ? d : peak[0]; s = -0x800000; } if (0x7fffff < s) { double d = (double) s / 0x7fffff; peak[0] = peak[0] < d ? d : peak[0]; s = 0x7fffff; } } rawoutbuf.put(i * 3, (byte) (s & 255)); s >>= 8; rawoutbuf.put(i * 3 + 1, (byte) (s & 255)); s >>= 8; rawoutbuf.put(i * 3 + 2, (byte) (s & 255)); ch++; if (ch == nch) { ch = 0; } } } break; } } if (!init) { if (ending) { if ((double) sumread * dfrq / sfrq + 2 > sumwrite + nsmplwrt2) { rawoutbuf.position(0); rawoutbuf.limit(dbps * nch * nsmplwrt2); writeBuffers(fpo, rawoutbuf); sumwrite += nsmplwrt2; } else { rawoutbuf.position(0); int limitData = (int) (dbps * nch * (Math.floor((double) sumread * dfrq / sfrq) + 2 - sumwrite)); if (limitData > 0) { rawoutbuf.limit(limitData); writeBuffers(fpo, rawoutbuf); } break; } } else { rawoutbuf.position(0); rawoutbuf.limit(dbps * nch * nsmplwrt2); writeBuffers(fpo, rawoutbuf); sumwrite += nsmplwrt2; } } else { if (nsmplwrt2 < delay) { delay -= nsmplwrt2; } else { if (ending) { if ((double) sumread * dfrq / sfrq + 2 > sumwrite + nsmplwrt2 - delay) { rawoutbuf.position(dbps * nch * delay); rawoutbuf.limit(dbps * nch * nsmplwrt2); writeBuffers(fpo, rawoutbuf); sumwrite += nsmplwrt2 - delay; } else { rawoutbuf.position(dbps * nch * delay); rawoutbuf.limit((int) (dbps * nch * (Math.floor((double) sumread * dfrq / sfrq) + 2 - sumwrite))); writeBuffers(fpo, rawoutbuf); break; } } else { rawoutbuf.position(dbps * nch * delay); rawoutbuf.limit(dbps * nch * (nsmplwrt2)); writeBuffers(fpo, rawoutbuf); sumwrite += nsmplwrt2 - delay; init = false; } } } { ds = (rp - 1) / (fs1 / sfrq); assert (inbuflen >= ds); System.arraycopy(inbuf, nch * ds, inbuf, 0, nch * (inbuflen - ds)); // memmove TODO overlap inbuflen -= ds; rp -= ds * (fs1 / sfrq); } if ((spcount++ & 7) == 7) { showprogress((double) sumread / chanklen); } } } showprogress(1); return peak[0]; } /** */ public double downsample(InputStream fpi, OutputStream fpo, int nch, int bps, int dbps, int sfrq, int dfrq, double gain, int chanklen, boolean twopass, int dither) throws IOException { int frqgcd, osf = 0, fs1, fs2; double[] stage1; double[][] stage2; int n2, n2x, n2y, n1, n1b; int filter1len; int[] f2order, f2inc; int[] fft_ip = null; double[] fft_w = null; ByteBuffer rawinbuf, rawoutbuf; double[] inbuf, outbuf; double[][] buf1, buf2; int i, j; int spcount = 0; double[] peak = new double[]{0}; // System.err.println("downsample"); filter1len = FFTFIRLEN; // stage 1 filter length // Make stage 1 filter { double aa = AA; // stop band attenuation(dB) double lpf, d, df, alp, iza; // double delta; int ipsize, wsize; frqgcd = gcd(sfrq, dfrq); if (dfrq / frqgcd == 1) { osf = 1; } else if (dfrq / frqgcd % 2 == 0) { osf = 2; } else if (dfrq / frqgcd % 3 == 0) { osf = 3; } else { throw new IllegalArgumentException( String.format("Resampling from %dHz to %dHz is not supported.\n" + "%d/gcd(%d,%d)=%d must be divided by 2 or 3.", sfrq, dfrq, dfrq, sfrq, dfrq, dfrq / frqgcd)); } fs1 = sfrq * osf; // delta = Math.pow(10, -aa / 20); if (aa <= 21) { d = 0.9222; } else { d = (aa - 7.95) / 14.36; } n1 = filter1len; for (i = 1; ; i = i * 2) { n1 = filter1len * i; if (n1 % 2 == 0) { n1--; } df = (fs1 * d) / (n1 - 1); lpf = (dfrq - df) / 2; if (df < DF) { break; } } alp = alpha(aa); iza = I0Bessel.value(alp); //System.err.printf("iza %f, alp: %f\n", iza, alp); // OK for (n1b = 1; n1b < n1; n1b *= 2) { } n1b *= 2; stage1 = new double[n1b]; for (i = -(n1 / 2); i <= n1 / 2; i++) { stage1[i + n1 / 2] = win(i, n1, alp, iza) * hn_lpf(i, lpf, fs1) * fs1 / sfrq / n1b * 2; //System.err.printf("1: %06d: %e\n", i + n1 / 2, stage1[i + n1 / 2]); // OK } ipsize = (int) (2 + Math.sqrt(n1b)); fft_ip = new int[ipsize]; fft_ip[0] = 0; wsize = n1b / 2; fft_w = new double[wsize]; fft.rdft(n1b, 1, stage1, fft_ip, fft_w); //for (i = -(n1 / 2); i <= n1 / 2; i++) { // System.err.printf("1': %06d: %e\n", i + n1 / 2, stage1[i + n1 / 2]); //} //for (i = 0; i < ipsize; i++) { // System.err.printf("ip: %06d: %d\n", i, fft_ip[i]); // OK //} //for (i = 0; i < wsize; i++) { // System.err.printf("w: %06d: %e\n", i, fft_w[i]); // OK //} } // Make stage 2 filter if (osf == 1) { fs2 = sfrq / frqgcd * dfrq; n2 = 1; n2y = n2x = 1; f2order = new int[n2y]; f2order[0] = 0; f2inc = new int[n2y]; f2inc[0] = sfrq / dfrq; stage2 = new double[n2y][n2x]; stage2[0][0] = 1; } else { double aa = AA; // stop band attenuation(dB) double lpf, d, df, alp, iza; // double delta; double guard = 2; fs2 = sfrq / frqgcd * dfrq; df = (fs1 / 2 - sfrq / 2) * 2 / guard; lpf = sfrq / 2 + (fs1 / 2 - sfrq / 2) / guard; // delta = Math.pow(10, -aa / 20); if (aa <= 21) { d = 0.9222; } else { d = (aa - 7.95) / 14.36; } n2 = (int) (fs2 / df * d + 1); if (n2 % 2 == 0) { n2++; } alp = alpha(aa); iza = I0Bessel.value(alp); //System.err.printf("iza %f, alp: %f\n", iza, alp); // OK n2y = fs2 / fs1; // 0Tvfs2Tv?H n2x = n2 / n2y + 1; f2order = new int[n2y]; for (i = 0; i < n2y; i++) { f2order[i] = fs2 / fs1 - (i * (fs2 / dfrq)) % (fs2 / fs1); if (f2order[i] == fs2 / fs1) { f2order[i] = 0; } } f2inc = new int[n2y]; for (i = 0; i < n2y; i++) { f2inc[i] = (fs2 / dfrq - f2order[i]) / (fs2 / fs1) + 1; if (f2order[i + 1 == n2y ? 0 : i + 1] == 0) { f2inc[i]--; } } stage2 = new double[n2y][n2x]; //System.err.printf("n2y: %d, n2: %d\n", n2y, n2); for (i = -(n2 / 2); i <= n2 / 2; i++) { stage2[(i + n2 / 2) % n2y][(i + n2 / 2) / n2y] = win(i, n2, alp, iza) * hn_lpf(i, lpf, fs2) * fs2 / fs1; //System.err.printf(" stage2[%02d][%02d]: %f\n", (i + n2 / 2) % n2y, (i + n2 / 2) / n2y, win(i, n2, alp, iza) * hn_lpf(i, lpf, fs2) * fs2 / fs1); // OK } } // Apply filters setstarttime(); { int n1b2 = n1b / 2; int rp; // inbuffs1Tv??? int rps; // rp(fs1/sfrq=osf)] int rp2; // buf2fs2Tv??? int ds; // disposesfrqTv? // ?t@Cinbuf?lvZ stage2 filternTv? // int nsmplwrt1; // ?t@Cinbuf?lvZ stage2 filternTv? int nsmplwrt2 = 0; int s2p; // stage1 filter?oTv?n1y*osf] boolean init, ending; // int osc; int bp; // rp2vZ?Dbuf2Tvu int rps_backup, s2p_backup; int k, ch, p; int inbuflen = 0; int sumread, sumwrite; int delay = 0; int op; // |....B....|....C....| buf1 n1b2+n1b2 // |.A.|....D....| buf2 n2x+n1b2 // // inbufBosf{TvORs?[ // CNA // BCstage 1 filter // DB // ADstage 2 filter // DA // CDRs?[ buf1 = new double[nch][n1b]; //rawoutbuf = calloc(nch*(n2b2/osf+1),dbps); buf2 = new double[nch][n2x + 1 + n1b2]; rawinbuf = ByteBuffer.allocate((nch * (n1b2 / osf + osf + 1)) * bps); //System.err.println((double) n1b2 * sfrq / dfrq + 1); rawoutbuf = ByteBuffer.allocate((int) (((double) n1b2 * dfrq / sfrq + 1) * (dbps * nch))); inbuf = new double[nch * (n1b2 / osf + osf + 1)]; outbuf = new double[(int) (nch * ((double) n1b2 * dfrq / sfrq + 1))]; op = 0; // outbuf s2p = 0; rp = 0; rps = 0; ds = 0; // osc = 0; rp2 = 0; init = true; ending = false; delay = (int) ((double) n1 / 2 / ((double) fs1 / dfrq) + (double) n2 / 2 / ((double) fs2 / dfrq)); sumread = sumwrite = 0; while (true) { int nsmplread; int toberead; rps = 0; //TODO settings this parameter to zero fixed a lot of problems toberead = (n1b2 - rps - 1) / osf + 1; if (toberead + sumread > chanklen) { toberead = chanklen - sumread; } rawinbuf.position(0); rawinbuf.limit(bps * nch * toberead); byte[] tempData = new byte[rawinbuf.limit()]; nsmplread = fpi.read(tempData); if (nsmplread < 0) { nsmplread = 0; } //TODO sometimes happens, investigate around it if (nsmplread < rawinbuf.limit()) { chanklen = sumread + nsmplread / bps * nch; } rawinbuf.limit(nsmplread); rawinbuf = ByteBuffer.wrap(tempData); rawinbuf.position(nsmplread); rawinbuf.flip(); nsmplread /= bps * nch; switch (bps) { case 1: for (i = 0; i < nsmplread * nch; i++) { inbuf[nch * inbuflen + i] = (1 / (double) 0x7f) * ((rawinbuf.get(i) & 0xff) - 128); } break; case 2: for (i = 0; i < nsmplread * nch; i++) { int v = rawinbuf.order(byteOrder).asShortBuffer().get(i); inbuf[nch * inbuflen + i] = (1 / (double) 0x7fff) * v; // System.err.printf("I: %f\n", inbuf[nch * inbuflen + i]); } break; case 3: for (i = 0; i < nsmplread * nch; i++) { inbuf[nch * inbuflen + i] = (1 / (double) 0x7fffff) * (((rawinbuf.get(i * 3) & 0xff) << 0) | ((rawinbuf.get(i * 3 + 1) & 0xff) << 8) | ((rawinbuf.get(i * 3 + 2) & 0xff) << 16)); } break; case 4: for (i = 0; i < nsmplread * nch; i++) { int v = rawinbuf.order(byteOrder).getInt(i); inbuf[nch * inbuflen + i] = (1 / (double) 0x7fffffff) * v; } break; } for (; i < nch * toberead; i++) { inbuf[i] = 0; } sumread += nsmplread; // ending = sumread >= chanklen; ending = fpi.available() < 0 || sumread >= chanklen; rps_backup = rps; s2p_backup = s2p; for (ch = 0; ch < nch; ch++) { rps = rps_backup; for (k = 0; k < rps; k++) { buf1[ch][k] = 0; } for (i = rps, j = 0; i < n1b2; i += osf, j++) { assert (j < ((n1b2 - rps - 1) / osf + 1)); buf1[ch][i] = inbuf[j * nch + ch]; for (k = i + 1; k < i + osf; k++) { buf1[ch][k] = 0; } } assert (j == ((n1b2 - rps - 1) / osf + 1)); for (k = n1b2; k < n1b; k++) { buf1[ch][k] = 0; } rps = i - n1b2; rp += j; fft.rdft(n1b, 1, buf1[ch], fft_ip, fft_w); buf1[ch][0] = stage1[0] * buf1[ch][0]; buf1[ch][1] = stage1[1] * buf1[ch][1]; for (i = 1; i < n1b2; i++) { double re, im; re = stage1[i * 2] * buf1[ch][i * 2] - stage1[i * 2 + 1] * buf1[ch][i * 2 + 1]; im = stage1[i * 2 + 1] * buf1[ch][i * 2] + stage1[i * 2] * buf1[ch][i * 2 + 1]; buf1[ch][i * 2] = re; buf1[ch][i * 2 + 1] = im; } fft.rdft(n1b, -1, buf1[ch], fft_ip, fft_w); for (i = 0; i < n1b2; i++) { buf2[ch][n2x + 1 + i] += buf1[ch][i]; } { int t1 = rp2 / (fs2 / fs1); if (rp2 % (fs2 / fs1) != 0) { t1++; } bp = buf2[0].length * ch + t1; // &(buf2[ch][t1]); } s2p = s2p_backup; for (p = 0; bp - (buf2[0].length * ch) < n1b2 + 1; p++) { // buf2[ch] double tmp = 0; int bp2; int s2o; bp2 = bp; s2o = f2order[s2p]; bp += f2inc[s2p]; s2p++; if (s2p == n2y) { s2p = 0; } assert ((bp2 - (buf2[0].length * ch)) * (fs2 / fs1) - (rp2 + p * (fs2 / dfrq)) == s2o); // &(buf2[ch][0]) for (i = 0; i < n2x; i++) { //System.err.printf("%d (%d, %d)\n", i, bp2 / buf2[0].length, bp2 % buf2[0].length); tmp += stage2[s2o][i] * buf2[bp2 / buf2[0].length][bp2 % buf2[0].length]; // *bp2++ bp2++; } outbuf[op + p * nch + ch] = tmp; //System.err.printf("O: %06d: %f\n", op + p * nch + ch, tmp); } nsmplwrt2 = p; } rp2 += nsmplwrt2 * (fs2 / dfrq); rawoutbuf.clear(); if (twopass) { for (i = 0; i < nsmplwrt2 * nch; i++) { double f = outbuf[i] > 0 ? outbuf[i] : -outbuf[i]; peak[0] = peak[0] < f ? f : peak[0]; //System.err.println("p: " + rawoutbuf.position() + ", l: " + rawoutbuf.limit()); rawoutbuf.asDoubleBuffer().put(i, outbuf[i]); //if (i < 100) { // System.err.printf("1: %06d: %f\n", i, outbuf[i]); //} //System.err.print(StringUtil.getDump(rawoutbuf, i, 8)); } } else { switch (dbps) { case 1: { double gain2 = gain * 0x7f; ch = 0; for (i = 0; i < nsmplwrt2 * nch; i++) { int s; if (dither != 0) { s = do_shaping(outbuf[i] * gain2, peak, dither, ch); } else { s = RINT(outbuf[i] * gain2); if (s < -0x80) { double d = (double) s / -0x80; peak[0] = peak[0] < d ? d : peak[0]; s = -0x80; } if (0x7f < s) { double d = (double) s / 0x7f; peak[0] = peak[0] < d ? d : peak[0]; s = 0x7f; } } rawoutbuf.put(i, (byte) (s + 0x80)); ch++; if (ch == nch) { ch = 0; } } } break; case 2: { double gain2 = gain * 0x7fff; ch = 0; for (i = 0; i < nsmplwrt2 * nch; i++) { int s; if (dither != 0) { s = do_shaping(outbuf[i] * gain2, peak, dither, ch); } else { s = RINT(outbuf[i] * gain2); if (s < -0x8000) { double d = (double) s / -0x8000; peak[0] = peak[0] < d ? d : peak[0]; s = -0x8000; } if (0x7fff < s) { double d = (double) s / 0x7fff; peak[0] = peak[0] < d ? d : peak[0]; s = 0x7fff; } } rawoutbuf.order(byteOrder).asShortBuffer().put(i, (short) s); ch++; if (ch == nch) { ch = 0; } } } break; case 3: { double gain2 = gain * 0x7fffff; ch = 0; for (i = 0; i < nsmplwrt2 * nch; i++) { int s; if (dither != 0) { s = do_shaping(outbuf[i] * gain2, peak, dither, ch); } else { s = RINT(outbuf[i] * gain2); if (s < -0x800000) { double d = (double) s / -0x800000; peak[0] = peak[0] < d ? d : peak[0]; s = -0x800000; } if (0x7fffff < s) { double d = (double) s / 0x7fffff; peak[0] = peak[0] < d ? d : peak[0]; s = 0x7fffff; } } rawoutbuf.put(i * 3, (byte) (s & 255)); s >>= 8; rawoutbuf.put(i * 3 + 1, (byte) (s & 255)); s >>= 8; rawoutbuf.put(i * 3 + 2, (byte) (s & 255)); ch++; if (ch == nch) { ch = 0; } } } break; } } if (!init) { if (ending) { if ((double) sumread * dfrq / sfrq + 2 > sumwrite + nsmplwrt2) { rawoutbuf.position(0); rawoutbuf.limit(dbps * nch * nsmplwrt2); writeBuffers(fpo, rawoutbuf); sumwrite += nsmplwrt2; } else { rawoutbuf.position(0); int limitData = (int) (dbps * nch * (Math.floor((double) sumread * dfrq / sfrq) + 2 - sumwrite)); if (limitData > 0) { rawoutbuf.limit(limitData); writeBuffers(fpo, rawoutbuf); } break; } } else { rawoutbuf.position(0); rawoutbuf.limit(dbps * nch * nsmplwrt2); writeBuffers(fpo, rawoutbuf); sumwrite += nsmplwrt2; } } else { if (nsmplwrt2 < delay) { delay -= nsmplwrt2; } else { if (ending) { if ((double) sumread * dfrq / sfrq + 2 > sumwrite + nsmplwrt2 - delay) { rawoutbuf.position(dbps * nch * delay); rawoutbuf.limit(dbps * nch * (nsmplwrt2 - delay)); writeBuffers(fpo, rawoutbuf); sumwrite += nsmplwrt2 - delay; } else { rawoutbuf.position(dbps * nch * delay); rawoutbuf.limit((int) (dbps * nch * (Math.floor((double) sumread * dfrq / sfrq) + 2 + sumwrite + nsmplwrt2 - delay))); //TODO fails with short signals (think that fixed this) writeBuffers(fpo, rawoutbuf); break; } } else { rawoutbuf.position(dbps * nch * delay); rawoutbuf.limit(dbps * nch * (nsmplwrt2)); writeBuffers(fpo, rawoutbuf); sumwrite += nsmplwrt2 - delay; init = false; } } } { ds = (rp2 - 1) / (fs2 / fs1); if (ds > n1b2) { ds = n1b2; } for (ch = 0; ch < nch; ch++) { System.arraycopy(buf2[ch], ds, buf2[ch], 0, n2x + 1 + n1b2 - ds); // memmove TODO overlap } rp2 -= ds * (fs2 / fs1); } for (ch = 0; ch < nch; ch++) { System.arraycopy(buf1[ch], n1b2, buf2[ch], n2x + 1, n1b2); } if ((spcount++ & 7) == 7) { showprogress((double) sumread / chanklen); } } } showprogress(1); return peak[0]; } /** */ public double no_src(InputStream fpi, OutputStream fpo, int nch, int bps, int dbps, double gain, int chanklen, boolean twopass, int dither) throws IOException { double[] peak = new double[]{ 0 }; int ch = 0, sumread = 0; setstarttime(); ByteBuffer leos = null; if (twopass) { leos = ByteBuffer.allocate(8); } ByteBuffer buf = ByteBuffer.allocate(4); while (sumread < chanklen * nch) { double f = 0; int s; switch (bps) { case 1: buf.position(0); buf.limit(1); byte[] tempData = new byte[buf.limit()]; fpi.read(tempData); buf = ByteBuffer.wrap(tempData); buf.position(buf.limit()); buf.flip(); f = (1 / (double) 0x7f) * (buf.get(0) - 128); break; case 2: buf.position(0); buf.limit(2); tempData = new byte[buf.limit()]; fpi.read(tempData); buf = ByteBuffer.wrap(tempData); buf.position(buf.limit()); buf.flip(); s = buf.order(byteOrder).asShortBuffer().get(0); f = (1 / (double) 0x7fff) * s; break; case 3: buf.position(0); buf.limit(3); tempData = new byte[buf.limit()]; fpi.read(tempData); buf = ByteBuffer.wrap(tempData); buf.position(buf.limit()); buf.flip(); f = (1 / (double) 0x7fffff) * (((buf.get(0) & 0xff) << 0) | ((buf.get(1) & 0xff) << 8) | ((buf.get(2) & 0xff) << 16)); break; case 4: buf.position(0); buf.limit(4); tempData = new byte[buf.limit()]; fpi.read(tempData); buf = ByteBuffer.wrap(tempData); buf.position(buf.limit()); buf.flip(); s = buf.order(byteOrder).asIntBuffer().get(0); f = (1 / (double) 0x7fffffff) * s; break; } ; if (fpi.available() == 0) { // if (fpi.position() == fpi.size()) { break; } f *= gain; if (!twopass) { switch (dbps) { case 1: f *= 0x7f; s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f); buf.position(0); buf.limit(1); buf.put(0, (byte) (s + 128)); buf.flip(); writeBuffers(fpo, buf); break; case 2: f *= 0x7fff; s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f); buf.position(0); buf.limit(2); buf.asShortBuffer().put(0, (short) s); buf.flip(); writeBuffers(fpo, buf); break; case 3: f *= 0x7fffff; s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f); buf.position(0); buf.limit(3); buf.put(0, (byte) (s & 255)); s >>= 8; buf.put(1, (byte) (s & 255)); s >>= 8; buf.put(2, (byte) (s & 255)); buf.flip(); writeBuffers(fpo, buf); break; } } else { double p = f > 0 ? f : -f; peak[0] = peak[0] < p ? p : peak[0]; leos.position(0); leos.putDouble(f); leos.flip(); writeBuffers(fpo, leos); } ch++; if (ch == nch) { ch = 0; } sumread++; if ((sumread & 0x3ffff) == 0) { showprogress((double) sumread / (chanklen * nch)); } } showprogress(1); return peak[0]; } /** */ public static void main(String[] args) throws Exception { new SSRC(args); } /** */ private static final double presets[] = { 0.7, 0.9, 0.18 }; public SSRC(){} /** */ SSRC(String[] argv) throws IOException { String sfn, dfn, tmpfn = null; FileInputStream fpi = null; File fo = null; FileOutputStream fpo = null; File ft = null; FileOutputStream fpto = null; boolean twopass, normalize; int dither, pdf, samp = 0; int nch, bps; int length; int sfrq, dfrq, dbps; double att, noiseamp; double[] peak = new double[]{0}; int i; // parse command line options dfrq = -1; att = 0; dbps = -1; twopass = false; normalize = false; dither = 0; pdf = 0; noiseamp = 0.18; for (i = 0; i < argv.length; i++) { if (argv[i].charAt(0) != '-') { break; } if (argv[i].equals("--rate")) { dfrq = Integer.parseInt(argv[++i]); //System.err.printf("dfrq: %d\n", dfrq); continue; } if (argv[i].equals("--att")) { att = Float.parseFloat(argv[++i]); continue; } if (argv[i].equals("--bits")) { dbps = Integer.parseInt(argv[++i]); if (dbps != 8 && dbps != 16 && dbps != 24) { throw new IllegalArgumentException("Error: Only 8bit, 16bit and 24bit PCM are supported."); } dbps /= 8; continue; } if (argv[i].equals("--twopass")) { twopass = true; continue; } if (argv[i].equals("--normalize")) { twopass = true; normalize = true; continue; } if (argv[i].equals("--dither")) { try { dither = Integer.parseInt(argv[i + 1]); if (dither < 0 || dither > 4) { throw new IllegalArgumentException("unrecognized dither type : " + argv[i + 1]); } i++; } catch (NumberFormatException e) { dither = -1; } continue; } if (argv[i].equals("--pdf")) { try { pdf = Integer.parseInt(argv[i + 1]); if (pdf < 0 || pdf > 2) { throw new IllegalArgumentException("unrecognized p.d.f. type : " + argv[i + 1]); } i++; } catch (NumberFormatException e) { throw new IllegalArgumentException("unrecognized p.d.f. type : " + argv[i + 1]); } try { noiseamp = Double.parseDouble(argv[i + 1]); i++; } catch (NumberFormatException e) { noiseamp = presets[pdf]; } continue; } if (argv[i].equals("--quiet")) { quiet = true; continue; } if (argv[i].equals("--tmpfile")) { tmpfn = argv[++i]; continue; } if (argv[i].equals("--profile")) { if (argv[i + 1].equals("fast")) { AA = 96; DF = 8000; FFTFIRLEN = 1024; } else if (argv[i + 1].equals("standard")) { /* nothing to do */ } else { throw new IllegalArgumentException("unrecognized profile : " + argv[i + 1]); } i++; continue; } throw new IllegalArgumentException("unrecognized option : " + argv[i]); } if (!quiet) { System.err.printf("Shibatch sampling rate converter version " + VERSION + "(high precision/nio)\n\n"); } if (argv.length - i != 2) { usage(); throw new IllegalStateException("too few arguments"); } sfn = argv[i]; dfn = argv[i + 1]; try { fpi = new FileInputStream(sfn); } catch (IOException e) { throw new IllegalArgumentException("cannot open input file."); } // read wav header { @SuppressWarnings("unused") short word; @SuppressWarnings("unused") int dword; ByteBuffer bb = ByteBuffer.allocate(256).order(ByteOrder.LITTLE_ENDIAN); bb.limit(36); fpi.getChannel().read(bb); bb.flip(); System.err.println("p: " + bb.position() + ", l: " + bb.limit()); if (bb.get() != 'R') fmterr(1); if (bb.get() != 'I') fmterr(1); if (bb.get() != 'F') fmterr(1); if (bb.get() != 'F') fmterr(1); dword = bb.getInt(); if (bb.get() != 'W') fmterr(2); if (bb.get() != 'A') fmterr(2); if (bb.get() != 'V') fmterr(2); if (bb.get() != 'E') fmterr(2); if (bb.get() != 'f') fmterr(2); if (bb.get() != 'm') fmterr(2); if (bb.get() != 't') fmterr(2); if (bb.get() != ' ') fmterr(2); int sizeOfFmt = bb.getInt(); if (bb.getShort() != 1) { throw new IllegalStateException("Error: Only PCM is supported."); } nch = bb.getShort(); sfrq = bb.getInt(); bps = bb.getInt(); if (bps % sfrq * nch != 0) { fmterr(4); } word = bb.getShort(); word = bb.getShort(); bps /= sfrq * nch; if (sizeOfFmt > 16) { bb.position(0); bb.limit(2); fpi.read(getDataFromByteBuffer(bb)); bb.flip(); int sizeofExtended = bb.getShort(); fpi.getChannel().position(fpi.getChannel().position() + sizeofExtended); } while (true) { bb.position(0); bb.limit(8); fpi.getChannel().read(bb); bb.flip(); int c0 = bb.get(); int c1 = bb.get(); int c2 = bb.get(); int c3 = bb.get(); length = bb.getInt(); System.err.printf("chunk: %c%c%c%c\n", c0, c1, c2, c3); if (c0 == 'd' && c1 == 'a' && c2 == 't' && c3 == 'a') { break; } if (fpi.getChannel().position() == fpi.getChannel().size()) { break; } fpi.getChannel().position(fpi.getChannel().position() + length); } if (fpi.getChannel().position() == fpi.getChannel().size()) { throw new IllegalStateException("Couldn't find data chank"); } } if (bps != 1 && bps != 2 && bps != 3 && bps != 4) { throw new IllegalStateException("Error : Only 8bit, 16bit, 24bit and 32bit PCM are supported."); } if (dbps == -1) { if (bps != 1) { dbps = bps; } else { dbps = 2; } if (dbps == 4) { dbps = 3; } } if (dfrq == -1) { dfrq = sfrq; } if (dither == -1) { if (dbps < bps) { if (dbps == 1) { dither = 4; } else { dither = 3; } } else { dither = 1; } } if (!quiet) { final String[] dtype = { "none", "no noise shaping", "triangular spectral shape", "ATH based noise shaping", "ATH based noise shaping(less amplitude)" }; final String[] ptype = { "rectangular", "triangular", "gaussian" }; System.err.printf("frequency : %d -> %d\n", sfrq, dfrq); System.err.printf("attenuation : %gdB\n", att); System.err.printf("bits per sample : %d -> %d\n", bps * 8, dbps * 8); System.err.printf("nchannels : %d\n", nch); System.err.printf("length : %d bytes, %g secs\n", length, (double) length / bps / nch / sfrq); if (dither == 0) { System.err.printf("dither type : none\n"); } else { System.err.printf("dither type : %s, %s p.d.f, amp = %g\n", dtype[dither], ptype[pdf], noiseamp); } System.err.printf("\n"); } if (twopass) { } try { fo = new File(dfn); fpo = new FileOutputStream(fo); } catch (IOException e) { throw new IllegalArgumentException("cannot open output file."); } // generate wav header { short word; int dword; ByteBuffer leos = ByteBuffer.allocate(44).order(ByteOrder.LITTLE_ENDIAN); leos.put("RIFF".getBytes()); dword = 0; leos.putInt(dword); leos.put("WAVEfmt ".getBytes()); dword = 16; leos.putInt(dword); word = 1; leos.putShort(word); // inAudioFormat category, PCM word = (short) nch; leos.putShort(word); // channels dword = dfrq; leos.putInt(dword); // sampling rate dword = dfrq * nch * dbps; leos.putInt(dword); // bytes per sec word = (short) (dbps * nch); leos.putShort(word); // block alignment word = (short) (dbps * 8); leos.putShort(word); // bits per sample leos.put("data".getBytes()); dword = 0; leos.putInt(dword); leos.flip(); writeBuffers(fpo, leos); } if (dither != 0) { int min = 0, max = 0; if (dbps == 1) { min = -0x80; max = 0x7f; } if (dbps == 2) { min = -0x8000; max = 0x7fff; } if (dbps == 3) { min = -0x800000; max = 0x7fffff; } if (dbps == 4) { min = -0x80000000; max = 0x7fffffff; } samp = init_shaper(dfrq, nch, min, max, dither, pdf, noiseamp); } if (twopass) { double gain = 0; int ch = 0; int fptlen, sumread; if (!quiet) { System.err.printf("Pass 1\n"); } try { if (tmpfn != null) { ft = new File(tmpfn); } else { ft = File.createTempFile("ssrc_", ".tmp"); } fpto = new FileOutputStream(ft); } catch (IOException e) { throw new IllegalStateException("cannot open temporary file."); } //System.err.printf("nch: %d, bps: %d, size: %d, sfrq: %d, dfrq: %d, ???: %d, ???: %d, twopass: %b, dither: %d\n", nch, bps, 8, sfrq, dfrq, 1, length / bps / nch, twopass, dither); if (normalize) { if (sfrq < dfrq) { peak[0] = upsample(fpi, fpto, nch, bps, 8, sfrq, dfrq, 1, length / bps / nch, twopass, dither); } else if (sfrq > dfrq) { peak[0] = downsample(fpi, fpto, nch, bps, 8, sfrq, dfrq, 1, length / bps / nch, twopass, dither); } else { peak[0] = no_src(fpi, fpto, nch, bps, 8, 1, length / bps / nch, twopass, dither); } } else { if (sfrq < dfrq) { peak[0] = upsample(fpi, fpto, nch, bps, 8, sfrq, dfrq, Math.pow(10, -att / 20), length / bps / nch, twopass, dither); } else if (sfrq > dfrq) { peak[0] = downsample(fpi, fpto, nch, bps, 8, sfrq, dfrq, Math.pow(10, -att / 20), length / bps / nch, twopass, dither); } else { peak[0] = no_src(fpi, fpto, nch, bps, 8, Math.pow(10, -att / 20), length / bps / nch, twopass, dither); } } fpto.close(); if (!quiet) { System.err.printf("\npeak : %gdB\n", 20 * Math.log10(peak[0])); } if (!normalize) { if (peak[0] < Math.pow(10, -att / 20)) { peak[0] = 1; } else { peak[0] *= Math.pow(10, att / 20); } } else { peak[0] *= Math.pow(10, att / 20); } if (!quiet) { System.err.printf("\nPass 2\n"); } if (dither != 0) { switch (dbps) { case 1: gain = (normalize || peak[0] >= (0x7f - samp) / (double) 0x7f) ? 1 / peak[0] * (0x7f - samp) : 1 / peak[0] * 0x7f; break; case 2: gain = (normalize || peak[0] >= (0x7fff - samp) / (double) 0x7fff) ? 1 / peak[0] * (0x7fff - samp) : 1 / peak[0] * 0x7fff; break; case 3: gain = (normalize || peak[0] >= (0x7fffff - samp) / (double) 0x7fffff) ? 1 / peak[0] * (0x7fffff - samp) : 1 / peak[0] * 0x7fffff; break; } } else { switch (dbps) { case 1: gain = 1 / peak[0] * 0x7f; break; case 2: gain = 1 / peak[0] * 0x7fff; break; case 3: gain = 1 / peak[0] * 0x7fffff; break; } } randptr = 0; setstarttime(); fptlen = (int) (ft.length() / 8); //System.err.println("tmp: " + fpt.getFilePointer()); FileChannel fpti = new FileInputStream(ft).getChannel(); ByteBuffer leis = ByteBuffer.allocate(8); for (sumread = 0; sumread < fptlen; ) { double f; int s; leis.clear(); fpti.read(leis); leis.flip(); f = leis.getDouble(); //if (sumread < 100) { // System.err.printf("2: %06d: %f\n", sumread, f); //} f *= gain; sumread++; switch (dbps) { case 1: { s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f); ByteBuffer buf = ByteBuffer.allocate(1); buf.put((byte) (s + 128)); buf.flip(); writeBuffers(fpo, buf); } break; case 2: { s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f); ByteBuffer buf = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN); buf.putShort((short) s); buf.flip(); writeBuffers(fpo, buf); } break; case 3: { s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f); ByteBuffer buf = ByteBuffer.allocate(3); buf.put((byte) (s & 255)); s >>= 8; buf.put((byte) (s & 255)); s >>= 8; buf.put((byte) (s & 255)); buf.flip(); writeBuffers(fpo, buf); } break; } ch++; if (ch == nch) { ch = 0; } if ((sumread & 0x3ffff) == 0) { showprogress((double) sumread / fptlen); } } showprogress(1); if (!quiet) { System.err.printf("\n"); } fpti.close(); if (ft != null) { //System.err.println("ft: " + ft); if (ft.delete() == false) { System.err.printf("Failed to remove %s\n", ft); } } } else { if (sfrq < dfrq) { peak[0] = upsample(fpi, fpo, nch, bps, dbps, sfrq, dfrq, Math.pow(10, -att / 20), length / bps / nch, twopass, dither); } else if (sfrq > dfrq) { peak[0] = downsample(fpi, fpo, nch, bps, dbps, sfrq, dfrq, Math.pow(10, -att / 20), length / bps / nch, twopass, dither); } else { peak[0] = no_src(fpi, fpo, nch, bps, dbps, Math.pow(10, -att / 20), length / bps / nch, twopass, dither); } if (!quiet) { System.err.printf("\n"); } } if (dither != 0) { quit_shaper(nch); } if (!twopass && peak[0] > 1) { if (!quiet) { System.err.printf("clipping detected : %gdB\n", 20 * Math.log10(peak[0])); } } { int dword; int len; fpo.close(); fo = new File(dfn); len = (int) fo.length(); FileChannel fpo1 = new RandomAccessFile(fo, "rw").getChannel(); ByteBuffer leos = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN); dword = len - 8; leos.position(0); leos.limit(4); leos.putInt(dword); leos.flip(); fpo1.write(leos, 4); dword = len - 44; leos.position(0); leos.limit(4); leos.putInt(dword); leos.flip(); fpo1.write(leos, 40); fpo1.close(); } } /** */ public SSRC(InputStream fpi, OutputStream fpo, int sfrq, int dfrq, int bps, int dbps, int nch, int length, double att, int dither, boolean quiet_) throws IOException { String tmpfn = null; boolean twopass, normalize; int pdf, samp = 0; double noiseamp; double[] peak = new double[]{0}; int i; // parse command line options twopass = false; normalize = false; pdf = 0; noiseamp = 0.18; if (dither < 0 || dither > 4) { throw new IllegalArgumentException("unrecognized dither type : " + dither); } this.quiet = quiet_; if (!quiet) { System.err.printf("Shibatch sampling rate converter version " + VERSION + "(high precision/nio)\n\n"); } if (bps != 1 && bps != 2 && bps != 3 && bps != 4) { throw new IllegalStateException("Error : Only 8bit, 16bit, 24bit and 32bit PCM are supported."); } if (dbps == -1) { if (bps != 1) { dbps = bps; } else { dbps = 2; } if (dbps == 4) { dbps = 3; } } if (dfrq == -1) { dfrq = sfrq; } if (dither == -1) { if (dbps < bps) { if (dbps == 1) { dither = 4; } else { dither = 3; } } else { dither = 1; } } if (!quiet) { final String[] dtype = { "none", "no noise shaping", "triangular spectral shape", "ATH based noise shaping", "ATH based noise shaping(less amplitude)" }; final String[] ptype = { "rectangular", "triangular", "gaussian" }; System.err.printf("frequency : %d -> %d\n", sfrq, dfrq); System.err.printf("attenuation : %gdB\n", att); System.err.printf("bits per sample : %d -> %d\n", bps * 8, dbps * 8); System.err.printf("nchannels : %d\n", nch); System.err.printf("length : %d bytes, %g secs\n", length, (double) length / bps / nch / sfrq); if (dither == 0) { System.err.printf("dither type : none\n"); } else { System.err.printf("dither type : %s, %s p.d.f, amp = %g\n", dtype[dither], ptype[pdf], noiseamp); } System.err.printf("\n"); } if (dither != 0) { int min = 0, max = 0; if (dbps == 1) { min = -0x80; max = 0x7f; } if (dbps == 2) { min = -0x8000; max = 0x7fff; } if (dbps == 3) { min = -0x800000; max = 0x7fffff; } if (dbps == 4) { min = -0x80000000; max = 0x7fffffff; } samp = init_shaper(dfrq, nch, min, max, dither, pdf, noiseamp); } if (sfrq < dfrq) { peak[0] = upsample(fpi, fpo, nch, bps, dbps, sfrq, dfrq, Math.pow(10, -att / 20), length / bps / nch, twopass, dither); } else if (sfrq > dfrq) { peak[0] = downsample(fpi, fpo, nch, bps, dbps, sfrq, dfrq, Math.pow(10, -att / 20), length / bps / nch, twopass, dither); } else { peak[0] = no_src(fpi, fpo, nch, bps, dbps, Math.pow(10, -att / 20), length / bps / nch, twopass, dither); } if (!quiet) { System.err.printf("\n"); } if (dither != 0) { quit_shaper(nch); } if (!twopass && peak[0] > 1) { if (!quiet) { System.err.printf("clipping detected : %gdB\n", 20 * Math.log10(peak[0])); } } } protected byte[] getDataFromByteBuffer(ByteBuffer rawoutbuf) { byte[] tempDataWrt = new byte[rawoutbuf.limit() - rawoutbuf.position()]; rawoutbuf.get(tempDataWrt, 0, tempDataWrt.length); return tempDataWrt; } protected void writeBuffers(OutputStream fpo, ByteBuffer rawoutbuf) { try { fpo.write(getDataFromByteBuffer(rawoutbuf)); } catch (IOException e) { // Some problems (Read end dead) } } } /* */