Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ebe777bdc5 | |||
| 3b5cacb8ad | |||
| db7d297dbc | |||
| 2f9abf7629 | |||
| b6063d2fed | |||
| e1f566f0c8 | |||
| 9fa892a6fd | |||
| 1572e163ef | |||
| 2791691057 | |||
| 575537ae66 | |||
| fde4a597c7 | |||
| e69ae52f50 | |||
| 8bdb06749f | |||
| 0cfd066f4f | |||
| 217ccb915a | |||
| 1dd3e9d86f | |||
| d3d81d71fe | |||
| 80c110afa9 | |||
| 1c08b9a51d | |||
| 4ad30106b6 | |||
| 9100d0e35a | |||
| b8dea19c30 | |||
| 671f2b9270 | |||
| d619fb2f4d | |||
| 3eb6704f2f | |||
| cd24beb8a6 | |||
| fa287305eb | |||
| 3fb656b693 | |||
| f514993525 | |||
| 81787260c9 | |||
| f9ad396228 | |||
| 8976f2e359 | |||
| 187c3edaf6 | |||
| 02e85fb354 | |||
| 289834021f | |||
| 04fea4acb2 | |||
| 54739b0a75 | |||
| ea5520a729 | |||
| 0a19d8d308 | |||
| 4dbe3e23b7 | |||
| c43cfc3b69 | |||
| babd3d2052 | |||
| 9464839b4d | |||
| bcf7875414 | |||
| 732894a0fb |
@@ -1,7 +1,4 @@
|
||||
<effect name="Alien">
|
||||
<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" />
|
||||
<lfo frequency="100" depth="0.5" />
|
||||
<!--biquad type="lowpass" fc="10000" q="1" gain="-10" /-->
|
||||
<lfo frequency="50" depth="1.0" waveform="triangle" mode="replace"/>
|
||||
</effect>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
<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>
|
||||
<delay samples="22000" gain="0.2" pan="-0.3">
|
||||
<biquad type="highpass" fc="300" q="1" gain="0" />
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
<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" attack="0.1" decay="0.01" />
|
||||
<agc ceiling="0.666" limit="1.5" attack="0.08" decay="0.01" />
|
||||
</effect>
|
||||
|
||||
15
ExampleFilters/cuthumagcstereo.eff
Normal file
15
ExampleFilters/cuthumagcstereo.eff
Normal 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>
|
||||
@@ -1,7 +1,4 @@
|
||||
<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" />
|
||||
<delayline>
|
||||
<delay samples="2000" gain="1.0" pan="-0.3">
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
<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" />
|
||||
<delayline>
|
||||
<delay samples="5500" gain="0.2" pan="-0.3">
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
<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" />
|
||||
<delayline>
|
||||
<delay samples="5500" gain="0.2" pan="-0.3">
|
||||
|
||||
3
ExampleFilters/panleft.eff
Normal file
3
ExampleFilters/panleft.eff
Normal file
@@ -0,0 +1,3 @@
|
||||
<effect name="Pan Left">
|
||||
<pan pan="-0.4" />
|
||||
</effect>
|
||||
3
ExampleFilters/panright.eff
Normal file
3
ExampleFilters/panright.eff
Normal file
@@ -0,0 +1,3 @@
|
||||
<effect name="Pan Right">
|
||||
<pan pan="0.4" />
|
||||
</effect>
|
||||
@@ -1,7 +1,4 @@
|
||||
<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="highshelf" fc="8000" q="10" gain="-20" />
|
||||
<delayline>
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<effect name="Radio">
|
||||
<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" />
|
||||
<biquad type="peak" fc="1000" q="10" gain="45" />
|
||||
<lfo frequency="5000" depth="0.3" />
|
||||
<clipping clip="0.3" />
|
||||
<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>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
<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" />
|
||||
<delayline>
|
||||
<delay samples="400" gain="1" pan="-0.3" />
|
||||
|
||||
10
ExampleFilters/stereochorus.eff
Normal file
10
ExampleFilters/stereochorus.eff
Normal 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>
|
||||
@@ -1 +1 @@
|
||||
version=0.1.7
|
||||
version=0.3.2
|
||||
|
||||
BIN
resources/uk/co/majenko/audiobookrecorder/icons/cut.png
Normal file
BIN
resources/uk/co/majenko/audiobookrecorder/icons/cut.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 674 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/uk/co/majenko/audiobookrecorder/icons/do-cut.png
Normal file
BIN
resources/uk/co/majenko/audiobookrecorder/icons/do-cut.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 576 B |
BIN
resources/uk/co/majenko/audiobookrecorder/icons/manuscript.png
Normal file
BIN
resources/uk/co/majenko/audiobookrecorder/icons/manuscript.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
resources/uk/co/majenko/audiobookrecorder/icons/play-to.png
Normal file
BIN
resources/uk/co/majenko/audiobookrecorder/icons/play-to.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 752 B |
BIN
resources/uk/co/majenko/audiobookrecorder/icons/split.png
Normal file
BIN
resources/uk/co/majenko/audiobookrecorder/icons/split.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 409 B |
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -25,31 +25,32 @@ public class AGC implements Effect {
|
||||
return getName();
|
||||
}
|
||||
|
||||
public void process(Sample[] samples) {
|
||||
public void process(double[][] samples) {
|
||||
gain = 1d;
|
||||
for (int i = 0; i < samples.length; i++) {
|
||||
double absSampleLeft = Math.abs(samples[i].left) * gain;
|
||||
double absSampleRight = Math.abs(samples[i].right) * gain;
|
||||
double absSampleLeft = Math.abs(samples[i][Sentence.LEFT]) * gain;
|
||||
double absSampleRight = Math.abs(samples[i][Sentence.RIGHT]) * gain;
|
||||
|
||||
double factor = 0.0d;
|
||||
|
||||
if (absSampleLeft > ceiling) {
|
||||
gain -= attack;
|
||||
if (gain < 0) gain = 0;
|
||||
factor = -attack;
|
||||
}
|
||||
|
||||
if (absSampleRight > ceiling) {
|
||||
gain -= attack;
|
||||
if (gain < 0) gain = 0;
|
||||
factor = -attack;
|
||||
}
|
||||
|
||||
if ((absSampleLeft < ceiling) && (absSampleRight < ceiling)) {
|
||||
gain += decay;
|
||||
if (gain > limit) {
|
||||
gain = limit;
|
||||
}
|
||||
factor = decay;
|
||||
}
|
||||
|
||||
samples[i].left *= gain;
|
||||
samples[i].right *= gain;
|
||||
gain += factor;
|
||||
if (gain > limit) gain = limit;
|
||||
if (gain < 0) gain = 0;
|
||||
|
||||
samples[i][Sentence.LEFT] *= gain;
|
||||
samples[i][Sentence.RIGHT] *= gain;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@ public class Amplifier implements Effect {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void process(Sample[] samples) {
|
||||
public void process(double[][] samples) {
|
||||
for (int i = 0; i < samples.length; i++) {
|
||||
samples[i].left *= gain;
|
||||
samples[i].right *= gain;
|
||||
samples[i][Sentence.LEFT] *= gain;
|
||||
samples[i][Sentence.RIGHT] *= gain;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -89,24 +89,24 @@ public class Biquad implements Effect {
|
||||
setPeakGain(peakGainDB);
|
||||
}
|
||||
|
||||
public void process(Sample[] samples) {
|
||||
public void process(double[][] samples) {
|
||||
lz1 = 0d;
|
||||
lz2 = 0d;
|
||||
rz1 = 0d;
|
||||
rz2 = 0d;
|
||||
for (Sample in : samples) {
|
||||
double lout = in.left * a0 + lz1;
|
||||
for (double[] in : samples) {
|
||||
double lout = in[Sentence.LEFT] * a0 + lz1;
|
||||
|
||||
lz1 = in.left * a1 + lz2 - b1 * lout;
|
||||
lz2 = in.left * a2 - b2 * lout;
|
||||
lz1 = in[Sentence.LEFT] * a1 + lz2 - b1 * lout;
|
||||
lz2 = in[Sentence.LEFT] * a2 - b2 * lout;
|
||||
|
||||
double rout = in.right * a0 + rz1;
|
||||
double rout = in[Sentence.RIGHT] * a0 + rz1;
|
||||
|
||||
rz1 = in.right * a1 + rz2 - b1 * rout;
|
||||
rz2 = in.right * a2 - b2 * rout;
|
||||
rz1 = in[Sentence.RIGHT] * a1 + rz2 - b1 * rout;
|
||||
rz2 = in[Sentence.RIGHT] * a2 - b2 * rout;
|
||||
|
||||
in.left = lout;
|
||||
in.right = rout;
|
||||
in[Sentence.LEFT] = lout;
|
||||
in[Sentence.RIGHT] = rout;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,20 @@ import java.nio.file.*;
|
||||
import javax.swing.tree.*;
|
||||
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 {
|
||||
|
||||
String name;
|
||||
@@ -17,6 +31,9 @@ public class Book extends DefaultMutableTreeNode {
|
||||
String genre;
|
||||
String comment;
|
||||
String ACX;
|
||||
String manuscript;
|
||||
|
||||
String defaultEffect = "none";
|
||||
|
||||
int sampleRate;
|
||||
int channels;
|
||||
@@ -31,7 +48,60 @@ public class Book extends DefaultMutableTreeNode {
|
||||
|
||||
prefs = p;
|
||||
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.setNotes(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) {
|
||||
Element node = getNode(r, n);
|
||||
if (node == null) return "";
|
||||
return node.getTextContent();
|
||||
}
|
||||
|
||||
public void setAuthor(String a) { author = a; }
|
||||
@@ -115,8 +185,12 @@ public class Book extends DefaultMutableTreeNode {
|
||||
}
|
||||
}
|
||||
|
||||
public File getBookPath() {
|
||||
return new File(Options.get("path.storage"), name);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (newDir.exists()) {
|
||||
@@ -206,4 +280,118 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.getNotes()));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,20 @@ import javax.swing.border.*;
|
||||
import java.util.*;
|
||||
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 {
|
||||
String name;
|
||||
String author;
|
||||
@@ -25,6 +39,7 @@ public class BookPanel extends JPanel {
|
||||
JPanel panel;
|
||||
|
||||
File root;
|
||||
File configFile;
|
||||
|
||||
boolean highlight = false;
|
||||
|
||||
@@ -36,13 +51,78 @@ public class BookPanel extends JPanel {
|
||||
try {
|
||||
root = r;
|
||||
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) {
|
||||
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) {
|
||||
try {
|
||||
name = props.getProperty("book.name");
|
||||
@@ -71,41 +151,44 @@ public class BookPanel extends JPanel {
|
||||
resizedCover = Utils.getScaledImage(cover.getImage(), 75, 75);
|
||||
iconLabel = new JLabel(new ImageIcon(resizedCover));
|
||||
}
|
||||
|
||||
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);
|
||||
populate();
|
||||
} 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() {
|
||||
return new File(root, "audiobook.abk");
|
||||
return configFile;
|
||||
}
|
||||
|
||||
public void highlight() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -23,7 +26,7 @@ public class BookTreeRenderer extends DefaultTreeCellRenderer {
|
||||
}
|
||||
|
||||
if (s.isLocked()) {
|
||||
ret.setForeground(new Color(0x00, 0x80, 0xFF));
|
||||
ret.setForeground(new Color(0x30, 0xb0, 0xFF));
|
||||
icn.add(Overlays.locked, OverlayIcon.BOTTOM_LEFT);
|
||||
}
|
||||
|
||||
@@ -31,6 +34,10 @@ 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);
|
||||
@@ -39,6 +46,28 @@ public class BookTreeRenderer extends DefaultTreeCellRenderer {
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
58
src/uk/co/majenko/audiobookrecorder/Chain.java
Normal file
58
src/uk/co/majenko/audiobookrecorder/Chain.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,6 +12,19 @@ import it.sauronsoftware.jave.*;
|
||||
import com.mpatric.mp3agic.*;
|
||||
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 {
|
||||
|
||||
@@ -28,7 +41,23 @@ public class Chapter extends DefaultMutableTreeNode {
|
||||
name = chaptername;
|
||||
preGap = Options.getInteger("catenation.pre-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"));
|
||||
|
||||
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() {
|
||||
@@ -116,13 +145,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 +261,42 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ public class Clipping implements Effect {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void process(Sample[] samples) {
|
||||
for (Sample sample : samples) {
|
||||
if (sample.left > clip) sample.left = clip;
|
||||
if (sample.left < -clip) sample.left = -clip;
|
||||
if (sample.right > clip) sample.right = clip;
|
||||
if (sample.right < -clip) sample.right = -clip;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
src/uk/co/majenko/audiobookrecorder/Debug.java
Normal file
12
src/uk/co/majenko/audiobookrecorder/Debug.java
Normal 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));
|
||||
}
|
||||
}
|
||||
@@ -16,22 +16,28 @@ public class DelayLine implements Effect {
|
||||
return "Delay Line (" + delayLines.size() + " lines)";
|
||||
}
|
||||
|
||||
public void process(Sample[] samples) {
|
||||
Sample[] savedSamples = new Sample[samples.length];
|
||||
public void process(double[][] samples) {
|
||||
double[][] savedSamples = new double[samples.length][2];
|
||||
for (int i = 0; i < samples.length; i++) {
|
||||
savedSamples[i] = new Sample(samples[i].left, samples[i].right);
|
||||
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].left = 0d;
|
||||
samples[i].right = 0d;
|
||||
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) {
|
||||
Sample[] subSamples = new Sample[samples.length];
|
||||
for (int i = 0; i < samples.length; i++) {
|
||||
subSamples[i] = new Sample(savedSamples[i].left, savedSamples[i].right);
|
||||
subSamples[i][Sentence.LEFT] = savedSamples[i][Sentence.LEFT];
|
||||
subSamples[i][Sentence.RIGHT] = savedSamples[i][Sentence.RIGHT];
|
||||
}
|
||||
|
||||
d.process(subSamples);
|
||||
@@ -40,31 +46,31 @@ public class DelayLine implements Effect {
|
||||
int off = i + d.getSamples();
|
||||
if ((off < samples.length) && (off > 0)) {
|
||||
|
||||
Sample ns = mix(samples[off], subSamples[i]);
|
||||
samples[off].left = ns.left;
|
||||
samples[off].right = ns.right;
|
||||
double[] ns = mix(samples[off], subSamples[i]);
|
||||
samples[off][Sentence.LEFT] = ns[Sentence.LEFT];
|
||||
samples[off][Sentence.RIGHT] = ns[Sentence.RIGHT];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sample mix(Sample a, Sample b) {
|
||||
Sample out = new Sample(0, 0);
|
||||
double[] mix(double[] a, double[] b) {
|
||||
double[] out = new double[2];
|
||||
|
||||
if ((a.left < 0) && (b.left < 0)) {
|
||||
out.left = (a.left + b.left) - (a.left * b.left);
|
||||
} else if ((a.left > 0) && (b.left > 0)) {
|
||||
out.left = (a.left + b.left) - (a.left * b.left);
|
||||
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.left = a.left + b.left;
|
||||
out[Sentence.LEFT] = a[Sentence.LEFT] + b[Sentence.LEFT];
|
||||
}
|
||||
|
||||
if ((a.right < 0) && (b.right < 0)) {
|
||||
out.right = (a.right + b.right) - (a.right * b.right);
|
||||
} else if ((a.right > 0) && (b.right > 0)) {
|
||||
out.right = (a.right + b.right) - (a.right * b.right);
|
||||
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.right = a.right + b.right;
|
||||
out[Sentence.RIGHT] = a[Sentence.RIGHT] + b[Sentence.RIGHT];
|
||||
}
|
||||
|
||||
return out;
|
||||
|
||||
@@ -24,21 +24,21 @@ public class DelayLineStore {
|
||||
effects = new ArrayList<Effect>();
|
||||
}
|
||||
|
||||
public void process(Sample[] samples) {
|
||||
public void process(double[][] samples) {
|
||||
for (Effect e : effects) {
|
||||
e.process(samples);
|
||||
}
|
||||
|
||||
for (Sample sample : samples) {
|
||||
sample.left *= gain;
|
||||
sample.right *= gain;
|
||||
for (double[] sample : samples) {
|
||||
sample[Sentence.LEFT] *= gain;
|
||||
sample[Sentence.RIGHT] *= gain;
|
||||
|
||||
if (pan < 0) {
|
||||
double p = 1 + pan;
|
||||
sample.right *= p;
|
||||
sample[Sentence.RIGHT] *= p;
|
||||
} else {
|
||||
double p = 1 - pan;
|
||||
sample.left *= p;
|
||||
sample[Sentence.LEFT] *= p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package uk.co.majenko.audiobookrecorder;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public interface Effect {
|
||||
public void process(Sample[] samples);
|
||||
public void process(double[][] samples);
|
||||
public String getName();
|
||||
public ArrayList<Effect> getChildEffects();
|
||||
public void dump();
|
||||
|
||||
@@ -16,7 +16,7 @@ public class EffectGroup implements Effect {
|
||||
effects = new ArrayList<Effect>();
|
||||
}
|
||||
|
||||
public void process(Sample[] samples) {
|
||||
public void process(double[][] samples) {
|
||||
for (Effect e : effects) {
|
||||
e.process(samples);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -33,4 +33,10 @@ 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"));
|
||||
static public final ImageIcon manuscript = new ImageIcon(Icons.class.getResource("icons/manuscript.png"));
|
||||
}
|
||||
|
||||
@@ -9,37 +9,73 @@ 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 void process(Sample[] samples) {
|
||||
for (Sample sample : samples) {
|
||||
double v = Math.sin(phase);
|
||||
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);
|
||||
}
|
||||
|
||||
// // 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.left += (sample.left * v);
|
||||
sample.right += (sample.right * v);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,15 @@ public class MainToolBar extends JToolBar {
|
||||
JButtonSpacePlay recordRoomNoise;
|
||||
JButtonSpacePlay playSentence;
|
||||
JButtonSpacePlay playonSentence;
|
||||
JButtonSpacePlay playtoSentence;
|
||||
JButtonSpacePlay stopPlaying;
|
||||
JButtonSpacePlay eq;
|
||||
JToggleButton mic;
|
||||
JButtonSpacePlay openManuscript;
|
||||
JToggleButtonSpacePlay mic;
|
||||
|
||||
JComboBox<String> playbackSpeed;
|
||||
|
||||
JToggleButtonSpacePlay disableEffects;
|
||||
|
||||
AudiobookRecorder root;
|
||||
|
||||
@@ -78,6 +84,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 +103,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 +132,65 @@ 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);
|
||||
|
||||
addSeparator();
|
||||
openManuscript = new JButtonSpacePlay(Icons.manuscript, "Open Manuscript", new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
root.openManuscript();
|
||||
}
|
||||
});
|
||||
add(openManuscript);
|
||||
|
||||
|
||||
setFloatable(false);
|
||||
}
|
||||
|
||||
public void enablePlayTo(boolean b) {
|
||||
playtoSentence.setEnabled(b);
|
||||
}
|
||||
|
||||
public float getPlaybackSpeed() {
|
||||
int v = playbackSpeed.getSelectedIndex();
|
||||
return 0.75f + (0.25f * v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -86,10 +86,16 @@ public class OpenBookPanel extends JPanel {
|
||||
for (File b : dir.listFiles()) {
|
||||
if (b == null) continue;
|
||||
if (!b.isDirectory()) continue;
|
||||
File xml = new File(b, "audiobook.abk");
|
||||
File xml = new File(b, "audiobook.abx");
|
||||
if (xml.exists()) {
|
||||
BookPanel book = new BookPanel(b);
|
||||
model.addBook(book);
|
||||
} else {
|
||||
xml = new File(b, "audiobook.abk");
|
||||
if (xml.exists()) {
|
||||
BookPanel book = new BookPanel(b);
|
||||
model.addBook(book);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -297,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);
|
||||
@@ -318,6 +323,7 @@ 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"));
|
||||
|
||||
|
||||
@@ -331,7 +337,7 @@ public class Options extends JDialog {
|
||||
|
||||
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));
|
||||
@@ -565,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");
|
||||
@@ -580,6 +588,7 @@ 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");
|
||||
|
||||
@@ -703,11 +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("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());
|
||||
@@ -775,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>();
|
||||
|
||||
@@ -19,14 +19,14 @@ public class Pan implements Effect {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void process(Sample[] samples) {
|
||||
for (Sample sample : samples) {
|
||||
public void process(double[][] samples) {
|
||||
for (double[] sample : samples) {
|
||||
if (pan < 0) {
|
||||
double p = 1 + pan;
|
||||
sample.right *= p;
|
||||
sample[Sentence.RIGHT] *= p;
|
||||
} else {
|
||||
double p = 1 - pan;
|
||||
sample.left *= p;
|
||||
sample[Sentence.LEFT] *= p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package uk.co.majenko.audiobookrecorder;
|
||||
|
||||
public class Sample {
|
||||
public double left;
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,20 @@ import org.json.*;
|
||||
|
||||
import java.util.Timer;
|
||||
|
||||
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.Text;
|
||||
|
||||
public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
|
||||
|
||||
String text;
|
||||
String id;
|
||||
int postGap;
|
||||
@@ -39,6 +50,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
int endOffset = 0;
|
||||
int crossStartOffset = -1;
|
||||
int crossEndOffset = -1;
|
||||
String postGapType = "none";
|
||||
|
||||
int sampleSize = -1;
|
||||
|
||||
@@ -58,14 +70,18 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
public void setOverrideText(String s) { overrideText = s; }
|
||||
public String getOverrideText() { return overrideText; }
|
||||
|
||||
public static final int LEFT = 0;
|
||||
public static final int RIGHT = 1;
|
||||
|
||||
TargetDataLine line;
|
||||
AudioInputStream inputStream;
|
||||
AudioFormat storedFormat = null;
|
||||
double storedLength = -1d;
|
||||
|
||||
Sample[] audioData = null;
|
||||
Sample[] processedAudio = null;
|
||||
|
||||
double[][] audioData = null;
|
||||
|
||||
double[][] processedAudio = null;
|
||||
|
||||
RecordingThread recordingThread;
|
||||
|
||||
boolean effectEthereal = false;
|
||||
@@ -90,7 +106,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
try {
|
||||
running = true;
|
||||
recording = true;
|
||||
byte[] buf = new byte[AudiobookRecorder.window.microphone.getBufferSize()];
|
||||
byte[] buf = new byte[1024]; //AudiobookRecorder.window.microphone.getBufferSize()];
|
||||
FileOutputStream fos = new FileOutputStream(tempFile);
|
||||
int len = 0;
|
||||
AudiobookRecorder.window.microphone.flush();
|
||||
@@ -134,7 +150,6 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Sentence() {
|
||||
super("");
|
||||
id = UUID.randomUUID().toString();
|
||||
@@ -151,12 +166,29 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
postGap = Options.getInteger("catenation.post-sentence");
|
||||
}
|
||||
|
||||
public Sentence(Element root) {
|
||||
super("");
|
||||
id = root.getAttribute("id");
|
||||
text = Book.getTextNode(root, "text");
|
||||
setUserObject(text);
|
||||
setPostGap(Utils.s2i(Book.getTextNode(root, "post-gap")));
|
||||
setStartOffset(Utils.s2i(Book.getTextNode(root, "start-offset")));
|
||||
setEndOffset(Utils.s2i(Book.getTextNode(root, "end-offset")));
|
||||
setLocked(Utils.s2b(Book.getTextNode(root, "locked")));
|
||||
setAttentionFlag(Utils.s2b(Book.getTextNode(root, "attention")));
|
||||
setGain(Utils.s2d(Book.getTextNode(root, "gain")));
|
||||
setEffectChain(Book.getTextNode(root, "effect"));
|
||||
setPostGapType(Book.getTextNode(root, "gaptype"));
|
||||
sampleSize = Utils.s2i(Book.getTextNode(root, "samples"));
|
||||
}
|
||||
|
||||
public boolean startRecording() {
|
||||
if (AudiobookRecorder.window.microphone == null) {
|
||||
JOptionPane.showMessageDialog(AudiobookRecorder.window, "Microphone not started. Start the microphone first.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
|
||||
CacheManager.removeFromCache(this);
|
||||
|
||||
recordingThread = new RecordingThread(getTempFile(), getFile(), Options.getAudioFormat());
|
||||
|
||||
@@ -173,76 +205,67 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
audioData = null;
|
||||
storedFormat = null;
|
||||
storedLength = -1;
|
||||
CacheManager.removeFromCache(this);
|
||||
|
||||
if (!id.equals("room-noise")) {
|
||||
String tm = Options.get("audio.recording.trim");
|
||||
if (tm.equals("peak")) {
|
||||
autoTrimSamplePeak();
|
||||
} else if (tm.equals("fft")) {
|
||||
autoTrimSampleFFT();
|
||||
}
|
||||
autoTrimSample(true);
|
||||
if (Options.getBoolean("process.sphinx")) {
|
||||
recognise();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void autoTrimSample() {
|
||||
autoTrimSample(false);
|
||||
}
|
||||
|
||||
public void autoTrimSample(boolean useRaw) {
|
||||
String tm = Options.get("audio.recording.trim");
|
||||
if (tm.equals("peak")) {
|
||||
autoTrimSamplePeak(useRaw);
|
||||
} else if (tm.equals("fft")) {
|
||||
autoTrimSampleFFT(useRaw);
|
||||
}
|
||||
}
|
||||
|
||||
public static final int FFTBuckets = 1024;
|
||||
|
||||
public double[][] getFFTProfile() {
|
||||
double[] real = new double[FFTBuckets];
|
||||
double[] imag = new double[FFTBuckets];
|
||||
|
||||
Sample[] samples = getProcessedAudioData();
|
||||
int slices = (samples.length / FFTBuckets) + 1;
|
||||
|
||||
double[][] out = new double[slices][];
|
||||
|
||||
int slice = 0;
|
||||
|
||||
for (int i = 0; i < samples.length; i += FFTBuckets) {
|
||||
for (int j = 0; j < FFTBuckets; j++) {
|
||||
if (i + j < samples.length) {
|
||||
real[j] = samples[i+j].getMono();
|
||||
imag[j] = 0;
|
||||
} else {
|
||||
real[j] = 0;
|
||||
imag[j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
out[slice++] = FFT.fft(real, imag, true);
|
||||
}
|
||||
|
||||
return out;
|
||||
|
||||
|
||||
public void autoTrimSampleFFT() {
|
||||
autoTrimSampleFFT(false);
|
||||
}
|
||||
|
||||
public void autoTrimSampleFFT() {
|
||||
public void autoTrimSampleFFT(boolean useRaw) {
|
||||
crossStartOffset = -1;
|
||||
crossEndOffset = -1;
|
||||
Sample[] samples = getProcessedAudioData();
|
||||
if (samples == null) return;
|
||||
double[][] samples;
|
||||
if (useRaw) {
|
||||
samples = getRawAudioData();
|
||||
} else {
|
||||
samples = getProcessedAudioData();
|
||||
}
|
||||
if (samples == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int blocks = samples.length / 4096 + 1;
|
||||
int fftSize = Options.getInteger("audio.recording.trim.blocksize");
|
||||
|
||||
int blocks = samples.length / fftSize + 1;
|
||||
|
||||
int[] intens = new int[blocks];
|
||||
int block = 0;
|
||||
|
||||
for (int i = 0; i < samples.length; i+= 4096) {
|
||||
double[] real = new double[4096];
|
||||
double[] imag = new double[4096];
|
||||
for (int i = 0; i < samples.length; i+= fftSize) {
|
||||
double[] real = new double[fftSize];
|
||||
double[] imag = new double[fftSize];
|
||||
|
||||
for (int j = 0; j < 4096; j++) {
|
||||
for (int j = 0; j < fftSize; j++) {
|
||||
if (i + j < samples.length) {
|
||||
real[j] = samples[i+j].getMono();
|
||||
real[j] = (samples[i+j][LEFT] + samples[i+j][RIGHT]) / 2d;
|
||||
imag[j] = 0;
|
||||
} else {
|
||||
real[j] = 0;
|
||||
@@ -252,14 +275,14 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
|
||||
double[] buckets = FFT.fft(real, imag, true);
|
||||
double av = 0;
|
||||
for (int j = 1; j < 2048; j++) {
|
||||
for (int j = 1; j < fftSize/2; j++) {
|
||||
av += Math.abs(buckets[j]);
|
||||
}
|
||||
av /= 2047d;
|
||||
av /= (fftSize / 2);
|
||||
|
||||
intens[block] = 0;
|
||||
|
||||
for (int j = 2; j < 4096; j += 2) {
|
||||
for (int j = 2; j < fftSize; j += 2) {
|
||||
double d = Math.abs(av - buckets[j]);
|
||||
if (d > 0.05) {
|
||||
intens[block]++;
|
||||
@@ -268,6 +291,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
block++;
|
||||
}
|
||||
|
||||
|
||||
int limit = Options.getInteger("audio.recording.trim.fft");
|
||||
|
||||
// Find first block with > 1 intensity and subtract one.
|
||||
@@ -282,7 +306,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
startOffset = start * 4096;
|
||||
startOffset = start * fftSize;
|
||||
if (startOffset < 0) startOffset = 0;
|
||||
if (startOffset >= samples.length) startOffset = samples.length;
|
||||
|
||||
@@ -299,19 +323,30 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
end = blocks - 1;
|
||||
}
|
||||
|
||||
endOffset = end * 4096;
|
||||
endOffset = (end+1) * fftSize;
|
||||
|
||||
if (endOffset <= startOffset) endOffset = startOffset + 4096;
|
||||
if (endOffset <= startOffset) endOffset = startOffset + fftSize;
|
||||
if (endOffset < 0) endOffset = 0;
|
||||
if (endOffset >= samples.length) endOffset = samples.length;
|
||||
updateCrossings();
|
||||
updateCrossings(useRaw);
|
||||
intens = null;
|
||||
samples = null;
|
||||
|
||||
}
|
||||
|
||||
public void autoTrimSamplePeak() {
|
||||
autoTrimSamplePeak(false);
|
||||
}
|
||||
|
||||
public void autoTrimSamplePeak(boolean useRaw) {
|
||||
crossStartOffset = -1;
|
||||
crossEndOffset = -1;
|
||||
Sample[] samples = getProcessedAudioData();
|
||||
double[][] samples;
|
||||
if (useRaw) {
|
||||
samples = getRawAudioData();
|
||||
} else {
|
||||
samples = getProcessedAudioData();
|
||||
}
|
||||
if (samples == null) return;
|
||||
double noiseFloor = AudiobookRecorder.window.getNoiseFloor();
|
||||
noiseFloor *= 1.1;
|
||||
@@ -319,7 +354,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
// Find start
|
||||
for (int i = 0; i < samples.length; i++) {
|
||||
startOffset = i;
|
||||
if (Math.abs(samples[i].getMono()) > noiseFloor) {
|
||||
if (Math.abs((samples[i][LEFT] + samples[i][RIGHT])/2d) > noiseFloor) {
|
||||
startOffset --;
|
||||
if (startOffset < 0) startOffset = 0;
|
||||
break;
|
||||
@@ -330,27 +365,28 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
startOffset = 0;
|
||||
}
|
||||
|
||||
startOffset -= 4096;
|
||||
int fftSize = Options.getInteger("audio.recording.trim.blocksize");
|
||||
startOffset -= fftSize;
|
||||
|
||||
for (int i = samples.length-1; i >= 0; i--) {
|
||||
endOffset = i;
|
||||
if (Math.abs(samples[i].getMono()) > noiseFloor) {
|
||||
if (Math.abs((samples[i][LEFT] + samples[i][RIGHT])/2d) > noiseFloor) {
|
||||
endOffset ++;
|
||||
if (endOffset >= samples.length-1) endOffset = samples.length-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
endOffset += 4096;
|
||||
endOffset += fftSize;
|
||||
|
||||
if (endOffset <= startOffset) endOffset = startOffset + 4096;
|
||||
if (endOffset <= startOffset) endOffset = startOffset + fftSize;
|
||||
if (endOffset <= 0) {
|
||||
endOffset = samples.length-1;
|
||||
}
|
||||
|
||||
if (startOffset < 0) startOffset = 0;
|
||||
if (endOffset >= samples.length) endOffset = samples.length-1;
|
||||
updateCrossings();
|
||||
updateCrossings(useRaw);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
@@ -434,19 +470,31 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
}
|
||||
|
||||
public void updateCrossings() {
|
||||
updateStartCrossing();
|
||||
updateEndCrossing();
|
||||
updateCrossings(false);
|
||||
}
|
||||
|
||||
public void updateCrossings(boolean useRaw) {
|
||||
updateStartCrossing(useRaw);
|
||||
updateEndCrossing(useRaw);
|
||||
}
|
||||
|
||||
public void updateStartCrossing() {
|
||||
updateStartCrossing(false);
|
||||
}
|
||||
|
||||
public void updateStartCrossing(boolean useRaw) {
|
||||
if (crossStartOffset == -1) {
|
||||
crossStartOffset = findNearestZeroCrossing(startOffset, 4096);
|
||||
crossStartOffset = findNearestZeroCrossing(useRaw, startOffset, 4096);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateEndCrossing() {
|
||||
updateEndCrossing(false);
|
||||
}
|
||||
|
||||
public void updateEndCrossing(boolean useRaw) {
|
||||
if (crossEndOffset == -1) {
|
||||
crossEndOffset = findNearestZeroCrossing(endOffset, 4096);
|
||||
crossEndOffset = findNearestZeroCrossing(useRaw, endOffset, 4096);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,6 +614,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
public void clearCache() {
|
||||
audioData = null;
|
||||
processedAudio = null;
|
||||
storedFormat = null;
|
||||
storedLength = -1;
|
||||
}
|
||||
|
||||
public boolean lockedInCache() {
|
||||
@@ -573,7 +623,16 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
}
|
||||
|
||||
public int findNearestZeroCrossing(int pos, int range) {
|
||||
Sample[] data = getProcessedAudioData();
|
||||
return findNearestZeroCrossing(false, pos, range);
|
||||
}
|
||||
|
||||
public int findNearestZeroCrossing(boolean useRaw, int pos, int range) {
|
||||
double[][] data = null;
|
||||
if (useRaw) {
|
||||
data = getRawAudioData();
|
||||
} else {
|
||||
data = getProcessedAudioData();
|
||||
}
|
||||
if (data == null) return 0;
|
||||
if (data.length == 0) return 0;
|
||||
|
||||
@@ -583,19 +642,19 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
int backwards = pos;
|
||||
int forwards = pos;
|
||||
|
||||
double backwardsPrev = data[backwards].getMono();
|
||||
double forwardsPrev = data[forwards].getMono();
|
||||
double backwardsPrev = (data[backwards][LEFT] + data[backwards][RIGHT]) / 2d;
|
||||
double forwardsPrev = (data[forwards][LEFT] + data[forwards][RIGHT]) / 2d;
|
||||
|
||||
while (backwards > 0 || forwards < data.length-2) {
|
||||
|
||||
if (forwards < data.length-2) forwards++;
|
||||
if (backwards > 0) backwards--;
|
||||
|
||||
if (backwardsPrev >= 0 && data[backwards].getMono() < 0) { // Found one!
|
||||
if (backwardsPrev >= 0 && ((data[backwards][LEFT] + data[backwards][RIGHT]) / 2d) < 0) { // Found one!
|
||||
return backwards;
|
||||
}
|
||||
|
||||
if (forwardsPrev < 0 && data[forwards].getMono() >= 0) {
|
||||
if (forwardsPrev < 0 && ((data[forwards][LEFT] + data[forwards][RIGHT]) / 2d) >= 0) {
|
||||
return forwards;
|
||||
}
|
||||
|
||||
@@ -604,8 +663,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
return pos;
|
||||
}
|
||||
|
||||
backwardsPrev = data[backwards].getMono();
|
||||
forwardsPrev = data[forwards].getMono();
|
||||
backwardsPrev = (data[backwards][LEFT] + data[backwards][RIGHT]) / 2d;
|
||||
forwardsPrev = (data[forwards][LEFT] + data[forwards][RIGHT]) / 2d;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
@@ -647,17 +706,30 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
}
|
||||
|
||||
public double getPeakValue() {
|
||||
return getPeakValue(false, true);
|
||||
}
|
||||
|
||||
public double getPeakValue(boolean useRaw) {
|
||||
return getPeakValue(useRaw, true);
|
||||
}
|
||||
|
||||
public double getPeakValue(boolean useRaw, boolean applyGain) {
|
||||
double oldGain = gain;
|
||||
gain = 1.0d;
|
||||
Sample[] samples = getProcessedAudioData();
|
||||
double[][] samples = null;
|
||||
if (useRaw) {
|
||||
samples = getRawAudioData();
|
||||
} else {
|
||||
samples = getProcessedAudioData(true, applyGain);
|
||||
}
|
||||
gain = oldGain;
|
||||
if (samples == null) {
|
||||
return 0;
|
||||
}
|
||||
double ms = 0;
|
||||
for (int i = 0; i < samples.length; i++) {
|
||||
if (Math.abs(samples[i].getMono()) > ms) {
|
||||
ms = Math.abs(samples[i].getMono());
|
||||
if (Math.abs((samples[i][LEFT] + samples[i][RIGHT]) / 2d) > ms) {
|
||||
ms = Math.abs((samples[i][LEFT] + samples[i][RIGHT]) / 2d);
|
||||
}
|
||||
}
|
||||
return ms;
|
||||
@@ -676,8 +748,10 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
if (g <= 0.0001d) g = 1.0d;
|
||||
if (g == gain) return;
|
||||
|
||||
if (gain != g) {
|
||||
clearCache();
|
||||
int gint = (int)(g * 100d);
|
||||
int gainint = (int)(gain * 100d);
|
||||
if (gint != gainint) {
|
||||
CacheManager.removeFromCache(this);
|
||||
}
|
||||
gain = g;
|
||||
}
|
||||
@@ -688,7 +762,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
|
||||
public void normalize() {
|
||||
if (locked) return;
|
||||
double max = getPeakValue();
|
||||
double max = getPeakValue(true, false);
|
||||
double d = 0.708 / max;
|
||||
if (d > 1d) d = 1d;
|
||||
setGain(d);
|
||||
@@ -723,7 +797,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
proc.waitFor();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
clearCache();
|
||||
CacheManager.removeFromCache(Sentence.this);
|
||||
AudiobookRecorder.window.updateWaveform();
|
||||
}
|
||||
}
|
||||
@@ -749,7 +823,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
}
|
||||
}
|
||||
if (backupNumber == -1) {
|
||||
System.err.println("Out of backup space!");
|
||||
debug("Out of backup space!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -813,7 +887,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
}
|
||||
}
|
||||
|
||||
clearCache();
|
||||
CacheManager.removeFromCache(Sentence.this);
|
||||
AudiobookRecorder.window.updateWaveform();
|
||||
}
|
||||
}
|
||||
@@ -856,18 +930,17 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
clearCache();
|
||||
CacheManager.removeFromCache(this);
|
||||
AudiobookRecorder.window.updateWaveform();
|
||||
}
|
||||
|
||||
public Sample[] getDoubleDataS16LE(AudioInputStream s, AudioFormat format) throws IOException {
|
||||
public double[][] getDoubleDataS16LE(AudioInputStream s, AudioFormat format) throws IOException {
|
||||
long len = s.getFrameLength();
|
||||
int frameSize = format.getFrameSize();
|
||||
int chans = format.getChannels();
|
||||
int bytes = frameSize / chans;
|
||||
|
||||
byte[] frame = new byte[frameSize];
|
||||
Sample[] samples = new Sample[(int)len];
|
||||
double[][] samples = new double[(int)len][2];
|
||||
|
||||
for (long fno = 0; fno < len; fno++) {
|
||||
|
||||
@@ -881,27 +954,166 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
int right = (rh << 8) | rl;
|
||||
if ((left & 0x8000) == 0x8000) left |= 0xFFFF0000;
|
||||
if ((right & 0x8000) == 0x8000) right |= 0xFFFF0000;
|
||||
samples[(int)fno] = new Sample((double)left / 32768d, (double)right / 32768d);
|
||||
samples[(int)fno][LEFT] = (double)left / 32767d;
|
||||
samples[(int)fno][RIGHT] = (double)right / 32767d;
|
||||
} else {
|
||||
int l = frame[0] >= 0 ? frame[0] : 256 + frame[0];
|
||||
int h = frame[1] >= 0 ? frame[1] : 256 + frame[1];
|
||||
int mono = (h << 8) | l;
|
||||
if ((mono & 0x8000) == 0x8000) mono |= 0xFFFF0000;
|
||||
samples[(int)fno] = new Sample((double)mono / 32768d);
|
||||
samples[(int)fno][LEFT] = (double)mono / 32767d;
|
||||
samples[(int)fno][RIGHT] = (double)mono / 32767d;
|
||||
}
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
public Sample[] getDoubleDataS24LE(AudioInputStream s, AudioFormat format) throws IOException {
|
||||
public void writeDoubleDataS16LE(double[][] samples, AudioFormat format) throws IOException {
|
||||
int chans = format.getChannels();
|
||||
|
||||
int frames = samples.length;
|
||||
|
||||
byte[] buffer;
|
||||
|
||||
if (chans == 2) {
|
||||
int buflen = frames * 4;
|
||||
buffer = new byte[buflen];
|
||||
|
||||
for (int i = 0; i < frames; i++) {
|
||||
double left = samples[i][LEFT];
|
||||
double right = samples[i][RIGHT];
|
||||
int off = i * 4;
|
||||
left *= 32767d;
|
||||
right *= 32767d;
|
||||
int li = (int)left;
|
||||
int ri = (int)right;
|
||||
|
||||
if (li > 32767) li = 32767;
|
||||
if (li < -32767) li = -32767;
|
||||
if (ri > 32767) ri = 32767;
|
||||
if (ri < -32767) ri = -32767;
|
||||
|
||||
buffer[off + 0] = (byte)(li & 0xFF);
|
||||
buffer[off + 1] = (byte)((li >> 8) & 0xFF);
|
||||
buffer[off + 2] = (byte)(ri & 0xFF);
|
||||
buffer[off + 3] = (byte)((ri >> 8) & 0xFF);
|
||||
}
|
||||
} else {
|
||||
int buflen = frames * 2;
|
||||
buffer = new byte[buflen];
|
||||
|
||||
for (int i = 0; i < frames; i++) {
|
||||
double left = samples[i][LEFT];
|
||||
double right = samples[i][RIGHT];
|
||||
double mono = (left + right) / 2d;
|
||||
int off = i * 2;
|
||||
mono *= 32767d;
|
||||
int mi = (int)mono;
|
||||
|
||||
if (mi > 32767) mi = 32767;
|
||||
if (mi < -32767) mi = -32767;
|
||||
|
||||
buffer[off + 0] = (byte)(mi & 0xFF);
|
||||
buffer[off + 1] = (byte)((mi >> 8) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
backup();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(buffer);
|
||||
AudioInputStream ais = new AudioInputStream(bis, format, frames);
|
||||
File wavFile = getFile();
|
||||
FileOutputStream fos = new FileOutputStream(wavFile);
|
||||
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, fos);
|
||||
fos.close();
|
||||
ais.close();
|
||||
}
|
||||
|
||||
public void writeDoubleDataS24LE(double[][] samples, AudioFormat format) throws IOException {
|
||||
int chans = format.getChannels();
|
||||
|
||||
int frames = samples.length;
|
||||
|
||||
byte[] buffer;
|
||||
|
||||
if (chans == 2) {
|
||||
int buflen = frames * 6;
|
||||
buffer = new byte[buflen];
|
||||
|
||||
for (int i = 0; i < frames; i++) {
|
||||
double left = samples[i][LEFT];
|
||||
double right = samples[i][RIGHT];
|
||||
int off = i * 6;
|
||||
left *= 8388607d;
|
||||
right *= 8388607d;
|
||||
int li = (int)left;
|
||||
int ri = (int)right;
|
||||
|
||||
if (li > 8388607) li = 8388607;
|
||||
if (li < -8388607) li = -8388607;
|
||||
if (ri > 8388607) ri = 8388607;
|
||||
if (ri < -8388607) ri = -8388607;
|
||||
|
||||
buffer[off + 0] = (byte)(li & 0xFF);
|
||||
buffer[off + 1] = (byte)((li >> 8) & 0xFF);
|
||||
buffer[off + 2] = (byte)((li >> 16) & 0xFF);
|
||||
buffer[off + 3] = (byte)(ri & 0xFF);
|
||||
buffer[off + 4] = (byte)((ri >> 8) & 0xFF);
|
||||
buffer[off + 5] = (byte)((ri >> 16) & 0xFF);
|
||||
}
|
||||
} else {
|
||||
int buflen = frames * 3;
|
||||
buffer = new byte[buflen];
|
||||
|
||||
for (int i = 0; i < frames; i++) {
|
||||
double left = samples[i][LEFT];
|
||||
double right = samples[i][RIGHT];
|
||||
double mono = (left + right) / 2d;
|
||||
int off = i * 3;
|
||||
mono *= 8388607d;
|
||||
int mi = (int)mono;
|
||||
|
||||
if (mi > 8388607) mi = 8388607;
|
||||
if (mi < -8388607) mi = -8388607;
|
||||
|
||||
buffer[off + 0] = (byte)(mi & 0xFF);
|
||||
buffer[off + 1] = (byte)((mi >> 8) & 0xFF);
|
||||
buffer[off + 2] = (byte)((mi >> 16) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
backup();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(buffer);
|
||||
AudioInputStream ais = new AudioInputStream(bis, format, frames);
|
||||
File wavFile = getFile();
|
||||
FileOutputStream fos = new FileOutputStream(wavFile);
|
||||
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, fos);
|
||||
fos.close();
|
||||
ais.close();
|
||||
}
|
||||
|
||||
public void writeAudioData(double[][] samples) throws IOException {
|
||||
AudioFormat format = getAudioFormat();
|
||||
|
||||
switch (format.getSampleSizeInBits()) {
|
||||
case 16:
|
||||
writeDoubleDataS16LE(samples, format);
|
||||
break;
|
||||
case 24:
|
||||
writeDoubleDataS24LE(samples, format);
|
||||
break;
|
||||
}
|
||||
|
||||
CacheManager.removeFromCache(this);
|
||||
}
|
||||
|
||||
public double[][] getDoubleDataS24LE(AudioInputStream s, AudioFormat format) throws IOException {
|
||||
long len = s.getFrameLength();
|
||||
int frameSize = format.getFrameSize();
|
||||
int chans = format.getChannels();
|
||||
int bytes = frameSize / chans;
|
||||
|
||||
byte[] frame = new byte[frameSize];
|
||||
Sample[] samples = new Sample[(int)len];
|
||||
double[][] samples = new double[(int)len][2];
|
||||
|
||||
for (long fno = 0; fno < len; fno++) {
|
||||
|
||||
@@ -918,14 +1130,16 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
int right = (rh << 16) | (rm << 8) | rl;
|
||||
if ((left & 0x800000) == 0x800000) left |= 0xFF000000;
|
||||
if ((right & 0x800000) == 0x800000) right |= 0xFF000000;
|
||||
samples[(int)fno] = new Sample((double)left / 8388608d, (double)right / 8388608d);
|
||||
samples[(int)fno][LEFT] = (double)left / 8388607d;
|
||||
samples[(int)fno][RIGHT] = (double)right / 8388607d;
|
||||
} else {
|
||||
int l = frame[0] >= 0 ? frame[0] : 256 + frame[0];
|
||||
int m = frame[1] >= 0 ? frame[1] : 256 + frame[1];
|
||||
int h = frame[2] >= 0 ? frame[2] : 256 + frame[2];
|
||||
int mono = (h << 16) | (m << 8) | l;
|
||||
if ((mono & 0x800000) == 0x800000) mono |= 0xFF000000;
|
||||
samples[(int)fno] = new Sample((double)mono / 8388608d);
|
||||
samples[(int)fno][LEFT] = (double)mono / 8388607d;
|
||||
samples[(int)fno][RIGHT] = (double)mono / 8388607d;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -933,14 +1147,20 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
}
|
||||
|
||||
public void loadFile() {
|
||||
if (audioData != null) return;
|
||||
if (audioData != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
File f = getFile();
|
||||
try {
|
||||
if (!f.exists()) {
|
||||
debug("TODO: Race condition: wav file doesn't exist yet");
|
||||
return;
|
||||
}
|
||||
AudioInputStream s = AudioSystem.getAudioInputStream(f);
|
||||
AudioFormat format = getAudioFormat();
|
||||
|
||||
Sample[] samples = null;
|
||||
double[][] samples = null;
|
||||
|
||||
switch (format.getSampleSizeInBits()) {
|
||||
case 16:
|
||||
@@ -956,17 +1176,34 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
audioData = samples;
|
||||
CacheManager.addToCache(this);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Sample[] getProcessedAudioData() {
|
||||
synchronized public double[][] getRawAudioData() {
|
||||
loadFile();
|
||||
if (processedAudio != null) return processedAudio;
|
||||
return audioData;
|
||||
}
|
||||
|
||||
synchronized public double[][] getProcessedAudioData() {
|
||||
return getProcessedAudioData(true, true);
|
||||
}
|
||||
|
||||
synchronized public double[][] getProcessedAudioData(boolean effectsEnabled) {
|
||||
return getProcessedAudioData(effectsEnabled, true);
|
||||
}
|
||||
|
||||
synchronized public double[][] getProcessedAudioData(boolean effectsEnabled, boolean applyGain) {
|
||||
loadFile();
|
||||
if (processedAudio != null) {
|
||||
return processedAudio;
|
||||
}
|
||||
|
||||
if (audioData == null) return null;
|
||||
processedAudio = new Sample[audioData.length];
|
||||
processedAudio = new double[audioData.length][2];
|
||||
for (int i = 0; i < audioData.length; i++) {
|
||||
processedAudio[i] = new Sample(audioData[i].left, audioData[i].right);
|
||||
processedAudio[i][LEFT] = audioData[i][LEFT];
|
||||
processedAudio[i][RIGHT] = audioData[i][RIGHT];
|
||||
}
|
||||
// Add processing in here.
|
||||
|
||||
@@ -974,64 +1211,82 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
String def = AudiobookRecorder.window.getDefaultEffectsChain();
|
||||
Effect eff = AudiobookRecorder.window.effects.get(def);
|
||||
|
||||
if (eff != null) {
|
||||
eff.init(getAudioFormat().getFrameRate());
|
||||
eff.process(processedAudio);
|
||||
}
|
||||
if (effectsEnabled) {
|
||||
if (eff != null) {
|
||||
eff.init(getAudioFormat().getFrameRate());
|
||||
eff.process(processedAudio);
|
||||
}
|
||||
|
||||
if (effectChain != null) {
|
||||
// Don't double up the default chain
|
||||
if (!effectChain.equals(def)) {
|
||||
eff = AudiobookRecorder.window.effects.get(effectChain);
|
||||
if (eff != null) {
|
||||
eff.init(getAudioFormat().getFrameRate());
|
||||
eff.process(processedAudio);
|
||||
if (effectChain != null) {
|
||||
// Don't double up the default chain
|
||||
if (!effectChain.equals(def)) {
|
||||
eff = AudiobookRecorder.window.effects.get(effectChain);
|
||||
if (eff != null) {
|
||||
eff.init(getAudioFormat().getFrameRate());
|
||||
eff.process(processedAudio);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add final master gain stage
|
||||
for (int i = 0; i < processedAudio.length; i++) {
|
||||
processedAudio[i].left = processedAudio[i].left * gain;
|
||||
processedAudio[i].right = processedAudio[i].right * gain;
|
||||
|
||||
if (applyGain) {
|
||||
// Add final master gain stage
|
||||
for (int i = 0; i < processedAudio.length; i++) {
|
||||
processedAudio[i][LEFT] *= gain;
|
||||
processedAudio[i][RIGHT] *= gain;
|
||||
}
|
||||
}
|
||||
|
||||
return processedAudio;
|
||||
}
|
||||
|
||||
public Sample[] getDoubleAudioData() {
|
||||
return getProcessedAudioData();
|
||||
public double[][] getDoubleAudioData() {
|
||||
return getDoubleAudioData(true);
|
||||
}
|
||||
|
||||
public Sample[] getCroppedAudioData() {
|
||||
Sample[] inSamples = getDoubleAudioData();
|
||||
public double[][] getDoubleAudioData(boolean effectsEnabled) {
|
||||
return getProcessedAudioData(effectsEnabled);
|
||||
}
|
||||
|
||||
public double[][] getCroppedAudioData() {
|
||||
return getCroppedAudioData(true);
|
||||
}
|
||||
|
||||
public double[][] getCroppedAudioData(boolean effectsEnabled) {
|
||||
double[][] inSamples = getDoubleAudioData(effectsEnabled);
|
||||
if (inSamples == null) return null;
|
||||
updateCrossings();
|
||||
|
||||
int length = crossEndOffset - crossStartOffset;
|
||||
|
||||
Sample[] samples = new Sample[length];
|
||||
double[][] samples = new double[length][2];
|
||||
for (int i = 0; i < length; i++) {
|
||||
samples[i] = inSamples[crossStartOffset + i];
|
||||
samples[i][LEFT] = inSamples[crossStartOffset + i][LEFT];
|
||||
samples[i][RIGHT] = inSamples[crossStartOffset + i][RIGHT];
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
public byte[] getPCMData() {
|
||||
Sample[] croppedData = getCroppedAudioData();
|
||||
return getPCMData(true);
|
||||
}
|
||||
|
||||
public byte[] getPCMData(boolean effectsEnabled) {
|
||||
double[][] croppedData = getCroppedAudioData(effectsEnabled);
|
||||
if (croppedData == null) return null;
|
||||
int length = croppedData.length;
|
||||
byte[] pcmData = new byte[length * 4];
|
||||
for (int i = 0; i < length; i++) {
|
||||
double sd = croppedData[i].left * 32768d;
|
||||
double sd = croppedData[i][LEFT] * 32767d;
|
||||
int si = (int)sd;
|
||||
if (si > 32767) si = 32767;
|
||||
if (si < -32768) si = -32768;
|
||||
if (si < -32767) si = -32767;
|
||||
pcmData[i * 4] = (byte)(si & 0xFF);
|
||||
pcmData[(i * 4) + 1] = (byte)((si & 0xFF00) >> 8);
|
||||
sd = croppedData[i].right * 32768d;
|
||||
sd = croppedData[i][RIGHT] * 32767d;
|
||||
si = (int)sd;
|
||||
if (si > 32767) si = 32767;
|
||||
if (si < -32768) si = -32768;
|
||||
if (si < -32767) si = -32767;
|
||||
pcmData[(i * 4) + 2] = (byte)(si & 0xFF);
|
||||
pcmData[(i * 4) + 3] = (byte)((si & 0xFF00) >> 8);
|
||||
}
|
||||
@@ -1040,7 +1295,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
|
||||
public void setEffectChain(String key) {
|
||||
if ((effectChain != null) && (!effectChain.equals(key))) {
|
||||
clearCache();
|
||||
CacheManager.removeFromCache(this);
|
||||
}
|
||||
effectChain = key;
|
||||
}
|
||||
@@ -1049,4 +1304,94 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
if (effectChain == null) return "none";
|
||||
return effectChain;
|
||||
}
|
||||
|
||||
public String getPostGapType() {
|
||||
return postGapType;
|
||||
}
|
||||
|
||||
public void setPostGapType(String t) {
|
||||
if (t == null || t.equals("none")) {
|
||||
if (getPostGap() == Options.getInteger("catenation.short-sentence")) {
|
||||
t = "continuation";
|
||||
} else if (getPostGap() == Options.getInteger("catenation.post-paragraph")) {
|
||||
t = "paragraph";
|
||||
} else if (getPostGap() == Options.getInteger("catenation.post-section")) {
|
||||
t = "section";
|
||||
} else if (getPostGap() == Options.getInteger("catenation.post-sentence")) {
|
||||
t = "sentence";
|
||||
} else {
|
||||
t = "sentence";
|
||||
}
|
||||
}
|
||||
postGapType = t;
|
||||
}
|
||||
|
||||
public void resetPostGap() {
|
||||
if (postGapType == null) {
|
||||
postGapType = "sentence";
|
||||
}
|
||||
if (postGapType.equals("continuation")) {
|
||||
setPostGap(Options.getInteger("catenation.short-sentence"));
|
||||
} else if (postGapType.equals("paragraph")) {
|
||||
setPostGap(Options.getInteger("catenation.post-paragraph"));
|
||||
} else if (postGapType.equals("section")) {
|
||||
setPostGap(Options.getInteger("catenation.post-section"));
|
||||
} else {
|
||||
setPostGap(Options.getInteger("catenation.post-sentence"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void debug(String txt) {
|
||||
Debug.debug(String.format("%s: %s", id, txt));
|
||||
}
|
||||
|
||||
public TreeMap<String, String> getSentenceData() {
|
||||
|
||||
TreeMap<String, String> out = new TreeMap<String, String>();
|
||||
|
||||
out.put("id", getId());
|
||||
out.put("text", getText());
|
||||
out.put("post-gap", Integer.toString(getPostGap()));
|
||||
out.put("start-offset", Integer.toString(getStartOffset()));
|
||||
out.put("end-offset", Integer.toString(getEndOffset()));
|
||||
out.put("locked", isLocked() ? "true" : "false");
|
||||
out.put("attention", getAttentionFlag() ? "true" : "false");
|
||||
out.put("gain", String.format("%.8f", getGain()));
|
||||
out.put("effect", getEffectChain());
|
||||
out.put("gaptype", getPostGapType());
|
||||
// out.put("samples", Integer.toString(getSampleSize()));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public void purgeBackups() {
|
||||
File whereto = getFile().getParentFile();
|
||||
String name = getFile().getName();
|
||||
|
||||
File[] files = whereto.listFiles();
|
||||
for (File f : files) {
|
||||
String fn = f.getName();
|
||||
if (fn.startsWith("backup-") && fn.endsWith("-" + name)) {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Element getSentenceXML(Document doc) {
|
||||
Element sentenceNode = doc.createElement("sentence");
|
||||
sentenceNode.setAttribute("id", getId());
|
||||
sentenceNode.appendChild(Book.makeTextNode(doc, "text", getText()));
|
||||
sentenceNode.appendChild(Book.makeTextNode(doc, "post-gap", getPostGap()));
|
||||
sentenceNode.appendChild(Book.makeTextNode(doc, "start-offset", getStartOffset()));
|
||||
sentenceNode.appendChild(Book.makeTextNode(doc, "end-offset", getEndOffset()));
|
||||
sentenceNode.appendChild(Book.makeTextNode(doc, "locked", isLocked()));
|
||||
sentenceNode.appendChild(Book.makeTextNode(doc, "attention", getAttentionFlag()));
|
||||
sentenceNode.appendChild(Book.makeTextNode(doc, "gain", getGain()));
|
||||
sentenceNode.appendChild(Book.makeTextNode(doc, "effect", getEffectChain()));
|
||||
sentenceNode.appendChild(Book.makeTextNode(doc, "gaptype", getPostGapType()));
|
||||
sentenceNode.appendChild(Book.makeTextNode(doc, "samples", getSampleSize()));
|
||||
return sentenceNode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import javax.sound.sampled.*;
|
||||
|
||||
public class Waveform extends JPanel implements MouseListener, MouseMotionListener {
|
||||
|
||||
Sample[] 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].getMono();
|
||||
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(Sample[] 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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user