diff --git a/resources/uk/co/majenko/audiobookrecorder/icons/spinner0.png b/resources/uk/co/majenko/audiobookrecorder/icons/spinner0.png new file mode 100644 index 0000000..8341a1b Binary files /dev/null and b/resources/uk/co/majenko/audiobookrecorder/icons/spinner0.png differ diff --git a/resources/uk/co/majenko/audiobookrecorder/icons/spinner1.png b/resources/uk/co/majenko/audiobookrecorder/icons/spinner1.png new file mode 100644 index 0000000..53f29fd Binary files /dev/null and b/resources/uk/co/majenko/audiobookrecorder/icons/spinner1.png differ diff --git a/resources/uk/co/majenko/audiobookrecorder/icons/spinner2.png b/resources/uk/co/majenko/audiobookrecorder/icons/spinner2.png new file mode 100644 index 0000000..1a939e1 Binary files /dev/null and b/resources/uk/co/majenko/audiobookrecorder/icons/spinner2.png differ diff --git a/resources/uk/co/majenko/audiobookrecorder/icons/spinner3.png b/resources/uk/co/majenko/audiobookrecorder/icons/spinner3.png new file mode 100644 index 0000000..82cb461 Binary files /dev/null and b/resources/uk/co/majenko/audiobookrecorder/icons/spinner3.png differ diff --git a/src/it/sauronsoftware/jave/Encoder.java b/src/it/sauronsoftware/jave/Encoder.java index 2bdf0f8..4220f86 100644 --- a/src/it/sauronsoftware/jave/Encoder.java +++ b/src/it/sauronsoftware/jave/Encoder.java @@ -41,6 +41,9 @@ public class Encoder { private static final Pattern FORMAT_PATTERN = Pattern .compile("^\\s*([D ])([E ])\\s+([\\w,]+)\\s+.+$"); + private static final Pattern TIME_PATTERN = Pattern + .compile("^(\\d+):(\\d+):(\\d+)\\.(\\d+)$"); + /** * This regexp is used to parse the ffmpeg output about the included * encoders/decoders. @@ -895,26 +898,52 @@ public class Encoder { if (listener != null) { String time = (String) table.get("time"); if (time != null) { - int dot = time.indexOf('.'); - if (dot > 0 && dot == time.length() - 2 - && duration > 0) { - String p1 = time.substring(0, dot); - String p2 = time.substring(dot + 1); - try { - long i1 = Long.parseLong(p1); - long i2 = Long.parseLong(p2); - progress = (i1 * 1000L) - + (i2 * 100L); - int perm = (int) Math - .round((double) (progress * 1000L) - / (double) duration); - if (perm > 1000) { - perm = 1000; - } - listener.progress(perm); - } catch (NumberFormatException e) { - ; - } + + Matcher tm = TIME_PATTERN.matcher(time); + if (tm.matches()) { + try { + long hours = Long.parseLong(tm.group(1)); + long minutes = Long.parseLong(tm.group(2)); + long seconds = Long.parseLong(tm.group(3)); + long millis = Long.parseLong(tm.group(4)); + progress = (hours * 3600L * 1000L) + + (minutes * 60L * 1000L) + + (seconds * 1000L) + + (millis * 100L); + + int perm = (int) Math + .round((double) (progress * 1000L) + / (double) duration); + if (perm > 1000) { + perm = 1000; + } + listener.progress(perm); + + } catch (NumberFormatException e) { + ; + } + } else { + int dot = time.indexOf('.'); + if (dot > 0 && dot == time.length() - 2 + && duration > 0) { + String p1 = time.substring(0, dot); + String p2 = time.substring(dot + 1); + try { + long i1 = Long.parseLong(p1); + long i2 = Long.parseLong(p2); + progress = (i1 * 1000L) + + (i2 * 100L); + int perm = (int) Math + .round((double) (progress * 1000L) + / (double) duration); + if (perm > 1000) { + perm = 1000; + } + listener.progress(perm); + } catch (NumberFormatException e) { + ; + } + } } } } diff --git a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java index d838d26..7f1525e 100644 --- a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java +++ b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java @@ -1145,13 +1145,20 @@ public class AudiobookRecorder extends JFrame { i++; } } + FileOutputStream fos = null; try { - FileOutputStream fos = new FileOutputStream(config); + fos = new FileOutputStream(config); prefs.storeToXML(fos, "Audiobook Recorder Description"); } catch (Exception e) { e.printStackTrace(); } + if (fos != null) { + try { + fos.close(); + } catch (Exception e) { + } + } } public void loadBookStructure(File f) { @@ -1471,133 +1478,160 @@ public class AudiobookRecorder extends JFrame { } + class ExportThread implements Runnable { + ExportDialog exportDialog; + + public ExportThread(ExportDialog e) { + super(); + exportDialog = e; + } + + public void run() { + + try { + File bookRoot = new File(Options.get("path.storage"), book.getName()); + if (!bookRoot.exists()) { + bookRoot.mkdirs(); + } + + File export = new File(bookRoot, "export"); + if (!export.exists()) { + export.mkdirs(); + } + Encoder encoder; + + String ffloc = Options.get("path.ffmpeg"); + if (ffloc != null && !ffloc.equals("")) { + encoder = new Encoder(new FFMPEGLocator() { + public String getFFMPEGExecutablePath() { + return Options.get("path.ffmpeg"); + } + }); + } else { + encoder = new Encoder(); + } + EncodingAttributes attributes = new EncodingAttributes(); + + AudioAttributes audioAttributes = new AudioAttributes(); + audioAttributes.setCodec("libmp3lame"); + audioAttributes.setBitRate(Options.getInteger("audio.export.bitrate")); + audioAttributes.setSamplingRate(Options.getInteger("audio.export.samplerate")); + audioAttributes.setChannels(new Integer(2)); + attributes.setFormat("mp3"); + attributes.setAudioAttributes(audioAttributes); + + + AudioFormat format = roomNoise.getAudioFormat(); + byte[] data; + + for (Enumeration o = book.children(); o.hasMoreElements();) { + + int fullLength = 0; + + + Chapter c = o.nextElement(); + + if (c.getChildCount() == 0) continue; + int kids = c.getChildCount(); + if (kids == 0) continue; + String name = c.getName(); + exportDialog.setMessage("Exporting " + name); + exportDialog.setProgress(0); + + File exportFile = new File(export, name + ".wax"); + File wavFile = new File(export, name + ".wav"); + + FileOutputStream fos = new FileOutputStream(exportFile); + data = getRoomNoise(s2i(Options.get("catenation.pre-chapter"))); + fullLength += data.length; + fos.write(data); + + int kidno = 0; + + + for (Enumeration s = c.children(); s.hasMoreElements();) { + kidno++; + exportDialog.setProgress(kidno * 1000 / kids); + Sentence snt = s.nextElement(); + data = snt.getRawAudioData(); + + fullLength += data.length; + fos.write(data); + + if (s.hasMoreElements()) { + data = getRoomNoise(snt.getPostGap()); + } else { + data = getRoomNoise(s2i(Options.get("catenation.post-chapter"))); + } + fullLength += data.length; + fos.write(data); + } + fos.close(); + FileInputStream fis = new FileInputStream(exportFile); + AudioInputStream ais = new AudioInputStream(fis, format, fullLength); + fos = new FileOutputStream(wavFile); + AudioSystem.write(ais, AudioFileFormat.Type.WAVE, fos); + fos.flush(); + fos.close(); + fis.close(); + exportFile.delete(); + } + + + + for (Enumeration o = book.children(); o.hasMoreElements();) { + + Chapter c = o.nextElement(); + if (c.getChildCount() == 0) continue; + String name = c.getName(); + + exportDialog.setMessage("Converting " + name); + + File wavFile = new File(export, name + ".wav"); + File mp3File = new File(export, name + "-untagged.mp3"); + File taggedFile = new File(export, name + ".mp3"); + + encoder.encode(wavFile, mp3File, attributes, exportDialog); + + Mp3File id3 = new Mp3File(mp3File); + + ID3v2 tags = new ID3v24Tag(); + id3.setId3v2Tag(tags); + + tags.setTrack(Integer.toString(s2i(c.getId()) - 0)); + tags.setTitle(c.getName()); + tags.setAlbum(book.getName()); + tags.setArtist(book.getAuthor()); + + // ID3v2TextFrameData g = new ID3v2TextFrameData(false, new EncodedText(book.getGenre())); + // tags.addFrame(tags.createFrame("TCON", g.toBytes(), true)); + + tags.setComment(book.getComment()); + + id3.save(taggedFile.getAbsolutePath()); + mp3File.delete(); + wavFile.delete(); + + } + + + } catch (Exception e) { + e.printStackTrace(); + } + + exportDialog.closeDialog(); + } + } + @SuppressWarnings("unchecked") public void exportAudio() { - try { - File bookRoot = new File(Options.get("path.storage"), book.getName()); - if (!bookRoot.exists()) { - bookRoot.mkdirs(); - } + ExportDialog ed = new ExportDialog("Exporting book..."); - File export = new File(bookRoot, "export"); - if (!export.exists()) { - export.mkdirs(); - } - - Encoder encoder; - - String ffloc = Options.get("path.ffmpeg"); - if (ffloc != null && !ffloc.equals("")) { - encoder = new Encoder(new FFMPEGLocator() { - public String getFFMPEGExecutablePath() { - return Options.get("path.ffmpeg"); - } - }); - } else { - encoder = new Encoder(); - } - EncodingAttributes attributes = new EncodingAttributes(); - - AudioAttributes audioAttributes = new AudioAttributes(); - audioAttributes.setCodec("libmp3lame"); - audioAttributes.setBitRate(Options.getInteger("audio.export.bitrate")); - audioAttributes.setSamplingRate(Options.getInteger("audio.export.samplerate")); - audioAttributes.setChannels(new Integer(2)); - - attributes.setFormat("mp3"); - attributes.setAudioAttributes(audioAttributes); - - - AudioFormat format = roomNoise.getAudioFormat(); - byte[] data; - - for (Enumeration o = book.children(); o.hasMoreElements();) { - - int fullLength = 0; - - Chapter c = o.nextElement(); - if (c.getChildCount() == 0) continue; - String name = c.getName(); - - File exportFile = new File(export, name + ".wax"); - File wavFile = new File(export, name + ".wav"); - - FileOutputStream fos = new FileOutputStream(exportFile); - - data = getRoomNoise(s2i(Options.get("catenation.pre-chapter"))); - fullLength += data.length; - fos.write(data); - - for (Enumeration s = c.children(); s.hasMoreElements();) { - Sentence snt = s.nextElement(); - data = snt.getRawAudioData(); - - fullLength += data.length; - fos.write(data); - - if (s.hasMoreElements()) { - data = getRoomNoise(snt.getPostGap()); - } else { - data = getRoomNoise(s2i(Options.get("catenation.post-chapter"))); - } - fullLength += data.length; - fos.write(data); - } - fos.close(); - - FileInputStream fis = new FileInputStream(exportFile); - AudioInputStream ais = new AudioInputStream(fis, format, fullLength); - fos = new FileOutputStream(wavFile); - AudioSystem.write(ais, AudioFileFormat.Type.WAVE, fos); - fos.flush(); - fos.close(); - fis.close(); - exportFile.delete(); - } - - - - for (Enumeration o = book.children(); o.hasMoreElements();) { - - Chapter c = o.nextElement(); - if (c.getChildCount() == 0) continue; - String name = c.getName(); - - File wavFile = new File(export, name + ".wav"); - File mp3File = new File(export, name + "-untagged.mp3"); - File taggedFile = new File(export, name + ".mp3"); - - encoder.encode(wavFile, mp3File, attributes); - - Mp3File id3 = new Mp3File(mp3File); - - ID3v2 tags = new ID3v24Tag(); - id3.setId3v2Tag(tags); - - tags.setTrack(Integer.toString(s2i(c.getId()) - 0)); - tags.setTitle(c.getName()); - tags.setAlbum(book.getName()); - tags.setArtist(book.getAuthor()); - -// ID3v2TextFrameData g = new ID3v2TextFrameData(false, new EncodedText(book.getGenre())); -// tags.addFrame(tags.createFrame("TCON", g.toBytes(), true)); - - tags.setComment(book.getComment()); - - id3.save(taggedFile.getAbsolutePath()); - - mp3File.delete(); - wavFile.delete(); - - } - - JOptionPane.showMessageDialog(this, "Book exported.", "Done", JOptionPane.INFORMATION_MESSAGE); - - - } catch (Exception e) { - e.printStackTrace(); - } + ExportThread t = new ExportThread(ed); + Thread nt = new Thread(t); + nt.start(); + ed.setVisible(true); } diff --git a/src/uk/co/majenko/audiobookrecorder/ExportDialog.java b/src/uk/co/majenko/audiobookrecorder/ExportDialog.java new file mode 100644 index 0000000..f284940 --- /dev/null +++ b/src/uk/co/majenko/audiobookrecorder/ExportDialog.java @@ -0,0 +1,84 @@ +package uk.co.majenko.audiobookrecorder; + +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.border.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import it.sauronsoftware.jave.*; + +public class ExportDialog extends JDialog implements EncoderProgressListener { + JLabel message; + JLabel icon; + + JProgressBar progress; + + int spin = 0; + + + + public ExportDialog(String m) { + super(); + + setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + + message = new JLabel(m); + icon = new JLabel(Icons.spinner0); + progress = new JProgressBar(0, 1000); + + setLayout(new BorderLayout()); + add(message, BorderLayout.CENTER); + + icon.setBorder(new EmptyBorder(10, 10, 10, 10)); + + add(icon, BorderLayout.WEST); + + + add(progress, BorderLayout.SOUTH); + + setLocationRelativeTo(AudiobookRecorder.window); + + setPreferredSize(new Dimension(300, 100)); + + pack(); + + setSize(new Dimension(300, 100)); + +// setVisible(true); + } + + public void setMessage(String m) { + message.setText(m); + } + + public void closeDialog() { + dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING)); + } + + public void spin() { + spin++; + if (spin == 4) spin = 0; + + switch (spin) { + case 0: icon.setIcon(Icons.spinner0); break; + case 1: icon.setIcon(Icons.spinner1); break; + case 2: icon.setIcon(Icons.spinner2); break; + case 3: icon.setIcon(Icons.spinner3); break; + } + } + + public void progress(int p) { setProgress(p); } + + public void setProgress(int p) { + progress.setValue(p); + progress.setString((p / 10) + "%"); + spin(); + } + + public void message(String m) { + } + + public void sourceInfo(MultimediaInfo i) { + } +} diff --git a/src/uk/co/majenko/audiobookrecorder/Icons.java b/src/uk/co/majenko/audiobookrecorder/Icons.java index d82a267..4db759f 100644 --- a/src/uk/co/majenko/audiobookrecorder/Icons.java +++ b/src/uk/co/majenko/audiobookrecorder/Icons.java @@ -22,5 +22,9 @@ public class Icons { static public final ImageIcon appIcon = new ImageIcon(Icons.class.getResource("icons/appIcon.png")); static public final ImageIcon star = new ImageIcon(Icons.class.getResource("icons/star.png")); static public final ImageIcon important = new ImageIcon(Icons.class.getResource("icons/important.png")); + static public final ImageIcon spinner0 = new ImageIcon(Icons.class.getResource("icons/spinner0.png")); + static public final ImageIcon spinner1 = new ImageIcon(Icons.class.getResource("icons/spinner1.png")); + static public final ImageIcon spinner2 = new ImageIcon(Icons.class.getResource("icons/spinner2.png")); + static public final ImageIcon spinner3 = new ImageIcon(Icons.class.getResource("icons/spinner3.png")); } diff --git a/src/uk/co/majenko/audiobookrecorder/Sentence.java b/src/uk/co/majenko/audiobookrecorder/Sentence.java index e1ee121..f6df4a7 100644 --- a/src/uk/co/majenko/audiobookrecorder/Sentence.java +++ b/src/uk/co/majenko/audiobookrecorder/Sentence.java @@ -96,6 +96,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { try { AudioSystem.write(inputStream, AudioFileFormat.Type.WAVE, audioFile); } catch (Exception e) { + inputStream = null; e.printStackTrace(); } } @@ -112,8 +113,10 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { public void stopRecording() { try { inputStream.close(); + inputStream = null; line.stop(); line.close(); + line = null; } catch (Exception e) { e.printStackTrace(); }