Compare commits

...

13 Commits

12 changed files with 1090 additions and 171 deletions

View File

@@ -1 +1 @@
version=0.2.2
version=0.3.3

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -3,6 +3,7 @@ package uk.co.majenko.audiobookrecorder;
import javax.sound.sampled.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import java.nio.file.*;
@@ -26,7 +27,21 @@ import edu.cmu.sphinx.result.*;
import org.w3c.dom.Node;
import java.util.concurrent.*;
public class AudiobookRecorder extends JFrame {
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 javax.xml.transform.OutputKeys;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
public class AudiobookRecorder extends JFrame implements DocumentListener {
// Settings - tweakable
@@ -35,8 +50,6 @@ public class AudiobookRecorder extends JFrame {
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;
@@ -67,6 +80,7 @@ public class AudiobookRecorder extends JFrame {
JMenuItem toolsMerge;
JMenuItem toolsArchive;
JMenuItem toolsCoverArt;
JMenuItem toolsManuscript;
JMenuItem toolsOptions;
JMenuItem helpAbout;
@@ -92,6 +106,18 @@ public class AudiobookRecorder extends JFrame {
JPanel sampleControl;
public Waveform sampleWaveform;
JScrollBar sampleScroll;
JSplitPane mainSplit;
JTabbedPane notesTabs;
JTextArea bookNotesArea;
JScrollPane bookNotesScroll;
JTextArea chapterNotesArea;
JScrollPane chapterNotesScroll;
JTextArea sentenceNotesArea;
JScrollPane sentenceNotesScroll;
JSpinner postSentenceGap;
JSpinner gainPercent;
@@ -278,6 +304,13 @@ public class AudiobookRecorder extends JFrame {
}
});
toolsManuscript = new JMenuItem("Import Manuscript...");
toolsManuscript.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
loadManuscript();
}
});
toolsOptions = new JMenuItem("Options");
toolsOptions.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
@@ -288,6 +321,7 @@ public class AudiobookRecorder extends JFrame {
toolsMenu.add(toolsMerge);
toolsMenu.add(toolsArchive);
toolsMenu.add(toolsCoverArt);
toolsMenu.add(toolsManuscript);
toolsMenu.addSeparator();
toolsMenu.add(toolsOptions);
@@ -549,11 +583,11 @@ public class AudiobookRecorder extends JFrame {
controlsTop.add(attention);
controlsTop.add(Box.createHorizontalGlue());
controlsTop.add(new JLabel("Post gap:"));
controlsTop.add(new JLabel(" Post gap:"));
controlsTop.add(postSentenceGap);
controlsTop.add(new JLabel("ms"));
controlsTop.add(new JLabel("ms "));
controlsTop.add(new JLabel("Gain:"));
controlsTop.add(new JLabel(" Gain:"));
controlsTop.add(gainPercent);
controlsTop.add(new JLabel("%"));
@@ -613,29 +647,13 @@ public class AudiobookRecorder extends JFrame {
buildToolbar(centralPanel);
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F"), "startRecordShort");
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released F"), "stopRecord");
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("R"), "startRecord");
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released R"), "stopRecord");
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("T"), "startRecordNewPara");
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released T"), "stopRecord");
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("Y"), "startRecordNewSection");
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released Y"), "stopRecord");
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released D"), "deleteLast");
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("SPACE"), "startStopPlayback");
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_DOWN_MASK), "startPlaybackFrom");
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("E"), "startRerecord");
centralPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released E"), "stopRecord");
centralPanel.getActionMap().put("startRecord", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (!getLock()) return;
if (getFocusOwner() == bookNotesArea) { freeLock(); return; }
if (getFocusOwner() == chapterNotesArea) { freeLock(); return; }
if (getFocusOwner() == sentenceNotesArea) { freeLock(); return; }
if (bookTree.isEditing()) {
freeLock();
return;
@@ -651,6 +669,9 @@ public class AudiobookRecorder extends JFrame {
centralPanel.getActionMap().put("startRecordShort", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (!getLock()) return;
if (getFocusOwner() == bookNotesArea) { freeLock(); return; }
if (getFocusOwner() == chapterNotesArea) { freeLock(); return; }
if (getFocusOwner() == sentenceNotesArea) { freeLock(); return; }
if (bookTree.isEditing()) {
freeLock();
return;
@@ -666,6 +687,9 @@ public class AudiobookRecorder extends JFrame {
centralPanel.getActionMap().put("startRecordNewPara", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (!getLock()) return;
if (getFocusOwner() == bookNotesArea) { freeLock(); return; }
if (getFocusOwner() == chapterNotesArea) { freeLock(); return; }
if (getFocusOwner() == sentenceNotesArea) { freeLock(); return; }
if (bookTree.isEditing()) {
freeLock();
return;
@@ -681,6 +705,9 @@ public class AudiobookRecorder extends JFrame {
centralPanel.getActionMap().put("startRecordNewSection", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (!getLock()) return;
if (getFocusOwner() == bookNotesArea) { freeLock(); return; }
if (getFocusOwner() == chapterNotesArea) { freeLock(); return; }
if (getFocusOwner() == sentenceNotesArea) { freeLock(); return; }
if (bookTree.isEditing()) {
freeLock();
return;
@@ -696,6 +723,9 @@ public class AudiobookRecorder extends JFrame {
centralPanel.getActionMap().put("startRerecord", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (!getLock()) return;
if (getFocusOwner() == bookNotesArea) { freeLock(); return; }
if (getFocusOwner() == chapterNotesArea) { freeLock(); return; }
if (getFocusOwner() == sentenceNotesArea) { freeLock(); return; }
if (bookTree.isEditing()) {
freeLock();
return;
@@ -710,6 +740,9 @@ public class AudiobookRecorder extends JFrame {
});
centralPanel.getActionMap().put("stopRecord", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (getFocusOwner() == bookNotesArea) { return; }
if (getFocusOwner() == chapterNotesArea) { return; }
if (getFocusOwner() == sentenceNotesArea) { return; }
if (bookTree.isEditing()) return;
stopLock();
stopRecording();
@@ -718,6 +751,9 @@ public class AudiobookRecorder extends JFrame {
});
centralPanel.getActionMap().put("deleteLast", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (getFocusOwner() == bookNotesArea) { return; }
if (getFocusOwner() == chapterNotesArea) { return; }
if (getFocusOwner() == sentenceNotesArea) { return; }
if (bookTree.isEditing()) return;
deleteLastRecording();
}
@@ -725,6 +761,9 @@ public class AudiobookRecorder extends JFrame {
centralPanel.getActionMap().put("startStopPlayback", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (getFocusOwner() == bookNotesArea) { return; }
if (getFocusOwner() == chapterNotesArea) { return; }
if (getFocusOwner() == sentenceNotesArea) { return; }
if (bookTree.isEditing()) return;
if (playing == null) {
playSelectedSentence();
@@ -736,6 +775,9 @@ public class AudiobookRecorder extends JFrame {
centralPanel.getActionMap().put("startPlaybackFrom", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (getFocusOwner() == bookNotesArea) { return; }
if (getFocusOwner() == chapterNotesArea) { return; }
if (getFocusOwner() == sentenceNotesArea) { return; }
if (bookTree.isEditing()) return;
if (playing == null) {
playFromSelectedSentence();
@@ -744,24 +786,78 @@ public class AudiobookRecorder extends JFrame {
});
mainScroll = new JScrollPane();
centralPanel.add(mainScroll, BorderLayout.CENTER);
bookNotesArea = new JTextArea();
bookNotesArea.setFont(new Font("Monospaced", Font.PLAIN, 10));
bookNotesScroll = new JScrollPane();
bookNotesScroll.setViewportView(bookNotesArea);
chapterNotesArea = new JTextArea();
chapterNotesArea.setFont(new Font("Monospaced", Font.PLAIN, 10));
chapterNotesArea.getDocument().addDocumentListener(this);
chapterNotesScroll = new JScrollPane();
chapterNotesScroll.setViewportView(chapterNotesArea);
sentenceNotesArea = new JTextArea();
sentenceNotesArea.setFont(new Font("Monospaced", Font.PLAIN, 10));
sentenceNotesArea.getDocument().addDocumentListener(this);
sentenceNotesScroll = new JScrollPane();
sentenceNotesScroll.setViewportView(sentenceNotesArea);
notesTabs = new JTabbedPane();
notesTabs.add("Book", bookNotesScroll);
notesTabs.add("Chapter", chapterNotesScroll);
notesTabs.add("Phrase", sentenceNotesScroll);
mainSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mainScroll, notesTabs);
centralPanel.add(mainSplit, BorderLayout.CENTER);
mainSplit.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
if (ev.getPropertyName().equals("dividerLocation")) {
if ((bookTreeModel != null) && (book != null)) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
bookTreeModel.reload(book);
}
});
}
}
}
});
setTitle("AudioBook Recorder");
setIconImage(Icons.appIcon.getImage());
bindKeys(centralPanel);
mainSplit.setResizeWeight(0.8d);
pack();
setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
mainSplit.setDividerLocation(0.8d);
}
});
String lastBook = Options.get("path.last-book");
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);
}
}
}
}
@@ -779,6 +875,50 @@ public class AudiobookRecorder extends JFrame {
}
void bindKeys(JComponent component) {
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F"), "startRecordShort");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released F"), "stopRecord");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("R"), "startRecord");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released R"), "stopRecord");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("T"), "startRecordNewPara");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released T"), "stopRecord");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("Y"), "startRecordNewSection");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released Y"), "stopRecord");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released D"), "deleteLast");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("SPACE"), "startStopPlayback");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_DOWN_MASK), "startPlaybackFrom");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("E"), "startRerecord");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released E"), "stopRecord");
}
void unbindKeys(JComponent component) {
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F"), "ignore");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released F"), "ignore");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("R"), "ignore");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released R"), "ignore");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("T"), "ignore");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released T"), "ignore");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("Y"), "ignore");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released Y"), "ignore");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released D"), "ignore");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("SPACE"), "ignore");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_DOWN_MASK), "ignore");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("E"), "ignore");
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released E"), "ignore");
}
public static void main(String args[]) {
try {
config.load(AudiobookRecorder.class.getResourceAsStream("config.txt"));
@@ -803,30 +943,46 @@ 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();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
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 {
@@ -924,7 +1080,6 @@ public class AudiobookRecorder extends JFrame {
@SuppressWarnings("unchecked")
void treePopup(MouseEvent e) {
int selRow = bookTree.getRowForLocation(e.getX(), e.getY());
TreePath selPath = bookTree.getPathForLocation(e.getX(), e.getY());
if (selRow != -1) {
@@ -1169,6 +1324,34 @@ public class AudiobookRecorder extends JFrame {
}
});
JMenuObject peaknew = new JMenuObject("Auto-trim new (Peak)", c, new ActionListener() {
public void actionPerformed(ActionEvent e) {
JMenuObject o = (JMenuObject)e.getSource();
Chapter chap = (Chapter)o.getObject();
ProgressDialog ed = new ProgressDialog("Auto-trimming " + chap.getName());
AutoTrimThread t = new AutoTrimThread(chap, ed, AutoTrimThread.Peak, AutoTrimThread.NewOnly);
Thread nt = new Thread(t);
nt.start();
ed.setVisible(true);
}
});
JMenuObject fftnew = new JMenuObject("Auto-trim new (FFT)", c, new ActionListener() {
public void actionPerformed(ActionEvent e) {
JMenuObject o = (JMenuObject)e.getSource();
Chapter chap = (Chapter)o.getObject();
ProgressDialog ed = new ProgressDialog("Auto-trimming " + chap.getName());
AutoTrimThread t = new AutoTrimThread(chap, ed, AutoTrimThread.FFT, AutoTrimThread.NewOnly);
Thread nt = new Thread(t);
nt.start();
ed.setVisible(true);
}
});
JMenuObject peak = new JMenuObject("Auto-trim all (Peak)", c, new ActionListener() {
public void actionPerformed(ActionEvent e) {
JMenuObject o = (JMenuObject)e.getSource();
@@ -1176,7 +1359,7 @@ public class AudiobookRecorder extends JFrame {
ProgressDialog ed = new ProgressDialog("Auto-trimming " + chap.getName());
AutoTrimThread t = new AutoTrimThread(chap, ed, AutoTrimThread.Peak);
AutoTrimThread t = new AutoTrimThread(chap, ed, AutoTrimThread.Peak, AutoTrimThread.All);
Thread nt = new Thread(t);
nt.start();
ed.setVisible(true);
@@ -1190,7 +1373,7 @@ public class AudiobookRecorder extends JFrame {
ProgressDialog ed = new ProgressDialog("Auto-trimming " + chap.getName());
AutoTrimThread t = new AutoTrimThread(chap, ed, AutoTrimThread.FFT);
AutoTrimThread t = new AutoTrimThread(chap, ed, AutoTrimThread.FFT, AutoTrimThread.All);
Thread nt = new Thread(t);
nt.start();
ed.setVisible(true);
@@ -1383,6 +1566,8 @@ public class AudiobookRecorder extends JFrame {
menu.addSeparator();
menu.add(mergeWith);
menu.addSeparator();
menu.add(peaknew);
menu.add(fftnew);
menu.add(peak);
menu.add(fft);
menu.addSeparator();
@@ -1426,7 +1611,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());
@@ -1452,7 +1637,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);
@@ -1797,53 +1982,143 @@ 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();
TreeMap<String, String> settings = snt.getSentenceData();
for (String key : settings.keySet()) {
prefs.setProperty(String.format("%s.sentence.%08d.%s", keybase, i, key), settings.get(key));
}
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();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
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 BookTreeNode) {
BookTreeNode btn = (BookTreeNode)n;
btn.onSelect();
}
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();
}
}
@@ -1876,6 +2151,7 @@ public class AudiobookRecorder extends JFrame {
}
}
/* Retained for legacy use...! */
public void buildBook(Properties prefs) {
book = new Book(prefs, prefs.getProperty("book.name"));
@@ -1887,11 +2163,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");
@@ -1927,6 +2206,11 @@ public class AudiobookRecorder extends JFrame {
bookTree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
DefaultMutableTreeNode n = (DefaultMutableTreeNode)bookTree.getLastSelectedPathComponent();
if (n instanceof BookTreeNode) {
BookTreeNode btn = (BookTreeNode)n;
btn.onSelect();
}
if (n instanceof Sentence) {
Sentence s = (Sentence)n;
selectedSentence = s;
@@ -2103,12 +2387,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();
@@ -2258,15 +2546,19 @@ public class AudiobookRecorder extends JFrame {
ProgressDialog dialog;
Chapter chapter;
int type;
int scope;
public final static int FFT = 0;
public final static int Peak = 1;
public final static int NewOnly = 0;
public final static int All = 1;
public AutoTrimThread(Chapter c, ProgressDialog e, int t) {
public AutoTrimThread(Chapter c, ProgressDialog e, int t, int sc) {
super();
dialog = e;
chapter = c;
type = t;
scope = sc;
}
@SuppressWarnings("unchecked")
@@ -2278,6 +2570,9 @@ public class AudiobookRecorder extends JFrame {
kidCount++;
dialog.setProgress(kidCount * 2000 / numKids);
Sentence snt = (Sentence)s.nextElement();
if (scope == NewOnly) {
if (snt.isProcessed()) continue;
}
switch (type) {
case FFT:
snt.autoTrimSampleFFT();
@@ -2517,7 +2812,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;
@@ -2629,8 +2924,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;
}
@@ -2875,7 +3170,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;
@@ -2885,15 +3189,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 (
@@ -2901,13 +3218,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));
@@ -2928,8 +3245,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();
@@ -3295,7 +3617,7 @@ public class AudiobookRecorder extends JFrame {
if (ent != null) {
setEffectChain(ent.getKey());
} else {
setEffectChain(defaultEffectChain);
setEffectChain(book.getDefaultEffect());
}
}
@@ -3309,8 +3631,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);
@@ -3319,7 +3641,7 @@ public class AudiobookRecorder extends JFrame {
}
public String getDefaultEffectsChain() {
return defaultEffectChain;
return book.getDefaultEffect();
}
public synchronized boolean getLock() {
@@ -3440,4 +3762,127 @@ public class AudiobookRecorder extends JFrame {
System.err.println("Effects Enabled: " + b);
}
public void setBookNotes(String text) {
bookNotesArea.setText(text);
}
public void setChapterNotes(String text) {
chapterNotesArea.setText(text);
}
public void setSentenceNotes(String text) {
sentenceNotesArea.setText(text);
}
public String getBookNotes() {
return bookNotesArea.getText();
}
public String getChapterNotes() {
return chapterNotesArea.getText();
}
public String getSentenceNotes() {
return sentenceNotesArea.getText();
}
public void openManuscript() {
if (book == null) return;
File ms = book.getManuscript();
if (ms == null) return;
try {
Desktop.getDesktop().open(ms);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void loadManuscript() {
if (book == null) return;
JFileChooser jc = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("Document Files", "doc", "docx", "pdf", "odt");
jc.addChoosableFileFilter(filter);
jc.setFileFilter(filter);
jc.setDialogTitle("Select manuscript");
int r = jc.showOpenDialog(this);
if (r == JFileChooser.APPROVE_OPTION) {
File src = jc.getSelectedFile();
if (src.exists()) {
book.setManuscript(src);
}
}
}
//* DocumentListener
public void changedUpdate(DocumentEvent e) {
javax.swing.text.Document doc = e.getDocument();
if (doc == chapterNotesArea.getDocument()) {
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)bookTree.getLastSelectedPathComponent();
if (selectedNode instanceof Sentence) {
selectedNode = (DefaultMutableTreeNode)selectedNode.getParent();
}
if (! (selectedNode instanceof Chapter)) {
return;
}
Chapter c = (Chapter)selectedNode;
c.setNotes(chapterNotesArea.getText());
} else if (doc == sentenceNotesArea.getDocument()) {
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)bookTree.getLastSelectedPathComponent();
if (! (selectedNode instanceof Sentence)) {
return;
}
Sentence s = (Sentence)selectedNode;
s.setNotes(sentenceNotesArea.getText());
}
}
public void removeUpdate(DocumentEvent e) {
javax.swing.text.Document doc = e.getDocument();
if (doc == chapterNotesArea.getDocument()) {
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)bookTree.getLastSelectedPathComponent();
if (selectedNode instanceof Sentence) {
selectedNode = (DefaultMutableTreeNode)selectedNode.getParent();
}
if (! (selectedNode instanceof Chapter)) {
return;
}
Chapter c = (Chapter)selectedNode;
c.setNotes(chapterNotesArea.getText());
} else if (doc == sentenceNotesArea.getDocument()) {
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)bookTree.getLastSelectedPathComponent();
if (! (selectedNode instanceof Sentence)) {
return;
}
Sentence s = (Sentence)selectedNode;
s.setNotes(sentenceNotesArea.getText());
}
}
public void insertUpdate(DocumentEvent e) {
javax.swing.text.Document doc = e.getDocument();
if (doc == chapterNotesArea.getDocument()) {
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)bookTree.getLastSelectedPathComponent();
if (selectedNode instanceof Sentence) {
selectedNode = (DefaultMutableTreeNode)selectedNode.getParent();
}
if (! (selectedNode instanceof Chapter)) {
return;
}
Chapter c = (Chapter)selectedNode;
c.setNotes(chapterNotesArea.getText());
} else if (doc == sentenceNotesArea.getDocument()) {
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)bookTree.getLastSelectedPathComponent();
if (! (selectedNode instanceof Sentence)) {
return;
}
Sentence s = (Sentence)selectedNode;
s.setNotes(sentenceNotesArea.getText());
}
}
// DocumentListener *//
}

View File

@@ -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.setBookNotes(getTextNode(root, "notes"));
Element settings = getNode(root, "settings");
Element audioSettings = getNode(settings, "audio");
Element effectSettings = getNode(settings, "effects");
sampleRate = Utils.s2i(getTextNode(audioSettings, "samplerate"));
channels = Utils.s2i(getTextNode(audioSettings, "channels"));
resolution = Utils.s2i(getTextNode(audioSettings, "resolution"));
defaultEffect = getTextNode(settings, "default");
AudiobookRecorder.window.setTitle("AudioBook Recorder :: " + name); // This should be in the load routine!!!!
Element chapters = getNode(root, "chapters");
NodeList chapterList = chapters.getElementsByTagName("chapter");
for (int i = 0; i < chapterList.getLength(); i++) {
Element chapterElement = (Element)chapterList.item(i);
Chapter newChapter = new Chapter(chapterElement, model);
model.insertNodeInto(newChapter, this, getChildCount());
}
}
public static Element getNode(Element r, String n) {
NodeList nl = r.getElementsByTagName(n);
if (nl == null) return null;
if (nl.getLength() == 0) return null;
return (Element)nl.item(0);
}
public static String getTextNode(Element r, String n) {
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()) {
@@ -216,4 +290,108 @@ public class Book extends DefaultMutableTreeNode {
}
}
public Document buildDocument() throws ParserConfigurationException {
DocumentBuilderFactory dbFactory =
DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.newDocument();
Element root = doc.createElement("book");
doc.appendChild(root);
root.appendChild(makeTextNode(doc, "title", name));
root.appendChild(makeTextNode(doc, "author", author));
root.appendChild(makeTextNode(doc, "comment", comment));
root.appendChild(makeTextNode(doc, "genre", genre));
root.appendChild(makeTextNode(doc, "acx", ACX));
root.appendChild(makeTextNode(doc, "manuscript", manuscript));
root.appendChild(makeTextNode(doc, "notes", AudiobookRecorder.window.getBookNotes()));
Element settingsNode = doc.createElement("settings");
root.appendChild(settingsNode);
Element audioSettingsNode = doc.createElement("audio");
settingsNode.appendChild(audioSettingsNode);
audioSettingsNode.appendChild(makeTextNode(doc, "channels", channels));
audioSettingsNode.appendChild(makeTextNode(doc, "resolution", resolution));
audioSettingsNode.appendChild(makeTextNode(doc, "samplerate", sampleRate));
Element effectsNode = doc.createElement("effects");
settingsNode.appendChild(effectsNode);
effectsNode.appendChild(makeTextNode(doc, "default", defaultEffect));
Element chaptersNode = doc.createElement("chapters");
root.appendChild(chaptersNode);
for (Enumeration o = children(); o.hasMoreElements();) {
Object ob = (Object)o.nextElement();
if (ob instanceof Chapter) {
Chapter c = (Chapter)ob;
chaptersNode.appendChild(c.getChapterXML(doc));
}
}
return doc;
}
public static Element makeTextNode(Document doc, String name, String text) {
Element node = doc.createElement(name);
Text tnode = doc.createTextNode(text == null ? "" : text);
node.appendChild(tnode);
return node;
}
public static Element makeTextNode(Document doc, String name, Integer text) {
Element node = doc.createElement(name);
Text tnode = doc.createTextNode(Integer.toString(text));
node.appendChild(tnode);
return node;
}
public static Element makeTextNode(Document doc, String name, Double text) {
Element node = doc.createElement(name);
Text tnode = doc.createTextNode(String.format("%.8f", text));
node.appendChild(tnode);
return node;
}
public static Element makeTextNode(Document doc, String name, Boolean text) {
Element node = doc.createElement(name);
Text tnode = doc.createTextNode(text ? "true" : "false");
node.appendChild(tnode);
return node;
}
public String getDefaultEffect() {
return defaultEffect;
}
public void setDefaultEffect(String eff) {
defaultEffect = eff;
}
public void setManuscript(File f) {
manuscript = f.getName();
File dst = new File(getBookPath(), manuscript);
try {
Files.copy(f.toPath(), dst.toPath());
} catch (Exception ex) {
ex.printStackTrace();
}
}
public File getManuscript() {
if (manuscript == null) return null;
if (manuscript.equals("")) return null;
File f = new File(getBookPath(), manuscript);
if (f.exists()) {
return f;
}
return null;
}
}

View File

@@ -9,6 +9,20 @@ import javax.swing.border.*;
import java.util.*;
import java.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() {

View File

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

View File

@@ -20,13 +20,17 @@ public class BookTreeRenderer extends DefaultTreeCellRenderer {
ret.setText(s.getOverrideText());
}
if (!s.isProcessed()) {
ret.setForeground(new Color(0x88, 0x88, 0x88));
}
if (s.getAttentionFlag()) {
ret.setForeground(new Color(0xFF, 0xFF, 0x00));
icn.add(Overlays.attention, OverlayIcon.TOP_LEFT);
}
if (s.isLocked()) {
ret.setForeground(new Color(0x00, 0x80, 0xFF));
ret.setForeground(new Color(0x30, 0xb0, 0xFF));
icn.add(Overlays.locked, OverlayIcon.BOTTOM_LEFT);
}

View File

@@ -12,8 +12,21 @@ 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 {
public class Chapter extends BookTreeNode {
String name;
String id;
@@ -21,6 +34,8 @@ public class Chapter extends DefaultMutableTreeNode {
int preGap;
int postGap;
String notes;
public Chapter(String i, String chaptername) {
super(chaptername);
@@ -28,7 +43,25 @@ 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"));
notes = Book.getTextNode(root, "notes");
Element sentencesNode = Book.getNode(root, "sentences");
NodeList sentences = sentencesNode.getElementsByTagName("sentence");
for (int i = 0; i < sentences.getLength(); i++) {
Element sentenceElement = (Element)sentences.item(i);
Sentence newSentence = new Sentence(sentenceElement);
model.insertNodeInto(newSentence, this, getChildCount());
}
}
public String getId() {
@@ -249,4 +282,38 @@ public class Chapter extends DefaultMutableTreeNode {
}
}
public Element getChapterXML(Document doc) {
Element chapterNode = doc.createElement("chapter");
chapterNode.setAttribute("id", id);
chapterNode.appendChild(Book.makeTextNode(doc, "name", name));
chapterNode.appendChild(Book.makeTextNode(doc, "pre-gap", preGap));
chapterNode.appendChild(Book.makeTextNode(doc, "post-gap", postGap));
chapterNode.appendChild(Book.makeTextNode(doc, "notes", notes));
Element sentencesNode = doc.createElement("sentences");
chapterNode.appendChild(sentencesNode);
for (Enumeration o = children(); o.hasMoreElements();) {
Object ob = (Object)o.nextElement();
if (ob instanceof Sentence) {
Sentence s = (Sentence)ob;
sentencesNode.appendChild(s.getSentenceXML(doc));
}
}
return chapterNode;
}
public String getNotes() {
return notes;
}
public void setNotes(String t) {
notes = t;
}
public void onSelect() {
AudiobookRecorder.window.setChapterNotes(notes);
}
}

View File

@@ -38,4 +38,5 @@ public class Icons {
static public final ImageIcon docut = new ImageIcon(Icons.class.getResource("icons/do-cut.png"));
static public final ImageIcon playto = new ImageIcon(Icons.class.getResource("icons/play-to.png"));
static public final ImageIcon disable = new ImageIcon(Icons.class.getResource("icons/disable-effects.png"));
static public final ImageIcon manuscript = new ImageIcon(Icons.class.getResource("icons/manuscript.png"));
}

View File

@@ -17,6 +17,7 @@ public class MainToolBar extends JToolBar {
JButtonSpacePlay playtoSentence;
JButtonSpacePlay stopPlaying;
JButtonSpacePlay eq;
JButtonSpacePlay openManuscript;
JToggleButtonSpacePlay mic;
JComboBox<String> playbackSpeed;
@@ -171,6 +172,15 @@ public class MainToolBar extends JToolBar {
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);
}

View File

@@ -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);
}
}
}
}

