Compare commits

...

48 Commits

Author SHA1 Message Date
90ca84cfbf Released 0.3.4 2020-01-27 01:18:29 +00:00
a18ca1ce21 Fix reload tree on split and cut operations 2020-01-26 17:41:16 +00:00
b92babb5cd Major improvements to tree list, and added time recording 2020-01-20 23:58:56 +00:00
e3231ec495 Cure tree flashing on playback 2020-01-20 16:00:29 +00:00
4896ee7a65 Released 0.3.3 2020-01-17 14:16:31 +00:00
ce3eb7165a Added book/chapter/phrase notes area tabs 2020-01-17 12:31:25 +00:00
4de00b8fac Add processed flag for batch processing of new recordings 2020-01-15 11:35:49 +00:00
ebe777bdc5 Released 0.3.2 2020-01-12 23:43:08 +00:00
3b5cacb8ad Added import / open manuscript file 2020-01-12 22:46:15 +00:00
db7d297dbc Improve controls spacing 2020-01-12 15:23:16 +00:00
2f9abf7629 Fix split location problem 2020-01-12 15:01:22 +00:00
b6063d2fed Fix tree redraw on split resize 2020-01-12 13:15:52 +00:00
e1f566f0c8 Released 0.3.1 2020-01-12 12:23:14 +00:00
9fa892a6fd Added missing IDs in xml files 2020-01-12 12:22:58 +00:00
1572e163ef Added notes panel 2020-01-12 10:38:16 +00:00
2791691057 Switch book data file to full XML 2020-01-11 20:45:07 +00:00
575537ae66 Fix normalize 2020-01-11 12:31:59 +00:00
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
41 changed files with 2562 additions and 375 deletions

View File

@@ -1,7 +1,4 @@
<effect name="Alien"> <effect name="Alien">
<biquad type="notch" fc="28" q="20" gain="-50" /> <!--biquad type="lowpass" fc="10000" q="1" gain="-10" /-->
<biquad type="notch" fc="91" q="20" gain="-50" /> <lfo frequency="50" depth="1.0" waveform="triangle" mode="replace"/>
<biquad type="notch" fc="120" q="20" gain="-50" />
<biquad type="lowpass" fc="10000" q="1" gain="-10" />
<lfo frequency="100" depth="0.5" />
</effect> </effect>

View File

@@ -1,7 +1,4 @@
<effect name="Big Echo"> <effect name="Big Echo">
<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" />
<delayline> <delayline>
<delay samples="22000" gain="0.2" pan="-0.3"> <delay samples="22000" gain="0.2" pan="-0.3">
<biquad type="highpass" fc="300" q="1" gain="0" /> <biquad type="highpass" fc="300" q="1" gain="0" />

View File

@@ -3,5 +3,5 @@
<biquad type="notch" fc="91" q="20" gain="-50" /> <biquad type="notch" fc="91" q="20" gain="-50" />
<biquad type="notch" fc="120" q="20" gain="-50" /> <biquad type="notch" fc="120" q="20" gain="-50" />
<biquad type="lowpass" fc="10000" q="1" gain="-10" /> <biquad type="lowpass" fc="10000" q="1" gain="-10" />
<agc ceiling="0.666" limit="1" attack="0.1" decay="0.01" /> <agc ceiling="0.666" limit="1.5" attack="0.08" decay="0.01" />
</effect> </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,7 +1,4 @@
<effect name="Ethereal Voice"> <effect name="Ethereal Voice">
<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" />
<amplifier gain="0.1" /> <amplifier gain="0.1" />
<delayline> <delayline>
<delay samples="2000" gain="1.0" pan="-0.3"> <delay samples="2000" gain="1.0" pan="-0.3">

View File

@@ -1,7 +1,4 @@
<effect name="Large Room (quiet)"> <effect name="Large Room (quiet)">
<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" /> <biquad type="lowpass" fc="10000" q="1" gain="-10" />
<delayline> <delayline>
<delay samples="5500" gain="0.2" pan="-0.3"> <delay samples="5500" gain="0.2" pan="-0.3">

View File

@@ -1,7 +1,4 @@
<effect name="Large Room (loud)"> <effect name="Large Room (loud)">
<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" /> <biquad type="lowpass" fc="10000" q="1" gain="-10" />
<delayline> <delayline>
<delay samples="5500" gain="0.2" pan="-0.3"> <delay samples="5500" gain="0.2" pan="-0.3">

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,7 +1,4 @@
<effect name="Telephone"> <effect name="Telephone">
<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="lowshelf" fc="400" q="10" gain="-20" /> <biquad type="lowshelf" fc="400" q="10" gain="-20" />
<biquad type="highshelf" fc="8000" q="10" gain="-20" /> <biquad type="highshelf" fc="8000" q="10" gain="-20" />
<delayline> <delayline>

View File

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

View File

@@ -1,7 +1,4 @@
<effect name="Robotic"> <effect name="Robotic">
<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="lowshelf" fc="100" q="2" gain="-20" /> <biquad type="lowshelf" fc="100" q="2" gain="-20" />
<delayline> <delayline>
<delay samples="400" gain="1" pan="-0.3" /> <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>

View File

@@ -1 +1 @@
version=0.1.8 version=0.3.4

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: 1.6 KiB

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

View File

