From 927eb3e1fff460ed6ecafe7b1dcf09e1c0dc1405 Mon Sep 17 00:00:00 2001 From: Matt Jenkins Date: Tue, 15 Jan 2019 22:43:02 +0000 Subject: [PATCH] Implemented new effect chain system instead of EQ --- .../jeq/core/EqualizerInputStream.java | 484 ---------------- src/davaguine/jeq/core/IIR.java | 292 ---------- src/davaguine/jeq/core/IIRBase.java | 486 ---------------- src/davaguine/jeq/core/IIRCoefficients.java | 36 -- src/davaguine/jeq/core/IIRControls.java | 214 ------- src/davaguine/jeq/core/XYData.java | 53 -- .../jeq/spi/EqualizerInputStream.java | 265 --------- .../majenko/audiobookrecorder/Amplifier.java | 45 ++ .../audiobookrecorder/AudiobookRecorder.java | 412 +++++++++----- .../co/majenko/audiobookrecorder/Biquad.java | 88 ++- src/uk/co/majenko/audiobookrecorder/Book.java | 33 +- .../audiobookrecorder/BookTreeRenderer.java | 4 - .../co/majenko/audiobookrecorder/Chapter.java | 23 +- .../majenko/audiobookrecorder/DelayLine.java | 66 +++ .../audiobookrecorder/DelayLineStore.java | 81 +++ .../co/majenko/audiobookrecorder/Effect.java | 11 + .../audiobookrecorder/EffectGroup.java | 72 +++ .../majenko/audiobookrecorder/Equaliser.java | 106 ---- .../audiobookrecorder/EqualiserChannel.java | 96 ---- .../audiobookrecorder/MainToolBar.java | 4 +- .../co/majenko/audiobookrecorder/Options.java | 123 +++-- .../co/majenko/audiobookrecorder/Sample.java | 28 + .../majenko/audiobookrecorder/Sentence.java | 520 +++++++----------- .../majenko/audiobookrecorder/Waveform.java | 32 +- 24 files changed, 1012 insertions(+), 2562 deletions(-) delete mode 100644 src/davaguine/jeq/core/EqualizerInputStream.java delete mode 100644 src/davaguine/jeq/core/IIR.java delete mode 100644 src/davaguine/jeq/core/IIRBase.java delete mode 100644 src/davaguine/jeq/core/IIRCoefficients.java delete mode 100644 src/davaguine/jeq/core/IIRControls.java delete mode 100644 src/davaguine/jeq/core/XYData.java delete mode 100644 src/davaguine/jeq/spi/EqualizerInputStream.java create mode 100644 src/uk/co/majenko/audiobookrecorder/Amplifier.java create mode 100644 src/uk/co/majenko/audiobookrecorder/DelayLine.java create mode 100644 src/uk/co/majenko/audiobookrecorder/DelayLineStore.java create mode 100644 src/uk/co/majenko/audiobookrecorder/Effect.java create mode 100644 src/uk/co/majenko/audiobookrecorder/EffectGroup.java delete mode 100644 src/uk/co/majenko/audiobookrecorder/Equaliser.java delete mode 100644 src/uk/co/majenko/audiobookrecorder/EqualiserChannel.java create mode 100644 src/uk/co/majenko/audiobookrecorder/Sample.java diff --git a/src/davaguine/jeq/core/EqualizerInputStream.java b/src/davaguine/jeq/core/EqualizerInputStream.java deleted file mode 100644 index 2519607..0000000 --- a/src/davaguine/jeq/core/EqualizerInputStream.java +++ /dev/null @@ -1,484 +0,0 @@ -/* - * 21.04.2004 Original verion. davagin@udm.ru. - *----------------------------------------------------------------------- - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - *---------------------------------------------------------------------- - */ -package davaguine.jeq.core; - -import java.io.IOException; -import java.io.InputStream; - -/** - * The EqualizerInputStream class - * Author: Dmitry Vaguine - * Date: 02.05.2004 - * Time: 12:00:29 - */ -public class EqualizerInputStream extends InputStream { - private InputStream stream; - private IIR iir; - - private final static int BUFFER_SIZE = 65536; - private byte[] inbuf = new byte[BUFFER_SIZE]; - private int[] workbuf = new int[BUFFER_SIZE]; - private byte[] outbuf = new byte[BUFFER_SIZE]; - private int inpos = 0; - private int inlen = 0; - private int outpos = 0; - private int outlen = 0; - - private boolean signed; - private int samplesize; - private boolean bigendian; - - /** - * Constructs new EqualizerInputStream object - * - * @param stream is an input stream for pcm data - * @param samplerate is a sample rate of input data - * @param channels is the number of channels - * @param signed indicates that the data is signed - * @param samplesize is the sample bit size of data - * @param bigendian indicates that the dat is in "big endian" encoding - * @param bands is the number of bands - */ - public EqualizerInputStream(InputStream stream, float samplerate, int channels, boolean signed, int samplesize, boolean bigendian, int bands) { - this.stream = stream; - this.iir = new IIR(bands, samplerate, channels); - this.signed = signed; - this.samplesize = samplesize; - this.bigendian = bigendian; - - if (!isParamsSupported(samplerate, channels, samplesize, bands)) - throw new IllegalArgumentException("Unsupported sample bit size"); - } - - /** - * Returns Controls of equalizer - * - * @return Controls of equalizer - */ - public IIRControls getControls() { - return iir.getControls(); - } - - /** - * This is special method for checking of supported parameters of equalizer - * - * @param bands is the number of bands - * @param samplerate is the sample rate of data - * @param channels is the number of channels - * @param samplesize is the size of sample in bits - * @return true if parameters are supported - */ - public static boolean isParamsSupported(float samplerate, int channels, int samplesize, int bands) { - switch (samplesize) { - case 8: - case 16: - case 24: - case 32: - break; - default: - return false; - } - - return IIR.isParamsSupported(bands, samplerate, channels); - } - - private boolean fillInBuffer() throws IOException { - if (inpos != 0 && inlen > 0) - System.arraycopy(inbuf, inpos, inbuf, 0, inlen); - inpos = 0; - int num; - boolean eof = false; - while (inlen != inbuf.length) { - num = stream.read(inbuf, inlen, inbuf.length - inlen); - if (num < 0) { - eof = true; - break; - } - inlen += num; - } - return eof; - } - - private void fillOutBuffer() { - if (outpos != 0 && outlen > 0) - System.arraycopy(outbuf, outpos, outbuf, 0, outlen); - outpos = 0; - int len = outbuf.length - outlen; - len = inlen < len ? inlen : len; - len = convertToInt(len); - if (len > 0) { - iir.iir(workbuf, len); - len = convertToByte(outbuf, outlen, len); - outlen += len; - } - } - - private int convertToInt(int length) { - int l = length; - int temp; - byte a1[]; - switch (samplesize) { - case 8: { - if (length > 0) { - System.arraycopy(inbuf, 0, workbuf, 0, length); - inpos += length; - inlen -= length; - } - break; - } - case 16: { - l = length >> 1; - if (l > 0) { - if (bigendian) - for (int i = 0; i < l; i++) { - temp = (((a1 = inbuf)[inpos++] & 0xff) << 8) | (a1[inpos++] & 0xff); - workbuf[i] = signed && temp > 32767 ? temp - 65536 : temp; - } - else - for (int i = 0; i < l; i++) { - temp = ((a1 = inbuf)[inpos++] & 0xff) | ((a1[inpos++] & 0xff) << 8); - workbuf[i] = signed && temp > 32767 ? temp - 65536 : temp; - } - inlen -= inpos; - } - break; - } - case 24: { - l = length / 3; - if (l > 0) { - if (bigendian) - for (int i = 0; i < l; i++) { - temp = ((a1 = inbuf)[inpos++] & 0xff) | ((a1[inpos++] & 0xff) << 8) | ((a1[inpos++] & 0xff) << 16); - workbuf[i] = signed && temp > 8388607 ? temp - 16777216 : temp; - } - else - for (int i = 0; i < l; i++) { - temp = (((a1 = inbuf)[inpos++] & 0xff) << 16) | ((a1[inpos++] & 0xff) << 8) | (a1[inpos++] & 0xff); - workbuf[i] = signed && temp > 8388607 ? temp - 16777216 : temp; - } - inlen -= inpos; - } - break; - } - case 32: { - l = length / 4; - if (l > 0) { - if (bigendian) - for (int i = 0; i < l; i++) { - temp = ((a1 = inbuf)[inpos++] & 0xff) | ((a1[inpos++] & 0xff) << 8) | ((a1[inpos++] & 0xff) << 16) | ((a1[inpos++] & 0xff) << 24); - workbuf[i] = temp; //signed && temp > 8388607 ? temp - 16777216 : temp; - } - else - for (int i = 0; i < l; i++) { - temp = (((a1 = inbuf)[inpos++] & 0xff) << 24) | ((a1[inpos++] & 0xff) << 16) | ((a1[inpos++] & 0xff) << 8) | (a1[inpos++] & 0xff); - workbuf[i] = temp; //signed && temp > 8388607 ? temp - 16777216 : temp; - } - inlen -= inpos; - } - break; - } - } - return l; - } - - private int wrap8Bit(int data) { - if (data > 127) - data = 127; - else if (data < -128) - data = -128; - if (data < 0) - data += 256; - return data; - } - - private int wrap16Bit(int data) { - if (data > 32767) - data = 32767; - else if (data < -32768) - data = -32768; - if (data < 0) - data += 65536; - return data; - } - - private int wrap24Bit(int data) { - if (data > 8388607) - data = 8388607; - else if (data < -8388608) - data = -8388608; - if (data < 0) - data += 16777216; - return data; - } - - private int convertToByte(byte[] b, int off, int length) { - int p = off; - int d; - switch (samplesize) { - case 8: { - for (int i = 0; i < length; i++) - b[p++] = (byte) (wrap8Bit(workbuf[i]) & 0xff); - break; - } - case 16: { - if (bigendian) { - for (int i = 0; i < length; i++) { - d = wrap16Bit(workbuf[i]); - b[p++] = (byte) ((d & 0xff00) >> 8); - b[p++] = (byte) (d & 0xff); - } - } else { - for (int i = 0; i < length; i++) { - d = wrap16Bit(workbuf[i]); - b[p++] = (byte) (d & 0xff); - b[p++] = (byte) ((d & 0xff00) >> 8); - } - } - break; - } - case 24: { - if (bigendian) { - for (int i = 0; i < length; i++) { - d = wrap24Bit(workbuf[i]); - b[p++] = (byte) (d & 0xff); - b[p++] = (byte) ((d & 0xff00) >> 8); - b[p++] = (byte) ((d & 0xff0000) >> 16); - } - } else { - for (int i = 0; i < length; i++) { - d = wrap24Bit(workbuf[i]); - b[p++] = (byte) ((d & 0xff0000) >> 16); - b[p++] = (byte) ((d & 0xff00) >> 8); - b[p++] = (byte) (d & 0xff); - } - } - break; - } - case 32: { - if (bigendian) { - for (int i = 0; i < length; i++) { - d = workbuf[i]; - b[p++] = (byte) (d & 0xff); - b[p++] = (byte) ((d & 0xff00) >> 8); - b[p++] = (byte) ((d & 0xff0000) >> 16); - b[p++] = (byte) ((d & 0xff000000) >> 24); - } - } else { - for (int i = 0; i < length; i++) { - d = wrap24Bit(workbuf[i]); - b[p++] = (byte) ((d & 0xff000000) >> 24); - b[p++] = (byte) ((d & 0xff0000) >> 16); - b[p++] = (byte) ((d & 0xff00) >> 8); - b[p++] = (byte) (d & 0xff); - } - } - break; - } - - } - return p - off; - } - - /** - * Returns the number of bytes that can be read (or skipped over) from - * this input stream without blocking by the next caller of a method for - * this input stream. The next caller might be the same thread or - * another thread. - * - * @return the number of bytes that can be read from this input stream - * without blocking. - * @throws IOException if an I/O error occurs. - */ - public int available() throws IOException { - return outlen; - } - - /** - * Closes this input stream and releases any system resources associated - * with the stream. - * - * @throws IOException if an I/O error occurs. - */ - public void close() throws IOException { - stream.close(); - } - - /** - *

The mark method of EqualizerInputStream does - * nothing. - * - * @param readlimit the maximum limit of bytes that can be read before - * the mark position becomes invalid. - */ - public synchronized void mark(int readlimit) { - } - - /** - * Tests if this input stream supports the mark and - * reset methods. Whether or not mark and - * reset are supported is an invariant property of a - * particular input stream instance. The markSupported method - * of EqualizerInputStream returns false. - * - * @return false - */ - public boolean markSupported() { - return false; - } - - /** - * Reads the next byte of data from the input stream. The value byte is - * returned as an int in the range 0 to - * 255. If no byte is available because the end of the stream - * has been reached, the value -1 is returned. This method - * blocks until input data is available, the end of the stream is detected, - * or an exception is thrown. - * - * @return the next byte of data, or -1 if the end of the - * stream is reached. - * @throws IOException if an I/O error occurs. - */ - public int read() throws IOException { - if (outlen == 0) { - boolean eof = fillInBuffer(); - fillOutBuffer(); - if (outlen == 0 && eof) - return -1; - if (outlen == 0 && !eof) - throw new IOException("Impossible state"); - } - int b = outbuf[outpos++]; - outlen--; - return b; - } - - /** - * Reads up to len bytes of data from the input stream into - * an array of bytes. An attempt is made to read as many as - * len bytes, but a smaller number may be read. - * The number of bytes actually read is returned as an integer. - *

- *

This method blocks until input data is available, end of file is - * detected, or an exception is thrown. - *

- *

If b is null, a - * NullPointerException is thrown. - *

- *

If off is negative, or len is negative, or - * off+len is greater than the length of the array - * b, then an IndexOutOfBoundsException is - * thrown. - *

- *

If len is zero, then no bytes are read and - * 0 is returned; otherwise, there is an attempt to read at - * least one byte. If no byte is available because the stream is at end of - * file, the value -1 is returned; otherwise, at least one - * byte is read and stored into b. - *

- *

The first byte read is stored into element b[off], the - * next one into b[off+1], and so on. The number of bytes read - * is, at most, equal to len. Let k be the number of - * bytes actually read; these bytes will be stored in elements - * b[off] through b[off+k-1], - * leaving elements b[off+k] through - * b[off+len-1] unaffected. - *

- *

In every case, elements b[0] through - * b[off] and elements b[off+len] through - * b[b.length-1] are unaffected. - *

- *

If the first byte cannot be read for any reason other than end of - * file, then an IOException is thrown. In particular, an - * IOException is thrown if the input stream has been closed. - * - * @param b the buffer into which the data is read. - * @param off the start offset in array b - * at which the data is written. - * @param len the maximum number of bytes to read. - * @return the total number of bytes read into the buffer, or - * -1 if there is no more data because the end of - * the stream has been reached. - * @throws IOException if an I/O error occurs. - * @throws NullPointerException if b is null. - */ - public int read(byte[] b, int off, int len) throws IOException { - if (outlen < len) { - boolean eof = fillInBuffer(); - fillOutBuffer(); - if (outlen == 0 && eof) - return -1; - if (outlen == 0 && !eof) - throw new IOException("Impossible state"); - } - len = outlen < len ? outlen : len; - if (len > 0) { - System.arraycopy(outbuf, outpos, b, off, len); - outpos += len; - outlen -= len; - } - return len; - } - - /** - *

The method reset for class EqualizerInputStream - * does nothing except throw an IOException. - * - * @throws IOException as an indication that the mark feature doesn't supported by EqualizerInputStream. - */ - public void reset() throws IOException { - throw new IOException("mark/reset not supported"); - } - - /** - * Skips over and discards n bytes of data from this input - * stream. The skip method may, for a variety of reasons, end - * up skipping over some smaller number of bytes, possibly 0. - * This may result from any of a number of conditions; reaching end of file - * before n bytes have been skipped is only one possibility. - * The actual number of bytes skipped is returned. If n is - * negative, no bytes are skipped. - * - * @param n the number of bytes to be skipped. - * @return the actual number of bytes skipped. - * @throws IOException if an I/O error occurs. - */ - public long skip(long n) throws IOException { - int l; - if (n <= outlen) { - outpos += n; - outlen -= n; - return n; - } - n -= outlen; - l = outlen; - outlen = 0; - outpos = 0; - if (n <= inlen) { - inpos += n; - inlen -= n; - return l + n; - } - n -= inlen; - l += inlen; - inlen = 0; - inpos = 0; - return stream.skip(n) + l; - } - -} diff --git a/src/davaguine/jeq/core/IIR.java b/src/davaguine/jeq/core/IIR.java deleted file mode 100644 index 5d2a4f2..0000000 --- a/src/davaguine/jeq/core/IIR.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * 21.04.2004 Original verion. davagin@udm.ru. - *----------------------------------------------------------------------- - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - *---------------------------------------------------------------------- - */ -package davaguine.jeq.core; - -/** - * Generic wrapper around IIR algorithm. - * Author: Dmitry Vaguine - * Date: 02.05.2004 - * Time: 12:00:29 - */ -public class IIR extends IIRBase { - /** - * Max number of channels supported - */ - public final static int EQ_MAX_CHANNELS = 2; - /** - * Max bands supported by the code - */ - public final static int EQ_MAX_BANDS = 31; - /** - * Supported sample rates - */ - public final static float EQ_11025_RATE = 11025; - public final static float EQ_22050_RATE = 22050; - public final static float EQ_44100_RATE = 44100; - public final static float EQ_48000_RATE = 48000; - public final static float EQ_96000_RATE = 96000; - /** - * Supported number of bands - */ - public final static int EQ_10_BANDS = 10; - public final static int EQ_15_BANDS = 15; - public final static int EQ_25_BANDS = 25; - public final static int EQ_31_BANDS = 31; - - /* Indexes for the history arrays - * These have to be kept between calls to this function - * hence they are static */ - private int i; - private int j; - private int k; - - /* History for two filters */ - private XYData[][] dataHistory = new XYData[EQ_MAX_BANDS][EQ_MAX_CHANNELS]; - private XYData[][] dataHistory2 = new XYData[EQ_MAX_BANDS][EQ_MAX_CHANNELS]; - - /* Coefficients */ - private IIRCoefficients[] iircf; - - /* Equalizer config */ - private IIRControls eqcfg; - /* rate */ - private float rate; - /* channels */ - private int channels; - /* bands */ - private int bands; - - /** - * Constructs equalizer with given config - * - * @param bands is the number of bands to be used - * @param rate is the sample rate of equalizer - * @param channels is the number of channels - */ - public IIR(int bands, float rate, int channels) { - this.rate = rate; - this.channels = channels; - this.bands = bands; - this.eqcfg = new IIRControls(bands, channels); - - if (!isParamsSupported(bands, rate, channels)) - throw new IllegalArgumentException("Unsupported parameters"); - - initIIR(); - } - - /** - * Returns Controls of equalizer - * - * @return Controls of equalizer - */ - public IIRControls getControls() { - return eqcfg; - } - - /** - * This is special method for checking of supported parameters of equalizer - * - * @param bands is the number of bands - * @param rate is the sample rate of data - * @param channels is the number of channels - * @return true if parameters are supported - */ - public static boolean isParamsSupported(int bands, float rate, int channels) { - if (rate != EQ_11025_RATE && rate != EQ_22050_RATE && rate != EQ_44100_RATE && rate != EQ_48000_RATE && rate != EQ_96000_RATE) - return false; - - switch (bands) { - case EQ_10_BANDS: - case EQ_15_BANDS: - case EQ_25_BANDS: - case EQ_31_BANDS: - break; - default: - return false; - } - - switch (channels) { - case 1: - case 2: - break; - default: - return false; - } - - return (rate != EQ_11025_RATE && rate != EQ_22050_RATE) || bands == EQ_10_BANDS; - } - - /* Init the filters */ - private void initIIR() { - setFilters(); - for (int ii = 0; ii < EQ_MAX_BANDS; ii++) - for (int jj = 0; jj < EQ_MAX_CHANNELS; jj++) { - dataHistory[ii][jj] = new XYData(); - dataHistory2[ii][jj] = new XYData(); - } - i = 0; - j = 2; - k = 1; - } - - private void setFilters() { - if (rate == EQ_11025_RATE) - iircf = iir_cf10_11k_11025; - else if (rate == EQ_22050_RATE) - iircf = iir_cf10_22k_22050; - else if (rate == EQ_44100_RATE) { - switch (bands) { - case 31: - iircf = iir_cf31_44100; - break; - case 25: - iircf = iir_cf25_44100; - break; - case 15: - iircf = iir_cf15_44100; - break; - default: - iircf = iir_cf10_44100; - break; - } - } else if (rate == EQ_48000_RATE) { - switch (bands) { - case 31: - iircf = iir_cf31_48000; - break; - case 25: - iircf = iir_cf25_48000; - break; - case 15: - iircf = iir_cf15_48000; - break; - default: - iircf = iir_cf10_48000; - break; - } - } else if (rate == EQ_96000_RATE) { - switch (bands) { - case 31: - iircf = iir_cf31_96000; - break; - } - } - } - - /** - * Clear filter history. - */ - public void cleanHistory() { - /* Zero the history arrays */ - for (int ii = 0; ii < EQ_MAX_BANDS; ii++) - for (int jj = 0; jj < EQ_MAX_CHANNELS; jj++) { - dataHistory[ii][jj].zero(); - dataHistory2[ii][jj].zero(); - } - i = 0; - j = 2; - k = 1; - } - - /** - * Main filtering method. - * - * @param data - data to be filtered - * @param length - length of data in buffer - */ - public void iir(int[] data, int length) { - int index, band, channel; - float eqpreamp[] = eqcfg.getPreamp(); - float eqbands[][] = eqcfg.getBands(); - double pcm, out; - - /** - * IIR filter equation is - * y[n] = 2 * (alpha*(x[n]-x[n-2]) + gamma*y[n-1] - beta*y[n-2]) - * - * NOTE: The 2 factor was introduced in the coefficients to save - * a multiplication - * - * This algorithm cascades two filters to get nice filtering - * at the expense of extra CPU cycles - */ - IIRCoefficients tempcf; - XYData tempd; - for (index = 0; index < length; index += channels) { - /* For each channel */ - for (channel = 0; channel < channels; channel++) { - /* Preamp gain */ - pcm = data[index + channel] * eqpreamp[channel]; - - out = 0f; - /* For each band */ - for (band = 0; band < bands; band++) { - /* Store Xi(n) */ - tempd = dataHistory[band][channel]; - tempd.x[i] = pcm; - /* Calculate and store Yi(n) */ - tempcf = iircf[band]; - tempd.y[i] = - ( - /* = alpha * [x(n)-x(n-2)] */ - tempcf.alpha * (pcm - tempd.x[k]) - /* + gamma * y(n-1) */ - + tempcf.gamma * tempd.y[j] - /* - beta * y(n-2) */ - - tempcf.beta * tempd.y[k] - ); - /* - * The multiplication by 2.0 was 'moved' into the coefficients to save - * CPU cycles here */ - /* Apply the gain */ - out += (tempd.y[i] * eqbands[band][channel]); // * 2.0; - } /* For each band */ - - /* Volume stuff - Scale down original PCM sample and add it to the filters - output. This substitutes the multiplication by 0.25 - Go back to use the floating point multiplication before the - conversion to give more dynamic range - */ - out += (pcm * 0.25); - - /* Normalize the output */ - out *= 4; - - /* Round and convert to integer */ - data[index + channel] = (int) out; - } /* For each channel */ - - i++; - j++; - k++; - - /* Wrap around the indexes */ - if (i == 3) - i = 0; - else if (j == 3) - j = 0; - else - k = 0; - - - }/* For each pair of samples */ - } -} diff --git a/src/davaguine/jeq/core/IIRBase.java b/src/davaguine/jeq/core/IIRBase.java deleted file mode 100644 index 73134bb..0000000 --- a/src/davaguine/jeq/core/IIRBase.java +++ /dev/null @@ -1,486 +0,0 @@ -/* - * 21.04.2004 Original verion. davagin@udm.ru. - *----------------------------------------------------------------------- - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - *---------------------------------------------------------------------- - */ -package davaguine.jeq.core; - -/** - * Author: Dmitry Vaguine - * Date: 02.05.2004 - * Time: 12:00:29 - */ -public class IIRBase { - /* BETA, ALPHA, GAMMA */ - public final static IIRCoefficients iir_cf10_11k_11025[] = { - /* 31 Hz*/ - new IIRCoefficients(9.8758524689e-01, 6.2073765555e-03, 1.9872750693e+00), - /* 62 Hz*/ - new IIRCoefficients(9.7532461998e-01, 1.2337690008e-02, 1.9740916593e+00), - /* 125 Hz*/ - new IIRCoefficients(9.5087485437e-01, 2.4562572817e-02, 1.9459267562e+00), - /* 250 Hz*/ - new IIRCoefficients(9.0416308662e-01, 4.7918456688e-02, 1.8848691023e+00), - /* 500 Hz*/ - new IIRCoefficients(8.1751373987e-01, 9.1243130064e-02, 1.7442229115e+00), - /* 1k Hz*/ - new IIRCoefficients(6.6840529852e-01, 1.6579735074e-01, 1.4047189863e+00), - /* 2k Hz*/ - new IIRCoefficients(4.4858358977e-01, 2.7570820511e-01, 6.0517475334e-01), - /* 3k Hz*/ - new IIRCoefficients(3.1012671838e-01, 3.4493664081e-01, -1.8141012760e-01), - /* 4k Hz*/ - new IIRCoefficients(2.4198119087e-01, 3.7900940457e-01, -8.0845085113e-01), - /* 5.5k Hz*/ - new IIRCoefficients(3.3453245058e-01, 3.3273377471e-01, -1.3344985880e+00) - }; - - public final static IIRCoefficients iir_cf10_22k_22050[] = { - /* 31 Hz*/ - new IIRCoefficients(9.9377323686e-01, 3.1133815717e-03, 1.9936954495e+00), - /* 62 Hz*/ - new IIRCoefficients(9.8758524689e-01, 6.2073765555e-03, 1.9872750693e+00), - /* 125 Hz*/ - new IIRCoefficients(9.7512812040e-01, 1.2435939802e-02, 1.9738753198e+00), - /* 250 Hz*/ - new IIRCoefficients(9.5087485437e-01, 2.4562572817e-02, 1.9459267562e+00), - /* 500 Hz*/ - new IIRCoefficients(9.0416308662e-01, 4.7918456688e-02, 1.8848691023e+00), - /* 1k Hz*/ - new IIRCoefficients(8.1751373987e-01, 9.1243130064e-02, 1.7442229115e+00), - /* 2k Hz*/ - new IIRCoefficients(6.6840529852e-01, 1.6579735074e-01, 1.4047189863e+00), - /* 4k Hz*/ - new IIRCoefficients(4.4858358977e-01, 2.7570820511e-01, 6.0517475334e-01), - /* 8k Hz*/ - new IIRCoefficients(2.4198119087e-01, 3.7900940457e-01, -8.0845085113e-01), - /* 11k Hz*/ - new IIRCoefficients(3.3453245058e-01, 3.3273377471e-01, -1.3344985880e+00) - }; - - public final static IIRCoefficients iir_cf10_44100[] = { - /* 31 Hz*/ - new IIRCoefficients(9.9688176273e-01, 1.5591186337e-03, 1.9968622855e+00), - /* 62 Hz*/ - new IIRCoefficients(9.9377323686e-01, 3.1133815717e-03, 1.9936954495e+00), - /* 125 Hz*/ - new IIRCoefficients(9.8748575691e-01, 6.2571215431e-03, 1.9871705722e+00), - /* 250 Hz*/ - new IIRCoefficients(9.7512812040e-01, 1.2435939802e-02, 1.9738753198e+00), - /* 500 Hz*/ - new IIRCoefficients(9.5087485437e-01, 2.4562572817e-02, 1.9459267562e+00), - /* 1k Hz*/ - new IIRCoefficients(9.0416308662e-01, 4.7918456688e-02, 1.8848691023e+00), - /* 2k Hz*/ - new IIRCoefficients(8.1751373987e-01, 9.1243130064e-02, 1.7442229115e+00), - /* 4k Hz*/ - new IIRCoefficients(6.6840529852e-01, 1.6579735074e-01, 1.4047189863e+00), - /* 8k Hz*/ - new IIRCoefficients(4.4858358977e-01, 2.7570820511e-01, 6.0517475334e-01), - /* 16k Hz*/ - new IIRCoefficients(2.4198119087e-01, 3.7900940457e-01, -8.0845085113e-01) - }; - - public final static IIRCoefficients iir_cf10_48000[] = { - /* 31 Hz*/ - new IIRCoefficients(9.9713475915e-01, 1.4326204244e-03, 1.9971183163e+00), - /* 62 Hz*/ - new IIRCoefficients(9.9427771143e-01, 2.8611442874e-03, 1.9942120343e+00), - /* 125 Hz*/ - new IIRCoefficients(9.8849666727e-01, 5.7516663664e-03, 1.9882304829e+00), - /* 250 Hz*/ - new IIRCoefficients(9.7712566171e-01, 1.1437169144e-02, 1.9760670839e+00), - /* 500 Hz*/ - new IIRCoefficients(9.5477456091e-01, 2.2612719547e-02, 1.9505892385e+00), - /* 1k Hz*/ - new IIRCoefficients(9.1159452679e-01, 4.4202736607e-02, 1.8952405706e+00), - /* 2k Hz*/ - new IIRCoefficients(8.3100647694e-01, 8.4496761532e-02, 1.7686164442e+00), - /* 4k Hz*/ - new IIRCoefficients(6.9062328809e-01, 1.5468835596e-01, 1.4641227157e+00), - /* 8k Hz*/ - new IIRCoefficients(4.7820368352e-01, 2.6089815824e-01, 7.3910184176e-01), - /* 16k Hz*/ - new IIRCoefficients(2.5620076154e-01, 3.7189961923e-01, -6.2810038077e-01) - }; - - public final static IIRCoefficients iir_cf15_44100[] = { - /* 25 Hz*/ - new IIRCoefficients(9.9834072702e-01, 8.2963648917e-04, 1.9983280505e+00), - /* 40 Hz*/ - new IIRCoefficients(9.9734652663e-01, 1.3267366865e-03, 1.9973140908e+00), - /* 63 Hz*/ - new IIRCoefficients(9.9582396353e-01, 2.0880182333e-03, 1.9957435641e+00), - /* 100 Hz*/ - new IIRCoefficients(9.9337951306e-01, 3.3102434709e-03, 1.9931771947e+00), - /* 160 Hz*/ - new IIRCoefficients(9.8942832039e-01, 5.2858398053e-03, 1.9889114258e+00), - /* 250 Hz*/ - new IIRCoefficients(9.8353109588e-01, 8.2344520610e-03, 1.9822729654e+00), - /* 400 Hz*/ - new IIRCoefficients(9.7378088082e-01, 1.3109559588e-02, 1.9705764276e+00), - /* 630 Hz*/ - new IIRCoefficients(9.5901979676e-01, 2.0490101620e-02, 1.9511333590e+00), - /* 1k Hz*/ - new IIRCoefficients(9.3574903986e-01, 3.2125480071e-02, 1.9161350100e+00), - /* 1.6k Hz*/ - new IIRCoefficients(8.9923630641e-01, 5.0381846793e-02, 1.8501014162e+00), - /* 2.5k Hz*/ - new IIRCoefficients(8.4722457681e-01, 7.6387711593e-02, 1.7312785699e+00), - /* 4k Hz*/ - new IIRCoefficients(7.6755471307e-01, 1.1622264346e-01, 1.4881981417e+00), - /* 6.3k Hz*/ - new IIRCoefficients(6.6125377473e-01, 1.6937311263e-01, 1.0357747868e+00), - /* 10k Hz*/ - new IIRCoefficients(5.2683267950e-01, 2.3658366025e-01, 2.2218349322e-01), - /* 16k Hz*/ - new IIRCoefficients(4.0179628792e-01, 2.9910185604e-01, -9.1248032613e-01) - }; - - public final static IIRCoefficients iir_cf15_48000[] = { - /* 25 Hz*/ - new IIRCoefficients(9.9847546664e-01, 7.6226668143e-04, 1.9984647656e+00), - /* 40 Hz*/ - new IIRCoefficients(9.9756184654e-01, 1.2190767289e-03, 1.9975344645e+00), - /* 63 Hz*/ - new IIRCoefficients(9.9616261379e-01, 1.9186931041e-03, 1.9960947369e+00), - /* 100 Hz*/ - new IIRCoefficients(9.9391578543e-01, 3.0421072865e-03, 1.9937449618e+00), - /* 160 Hz*/ - new IIRCoefficients(9.9028307215e-01, 4.8584639242e-03, 1.9898465702e+00), - /* 250 Hz*/ - new IIRCoefficients(9.8485897264e-01, 7.5705136795e-03, 1.9837962543e+00), - /* 400 Hz*/ - new IIRCoefficients(9.7588512657e-01, 1.2057436715e-02, 1.9731772447e+00), - /* 630 Hz*/ - new IIRCoefficients(9.6228521814e-01, 1.8857390928e-02, 1.9556164694e+00), - /* 1k Hz*/ - new IIRCoefficients(9.4080933132e-01, 2.9595334338e-02, 1.9242054384e+00), - /* 1.6k Hz*/ - new IIRCoefficients(9.0702059196e-01, 4.6489704022e-02, 1.8653476166e+00), - /* 2.5k Hz*/ - new IIRCoefficients(8.5868004289e-01, 7.0659978553e-02, 1.7600401337e+00), - /* 4k Hz*/ - new IIRCoefficients(7.8409610788e-01, 1.0795194606e-01, 1.5450725522e+00), - /* 6.3k Hz*/ - new IIRCoefficients(6.8332861002e-01, 1.5833569499e-01, 1.1426447155e+00), - /* 10k Hz*/ - new IIRCoefficients(5.5267518228e-01, 2.2366240886e-01, 4.0186190803e-01), - /* 16k Hz*/ - new IIRCoefficients(4.1811888447e-01, 2.9094055777e-01, -7.0905944223e-01) - }; - - public final static IIRCoefficients iir_cf25_44100[] = { - /* 20 Hz*/ - new IIRCoefficients(9.9934037157e-01, 3.2981421662e-04, 1.9993322545e+00), - /* 31.5 Hz*/ - new IIRCoefficients(9.9896129025e-01, 5.1935487310e-04, 1.9989411587e+00), - /* 40 Hz*/ - new IIRCoefficients(9.9868118265e-01, 6.5940867495e-04, 1.9986487252e+00), - /* 50 Hz*/ - new IIRCoefficients(9.9835175161e-01, 8.2412419683e-04, 1.9983010452e+00), - /* 80 Hz*/ - new IIRCoefficients(9.9736411067e-01, 1.3179446674e-03, 1.9972343673e+00), - /* 100 Hz*/ - new IIRCoefficients(9.9670622662e-01, 1.6468866919e-03, 1.9965035707e+00), - /* 125 Hz*/ - new IIRCoefficients(9.9588448566e-01, 2.0577571681e-03, 1.9955679690e+00), - /* 160 Hz*/ - new IIRCoefficients(9.9473519326e-01, 2.6324033689e-03, 1.9942169198e+00), - /* 250 Hz*/ - new IIRCoefficients(9.9178600786e-01, 4.1069960678e-03, 1.9905226414e+00), - /* 315 Hz*/ - new IIRCoefficients(9.8966154150e-01, 5.1692292513e-03, 1.9876580847e+00), - /* 400 Hz*/ - new IIRCoefficients(9.8689036168e-01, 6.5548191616e-03, 1.9836646251e+00), - /* 500 Hz*/ - new IIRCoefficients(9.8364027156e-01, 8.1798642207e-03, 1.9786090689e+00), - /* 800 Hz*/ - new IIRCoefficients(9.7395577681e-01, 1.3022111597e-02, 1.9611472340e+00), - /* 1k Hz*/ - new IIRCoefficients(9.6755437936e-01, 1.6222810321e-02, 1.9476180811e+00), - /* 1.25k Hz*/ - new IIRCoefficients(9.5961458750e-01, 2.0192706249e-02, 1.9286193446e+00), - /* 1.6k Hz*/ - new IIRCoefficients(9.4861481164e-01, 2.5692594182e-02, 1.8982024567e+00), - /* 2.5k Hz*/ - new IIRCoefficients(9.2095325455e-01, 3.9523372724e-02, 1.8003794694e+00), - /* 3.15k Hz*/ - new IIRCoefficients(9.0153642498e-01, 4.9231787512e-02, 1.7132251201e+00), - /* 4k Hz*/ - new IIRCoefficients(8.7685876255e-01, 6.1570618727e-02, 1.5802270232e+00), - /* 5k Hz*/ - new IIRCoefficients(8.4886734822e-01, 7.5566325889e-02, 1.3992391376e+00), - /* 8k Hz*/ - new IIRCoefficients(7.7175298860e-01, 1.1412350570e-01, 7.4018523020e-01), - /* 10k Hz*/ - new IIRCoefficients(7.2627049462e-01, 1.3686475269e-01, 2.5120552756e-01), - /* 12.5k Hz*/ - new IIRCoefficients(6.7674787974e-01, 1.6162606013e-01, -3.4978377639e-01), - /* 16k Hz*/ - new IIRCoefficients(6.2482197550e-01, 1.8758901225e-01, -1.0576558797e+00), - /* 20k Hz*/ - new IIRCoefficients(6.1776148240e-01, 1.9111925880e-01, -1.5492465594e+00) - }; - - public final static IIRCoefficients iir_cf25_48000[] = { - /* 20 Hz*/ - new IIRCoefficients(9.9939388451e-01, 3.0305774630e-04, 1.9993870327e+00), - /* 31.5 Hz*/ - new IIRCoefficients(9.9904564663e-01, 4.7717668529e-04, 1.9990286528e+00), - /* 40 Hz*/ - new IIRCoefficients(9.9878827195e-01, 6.0586402557e-04, 1.9987608731e+00), - /* 50 Hz*/ - new IIRCoefficients(9.9848556942e-01, 7.5721528829e-04, 1.9984427652e+00), - /* 80 Hz*/ - new IIRCoefficients(9.9757801538e-01, 1.2109923088e-03, 1.9974684869e+00), - /* 100 Hz*/ - new IIRCoefficients(9.9697343933e-01, 1.5132803374e-03, 1.9968023538e+00), - /* 125 Hz*/ - new IIRCoefficients(9.9621823598e-01, 1.8908820086e-03, 1.9959510180e+00), - /* 160 Hz*/ - new IIRCoefficients(9.9516191728e-01, 2.4190413595e-03, 1.9947243453e+00), - /* 250 Hz*/ - new IIRCoefficients(9.9245085008e-01, 3.7745749576e-03, 1.9913840669e+00), - /* 315 Hz*/ - new IIRCoefficients(9.9049749914e-01, 4.7512504310e-03, 1.9888056233e+00), - /* 400 Hz*/ - new IIRCoefficients(9.8794899744e-01, 6.0255012789e-03, 1.9852245824e+00), - /* 500 Hz*/ - new IIRCoefficients(9.8495930023e-01, 7.5203498850e-03, 1.9807093500e+00), - /* 800 Hz*/ - new IIRCoefficients(9.7604570090e-01, 1.1977149551e-02, 1.9652207158e+00), - /* 1k Hz*/ - new IIRCoefficients(9.7014963927e-01, 1.4925180364e-02, 1.9532947360e+00), - /* 1.25k Hz*/ - new IIRCoefficients(9.6283181641e-01, 1.8584091793e-02, 1.9366149237e+00), - /* 1.6k Hz*/ - new IIRCoefficients(9.5268463224e-01, 2.3657683878e-02, 1.9100137880e+00), - /* 2.5k Hz*/ - new IIRCoefficients(9.2711765003e-01, 3.6441174983e-02, 1.8248457659e+00), - /* 3.15k Hz*/ - new IIRCoefficients(9.0912548757e-01, 4.5437256213e-02, 1.7491177803e+00), - /* 4k Hz*/ - new IIRCoefficients(8.8619860800e-01, 5.6900696000e-02, 1.6334959111e+00), - /* 5k Hz*/ - new IIRCoefficients(8.6010264114e-01, 6.9948679430e-02, 1.4757186436e+00), - /* 8k Hz*/ - new IIRCoefficients(7.8757448309e-01, 1.0621275845e-01, 8.9378724155e-01), - /* 10k Hz*/ - new IIRCoefficients(7.4415362476e-01, 1.2792318762e-01, 4.5142017567e-01), - /* 12.5k Hz*/ - new IIRCoefficients(6.9581428034e-01, 1.5209285983e-01, -1.1091156053e-01), - /* 16k Hz*/ - new IIRCoefficients(6.4120506488e-01, 1.7939746756e-01, -8.2060253244e-01), - /* 20k Hz*/ - new IIRCoefficients(6.0884213704e-01, 1.9557893148e-01, -1.3932981614e+00) - }; - - public final static IIRCoefficients iir_cf31_44100[] = { - /* 20 Hz*/ - new IIRCoefficients(9.9934037157e-01, 3.2981421662e-04, 1.9993322545e+00), - /* 25 Hz*/ - new IIRCoefficients(9.9917555233e-01, 4.1222383516e-04, 1.9991628705e+00), - /* 31.5 Hz*/ - new IIRCoefficients(9.9896129025e-01, 5.1935487310e-04, 1.9989411587e+00), - /* 40 Hz*/ - new IIRCoefficients(9.9868118265e-01, 6.5940867495e-04, 1.9986487252e+00), - /* 50 Hz*/ - new IIRCoefficients(9.9835175161e-01, 8.2412419683e-04, 1.9983010452e+00), - /* 63 Hz*/ - new IIRCoefficients(9.9792365217e-01, 1.0381739160e-03, 1.9978431682e+00), - /* 80 Hz*/ - new IIRCoefficients(9.9736411067e-01, 1.3179446674e-03, 1.9972343673e+00), - /* 100 Hz*/ - new IIRCoefficients(9.9670622662e-01, 1.6468866919e-03, 1.9965035707e+00), - /* 125 Hz*/ - new IIRCoefficients(9.9588448566e-01, 2.0577571681e-03, 1.9955679690e+00), - /* 160 Hz*/ - new IIRCoefficients(9.9473519326e-01, 2.6324033689e-03, 1.9942169198e+00), - /* 200 Hz*/ - new IIRCoefficients(9.9342335280e-01, 3.2883236020e-03, 1.9926141028e+00), - /* 250 Hz*/ - new IIRCoefficients(9.9178600786e-01, 4.1069960678e-03, 1.9905226414e+00), - /* 315 Hz*/ - new IIRCoefficients(9.8966154150e-01, 5.1692292513e-03, 1.9876580847e+00), - /* 400 Hz*/ - new IIRCoefficients(9.8689036168e-01, 6.5548191616e-03, 1.9836646251e+00), - /* 500 Hz*/ - new IIRCoefficients(9.8364027156e-01, 8.1798642207e-03, 1.9786090689e+00), - /* 630 Hz*/ - new IIRCoefficients(9.7943153305e-01, 1.0284233476e-02, 1.9714629236e+00), - /* 800 Hz*/ - new IIRCoefficients(9.7395577681e-01, 1.3022111597e-02, 1.9611472340e+00), - /* 1k Hz*/ - new IIRCoefficients(9.6755437936e-01, 1.6222810321e-02, 1.9476180811e+00), - /* 1.25k Hz*/ - new IIRCoefficients(9.5961458750e-01, 2.0192706249e-02, 1.9286193446e+00), - /* 1.6k Hz*/ - new IIRCoefficients(9.4861481164e-01, 2.5692594182e-02, 1.8982024567e+00), - /* 2k Hz*/ - new IIRCoefficients(9.3620971896e-01, 3.1895140519e-02, 1.8581325022e+00), - /* 2.5k Hz*/ - new IIRCoefficients(9.2095325455e-01, 3.9523372724e-02, 1.8003794694e+00), - /* 3.15k Hz*/ - new IIRCoefficients(9.0153642498e-01, 4.9231787512e-02, 1.7132251201e+00), - /* 4k Hz*/ - new IIRCoefficients(8.7685876255e-01, 6.1570618727e-02, 1.5802270232e+00), - /* 5k Hz*/ - new IIRCoefficients(8.4886734822e-01, 7.5566325889e-02, 1.3992391376e+00), - /* 6.3k Hz*/ - new IIRCoefficients(8.1417575446e-01, 9.2912122771e-02, 1.1311200817e+00), - /* 8k Hz*/ - new IIRCoefficients(7.7175298860e-01, 1.1412350570e-01, 7.4018523020e-01), - /* 10k Hz*/ - new IIRCoefficients(7.2627049462e-01, 1.3686475269e-01, 2.5120552756e-01), - /* 12.5k Hz*/ - new IIRCoefficients(6.7674787974e-01, 1.6162606013e-01, -3.4978377639e-01), - /* 16k Hz*/ - new IIRCoefficients(6.2482197550e-01, 1.8758901225e-01, -1.0576558797e+00), - /* 20k Hz*/ - new IIRCoefficients(6.1776148240e-01, 1.9111925880e-01, -1.5492465594e+00) - }; - - public final static IIRCoefficients iir_cf31_48000[] = { - /* 20 Hz*/ - new IIRCoefficients(9.9939388451e-01, 3.0305774630e-04, 1.9993870327e+00), - /* 25 Hz*/ - new IIRCoefficients(9.9924247917e-01, 3.7876041632e-04, 1.9992317740e+00), - /* 31.5 Hz*/ - new IIRCoefficients(9.9904564663e-01, 4.7717668529e-04, 1.9990286528e+00), - /* 40 Hz*/ - new IIRCoefficients(9.9878827195e-01, 6.0586402557e-04, 1.9987608731e+00), - /* 50 Hz*/ - new IIRCoefficients(9.9848556942e-01, 7.5721528829e-04, 1.9984427652e+00), - /* 63 Hz*/ - new IIRCoefficients(9.9809219264e-01, 9.5390367779e-04, 1.9980242502e+00), - /* 80 Hz*/ - new IIRCoefficients(9.9757801538e-01, 1.2109923088e-03, 1.9974684869e+00), - /* 100 Hz*/ - new IIRCoefficients(9.9697343933e-01, 1.5132803374e-03, 1.9968023538e+00), - /* 125 Hz*/ - new IIRCoefficients(9.9621823598e-01, 1.8908820086e-03, 1.9959510180e+00), - /* 160 Hz*/ - new IIRCoefficients(9.9516191728e-01, 2.4190413595e-03, 1.9947243453e+00), - /* 200 Hz*/ - new IIRCoefficients(9.9395607757e-01, 3.0219612131e-03, 1.9932727986e+00), - /* 250 Hz*/ - new IIRCoefficients(9.9245085008e-01, 3.7745749576e-03, 1.9913840669e+00), - /* 315 Hz*/ - new IIRCoefficients(9.9049749914e-01, 4.7512504310e-03, 1.9888056233e+00), - /* 400 Hz*/ - new IIRCoefficients(9.8794899744e-01, 6.0255012789e-03, 1.9852245824e+00), - /* 500 Hz*/ - new IIRCoefficients(9.8495930023e-01, 7.5203498850e-03, 1.9807093500e+00), - /* 630 Hz*/ - new IIRCoefficients(9.8108651246e-01, 9.4567437704e-03, 1.9743538683e+00), - /* 800 Hz*/ - new IIRCoefficients(9.7604570090e-01, 1.1977149551e-02, 1.9652207158e+00), - /* 1k Hz*/ - new IIRCoefficients(9.7014963927e-01, 1.4925180364e-02, 1.9532947360e+00), - /* 1.25k Hz*/ - new IIRCoefficients(9.6283181641e-01, 1.8584091793e-02, 1.9366149237e+00), - /* 1.6k Hz*/ - new IIRCoefficients(9.5268463224e-01, 2.3657683878e-02, 1.9100137880e+00), - /* 2k Hz*/ - new IIRCoefficients(9.4122788957e-01, 2.9386055213e-02, 1.8750821533e+00), - /* 2.5k Hz*/ - new IIRCoefficients(9.2711765003e-01, 3.6441174983e-02, 1.8248457659e+00), - /* 3.15k Hz*/ - new IIRCoefficients(9.0912548757e-01, 4.5437256213e-02, 1.7491177803e+00), - /* 4k Hz*/ - new IIRCoefficients(8.8619860800e-01, 5.6900696000e-02, 1.6334959111e+00), - /* 5k Hz*/ - new IIRCoefficients(8.6010264114e-01, 6.9948679430e-02, 1.4757186436e+00), - /* 6.3k Hz*/ - new IIRCoefficients(8.2760520925e-01, 8.6197395374e-02, 1.2405797786e+00), - /* 8k Hz*/ - new IIRCoefficients(7.8757448309e-01, 1.0621275845e-01, 8.9378724155e-01), - /* 10k Hz*/ - new IIRCoefficients(7.4415362476e-01, 1.2792318762e-01, 4.5142017567e-01), - /* 12.5k Hz*/ - new IIRCoefficients(6.9581428034e-01, 1.5209285983e-01, -1.1091156053e-01), - /* 16k Hz*/ - new IIRCoefficients(6.4120506488e-01, 1.7939746756e-01, -8.2060253244e-01), - /* 20k Hz*/ - new IIRCoefficients(6.0884213704e-01, 1.9557893148e-01, -1.3932981614e+00), - }; - - public final static IIRCoefficients iir_cf31_96000[] = { - /* 20.0 Hz */ - new IIRCoefficients(9.9969659208e-01, 1.517040e-04, 1.9996948789e+00), - /* 25.0 Hz */ - new IIRCoefficients(9.9962116126e-01, 1.894194e-04, 1.9996184845e+00), - /* 31.5 Hz */ - new IIRCoefficients(9.9952283343e-01, 2.385833e-04, 1.9995185840e+00), - /* 40.0 Hz */ - new IIRCoefficients(9.9939398196e-01, 3.030090e-04, 1.9993871301e+00), - /* 50.0 Hz */ - new IIRCoefficients(9.9924251688e-01, 3.787416e-04, 1.9992318117e+00), - /* 63.0 Hz */ - new IIRCoefficients(9.9904565182e-01, 4.771741e-04, 1.9990286580e+00), - /* 80.0 Hz */ - new IIRCoefficients(9.9878828573e-01, 6.058571e-04, 1.9987608868e+00), - /* 100.0 Hz */ - new IIRCoefficients(9.9848557077e-01, 7.572146e-04, 1.9984427665e+00), - /* 125.0 Hz */ - new IIRCoefficients(9.9810732562e-01, 9.463372e-04, 1.9980404568e+00), - /* 160.0 Hz */ - new IIRCoefficients(9.9757801618e-01, 1.210992e-03, 1.9974684877e+00), - /* 200.0 Hz */ - new IIRCoefficients(9.9697343858e-01, 1.513281e-03, 1.9968023530e+00), - /* 250.0 Hz */ - new IIRCoefficients(9.9621823615e-01, 1.890882e-03, 1.9959510182e+00), - /* 315.0 Hz */ - new IIRCoefficients(9.9523733132e-01, 2.381334e-03, 1.9948133101e+00), - /* 400.0 Hz */ - new IIRCoefficients(9.9395607744e-01, 3.021961e-03, 1.9932727985e+00), - /* 500.0 Hz */ - new IIRCoefficients(9.9245084999e-01, 3.774575e-03, 1.9913840669e+00), - /* 630.0 Hz */ - new IIRCoefficients(9.9049749915e-01, 4.751250e-03, 1.9888056234e+00), - /* 800.0 Hz */ - new IIRCoefficients(9.8794899744e-01, 6.025501e-03, 1.9852245824e+00), - /* 1000.0 Hz */ - new IIRCoefficients(9.8495930024e-01, 7.520350e-03, 1.9807093500e+00), - /* 1250.0 Hz */ - new IIRCoefficients(9.8123517675e-01, 9.382412e-03, 1.9746084192e+00), - /* 1600.0 Hz */ - new IIRCoefficients(9.7604570090e-01, 1.197715e-02, 1.9652207158e+00), - /* 2000.0 Hz */ - new IIRCoefficients(9.7014963927e-01, 1.492518e-02, 1.9532947360e+00), - /* 2500.0 Hz */ - new IIRCoefficients(9.6283181641e-01, 1.858409e-02, 1.9366149237e+00), - /* 3150.0 Hz */ - new IIRCoefficients(9.5340564248e-01, 2.329718e-02, 1.9120378855e+00), - /* 4000.0 Hz */ - new IIRCoefficients(9.4122788957e-01, 2.938606e-02, 1.8750821533e+00), - /* 5000.0 Hz */ - new IIRCoefficients(9.2711765003e-01, 3.644117e-02, 1.8248457659e+00), - /* 6300.0 Hz */ - new IIRCoefficients(9.0912548757e-01, 4.543726e-02, 1.7491177803e+00), - /* 8000.0 Hz */ - new IIRCoefficients(8.8619860800e-01, 5.690070e-02, 1.6334959111e+00), - /* 10000.0 Hz */ - new IIRCoefficients(8.6010264114e-01, 6.994868e-02, 1.4757186436e+00), - /* 12500.0 Hz */ - new IIRCoefficients(8.2882509035e-01, 8.558745e-02, 1.2501707535e+00), - /* 16000.0 Hz */ - new IIRCoefficients(7.8757448309e-01, 1.062128e-01, 8.9378724155e-01), - /* 20000.0 Hz */ - new IIRCoefficients(7.4415362476e-01, 1.279232e-01, 4.5142017567e-01), - }; -} diff --git a/src/davaguine/jeq/core/IIRCoefficients.java b/src/davaguine/jeq/core/IIRCoefficients.java deleted file mode 100644 index bf7b8bf..0000000 --- a/src/davaguine/jeq/core/IIRCoefficients.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 21.04.2004 Original verion. davagin@udm.ru. - *----------------------------------------------------------------------- - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - *---------------------------------------------------------------------- - */ -package davaguine.jeq.core; - -/** - * Author: Dmitry Vaguine - * Date: 02.05.2004 - * Time: 12:00:29 - */ -public class IIRCoefficients { - public double beta; - public double alpha; - public double gamma; - - public IIRCoefficients(double beta, double alpha, double gamma) { - this.beta = beta; - this.alpha = alpha; - this.gamma = gamma; - } -} diff --git a/src/davaguine/jeq/core/IIRControls.java b/src/davaguine/jeq/core/IIRControls.java deleted file mode 100644 index 430cce2..0000000 --- a/src/davaguine/jeq/core/IIRControls.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * 21.04.2004 Original verion. davagin@udm.ru. - *----------------------------------------------------------------------- - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - *---------------------------------------------------------------------- - */ -package davaguine.jeq.core; - -/** - * Constols of equalizer - * Author: Dmitry Vaguine - * Date: 02.05.2004 - * Time: 12:00:29 - */ -public class IIRControls { - /** - * Volume gain - * values should be between 0.0 and 1.0 - */ - private float preamp[]; - /** - * Gain for each band - * values should be between -0.2 and 1.0 - */ - private float bands[][]; - - /** - * Creates new IIRControls object for given number of bands - * - * @param bandsnum is the number of bands - * @param channels is the number of channels - */ - public IIRControls(int bandsnum, int channels) { - preamp = new float[channels]; - bands = new float[bandsnum][channels]; - for (int j = 0; j < channels; j++) { - preamp[j] = 1.0f; - for (int i = 0; i < bandsnum; i++) - bands[i][j] = 0f; - } - } - - /** - * Returns the maximum value for band control - * - * @return the maximum value for band control - */ - public float getMaximumBandValue() { - return 1.0f; - } - - /** - * Returns the minimum value for band control - * - * @return the minimum value for band control - */ - public float getMinimumBandValue() { - return -0.2f; - } - - /** - * Returns the maximum value for band control (in Db) - * - * @return the maximum value for band control - */ - public float getMaximumBandDbValue() { - return 12; - } - - /** - * Returns the minimum value for band control (in Db) - * - * @return the minimum value for band control - */ - public float getMinimumBandDbValue() { - return -12f; - } - - /** - * Returns the maximum value for preamp control - * - * @return the maximum value for preamp control - */ - public float getMaximumPreampValue() { - return 1.0f; - } - - /** - * Returns the minimum value for preamp control - * - * @return the minimum value for preamp control - */ - public float getMinimumPreampValue() { - return 0f; - } - - /** - * Returns the maximum value for preamp control (in Db) - * - * @return the maximum value for preamp control - */ - public float getMaximumPreampDbValue() { - return 12f; - } - - /** - * Returns the minimum value for preamp control (in Db) - * - * @return the minimum value for preamp control - */ - public float getMinimumPreampDbValue() { - return -12f; - } - - /** - * Returns bands array - * - * @return bands array - */ - float[][] getBands() { - return bands; - } - - /** - * Returns preamp array - * - * @return preamp array - */ - float[] getPreamp() { - return preamp; - } - - /** - * Returns value of control for given band and channel - * - * @param band is the index of band - * @param channel is the index of channel - * @return the value - */ - public float getBandValue(int band, int channel) { - return bands[band][channel]; - } - - /** - * Setter for value of control for given band and channel - * - * @param band is the index of band - * @param channel is the index of channel - * @param value is the new value - */ - public void setBandValue(int band, int channel, float value) { - bands[band][channel] = value; - } - - /** - * Setter for value of control for given band and channel (in Db) - * - * @param band is the index of band - * @param channel is the index of channel - * @param value is the new value - */ - public void setBandDbValue(int band, int channel, float value) { - /* Map the gain and preamp values */ - /* -12dB .. 12dB mapping */ - bands[band][channel] = (float) (2.5220207857061455181125E-01 * - Math.exp(8.0178361802353992349168E-02 * value) - - 2.5220207852836562523180E-01); - } - - /** - * Returns value of preamp control for given channel - * - * @param channel is the index of channel - * @return the value - */ - public float getPreampValue(int channel) { - return preamp[channel]; - } - - /** - * Setter for value of preamp control for given channel - * - * @param channel is the index of channel - * @param value is the new value - */ - public void setPreampValue(int channel, float value) { - preamp[channel] = value; - } - - /** - * Setter for value of preamp control for given channel (in Db) - * - * @param channel is the index of channel - * @param value is the new value - */ - public void setPreampDbValue(int channel, float value) { - /* -12dB .. 12dB mapping */ - preamp[channel] = (float) (9.9999946497217584440165E-01 * - Math.exp(6.9314738656671842642609E-02 * value) - + 3.7119444716771825623636E-07); - } -} diff --git a/src/davaguine/jeq/core/XYData.java b/src/davaguine/jeq/core/XYData.java deleted file mode 100644 index 43abad7..0000000 --- a/src/davaguine/jeq/core/XYData.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 21.04.2004 Original verion. davagin@udm.ru. - *----------------------------------------------------------------------- - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - *---------------------------------------------------------------------- - */ -package davaguine.jeq.core; - -/** - * Structure for storing XYData of equalizer. - * Author: Dmitry Vaguine - * Date: 02.05.2004 - * Time: 12:00:29 - */ -public class XYData { - /** - * X data - */ - public double x[] = new double[3]; /* x[n], x[n-1], x[n-2] */ - /** - * Y data - */ - public double y[] = new double[3]; /* y[n], y[n-1], y[n-2] */ - - /** - * Constructs new XYData object - */ - public XYData() { - zero(); - } - - /** - * Fills all content with zero - */ - public void zero() { - for (int i = 0; i < 3; i++) { - x[i] = 0; - y[i] = 0; - } - } -} diff --git a/src/davaguine/jeq/spi/EqualizerInputStream.java b/src/davaguine/jeq/spi/EqualizerInputStream.java deleted file mode 100644 index bc90e15..0000000 --- a/src/davaguine/jeq/spi/EqualizerInputStream.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * 21.04.2004 Original verion. davagin@udm.ru. - *----------------------------------------------------------------------- - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - *---------------------------------------------------------------------- - */ -package davaguine.jeq.spi; - -import davaguine.jeq.core.IIRControls; - -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioInputStream; -import java.io.IOException; - -/** - * The EqualizerInputStream input stream - * Author: Dmitry Vaguine - * Date: 02.05.2004 - * Time: 12:00:29 - */ -public class EqualizerInputStream extends AudioInputStream { - private davaguine.jeq.core.EqualizerInputStream eq; - - /** - * Constructs new audio stream - * - * @param stream input stream with audio data - * @param bands is the number of bands - */ - public EqualizerInputStream(AudioInputStream stream, int bands) { - super(stream, stream.getFormat(), stream.getFrameLength()); - AudioFormat format = stream.getFormat(); - if (!format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED) && !!format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED)) - throw new IllegalArgumentException("Unsupported encoding"); - eq = new davaguine.jeq.core.EqualizerInputStream(stream, - format.getSampleRate(), - format.getChannels(), - format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED), - format.getSampleSizeInBits(), - format.isBigEndian(), - bands); - } - - /** - * Returns Controls of equalizer - * - * @return Controls of equalizer - */ - public IIRControls getControls() { - return eq.getControls(); - } - - /** - * This is special method helps to determine supported audio format - * - * @param format is an audio format - * @param bands is the number of bands - * @return true if params supported - */ - public static boolean isParamsSupported(AudioFormat format, int bands) { - if (!format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED) && !!format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED)) - return false; - return davaguine.jeq.core.EqualizerInputStream.isParamsSupported( - format.getSampleRate(), - format.getChannels(), - format.getSampleSizeInBits(), - bands); - } - - /** - * Returns the number of bytes that can be read (or skipped over) from - * this input stream without blocking by the next caller of a method for - * this input stream. The next caller might be the same thread or - * another thread. - * - * @return the number of bytes that can be read from this input stream - * without blocking. - * @throws java.io.IOException if an I/O error occurs. - */ - public int available() throws IOException { - return eq.available(); - } - - /** - * Closes this input stream and releases any system resources associated - * with the stream. - * - * @throws IOException if an I/O error occurs. - */ - public void close() throws IOException { - eq.close(); - } - - /** - *

The mark method of EqualizerInputStream does - * nothing. - * - * @param readlimit the maximum limit of bytes that can be read before - * the mark position becomes invalid. - */ - public synchronized void mark(int readlimit) { - eq.mark(readlimit); - } - - /** - * Tests if this input stream supports the mark and - * reset methods. Whether or not mark and - * reset are supported is an invariant property of a - * particular input stream instance. The markSupported method - * of EqualizerInputStream returns false. - * - * @return false - */ - public boolean markSupported() { - return eq.markSupported(); - } - - /** - * Reads the next byte of data from the input stream. The value byte is - * returned as an int in the range 0 to - * 255. If no byte is available because the end of the stream - * has been reached, the value -1 is returned. This method - * blocks until input data is available, the end of the stream is detected, - * or an exception is thrown. - * - * @return the next byte of data, or -1 if the end of the - * stream is reached. - * @throws IOException if an I/O error occurs. - */ - public int read() throws IOException { - return eq.read(); - } - - /** - * Reads some number of bytes from the input stream and stores them into - * the buffer array b. The number of bytes actually read is - * returned as an integer. This method blocks until input data is - * available, end of file is detected, or an exception is thrown. - *

- *

If b is null, a - * NullPointerException is thrown. If the length of - * b is zero, then no bytes are read and 0 is - * returned; otherwise, there is an attempt to read at least one byte. If - * no byte is available because the stream is at end of file, the value - * -1 is returned; otherwise, at least one byte is read and - * stored into b. - *

- *

The first byte read is stored into element b[0], the - * next one into b[1], and so on. The number of bytes read is, - * at most, equal to the length of b. Let k be the - * number of bytes actually read; these bytes will be stored in elements - * b[0] through b[k-1], - * leaving elements b[k] through - * b[b.length-1] unaffected. - *

- *

If the first byte cannot be read for any reason other than end of - * file, then an IOException is thrown. In particular, an - * IOException is thrown if the input stream has been closed. - *

- *

The read(b) method for class EqualizerInputStream - * has the same effect as:

 read(b, 0, b.length) 
- * - * @param b the buffer into which the data is read. - * @return the total number of bytes read into the buffer, or - * -1 is there is no more data because the end of - * the stream has been reached. - * @throws IOException if an I/O error occurs. - * @throws NullPointerException if b is null. - */ - public int read(byte b[]) throws IOException { - return read(b, 0, b.length); - } - - /** - * Reads up to len bytes of data from the input stream into - * an array of bytes. An attempt is made to read as many as - * len bytes, but a smaller number may be read. - * The number of bytes actually read is returned as an integer. - *

- *

This method blocks until input data is available, end of file is - * detected, or an exception is thrown. - *

- *

If b is null, a - * NullPointerException is thrown. - *

- *

If off is negative, or len is negative, or - * off+len is greater than the length of the array - * b, then an IndexOutOfBoundsException is - * thrown. - *

- *

If len is zero, then no bytes are read and - * 0 is returned; otherwise, there is an attempt to read at - * least one byte. If no byte is available because the stream is at end of - * file, the value -1 is returned; otherwise, at least one - * byte is read and stored into b. - *

- *

The first byte read is stored into element b[off], the - * next one into b[off+1], and so on. The number of bytes read - * is, at most, equal to len. Let k be the number of - * bytes actually read; these bytes will be stored in elements - * b[off] through b[off+k-1], - * leaving elements b[off+k] through - * b[off+len-1] unaffected. - *

- *

In every case, elements b[0] through - * b[off] and elements b[off+len] through - * b[b.length-1] are unaffected. - *

- *

If the first byte cannot be read for any reason other than end of - * file, then an IOException is thrown. In particular, an - * IOException is thrown if the input stream has been closed. - * - * @param b the buffer into which the data is read. - * @param off the start offset in array b - * at which the data is written. - * @param len the maximum number of bytes to read. - * @return the total number of bytes read into the buffer, or - * -1 if there is no more data because the end of - * the stream has been reached. - * @throws IOException if an I/O error occurs. - * @throws NullPointerException if b is null. - */ - public int read(byte[] b, int off, int len) throws IOException { - return eq.read(b, off, len); - } - - /** - *

The method reset for class EqualizerInputStream - * does nothing except throw an IOException. - * - * @throws IOException as an indication that the mark feature doesn't supported by EqualizerInputStream. - */ - public void reset() throws IOException { - eq.reset(); - } - - /** - * Skips over and discards n bytes of data from this input - * stream. The skip method may, for a variety of reasons, end - * up skipping over some smaller number of bytes, possibly 0. - * This may result from any of a number of conditions; reaching end of file - * before n bytes have been skipped is only one possibility. - * The actual number of bytes skipped is returned. If n is - * negative, no bytes are skipped. - * - * @param n the number of bytes to be skipped. - * @return the actual number of bytes skipped. - * @throws IOException if an I/O error occurs. - */ - public long skip(long n) throws IOException { - return eq.skip(n); - } -} diff --git a/src/uk/co/majenko/audiobookrecorder/Amplifier.java b/src/uk/co/majenko/audiobookrecorder/Amplifier.java new file mode 100644 index 0000000..0c3e425 --- /dev/null +++ b/src/uk/co/majenko/audiobookrecorder/Amplifier.java @@ -0,0 +1,45 @@ +package uk.co.majenko.audiobookrecorder; + +import java.util.ArrayList; +import javax.swing.tree.*; + +public class Amplifier extends DefaultMutableTreeNode implements Effect { + double gain; + public Amplifier() { + gain = 1.0d; + } + public Amplifier(double g) { + gain = g; + } + + public String getName() { + return "Amplifier (" + gain + ")"; + } + + public ArrayList getChildEffects() { + return null; + } + + public double process(double sample) { + return sample * gain; + } + + public double getGain() { + return gain; + } + + public void setGain(double g) { + gain = g; + } + + public String toString() { + return getName(); + } + + public void dump() { + System.out.println(toString()); + } + + public void init(double sf) { + } +} diff --git a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java index 8b3ff68..c6150b0 100644 --- a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java +++ b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java @@ -17,6 +17,9 @@ import java.nio.file.Files; import java.util.zip.*; import javax.swing.filechooser.*; import javax.imageio.*; +import org.w3c.dom.*; +import javax.xml.parsers.*; +import java.io.*; public class AudiobookRecorder extends JFrame { @@ -25,6 +28,9 @@ public class AudiobookRecorder extends JFrame { public static final int PLAYBACK_CHUNK_SIZE = 256; // Was 1024 static Properties config = new Properties(); + HashMap effects; + + String defaultEffectChain = "none"; MainToolBar toolBar; @@ -82,13 +88,12 @@ public class AudiobookRecorder extends JFrame { JSpinner gainPercent; JCheckBox locked; JCheckBox attention; - JCheckBox ethereal; JButtonSpacePlay reprocessAudioFFT; JButtonSpacePlay reprocessAudioPeak; JButtonSpacePlay normalizeAudio; - JComboBox eqProfile; + JComboBox> effectChain; Thread playingThread = null; @@ -367,7 +372,7 @@ public class AudiobookRecorder extends JFrame { public void actionPerformed(ActionEvent e) { if (selectedSentence != null) { selectedSentence.autoTrimSampleFFT(); - sampleWaveform.setData(selectedSentence.getAudioData()); + sampleWaveform.setData(selectedSentence.getDoubleAudioData()); sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset()); sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing()); postSentenceGap.setValue(selectedSentence.getPostGap()); @@ -380,7 +385,7 @@ public class AudiobookRecorder extends JFrame { public void actionPerformed(ActionEvent e) { if (selectedSentence != null) { selectedSentence.autoTrimSamplePeak(); - sampleWaveform.setData(selectedSentence.getAudioData()); + sampleWaveform.setData(selectedSentence.getDoubleAudioData()); sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset()); sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing()); postSentenceGap.setValue(selectedSentence.getPostGap()); @@ -393,7 +398,7 @@ public class AudiobookRecorder extends JFrame { public void actionPerformed(ActionEvent e) { if (selectedSentence != null) { selectedSentence.normalize(); - sampleWaveform.setData(selectedSentence.getAudioData()); + sampleWaveform.setData(selectedSentence.getDoubleAudioData()); } } }); @@ -419,7 +424,7 @@ public class AudiobookRecorder extends JFrame { JSpinner ob = (JSpinner)e.getSource(); if (selectedSentence != null) { selectedSentence.setGain((Integer)ob.getValue() / 100d); - sampleWaveform.setData(selectedSentence.getAudioData()); + sampleWaveform.setData(selectedSentence.getDoubleAudioData()); } } }); @@ -484,27 +489,6 @@ public class AudiobookRecorder extends JFrame { controlsTop.add(attention); - ethereal = new JCheckBox("Ethereal voice"); - ethereal.setFocusable(false); - - ethereal.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - JCheckBox c = (JCheckBox)e.getSource(); - if (c.isSelected()) { - if (selectedSentence != null) { - selectedSentence.setEthereal(true); - } - } else { - if (selectedSentence != null) { - selectedSentence.setEthereal(false); - } - } - bookTreeModel.reload(selectedSentence); - } - }); - - controlsTop.add(ethereal); - controlsTop.add(Box.createHorizontalGlue()); controlsTop.add(new JLabel("Post gap:")); controlsTop.add(postSentenceGap); @@ -531,19 +515,18 @@ public class AudiobookRecorder extends JFrame { }); controlsRight.add(zoomOut); - controlsBottom.add(new JLabel("EQ Profile: ")); + controlsBottom.add(new JLabel("Effects Chain: ")); - String[] profiles = new String[2]; - profiles[0] = "Default"; - profiles[1] = "Phone"; + effectChain = new JComboBox>(); + controlsBottom.add(effectChain); - eqProfile = new JComboBox(profiles); - controlsBottom.add(eqProfile); - - eqProfile.addActionListener(new ActionListener() { + effectChain.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (selectedSentence != null) { - selectedSentence.setEQProfile(eqProfile.getSelectedIndex()); + int i = effectChain.getSelectedIndex(); + KVPair p = effectChain.getItemAt(i); + selectedSentence.setEffectChain(p.getKey()); + updateWaveform(); } } }); @@ -1197,34 +1180,32 @@ public class AudiobookRecorder extends JFrame { ); tabs.add("Data", info); - JPanel effects = new JPanel(); - effects.setLayout(new GridBagLayout()); + JPanel effectsPanel = new JPanel(); + effectsPanel.setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.gridx = 0; c.gridy = 0; - effects.add(new JLabel("Ethereal Iterations:"), c); + effectsPanel.add(new JLabel("Default Effect:"), c); c.gridx = 1; - JSpinner ethIt = new JSpinner(new SteppedNumericSpinnerModel(1, 10, 1, book.getInteger("effects.ethereal.iterations"))); - effects.add(ethIt, c); - c.gridx = 0; - c.gridy++; - effects.add(new JLabel("Ethereal Attenuation:"), c); - c.gridx = 1; - JSpinner ethAt = new JSpinner(new SteppedNumericSpinnerModel(0, 100, 1, book.getInteger("effects.ethereal.attenuation"))); - effects.add(ethAt, c); - c.gridx = 0; - c.gridy++; + JComboBox> defEff = new JComboBox>(); + int selEff = -1; + int i = 0; + for (String k : effects.keySet()) { + if (k.equals(defaultEffectChain)) { + selEff = i; + } + KVPair p = new KVPair(k, effects.get(k).toString()); + defEff.addItem(p); + i++; + } - effects.add(new JLabel("Ethereal Offset:"), c); - c.gridx = 1; - JSpinner ethOf = new JSpinner(new SteppedNumericSpinnerModel(0, 2000, 10, book.getInteger("effects.ethereal.offset"))); - effects.add(ethOf, c); - c.gridx = 0; - c.gridy++; + defEff.setSelectedIndex(selEff); + + effectsPanel.add(defEff, c); - tabs.add("Effects", effects); + tabs.add("Effects", effectsPanel); int r = JOptionPane.showConfirmDialog(AudiobookRecorder.this, tabs, "Edit Book", JOptionPane.OK_CANCEL_OPTION); if (r != JOptionPane.OK_OPTION) return; @@ -1235,9 +1216,9 @@ public class AudiobookRecorder extends JFrame { String com = info.getComment(); String acx = info.getACX(); - book.set("effects.ethereal.iterations", (Integer)ethIt.getValue()); - book.set("effects.ethereal.attenuation", (Integer)ethAt.getValue()); - book.set("effects.ethereal.offset", (Integer)ethOf.getValue()); + i = defEff.getSelectedIndex(); + KVPair de = defEff.getItemAt(i); + defaultEffectChain = de.getKey(); book.setAuthor(aut); book.setGenre(gen); @@ -1519,10 +1500,6 @@ public class AudiobookRecorder extends JFrame { File config = new File(bookRoot, "audiobook.abk"); Properties prefs = new Properties(); - prefs.setProperty("effects.ethereal.iterations", book.get("effects.ethereal.iterations")); - prefs.setProperty("effects.ethereal.offset", book.get("effects.ethereal.offset")); - prefs.setProperty("effects.ethereal.attenuation", book.get("effects.ethereal.attenuation")); - prefs.setProperty("book.name", book.getName()); prefs.setProperty("book.author", book.getAuthor()); prefs.setProperty("book.genre", book.getGenre()); @@ -1533,11 +1510,7 @@ public class AudiobookRecorder extends JFrame { prefs.setProperty("audio.recording.resolution", "" + book.getResolution()); prefs.setProperty("audio.recording.channels", "" + book.getChannels()); - for (int e = 0; e < book.equaliser.length; e++) { - for (int i = 0; i < 31; i++) { - prefs.setProperty(String.format("audio.eq.profiles.%d.%d", e, i), String.format("%.3f", book.equaliser[e].getChannel(i))); - } - } + prefs.setProperty("audio.effect.default", defaultEffectChain); for (Enumeration o = book.children(); o.hasMoreElements();) { @@ -1557,9 +1530,8 @@ public class AudiobookRecorder extends JFrame { prefs.setProperty(String.format("%s.sentence.%08d.end-offset", keybase, i), Integer.toString(snt.getEndOffset())); prefs.setProperty(String.format("%s.sentence.%08d.locked", keybase, i), snt.isLocked() ? "true" : "false"); prefs.setProperty(String.format("%s.sentence.%08d.attention", keybase, i), snt.getAttentionFlag() ? "true" : "false"); - prefs.setProperty(String.format("%s.sentence.%08d.ethereal", keybase, i), snt.getEthereal() ? "true" : "false"); prefs.setProperty(String.format("%s.sentence.%08d.gain", keybase, i), String.format("%.8f", snt.getGain())); - prefs.setProperty(String.format("%s.sentence.%08d.eqprofile", keybase, i), Integer.toString(snt.getEQProfile())); + prefs.setProperty(String.format("%s.sentence.%08d.effect", keybase, i), snt.getEffectChain()); i++; } } @@ -1585,9 +1557,11 @@ public class AudiobookRecorder extends JFrame { FileInputStream fis = new FileInputStream(f); prefs.loadFromXML(fis); + File r = f.getParentFile(); + loadEffects(); + buildBook(prefs); - File r = f.getParentFile(); File cf = new File(r, "coverart.png"); if (!cf.exists()) { cf = new File(r, "coverart.jpg"); @@ -1616,6 +1590,11 @@ public class AudiobookRecorder extends JFrame { book.setComment(prefs.getProperty("book.comment")); book.setACX(prefs.getProperty("book.acx")); + defaultEffectChain = prefs.getProperty("audio.effect.default"); + if (defaultEffectChain == null) { + defaultEffectChain = "none"; + } + int sr = Utils.s2i(prefs.getProperty("audio.recording.samplerate")); if (sr == 0) { sr = Options.getInteger("audio.recording.samplerate"); @@ -1634,17 +1613,6 @@ public class AudiobookRecorder extends JFrame { } book.setResolution(res); - - for (int e = 0; e < book.equaliser.length; e++) { - for (int i = 0; i < 31; i++) { - if (prefs.getProperty(String.format("audio.eq.profiles.%d.%d", e, i)) == null) { - book.equaliser[e].setChannel(i, Options.getFloat("audio.eq." + i)); - } else { - book.equaliser[e].setChannel(i, Utils.s2f(prefs.getProperty(String.format("audio.eq.profiles.%d.%d", e, i)))); - } - } - } - bookTreeModel = new DefaultTreeModel(book); bookTree = new JTree(bookTreeModel); bookTree.setEditable(true); @@ -1664,7 +1632,7 @@ public class AudiobookRecorder extends JFrame { if (n instanceof Sentence) { Sentence s = (Sentence)n; selectedSentence = s; - sampleWaveform.setData(s.getAudioData()); + sampleWaveform.setData(s.getDoubleAudioData()); sampleWaveform.setMarkers(s.getStartOffset(), s.getEndOffset()); s.updateCrossings(); sampleWaveform.setAltMarkers(s.getStartCrossing(), s.getEndCrossing()); @@ -1672,8 +1640,8 @@ public class AudiobookRecorder extends JFrame { gainPercent.setValue((int)(s.getGain() * 100d)); locked.setSelected(s.isLocked()); attention.setSelected(s.getAttentionFlag()); - ethereal.setSelected(s.getEthereal()); - eqProfile.setSelectedIndex(s.getEQProfile()); + + setEffectChain(s.getEffectChain()); postSentenceGap.setEnabled(!s.isLocked()); gainPercent.setEnabled(!s.isLocked()); @@ -1684,10 +1652,8 @@ public class AudiobookRecorder extends JFrame { sampleWaveform.clearData(); postSentenceGap.setValue(0); gainPercent.setValue(100); - eqProfile.setSelectedIndex(0); locked.setSelected(false); attention.setSelected(false); - ethereal.setSelected(false); } } }); @@ -1727,9 +1693,8 @@ public class AudiobookRecorder extends JFrame { s.setEndOffset(Utils.s2i(prefs.getProperty(String.format("chapter.audition.sentence.%08d.end-offset", i)))); s.setLocked(Utils.s2b(prefs.getProperty(String.format("chapter.audition.sentence.%08d.locked", i)))); s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.audition.sentence.%08d.attention", i)))); - s.setEthereal(Utils.s2b(prefs.getProperty(String.format("chapter.audition.sentence.%08d.ethereal", i)))); s.setGain(Utils.s2d(prefs.getProperty(String.format("chapter.audition.sentence.%08d.gain", i)))); - s.setEQProfile(Utils.s2i(prefs.getProperty(String.format("chapter.audition.sentence.%08d.eqprofile", i)))); + s.setEffectChain(prefs.getProperty(String.format("chapter.audition.sentence.%08d.effect", i))); bookTreeModel.insertNodeInto(s, c, c.getChildCount()); } @@ -1749,9 +1714,8 @@ public class AudiobookRecorder extends JFrame { s.setEndOffset(Utils.s2i(prefs.getProperty(String.format("chapter.open.sentence.%08d.end-offset", i)))); s.setLocked(Utils.s2b(prefs.getProperty(String.format("chapter.open.sentence.%08d.locked", i)))); s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.open.sentence.%08d.attention", i)))); - s.setEthereal(Utils.s2b(prefs.getProperty(String.format("chapter.open.sentence.%08d.ethereal", i)))); s.setGain(Utils.s2d(prefs.getProperty(String.format("chapter.open.sentence.%08d.gain", i)))); - s.setEQProfile(Utils.s2i(prefs.getProperty(String.format("chapter.open.sentence.%08d.eqprofile", i)))); + s.setEffectChain(prefs.getProperty(String.format("chapter.open.sentence.%08d.effect", i))); bookTreeModel.insertNodeInto(s, c, c.getChildCount()); } @@ -1777,9 +1741,8 @@ public class AudiobookRecorder extends JFrame { s.setEndOffset(Utils.s2i(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.end-offset", cno, i)))); s.setLocked(Utils.s2b(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.locked", cno, i)))); s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.attention", cno, i)))); - s.setEthereal(Utils.s2b(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.ethereal", cno, i)))); s.setGain(Utils.s2d(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.gain", cno, i)))); - s.setEQProfile(Utils.s2i(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.eqprofile", cno, i)))); + s.setEffectChain(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.effect", cno, i))); bookTreeModel.insertNodeInto(s, c, c.getChildCount()); } } @@ -1800,9 +1763,8 @@ public class AudiobookRecorder extends JFrame { s.setEndOffset(Utils.s2i(prefs.getProperty(String.format("chapter.close.sentence.%08d.end-offset", i)))); s.setLocked(Utils.s2b(prefs.getProperty(String.format("chapter.close.sentence.%08d.locked", i)))); s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.close.sentence.%08d.attention", i)))); - s.setEthereal(Utils.s2b(prefs.getProperty(String.format("chapter.close.sentence.%08d.ethereal", i)))); s.setGain(Utils.s2d(prefs.getProperty(String.format("chapter.close.sentence.%08d.gain", i)))); - s.setEQProfile(Utils.s2i(prefs.getProperty(String.format("chapter.close.sentence.%08d.eqprofile", i)))); + s.setEffectChain(prefs.getProperty(String.format("chapter.close.sentence.%08d.effect", i))); bookTreeModel.insertNodeInto(s, c, c.getChildCount()); } @@ -1852,28 +1814,27 @@ public class AudiobookRecorder extends JFrame { return bf; } - public int getNoiseFloor() { + public double getNoiseFloor() { if (roomNoise == null) return 0; - int[] samples = roomNoise.getAudioData(); + double[] samples = roomNoise.getDoubleAudioData(); if (samples == null) { return 0; } - int ms = 0; + double ms = 0; for (int i = 0; i < samples.length; i++) { if (Math.abs(samples[i]) > ms) { ms = Math.abs(samples[i]); } } - ms *= 10; - ms /= 7; + ms *= 10d; + ms /= 7d; return ms; } public int getNoiseFloorDB() { - int nf = getNoiseFloor(); - if (nf == 0) return 0; - double r = nf / 32767d; + double r = getNoiseFloor(); + if (r == 0) return 0; double l10 = Math.log10(r); double db = 20d * l10; @@ -1912,13 +1873,15 @@ public class AudiobookRecorder extends JFrame { try { - AudioFormat format = s.getAudioFormat(); + AudioFormat sampleformat = s.getAudioFormat(); + AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 1, true, false); + play = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer()); play.open(format); play.start(); bookTree.scrollPathToVisible(new TreePath(s.getPath())); - data = s.getRawAudioData(); + data = s.getPCMData(); for (int pos = 0; pos < data.length; pos += PLAYBACK_CHUNK_SIZE) { sampleWaveform.setPlayMarker(pos / format.getFrameSize()); int l = data.length - pos; @@ -2004,7 +1967,8 @@ public class AudiobookRecorder extends JFrame { try { - AudioFormat format = s.getAudioFormat(); + AudioFormat sampleformat = s.getAudioFormat(); + AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 1, true, false); play = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer()); play.open(format); play.start(); @@ -2022,7 +1986,7 @@ public class AudiobookRecorder extends JFrame { data = getRoomNoise(Utils.s2i(Options.get("catenation.pre-chapter"))); play.write(data, 0, data.length); } - data = s.getRawAudioData(); + data = s.getPCMData(); for (int pos = 0; pos < data.length; pos += PLAYBACK_CHUNK_SIZE) { sampleWaveform.setPlayMarker(pos / format.getFrameSize()); int l = data.length - pos; @@ -2091,7 +2055,7 @@ public class AudiobookRecorder extends JFrame { roomNoise.setStartOffset(start); roomNoise.setEndOffset(end); - byte[] data = roomNoise.getRawAudioData(); + byte[] data = roomNoise.getPCMData(); return data; } @@ -2108,23 +2072,9 @@ public class AudiobookRecorder extends JFrame { JOptionPane.showMessageDialog(this, "You must record room noise\nbefore recording or playback", "Error", JOptionPane.ERROR_MESSAGE); } - public void showEqualiser() { - if (equaliserWindow == null) { - equaliserWindow = new JDialog(); - equaliserWindow.setTitle("Equaliser"); - JTabbedPane tabs = new JTabbedPane(); - equaliserWindow.add(tabs); - for (int i = 0; i < book.equaliser.length; i++) { - tabs.add(book.equaliser[i].getName(), new JScrollPane(book.equaliser[i])); - } - equaliserWindow.pack(); - } - equaliserWindow.setVisible(true); - equaliserWindow.setLocationRelativeTo(this); - } - public boolean enableMicrophone() { AudioFormat format = Options.getAudioFormat(); +System.err.println(format); Mixer.Info mixer = Options.getRecordingMixer(); @@ -2375,6 +2325,33 @@ public class AudiobookRecorder extends JFrame { } } + // Now grab any used effects that aren't already part of the book folder + ArrayList usedEffects = book.getUsedEffects(); + for (String ef : usedEffects) { + File inBook = new File(bookDir, ef + ".eff"); + if (!inBook.exists()) { + File sys = new File(storageDir, "System"); + File sysFile = new File(sys, ef + ".eff"); + if (sysFile.exists()) { + ZipEntry entry = new ZipEntry(name + "/" + ef + ".eff"); + entry.setSize(sysFile.length()); + entry.setTime(sysFile.lastModified()); + zos.putNextEntry(entry); + + FileInputStream fis = new FileInputStream(sysFile); + byte[] buffer = new byte[1024]; + int bytesRead = 0; + while ((bytesRead = fis.read(buffer, 0, 1024)) != -1) { + zos.write(buffer, 0, bytesRead); + } + fis.close(); + zos.closeEntry(); + + } + } + } + + zos.flush(); zos.close(); @@ -2530,7 +2507,194 @@ public class AudiobookRecorder extends JFrame { public void updateWaveform() { if (selectedSentence != null) { - sampleWaveform.setData(selectedSentence.getAudioData()); + sampleWaveform.setData(selectedSentence.getDoubleAudioData()); + } + } + + public void loadEffects() { + effects = new HashMap(); + loadEffectsFromFolder(new File(Options.get("path.storage"), "System")); + if (book != null) { + loadEffectsFromFolder(new File(Options.get("path.storage"), book.getName())); + } + updateEffectChains(); + } + + public void loadEffectsFromFolder(File dir) { + if (dir == null) return; + File[] files = dir.listFiles(); + for (File f : files) { + if (f.getName().endsWith(".eff")) { + EffectGroup g = loadEffect(f); + if (g != null) { + String fn = f.getName().replace(".eff",""); + effects.put(fn, g); + } + } + } + } + + public EffectGroup loadEffect(File xml) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(xml); + + Element root = document.getDocumentElement(); + if (root.getTagName().equals("effect")) { + EffectGroup g = loadEffectGroup(root); + return g; + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public EffectGroup loadEffectGroup(Element root) { + EffectGroup group = new EffectGroup(root.getAttribute("name")); + NodeList kids = root.getChildNodes(); + for (int i = 0; i < kids.getLength(); i++) { + Node kid = kids.item(i); + if (kid instanceof Element) { + Element e = (Element)kid; + if (e.getTagName().equals("biquad")) { + Effect eff = (Effect)loadBiquad(e); + if (eff != null) { + group.addEffect(eff); + } + } else if (e.getTagName().equals("delayline")) { + Effect eff = (Effect)loadDelayLine(e); + if (eff != null) { + group.addEffect(eff); + } + } else if (e.getTagName().equals("amplifier")) { + Effect eff = (Effect)loadAmplifier(e); + if (eff != null) { + group.addEffect(eff); + } + } else if (e.getTagName().equals("group")) { + Effect eff = (Effect)loadEffectGroup(e); + if (eff != null) { + group.addEffect(eff); + } + } + } + } + return group; + } + + public Biquad loadBiquad(Element root) { + String type = root.getAttribute("type").toLowerCase(); + Biquad bq = new Biquad(); + + if (type.equals("lowpass")) { + bq.setType(Biquad.Lowpass); + } else if (type.equals("highpass")) { + bq.setType(Biquad.Highpass); + } else if (type.equals("bandpass")) { + bq.setType(Biquad.Bandpass); + } else if (type.equals("notch")) { + bq.setType(Biquad.Notch); + } else if (type.equals("peak")) { + bq.setType(Biquad.Peak); + } else if (type.equals("lowshelf")) { + bq.setType(Biquad.Lowshelf); + } else if (type.equals("highshelf")) { + bq.setType(Biquad.Highshelf); + } else { + System.err.println("Bad Biquad type: " + type); + return null; + } + + bq.setQ(Utils.s2d(root.getAttribute("q"))); + bq.setFc(Utils.s2d(root.getAttribute("fc"))); + bq.setPeakGain(Utils.s2d(root.getAttribute("gain"))); + return bq; + } + + public DelayLine loadDelayLine(Element root) { + DelayLine line = new DelayLine(); + + NodeList list = root.getChildNodes(); + + for (int i = 0; i < list.getLength(); i++) { + Node n = list.item(i); + if (n instanceof Element) { + Element e = (Element)n; + if (e.getTagName().equals("delay")) { + int samples = Utils.s2i(e.getAttribute("samples")); + double gain = Utils.s2d(e.getAttribute("gain")); + DelayLineStore store = line.addDelayLine(samples, gain); + + NodeList inner = e.getChildNodes(); + for (int j = 0; j < inner.getLength(); j++) { + Node in = inner.item(j); + if (in instanceof Element) { + Element ie = (Element)in; + + if (ie.getTagName().equals("biquad")) { + Effect eff = (Effect)loadBiquad(ie); + if (eff != null) { + store.addEffect(eff); + } + } else if (ie.getTagName().equals("delayline")) { + Effect eff = (Effect)loadDelayLine(ie); + if (eff != null) { + store.addEffect(eff); + } + } else if (ie.getTagName().equals("amplifier")) { + Effect eff = (Effect)loadAmplifier(ie); + if (eff != null) { + store.addEffect(eff); + } + } else if (ie.getTagName().equals("group")) { + Effect eff = (Effect)loadEffectGroup(ie); + if (eff != null) { + store.addEffect(eff); + } + } + } + } + } + } + } + + return line; + } + + public Amplifier loadAmplifier(Element root) { + Amplifier a = new Amplifier(Utils.s2d(root.getAttribute("gain"))); + return a; + } + + public void updateEffectChains() { + while (effectChain.getItemCount() > 0) { + effectChain.removeItemAt(0); + } + for (String k : effects.keySet()) { + Effect e = effects.get(k); + KVPair p = new KVPair(k, e.toString()); + effectChain.addItem(p); + } + } + + public void setEffectChain(String key) { + for (int i = 0; i < effectChain.getItemCount(); i++) { + KVPair p = effectChain.getItemAt(i); + if (p.getKey().equals(key)) { + effectChain.setSelectedIndex(i); + updateWaveform(); + return; + } + } + + if (effects.get(defaultEffectChain) != null) { + setEffectChain(defaultEffectChain); + updateWaveform(); + } else { + effectChain.setSelectedIndex(0); + updateWaveform(); } } } diff --git a/src/uk/co/majenko/audiobookrecorder/Biquad.java b/src/uk/co/majenko/audiobookrecorder/Biquad.java index 735506a..0dc3798 100644 --- a/src/uk/co/majenko/audiobookrecorder/Biquad.java +++ b/src/uk/co/majenko/audiobookrecorder/Biquad.java @@ -1,6 +1,6 @@ package uk.co.majenko.audiobookrecorder; -// Biquad.h +// Biquad.java // // Created by Nigel Redmon on 11/24/12 // EarLevel Engineering: earlevel.com @@ -18,38 +18,44 @@ package uk.co.majenko.audiobookrecorder; // for your own purposes, free or commercial. // -public class Biquad { - public static final int bq_type_lowpass = 0; - public static final int bq_type_highpass = 1; - public static final int bq_type_bandpass = 2; - public static final int bq_type_notch = 3; - public static final int bq_type_peak = 4; - public static final int bq_type_lowshelf = 5; - public static final int bq_type_highshelf = 6; +import java.util.ArrayList; +import javax.swing.tree.*; + +public class Biquad extends DefaultMutableTreeNode implements Effect { + public static final int Lowpass = 0; + public static final int Highpass = 1; + public static final int Bandpass = 2; + public static final int Notch = 3; + public static final int Peak = 4; + public static final int Lowshelf = 5; + public static final int Highshelf = 6; int type; double a0, a1, a2, b1, b2; double Fc, Q, peakGain; double z1, z2; + double sampleFrequency; public Biquad() { - type = bq_type_lowpass; + type = Lowpass; a0 = 1.0d; a1 = 0.0d; a2 = 0.0d; b1 = 0.0d; b2 = 0.0d; - Fc = 0.50d; + Fc = 440d; Q = 0.707d; peakGain = 0.0d; z1 = 0.0d; z2 = 0.0d; + sampleFrequency = 44100d; } public Biquad(int type, double Fc, double Q, double peakGainDB) { setBiquad(type, Fc, Q, peakGainDB); z1 = 0.0; z2 = 0.0; + sampleFrequency = 44100d; } public void setType(int typei) { @@ -79,20 +85,27 @@ public class Biquad { setPeakGain(peakGainDB); } - public float process(float in) { + public double process(double in) { double out = in * a0 + z1; z1 = in * a1 + z2 - b1 * out; z2 = in * a2 - b2 * out; - return (float)out; + return out; + } + + public void init(double sf) { + sampleFrequency = sf; + z1 = 0d; + z2 = 0d; + calcBiquad(); } void calcBiquad() { double norm; double V = Math.pow(10, Math.abs(peakGain) / 20.0); - double K = Math.tan(Math.PI * Fc); + double K = Math.tan(Math.PI * (Fc/sampleFrequency)); switch (type) { - case bq_type_lowpass: + case Lowpass: norm = 1d / (1d + K / Q + K * K); a0 = K * K * norm; a1 = 2d * a0; @@ -101,7 +114,7 @@ public class Biquad { b2 = (1d - K / Q + K * K) * norm; break; - case bq_type_highpass: + case Highpass: norm = 1d / (1d + K / Q + K * K); a0 = 1d * norm; a1 = -2d * a0; @@ -110,7 +123,7 @@ public class Biquad { b2 = (1d - K / Q + K * K) * norm; break; - case bq_type_bandpass: + case Bandpass: norm = 1d / (1d + K / Q + K * K); a0 = K / Q * norm; a1 = 0d; @@ -119,7 +132,7 @@ public class Biquad { b2 = (1d - K / Q + K * K) * norm; break; - case bq_type_notch: + case Notch: norm = 1d / (1d + K / Q + K * K); a0 = (1d + K * K) * norm; a1 = 2d * (K * K - 1d) * norm; @@ -128,7 +141,7 @@ public class Biquad { b2 = (1d - K / Q + K * K) * norm; break; - case bq_type_peak: + case Peak: if (peakGain >= 0d) { // boost norm = 1d / (1d + 1d/Q * K + K * K); a0 = (1d + V/Q * K + K * K) * norm; @@ -146,7 +159,7 @@ public class Biquad { b2 = (1d - V/Q * K + K * K) * norm; } break; - case bq_type_lowshelf: + case Lowshelf: if (peakGain >= 0) { // boost norm = 1d / (1 + Math.sqrt(2d) * K + K * K); a0 = (1d + Math.sqrt(2d*V) * K + V * K * K) * norm; @@ -164,7 +177,7 @@ public class Biquad { b2 = (1d - Math.sqrt(2d*V) * K + V * K * K) * norm; } break; - case bq_type_highshelf: + case Highshelf: if (peakGain >= 0d) { // boost norm = 1d / (1d + Math.sqrt(2d) * K + K * K); a0 = (V + Math.sqrt(2d*V) * K + K * K) * norm; @@ -186,4 +199,37 @@ public class Biquad { return; } + + public String getName() { + String n = "Biquad Filter ("; + switch (type) { + case Lowpass: n += "Lowpass"; break; + case Highpass: n += "Highpass"; break; + case Bandpass: n += "Bandpass"; break; + case Notch: n += "Notch"; break; + case Peak: n += "Peak"; break; + case Lowshelf: n += "Lowshelf"; break; + case Highshelf: n += "Highshelf"; break; + } + n += ", Fc="; + n += Fc; + n += ", Q="; + n += Q; + n += ", Gain="; + n += peakGain; + n += "dB)"; + return n; + } + + public ArrayList getChildEffects() { + return null; + } + + public String toString() { + return getName(); + } + + public void dump() { + System.out.println(toString()); + } } diff --git a/src/uk/co/majenko/audiobookrecorder/Book.java b/src/uk/co/majenko/audiobookrecorder/Book.java index e79ee25..39399e7 100644 --- a/src/uk/co/majenko/audiobookrecorder/Book.java +++ b/src/uk/co/majenko/audiobookrecorder/Book.java @@ -8,7 +8,6 @@ import java.util.*; import java.io.*; import java.nio.file.*; import javax.swing.tree.*; -import davaguine.jeq.core.IIRControls; import javax.sound.sampled.*; public class Book extends DefaultMutableTreeNode { @@ -25,10 +24,6 @@ public class Book extends DefaultMutableTreeNode { ImageIcon icon; - public Equaliser[] equaliser = new Equaliser[2]; - - float[] eqChannels = new float[31]; - Properties prefs; public Book(Properties p, String bookname) { @@ -36,8 +31,6 @@ public class Book extends DefaultMutableTreeNode { prefs = p; name = bookname; - equaliser[0] = new Equaliser("Default"); - equaliser[1] = new Equaliser("Phone"); AudiobookRecorder.window.setTitle("AudioBook Recorder :: " + name); } @@ -187,4 +180,30 @@ public class Book extends DefaultMutableTreeNode { public void set(String key, Integer value) { prefs.setProperty(key, "" + value); } + + public File getBookFolder() { + File dir = new File(Options.get("path.storage"), name); + return dir; + } + + public ArrayList getUsedEffects() { + + ArrayList out = new ArrayList(); + + for (Enumeration o = children(); o.hasMoreElements();) { + Object ob = (Object)o.nextElement(); + if (ob instanceof Chapter) { + Chapter c = (Chapter)ob; + ArrayList effs = c.getUsedEffects(); + for (String ef : effs) { + if (out.indexOf(ef) == -1) { + out.add(ef); + } + } + } + } + + return out; + } + } diff --git a/src/uk/co/majenko/audiobookrecorder/BookTreeRenderer.java b/src/uk/co/majenko/audiobookrecorder/BookTreeRenderer.java index 659d64f..f9b1622 100644 --- a/src/uk/co/majenko/audiobookrecorder/BookTreeRenderer.java +++ b/src/uk/co/majenko/audiobookrecorder/BookTreeRenderer.java @@ -31,10 +31,6 @@ public class BookTreeRenderer extends DefaultTreeCellRenderer { icn.add(Overlays.important, OverlayIcon.TOP_RIGHT); } - if (s.getEthereal()) { - icn.add(Overlays.filter, OverlayIcon.BOTTOM_RIGHT); - } - ret.setIcon(icn); } else if (value instanceof Chapter) { diff --git a/src/uk/co/majenko/audiobookrecorder/Chapter.java b/src/uk/co/majenko/audiobookrecorder/Chapter.java index b8e2f4c..25042d2 100644 --- a/src/uk/co/majenko/audiobookrecorder/Chapter.java +++ b/src/uk/co/majenko/audiobookrecorder/Chapter.java @@ -121,7 +121,8 @@ public class Chapter extends DefaultMutableTreeNode { attributes.setAudioAttributes(audioAttributes); - AudioFormat format = AudiobookRecorder.window.roomNoise.getAudioFormat(); + AudioFormat sampleformat = AudiobookRecorder.window.roomNoise.getAudioFormat(); + AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 1, true, false); byte[] data; int fullLength = 0; @@ -149,7 +150,7 @@ public class Chapter extends DefaultMutableTreeNode { kidno++; if (exportDialog != null) exportDialog.setProgress(kidno * 1000 / kids); Sentence snt = (Sentence)s.nextElement(); - data = snt.getRawAudioData(); + data = snt.getPCMData(); fullLength += data.length; fos.write(data); @@ -214,4 +215,22 @@ public class Chapter extends DefaultMutableTreeNode { return totalTime; } + public ArrayList getUsedEffects() { + + ArrayList out = new ArrayList(); + + for (Enumeration o = children(); o.hasMoreElements();) { + Object ob = (Object)o.nextElement(); + if (ob instanceof Sentence) { + Sentence s = (Sentence)ob; + String ec = s.getEffectChain(); + if (out.indexOf(ec) == -1) { + out.add(ec); + } + } + } + return out; + } + + } diff --git a/src/uk/co/majenko/audiobookrecorder/DelayLine.java b/src/uk/co/majenko/audiobookrecorder/DelayLine.java new file mode 100644 index 0000000..9483b75 --- /dev/null +++ b/src/uk/co/majenko/audiobookrecorder/DelayLine.java @@ -0,0 +1,66 @@ +package uk.co.majenko.audiobookrecorder; + +import java.util.ArrayList; +import javax.swing.tree.*; + +public class DelayLine extends DefaultMutableTreeNode implements Effect { + + ArrayList delayLines; + + public DelayLine() { + delayLines = new ArrayList(); + } + + public String getName() { + return "Delay Line (" + delayLines.size() + " lines)"; + } + + public double process(double sample) { + double s = sample; + for (DelayLineStore d : delayLines) { + double echo = d.pass(sample); + s = mix(s, echo); + } + return s; + } + + double mix(double a, double b) { + if ((a < 0) && (b < 0)) { + return (a + b) - (a * b); + } + if ((a > 0) && (b > 0)) { + return (a + b) - (a * b); + } + return a + b; + } + + public DelayLineStore addDelayLine(int samples, double gain) { + DelayLineStore s = new DelayLineStore(samples, gain); + delayLines.add(s); + return s; + } + + public ArrayList getChildEffects() { + return null; + } + + public String toString() { + return getName(); + } + + public void dump() { + System.out.println(toString()); + for (DelayLineStore s : delayLines) { + s.dump(); + } + } + + public void init(double sf) { + for (DelayLineStore s : delayLines) { + s.init(sf); + s.purge(); + } + } + + +} diff --git a/src/uk/co/majenko/audiobookrecorder/DelayLineStore.java b/src/uk/co/majenko/audiobookrecorder/DelayLineStore.java new file mode 100644 index 0000000..7f9fec9 --- /dev/null +++ b/src/uk/co/majenko/audiobookrecorder/DelayLineStore.java @@ -0,0 +1,81 @@ +package uk.co.majenko.audiobookrecorder; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.ArrayList; + +public class DelayLineStore { + ArrayBlockingQueue fifo; + double gain; + int numSamples; + + ArrayList effects; + + public DelayLineStore(int s, double g) { + fifo = new ArrayBlockingQueue(s); + for (int i = 0; i < s; i++) { + fifo.add(0d); + } + numSamples = s; + gain = g; + effects = new ArrayList(); + } + + public double pass(double s) { + try { + for (Effect e : effects) { + s = e.process(s); + } + double v = s * gain; + double t = fifo.poll(); + fifo.add(v); + return t; + } catch (Exception e) { + e.printStackTrace(); + } + return 0d; + } + + public void setSamples(int s) { + fifo = new ArrayBlockingQueue(s); + for (int i = 0; i < s; i++) { + fifo.add(0d); + } + numSamples = s; + } + + public void setGain(double g) { + gain = g; + } + + public int getSamples() { + return numSamples; + } + + public double getGain() { + return gain; + } + + public void purge() { + fifo.clear(); + for (int i = 0; i < numSamples; i++) { + fifo.add(0d); + } + } + + public void addEffect(Effect e) { + effects.add(e); + } + + public void init(double sf) { + for (Effect e : effects) { + e.init(sf); + } + } + + public void dump() { + System.out.println(" Samples: " + numSamples + ", gain: " + gain); + for (Effect e : effects) { + e.dump(); + } + } +} diff --git a/src/uk/co/majenko/audiobookrecorder/Effect.java b/src/uk/co/majenko/audiobookrecorder/Effect.java new file mode 100644 index 0000000..e889cd8 --- /dev/null +++ b/src/uk/co/majenko/audiobookrecorder/Effect.java @@ -0,0 +1,11 @@ +package uk.co.majenko.audiobookrecorder; + +import java.util.ArrayList; + +public interface Effect { + public double process(double sample); + public String getName(); + public ArrayList getChildEffects(); + public void dump(); + public void init(double sr); +} diff --git a/src/uk/co/majenko/audiobookrecorder/EffectGroup.java b/src/uk/co/majenko/audiobookrecorder/EffectGroup.java new file mode 100644 index 0000000..df327e4 --- /dev/null +++ b/src/uk/co/majenko/audiobookrecorder/EffectGroup.java @@ -0,0 +1,72 @@ +package uk.co.majenko.audiobookrecorder; + +import java.util.ArrayList; +import javax.swing.tree.*; + +public class EffectGroup extends DefaultMutableTreeNode implements Effect { + String name; + ArrayList effects; + + public EffectGroup(String n) { + name = n; + effects = new ArrayList(); + } + + public EffectGroup() { + name = "Unnamed Group"; + effects = new ArrayList(); + } + + public double process(double sample) { + double out = sample; + for (Effect e : effects) { + out = e.process(out); + } + return out; + } + + public void setName(String n) { + name = n; + } + + public String getName() { + return name; + } + + public void addEffect(Effect e) { + effects.add(e); + } + + public void clearEffects() { + effects.clear(); + } + + public void removeEffect(Effect e) { + effects.remove(e); + } + + public void removeEffect(int n) { + effects.remove(n); + } + + public ArrayList getChildEffects() { + return effects; + } + + public String toString() { + return name; + } + + public void dump() { + System.out.println(toString() + " >> "); + for (Effect e : effects) { + e.dump(); + } + } + + public void init(double sf) { + for (Effect e : effects) { + e.init(sf); + } + } +} diff --git a/src/uk/co/majenko/audiobookrecorder/Equaliser.java b/src/uk/co/majenko/audiobookrecorder/Equaliser.java deleted file mode 100644 index 6623f5d..0000000 --- a/src/uk/co/majenko/audiobookrecorder/Equaliser.java +++ /dev/null @@ -1,106 +0,0 @@ -package uk.co.majenko.audiobookrecorder; - -import javax.swing.*; -import javax.swing.event.*; -import java.awt.*; -import java.awt.event.*; -import java.util.*; -import java.io.*; -import java.nio.file.*; -import javax.swing.tree.*; -import javax.sound.sampled.*; -import davaguine.jeq.core.IIRControls; - -public class Equaliser extends JPanel { - - EqualiserChannel channels[]; - String name; - - static final double[] frequencies = { - 20d, 25d, 31.5d, 40d, 50d, 63d, 80d, 100d, 125d, 160d, 200d, - 250d, 315d, 400d, 500d, 630d, 800d, 1000d, 1250d, 1600d, 2000d, - 2500d, 3150d, 4000d, 5000d, 6300d, 8000d, 10000d, 12500d, 16000d, - 20000d - }; - - public Equaliser(String n) { - super(); - - name = n; - - channels = new EqualiserChannel[31]; - - setLayout(new BorderLayout()); - - JPanel inner = new JPanel(); - - inner.setLayout(new FlowLayout()); - - for (int i = 0; i < 31; i++) { - channels[i] = new EqualiserChannel(frequencies[i]); - inner.add(channels[i]); - } - - add(inner, BorderLayout.CENTER); - - - JButton smooth = new JButton("Smooth curve"); - smooth.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Float ave[] = new Float[31]; - for (int i = 1; i < 30; i++) { - ave[i] = (channels[i-1].getValue() + channels[i].getValue() + channels[i+1].getValue()) / 3.0f; - } - - for (int i = 1; i < 30; i++) { - channels[i].setValue(ave[i]); - } - } - }); - JButton def = new JButton("Set as default"); - def.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - for (int i = 0; i < 31; i++) { - Options.set("audio.eq." + i, channels[i].getValue()); - } - } - }); - JButton load = new JButton("Load from default"); - load.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - for (int i = 0; i < 31; i++) { - channels[i].setValue(Options.getFloat("audio.eq." + i)); - } - } - }); - - JPanel buttons = new JPanel(); - buttons.setLayout(new FlowLayout()); - buttons.add(smooth); - buttons.add(def); - buttons.add(load); - - add(buttons, BorderLayout.SOUTH); - } - - public float getChannel(int c) { - return channels[c].getValue(); - } - - public void setChannel(int c, float v) { - channels[c].setValue(v); - } - - public void apply(IIRControls c, int chans) { - for (int i = 0; i < 31; i++) { - c.setBandDbValue(i, 0, channels[i].getValue()); - if (chans == 2) { - c.setBandDbValue(i, 1, channels[i].getValue()); - } - } - } - - public String getName() { - return name; - } -} diff --git a/src/uk/co/majenko/audiobookrecorder/EqualiserChannel.java b/src/uk/co/majenko/audiobookrecorder/EqualiserChannel.java deleted file mode 100644 index 8e17dbd..0000000 --- a/src/uk/co/majenko/audiobookrecorder/EqualiserChannel.java +++ /dev/null @@ -1,96 +0,0 @@ -package uk.co.majenko.audiobookrecorder; - -import javax.swing.*; -import javax.swing.event.*; -import java.awt.*; -import java.awt.event.*; -import java.util.*; -import java.io.*; -import java.nio.file.*; -import javax.swing.tree.*; -import javax.sound.sampled.*; - -public class EqualiserChannel extends JPanel { - - float value; - JSlider slider; - JTextField textbox; - JLabel frequency; - - public EqualiserChannel(double freq) { - super(); - - - value = 0; - - slider = new JSlider(-120, 120, 0); - textbox = new JTextField(); - - String suffix = "Hz"; - if (freq > 1000) { - freq /= 1000; - suffix = "kHz"; - } - - String ftxt = String.format("%.4f", freq); - while (ftxt.endsWith("0")) { - ftxt = ftxt.substring(0, ftxt.length() - 1); - } - if (ftxt.endsWith(".")) { - ftxt = ftxt.substring(0, ftxt.length() - 1); - } - frequency = new JLabel(ftxt + suffix); - - setLayout(new BorderLayout()); - - add(frequency, BorderLayout.NORTH); - slider.setOrientation(SwingConstants.VERTICAL); - add(slider, BorderLayout.CENTER); - textbox = new JTextField("0.0"); - add(textbox, BorderLayout.SOUTH); - - textbox.setPreferredSize(new Dimension(40, 20)); - slider.addChangeListener(new ChangeListener() { - public void stateChanged(ChangeEvent e) { - value = (float)slider.getValue() / 10.0f; - textbox.setText(String.format("%.1f", value)); - } - }); - - textbox.addFocusListener(new FocusListener() { - public void focusGained(FocusEvent e) { - textbox.selectAll(); - } - - public void focusLost(FocusEvent e) { - value = Utils.s2f(textbox.getText()); - if (value < -12f) value = -12f; - if (value > 12f) value = 12f; - - slider.setValue((int)(value * 10)); - textbox.setText(String.format("%.1f", value)); - } - }); - - textbox.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - value = Utils.s2f(textbox.getText()); - if (value < -12f) value = -12f; - if (value > 12f) value = 12f; - - slider.setValue((int)(value * 10)); - textbox.setText(String.format("%.1f", value)); - } - }); - } - - public float getValue() { - return value; - } - - public void setValue(float v) { - value = v; - slider.setValue((int)(value * 10)); - textbox.setText(String.format("%.1f", value)); - } -} diff --git a/src/uk/co/majenko/audiobookrecorder/MainToolBar.java b/src/uk/co/majenko/audiobookrecorder/MainToolBar.java index 758ff04..9a7ff67 100644 --- a/src/uk/co/majenko/audiobookrecorder/MainToolBar.java +++ b/src/uk/co/majenko/audiobookrecorder/MainToolBar.java @@ -86,9 +86,9 @@ public class MainToolBar extends JToolBar { add(stopPlaying); addSeparator(); - eq = new JButtonSpacePlay(Icons.eq, "Equaliser", new ActionListener() { + eq = new JButtonSpacePlay(Icons.eq, "Reload Effects", new ActionListener() { public void actionPerformed(ActionEvent e) { - root.showEqualiser(); + root.loadEffects(); } }); diff --git a/src/uk/co/majenko/audiobookrecorder/Options.java b/src/uk/co/majenko/audiobookrecorder/Options.java index 5929549..b3aea68 100644 --- a/src/uk/co/majenko/audiobookrecorder/Options.java +++ b/src/uk/co/majenko/audiobookrecorder/Options.java @@ -8,6 +8,7 @@ import java.awt.event.*; import java.util.*; import java.util.prefs.*; import java.io.*; +import javax.swing.tree.*; public class Options extends JDialog { @@ -15,6 +16,8 @@ public class Options extends JDialog { GridBagConstraints constraint; + public static ArrayList effectChains; + JComboBox mixerList; JComboBox playbackList; JComboBox channelList; @@ -42,8 +45,6 @@ public class Options extends JDialog { JTextField externalEditor; - Equaliser equaliser; - JTextArea startupScript; ArrayList processorList; @@ -332,11 +333,23 @@ public class Options extends JDialog { addSeparator(optionsPanel); tabs.add("Options", new JScrollPane(optionsPanel)); - equaliser = new Equaliser("Default"); - for (int i = 0; i < 31; i++) { - equaliser.setChannel(i, Options.getFloat("audio.eq." + i)); - } - tabs.add("Default EQ", new JScrollPane(equaliser)); + + JPanel effectChains = new JPanel(); + effectChains.setLayout(new BorderLayout()); + + JPanel effectDetails = new JPanel() { + public Dimension getPreferredSize() { + return new Dimension(200, 400); + } + }; + + DefaultTreeModel m = new DefaultTreeModel(new DefaultMutableTreeNode("Effect Chains")); + + JTree effectChainTree = new JTree(m); + effectChains.add(effectChainTree, BorderLayout.CENTER); + effectChains.add(effectDetails, BorderLayout.EAST); + + tabs.add("Effects Chains", new JScrollPane(effectChains)); JPanel startScript = new JPanel(); startScript.setLayout(new BorderLayout()); @@ -440,6 +453,7 @@ public class Options extends JDialog { setVisible(true); } + @SuppressWarnings("unchecked") static KVPair[] getRecordingMixerList() { TreeSet> list = new TreeSet>(); @@ -570,38 +584,6 @@ public class Options extends JDialog { defaultPrefs.put("cache.size", "100"); - defaultPrefs.put("audio.eq.0", "0.00"); - defaultPrefs.put("audio.eq.1", "0.00"); - defaultPrefs.put("audio.eq.2", "0.00"); - defaultPrefs.put("audio.eq.3", "0.00"); - defaultPrefs.put("audio.eq.4", "0.00"); - defaultPrefs.put("audio.eq.5", "0.00"); - defaultPrefs.put("audio.eq.6", "0.00"); - defaultPrefs.put("audio.eq.7", "0.00"); - defaultPrefs.put("audio.eq.8", "0.00"); - defaultPrefs.put("audio.eq.9", "0.00"); - defaultPrefs.put("audio.eq.10", "0.00"); - defaultPrefs.put("audio.eq.11", "0.00"); - defaultPrefs.put("audio.eq.12", "0.00"); - defaultPrefs.put("audio.eq.13", "0.00"); - defaultPrefs.put("audio.eq.14", "0.00"); - defaultPrefs.put("audio.eq.15", "0.00"); - defaultPrefs.put("audio.eq.16", "0.00"); - defaultPrefs.put("audio.eq.17", "0.00"); - defaultPrefs.put("audio.eq.18", "0.00"); - defaultPrefs.put("audio.eq.19", "-1.00"); - defaultPrefs.put("audio.eq.20", "-2.00"); - defaultPrefs.put("audio.eq.21", "-3.00"); - defaultPrefs.put("audio.eq.22", "-4.00"); - defaultPrefs.put("audio.eq.23", "-5.00"); - defaultPrefs.put("audio.eq.24", "-6.00"); - defaultPrefs.put("audio.eq.25", "-7.00"); - defaultPrefs.put("audio.eq.26", "-8.00"); - defaultPrefs.put("audio.eq.27", "-9.00"); - defaultPrefs.put("audio.eq.28", "-10.00"); - defaultPrefs.put("audio.eq.29", "-11.00"); - defaultPrefs.put("audio.eq.30", "-12.00"); - defaultPrefs.put("scripts.startup", ""); defaultPrefs.put("effects.ethereal.offset", "50"); @@ -639,6 +621,14 @@ public class Options extends JDialog { return 0; } + public static Double getDouble(String key) { + try { + Double f = Double.parseDouble(get(key)); + return f; + } catch (Exception e) { + } + return 0.0d; + } public static Float getFloat(String key) { try { Float f = Float.parseFloat(get(key)); @@ -719,10 +709,6 @@ public class Options extends JDialog { set("effects.ethereal.iterations", etherealIterations.getValue()); set("effects.ethereal.attenuation", etherealAttenuation.getValue()); - for (int i = 0; i < 31; i++) { - set("audio.eq." + i, equaliser.getChannel(i)); - } - set("scripts.startup", startupScript.getText()); int procNo = 0; @@ -778,8 +764,9 @@ public class Options extends JDialog { } public static KVPair[] getResolutionList() { - KVPair[] pairs = new KVPair[1]; + KVPair[] pairs = new KVPair[2]; pairs[0] = new KVPair("16", "16 Bit"); + pairs[1] = new KVPair("24", "24 Bit"); return pairs; } @@ -789,4 +776,52 @@ public class Options extends JDialog { pairs[1] = new KVPair("fft", "FFT Analysis"); return pairs; } + + + public static void createEffectChains() { + effectChains = new ArrayList(); + + for (int i = 0; i < 999999; i++) { + if (get("effects." + i + ".name") == null) { + EffectGroup e = new EffectGroup(get("effects." + i + ".name")); + for (int j = 0; i < 999999; j++) { + String type = get("effects." + i + ".children." + j + ".type"); + if (type == null) break; + + if (type.equals("biquad")) { + int bqt = getInteger("effects." + i + ".children." + j + ".filtertype"); + double fc = getDouble("effects." + i + ".children." + j + ".fc"); + double q = getDouble("effects." + i + ".children." + j + ".q"); + double gain = getDouble("effects." + i + ".children." + j + ".gain"); + Biquad b = new Biquad(bqt, fc, q, gain); + e.addEffect(b); + continue; + } + + if (type.equals("amplifier")) { + double gain = getDouble("effects." + i + ".children." + j + ".gain"); + Amplifier a = new Amplifier(gain); + e.addEffect(a); + continue; + } + + if (type.equals("delayline")) { + DelayLine l = new DelayLine(); + for (int c = 0; c < 999999; c++) { + if (get("effects." + i + ".children." + j + ".lines." + c + ".samples") == null) break; + int samples = getInteger("effects." + i + ".children." + j + ".lines." + c + ".samples"); + double gain = getDouble("effects." + i + ".children." + j + ".lines." + c + ".gain"); + l.addDelayLine(samples, gain); + } + e.addEffect(l); + continue; + } + + + } + effectChains.add(e); + break; + } + } + } } diff --git a/src/uk/co/majenko/audiobookrecorder/Sample.java b/src/uk/co/majenko/audiobookrecorder/Sample.java new file mode 100644 index 0000000..add80fb --- /dev/null +++ b/src/uk/co/majenko/audiobookrecorder/Sample.java @@ -0,0 +1,28 @@ +package uk.co.majenko.audiobookrecorder; + +public class Sample { + double left; + double right; + + public Sample(double m) { + left = m; + right = m; + } + + public Sample(double l, double r) { + left = l; + right = r; + } + + public double getLeft() { + return left; + } + + public double getRight() { + return right; + } + + public double getMono() { + return (left + right) / 2.0; + } +} diff --git a/src/uk/co/majenko/audiobookrecorder/Sentence.java b/src/uk/co/majenko/audiobookrecorder/Sentence.java index 557a004..27c4313 100644 --- a/src/uk/co/majenko/audiobookrecorder/Sentence.java +++ b/src/uk/co/majenko/audiobookrecorder/Sentence.java @@ -9,8 +9,6 @@ import java.io.*; import java.nio.file.*; import javax.swing.tree.*; import javax.sound.sampled.*; -import davaguine.jeq.spi.EqualizerInputStream; -import davaguine.jeq.core.IIRControls; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; @@ -47,7 +45,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { boolean inSample; boolean attention = false; - int eqProfile = 0; + String effectChain = null; double gain = 1.0d; @@ -69,7 +67,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { AudioFormat storedFormat = null; double storedLength = -1d; - int[] storedAudioData = null; + double[] audioData = null; RecordingThread recordingThread; @@ -89,6 +87,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { tempFile = tf; wavFile = wf; format = af; +System.err.println(format); } public void run() { @@ -163,7 +162,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } - recordingThread = new RecordingThread(getTempFile(), getFile(), AudiobookRecorder.window.book.getAudioFormat()); + recordingThread = new RecordingThread(getTempFile(), getFile(), Options.getAudioFormat()); Thread rc = new Thread(recordingThread); rc.setDaemon(true); @@ -181,7 +180,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } } - storedAudioData = null; + audioData = null; storedFormat = null; storedLength = -1; @@ -205,7 +204,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { double[] real = new double[FFTBuckets]; double[] imag = new double[FFTBuckets]; - int[] samples = getAudioData(); + double[] samples = getProcessedAudioData(); int slices = (samples.length / FFTBuckets) + 1; double[][] out = new double[slices][]; @@ -215,7 +214,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { for (int i = 0; i < samples.length; i += FFTBuckets) { for (int j = 0; j < FFTBuckets; j++) { if (i + j < samples.length) { - real[j] = samples[i+j] / 32768d; + real[j] = samples[i+j]; imag[j] = 0; } else { real[j] = 0; @@ -234,7 +233,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public void autoTrimSampleFFT() { crossStartOffset = -1; crossEndOffset = -1; - int[] samples = getAudioData(); + double[] samples = getProcessedAudioData(); if (samples == null) return; int blocks = samples.length / 4096 + 1; @@ -248,7 +247,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { for (int j = 0; j < 4096; j++) { if (i + j < samples.length) { - real[j] = samples[i+j] / 32768d; + real[j] = samples[i+j]; imag[j] = 0; } else { real[j] = 0; @@ -316,9 +315,9 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public void autoTrimSamplePeak() { crossStartOffset = -1; crossEndOffset = -1; - int[] samples = getAudioData(); + double[] samples = getProcessedAudioData(); if (samples == null) return; - int noiseFloor = AudiobookRecorder.window.getNoiseFloor(); + double noiseFloor = AudiobookRecorder.window.getNoiseFloor(); // Find start for (int i = 0; i < samples.length; i++) { @@ -425,91 +424,6 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } } - public int[] getAudioDataS16LE(AudioInputStream s, AudioFormat format) throws IOException { - return getAudioDataS16LE(s, format, true); - } - - public int[] getAudioDataS16LE(AudioInputStream s, AudioFormat format, boolean amplify) throws IOException { - long len = s.getFrameLength(); - int frameSize = format.getFrameSize(); - int chans = format.getChannels(); - int bytes = frameSize / chans; - - byte[] frame = new byte[frameSize]; - int[] samples = new int[(int)len]; - - for (long fno = 0; fno < len; fno++) { - - s.read(frame); - int sample = 0; - if (chans == 2) { // Stereo - int left = (frame[1] << 8) | frame[0]; - int right = (frame[3] << 8) | frame[2]; - sample = (left + right) / 2; - } else { - sample = (frame[1] << 8) | frame[0]; - } - if (amplify) { - double amped = (double)sample * gain; - samples[(int)fno] = (int)amped; - } else { - samples[(int)fno] = sample; - } - } - - return samples; - } - - public int[] getAudioData() { - if (storedAudioData != null) { - return storedAudioData; - } - File f = getFile(); - try { - AudioInputStream s = AudioSystem.getAudioInputStream(f); - AudioFormat format = getAudioFormat(); - - int[] samples = null; - - switch (format.getSampleSizeInBits()) { - case 16: - samples = getAudioDataS16LE(s, format); - break; - } - - - s.close(); - sampleSize = samples.length; - storedAudioData = samples; - CacheManager.addToCache(this); - return samples; - } catch (Exception e) { - } - return null; - } - - public int[] getUnprocessedAudioData() { - File f = getFile(); - try { - AudioInputStream s = AudioSystem.getAudioInputStream(f); - AudioFormat format = getAudioFormat(); - - int[] samples = null; - - switch (format.getSampleSizeInBits()) { - case 16: - samples = getAudioDataS16LE(s, format, false); - break; - } - - s.close(); - return samples; - } catch (Exception e) { - } - return null; - } - - public int getStartCrossing() { return crossStartOffset; } @@ -559,33 +473,11 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public int getSampleSize() { if (sampleSize == -1) { - getAudioData(); + loadFile(); } return sampleSize; } - // Open the audio file as an AudioInputStream and automatically - // skip the first startOffset frames. - public EqualizerInputStream getAudioStream() { - File f = getFile(); - try { - AudioInputStream s = AudioSystem.getAudioInputStream(f); - EqualizerInputStream eq = new EqualizerInputStream(s, 31); - AudioFormat format = getAudioFormat(); - IIRControls controls = eq.getControls(); - AudiobookRecorder.window.book.equaliser[eqProfile].apply(controls, format.getChannels()); - - int frameSize = format.getFrameSize(); - - eq.skip(frameSize * startOffset); - - return eq; - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - public AudioFormat getAudioFormat() { if (storedFormat != null) return storedFormat; @@ -600,150 +492,6 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } return null; } - - public byte[] getRawAudioData() { - File f = getFile(); - try { - updateCrossings(); - AudioInputStream s = AudioSystem.getAudioInputStream(f); - EqualizerInputStream eq = new EqualizerInputStream(s, 31); - - - AudioFormat format = getAudioFormat(); - IIRControls controls = eq.getControls(); - AudiobookRecorder.window.book.equaliser[eqProfile].apply(controls, format.getChannels()); - - int frameSize = format.getFrameSize(); - int length = crossEndOffset - crossStartOffset; - - int bytesToRead = length * frameSize; - byte[] data = new byte[bytesToRead]; - byte[] buf = new byte[65536]; - - int pos = 0; - - eq.skip(crossStartOffset * frameSize); - - while (bytesToRead > 0) { - int r = eq.read(buf, 0, bytesToRead > 65536 ? 65536 : bytesToRead); - for (int i = 0; i < r; i++) { - data[pos++] = buf[i]; - bytesToRead--; - } - } - - data = postProcessData(data); - - return data; - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - byte[] adjustGain(byte[] data) { - AudioFormat format = getAudioFormat(); - int frameSize = format.getFrameSize(); - int channels = format.getChannels(); - int bytesPerChannel = frameSize / channels; - - int frames = data.length / frameSize; - - int byteNo = 0; - - byte[] out = new byte[data.length]; - - for (int i = 0; i < frames; i++) { - if (channels == 1) { - int l = data[i * frameSize] >= 0 ? data[i * frameSize] : 256 + data[i * frameSize]; - int h = data[(i * frameSize) + 1] >= 0 ? data[(i * frameSize) + 1] : 256 + data[(i * frameSize) + 1]; - - int sample = (h << 8) | l; - if ((sample & 0x8000) == 0x8000) sample |= 0xFFFF0000; - - double sampleDouble = (double)sample; - sampleDouble *= gain; - sample = (int)sampleDouble; - - if (sample > 32767) sample = 32767; - if (sample < -32768) sample = -32768; - out[i * frameSize] = (byte)(sample & 0xFF); - out[(i * frameSize) + 1] = (byte)((sample & 0xFF00) >> 8); - - } else { - return data; - } - } - - return out; - } - - byte[] postProcessData(byte[] data) { - data = adjustGain(data); - - if (effectEthereal) { - data = processEtherealEffect(data); - } - return data; - } - - byte[] processEtherealEffect(byte[] data) { - AudioFormat format = getAudioFormat(); - int frameSize = format.getFrameSize(); - int channels = format.getChannels(); - int bytesPerChannel = frameSize / channels; - - int frames = data.length / frameSize; - - int byteNo = 0; - - double fpms = (double)format.getFrameRate() / 1000d; - double doubleOffset = fpms * (double) AudiobookRecorder.window.book.getInteger("effects.ethereal.offset"); - int offset = (int)doubleOffset; - double attenuation = 1d - ((double)AudiobookRecorder.window.book.getInteger("effects.ethereal.attenuation") / 100d); - - int copies = AudiobookRecorder.window.book.getInteger("effects.ethereal.iterations"); - - byte[] out = new byte[data.length]; - - for (int i = 0; i < frames; i++) { - if (channels == 1) { - int l = data[i * frameSize] >= 0 ? data[i * frameSize] : 256 + data[i * frameSize]; - int h = data[(i * frameSize) + 1] >= 0 ? data[(i * frameSize) + 1] : 256 + data[(i * frameSize) + 1]; - - int sample = (h << 8) | l; - if ((sample & 0x8000) == 0x8000) sample |= 0xFFFF0000; - - double sampleDouble = (double)sample; - - int used = 0; - for (int j = 0; j < copies; j++) { - if (i + (j * offset) < frames) { - used++; - int lx = data[(i + (j * offset)) * frameSize] >= 0 ? data[(i + (j * offset)) * frameSize] : 256 + data[(i + (j * offset)) * frameSize]; - int hx = data[((i + (j * offset)) * frameSize) + 1] >= 0 ? data[((i + (j * offset)) * frameSize) + 1] : 256 + data[((i + (j * offset)) * frameSize) + 1]; - int futureSample = (hx << 8) | lx; - if ((futureSample & 0x8000) == 0x8000) futureSample |= 0xFFFF0000; - double futureDouble = (double)futureSample; - for (int k = 0; k < copies; k++) { - futureDouble *= attenuation; - } - sampleDouble = mix(sampleDouble, futureDouble); - } - } - sample = (int)sampleDouble; - if (sample > 32767) sample = 32767; - if (sample < -32768) sample = -32768; - out[i * frameSize] = (byte)(sample & 0xFF); - out[(i * frameSize) + 1] = (byte)((sample & 0xFF00) >> 8); - - } else { - return data; - } - } - - return out; - } public void recognise() { AudiobookRecorder.window.havenQueue.submit(Sentence.this); @@ -766,8 +514,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } public void clearCache() { - storedAudioData = null; -// System.gc(); + audioData = null; } public boolean lockedInCache() { @@ -775,7 +522,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } public int findNearestZeroCrossing(int pos, int range) { - int[] data = getAudioData(); + double[] data = getProcessedAudioData(); if (data == null) return 0; if (data.length == 0) return 0; @@ -785,8 +532,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { int backwards = pos; int forwards = pos; - int backwardsPrev = data[backwards]; - int forwardsPrev = data[forwards]; + double backwardsPrev = data[backwards]; + double forwardsPrev = data[forwards]; while (backwards > 0 || forwards < data.length-2) { @@ -864,6 +611,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { havenStatus = i; } + @SuppressWarnings("deprecation") public boolean postHavenData() { String apiKey = Options.get("process.haven.apikey"); if (apiKey == null || apiKey.equals("")) return false; @@ -978,36 +726,15 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } } - public void setEthereal(boolean e) { - effectEthereal = e; - } - - public boolean getEthereal() { - return effectEthereal; - } - - public double mix(double a, double b) { - double z; - double fa, fb, fz; - fa = a + 32768d; - fb = b + 32768d; - - if (fa < 32768d && fb < 32768d) { - fz = (fa * fb) / 32768d; - } else { - fz = (2d * (fa + fb)) - ((fa * fb) / 32768d) - 65536d; - } - - z = fz - 32768d; - return z; - } - - public int getPeakValue() { - int[] samples = getUnprocessedAudioData(); + public double getPeakValue() { + double oldGain = gain; + gain = 1.0d; + double[] samples = getProcessedAudioData(); + gain = oldGain; if (samples == null) { return 0; } - int ms = 0; + double ms = 0; for (int i = 0; i < samples.length; i++) { if (Math.abs(samples[i]) > ms) { ms = Math.abs(samples[i]); @@ -1017,9 +744,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } public int getHeadroom() { - int nf = getPeakValue(); - if (nf == 0) return 0; - double r = nf / 32767d; + double r = getPeakValue(); + if (r == 0) return 0; double l10 = Math.log10(r); double db = 20d * l10; @@ -1039,20 +765,12 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public void normalize() { if (locked) return; - int max = getPeakValue(); - double d = 23192d / max; - if (d > 1.1d) d = 1.1d; + double max = getPeakValue(); + double d = 0.708 / max; + if (d > 1d) d = 1d; setGain(d); } - public int getEQProfile() { - return eqProfile; - } - - public void setEQProfile(int e) { - eqProfile = e; - } - class ExternalEditor implements Runnable { Sentence sentence; ExternalEditor(Sentence s) { @@ -1219,4 +937,186 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { AudiobookRecorder.window.updateWaveform(); } + public double[] getDoubleDataS16LE(AudioInputStream s, AudioFormat format) throws IOException { + long len = s.getFrameLength(); + int frameSize = format.getFrameSize(); + int chans = format.getChannels(); + int bytes = frameSize / chans; + + byte[] frame = new byte[frameSize]; + double[] samples = new double[(int)len]; + + for (long fno = 0; fno < len; fno++) { + + s.read(frame); + int sample = 0; + if (chans == 2) { // Stereo + int ll = frame[0] >= 0 ? frame[0] : 256 + frame[0]; + int lh = frame[1] >= 0 ? frame[1] : 256 + frame[1]; + int rl = frame[2] >= 0 ? frame[2] : 256 + frame[2]; + int rh = frame[3] >= 0 ? frame[3] : 256 + frame[3]; + int left = (lh << 8) | ll; + int right = (rh << 8) | rl; + if ((left & 0x8000) == 0x8000) left |= 0xFFFF0000; + if ((right & 0x8000) == 0x8000) right |= 0xFFFF0000; + sample = (left + right) / 2; + } else { + int l = frame[0] >= 0 ? frame[0] : 256 + frame[0]; + int h = frame[1] >= 0 ? frame[1] : 256 + frame[1]; + sample = (h << 8) | l; + if ((sample & 0x8000) == 0x8000) sample |= 0xFFFF0000; + } + samples[(int)fno] = (double)sample / 32768d; + } + + return samples; + } + + public double[] getDoubleDataS24LE(AudioInputStream s, AudioFormat format) throws IOException { + long len = s.getFrameLength(); + int frameSize = format.getFrameSize(); + int chans = format.getChannels(); + int bytes = frameSize / chans; + + byte[] frame = new byte[frameSize]; + double[] samples = new double[(int)len]; + + for (long fno = 0; fno < len; fno++) { + + s.read(frame); + int sample = 0; + if (chans == 2) { // Stereo + int ll = frame[0] >= 0 ? frame[0] : 256 + frame[0]; + int lm = frame[1] >= 0 ? frame[1] : 256 + frame[1]; + int lh = frame[2] >= 0 ? frame[2] : 256 + frame[2]; + int rl = frame[3] >= 0 ? frame[3] : 256 + frame[3]; + int rm = frame[4] >= 0 ? frame[4] : 256 + frame[4]; + int rh = frame[5] >= 0 ? frame[5] : 256 + frame[5]; + int left = (lh << 16) | (lm << 8) | ll; + int right = (rh << 16) | (rm << 8) | rl; + if ((left & 0x800000) == 0x800000) left |= 0xFF000000; + if ((right & 0x800000) == 0x800000) right |= 0xFF000000; + sample = (left + right) / 2; + } else { + int l = frame[0] >= 0 ? frame[0] : 256 + frame[0]; + int m = frame[1] >= 0 ? frame[1] : 256 + frame[1]; + int h = frame[2] >= 0 ? frame[2] : 256 + frame[2]; + sample = (h << 16) | (m << 8) | l; + if ((sample & 0x800000) == 0x800000) sample |= 0xFF000000; + } + samples[(int)fno] = (double)sample / 8388608d; + } + + return samples; + } + + + + public void loadFile() { + if (audioData != null) return; + + File f = getFile(); + try { + AudioInputStream s = AudioSystem.getAudioInputStream(f); + AudioFormat format = getAudioFormat(); + + double[] samples = null; + + switch (format.getSampleSizeInBits()) { + case 16: + samples = getDoubleDataS16LE(s, format); + break; + case 24: + samples = getDoubleDataS24LE(s, format); + break; + } + + s.close(); + sampleSize = samples.length; + audioData = samples; + CacheManager.addToCache(this); + } catch (Exception e) { + } + } + + public double[] getProcessedAudioData() { + loadFile(); + if (audioData == null) return null; + double[] samples = new double[audioData.length]; + for (int i = 0; i < audioData.length; i++) { + samples[i] = audioData[i]; + } + // Add processing in here. + + if (effectChain == null) effectChain = AudiobookRecorder.window.defaultEffectChain; + + Effect eff = AudiobookRecorder.window.effects.get(effectChain); + if (eff == null) { + effectChain = AudiobookRecorder.window.defaultEffectChain; + eff = AudiobookRecorder.window.effects.get(effectChain); + } + + if (eff != null) { + eff.init(getAudioFormat().getFrameRate()); + for (int i = 0; i < samples.length; i++) { + samples[i] = eff.process(samples[i]); + } + } + + // Cuts out the computer hum. +// Biquad bq = new Biquad(Biquad.Notch, 140d/44100d, 20.0d, -50); +// DelayLine dl = new DelayLine(); +// dl.addDelayLine(2205, 0.8); +// dl.addDelayLine(4410, 0.4); +// dl.addDelayLine(6615, 0.2); + + // Add final master gain stage + for (int i = 0; i < samples.length; i++) { + samples[i] = samples[i] * gain; + } + return samples; + } + + public double[] getDoubleAudioData() { + return getProcessedAudioData(); + } + + public double[] getCroppedAudioData() { + double[] inSamples = getDoubleAudioData(); + if (inSamples == null) return null; + updateCrossings(); + + int length = crossEndOffset - crossStartOffset; + + double[] samples = new double[length]; + for (int i = 0; i < length; i++) { + samples[i] = inSamples[crossStartOffset + i]; + } + return samples; + } + + public byte[] getPCMData() { + double[] croppedData = getCroppedAudioData(); + if (croppedData == null) return null; + int length = croppedData.length; + byte[] pcmData = new byte[length * 2]; + for (int i = 0; i < length; i++) { + double sd = croppedData[i] * 32768d; + int si = (int)sd; + if (si > 32767) si = 32767; + if (si < -32768) si = -32768; + pcmData[i * 2] = (byte)(si & 0xFF); + pcmData[(i * 2) + 1] = (byte)((si & 0xFF00) >> 8); + } + return pcmData; + } + + public void setEffectChain(String key) { + effectChain = key; + } + + public String getEffectChain() { + if (effectChain == null) return AudiobookRecorder.window.defaultEffectChain; + return effectChain; + } } diff --git a/src/uk/co/majenko/audiobookrecorder/Waveform.java b/src/uk/co/majenko/audiobookrecorder/Waveform.java index 54bc96b..3fa42b0 100644 --- a/src/uk/co/majenko/audiobookrecorder/Waveform.java +++ b/src/uk/co/majenko/audiobookrecorder/Waveform.java @@ -9,7 +9,7 @@ import javax.sound.sampled.*; public class Waveform extends JPanel implements MouseListener, MouseMotionListener { - int[] samples = null; + double[] samples = null; int leftMarker = 0; int rightMarker = 0; @@ -67,7 +67,7 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen g.drawLine(x, h/4*3, x, h/4*3); } - int scale = 32768/(h/2); + double scale = (h/2); if (samples != null) { @@ -81,15 +81,15 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen for (int n = 0; n < w; n++) { int hcnt = 0; - long have = 0; - int hmax = 0; + double have = 0; + double hmax = 0; int lcnt = 0; - int lave = 0; - int lmax = 0; + double lave = 0; + double lmax = 0; for (int o = 0; o < step; o++) { - int sample = samples[offset + (n * step) + o]; + double sample = samples[offset + (n * step) + o]; if (sample >= 0) { have += sample; hcnt++; @@ -107,27 +107,27 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen boolean clip = false; - if (lmax > 32000) { // -3dB == 23198? + if (lmax > 0.708) { // -3dB == 23198? clip = true; } - if (hmax > 32000) { // -3dB + if (hmax > 0.708) { // -3dB clip = true; } - hmax /= scale; - lmax /= scale; - have /= scale; - lave /= scale; + hmax *= scale; + lmax *= scale; + have *= scale; + lave *= scale; if (clip) { g.setColor(new Color(200, 20, 0)); } else { g.setColor(new Color(0, 20, 200)); } - g.drawLine(n, h/2 + lmax, n, h/2 - hmax); + g.drawLine(n, (int)(h/2 + lmax), n, (int)(h/2 - hmax)); g.setColor(new Color(0, 100, 255)); - g.drawLine(n, h/2 + (int)lave, n, h/2 - (int)have); + g.drawLine(n, (int)(h/2 + lave), n, (int)(h/2 - have)); } g.setColor(new Color(255, 0, 0, 32)); @@ -186,7 +186,7 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen repaint(); } - public void setData(int[] s) { + public void setData(double[] s) { samples = s; playMarker = 0; repaint();