View File

@@ -29,11 +29,23 @@ 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 {
public class Sentence extends BookTreeNode implements Cacheable {
String text;
String id;
String notes;
int postGap;
int startOffset = 0;
int endOffset = 0;
@@ -49,6 +61,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
boolean inSample;
boolean attention = false;
boolean processed = false;
String effectChain = null;
@@ -75,20 +88,26 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
boolean effectEthereal = false;
public void setSampleSize(int s) {
sampleSize = s;
}
static class RecordingThread implements Runnable {
boolean running = false;
boolean recording = false;
Sentence sent = null;
File tempFile;
File wavFile;
AudioFormat format;
public RecordingThread(File tf, File wf, AudioFormat af) {
public RecordingThread(File tf, File wf, AudioFormat af, Sentence s) {
tempFile = tf;
wavFile = wf;
format = af;
sent = s;
}
public void run() {
@@ -110,6 +129,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
fos.write(buf, 0, nr);
fos.close();
sent.setSampleSize(len / format.getFrameSize());
FileInputStream fis = new FileInputStream(tempFile);
AudioInputStream ais = new AudioInputStream(fis, format, len / format.getFrameSize());
fos = new FileOutputStream(wavFile);
@@ -155,6 +176,24 @@ 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");
notes = Book.getTextNode(root, "notes");
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"));
processed = Utils.s2b(Book.getTextNode(root, "processed"));
}
public boolean startRecording() {
if (AudiobookRecorder.window.microphone == null) {
JOptionPane.showMessageDialog(AudiobookRecorder.window, "Microphone not started. Start the microphone first.", "Error", JOptionPane.ERROR_MESSAGE);
@@ -163,7 +202,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
CacheManager.removeFromCache(this);
recordingThread = new RecordingThread(getTempFile(), getFile(), Options.getAudioFormat());
recordingThread = new RecordingThread(getTempFile(), getFile(), Options.getAudioFormat(), this);
Thread rc = new Thread(recordingThread);
rc.setDaemon(true);
@@ -203,6 +242,12 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
autoTrimSamplePeak(useRaw);
} else if (tm.equals("fft")) {
autoTrimSampleFFT(useRaw);
} else {
startOffset = 0;
crossStartOffset = 0;
endOffset = sampleSize - 1;
crossEndOffset = sampleSize - 1;
processed = false;
}
}
@@ -304,7 +349,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
updateCrossings(useRaw);
intens = null;
samples = null;
processed = true;
AudiobookRecorder.window.bookTreeModel.reload(this);
}
public void autoTrimSamplePeak() {
@@ -360,6 +406,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
if (startOffset < 0) startOffset = 0;
if (endOffset >= samples.length) endOffset = samples.length-1;
updateCrossings(useRaw);
processed = true;
AudiobookRecorder.window.bookTreeModel.reload(this);
}
public String getId() {
@@ -369,6 +417,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
public void setText(String t) {
overrideText = null;
text = t;
AudiobookRecorder.window.bookTreeModel.reload(this);
}
public String getText() {
@@ -416,6 +465,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
if (o instanceof String) {
String so = (String)o;
text = so;
AudiobookRecorder.window.bookTreeModel.reload(this);
}
}
@@ -475,6 +525,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
if (startOffset != o) {
startOffset = o;
crossStartOffset = -1;
AudiobookRecorder.window.bookTreeModel.reload(this);
}
}
@@ -490,6 +541,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
if (endOffset != o) {
endOffset = o;
crossEndOffset = -1;
AudiobookRecorder.window.bookTreeModel.reload(this);
}
}
@@ -570,6 +622,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
public void setLocked(boolean l) {
locked = l;
AudiobookRecorder.window.bookTreeModel.reload(this);
}
public boolean isLocked() {
@@ -672,6 +725,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
public void setAttentionFlag(boolean f) {
attention = f;
AudiobookRecorder.window.bookTreeModel.reload(this);
}
public boolean getAttentionFlag() {
@@ -679,17 +733,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) {
@@ -731,7 +789,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);
@@ -1155,10 +1213,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;
@@ -1194,14 +1256,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;
}
@@ -1263,6 +1325,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
CacheManager.removeFromCache(this);
}
effectChain = key;
AudiobookRecorder.window.bookTreeModel.reload(this);
}
public String getEffectChain() {
@@ -1289,6 +1352,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
}
}
postGapType = t;
AudiobookRecorder.window.bookTreeModel.reload(this);
}
public void resetPostGap() {
@@ -1325,6 +1389,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
out.put("gain", String.format("%.8f", getGain()));
out.put("effect", getEffectChain());
out.put("gaptype", getPostGapType());
// out.put("samples", Integer.toString(getSampleSize()));
return out;
}
@@ -1341,4 +1406,44 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
}
}
}
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()));
sentenceNode.appendChild(Book.makeTextNode(doc, "processed", isProcessed()));
sentenceNode.appendChild(Book.makeTextNode(doc, "notes", getNotes()));
return sentenceNode;
}
public boolean isProcessed() {
return processed;
}
public void setProcessed(boolean p) {
processed = p;
AudiobookRecorder.window.bookTreeModel.reload(this);
}
public void setNotes(String n) {
notes = n;
}
public String getNotes() {
return notes;
}
public void onSelect() {
AudiobookRecorder.window.setSentenceNotes(notes);
}
}