Compare commits

...

48 Commits

Author SHA1 Message Date
fde4a597c7 Released 0.2.2 2020-01-10 20:23:43 +00:00
e69ae52f50 Updated example filters 2020-01-10 18:55:46 +00:00
8bdb06749f Improvements to LFO 2020-01-10 18:29:22 +00:00
0cfd066f4f Released 0.2.1 2019-12-18 11:31:43 +00:00
217ccb915a Added mono/stereo export option 2019-12-16 15:54:34 +00:00
1dd3e9d86f Add purge backup files menu entry 2019-11-28 14:32:11 +00:00
d3d81d71fe Remove play stop/start 2019-11-28 12:19:31 +00:00
80c110afa9 Added playback block size option 2019-10-13 20:11:07 +01:00
1c08b9a51d Purge cache when changing base effect or reloading effects 2019-09-08 22:37:27 +01:00
4ad30106b6 Move save population to sentence 2019-09-08 22:36:58 +01:00
9100d0e35a Improvements to AGC limiting 2019-09-08 22:36:37 +01:00
b8dea19c30 Make splits continuations and have single playback always 1.00x 2019-09-08 12:30:52 +01:00
671f2b9270 Added adjustable FFT block size 2019-09-07 21:24:46 +01:00
d619fb2f4d Added playback speed option 2019-09-07 20:34:02 +01:00
3eb6704f2f Released 0.2.0 2019-09-05 00:21:04 +01:00
cd24beb8a6 Removed pointless garbage collection calls. Massive speedup 2019-09-05 00:20:29 +01:00
fa287305eb Use 100* integer for gain comparison 2019-09-03 00:12:45 +01:00
3fb656b693 Add small visual gap for paragraphs 2019-09-03 00:12:26 +01:00
f514993525 Added global effects disable and fixed caching problems 2019-09-02 21:44:44 +01:00
81787260c9 Lighten font and make flashing panel alternate red/green 2019-08-27 17:14:17 +01:00
f9ad396228 Added chapter and book level resetting of post gaps 2019-08-25 14:08:25 +01:00
8976f2e359 Add post gap classification 2019-08-25 13:41:46 +01:00
187c3edaf6 Add waveform split and cut functionality 2019-08-14 19:58:06 +01:00
02e85fb354 Released 0.1.9 2019-08-13 14:03:45 +01:00
289834021f Finally fixed pesky memory leak 2019-08-13 14:02:14 +01:00
04fea4acb2 Added race condition debugging and reduced recording buffer size for better responsiveness 2019-08-11 12:51:10 +01:00
54739b0a75 Add Tritex to recording controls 2019-08-11 11:15:55 +01:00
ea5520a729 Fix double apply gain shift in AGC 2019-07-26 14:06:30 +01:00
0a19d8d308 Protect against no effects 2019-07-24 14:17:38 +01:00
4dbe3e23b7 Remove debugging cruft 2019-07-23 12:45:46 +01:00
c43cfc3b69 Improve caching policy 2019-07-23 12:44:24 +01:00
babd3d2052 Release 0.1.8 2019-07-22 23:24:16 +01:00
9464839b4d Turn on JTattoo 2019-07-22 23:24:07 +01:00
bcf7875414 Scrap Sample object and use double[][] instead. Disable processed audio caching - it causes heap overflows 2019-07-22 23:23:03 +01:00
732894a0fb Add Chain effect to connect effects together 2019-07-22 10:43:53 +01:00
146bf5a3c2 Release 0.1.7 2019-07-21 20:03:05 +01:00
b05bfde094 Add pan effect 2019-07-21 20:02:16 +01:00
37d372b8f5 Fix caching of processed audio. Add effect icon and name to tree 2019-07-21 20:02:07 +01:00
c01fee3b73 Switch to Sample object instead of double, and refactor effects for whole sampleset processing 2019-07-21 17:21:50 +01:00
c907e735c6 Prevent stacking of default effect chain 2019-07-21 15:36:33 +01:00
7545e33d2f Make effects two-layerd 2019-07-20 23:03:05 +01:00
ebf961449a Reinstate sphinx to replace defunct Haven 2019-07-18 15:07:04 +01:00
45d6882527 Remove defunct Haven processing 2019-07-18 14:31:59 +01:00
2ec370ad61 Add Shift+Space for Play From 2019-07-18 11:51:07 +01:00
63d5c4af8e Released 0.1.6 2019-07-17 23:14:29 +01:00
9619fd574e Apply default effect to room noise 2019-07-17 23:13:39 +01:00
adeb42070d Added more example filters 2019-07-15 00:50:58 +01:00
d207d246bf Added clipping filter 2019-07-14 22:15:10 +01:00
51 changed files with 2016 additions and 624 deletions

4
ExampleFilters/alien.eff Normal file
View File

@@ -0,0 +1,4 @@
<effect name="Alien">
<!--biquad type="lowpass" fc="10000" q="1" gain="-10" /-->
<lfo frequency="50" depth="1.0" waveform="triangle" mode="replace"/>
</effect>

View File

@@ -1,5 +1,4 @@
<effect name="Big Echo">
<biquad type="notch" fc="140" q="20" gain="-50" />
<delayline>
<delay samples="22000" gain="0.2" pan="-0.3">
<biquad type="highpass" fc="300" q="1" gain="0" />

View File

@@ -1,4 +1,6 @@
<effect name="Cut Computer Hum">
<biquad type="notch" fc="140" q="20" gain="-50" />
<biquad type="notch" fc="28" q="20" gain="-50" />
<biquad type="notch" fc="91" q="20" gain="-50" />
<biquad type="notch" fc="120" q="20" gain="-50" />
<biquad type="lowpass" fc="10000" q="1" gain="-10" />
</effect>

View File

@@ -0,0 +1,7 @@
<effect name="Cut Computer Hum (with AGC)">
<biquad type="notch" fc="28" q="20" gain="-50" />
<biquad type="notch" fc="91" q="20" gain="-50" />
<biquad type="notch" fc="120" q="20" gain="-50" />
<biquad type="lowpass" fc="10000" q="1" gain="-10" />
<agc ceiling="0.666" limit="1.5" attack="0.08" decay="0.01" />
</effect>

View File

@@ -0,0 +1,15 @@
<effect name="Cut Computer Hum (with AGC and Stereo)">
<biquad type="notch" fc="28" q="20" gain="-50" />
<biquad type="notch" fc="91" q="20" gain="-50" />
<biquad type="notch" fc="120" q="20" gain="-50" />
<biquad type="lowpass" fc="10000" q="1" gain="-10" />
<delayline wetonly="false">
<delay samples="-100" gain="0.1" pan="-1.0">
<biquad type="highpass" fc="300" q="1" gain="0" />
</delay>
<delay samples="100" gain="0.1" pan="1.0">
<biquad type="highpass" fc="300" q="1" gain="0" />
</delay>
</delayline>
<agc ceiling="0.666" limit="1.5" attack="0.1" decay="0.01" />
</effect>

View File

