Compare commits

..

7 Commits

17 changed files with 319 additions and 210 deletions

BIN
deps/json-20190722.jar LFS vendored Normal file

Binary file not shown.

View File

@@ -1 +1 @@
version=0.3.8 version=0.3.9

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -68,6 +68,7 @@ import javax.swing.JTree;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath; import javax.swing.tree.TreePath;
@@ -139,6 +140,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
JMenuItem toolsArchive; JMenuItem toolsArchive;
JMenuItem toolsCoverArt; JMenuItem toolsCoverArt;
JMenuItem toolsManuscript; JMenuItem toolsManuscript;
JMenuItem toolsReloadEffects;
JMenuItem toolsOptions; JMenuItem toolsOptions;
JMenuItem helpAbout; JMenuItem helpAbout;
@@ -147,7 +149,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
JPanel statusBar; JPanel statusBar;
JLabel statusLabel; NoiseFloor noiseFloorLabel;
JScrollPane mainScroll; JScrollPane mainScroll;
@@ -373,6 +375,18 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
} }
}); });
toolsReloadEffects = new JMenuItem("Reload effects");
toolsReloadEffects.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Debug.trace();
queueJob(new Runnable() {
public void run() {
loadEffects();
}
});
}
});
toolsOptions = new JMenuItem("Options"); toolsOptions = new JMenuItem("Options");
toolsOptions.addActionListener(new ActionListener() { toolsOptions.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
@@ -385,6 +399,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
toolsMenu.add(toolsArchive); toolsMenu.add(toolsArchive);
toolsMenu.add(toolsCoverArt); toolsMenu.add(toolsCoverArt);
toolsMenu.add(toolsManuscript); toolsMenu.add(toolsManuscript);
toolsMenu.add(toolsReloadEffects);
toolsMenu.addSeparator(); toolsMenu.addSeparator();
toolsMenu.add(toolsOptions); toolsMenu.add(toolsOptions);
@@ -752,11 +767,13 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
centralPanel.add(sampleControl, BorderLayout.SOUTH); centralPanel.add(sampleControl, BorderLayout.SOUTH);
statusBar = new JPanel(); statusBar = new JPanel();
statusBar.setLayout(new FlowLayout(FlowLayout.CENTER)); statusBar.setLayout(new FlowLayout(FlowLayout.RIGHT));
add(statusBar, BorderLayout.SOUTH); add(statusBar, BorderLayout.SOUTH);
statusLabel = new JLabel("Noise floor: " + getNoiseFloorDB() + "dB"); noiseFloorLabel = new NoiseFloor();
statusBar.add(statusLabel);
statusBar.add(noiseFloorLabel);
// statusBar.add(Box.createHorizontalStrut(2));
statusBar.add(queueMonitor); statusBar.add(queueMonitor);
buildToolbar(centralPanel); buildToolbar(centralPanel);
@@ -995,6 +1012,8 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
Options.savePreferences(); Options.savePreferences();
} }
queueJob(new VersionChecker());
} }
void bindKeys(JComponent component) { void bindKeys(JComponent component) {
@@ -1429,7 +1448,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
Debug.trace(); Debug.trace();
JMenuObject o = (JMenuObject)e.getSource(); JMenuObject o = (JMenuObject)e.getSource();
Sentence s = (Sentence)o.getObject(); Sentence s = (Sentence)o.getObject();
s.runExternalProcessor(Utils.s2i(o.getActionCommand())); queueJob(new SentenceExternalJob(s, Utils.s2i(o.getActionCommand())));
} }
}); });
ob.setActionCommand(Integer.toString(i)); ob.setActionCommand(Integer.toString(i));
@@ -1725,7 +1744,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
for (Enumeration s = c.children(); s.hasMoreElements();) { for (Enumeration s = c.children(); s.hasMoreElements();) {
Sentence snt = (Sentence)s.nextElement(); Sentence snt = (Sentence)s.nextElement();
if (!snt.isLocked()) { if (!snt.isLocked()) {
snt.runExternalProcessor(Utils.s2i(o.getActionCommand())); queueJob(new SentenceExternalJob(snt, Utils.s2i(o.getActionCommand())));
} }
} }
} }
@@ -2155,9 +2174,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
public void addChapter() { public void addChapter() {
Debug.trace(); Debug.trace();
Chapter c = book.addChapter(); Chapter c = book.addChapter();
Chapter lc = book.getLastChapter(); bookTreeModel.insertNodeInto(c, book, book.getChildCount());
int i = bookTreeModel.getIndexOfChild(book, lc);
bookTreeModel.insertNodeInto(c, book, i+1);
bookTree.scrollPathToVisible(new TreePath(c.getPath())); bookTree.scrollPathToVisible(new TreePath(c.getPath()));
} }
@@ -2212,11 +2229,11 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
bookTree.setEditable(true); bookTree.setEditable(true);
bookTree.setUI(new CustomTreeUI(mainScroll)); bookTree.setUI(new CustomTreeUI(mainScroll));
TreeCellRenderer renderer = new BookTreeRenderer();
try { try {
bookTree.setCellRenderer(new BookTreeRenderer()); bookTree.setCellRenderer(renderer);
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); bookTree.setCellRenderer(renderer);
bookTree.setCellRenderer(new BookTreeRenderer());
} }
InputMap im = bookTree.getInputMap(JComponent.WHEN_FOCUSED); InputMap im = bookTree.getInputMap(JComponent.WHEN_FOCUSED);
@@ -2315,7 +2332,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
bookTree.expandPath(new TreePath(book.getPath())); bookTree.expandPath(new TreePath(book.getPath()));
statusLabel.setText("Noise floor: " + getNoiseFloorDB() + "dB"); noiseFloorLabel.setNoiseFloor(getNoiseFloorDB());
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
@@ -2573,7 +2590,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
bookTree.expandPath(new TreePath(book.getPath())); bookTree.expandPath(new TreePath(book.getPath()));
statusLabel.setText("Noise floor: " + getNoiseFloorDB() + "dB"); noiseFloorLabel.setNoiseFloor(getNoiseFloorDB());
book.setIcon(Icons.book); book.setIcon(Icons.book);
} }
@@ -2646,7 +2663,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
Debug.trace(); Debug.trace();
roomNoise.stopRecording(); roomNoise.stopRecording();
centralPanel.setFlash(false); centralPanel.setFlash(false);
statusLabel.setText("Noise floor: " + getNoiseFloorDB() + "dB"); noiseFloorLabel.setNoiseFloor(getNoiseFloorDB());
} }
}, 5000); // 5 seconds of recording }, 5000); // 5 seconds of recording
} }
@@ -4180,6 +4197,12 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
} }
} }
} }
if (orphans.getChildCount() == 0) {
try {
bookTreeModel.removeNodeFromParent(orphans);
} catch (Exception ignored) {
}
}
} }
public Chapter getChapterById(String id) { public Chapter getChapterById(String id) {

View File

@@ -1,23 +1,5 @@
package uk.co.majenko.audiobookrecorder; package uk.co.majenko.audiobookrecorder;
// Biquad.java
//
// Created by Nigel Redmon on 11/24/12
// EarLevel Engineering: earlevel.com
// Copyright 2012 Nigel Redmon
// Translated to Java 2019 Majenko Technologies
//
// For a complete explanation of the Biquad code:
// http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
//
// License:
//
// This source code is provided as is, without warranty.
// You may copy and distribute verbatim copies of this document.
// You may modify and use this source code to create binary code
// for your own purposes, free or commercial.
//
import java.util.ArrayList; import java.util.ArrayList;
public class Biquad implements Effect { public class Biquad implements Effect {

View File

@@ -155,12 +155,7 @@ public class Book extends BookTreeNode {
public Chapter getLastChapter() { public Chapter getLastChapter() {
Debug.trace(); Debug.trace();
Chapter cc = getClosingCredits(); return (Chapter)getLastLeaf();
if (cc == null) return null;
Chapter c = (Chapter)getChildBefore(cc);
if (c == null) return null;
if (c.getId().equals("open")) return null;
return c;
} }
public Chapter getChapter(int n) { public Chapter getChapter(int n) {
@@ -453,7 +448,12 @@ public class Book extends BookTreeNode {
Debug.trace(); Debug.trace();
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
public void run() { public void run() {
AudiobookRecorder.window.bookTreeModel.reload(Book.this); if (AudiobookRecorder.window == null) return;
if (AudiobookRecorder.window.bookTreeModel == null) return;
try {
AudiobookRecorder.window.bookTreeModel.reload(Book.this);
} catch (Exception ignored) {
}
} }
}); });
} }

View File

@@ -1,130 +1,93 @@
package uk.co.majenko.audiobookrecorder; package uk.co.majenko.audiobookrecorder;
/**
* @author Orlando Selenu
*
*/
public class FFT { public class FFT {
/** public static double[] fft(final double[] inputReal, double[] inputImag, boolean DIRECT) {
* The Fast Fourier Transform (generic version, with NO optimizations). int n = inputReal.length;
*
* @param inputReal
* an array of length n, the real part
* @param inputImag
* an array of length n, the imaginary part
* @param DIRECT
* TRUE = direct transform, FALSE = inverse transform
* @return a new array of length 2n
*/
public static double[] fft(final double[] inputReal, double[] inputImag,
boolean DIRECT) {
// - n is the dimension of the problem
// - nu is its logarithm in base e
int n = inputReal.length;
// If n is a power of 2, then ld is an integer (_without_ decimals) double ld = Math.log(n) / Math.log(2.0);
double ld = Math.log(n) / Math.log(2.0);
// Here I check if n is a power of 2. If exist decimals in ld, I quit if (((int) ld) - ld != 0) {
// from the function returning null. System.out.println("The number of elements is not a power of 2.");
if (((int) ld) - ld != 0) { return null;
System.out.println("The number of elements is not a power of 2."); }
return null;
}
// Declaration and initialization of the variables int nu = (int) ld;
// ld should be an integer, actually, so I don't lose any information in int n2 = n / 2;
// the cast int nu1 = nu - 1;
int nu = (int) ld; double[] xReal = new double[n];
int n2 = n / 2; double[] xImag = new double[n];
int nu1 = nu - 1; double tReal, tImag, p, arg, c, s;
double[] xReal = new double[n];
double[] xImag = new double[n];
double tReal, tImag, p, arg, c, s;
// Here I check if I'm going to do the direct transform or the inverse double constant;
// transform. if (DIRECT) {
double constant; constant = -2 * Math.PI;
if (DIRECT) } else {
constant = -2 * Math.PI; constant = 2 * Math.PI;
else }
constant = 2 * Math.PI;
// I don't want to overwrite the input arrays, so here I copy them. This for (int i = 0; i < n; i++) {
// choice adds \Theta(2n) to the complexity. xReal[i] = inputReal[i];
for (int i = 0; i < n; i++) { xImag[i] = inputImag[i];
xReal[i] = inputReal[i]; }
xImag[i] = inputImag[i];
}
// First phase - calculation int k = 0;
int k = 0; for (int l = 1; l <= nu; l++) {
for (int l = 1; l <= nu; l++) { while (k < n) {
while (k < n) { for (int i = 1; i <= n2; i++) {
for (int i = 1; i <= n2; i++) { p = bitreverseReference(k >> nu1, nu);
p = bitreverseReference(k >> nu1, nu); // direct FFT or inverse FFT
// direct FFT or inverse FFT arg = constant * p / n;
arg = constant * p / n; c = Math.cos(arg);
c = Math.cos(arg); s = Math.sin(arg);
s = Math.sin(arg); tReal = xReal[k + n2] * c + xImag[k + n2] * s;
tReal = xReal[k + n2] * c + xImag[k + n2] * s; tImag = xImag[k + n2] * c - xReal[k + n2] * s;
tImag = xImag[k + n2] * c - xReal[k + n2] * s; xReal[k + n2] = xReal[k] - tReal;
xReal[k + n2] = xReal[k] - tReal; xImag[k + n2] = xImag[k] - tImag;
xImag[k + n2] = xImag[k] - tImag; xReal[k] += tReal;
xReal[k] += tReal; xImag[k] += tImag;
xImag[k] += tImag; k++;
k++; }
k += n2;
} }
k += n2; k = 0;
nu1--;
n2 /= 2;
} }
k = 0; k = 0;
nu1--; int r;
n2 /= 2; while (k < n) {
} r = bitreverseReference(k, nu);
if (r > k) {
// Second phase - recombination tReal = xReal[k];
k = 0; tImag = xImag[k];
int r; xReal[k] = xReal[r];
while (k < n) { xImag[k] = xImag[r];
r = bitreverseReference(k, nu); xReal[r] = tReal;
if (r > k) { xImag[r] = tImag;
tReal = xReal[k]; }
tImag = xImag[k]; k++;
xReal[k] = xReal[r];
xImag[k] = xImag[r];
xReal[r] = tReal;
xImag[r] = tImag;
} }
k++;
double[] newArray = new double[xReal.length * 2];
double radice = 1 / Math.sqrt(n);
for (int i = 0; i < newArray.length; i += 2) {
int i2 = i / 2;
newArray[i] = xReal[i2] * radice;
newArray[i + 1] = xImag[i2] * radice;
}
return newArray;
} }
// Here I have to mix xReal and xImag to have an array (yes, it should private static int bitreverseReference(int j, int nu) {
// be possible to do this stuff in the earlier parts of the code, but int j2;
// it's here to readibility). int j1 = j;
double[] newArray = new double[xReal.length * 2]; int k = 0;
double radice = 1 / Math.sqrt(n); for (int i = 1; i <= nu; i++) {
for (int i = 0; i < newArray.length; i += 2) { j2 = j1 / 2;
int i2 = i / 2; k = 2 * k + j1 - 2 * j2;
// I used Stephen Wolfram's Mathematica as a reference so I'm going j1 = j2;
// to normalize the output while I'm copying the elements. }
newArray[i] = xReal[i2] * radice; return k;
newArray[i + 1] = xImag[i2] * radice;
} }
return newArray;
}
/**
* The reference bitreverse function.
*/
private static int bitreverseReference(int j, int nu) {
int j2;
int j1 = j;
int k = 0;
for (int i = 1; i <= nu; i++) {
j2 = j1 / 2;
k = 2 * k + j1 - 2 * j2;
j1 = j2;
}
return k;
}
} }