@@ -125,11 +125,11 @@ public class HiFiDefaultTheme extends AbstractTheme {
tooltipForegroundColor = white; tooltipForegroundColor = white;
tooltipBackgroundColor = new ColorUIResource(24, 24, 24); tooltipBackgroundColor = new ColorUIResource(24, 24, 24);
controlFont = new FontUIResource("Dialog", Font.BOLD, 12); controlFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
systemFont = new FontUIResource("Dialog", Font.BOLD, 12); systemFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
userFont = new FontUIResource("Dialog", Font.BOLD, 12); userFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
menuFont = new FontUIResource("Dialog", Font.BOLD, 12); menuFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
windowTitleFont = new FontUIResource("Dialog", Font.BOLD, 12); windowTitleFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
smallFont = new FontUIResource("Dialog", Font.PLAIN, 10); 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(); private static final Properties giantFontProps = new Properties();
static { static {
smallFontProps.setProperty("controlTextFont", "Dialog bold 10"); smallFontProps.setProperty("controlTextFont", "Dialog 10"); // bold
smallFontProps.setProperty("systemTextFont", "Dialog bold 10"); smallFontProps.setProperty("systemTextFont", "Dialog 10"); // bold
smallFontProps.setProperty("userTextFont", "Dialog 10"); smallFontProps.setProperty("userTextFont", "Dialog 10");
smallFontProps.setProperty("menuTextFont", "Dialog bold 10"); smallFontProps.setProperty("menuTextFont", "Dialog 10"); // bold
smallFontProps.setProperty("windowTitleFont", "Dialog bold 10"); smallFontProps.setProperty("windowTitleFont", "Dialog 10"); // bold
smallFontProps.setProperty("subTextFont", "Dialog 8"); smallFontProps.setProperty("subTextFont", "Dialog 8");
largeFontProps.setProperty("controlTextFont", "Dialog bold 14"); largeFontProps.setProperty("controlTextFont", "Dialog 14"); // bold
largeFontProps.setProperty("systemTextFont", "Dialog bold 14"); largeFontProps.setProperty("systemTextFont", "Dialog 14"); // bold
largeFontProps.setProperty("userTextFont", "Dialog bold 14"); largeFontProps.setProperty("userTextFont", "Dialog 14"); // bold
largeFontProps.setProperty("menuTextFont", "Dialog bold 14"); largeFontProps.setProperty("menuTextFont", "Dialog 14"); // bold
largeFontProps.setProperty("windowTitleFont", "Dialog bold 14"); largeFontProps.setProperty("windowTitleFont", "Dialog 14"); // bold
largeFontProps.setProperty("subTextFont", "Dialog 12"); largeFontProps.setProperty("subTextFont", "Dialog 12");
giantFontProps.setProperty("controlTextFont", "Dialog 18"); giantFontProps.setProperty("controlTextFont", "Dialog 18");

View File

@@ -31,23 +31,24 @@ public class AGC implements Effect {
double absSampleLeft = Math.abs(samples[i][Sentence.LEFT]) * gain; double absSampleLeft = Math.abs(samples[i][Sentence.LEFT]) * gain;
double absSampleRight = Math.abs(samples[i][Sentence.RIGHT]) * gain; double absSampleRight = Math.abs(samples[i][Sentence.RIGHT]) * gain;
double factor = 0.0d;
if (absSampleLeft > ceiling) { if (absSampleLeft > ceiling) {
gain -= attack; factor = -attack;
if (gain < 0) gain = 0;
} }
if (absSampleRight > ceiling) { if (absSampleRight > ceiling) {
gain -= attack; factor = -attack;
if (gain < 0) gain = 0;
} }
if ((absSampleLeft < ceiling) && (absSampleRight < ceiling)) { if ((absSampleLeft < ceiling) && (absSampleRight < ceiling)) {
gain += decay; factor = decay;
if (gain > limit) {
gain = limit;
}
} }
gain += factor;
if (gain > limit) gain = limit;
if (gain < 0) gain = 0;
samples[i][Sentence.LEFT] *= gain; samples[i][Sentence.LEFT] *= gain;
samples[i][Sentence.RIGHT] *= gain; samples[i][Sentence.RIGHT] *= gain;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,20 @@ import java.nio.file.*;
import javax.swing.tree.*; import javax.swing.tree.*;
import javax.sound.sampled.*; import javax.sound.sampled.*;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
public class Book extends DefaultMutableTreeNode { public class Book extends DefaultMutableTreeNode {
String name; String name;
@@ -17,6 +31,9 @@ public class Book extends DefaultMutableTreeNode {
String genre; String genre;
String comment; String comment;
String ACX; String ACX;
String manuscript;
String defaultEffect = "none";
int sampleRate; int sampleRate;
int channels; int channels;
@@ -31,7 +48,64 @@ public class Book extends DefaultMutableTreeNode {
prefs = p; prefs = p;
name = bookname; name = bookname;
AudiobookRecorder.window.setTitle("AudioBook Recorder :: " + name); AudiobookRecorder.window.setTitle("AudioBook Recorder :: " + name); // This should be in the load routine!!!!
}
public Book(Element root) {
super(getTextNode(root, "title"));
name = getTextNode(root, "title");
AudiobookRecorder.window.setTitle("AudioBook Recorder :: " + name); // This should be in the load routine!!!!
}
public void loadBookXML(Element root, DefaultTreeModel model) {
name = getTextNode(root, "title");
author = getTextNode(root, "author");
genre = getTextNode(root, "genre");
comment = getTextNode(root, "comment");
ACX = getTextNode(root, "acx");
manuscript = getTextNode(root, "manuscript");
AudiobookRecorder.window.setBookNotes(getTextNode(root, "notes"));
Element settings = getNode(root, "settings");
Element audioSettings = getNode(settings, "audio");
Element effectSettings = getNode(settings, "effects");
sampleRate = Utils.s2i(getTextNode(audioSettings, "samplerate"));
channels = Utils.s2i(getTextNode(audioSettings, "channels"));
resolution = Utils.s2i(getTextNode(audioSettings, "resolution"));
defaultEffect = getTextNode(settings, "default");
AudiobookRecorder.window.setTitle("AudioBook Recorder :: " + name); // This should be in the load routine!!!!
Element chapters = getNode(root, "chapters");
NodeList chapterList = chapters.getElementsByTagName("chapter");
for (int i = 0; i < chapterList.getLength(); i++) {
Element chapterElement = (Element)chapterList.item(i);
Chapter newChapter = new Chapter(chapterElement, model);
model.insertNodeInto(newChapter, this, getChildCount());
}
}
public static Element getNode(Element r, String n) {
NodeList nl = r.getElementsByTagName(n);
if (nl == null) return null;
if (nl.getLength() == 0) return null;
return (Element)nl.item(0);
}
public static String getTextNode(Element r, String n) {
return getTextNode(r, n, "");
}
public static String getTextNode(Element r, String n, String d) {
Element node = getNode(r, n);
if (node == null) return d;
return node.getTextContent();
} }
public void setAuthor(String a) { author = a; } public void setAuthor(String a) { author = a; }
@@ -115,8 +189,12 @@ public class Book extends DefaultMutableTreeNode {
} }
} }
public File getBookPath() {
return new File(Options.get("path.storage"), name);
}
public void renameBook(String newName) { public void renameBook(String newName) {
File oldDir = new File(Options.get("path.storage"), name); File oldDir = getBookPath();
File newDir = new File(Options.get("path.storage"), newName); File newDir = new File(Options.get("path.storage"), newName);
if (newDir.exists()) { if (newDir.exists()) {
@@ -206,4 +284,118 @@ public class Book extends DefaultMutableTreeNode {
return out; 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();
}
}
}
public Document buildDocument() throws ParserConfigurationException {
DocumentBuilderFactory dbFactory =
DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.newDocument();
Element root = doc.createElement("book");
doc.appendChild(root);
root.appendChild(makeTextNode(doc, "title", name));
root.appendChild(makeTextNode(doc, "author", author));
root.appendChild(makeTextNode(doc, "comment", comment));
root.appendChild(makeTextNode(doc, "genre", genre));
root.appendChild(makeTextNode(doc, "acx", ACX));
root.appendChild(makeTextNode(doc, "manuscript", manuscript));
root.appendChild(makeTextNode(doc, "notes", AudiobookRecorder.window.getBookNotes()));
Element settingsNode = doc.createElement("settings");
root.appendChild(settingsNode);
Element audioSettingsNode = doc.createElement("audio");
settingsNode.appendChild(audioSettingsNode);
audioSettingsNode.appendChild(makeTextNode(doc, "channels", channels));
audioSettingsNode.appendChild(makeTextNode(doc, "resolution", resolution));
audioSettingsNode.appendChild(makeTextNode(doc, "samplerate", sampleRate));
Element effectsNode = doc.createElement("effects");
settingsNode.appendChild(effectsNode);
effectsNode.appendChild(makeTextNode(doc, "default", defaultEffect));
Element chaptersNode = doc.createElement("chapters");
root.appendChild(chaptersNode);
for (Enumeration o = children(); o.hasMoreElements();) {
Object ob = (Object)o.nextElement();
if (ob instanceof Chapter) {
Chapter c = (Chapter)ob;
chaptersNode.appendChild(c.getChapterXML(doc));
}
}
return doc;
}
public static Element makeTextNode(Document doc, String name, String text) {
Element node = doc.createElement(name);
Text tnode = doc.createTextNode(text == null ? "" : text);
node.appendChild(tnode);
return node;
}
public static Element makeTextNode(Document doc, String name, Integer text) {
Element node = doc.createElement(name);
Text tnode = doc.createTextNode(Integer.toString(text));
node.appendChild(tnode);
return node;
}
public static Element makeTextNode(Document doc, String name, Double text) {
Element node = doc.createElement(name);
Text tnode = doc.createTextNode(String.format("%.8f", text));
node.appendChild(tnode);
return node;
}
public static Element makeTextNode(Document doc, String name, Boolean text) {
Element node = doc.createElement(name);
Text tnode = doc.createTextNode(text ? "true" : "false");
node.appendChild(tnode);
return node;
}
public String getDefaultEffect() {
return defaultEffect;
}
public void setDefaultEffect(String eff) {
defaultEffect = eff;
}
public void setManuscript(File f) {
manuscript = f.getName();
File dst = new File(getBookPath(), manuscript);
try {
Files.copy(f.toPath(), dst.toPath());
} catch (Exception ex) {
ex.printStackTrace();
}
}
public File getManuscript() {
if (manuscript == null) return null;
if (manuscript.equals("")) return null;
File f = new File(getBookPath(), manuscript);
if (f.exists()) {
return f;
}
return null;
}
} }

View File

@@ -9,6 +9,20 @@ import javax.swing.border.*;
import java.util.*; import java.util.*;
import java.io.*; import java.io.*;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
public class BookPanel extends JPanel { public class BookPanel extends JPanel {
String name; String name;
String author; String author;
@@ -25,6 +39,7 @@ public class BookPanel extends JPanel {
JPanel panel; JPanel panel;
File root; File root;
File configFile;
boolean highlight = false; boolean highlight = false;
@@ -36,13 +51,78 @@ public class BookPanel extends JPanel {
try { try {
root = r; root = r;
Properties props = new Properties(); Properties props = new Properties();
props.loadFromXML(new FileInputStream(new File(root, "audiobook.abk")));
loadBookData(props, null); configFile = new File(root, "audiobook.abx");
if (configFile.exists()) {
loadXMLData(configFile);
} else {
configFile = new File(root, "audiobook.abk");
props.loadFromXML(new FileInputStream(configFile));
loadBookData(props, null);
}
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
public BookPanel(String n, String a, String g, String c, ImageIcon i) {
name = n;
author = a;
genre = g;
comment = c;
cover = i;
if (i != null) {
cover = i;
resizedCover = Utils.getScaledImage(cover.getImage(), 75, 75);
iconLabel = new JLabel(new ImageIcon(resizedCover));
} else {
cover = null;
resizedCover = null;
iconLabel = new JLabel("");
}
populate();
}
public void loadXMLData(File inputFile) {
try {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(inputFile);
doc.getDocumentElement().normalize();
Element rootnode = doc.getDocumentElement();
name = Book.getTextNode(rootnode, "title");
author = Book.getTextNode(rootnode, "author");
genre = Book.getTextNode(rootnode, "genre");
comment = Book.getTextNode(rootnode, "comment");
File icon = new File(root, "coverart.png");
if (!icon.exists()) {
icon = new File(root, "coverart.jpg");
}
if (!icon.exists()) {
icon = new File(root, "coverart.gif");
}
if (icon.exists()) {
cover = new ImageIcon(icon.getAbsolutePath());
resizedCover = Utils.getScaledImage(cover.getImage(), 75, 75);
iconLabel = new JLabel(new ImageIcon(resizedCover));
} else {
cover = null;
resizedCover = null;
iconLabel = new JLabel("");
}
populate();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void loadBookData(Properties props, ImageIcon i) { public void loadBookData(Properties props, ImageIcon i) {
try { try {
name = props.getProperty("book.name"); name = props.getProperty("book.name");
@@ -71,41 +151,44 @@ public class BookPanel extends JPanel {
resizedCover = Utils.getScaledImage(cover.getImage(), 75, 75); resizedCover = Utils.getScaledImage(cover.getImage(), 75, 75);
iconLabel = new JLabel(new ImageIcon(resizedCover)); iconLabel = new JLabel(new ImageIcon(resizedCover));
} }
populate();
iconLabel.setSize(new Dimension(75, 75));
iconLabel.setPreferredSize(new Dimension(75, 75));
titleLabel = new JLabel(name);
authorLabel = new JLabel(author);
otherLabel = new JLabel(genre + " :: " + comment);
authorLabel.setForeground(new Color(0x80, 0x80, 0x80));
otherLabel.setForeground(new Color(0x80, 0x80, 0x80));
setLayout(new BorderLayout());
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(new EmptyBorder(10, 10, 10, 10));
panel.add(titleLabel);
panel.add(authorLabel);
panel.add(otherLabel);
add(iconLabel, BorderLayout.WEST);
add(panel, BorderLayout.CENTER);
panel.setBackground(new Color(0x20, 0x20, 0x20));
panel.setOpaque(true);
setBackground(new Color(0x20, 0x20, 0x20));
setOpaque(true);
} catch (Exception e) { } catch (Exception e) {
} }
} }
void populate() {
iconLabel.setSize(new Dimension(75, 75));
iconLabel.setPreferredSize(new Dimension(75, 75));
titleLabel = new JLabel(name);
authorLabel = new JLabel(author);
otherLabel = new JLabel(genre + " :: " + comment);
authorLabel.setForeground(new Color(0x80, 0x80, 0x80));
otherLabel.setForeground(new Color(0x80, 0x80, 0x80));
setLayout(new BorderLayout());
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(new EmptyBorder(10, 10, 10, 10));
panel.add(titleLabel);
panel.add(authorLabel);
panel.add(otherLabel);
add(iconLabel, BorderLayout.WEST);
add(panel, BorderLayout.CENTER);
panel.setBackground(new Color(0x20, 0x20, 0x20));
panel.setOpaque(true);
setBackground(new Color(0x20, 0x20, 0x20));
setOpaque(true);
}
public File getConfigFile() { public File getConfigFile() {
return new File(root, "audiobook.abk"); return configFile;
} }
public void highlight() { public void highlight() {

View File

@@ -0,0 +1,20 @@
package uk.co.majenko.audiobookrecorder;
import javax.swing.tree.DefaultMutableTreeNode;
public abstract class BookTreeNode extends DefaultMutableTreeNode {
public BookTreeNode(String t) {
super(t);
}
public BookTreeNode() {
super("");
}
public abstract void setNotes(String t);
public abstract String getNotes();
public abstract void onSelect();
}

View File

@@ -3,27 +3,38 @@ package uk.co.majenko.audiobookrecorder;
import javax.swing.*; import javax.swing.*;
import javax.swing.tree.*; import javax.swing.tree.*;
import java.awt.*; import java.awt.*;
import javax.swing.border.*;
public class BookTreeRenderer extends DefaultTreeCellRenderer { public class BookTreeRenderer extends DefaultTreeCellRenderer {
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { 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); JLabel ret = (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
ret.setIconTextGap(5);
ret.setBorder(new EmptyBorder(0, 0, 0, 0));
if (value instanceof Sentence) { if (value instanceof Sentence) {
Sentence s = (Sentence)value; Sentence s = (Sentence)value;
JPanel p = new JPanel();
p.setLayout(new GridBagLayout());
GridBagConstraints ctx = new GridBagConstraints();
OverlayIcon icn = new OverlayIcon(Icons.sentence); OverlayIcon icn = new OverlayIcon(Icons.sentence);
if (s.getOverrideText() != null) { if (s.getOverrideText() != null) {
ret.setText(s.getOverrideText()); ret.setText(s.getOverrideText());
} }
if (!s.isProcessed()) {
ret.setForeground(new Color(0x88, 0x88, 0x88));
}
if (s.getAttentionFlag()) { if (s.getAttentionFlag()) {
ret.setForeground(new Color(0xFF, 0xFF, 0x00)); ret.setForeground(new Color(0xFF, 0xFF, 0x00));
icn.add(Overlays.attention, OverlayIcon.TOP_LEFT); icn.add(Overlays.attention, OverlayIcon.TOP_LEFT);
} }
if (s.isLocked()) { if (s.isLocked()) {
ret.setForeground(new Color(0x00, 0x80, 0xFF)); ret.setForeground(new Color(0x30, 0xb0, 0xFF));
icn.add(Overlays.locked, OverlayIcon.BOTTOM_LEFT); icn.add(Overlays.locked, OverlayIcon.BOTTOM_LEFT);
} }
@@ -31,6 +42,10 @@ public class BookTreeRenderer extends DefaultTreeCellRenderer {
icn.add(Overlays.important, OverlayIcon.TOP_RIGHT); 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() != null) {
if (!s.getEffectChain().equals("none")) { if (!s.getEffectChain().equals("none")) {
icn.add(Overlays.filter, OverlayIcon.BOTTOM_RIGHT); icn.add(Overlays.filter, OverlayIcon.BOTTOM_RIGHT);
@@ -39,8 +54,85 @@ public class BookTreeRenderer extends DefaultTreeCellRenderer {
ret.setIcon(icn); 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")) {
p.setBorder(new EmptyBorder(0, 0, 0, 0));
} else if (gaptype.equals("continuation")) {
p.setBorder(new EmptyBorder(0, 0, 0, 0));
} else if (gaptype.equals("paragraph")) {
p.setBorder(new EmptyBorder(0, 0, 7, 0));
} else if (gaptype.equals("section")) {
p.setBorder(new EmptyBorder(0, 0, 15, 0));
}
JLabel time = new JLabel(Utils.secToTime(s.getLength(), "mm:ss.SSS") + " ");
ctx.gridx = 0;
ctx.gridy = 0;
ctx.fill = GridBagConstraints.HORIZONTAL;
ctx.anchor = GridBagConstraints.LINE_START;
String effectChain = s.getEffectChain();
if ((effectChain == null) || (effectChain.equals("none"))) {
ctx.weightx = 1.0d;
ctx.gridwidth = 2;
p.add(ret, ctx);
} else {
ctx.weightx = 0.1d;
ctx.gridwidth = 1;
p.add(ret, ctx);
Effect e = AudiobookRecorder.window.effects.get(effectChain);
JLabel eff = new JLabel(e.toString() + " ");
ctx.weightx = 0.0d;
ctx.gridwidth = 1;
ctx.gridx = 1;
p.add(eff);
}
ctx.weightx = 0.0d;
ctx.gridx = 2;
ctx.anchor = GridBagConstraints.LINE_END;
p.add(time, ctx);
p.setOpaque(false);
return p;
} else if (value instanceof Chapter) { } else if (value instanceof Chapter) {
Chapter c = (Chapter)value;
ret.setIcon(Icons.chapter); ret.setIcon(Icons.chapter);
JPanel p = new JPanel();
p.setLayout(new GridBagLayout());
GridBagConstraints ctx = new GridBagConstraints();
JLabel time = new JLabel(Utils.secToTime(c.getLength(), "mm:ss") + " ");
ctx.gridx = 0;
ctx.gridy = 0;
ctx.fill = GridBagConstraints.HORIZONTAL;
ctx.anchor = GridBagConstraints.LINE_START;
ctx.weightx = 1.0d;
p.add(ret, ctx);
ctx.weightx = 0.0d;
ctx.gridx = 1;
ctx.anchor = GridBagConstraints.LINE_END;
p.add(time, ctx);
p.setOpaque(false);
return p;
} else if (value instanceof Book) { } else if (value instanceof Book) {
ret.setIcon(((Book)value).getIcon()); ret.setIcon(((Book)value).getIcon());
} }

View File

@@ -13,6 +13,9 @@ public class CacheManager {
if (ob.lockedInCache()) { if (ob.lockedInCache()) {
cache.add(ob); cache.add(ob);
} else { } else {
if (ob instanceof Sentence) {
Sentence s = (Sentence)ob;
}
ob.clearCache(); ob.clearCache();
} }
} }
@@ -23,4 +26,19 @@ public class CacheManager {
public static void setCacheSize(int c) { public static void setCacheSize(int c) {
cacheSize = 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

@@ -12,8 +12,21 @@ import it.sauronsoftware.jave.*;
import com.mpatric.mp3agic.*; import com.mpatric.mp3agic.*;
import javax.sound.sampled.*; import javax.sound.sampled.*;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
public class Chapter extends DefaultMutableTreeNode { public class Chapter extends BookTreeNode {
String name; String name;
String id; String id;
@@ -21,6 +34,8 @@ public class Chapter extends DefaultMutableTreeNode {
int preGap; int preGap;
int postGap; int postGap;
String notes;
public Chapter(String i, String chaptername) { public Chapter(String i, String chaptername) {
super(chaptername); super(chaptername);
@@ -28,7 +43,25 @@ public class Chapter extends DefaultMutableTreeNode {
name = chaptername; name = chaptername;
preGap = Options.getInteger("catenation.pre-chapter"); preGap = Options.getInteger("catenation.pre-chapter");
postGap = Options.getInteger("catenation.post-chapter"); postGap = Options.getInteger("catenation.post-chapter");
}
public Chapter(Element root, DefaultTreeModel model) {
name = Book.getTextNode(root, "name");
id = root.getAttribute("id");
preGap = Utils.s2i(Book.getTextNode(root, "pre-gap"));
postGap = Utils.s2i(Book.getTextNode(root, "post-gap"));
notes = Book.getTextNode(root, "notes");
Element sentencesNode = Book.getNode(root, "sentences");
NodeList sentences = sentencesNode.getElementsByTagName("sentence");
for (int i = 0; i < sentences.getLength(); i++) {
Element sentenceElement = (Element)sentences.item(i);
Sentence newSentence = new Sentence(sentenceElement);
model.insertNodeInto(newSentence, this, getChildCount());
}
} }
public String getId() { public String getId() {
@@ -116,7 +149,7 @@ public class Chapter extends DefaultMutableTreeNode {
audioAttributes.setCodec("libmp3lame"); audioAttributes.setCodec("libmp3lame");
audioAttributes.setBitRate(Options.getInteger("audio.export.bitrate")); audioAttributes.setBitRate(Options.getInteger("audio.export.bitrate"));
audioAttributes.setSamplingRate(Options.getInteger("audio.export.samplerate")); 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.setFormat("mp3");
attributes.setAudioAttributes(audioAttributes); attributes.setAudioAttributes(audioAttributes);
@@ -232,5 +265,67 @@ public class Chapter extends DefaultMutableTreeNode {
return out; 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();
}
}
}
public Element getChapterXML(Document doc) {
Element chapterNode = doc.createElement("chapter");
chapterNode.setAttribute("id", id);
chapterNode.appendChild(Book.makeTextNode(doc, "name", name));
chapterNode.appendChild(Book.makeTextNode(doc, "pre-gap", preGap));
chapterNode.appendChild(Book.makeTextNode(doc, "post-gap", postGap));
chapterNode.appendChild(Book.makeTextNode(doc, "notes", notes));
Element sentencesNode = doc.createElement("sentences");
chapterNode.appendChild(sentencesNode);
for (Enumeration o = children(); o.hasMoreElements();) {
Object ob = (Object)o.nextElement();
if (ob instanceof Sentence) {
Sentence s = (Sentence)ob;
sentencesNode.appendChild(s.getSentenceXML(doc));
}
}
return chapterNode;
}
public String getNotes() {
return notes;
}
public void setNotes(String t) {
notes = t;
}
public void onSelect() {
AudiobookRecorder.window.setChapterNotes(notes);
}
public double getLength() {
double len = 0;
for (Enumeration o = children(); o.hasMoreElements();) {
Object ob = (Object)o.nextElement();
if (ob instanceof Sentence) {
Sentence s = (Sentence)ob;
len += s.getLength();
}
}
return len;
}
} }

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

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

View File

@@ -33,4 +33,10 @@ public class Icons {
static public final ImageIcon dollar = new ImageIcon(Icons.class.getResource("icons/dollar.png")); 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 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 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"));
static public final ImageIcon manuscript = new ImageIcon(Icons.class.getResource("icons/manuscript.png"));
} }

View File

@@ -9,37 +9,73 @@ public class LFO implements Effect {
double depth; double depth;
double sampleRate; double sampleRate;
double sampleStep; 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) { public LFO(double f, double d) {
frequency = f; this(f, d, 0, SINE, Math.PI, REPLACE);
depth = d;
phase = 0;
} }
public LFO(double f, double d, double p) { 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; frequency = f;
depth = d; depth = d;
phase = p; phase = p;
waveform = w;
duty = dty;
mode = m;
} }
public void process(double[][] samples) { public void process(double[][] samples) {
for (double[] sample : samples) { for (double[] sample : samples) {
double v = Math.sin(phase); 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; phase += sampleStep;
if (phase > (Math.PI * 2d)) { if (phase > (Math.PI * 2d)) {
phase -= (Math.PI * 2d); phase -= (Math.PI * 2d);
} }
// // Make it between 0 and 1.
// v = 1d + v;
// v /= 2d;
// Multiply it by the gain factor // Multiply it by the gain factor
v *= depth; v *= depth;
// Apply it to the sample // Apply it to the sample
sample[Sentence.LEFT] += (sample[Sentence.LEFT] * v); switch (mode) {
sample[Sentence.RIGHT] += (sample[Sentence.RIGHT] * v); 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;
}
} }
} }

View File

@@ -14,9 +14,15 @@ public class MainToolBar extends JToolBar {
JButtonSpacePlay recordRoomNoise; JButtonSpacePlay recordRoomNoise;
JButtonSpacePlay playSentence; JButtonSpacePlay playSentence;
JButtonSpacePlay playonSentence; JButtonSpacePlay playonSentence;
JButtonSpacePlay playtoSentence;
JButtonSpacePlay stopPlaying; JButtonSpacePlay stopPlaying;
JButtonSpacePlay eq; JButtonSpacePlay eq;
JToggleButton mic; JButtonSpacePlay openManuscript;
JToggleButtonSpacePlay mic;
JComboBox<String> playbackSpeed;
JToggleButtonSpacePlay disableEffects;
AudiobookRecorder root; AudiobookRecorder root;
@@ -78,6 +84,14 @@ public class MainToolBar extends JToolBar {
}); });
add(playonSentence); 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() { stopPlaying = new JButtonSpacePlay(Icons.stop, "Stop playing", new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
root.stopPlaying(); root.stopPlaying();
@@ -89,6 +103,7 @@ public class MainToolBar extends JToolBar {
eq = new JButtonSpacePlay(Icons.eq, "Reload Effects", new ActionListener() { eq = new JButtonSpacePlay(Icons.eq, "Reload Effects", new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
root.loadEffects(); root.loadEffects();
CacheManager.purgeCache();
} }
}); });
@@ -117,11 +132,65 @@ public class MainToolBar extends JToolBar {
} }
} }
}); });
add(mic); 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);
addSeparator();
openManuscript = new JButtonSpacePlay(Icons.manuscript, "Open Manuscript", new ActionListener() {
public void actionPerformed(ActionEvent e) {
root.openManuscript();
}
});
add(openManuscript);
setFloatable(false); 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

@@ -86,10 +86,16 @@ public class OpenBookPanel extends JPanel {
for (File b : dir.listFiles()) { for (File b : dir.listFiles()) {
if (b == null) continue; if (b == null) continue;
if (!b.isDirectory()) continue; if (!b.isDirectory()) continue;
File xml = new File(b, "audiobook.abk"); File xml = new File(b, "audiobook.abx");
if (xml.exists()) { if (xml.exists()) {
BookPanel book = new BookPanel(b); BookPanel book = new BookPanel(b);
model.addBook(book); model.addBook(book);
} else {
xml = new File(b, "audiobook.abk");
if (xml.exists()) {
BookPanel book = new BookPanel(b);
model.addBook(book);
}
} }
} }
} }

View File

@@ -24,6 +24,8 @@ public class Options extends JDialog {
JComboBox<KVPair> rateList; JComboBox<KVPair> rateList;
JComboBox<KVPair> bitDepth; JComboBox<KVPair> bitDepth;
JComboBox<KVPair> trimMethod; JComboBox<KVPair> trimMethod;
JComboBox<KVPair> fftBlockSize;
JComboBox<KVPair> playbackBlockSize;
JTextField storageFolder; JTextField storageFolder;
JTextField archiveFolder; JTextField archiveFolder;
JSpinner preChapterGap; JSpinner preChapterGap;
@@ -32,8 +34,10 @@ public class Options extends JDialog {
JSpinner shortSentenceGap; JSpinner shortSentenceGap;
JSpinner postParagraphGap; JSpinner postParagraphGap;
JSpinner postSectionGap; JSpinner postSectionGap;
JSpinner maxGainVariance;
JTextField ffmpegLocation; JTextField ffmpegLocation;
JComboBox<KVPair> bitRate; JComboBox<KVPair> bitRate;
JComboBox<KVPair> channels;
JComboBox<KVPair> exportRate; JComboBox<KVPair> exportRate;
JCheckBox enableParsing; JCheckBox enableParsing;
JSpinner cacheSize; JSpinner cacheSize;
@@ -297,10 +301,13 @@ public class Options extends JDialog {
bitDepth = addDropdown(optionsPanel, "Sample resolution:", getResolutionList(), get("audio.recording.resolution")); bitDepth = addDropdown(optionsPanel, "Sample resolution:", getResolutionList(), get("audio.recording.resolution"));
trimMethod = addDropdown(optionsPanel, "Auto-trim method:", getTrimMethods(), get("audio.recording.trim")); trimMethod = addDropdown(optionsPanel, "Auto-trim method:", getTrimMethods(), get("audio.recording.trim"));
fftThreshold = addSpinner(optionsPanel, "FFT threshold:", 0, 100, 1, getInteger("audio.recording.trim.fft"), ""); 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"));
maxGainVariance = addSpinner(optionsPanel, "Maximum gain variance:", 0, 100, 1, getInteger("audio.recording.variance"), "");
addSeparator(optionsPanel); addSeparator(optionsPanel);
playbackList = addDropdown(optionsPanel, "Playback device:", getPlaybackMixerList(), get("audio.playback.device")); playbackList = addDropdown(optionsPanel, "Playback device:", getPlaybackMixerList(), get("audio.playback.device"));
playbackBlockSize = addDropdown(optionsPanel, "Playback Block size:", getPlaybackBlockSizes(), get("audio.playback.blocksize"));
addSeparator(optionsPanel); addSeparator(optionsPanel);
storageFolder = addFilePath(optionsPanel, "Storage folder:", get("path.storage"), true); storageFolder = addFilePath(optionsPanel, "Storage folder:", get("path.storage"), true);
archiveFolder = addFilePath(optionsPanel, "Archive folder:", get("path.archive"), true); archiveFolder = addFilePath(optionsPanel, "Archive folder:", get("path.archive"), true);
@@ -318,6 +325,7 @@ public class Options extends JDialog {
ffmpegLocation = addFilePath(optionsPanel, "FFMPEG location:", get("path.ffmpeg"), false); ffmpegLocation = addFilePath(optionsPanel, "FFMPEG location:", get("path.ffmpeg"), false);
bitRate = addDropdown(optionsPanel, "Export bitrate:", getBitrates(), get("audio.export.bitrate")); 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")); exportRate = addDropdown(optionsPanel, "Export sample rate:", getSampleRateList(), get("audio.export.samplerate"));
@@ -331,7 +339,7 @@ public class Options extends JDialog {
addSeparator(optionsPanel); 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); addSeparator(optionsPanel);
tabs.add("Options", new JScrollPane(optionsPanel)); tabs.add("Options", new JScrollPane(optionsPanel));
@@ -565,6 +573,8 @@ public class Options extends JDialog {
} else { } else {
defaultPrefs.put("audio.playback.device", ""); 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.pre-chapter", "1000");
defaultPrefs.put("catenation.post-chapter", "1500"); defaultPrefs.put("catenation.post-chapter", "1500");
@@ -574,12 +584,14 @@ public class Options extends JDialog {
defaultPrefs.put("catenation.post-section", "3000"); defaultPrefs.put("catenation.post-section", "3000");
defaultPrefs.put("audio.recording.trim.fft", "10"); defaultPrefs.put("audio.recording.trim.fft", "10");
defaultPrefs.put("audio.recording.variance", "10");
defaultPrefs.put("path.storage", (new File(System.getProperty("user.home"), "Recordings")).toString()); defaultPrefs.put("path.storage", (new File(System.getProperty("user.home"), "Recordings")).toString());
defaultPrefs.put("path.archive", (new File(new File(System.getProperty("user.home"), "Recordings"),"archive")).toString()); defaultPrefs.put("path.archive", (new File(new File(System.getProperty("user.home"), "Recordings"),"archive")).toString());
defaultPrefs.put("path.ffmpeg", ""); defaultPrefs.put("path.ffmpeg", "");
defaultPrefs.put("audio.export.bitrate", "256000"); defaultPrefs.put("audio.export.bitrate", "256000");
defaultPrefs.put("audio.export.channels", "2");
defaultPrefs.put("audio.export.samplerate", "44100"); defaultPrefs.put("audio.export.samplerate", "44100");
defaultPrefs.put("process.sphinx", "false"); defaultPrefs.put("process.sphinx", "false");
@@ -703,11 +715,15 @@ public class Options extends JDialog {
set("catenation.post-paragraph", postParagraphGap.getValue()); set("catenation.post-paragraph", postParagraphGap.getValue());
set("catenation.post-section", postSectionGap.getValue()); set("catenation.post-section", postSectionGap.getValue());
set("audio.export.bitrate", ((KVPair)bitRate.getSelectedItem()).key); 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("audio.export.samplerate", ((KVPair)exportRate.getSelectedItem()).key);
set("process.sphinx", enableParsing.isSelected()); set("process.sphinx", enableParsing.isSelected());
set("editor.external", externalEditor.getText()); set("editor.external", externalEditor.getText());
set("cache.size", cacheSize.getValue()); set("cache.size", cacheSize.getValue());
set("audio.recording.trim.fft", fftThreshold.getValue()); set("audio.recording.trim.fft", fftThreshold.getValue());
set("audio.recording.variance", maxGainVariance.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.offset", etherealOffset.getValue());
set("effects.ethereal.iterations", etherealIterations.getValue()); set("effects.ethereal.iterations", etherealIterations.getValue());
@@ -782,6 +798,31 @@ public class Options extends JDialog {
return pairs; 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() { public static void createEffectChains() {
effectChains = new ArrayList<EffectGroup>(); effectChains = new ArrayList<EffectGroup>();

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,8 @@ import java.util.*;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.text.SimpleDateFormat;
public class Utils { public class Utils {
public static Image getScaledImage(Image srcImg, int w, int h){ public static Image getScaledImage(Image srcImg, int w, int h){
BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
@@ -63,4 +65,25 @@ 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()
));
}
public static String secToTime(double sec, String fmt) {
Date d = new Date((long)(sec * 1000d));
SimpleDateFormat df = new SimpleDateFormat(fmt);
df.setTimeZone(TimeZone.getTimeZone("GMT"));
String time = df.format(d);
return time;
}
} }

View File

@@ -21,6 +21,11 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
int leftAltMarker = 0; int leftAltMarker = 0;
int rightAltMarker = 0; int rightAltMarker = 0;
int cutEntry = 0;
int cutExit = 0;
boolean displayCut = false;
boolean displaySplit = false;
int dragging = 0; int dragging = 0;
int step = 1; int step = 1;
@@ -92,6 +97,7 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
double lmax = 0; double lmax = 0;
for (int o = 0; o < step; o++) { for (int o = 0; o < 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; double sample = (samples[offset + (n * step) + o][Sentence.LEFT] + samples[offset + (n * step) + o][Sentence.RIGHT]) / 2d;
if (sample >= 0) { if (sample >= 0) {
have += sample; have += sample;
@@ -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.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(0, 0, (leftAltMarker - offset)/step, h);
g.fillRect((rightAltMarker - offset)/step, 0, (num - rightAltMarker) / 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((leftMarker - offset)/step, 0, (leftMarker - offset)/step, h);
g.drawLine((rightMarker - offset)/step, 0, (rightMarker - 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)); g.setColor(new Color(0, 255, 255));
for (int i = 0; i < h; i += 2) { for (int i = 0; i < h; i += 2) {
g.drawLine((playMarker - offset) / step, i, (playMarker - offset) / step, i); g.drawLine((playMarker - offset) / step, i, (playMarker - offset) / step, i);
@@ -192,6 +211,8 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
public void setData(double[][] s) { public void setData(double[][] s) {
samples = s; samples = s;
playMarker = 0; playMarker = 0;
displayCut = false;
displaySplit = false;
repaint(); repaint();
} }
@@ -221,6 +242,19 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
dragging = 2; dragging = 2;
return; 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) { public void mouseReleased(MouseEvent e) {
@@ -251,6 +285,19 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
return; 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)); setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
@@ -270,6 +317,18 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
if (rightMarker < leftMarker) { if (rightMarker < leftMarker) {
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(); repaint();
@@ -299,4 +358,33 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
playMarker = leftAltMarker + m; playMarker = leftAltMarker + m;
repaint(); 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;
}
} }