@@ -1,21 +1,20 @@
<effect name="Ethereal Voice">
<biquad type="notch" fc="140" q="20" gain="-50" />
<amplifier gain="0.1" />
<delayline>
<delay samples="4000" gain="1.0" pan="-0.3">
<delay samples="2000" gain="1.0" pan="-0.3">
<biquad type="highpass" fc="400" q="1" gain="0" />
</delay>
<delay samples="8000" gain="1.5" pan="0.3">
<biquad type="highpass" fc="300" q="1" gain="0" />
<delay samples="4000" gain="1.1" pan="0.3">
<biquad type="highpass" fc="800" q="1" gain="0" />
</delay>
<delay samples="12000" gain="2.0" pan="-0.5">
<biquad type="highpass" fc="200" q="1" gain="0" />
<delay samples="8000" gain="1.2" pan="-0.5">
<biquad type="highpass" fc="1000" q="1" gain="0" />
</delay>
<delay samples="16000" gain="3.0" pan="0.5">
<biquad type="highpass" fc="100" q="1" gain="0" />
<delay samples="10000" gain="1.3" pan="0.5">
<biquad type="highpass" fc="1500" q="1" gain="0" />
</delay>
<delay samples="20000" gain="15.0" pan="0" />
<delay samples="12000" gain="15.0" pan="0" />
</delayline>
<biquad type="lowshelf" fc="1000" q="2.2" gain="-10" />
<biquad type="lowshelf" fc="2000" q="2.2" gain="-10" />
<amplifier gain="1.5" />
</effect>

View File

@@ -0,0 +1,15 @@
<effect name="Large Room (quiet)">
<biquad type="lowpass" fc="10000" q="1" gain="-10" />
<delayline>
<delay samples="5500" gain="0.2" pan="-0.3">
<biquad type="highpass" fc="300" q="1" gain="0" />
</delay>
<delay samples="11000" gain="0.05" pan="0.3">
<biquad type="highpass" fc="600" q="1" gain="0" />
</delay>
<delay samples="16500" gain="0.01" pan="0.0">
<biquad type="highpass" fc="600" q="1" gain="0" />
</delay>
</delayline>
<amplifier gain="0.3" />
</effect>

View File

@@ -0,0 +1,15 @@
<effect name="Large Room (loud)">
<biquad type="lowpass" fc="10000" q="1" gain="-10" />
<delayline>
<delay samples="5500" gain="0.2" pan="-0.3">
<biquad type="highpass" fc="300" q="1" gain="0" />
</delay>
<delay samples="11000" gain="0.05" pan="0.3">
<biquad type="highpass" fc="600" q="1" gain="0" />
</delay>
<delay samples="16500" gain="0.01" pan="0.0">
<biquad type="highpass" fc="600" q="1" gain="0" />
</delay>
</delayline>
<amplifier gain="0.9" />
</effect>

View File

@@ -0,0 +1,3 @@
<effect name="Pan Left">
<pan pan="-0.4" />
</effect>

View File

@@ -0,0 +1,3 @@
<effect name="Pan Right">
<pan pan="0.4" />
</effect>

View File

@@ -1,5 +1,4 @@
<effect name="Telephone">
<biquad type="notch" fc="140" q="20" gain="-50" />
<biquad type="lowshelf" fc="400" q="10" gain="-20" />
<biquad type="highshelf" fc="8000" q="10" gain="-20" />
<delayline>

12
ExampleFilters/radio.eff Normal file
View File

@@ -0,0 +1,12 @@
<effect name="Radio">
<amplifier gain="0.5" />
<biquad type="peak" fc="1000" q="10" gain="25" />
<lfo frequency="3000" depth="0.3" waveform="sine" mode="add" />
<clipping clip="0.9" />
<biquad type="highshelf" fc="8000" q="1" gain="-20" />
<delayline>
<delay samples="100" gain="0.7" />
<delay samples="200" gain="0.5" />
</delayline>
<amplifier gain="0.3" />
</effect>

View File

@@ -1,5 +1,4 @@
<effect name="Robotic">
<biquad type="notch" fc="140" q="20" gain="-50" />
<biquad type="lowshelf" fc="100" q="2" gain="-20" />
<delayline>
<delay samples="400" gain="1" pan="-0.3" />

View File

@@ -0,0 +1,10 @@
<effect name="Stereo Chorus">
<delayline>
<delay samples="2000" gain="0.1" pan="-0.3">
<biquad type="highpass" fc="300" q="1" gain="0" />
</delay>
<delay samples="4000" gain="0.1" pan="0.3">
<biquad type="highpass" fc="300" q="1" gain="0" />
</delay>
</delayline>
</effect>

BIN
deps/sphinx4-core-5prealpha-SNAPSHOT.jar LFS vendored Normal file

Binary file not shown.

BIN
deps/sphinx4-data-5prealpha-SNAPSHOT.jar LFS vendored Normal file

Binary file not shown.

View File

@@ -1 +1 @@
version=0.1.5
version=0.2.2

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

View File