View File

@@ -42,4 +42,5 @@ public class Icons {
static public final ImageIcon tooltip = new ImageIcon(Icons.class.getResource("icons/tooltip.png")); static public final ImageIcon tooltip = new ImageIcon(Icons.class.getResource("icons/tooltip.png"));
static public final ImageIcon queued = new ImageIcon(Icons.class.getResource("icons/queued.png")); static public final ImageIcon queued = new ImageIcon(Icons.class.getResource("icons/queued.png"));
static public final ImageIcon processing = new ImageIcon(Icons.class.getResource("icons/processing.png")); static public final ImageIcon processing = new ImageIcon(Icons.class.getResource("icons/processing.png"));
static public final ImageIcon close = new ImageIcon(Icons.class.getResource("icons/close.png"));
} }

View File

@@ -14,10 +14,6 @@ public class KVPair<K,V> implements Comparable {
} }
public int compareTo(Object o) { public int compareTo(Object o) {
// if (o instanceof KVPair) {
// KVPair ko = (KVPair)o;
// return key.compareTo(ko.key);
// }
return 0; return 0;
} }

View File

@@ -19,7 +19,6 @@ public class MainToolBar extends JToolBar {
JButtonSpacePlay playonSentence; JButtonSpacePlay playonSentence;
JButtonSpacePlay playtoSentence; JButtonSpacePlay playtoSentence;
JButtonSpacePlay stopPlaying; JButtonSpacePlay stopPlaying;
JButtonSpacePlay eq;
JButtonSpacePlay openManuscript; JButtonSpacePlay openManuscript;
JToggleButtonSpacePlay mic; JToggleButtonSpacePlay mic;
@@ -102,16 +101,6 @@ public class MainToolBar extends JToolBar {
}); });
add(stopPlaying); add(stopPlaying);
addSeparator();
eq = new JButtonSpacePlay(Icons.eq, "Reload Effects", new ActionListener() {
public void actionPerformed(ActionEvent e) {
root.loadEffects();
CacheManager.purgeCache();
}
});
add(eq);
addSeparator(); addSeparator();
mic = new JToggleButtonSpacePlay(Icons.mic, "Enable / disable microphone", new ActionListener() { mic = new JToggleButtonSpacePlay(Icons.mic, "Enable / disable microphone", new ActionListener() {

View File

@@ -0,0 +1,51 @@
package uk.co.majenko.audiobookrecorder;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Dimension;
import java.awt.Color;
public class NoiseFloor extends JPanel {
int noiseFloor = 0;
public NoiseFloor() {
super();
noiseFloor = 0;
}
public void setNoiseFloor(int n) {
noiseFloor = n;
repaint();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(128, 24);
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
@Override
public Dimension getMaximumSize() {
return getPreferredSize();
}
@Override
public void paintComponent(Graphics g) {
Rectangle size = g.getClipBounds();
g.setColor(getBackground());
g.fillRect(0, 0, size.width - 1, size.height - 1);
g.setColor(new Color(10, 10, 10));
g.drawRect(0, 0, size.width - 1, size.height - 1);
g.setFont(getFont());
g.setColor(getForeground());
g.drawString("Noise Floor: " + noiseFloor + "dB", 6, 16);
}
}

View File

@@ -388,7 +388,7 @@ public class Options extends JDialog {
enableParsing = addCheckBox(optionsPanel, "Enable automatic speech-to-text (**SLOW**)", getBoolean("process.sphinx"), "This will automatically start recognising the speech in every sentence you record. This can really slow down recording though so it's recommended to leave it turned off and do your recognition afterwards as a batch operation."); enableParsing = addCheckBox(optionsPanel, "Enable automatic speech-to-text (**SLOW**)", getBoolean("process.sphinx"), "This will automatically start recognising the speech in every sentence you record. This can really slow down recording though so it's recommended to leave it turned off and do your recognition afterwards as a batch operation.");
speechCommand = addTextField(optionsPanel, "Speech to text command (must take 1 filename parameter):", get("process.command"), "This specifies what command to run to recognize the speech. This command must take only one parameter, which is the full path of the WAV file. It should return (on standard output) the recognised speech."); speechCommand = addTextField(optionsPanel, "Speech to text command (must take 1 filename parameter):", get("process.command"), "This specifies what command to run to recognize the speech. This command must take only one parameter, which is the full path of the WAV file. It should return (on standard output) the recognised speech.");
workerThreads = addSpinner(optionsPanel, "Worker threads:", 1, 100, 1, getInteger("process.threads"), "", "How many concurrent threads to run when processing speech. This should ideally be no more than the number of CPU cores you have in your computer."); workerThreads = addSpinner(optionsPanel, "Worker threads:", 1, 100, 1, getInteger("process.threads"), "", "How many concurrent threads to run when processing speech. This should ideally be no more than the number of CPU cores you have in your computer minus one.");
addSeparator(optionsPanel); addSeparator(optionsPanel);

View File

@@ -1,33 +1,3 @@
/*
* Copyright (c) 2015, Majenko Technologies
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* * Neither the name of Majenko Technologies nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package uk.co.majenko.audiobookrecorder; package uk.co.majenko.audiobookrecorder;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;

View File

@@ -7,15 +7,18 @@ import java.awt.Graphics;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Color; import java.awt.Color;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class QueueMonitor extends JPanel { public class QueueMonitor extends JPanel implements MouseListener {
ArrayList<WorkerThread> threadList = new ArrayList<WorkerThread>(); ArrayList<WorkerThread> threadList = new ArrayList<WorkerThread>();
Queue queue; Queue<Runnable> queue;
public QueueMonitor(Queue q) { public QueueMonitor(Queue<Runnable> q) {
super(); super();
queue = q; queue = q;
addMouseListener(this);
} }
public void addThread(WorkerThread t) { public void addThread(WorkerThread t) {
@@ -23,15 +26,22 @@ public class QueueMonitor extends JPanel {
} }
public void purgeQueue() { public void purgeQueue() {
Runnable work;
synchronized (queue) { synchronized (queue) {
queue.clear(); while (queue.size() > 0) {
work = queue.remove();
if (work instanceof SentenceJob) {
SentenceJob sj = (SentenceJob)work;
sj.setDequeued();
}
}
repaint(); repaint();
} }
} }
@Override @Override
public Dimension getPreferredSize() { public Dimension getPreferredSize() {
return new Dimension(100 + (24 * threadList.size()), 24); return new Dimension(150 + (24 * threadList.size()), 24);
} }
@Override @Override
@@ -65,6 +75,35 @@ public class QueueMonitor extends JPanel {
g.setColor(getForeground()); g.setColor(getForeground());
g.drawString("Queued: " + queue.size(), threadList.size() * 24 + 4, 16); g.drawString("Queued: " + queue.size(), threadList.size() * 24 + 4, 16);
if (queue.size() > 0) {
Icons.close.paintIcon(this, g, size.width - 23, 1);
}
}
@Override
public void mouseEntered(MouseEvent evt) {
}
@Override
public void mouseExited(MouseEvent evt) {
}
@Override
public void mousePressed(MouseEvent evt) {
}
@Override
public void mouseReleased(MouseEvent evt) {
}
@Override
public void mouseClicked(MouseEvent evt) {
if (queue.size() == 0) return; // No button - ignore it
Dimension size = getPreferredSize();
if (evt.getX() > (size.width - 24)) {
purgeQueue();
}
} }
} }

View File

@@ -956,8 +956,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
public void openInExternalEditor() { public void openInExternalEditor() {
Debug.trace(); Debug.trace();
ExternalEditor ed = new ExternalEditor(this); ExternalEditor ed = new ExternalEditor(this);
Thread t = new Thread(ed); ed.run();
t.start();
} }
public void backup() throws IOException { public void backup() throws IOException {
@@ -1051,8 +1050,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
Debug.trace(); Debug.trace();
if (isLocked()) return; if (isLocked()) return;
ExternalProcessor ed = new ExternalProcessor(this, num); ExternalProcessor ed = new ExternalProcessor(this, num);
Thread t = new Thread(ed); ed.run();
t.start();
} }
public void undo() { public void undo() {

View File

@@ -0,0 +1,17 @@
package uk.co.majenko.audiobookrecorder;
import java.lang.Runnable;
public class SentenceExternalJob extends SentenceJob {
protected int command;
public SentenceExternalJob(Sentence s, int c) {
super(s);
command = c;
}
@Override
public void run() {
sentence.runExternalProcessor(command);
}
}

View File

@@ -0,0 +1,77 @@
package uk.co.majenko.audiobookrecorder;
import java.lang.Runnable;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import org.json.JSONObject;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class VersionChecker implements Runnable {
public VersionChecker() {
}
public void run() {
try {
URL url = new URL("https://api.github.com/repos/MajenkoProjects/AudiobookRecorder/releases/latest");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
InputStream is = conn.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String inputLine;
StringBuilder jsonData = new StringBuilder();
while ((inputLine = br.readLine()) != null) {
jsonData.append(inputLine);
}
br.close();
JSONObject job = new JSONObject(jsonData.toString());
String installed = AudiobookRecorder.config.getProperty("version");
String available = job.getString("tag_name");
if (available.startsWith("v")) {
available = available.substring(1);
}
String website = job.getString("html_url");
String[] installedParts = installed.split("\\.");
String[] availableParts = available.split("\\.");
// Must be x.y.z
if (installedParts.length != 3) return;
if (availableParts.length != 3) return;
// Convert to xxxyyyzzz
String installedVersion = String.format("%03d%03d%03d", Utils.s2i(installedParts[0]), Utils.s2i(installedParts[1]), Utils.s2i(installedParts[2]));
String availableVersion = String.format("%03d%03d%03d", Utils.s2i(availableParts[0]), Utils.s2i(availableParts[1]), Utils.s2i(availableParts[2]));
if (Utils.s2i(installedVersion) >= Utils.s2i(availableVersion)) return;
System.err.println("Version installed: " + installed);
System.err.println("Version available: " + available);
System.err.println("URL: " + website);
JButton upgrade = new JButton("A new version is available.");
upgrade.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
Utils.browse(website);
}
});
AudiobookRecorder.window.statusBar.add(upgrade);
AudiobookRecorder.window.statusBar.revalidate();
} catch (Exception ignored) {
ignored.printStackTrace();
}
}
}