diff --git a/resources/uk/co/majenko/audiobookrecorder/config.txt b/resources/uk/co/majenko/audiobookrecorder/config.txt index f4f4619..6204417 100644 --- a/resources/uk/co/majenko/audiobookrecorder/config.txt +++ b/resources/uk/co/majenko/audiobookrecorder/config.txt @@ -1 +1 @@ -version=0.2.2 +version=0.3.0 diff --git a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java index 7db0cce..3e0a93f 100644 --- a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java +++ b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java @@ -26,6 +26,16 @@ 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 @@ -35,8 +45,6 @@ public class AudiobookRecorder extends JFrame { static Properties config = new Properties(); TreeMap effects; - String defaultEffectChain = "none"; - public final static int IDLE = 0; public final static int RECORDING = 1; public final static int STOPPING = 2; @@ -759,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); + } } } } @@ -803,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 { @@ -1426,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 p = new KVPair(k, effects.get(k).toString()); @@ -1452,7 +1479,7 @@ public class AudiobookRecorder extends JFrame { i = defEff.getSelectedIndex(); KVPair de = defEff.getItemAt(i); - defaultEffectChain = de.getKey(); + book.setDefaultEffect(de.getKey()); book.setAuthor(aut); book.setGenre(gen); @@ -1797,53 +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(); - TreeMap 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(); + 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(); } } @@ -1876,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")); @@ -1887,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"); @@ -2103,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(); @@ -2517,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; @@ -2629,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; } @@ -2875,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; @@ -2885,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 ( @@ -2901,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)); @@ -2928,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(); @@ -3295,7 +3440,7 @@ public class AudiobookRecorder extends JFrame { if (ent != null) { setEffectChain(ent.getKey()); } else { - setEffectChain(defaultEffectChain); + setEffectChain(book.getDefaultEffect()); } } @@ -3309,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); @@ -3319,7 +3464,7 @@ public class AudiobookRecorder extends JFrame { } public String getDefaultEffectsChain() { - return defaultEffectChain; + return book.getDefaultEffect(); } public synchronized boolean getLock() { diff --git a/src/uk/co/majenko/audiobookrecorder/Book.java b/src/uk/co/majenko/audiobookrecorder/Book.java index 3a576db..c6c81e2 100644 --- a/src/uk/co/majenko/audiobookrecorder/Book.java +++ b/src/uk/co/majenko/audiobookrecorder/Book.java @@ -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; } @@ -216,4 +282,85 @@ 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)); + + 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; + } + } diff --git a/src/uk/co/majenko/audiobookrecorder/BookPanel.java b/src/uk/co/majenko/audiobookrecorder/BookPanel.java index 48605a0..253719c 100644 --- a/src/uk/co/majenko/audiobookrecorder/BookPanel.java +++ b/src/uk/co/majenko/audiobookrecorder/BookPanel.java @@ -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() { diff --git a/src/uk/co/majenko/audiobookrecorder/Chapter.java b/src/uk/co/majenko/audiobookrecorder/Chapter.java index e300037..4420b50 100644 --- a/src/uk/co/majenko/audiobookrecorder/Chapter.java +++ b/src/uk/co/majenko/audiobookrecorder/Chapter.java @@ -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() { @@ -249,4 +277,25 @@ 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)); + + 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; + } + } diff --git a/src/uk/co/majenko/audiobookrecorder/OpenBookPanel.java b/src/uk/co/majenko/audiobookrecorder/OpenBookPanel.java index 8bcc007..91edc3b 100644 --- a/src/uk/co/majenko/audiobookrecorder/OpenBookPanel.java +++ b/src/uk/co/majenko/audiobookrecorder/OpenBookPanel.java @@ -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); + } } } } diff --git a/src/uk/co/majenko/audiobookrecorder/Sentence.java b/src/uk/co/majenko/audiobookrecorder/Sentence.java index 7743a87..1ecad3d 100644 --- a/src/uk/co/majenko/audiobookrecorder/Sentence.java +++ b/src/uk/co/majenko/audiobookrecorder/Sentence.java @@ -29,6 +29,17 @@ 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 { @@ -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); @@ -1202,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; } @@ -1333,6 +1360,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; } @@ -1349,4 +1377,21 @@ 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())); + return sentenceNode; + } + }