@@ -795,7 +795,7 @@ public abstract class AbstractTheme extends MetalTheme {
public FontUIResource getControlTextFont() {
if (controlFont == null) {
if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) {
controlFont = new FontUIResource(DIALOG, Font.BOLD, 14);
controlFont = new FontUIResource(DIALOG, Font.PLAIN, 14); // bold
} else {
controlFont = new FontUIResource(DIALOG, Font.PLAIN, 12);
}
@@ -806,7 +806,7 @@ public abstract class AbstractTheme extends MetalTheme {
public FontUIResource getSystemTextFont() {
if (systemFont == null) {
if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) {
systemFont = new FontUIResource(DIALOG, Font.BOLD, 14);
systemFont = new FontUIResource(DIALOG, Font.PLAIN, 14); // bold
} else {
systemFont = new FontUIResource(DIALOG, Font.PLAIN, 12);
}
@@ -817,7 +817,7 @@ public abstract class AbstractTheme extends MetalTheme {
public FontUIResource getUserTextFont() {
if (userFont == null) {
if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) {
userFont = new FontUIResource(DIALOG, Font.BOLD, 14);
userFont = new FontUIResource(DIALOG, Font.PLAIN, 14); // bold
} else {
userFont = new FontUIResource(DIALOG, Font.PLAIN, 12);
}
@@ -828,7 +828,7 @@ public abstract class AbstractTheme extends MetalTheme {
public FontUIResource getMenuTextFont() {
if (menuFont == null) {
if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) {
menuFont = new FontUIResource(DIALOG, Font.BOLD, 14);
menuFont = new FontUIResource(DIALOG, Font.PLAIN, 14); // bold
} else {
menuFont = new FontUIResource(DIALOG, Font.PLAIN, 12);
}
@@ -839,9 +839,9 @@ public abstract class AbstractTheme extends MetalTheme {
public FontUIResource getWindowTitleFont() {
if (windowTitleFont == null) {
if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) {
windowTitleFont = new FontUIResource(DIALOG, Font.BOLD, 14);
windowTitleFont = new FontUIResource(DIALOG, Font.PLAIN, 14); // bold
} else {
windowTitleFont = new FontUIResource(DIALOG, Font.BOLD, 12);
windowTitleFont = new FontUIResource(DIALOG, Font.PLAIN, 12); // bold
}
}
return windowTitleFont;
@@ -850,7 +850,7 @@ public abstract class AbstractTheme extends MetalTheme {
public FontUIResource getSubTextFont() {
if (smallFont == null) {
if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) {
smallFont = new FontUIResource(DIALOG, Font.BOLD, 12);
smallFont = new FontUIResource(DIALOG, Font.PLAIN, 12); // bold
} else {
smallFont = new FontUIResource(DIALOG, Font.PLAIN, 10);
}

View File

@@ -125,11 +125,11 @@ public class HiFiDefaultTheme extends AbstractTheme {
tooltipForegroundColor = white;
tooltipBackgroundColor = new ColorUIResource(24, 24, 24);
controlFont = new FontUIResource("Dialog", Font.BOLD, 12);
systemFont = new FontUIResource("Dialog", Font.BOLD, 12);
userFont = new FontUIResource("Dialog", Font.BOLD, 12);
menuFont = new FontUIResource("Dialog", Font.BOLD, 12);
windowTitleFont = new FontUIResource("Dialog", Font.BOLD, 12);
controlFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
systemFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
userFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
menuFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
windowTitleFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
smallFont = new FontUIResource("Dialog", Font.PLAIN, 10);
}

View File

@@ -42,18 +42,18 @@ public class HiFiLookAndFeel extends AbstractLookAndFeel {
private static final Properties giantFontProps = new Properties();
static {
smallFontProps.setProperty("controlTextFont", "Dialog bold 10");
smallFontProps.setProperty("systemTextFont", "Dialog bold 10");
smallFontProps.setProperty("controlTextFont", "Dialog 10"); // bold
smallFontProps.setProperty("systemTextFont", "Dialog 10"); // bold
smallFontProps.setProperty("userTextFont", "Dialog 10");
smallFontProps.setProperty("menuTextFont", "Dialog bold 10");
smallFontProps.setProperty("windowTitleFont", "Dialog bold 10");
smallFontProps.setProperty("menuTextFont", "Dialog 10"); // bold
smallFontProps.setProperty("windowTitleFont", "Dialog 10"); // bold
smallFontProps.setProperty("subTextFont", "Dialog 8");
largeFontProps.setProperty("controlTextFont", "Dialog bold 14");
largeFontProps.setProperty("systemTextFont", "Dialog bold 14");
largeFontProps.setProperty("userTextFont", "Dialog bold 14");
largeFontProps.setProperty("menuTextFont", "Dialog bold 14");
largeFontProps.setProperty("windowTitleFont", "Dialog bold 14");
largeFontProps.setProperty("controlTextFont", "Dialog 14"); // bold
largeFontProps.setProperty("systemTextFont", "Dialog 14"); // bold
largeFontProps.setProperty("userTextFont", "Dialog 14"); // bold
largeFontProps.setProperty("menuTextFont", "Dialog 14"); // bold
largeFontProps.setProperty("windowTitleFont", "Dialog 14"); // bold
largeFontProps.setProperty("subTextFont", "Dialog 12");
giantFontProps.setProperty("controlTextFont", "Dialog 18");

View File

@@ -14,6 +14,7 @@ public class AGC implements Effect {
attack = a;
decay = d;
limit = l;
gain = 1d;
}
public String getName() {
@@ -24,27 +25,37 @@ public class AGC implements Effect {
return getName();
}
public double process(double sample) {
double absSample = Math.abs(sample) * gain;
public void process(double[][] samples) {
gain = 1d;
for (int i = 0; i < samples.length; i++) {
double absSampleLeft = Math.abs(samples[i][Sentence.LEFT]) * gain;
double absSampleRight = Math.abs(samples[i][Sentence.RIGHT]) * gain;
if (absSample > ceiling) {
gain -= attack;
if (gain < 0) gain = 0;
}
if (absSample < ceiling) {
gain += decay;
if (gain > limit) {
gain = limit;
double factor = 0.0d;
if (absSampleLeft > ceiling) {
factor = -attack;
}
if (absSampleRight > ceiling) {
factor = -attack;
}
if ((absSampleLeft < ceiling) && (absSampleRight < ceiling)) {
factor = decay;
}
gain += factor;
if (gain > limit) gain = limit;
if (gain < 0) gain = 0;
samples[i][Sentence.LEFT] *= gain;
samples[i][Sentence.RIGHT] *= gain;
}
sample *= gain;
return sample;
}
public void init(double sr) {
gain = 1d;
}
public void dump() {

View File

@@ -19,8 +19,11 @@ public class Amplifier implements Effect {
return null;
}
public double process(double sample) {
return sample * gain;
public void process(double[][] samples) {
for (int i = 0; i < samples.length; i++) {
samples[i][Sentence.LEFT] *= gain;
samples[i][Sentence.RIGHT] *= gain;
}
}
public double getGain() {

File diff suppressed because it is too large Load Diff

View File

@@ -32,7 +32,8 @@ public class Biquad implements Effect {
int type;
double a0, a1, a2, b1, b2;
double Fc, Q, peakGain;
double z1, z2;
double lz1, lz2;
double rz1, rz2;
double sampleFrequency;
public Biquad() {
@@ -45,15 +46,19 @@ public class Biquad implements Effect {
Fc = 440d;
Q = 0.707d;
peakGain = 0.0d;
z1 = 0.0d;
z2 = 0.0d;
lz1 = 0.0d;
lz2 = 0.0d;
rz1 = 0.0d;
rz2 = 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;
lz1 = 0.0;
lz2 = 0.0;
rz1 = 0.0;
rz2 = 0.0;
sampleFrequency = 44100d;
}
@@ -84,17 +89,33 @@ public class Biquad implements Effect {
setPeakGain(peakGainDB);
}
public double process(double in) {
double out = in * a0 + z1;
z1 = in * a1 + z2 - b1 * out;
z2 = in * a2 - b2 * out;
return out;
public void process(double[][] samples) {
lz1 = 0d;
lz2 = 0d;
rz1 = 0d;
rz2 = 0d;
for (double[] in : samples) {
double lout = in[Sentence.LEFT] * a0 + lz1;
lz1 = in[Sentence.LEFT] * a1 + lz2 - b1 * lout;
lz2 = in[Sentence.LEFT] * a2 - b2 * lout;
double rout = in[Sentence.RIGHT] * a0 + rz1;
rz1 = in[Sentence.RIGHT] * a1 + rz2 - b1 * rout;
rz2 = in[Sentence.RIGHT] * a2 - b2 * rout;
in[Sentence.LEFT] = lout;
in[Sentence.RIGHT] = rout;
}
}
public void init(double sf) {
sampleFrequency = sf;
z1 = 0d;
z2 = 0d;
lz1 = 0d;
lz2 = 0d;
rz1 = 0d;
rz2 = 0d;
calcBiquad();
}

View File

@@ -206,4 +206,14 @@ public class Book extends DefaultMutableTreeNode {
return out;
}
public void purgeBackups() {
for (Enumeration o = children(); o.hasMoreElements();) {
Object ob = (Object)o.nextElement();
if (ob instanceof Chapter) {
Chapter c = (Chapter)ob;
c.purgeBackups();
}
}
}
}

View File

@@ -3,11 +3,14 @@ package uk.co.majenko.audiobookrecorder;
import javax.swing.*;
import javax.swing.tree.*;
import java.awt.*;
import javax.swing.border.*;
public class BookTreeRenderer extends DefaultTreeCellRenderer {
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
JLabel ret = (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
ret.setIconTextGap(0);
ret.setBorder(new EmptyBorder(0, 0, 0, 0));
if (value instanceof Sentence) {
Sentence s = (Sentence)value;
@@ -31,8 +34,40 @@ public class BookTreeRenderer extends DefaultTreeCellRenderer {
icn.add(Overlays.important, OverlayIcon.TOP_RIGHT);
}
if (s.getEndOffset() == s.getSampleSize() - 1) {
icn.add(Overlays.important, OverlayIcon.TOP_RIGHT);
}
if (s.getEffectChain() != null) {
if (!s.getEffectChain().equals("none")) {
icn.add(Overlays.filter, OverlayIcon.BOTTOM_RIGHT);
}
}
ret.setIcon(icn);
String gaptype = s.getPostGapType();
DefaultMutableTreeNode prev = s.getPreviousSibling();
String prevtype = "sentence";
if (prev instanceof Sentence) {
Sentence s2 = (Sentence)prev;
prevtype = s2.getPostGapType();
}
if (prevtype.equals("continuation")) {
ret.setIconTextGap(20);
}
if (gaptype.equals("sentence")) {
ret.setBorder(new EmptyBorder(0, 0, 0, 0));
} else if (gaptype.equals("continuation")) {
ret.setBorder(new EmptyBorder(0, 0, 0, 0));
} else if (gaptype.equals("paragraph")) {
ret.setBorder(new EmptyBorder(0, 0, 7, 0));
} else if (gaptype.equals("section")) {
ret.setBorder(new EmptyBorder(0, 0, 15, 0));
}
} else if (value instanceof Chapter) {
ret.setIcon(Icons.chapter);
} else if (value instanceof Book) {

View File

@@ -13,6 +13,9 @@ public class CacheManager {
if (ob.lockedInCache()) {
cache.add(ob);
} else {
if (ob instanceof Sentence) {
Sentence s = (Sentence)ob;
}
ob.clearCache();
}
}
@@ -23,4 +26,19 @@ public class CacheManager {
public static void setCacheSize(int c) {
cacheSize = c;
}
public static void removeFromCache(Cacheable c) {
if (c instanceof Sentence) {
Sentence s = (Sentence)c;
}
cache.remove(c);
c.clearCache();
}
public static void purgeCache() {
for (Cacheable c : cache) {
c.clearCache();
}
cache.clear();
}
}

View File

@@ -0,0 +1,58 @@
package uk.co.majenko.audiobookrecorder;
import java.util.ArrayList;
public class Chain implements Effect {
String target;
public Chain(String t) {
target = t;
}
public Chain() {
target = null;
}
public void process(double[][] samples) {
if (target != null) {
Effect t = AudiobookRecorder.window.effects.get(target);
if (t != null) {
t.process(samples);
}
}
}
public void setTarget(String t) {
target = t;
}
public String getTarget() {
return target;
}
public String toString() {
return "Chain to " + target;
}
public void dump() {
System.out.println(toString());
}
public void init(double sf) {
if (target != null) {
Effect t = AudiobookRecorder.window.effects.get(target);
if (t != null) {
t.init(sf);
}
}
}
public ArrayList<Effect> getChildEffects() {
return null;
}
public String getName() {
return toString();
}
}

View File

@@ -116,13 +116,13 @@ public class Chapter extends DefaultMutableTreeNode {
audioAttributes.setCodec("libmp3lame");
audioAttributes.setBitRate(Options.getInteger("audio.export.bitrate"));
audioAttributes.setSamplingRate(Options.getInteger("audio.export.samplerate"));
audioAttributes.setChannels(2); //new Integer(2));
audioAttributes.setChannels(Options.getInteger("audio.export.channels")); //new Integer(2));
attributes.setFormat("mp3");
attributes.setAudioAttributes(audioAttributes);
AudioFormat sampleformat = AudiobookRecorder.window.roomNoise.getAudioFormat();
AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 1, true, false);
AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 2, true, false);
byte[] data;
int fullLength = 0;
@@ -232,5 +232,21 @@ public class Chapter extends DefaultMutableTreeNode {
return out;
}
public void resetPostGaps() {
for (Enumeration s = children(); s.hasMoreElements();) {
Sentence snt = (Sentence)s.nextElement();
snt.resetPostGap();
}
}
public void purgeBackups() {
for (Enumeration o = children(); o.hasMoreElements();) {
Object ob = (Object)o.nextElement();
if (ob instanceof Sentence) {
Sentence s = (Sentence)ob;
s.purgeBackups();
}
}
}
}

View File

@@ -0,0 +1,49 @@
package uk.co.majenko.audiobookrecorder;
import java.util.ArrayList;
public class Clipping implements Effect {
double clip;
public Clipping() {
clip = 1.0d;
}
public Clipping(double g) {
clip = g;
}
public String getName() {
return "Clipping (" + clip + ")";
}
public ArrayList<Effect> getChildEffects() {
return null;
}
public void process(double[][] samples) {
for (double[] sample : samples) {
if (sample[Sentence.LEFT] > clip) sample[Sentence.LEFT] = clip;
if (sample[Sentence.LEFT] < -clip) sample[Sentence.LEFT] = -clip;
if (sample[Sentence.RIGHT] > clip) sample[Sentence.RIGHT] = clip;
if (sample[Sentence.RIGHT] < -clip) sample[Sentence.RIGHT] = -clip;
}
}
public double getClip() {
return clip;
}
public void setClip(double g) {
clip = g;
}
public String toString() {
return getName();
}
public void dump() {
System.out.println(toString());
}
public void init(double sf) {
}
}

View File

@@ -0,0 +1,12 @@
package uk.co.majenko.audiobookrecorder;
public class Debug {
static long timestamp;
static void debug(String msg) {
long now = System.currentTimeMillis();
long diff = now - timestamp;
timestamp = now;
System.err.println(String.format("%8d - %s", diff, msg));
}
}

View File

@@ -6,6 +6,8 @@ public class DelayLine implements Effect {
ArrayList<DelayLineStore> delayLines;
boolean wetOnly = false;
public DelayLine() {
delayLines = new ArrayList<DelayLineStore>();
}
@@ -14,23 +16,70 @@ public class DelayLine implements Effect {
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);
public void process(double[][] samples) {
double[][] savedSamples = new double[samples.length][2];
for (int i = 0; i < samples.length; i++) {
savedSamples[i][Sentence.LEFT] = samples[i][Sentence.LEFT];
savedSamples[i][Sentence.RIGHT] = samples[i][Sentence.RIGHT];
}
if (wetOnly) {
for (int i = 0; i < samples.length; i++) {
samples[i][Sentence.LEFT] = 0d;
samples[i][Sentence.RIGHT] = 0d;
}
}
double[][] subSamples = new double[samples.length][2];
for (int i = 0; i < samples.length; i++) {
subSamples[i][Sentence.LEFT] = savedSamples[i][Sentence.LEFT];
subSamples[i][Sentence.RIGHT] = savedSamples[i][Sentence.RIGHT];
}
for (DelayLineStore d : delayLines) {
for (int i = 0; i < samples.length; i++) {
subSamples[i][Sentence.LEFT] = savedSamples[i][Sentence.LEFT];
subSamples[i][Sentence.RIGHT] = savedSamples[i][Sentence.RIGHT];
}
d.process(subSamples);
for (int i = 0; i < subSamples.length; i++) {
int off = i + d.getSamples();
if ((off < samples.length) && (off > 0)) {
double[] ns = mix(samples[off], subSamples[i]);
samples[off][Sentence.LEFT] = ns[Sentence.LEFT];
samples[off][Sentence.RIGHT] = ns[Sentence.RIGHT];
}
}
}
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);
double[] mix(double[] a, double[] b) {
double[] out = new double[2];
if ((a[Sentence.LEFT] < 0) && (b[Sentence.LEFT] < 0)) {
out[Sentence.LEFT] = (a[Sentence.LEFT] + b[Sentence.LEFT]) - (a[Sentence.LEFT] * b[Sentence.LEFT]);
} else if ((a[Sentence.LEFT] > 0) && (b[Sentence.LEFT] > 0)) {
out[Sentence.LEFT] = (a[Sentence.LEFT] + b[Sentence.LEFT]) - (a[Sentence.LEFT] * b[Sentence.LEFT]);
} else {
out[Sentence.LEFT] = a[Sentence.LEFT] + b[Sentence.LEFT];
}
return a + b;
if ((a[Sentence.RIGHT] < 0) && (b[Sentence.RIGHT] < 0)) {
out[Sentence.RIGHT] = (a[Sentence.RIGHT] + b[Sentence.RIGHT]) - (a[Sentence.RIGHT] * b[Sentence.RIGHT]);
} else if ((a[Sentence.RIGHT] > 0) && (b[Sentence.RIGHT] > 0)) {
out[Sentence.RIGHT] = (a[Sentence.RIGHT] + b[Sentence.RIGHT]) - (a[Sentence.RIGHT] * b[Sentence.RIGHT]);
} else {
out[Sentence.RIGHT] = a[Sentence.RIGHT] + b[Sentence.RIGHT];
}
return out;
}
public DelayLineStore addDelayLine(int samples, double gain, double pan) {
DelayLineStore s = new DelayLineStore(samples, gain, pan);
delayLines.add(s);
return s;
}
public DelayLineStore addDelayLine(int samples, double gain) {
@@ -60,5 +109,7 @@ public class DelayLine implements Effect {
}
}
public void setWetOnly(boolean b) {
wetOnly = b;
}
}

View File

@@ -4,42 +4,46 @@ import java.util.concurrent.ArrayBlockingQueue;
import java.util.ArrayList;
public class DelayLineStore {
ArrayBlockingQueue<Double> fifo;
double gain;
int numSamples;
double pan;
ArrayList<Effect> effects;
public DelayLineStore(int s, double g) {
fifo = new ArrayBlockingQueue<Double>(s);
for (int i = 0; i < s; i++) {
fifo.add(0d);
}
public DelayLineStore(int s, double g, double p) {
numSamples = s;
gain = g;
pan = p;
effects = new ArrayList<Effect>();
}
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();
public DelayLineStore(int s, double g) {
numSamples = s;
gain = g;
pan = 0d;
effects = new ArrayList<Effect>();
}
public void process(double[][] samples) {
for (Effect e : effects) {
e.process(samples);
}
for (double[] sample : samples) {
sample[Sentence.LEFT] *= gain;
sample[Sentence.RIGHT] *= gain;
if (pan < 0) {
double p = 1 + pan;
sample[Sentence.RIGHT] *= p;
} else {
double p = 1 - pan;
sample[Sentence.LEFT] *= p;
}
}
return 0d;
}
public void setSamples(int s) {
fifo = new ArrayBlockingQueue<Double>(s);
for (int i = 0; i < s; i++) {
fifo.add(0d);
}
numSamples = s;
}
@@ -55,13 +59,6 @@ public class DelayLineStore {
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);
}
@@ -70,7 +67,6 @@ public class DelayLineStore {
for (Effect e : effects) {
e.init(sf);
}
purge();
}
public void dump() {

View File

@@ -3,7 +3,7 @@ package uk.co.majenko.audiobookrecorder;
import java.util.ArrayList;
public interface Effect {
public double process(double sample);
public void process(double[][] samples);
public String getName();
public ArrayList<Effect> getChildEffects();
public void dump();

View File

@@ -16,12 +16,10 @@ public class EffectGroup implements Effect {
effects = new ArrayList<Effect>();
}
public double process(double sample) {
double out = sample;
public void process(double[][] samples) {
for (Effect e : effects) {
out = e.process(out);
e.process(samples);
}
return out;
}
public void setName(String n) {

View File

@@ -27,6 +27,8 @@ public class FlashPanel extends JPanel {
public void setFlash(boolean f) {
flash = f;
col = true;
for (Component o : getComponents()) {
((JComponent)o).setVisible(!f);
}
@@ -42,7 +44,7 @@ public class FlashPanel extends JPanel {
if (col) {
g.setColor(Color.RED);
} else {
g.setColor(Color.BLACK);
g.setColor(Color.GREEN);
}
Dimension d = getSize();
g.fillRect(0, 0, d.width, d.height);

View File

@@ -1,83 +0,0 @@
package uk.co.majenko.audiobookrecorder;
import java.util.*;
import java.util.concurrent.*;
import javax.swing.*;
import java.awt.*;
import java.util.Timer;
public class HavenQueue extends JPanel {
ConcurrentLinkedQueue<Sentence> sentenceList = new ConcurrentLinkedQueue<Sentence>();
Timer timer = new Timer();
Sentence currentSentence = null;
JLabel count;
public HavenQueue() {
timer.schedule(new TimerTask() { public void run() { processQueue(); }}, 30000);
count = new JLabel("Haven queue: 0");
setLayout(new BorderLayout());
add(count, BorderLayout.CENTER);
count.setOpaque(false);
setOpaque(false);
count.setForeground(Color.WHITE);
}
public void processQueue() {
count.setText("Haven queue: " + sentenceList.size());
if (currentSentence == null) {
// Grab a new sentence to process.
currentSentence = sentenceList.poll();
if (currentSentence != null) {
if (!currentSentence.postHavenData()) { // Failed. Add to the end of the queue and wait a bit
submit(currentSentence);
currentSentence = null;
timer.schedule(new TimerTask() { public void run() { processQueue(); }}, 30000);
return;
}
timer.schedule(new TimerTask() { public void run() { processQueue(); }}, 5000);
return;
}
timer.schedule(new TimerTask() { public void run() { processQueue(); }}, 5000);
return;
}
if (currentSentence != null) {
currentSentence.processPendingHaven();
int status = currentSentence.getHavenStatus();
switch (status) {
case 0: // Um... not running...?
currentSentence = null;
timer.schedule(new TimerTask() { public void run() { processQueue(); }}, 30000);
return;
case 1: // Still processing...
timer.schedule(new TimerTask() { public void run() { processQueue(); }}, 5000);
return;
case 2: // Finished
currentSentence = null;
timer.schedule(new TimerTask() { public void run() { processQueue(); }}, 30000);
return;
case 3: // Failed
currentSentence = null;
timer.schedule(new TimerTask() { public void run() { processQueue(); }}, 30000);
return;
}
}
timer.schedule(new TimerTask() { public void run() { processQueue(); }}, 30000);
}
public void submit(Sentence s) {
s.setOverrideText("[queued...]");
AudiobookRecorder.window.bookTreeModel.reload(s);
sentenceList.add(s);
count.setText("Haven queue: " + sentenceList.size());
}
}

View File

@@ -33,4 +33,9 @@ public class Icons {
static public final ImageIcon dollar = new ImageIcon(Icons.class.getResource("icons/dollar.png"));
static public final ImageIcon attention = new ImageIcon(Icons.class.getResource("icons/attention.png"));
static public final ImageIcon normalize = new ImageIcon(Icons.class.getResource("icons/normalize.png"));
static public final ImageIcon split = new ImageIcon(Icons.class.getResource("icons/split.png"));
static public final ImageIcon cut = new ImageIcon(Icons.class.getResource("icons/cut.png"));
static public final ImageIcon docut = new ImageIcon(Icons.class.getResource("icons/do-cut.png"));
static public final ImageIcon playto = new ImageIcon(Icons.class.getResource("icons/play-to.png"));
static public final ImageIcon disable = new ImageIcon(Icons.class.getResource("icons/disable-effects.png"));
}

View File

@@ -9,37 +9,74 @@ public class LFO implements Effect {
double depth;
double sampleRate;
double sampleStep;
int waveform;
double duty;
int mode;
public static final int SINE = 0;
public static final int COSINE = 1;
public static final int SQUARE = 2;
public static final int TRIANGLE = 3;
public static final int SAWTOOTH = 4;
public static final int ADD = 0;
public static final int REPLACE = 1;
public LFO(double f, double d) {
frequency = f;
depth = d;
phase = 0;
this(f, d, 0, SINE, Math.PI, REPLACE);
}
public LFO(double f, double d, double p) {
this(f, d, p, SINE, Math.PI, REPLACE);
}
public LFO(double f, double d, double p, int w) {
this(f, d, p, w, Math.PI, REPLACE);
}
public LFO(double f, double d, double p, int w, double dty) {
this(f, d, p, w, dty, REPLACE);
}
public LFO(double f, double d, double p, int w, double dty, int m) {
frequency = f;
depth = d;
phase = p;
waveform = w;
duty = dty;
mode = m;
}
public double process(double sample) {
double v = Math.sin(phase);
phase += sampleStep;
if (phase > (Math.PI * 2d)) {
phase -= (Math.PI * 2d);
public void process(double[][] samples) {
for (double[] sample : samples) {
double v = 0;
switch (waveform) {
case SINE: v = Math.sin(phase); break;
case COSINE: v = Math.cos(phase); break;
case SQUARE: v = (phase > duty) ? 1d : 0d; break;
case TRIANGLE: v = (phase < Math.PI) ? (phase / Math.PI) : (1d - ((phase - Math.PI) / Math.PI)); break;
case SAWTOOTH: v = (phase / (Math.PI*2d)); break;
}
phase += sampleStep;
if (phase > (Math.PI * 2d)) {
phase -= (Math.PI * 2d);
}
// Multiply it by the gain factor
v *= depth;
// Apply it to the sample
switch (mode) {
case REPLACE:
sample[Sentence.LEFT] = (sample[Sentence.LEFT] * v);
sample[Sentence.RIGHT] = (sample[Sentence.RIGHT] * v);
break;
case ADD:
sample[Sentence.LEFT] += (sample[Sentence.LEFT] * v);
sample[Sentence.RIGHT] += (sample[Sentence.RIGHT] * v);
break;
}
}
// // Make it between 0 and 1.
// v = 1d + v;
// v /= 2d;
// Multiply it by the gain factor
v *= depth;
// Apply it to the sample
sample += (sample * v);
return sample;
}
public String getName() { return "Low Frequency Oscillator (" + frequency + " Hz, " + (depth * 100d) + "%)"; }

View File

@@ -14,9 +14,14 @@ public class MainToolBar extends JToolBar {
JButtonSpacePlay recordRoomNoise;
JButtonSpacePlay playSentence;
JButtonSpacePlay playonSentence;
JButtonSpacePlay playtoSentence;
JButtonSpacePlay stopPlaying;
JButtonSpacePlay eq;
JToggleButton mic;
JToggleButtonSpacePlay mic;
JComboBox<String> playbackSpeed;
JToggleButtonSpacePlay disableEffects;
AudiobookRecorder root;
@@ -78,6 +83,14 @@ public class MainToolBar extends JToolBar {
});
add(playonSentence);
playtoSentence = new JButtonSpacePlay(Icons.playto, "Play sentence to cut", new ActionListener() {
public void actionPerformed(ActionEvent e) {
root.playToSelectedSentence();
}
});
add(playtoSentence);
playtoSentence.setEnabled(false);
stopPlaying = new JButtonSpacePlay(Icons.stop, "Stop playing", new ActionListener() {
public void actionPerformed(ActionEvent e) {
root.stopPlaying();
@@ -89,6 +102,7 @@ public class MainToolBar extends JToolBar {
eq = new JButtonSpacePlay(Icons.eq, "Reload Effects", new ActionListener() {
public void actionPerformed(ActionEvent e) {
root.loadEffects();
CacheManager.purgeCache();
}
});
@@ -117,11 +131,56 @@ public class MainToolBar extends JToolBar {
}
}
});
add(mic);
addSeparator();
disableEffects = new JToggleButtonSpacePlay(Icons.disable, "Disable effects", new ActionListener() {
Color bgCol = null;
public void actionPerformed(ActionEvent e) {
JToggleButton b = (JToggleButton)e.getSource();
if (b.isSelected()) {
root.setEffectsEnabled(false);
bgCol = b.getBackground();
b.setBackground(Color.RED);
} else {
root.setEffectsEnabled(true);
if (bgCol != null) {
b.setBackground(bgCol);
}
}
}
});
add(disableEffects);
addSeparator();
add(new JLabel("Playback speed: "));
playbackSpeed = new JComboBox<String>(new String[] {
"0.75x",
"1.00x",
"1.25x",
"1.50x",
"1.75x"
});
playbackSpeed.setFocusable(false);
playbackSpeed.setSelectedIndex(1);
add(playbackSpeed);
setFloatable(false);
}
public void enablePlayTo(boolean b) {
playtoSentence.setEnabled(b);
}
public float getPlaybackSpeed() {
int v = playbackSpeed.getSelectedIndex();
return 0.75f + (0.25f * v);
}
}

View File

@@ -24,6 +24,8 @@ public class Options extends JDialog {
JComboBox<KVPair> rateList;
JComboBox<KVPair> bitDepth;
JComboBox<KVPair> trimMethod;
JComboBox<KVPair> fftBlockSize;
JComboBox<KVPair> playbackBlockSize;
JTextField storageFolder;
JTextField archiveFolder;
JSpinner preChapterGap;
@@ -34,6 +36,7 @@ public class Options extends JDialog {
JSpinner postSectionGap;
JTextField ffmpegLocation;
JComboBox<KVPair> bitRate;
JComboBox<KVPair> channels;
JComboBox<KVPair> exportRate;
JCheckBox enableParsing;
JSpinner cacheSize;
@@ -44,8 +47,6 @@ public class Options extends JDialog {
JSpinner etherealAttenuation;
JSpinner etherealOffset;
JTextField havenApiKey;
JTextField externalEditor;
JTextArea startupScript;
@@ -299,10 +300,12 @@ public class Options extends JDialog {
bitDepth = addDropdown(optionsPanel, "Sample resolution:", getResolutionList(), get("audio.recording.resolution"));
trimMethod = addDropdown(optionsPanel, "Auto-trim method:", getTrimMethods(), get("audio.recording.trim"));
fftThreshold = addSpinner(optionsPanel, "FFT threshold:", 0, 100, 1, getInteger("audio.recording.trim.fft"), "");
fftBlockSize = addDropdown(optionsPanel, "FFT Block size:", getFFTBlockSizes(), get("audio.recording.trim.blocksize"));
addSeparator(optionsPanel);
playbackList = addDropdown(optionsPanel, "Playback device:", getPlaybackMixerList(), get("audio.playback.device"));
playbackBlockSize = addDropdown(optionsPanel, "Playback Block size:", getPlaybackBlockSizes(), get("audio.playback.blocksize"));
addSeparator(optionsPanel);
storageFolder = addFilePath(optionsPanel, "Storage folder:", get("path.storage"), true);
archiveFolder = addFilePath(optionsPanel, "Archive folder:", get("path.archive"), true);
@@ -320,21 +323,21 @@ public class Options extends JDialog {
ffmpegLocation = addFilePath(optionsPanel, "FFMPEG location:", get("path.ffmpeg"), false);
bitRate = addDropdown(optionsPanel, "Export bitrate:", getBitrates(), get("audio.export.bitrate"));
channels = addDropdown(optionsPanel, "Export channels:", getChannelCountList(), get("audio.export.channels"));
exportRate = addDropdown(optionsPanel, "Export sample rate:", getSampleRateList(), get("audio.export.samplerate"));
addSeparator(optionsPanel);
enableParsing = addCheckBox(optionsPanel, "Enable automatic speech-to-text submission", getBoolean("process.haven.auto"));
havenApiKey = addTextField(optionsPanel, "Haven OnDemand API Key", get("process.haven.apikey"));
enableParsing = addCheckBox(optionsPanel, "Enable automatic sphinx speech-to-text (**SLOW**)", getBoolean("process.sphinx"));
addSeparator(optionsPanel);
externalEditor = addTextField(optionsPanel, "External Editor Command", get("editor.external"));
addSeparator(optionsPanel);
cacheSize = addSpinner(optionsPanel, "Cache size:", 0, 5000, 1, getInteger("cache.size"), "");
cacheSize = addSpinner(optionsPanel, "Cache size:", 2, 100, 1, getInteger("cache.size"), "");
addSeparator(optionsPanel);
tabs.add("Options", new JScrollPane(optionsPanel));
@@ -568,6 +571,8 @@ public class Options extends JDialog {
} else {
defaultPrefs.put("audio.playback.device", "");
}
defaultPrefs.put("audio.recording.trim.blocksize", "4096");
defaultPrefs.put("audio.playback.blocksize", "4096");
defaultPrefs.put("catenation.pre-chapter", "1000");
defaultPrefs.put("catenation.post-chapter", "1500");
@@ -583,10 +588,9 @@ public class Options extends JDialog {
defaultPrefs.put("path.ffmpeg", "");
defaultPrefs.put("audio.export.bitrate", "256000");
defaultPrefs.put("audio.export.channels", "2");
defaultPrefs.put("audio.export.samplerate", "44100");
defaultPrefs.put("process.sphinx", "false");
defaultPrefs.put("process.haven.apikey", "");
defaultPrefs.put("editor.external", "");
@@ -708,12 +712,14 @@ public class Options extends JDialog {
set("catenation.post-paragraph", postParagraphGap.getValue());
set("catenation.post-section", postSectionGap.getValue());
set("audio.export.bitrate", ((KVPair)bitRate.getSelectedItem()).key);
set("audio.export.channels", ((KVPair)channels.getSelectedItem()).key);
set("audio.export.samplerate", ((KVPair)exportRate.getSelectedItem()).key);
set("process.sphinx", enableParsing.isSelected());
set("process.haven.apikey", havenApiKey.getText());
set("editor.external", externalEditor.getText());
set("cache.size", cacheSize.getValue());
set("audio.recording.trim.fft", fftThreshold.getValue());
set("audio.recording.trim.blocksize", ((KVPair)fftBlockSize.getSelectedItem()).key);
set("audio.playback.blocksize", ((KVPair)playbackBlockSize.getSelectedItem()).key);
set("effects.ethereal.offset", etherealOffset.getValue());
set("effects.ethereal.iterations", etherealIterations.getValue());
@@ -781,12 +787,38 @@ public class Options extends JDialog {
}
public static KVPair[] getTrimMethods() {
KVPair[] pairs = new KVPair[2];
pairs[0] = new KVPair<String, String>("peak", "Peak Amplitude");
pairs[1] = new KVPair<String, String>("fft", "FFT Analysis");
KVPair[] pairs = new KVPair[3];
pairs[0] = new KVPair<String, String>("none", "None");
pairs[1] = new KVPair<String, String>("peak", "Peak Amplitude");
pairs[2] = new KVPair<String, String>("fft", "FFT Analysis");
return pairs;
}
public static KVPair[] getFFTBlockSizes() {
KVPair[] pairs = new KVPair[8];
pairs[0] = new KVPair<String, String>("1024", "1024");
pairs[1] = new KVPair<String, String>("2048", "2048");
pairs[2] = new KVPair<String, String>("4096", "4096");
pairs[3] = new KVPair<String, String>("8192", "8192");
pairs[4] = new KVPair<String, String>("16384", "16384");
pairs[5] = new KVPair<String, String>("32768", "32768");
pairs[6] = new KVPair<String, String>("65536", "65537");
pairs[7] = new KVPair<String, String>("131072", "131072");
return pairs;
}
public static KVPair[] getPlaybackBlockSizes() {
KVPair[] pairs = new KVPair[8];
pairs[0] = new KVPair<String, String>("1024", "1024");
pairs[1] = new KVPair<String, String>("2048", "2048");
pairs[2] = new KVPair<String, String>("4096", "4096");
pairs[3] = new KVPair<String, String>("8192", "8192");
pairs[4] = new KVPair<String, String>("16384", "16384");
pairs[5] = new KVPair<String, String>("32768", "32768");
pairs[6] = new KVPair<String, String>("65536", "65537");
pairs[7] = new KVPair<String, String>("131072", "131072");
return pairs;
}
public static void createEffectChains() {
effectChains = new ArrayList<EffectGroup>();

View File

@@ -0,0 +1,52 @@
package uk.co.majenko.audiobookrecorder;
import java.util.ArrayList;
public class Pan implements Effect {
double pan;
public Pan() {
pan = 0.0d;
}
public Pan(double p) {
pan = p;
}
public String getName() {
return "Pan (" + pan + ")";
}
public ArrayList<Effect> getChildEffects() {
return null;
}
public void process(double[][] samples) {
for (double[] sample : samples) {
if (pan < 0) {
double p = 1 + pan;
sample[Sentence.RIGHT] *= p;
} else {
double p = 1 - pan;
sample[Sentence.LEFT] *= p;
}
}
}
public double getPan() {
return pan;
}
public void setPan(double p) {
pan = p;
}
public String toString() {
return getName();
}
public void dump() {
System.out.println(toString());
}
public void init(double sf) {
}
}

View File

@@ -1,28 +0,0 @@
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;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -63,4 +63,17 @@ public class Utils {
}
}
}
static long millis = System.currentTimeMillis();
public static void report(String tag) {
long t = System.currentTimeMillis();
long d = t - millis;
millis = t;
System.err.println(String.format("%10d - %10s : %8d | %8d | %8d", d, tag,
Runtime.getRuntime().totalMemory(),
Runtime.getRuntime().maxMemory(),
Runtime.getRuntime().freeMemory()
));
}
}

View File

@@ -9,7 +9,7 @@ import javax.sound.sampled.*;
public class Waveform extends JPanel implements MouseListener, MouseMotionListener {
double[] samples = null;
double[][] samples = null;
int leftMarker = 0;
int rightMarker = 0;
@@ -21,6 +21,11 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
int leftAltMarker = 0;
int rightAltMarker = 0;
int cutEntry = 0;
int cutExit = 0;
boolean displayCut = false;
boolean displaySplit = false;
int dragging = 0;
int step = 1;
@@ -92,7 +97,8 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
double lmax = 0;
for (int o = 0; o < step; o++) {
double sample = samples[offset + (n * step) + o];
if (offset + (n * step) + o >= samples.length) break;
double sample = (samples[offset + (n * step) + o][Sentence.LEFT] + samples[offset + (n * step) + o][Sentence.RIGHT]) / 2d;
if (sample >= 0) {
have += sample;
hcnt++;
@@ -133,7 +139,7 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
g.drawLine(n, (int)(h/2 + lave), n, (int)(h/2 - have));
}
g.setColor(new Color(255, 0, 0, 32));
g.setColor(new Color(255, 0, 0, 64));
g.fillRect(0, 0, (leftAltMarker - offset)/step, h);
g.fillRect((rightAltMarker - offset)/step, 0, (num - rightAltMarker) / step , h);
@@ -145,6 +151,19 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
g.drawLine((leftMarker - offset)/step, 0, (leftMarker - offset)/step, h);
g.drawLine((rightMarker - offset)/step, 0, (rightMarker - offset)/step, h);
if (displayCut || displaySplit) {
g.setColor(new Color(0, 255, 255));
g.drawLine((cutEntry - offset)/step, 0, (cutEntry - offset)/step, h);
}
if (displayCut) {
g.setColor(new Color(0, 255, 255));
g.drawLine((cutExit - offset)/step, 0, (cutExit - offset)/step, h);
g.setColor(new Color(0, 255, 255, 80));
g.fillRect((cutEntry - offset)/step, 0, ((cutExit - offset) - (cutEntry - offset))/step , h);
}
g.setColor(new Color(0, 255, 255));
for (int i = 0; i < h; i += 2) {
g.drawLine((playMarker - offset) / step, i, (playMarker - offset) / step, i);
@@ -189,9 +208,11 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
repaint();
}
public void setData(double[] s) {
public void setData(double[][] s) {
samples = s;
playMarker = 0;
displayCut = false;
displaySplit = false;
repaint();
}
@@ -221,6 +242,19 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
dragging = 2;
return;
}
if (displayCut || displaySplit) {
if ((x >= ((cutEntry - offset)/step) - 10) && (x <= ((cutEntry - offset)/step) + 10)) {
dragging = 3;
return;
}
}
if (displayCut) {
if ((x >= ((cutExit - offset)/step) - 10) && (x <= ((cutExit - offset)/step) + 10)) {
dragging = 4;
return;
}
}
}
public void mouseReleased(MouseEvent e) {
@@ -251,6 +285,19 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
return;
}
if (displayCut || displaySplit) {
if ((x >= ((cutEntry - offset)/step) - 10) && (x <= ((cutEntry - offset)/step) + 10)) {
setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
return;
}
}
if (displayCut) {
if ((x >= ((cutExit - offset)/step) - 10) && (x <= ((cutExit - offset)/step) + 10)) {
setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
return;
}
}
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
@@ -270,6 +317,18 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
if (rightMarker < leftMarker) {
rightMarker = leftMarker;
}
} else if (dragging == 3) {
cutEntry = (x * step) + offset;
if (displayCut) {
if (cutEntry > cutExit) {
cutEntry = cutExit;
}
}
} else if (dragging == 4) {
cutExit = (x * step) + offset;
if (cutExit < cutEntry) {
cutExit = cutEntry;
}
}
repaint();
@@ -299,4 +358,33 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
playMarker = leftAltMarker + m;
repaint();
}
public void setDisplayCut(boolean c) {
displayCut = c;
displaySplit = false;
if (displayCut) {
int d = rightMarker - leftMarker;
cutEntry = leftMarker + (d / 3);
cutExit = leftMarker + (d / 3 * 2);
}
repaint();
}
public void setDisplaySplit(boolean c) {
displayCut = false;
displaySplit = c;
if (displaySplit) {
int d = rightMarker - leftMarker;
cutEntry = leftMarker + (d / 2);
}
repaint();
}
public int getCutStart() {
return cutEntry;
}
public int getCutEnd() {
return cutExit;
}
}