Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2791691057 | |||
| 575537ae66 | |||
| fde4a597c7 | |||
| e69ae52f50 | |||
| 8bdb06749f | |||
| 0cfd066f4f | |||
| 217ccb915a | |||
| 1dd3e9d86f | |||
| d3d81d71fe | |||
| 80c110afa9 | |||
| 1c08b9a51d | |||
| 4ad30106b6 | |||
| 9100d0e35a | |||
| b8dea19c30 | |||
| 671f2b9270 | |||
| d619fb2f4d |
@@ -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.2.0
|
||||
version=0.3.0
|
||||
|
||||
@@ -47,6 +47,7 @@ public class AGC implements Effect {
|
||||
|
||||
gain += factor;
|
||||
if (gain > limit) gain = limit;
|
||||
if (gain < 0) gain = 0;
|
||||
|
||||
samples[i][Sentence.LEFT] *= gain;
|
||||
samples[i][Sentence.RIGHT] *= gain;
|
||||
|
||||
@@ -26,19 +26,25 @@ import edu.cmu.sphinx.result.*;
|
||||
import org.w3c.dom.Node;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
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;
|
||||
|
||||
public class AudiobookRecorder extends JFrame {
|
||||
|
||||
// Settings - tweakable
|
||||
|
||||
public static final int PLAYBACK_CHUNK_SIZE = 1024;
|
||||
|
||||
public static final String SPHINX_MODEL = "resource:/edu/cmu/sphinx/models/en-us/en-us";
|
||||
|
||||
static Properties config = new Properties();
|
||||
TreeMap<String, EffectGroup> effects;
|
||||
|
||||
String defaultEffectChain = "none";
|
||||
|
||||
public final static int IDLE = 0;
|
||||
public final static int RECORDING = 1;
|
||||
public final static int STOPPING = 2;
|
||||
@@ -60,6 +66,7 @@ public class AudiobookRecorder extends JFrame {
|
||||
|
||||
JMenuItem bookNewChapter;
|
||||
JMenuItem bookExportAudio;
|
||||
JMenuItem bookPurgeBackups;
|
||||
JMenu bookVisitACX;
|
||||
JMenuItem bookVisitTitle;
|
||||
JMenuItem bookVisitAudition;
|
||||
@@ -214,8 +221,16 @@ public class AudiobookRecorder extends JFrame {
|
||||
}
|
||||
});
|
||||
|
||||
bookPurgeBackups = new JMenuItem("Purge Backups");
|
||||
bookPurgeBackups.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
book.purgeBackups();
|
||||
}
|
||||
});
|
||||
|
||||
bookMenu.add(bookNewChapter);
|
||||
bookMenu.add(bookExportAudio);
|
||||
bookMenu.add(bookPurgeBackups);
|
||||
|
||||
bookVisitACX = new JMenu("Visit ACX");
|
||||
bookMenu.add(bookVisitACX);
|
||||
@@ -752,9 +767,14 @@ public class AudiobookRecorder extends JFrame {
|
||||
if (lastBook != null && !lastBook.equals("")) {
|
||||
File f = new File(Options.get("path.storage"), lastBook);
|
||||
if (f.exists() && f.isDirectory()) {
|
||||
File x = new File(f, "audiobook.abk");
|
||||
if (x.exists()) {
|
||||
loadBookStructure(x);
|
||||
File y = new File(f, "audiobook.abx");
|
||||
if (y.exists()) {
|
||||
loadXMLBookStructure(y);
|
||||
} else {
|
||||
File x = new File(f, "audiobook.abk");
|
||||
if (x.exists()) {
|
||||
loadBookStructure(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -796,30 +816,44 @@ public class AudiobookRecorder extends JFrame {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Properties prefs = new Properties();
|
||||
Book newbook = new Book(prefs, info.getTitle().trim());
|
||||
newbook.setAuthor(info.getAuthor().trim());
|
||||
newbook.setGenre(info.getGenre().trim());
|
||||
newbook.setComment(info.getComment().trim());
|
||||
newbook.setACX(info.getACX().trim());
|
||||
|
||||
Properties prefs = new Properties();
|
||||
Chapter caud = new Chapter("audition", "Audition");
|
||||
Chapter copen = new Chapter("open", "Opening Credits");
|
||||
Chapter cone = new Chapter("0001", "Chapter 1");
|
||||
Chapter cclose = new Chapter("close", "Closing Credits");
|
||||
|
||||
prefs.setProperty("book.name", info.getTitle());
|
||||
prefs.setProperty("book.author", info.getAuthor());
|
||||
prefs.setProperty("book.genre", info.getGenre());
|
||||
prefs.setProperty("book.comment", info.getComment());
|
||||
prefs.setProperty("book.acx", info.getACX());
|
||||
prefs.setProperty("chapter.audition.name", "Audition");
|
||||
prefs.setProperty("chapter.audition.pre-gap", Options.get("catenation.pre-chapter"));
|
||||
prefs.setProperty("chapter.audition.post-gap", Options.get("catenation.post-chapter"));
|
||||
prefs.setProperty("chapter.open.name", "Opening Credits");
|
||||
prefs.setProperty("chapter.open.pre-gap", Options.get("catenation.pre-chapter"));
|
||||
prefs.setProperty("chapter.open.post-gap", Options.get("catenation.post-chapter"));
|
||||
prefs.setProperty("chapter.0001.name", "Chapter 1");
|
||||
prefs.setProperty("chapter.0001.pre-gap", Options.get("catenation.pre-chapter"));
|
||||
prefs.setProperty("chapter.0001.post-gap", Options.get("catenation.post-chapter"));
|
||||
prefs.setProperty("chapter.close.name", "Closing Credits");
|
||||
prefs.setProperty("chapter.close.pre-gap", Options.get("catenation.pre-chapter"));
|
||||
prefs.setProperty("chapter.close.post-gap", Options.get("catenation.post-chapter"));
|
||||
newbook.add(caud);
|
||||
newbook.add(copen);
|
||||
newbook.add(cone);
|
||||
newbook.add(cclose);
|
||||
|
||||
buildBook(prefs);
|
||||
File bookRoot = new File(Options.get("path.storage"), newbook.getName());
|
||||
if (!bookRoot.exists()) {
|
||||
bookRoot.mkdirs();
|
||||
}
|
||||
|
||||
Options.set("path.last-book", book.getName());
|
||||
File xml = new File(bookRoot, "audiobook.abx");
|
||||
Document doc = newbook.buildDocument();
|
||||
|
||||
|
||||
// write the content into xml file
|
||||
TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
Transformer transformer = transformerFactory.newTransformer();
|
||||
DOMSource source = new DOMSource(doc);
|
||||
StreamResult result = new StreamResult(xml);
|
||||
transformer.transform(source, result);
|
||||
|
||||
loadXMLBookStructure(xml);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
class JMenuObject extends JMenuItem {
|
||||
@@ -1419,7 +1453,7 @@ public class AudiobookRecorder extends JFrame {
|
||||
int i = 0;
|
||||
if (effects != null) {
|
||||
for (String k : effects.keySet()) {
|
||||
if (k.equals(defaultEffectChain)) {
|
||||
if (k.equals(book.getDefaultEffect())) {
|
||||
selEff = i;
|
||||
}
|
||||
KVPair<String, String> p = new KVPair<String, String>(k, effects.get(k).toString());
|
||||
@@ -1445,7 +1479,7 @@ public class AudiobookRecorder extends JFrame {
|
||||
|
||||
i = defEff.getSelectedIndex();
|
||||
KVPair<String, String> de = defEff.getItemAt(i);
|
||||
defaultEffectChain = de.getKey();
|
||||
book.setDefaultEffect(de.getKey());
|
||||
|
||||
book.setAuthor(aut);
|
||||
book.setGenre(gen);
|
||||
@@ -1454,6 +1488,8 @@ public class AudiobookRecorder extends JFrame {
|
||||
if (!(book.getName().equals(tit))) {
|
||||
book.renameBook(tit);
|
||||
}
|
||||
|
||||
CacheManager.purgeCache();
|
||||
}
|
||||
});
|
||||
menu.add(editData);
|
||||
@@ -1788,59 +1824,136 @@ public class AudiobookRecorder extends JFrame {
|
||||
bookRoot.mkdirs();
|
||||
}
|
||||
|
||||
File config = new File(bookRoot, "audiobook.abk");
|
||||
Properties prefs = new Properties();
|
||||
|
||||
prefs.setProperty("book.name", book.getName());
|
||||
prefs.setProperty("book.author", book.getAuthor());
|
||||
prefs.setProperty("book.genre", book.getGenre());
|
||||
prefs.setProperty("book.comment", book.getComment());
|
||||
prefs.setProperty("book.acx", book.getACX());
|
||||
|
||||
prefs.setProperty("audio.recording.samplerate", "" + book.getSampleRate());
|
||||
prefs.setProperty("audio.recording.resolution", "" + book.getResolution());
|
||||
prefs.setProperty("audio.recording.channels", "" + book.getChannels());
|
||||
|
||||
prefs.setProperty("audio.effect.default", defaultEffectChain);
|
||||
|
||||
for (Enumeration o = book.children(); o.hasMoreElements();) {
|
||||
|
||||
Chapter c = (Chapter)o.nextElement();
|
||||
String keybase = "chapter." + c.getId();
|
||||
prefs.setProperty(keybase + ".name", c.getName());
|
||||
prefs.setProperty(keybase + ".pre-gap", Integer.toString(c.getPreGap()));
|
||||
prefs.setProperty(keybase + ".post-gap", Integer.toString(c.getPostGap()));
|
||||
|
||||
int i = 0;
|
||||
for (Enumeration s = c.children(); s.hasMoreElements();) {
|
||||
Sentence snt = (Sentence)s.nextElement();
|
||||
prefs.setProperty(String.format("%s.sentence.%08d.id", keybase, i), snt.getId());
|
||||
prefs.setProperty(String.format("%s.sentence.%08d.text", keybase, i), snt.getText());
|
||||
prefs.setProperty(String.format("%s.sentence.%08d.post-gap", keybase, i), Integer.toString(snt.getPostGap()));
|
||||
prefs.setProperty(String.format("%s.sentence.%08d.start-offset", keybase, i), Integer.toString(snt.getStartOffset()));
|
||||
prefs.setProperty(String.format("%s.sentence.%08d.end-offset", keybase, i), Integer.toString(snt.getEndOffset()));
|
||||
prefs.setProperty(String.format("%s.sentence.%08d.locked", keybase, i), snt.isLocked() ? "true" : "false");
|
||||
prefs.setProperty(String.format("%s.sentence.%08d.attention", keybase, i), snt.getAttentionFlag() ? "true" : "false");
|
||||
prefs.setProperty(String.format("%s.sentence.%08d.gain", keybase, i), String.format("%.8f", snt.getGain()));
|
||||
prefs.setProperty(String.format("%s.sentence.%08d.effect", keybase, i), snt.getEffectChain());
|
||||
prefs.setProperty(String.format("%s.sentence.%08d.gaptype", keybase, i), snt.getPostGapType());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
FileOutputStream fos = null;
|
||||
|
||||
try {
|
||||
fos = new FileOutputStream(config);
|
||||
prefs.storeToXML(fos, "Audiobook Recorder Description");
|
||||
File xml = new File(bookRoot, "audiobook.abx");
|
||||
Document doc = book.buildDocument();
|
||||
|
||||
|
||||
// write the content into xml file
|
||||
TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
Transformer transformer = transformerFactory.newTransformer();
|
||||
DOMSource source = new DOMSource(doc);
|
||||
StreamResult result = new StreamResult(xml);
|
||||
transformer.transform(source, result);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
public void loadXMLBookStructure(File inputFile) {
|
||||
|
||||
try {
|
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
||||
Document doc = dBuilder.parse(inputFile);
|
||||
doc.getDocumentElement().normalize();
|
||||
|
||||
Element root = doc.getDocumentElement();
|
||||
|
||||
book = new Book(root);
|
||||
|
||||
bookTreeModel = new DefaultTreeModel(book);
|
||||
|
||||
book.loadBookXML(root, bookTreeModel);
|
||||
|
||||
loadEffects();
|
||||
|
||||
bookTree = new JTree(bookTreeModel);
|
||||
bookTree.setEditable(true);
|
||||
bookTree.setUI(new CustomTreeUI(mainScroll));
|
||||
|
||||
bookTree.setCellRenderer(new BookTreeRenderer());
|
||||
|
||||
|
||||
InputMap im = bookTree.getInputMap(JComponent.WHEN_FOCUSED);
|
||||
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "startStopPlayback");
|
||||
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_DOWN_MASK), "startPlaybackFrom");
|
||||
|
||||
roomNoise = new Sentence("room-noise", "Room Noise");
|
||||
|
||||
bookTree.addTreeSelectionListener(new TreeSelectionListener() {
|
||||
public void valueChanged(TreeSelectionEvent e) {
|
||||
DefaultMutableTreeNode n = (DefaultMutableTreeNode)bookTree.getLastSelectedPathComponent();
|
||||
if (n instanceof Sentence) {
|
||||
Sentence s = (Sentence)n;
|
||||
selectedSentence = s;
|
||||
sampleWaveform.setData(s.getDoubleAudioData(effectsEnabled));
|
||||
sampleWaveform.setMarkers(s.getStartOffset(), s.getEndOffset());
|
||||
s.updateCrossings();
|
||||
sampleWaveform.setAltMarkers(s.getStartCrossing(), s.getEndCrossing());
|
||||
postSentenceGap.setValue(s.getPostGap());
|
||||
gainPercent.setValue((int)(s.getGain() * 100d));
|
||||
locked.setSelected(s.isLocked());
|
||||
attention.setSelected(s.getAttentionFlag());
|
||||
|
||||
setEffectChain(s.getEffectChain());
|
||||
|
||||
postSentenceGap.setEnabled(!s.isLocked());
|
||||
gainPercent.setEnabled(!s.isLocked());
|
||||
reprocessAudioFFT.setEnabled(!s.isLocked());
|
||||
reprocessAudioPeak.setEnabled(!s.isLocked());
|
||||
selectCutMode.setEnabled(!s.isLocked());
|
||||
selectSplitMode.setEnabled(!s.isLocked());
|
||||
doCutSplit.setEnabled(false);
|
||||
selectCutMode.setSelected(false);
|
||||
selectSplitMode.setSelected(false);
|
||||
} else {
|
||||
selectedSentence = null;
|
||||
sampleWaveform.clearData();
|
||||
postSentenceGap.setValue(0);
|
||||
gainPercent.setValue(100);
|
||||
locked.setSelected(false);
|
||||
attention.setSelected(false);
|
||||
selectCutMode.setEnabled(false);
|
||||
selectSplitMode.setEnabled(false);
|
||||
doCutSplit.setEnabled(false);
|
||||
selectCutMode.setSelected(false);
|
||||
selectSplitMode.setSelected(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
bookTree.addMouseListener(new MouseAdapter() {
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (e.isPopupTrigger()) {
|
||||
treePopup(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
if (e.isPopupTrigger()) {
|
||||
treePopup(e);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
mainScroll.setViewportView(bookTree);
|
||||
File r = inputFile.getParentFile();
|
||||
|
||||
File cf = new File(r, "coverart.png");
|
||||
if (!cf.exists()) {
|
||||
cf = new File(r, "coverart.jpg");
|
||||
if (!cf.exists()) {
|
||||
cf = new File(r, "coverart.gif");
|
||||
}
|
||||
}
|
||||
|
||||
if (cf.exists()) {
|
||||
ImageIcon i = new ImageIcon(cf.getAbsolutePath());
|
||||
Image ri = Utils.getScaledImage(i.getImage(), 22, 22);
|
||||
book.setIcon(new ImageIcon(ri));
|
||||
bookTreeModel.reload(book);
|
||||
}
|
||||
|
||||
bookTree.expandPath(new TreePath(book.getPath()));
|
||||
|
||||
statusLabel.setText("Noise floor: " + getNoiseFloorDB() + "dB");
|
||||
book.setIcon(Icons.book);
|
||||
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1873,6 +1986,7 @@ public class AudiobookRecorder extends JFrame {
|
||||
}
|
||||
}
|
||||
|
||||
/* Retained for legacy use...! */
|
||||
public void buildBook(Properties prefs) {
|
||||
|
||||
book = new Book(prefs, prefs.getProperty("book.name"));
|
||||
@@ -1884,11 +1998,14 @@ public class AudiobookRecorder extends JFrame {
|
||||
|
||||
loadEffects();
|
||||
|
||||
defaultEffectChain = prefs.getProperty("audio.effect.default");
|
||||
String defaultEffectChain = prefs.getProperty("audio.effect.default");
|
||||
|
||||
if (defaultEffectChain == null) {
|
||||
defaultEffectChain = "none";
|
||||
}
|
||||
|
||||
book.setDefaultEffect(defaultEffectChain);
|
||||
|
||||
int sr = Utils.s2i(prefs.getProperty("audio.recording.samplerate"));
|
||||
if (sr == 0) {
|
||||
sr = Options.getInteger("audio.recording.samplerate");
|
||||
@@ -2100,12 +2217,16 @@ public class AudiobookRecorder extends JFrame {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!f.getName().endsWith(".abk")) {
|
||||
JOptionPane.showMessageDialog(this, "Not a .abk file.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
if (!(f.getName().endsWith(".abk") || f.getName().endsWith(".abx"))) {
|
||||
JOptionPane.showMessageDialog(this, "Not a .abk or .abx file.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
loadBookStructure(f);
|
||||
if (f.getName().endsWith(".abx")) {
|
||||
loadXMLBookStructure(f);
|
||||
} else {
|
||||
loadBookStructure(f);
|
||||
}
|
||||
|
||||
Options.set("path.last-book", book.getName());
|
||||
Options.savePreferences();
|
||||
@@ -2182,8 +2303,12 @@ public class AudiobookRecorder extends JFrame {
|
||||
|
||||
try {
|
||||
|
||||
int blockSize = Options.getInteger("audio.playback.blocksize");
|
||||
|
||||
AudioFormat sampleformat = s.getAudioFormat();
|
||||
AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 2, true, false);
|
||||
float sampleRate = sampleformat.getSampleRate();
|
||||
// sampleRate *= toolBar.getPlaybackSpeed();
|
||||
AudioFormat format = new AudioFormat(sampleRate, 16, 2, true, false);
|
||||
|
||||
play = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer());
|
||||
play.open(format);
|
||||
@@ -2192,10 +2317,10 @@ public class AudiobookRecorder extends JFrame {
|
||||
|
||||
bookTree.scrollPathToVisible(new TreePath(s.getPath()));
|
||||
data = s.getPCMData(effectsEnabled);
|
||||
for (int pos = 0; pos < data.length; pos += PLAYBACK_CHUNK_SIZE) {
|
||||
for (int pos = 0; pos < data.length; pos += blockSize) {
|
||||
sampleWaveform.setPlayMarker(pos / format.getFrameSize());
|
||||
int l = data.length - pos;
|
||||
if (l > PLAYBACK_CHUNK_SIZE) l = PLAYBACK_CHUNK_SIZE;
|
||||
if (l > blockSize) l = blockSize;
|
||||
play.write(data, pos, l);
|
||||
}
|
||||
|
||||
@@ -2344,6 +2469,8 @@ public class AudiobookRecorder extends JFrame {
|
||||
|
||||
try {
|
||||
|
||||
int blockSize = Options.getInteger("audio.playback.blocksize");
|
||||
|
||||
AudioFormat sampleformat = s.getAudioFormat();
|
||||
AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 2, true, false);
|
||||
|
||||
@@ -2375,10 +2502,10 @@ public class AudiobookRecorder extends JFrame {
|
||||
if (startPos > data.length) startPos = data.length;
|
||||
if (endPos > data.length) endPos = data.length;
|
||||
|
||||
for (int pos = startPos; pos < endPos; pos += PLAYBACK_CHUNK_SIZE) {
|
||||
for (int pos = startPos; pos < endPos; pos += blockSize) {
|
||||
sampleWaveform.setPlayMarker(pos / format.getFrameSize());
|
||||
int l = data.length - pos;
|
||||
if (l > PLAYBACK_CHUNK_SIZE) l = PLAYBACK_CHUNK_SIZE;
|
||||
if (l > blockSize) l = blockSize;
|
||||
play.write(data, pos, l);
|
||||
}
|
||||
|
||||
@@ -2420,8 +2547,12 @@ public class AudiobookRecorder extends JFrame {
|
||||
|
||||
try {
|
||||
|
||||
int blockSize = Options.getInteger("audio.playback.blocksize");
|
||||
|
||||
AudioFormat sampleformat = s.getAudioFormat();
|
||||
AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 2, true, false);
|
||||
float sampleRate = sampleformat.getSampleRate();
|
||||
sampleRate *= toolBar.getPlaybackSpeed();
|
||||
AudioFormat format = new AudioFormat(sampleRate, 16, 2, true, false);
|
||||
play = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer());
|
||||
play.open(format);
|
||||
play.start();
|
||||
@@ -2451,10 +2582,10 @@ public class AudiobookRecorder extends JFrame {
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
for (int pos = 0; pos < data.length; pos += PLAYBACK_CHUNK_SIZE) {
|
||||
for (int pos = 0; pos < data.length; pos += blockSize) {
|
||||
sampleWaveform.setPlayMarker(pos / format.getFrameSize());
|
||||
int l = data.length - pos;
|
||||
if (l > PLAYBACK_CHUNK_SIZE) l = PLAYBACK_CHUNK_SIZE;
|
||||
if (l > blockSize) l = blockSize;
|
||||
play.write(data, pos, l);
|
||||
}
|
||||
|
||||
@@ -2470,12 +2601,6 @@ public class AudiobookRecorder extends JFrame {
|
||||
play.write(data, 0, data.length);
|
||||
playing = null;
|
||||
} else {
|
||||
play.drain();
|
||||
play.stop();
|
||||
play.close();
|
||||
play.open(format);
|
||||
play.start();
|
||||
play.drain();
|
||||
data = getRoomNoise(s.getPostGap());
|
||||
play.write(data, 0, data.length);
|
||||
}
|
||||
@@ -2510,7 +2635,7 @@ public class AudiobookRecorder extends JFrame {
|
||||
|
||||
if (roomNoise == null) return null;
|
||||
|
||||
roomNoise.setEffectChain(defaultEffectChain);
|
||||
roomNoise.setEffectChain(book.getDefaultEffect());
|
||||
|
||||
int len = roomNoise.getSampleSize();
|
||||
if (len == 0) return null;
|
||||
@@ -2622,8 +2747,8 @@ public class AudiobookRecorder extends JFrame {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!f.getName().endsWith(".abk")) {
|
||||
JOptionPane.showMessageDialog(this, "Not a .abk file.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
if (!(f.getName().endsWith(".abk") || f.getName().endsWith(".abx"))) {
|
||||
JOptionPane.showMessageDialog(this, "Not a .abk or .abx file.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2868,7 +2993,16 @@ public class AudiobookRecorder extends JFrame {
|
||||
File f = jc.getSelectedFile();
|
||||
if (f.exists()) {
|
||||
|
||||
BookPanel pan = null;
|
||||
|
||||
try {
|
||||
|
||||
String bookName = null;
|
||||
String bookAuthor = null;
|
||||
String bookGenre = null;
|
||||
String bookComment = null;
|
||||
ImageIcon bookCover = null;
|
||||
|
||||
ZipInputStream zis = new ZipInputStream(new FileInputStream(f)) {
|
||||
public void close() throws IOException {
|
||||
return;
|
||||
@@ -2878,15 +3012,28 @@ public class AudiobookRecorder extends JFrame {
|
||||
ImageIcon cover = null;
|
||||
Properties props = new Properties();
|
||||
|
||||
boolean gotMeta = false;
|
||||
boolean gotCover = false;
|
||||
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
if (gotMeta && gotCover) break;
|
||||
|
||||
if (entry.getName().endsWith("/audiobook.abk")) {
|
||||
props.loadFromXML(zis);
|
||||
gotMeta = true;
|
||||
|
||||
if (bookName == null) bookName = props.getProperty("book.name");
|
||||
if (bookAuthor == null) bookAuthor = props.getProperty("book.author");
|
||||
if (bookGenre == null) bookGenre = props.getProperty("book.genre");
|
||||
if (bookComment == null) bookComment = props.getProperty("book.comment");
|
||||
}
|
||||
|
||||
if (entry.getName().endsWith("/audiobook.abx")) {
|
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
||||
Document doc = dBuilder.parse(zis);
|
||||
doc.getDocumentElement().normalize();
|
||||
|
||||
Element rootnode = doc.getDocumentElement();
|
||||
|
||||
bookName = Book.getTextNode(rootnode, "title");
|
||||
bookAuthor = Book.getTextNode(rootnode, "author");
|
||||
bookGenre = Book.getTextNode(rootnode, "genre");
|
||||
bookComment = Book.getTextNode(rootnode, "comment");
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -2894,13 +3041,13 @@ public class AudiobookRecorder extends JFrame {
|
||||
entry.getName().endsWith("/coverart.jpg") ||
|
||||
entry.getName().endsWith("/coverart.gif")
|
||||
) {
|
||||
cover = new ImageIcon(ImageIO.read(zis));
|
||||
gotCover = true;
|
||||
bookCover = new ImageIcon(ImageIO.read(zis));
|
||||
}
|
||||
}
|
||||
zis.close();
|
||||
|
||||
BookPanel pan = new BookPanel(props, cover);
|
||||
pan = new BookPanel(bookName, bookAuthor, bookGenre, bookComment, bookCover);
|
||||
|
||||
int okToImport = JOptionPane.showConfirmDialog(this, pan, "Import this book?", JOptionPane.OK_CANCEL_OPTION);
|
||||
if (okToImport == JOptionPane.OK_OPTION) {
|
||||
zis = new ZipInputStream(new FileInputStream(f));
|
||||
@@ -2921,8 +3068,13 @@ public class AudiobookRecorder extends JFrame {
|
||||
zis.close();
|
||||
|
||||
File bookdir = new File(Options.get("path.storage"), props.getProperty("book.name"));
|
||||
File conf = new File(bookdir, "audiobook.abk");
|
||||
loadBookStructure(conf);
|
||||
File conf = new File(bookdir, "audiobook.abx");
|
||||
if (conf.exists()) {
|
||||
loadXMLBookStructure(conf);
|
||||
} else {
|
||||
conf = new File(bookdir, "audiobook.abk");
|
||||
loadBookStructure(conf);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -3220,7 +3372,40 @@ public class AudiobookRecorder extends JFrame {
|
||||
double f = Utils.s2d(root.getAttribute("frequency"));
|
||||
double d = Utils.s2d(root.getAttribute("depth"));
|
||||
double p = Utils.s2d(root.getAttribute("phase"));
|
||||
return new LFO(f, d, p);
|
||||
double dty = Math.PI;
|
||||
String waveform = root.getAttribute("waveform");
|
||||
if (waveform == null) {
|
||||
waveform = "sine";
|
||||
}
|
||||
|
||||
int w = LFO.SINE;
|
||||
|
||||
switch (waveform.toLowerCase()) {
|
||||
case "sine": w = LFO.SINE; break;
|
||||
case "cosine": w = LFO.COSINE; break;
|
||||
case "square": w = LFO.SQUARE; break;
|
||||
case "triangle": w = LFO.TRIANGLE; break;
|
||||
case "sawtooth": w = LFO.SAWTOOTH; break;
|
||||
}
|
||||
|
||||
int m = LFO.ADD;
|
||||
|
||||
String mode = root.getAttribute("mode");
|
||||
|
||||
if (mode == null) {
|
||||
mode = "add";
|
||||
}
|
||||
|
||||
switch (mode.toLowerCase()) {
|
||||
case "add": m = LFO.ADD; break;
|
||||
case "replace": m = LFO.REPLACE; break;
|
||||
}
|
||||
|
||||
if (root.getAttribute("duty") != null) {
|
||||
int di = Utils.s2i(root.getAttribute("duty")); // 0-100;
|
||||
dty = (Math.PI * 2) * ((double)di / 100d);
|
||||
}
|
||||
return new LFO(f, d, p, w, dty, m);
|
||||
}
|
||||
|
||||
public AGC loadAGC(Element root) {
|
||||
@@ -3255,7 +3440,7 @@ public class AudiobookRecorder extends JFrame {
|
||||
if (ent != null) {
|
||||
setEffectChain(ent.getKey());
|
||||
} else {
|
||||
setEffectChain(defaultEffectChain);
|
||||
setEffectChain(book.getDefaultEffect());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3269,8 +3454,8 @@ public class AudiobookRecorder extends JFrame {
|
||||
}
|
||||
}
|
||||
|
||||
if (effects.get(defaultEffectChain) != null) {
|
||||
setEffectChain(defaultEffectChain);
|
||||
if (effects.get(book.getDefaultEffect()) != null) {
|
||||
setEffectChain(book.getDefaultEffect());
|
||||
updateWaveform();
|
||||
} else {
|
||||
effectChain.setSelectedIndex(0);
|
||||
@@ -3279,7 +3464,7 @@ public class AudiobookRecorder extends JFrame {
|
||||
}
|
||||
|
||||
public String getDefaultEffectsChain() {
|
||||
return defaultEffectChain;
|
||||
return book.getDefaultEffect();
|
||||
}
|
||||
|
||||
public synchronized boolean getLock() {
|
||||
@@ -3369,6 +3554,9 @@ public class AudiobookRecorder extends JFrame {
|
||||
}
|
||||
|
||||
newSentence.writeAudioData(startSamples);
|
||||
newSentence.setPostGapType("continuation");
|
||||
newSentence.setPostGap(Options.getInteger("catenation.short-sentence"));
|
||||
|
||||
selectedSentence.writeAudioData(endSamples);
|
||||
selectedSentence.autoTrimSample();
|
||||
newSentence.autoTrimSample();
|
||||
|
||||
@@ -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;
|
||||
@@ -18,6 +32,8 @@ public class Book extends DefaultMutableTreeNode {
|
||||
String comment;
|
||||
String ACX;
|
||||
|
||||
String defaultEffect = "none";
|
||||
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int resolution;
|
||||
@@ -31,7 +47,57 @@ 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");
|
||||
|
||||
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; }
|
||||
@@ -206,4 +272,95 @@ 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));
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -34,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);
|
||||
|
||||
@@ -34,4 +34,11 @@ public class CacheManager {
|
||||
cache.remove(c);
|
||||
c.clearCache();
|
||||
}
|
||||
|
||||
public static void purgeCache() {
|
||||
for (Cacheable c : cache) {
|
||||
c.clearCache();
|
||||
}
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,22 @@ 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");
|
||||
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,7 +144,7 @@ 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);
|
||||
|
||||
@@ -239,4 +267,35 @@ public class Chapter extends DefaultMutableTreeNode {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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(double[][] samples) {
|
||||
for (double[] sample : samples) {
|
||||
double v = Math.sin(phase);
|
||||
double v = 0;
|
||||
switch (waveform) {
|
||||
case SINE: v = Math.sin(phase); break;
|
||||
case COSINE: v = Math.cos(phase); break;
|
||||
case SQUARE: v = (phase > duty) ? 1d : 0d; break;
|
||||
case TRIANGLE: v = (phase < Math.PI) ? (phase / Math.PI) : (1d - ((phase - Math.PI) / Math.PI)); break;
|
||||
case SAWTOOTH: v = (phase / (Math.PI*2d)); break;
|
||||
}
|
||||
phase += sampleStep;
|
||||
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[Sentence.LEFT] += (sample[Sentence.LEFT] * v);
|
||||
sample[Sentence.RIGHT] += (sample[Sentence.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ public class MainToolBar extends JToolBar {
|
||||
JButtonSpacePlay eq;
|
||||
JToggleButtonSpacePlay mic;
|
||||
|
||||
JComboBox<String> playbackSpeed;
|
||||
|
||||
JToggleButtonSpacePlay disableEffects;
|
||||
|
||||
AudiobookRecorder root;
|
||||
@@ -100,6 +102,7 @@ public class MainToolBar extends JToolBar {
|
||||
eq = new JButtonSpacePlay(Icons.eq, "Reload Effects", new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
root.loadEffects();
|
||||
CacheManager.purgeCache();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -128,6 +131,7 @@ public class MainToolBar extends JToolBar {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
add(mic);
|
||||
|
||||
@@ -152,7 +156,20 @@ public class MainToolBar extends JToolBar {
|
||||
|
||||
add(disableEffects);
|
||||
|
||||
addSeparator();
|
||||
|
||||
add(new JLabel("Playback speed: "));
|
||||
playbackSpeed = new JComboBox<String>(new String[] {
|
||||
"0.75x",
|
||||
"1.00x",
|
||||
"1.25x",
|
||||
"1.50x",
|
||||
"1.75x"
|
||||
});
|
||||
playbackSpeed.setFocusable(false);
|
||||
|
||||
playbackSpeed.setSelectedIndex(1);
|
||||
add(playbackSpeed);
|
||||
|
||||
setFloatable(false);
|
||||
}
|
||||
@@ -161,4 +178,9 @@ public class MainToolBar extends JToolBar {
|
||||
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"));
|
||||
|
||||
|
||||
@@ -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());
|
||||
@@ -782,6 +794,31 @@ public class Options extends JDialog {
|
||||
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>();
|
||||
|
||||
@@ -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;
|
||||
@@ -155,6 +166,22 @@ 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);
|
||||
@@ -225,17 +252,18 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
return;
|
||||
}
|
||||
|
||||
int fftSize = Options.getInteger("audio.recording.trim.blocksize");
|
||||
|
||||
int blocks = samples.length / 4096 + 1;
|
||||
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][LEFT] + samples[i+j][RIGHT]) / 2d;
|
||||
imag[j] = 0;
|
||||
@@ -247,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]++;
|
||||
@@ -278,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;
|
||||
|
||||
@@ -295,9 +323,9 @@ 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(useRaw);
|
||||
@@ -337,7 +365,8 @@ 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;
|
||||
@@ -348,9 +377,9 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
}
|
||||
}
|
||||
|
||||
endOffset += 4096;
|
||||
endOffset += fftSize;
|
||||
|
||||
if (endOffset <= startOffset) endOffset = startOffset + 4096;
|
||||
if (endOffset <= startOffset) endOffset = startOffset + fftSize;
|
||||
if (endOffset <= 0) {
|
||||
endOffset = samples.length-1;
|
||||
}
|
||||
@@ -677,17 +706,21 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
}
|
||||
|
||||
public double getPeakValue() {
|
||||
return getPeakValue(false);
|
||||
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;
|
||||
double[][] samples = null;
|
||||
if (useRaw) {
|
||||
samples = getRawAudioData();
|
||||
} else {
|
||||
samples = getProcessedAudioData();
|
||||
samples = getProcessedAudioData(true, applyGain);
|
||||
}
|
||||
gain = oldGain;
|
||||
if (samples == null) {
|
||||
@@ -729,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);
|
||||
@@ -1153,10 +1186,14 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
}
|
||||
|
||||
synchronized public double[][] getProcessedAudioData() {
|
||||
return getProcessedAudioData(true);
|
||||
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;
|
||||
@@ -1192,14 +1229,14 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add final master gain stage
|
||||
for (int i = 0; i < processedAudio.length; i++) {
|
||||
processedAudio[i][LEFT] *= gain;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1308,4 +1345,53 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
|
||||
double lmax = 0;
|
||||
|
||||
for (int o = 0; o < step; o++) {
|
||||
if (offset + (n * step) + o >= samples.length) break;
|
||||
double sample = (samples[offset + (n * step) + o][Sentence.LEFT] + samples[offset + (n * step) + o][Sentence.RIGHT]) / 2d;
|
||||
if (sample >= 0) {
|
||||
have += sample;
|
||||
|
||||
Reference in New Issue
Block a user