Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 21d7d9d597 | |||
| 746f47a5fa | |||
| c67e6d6abc | |||
| 1f722f5df3 | |||
| df4eae1d66 | |||
| 4d435b4fc1 | |||
| f95ae10d03 | |||
| 423d840d83 | |||
| 1997b0bf9b | |||
| b206fb33aa | |||
| 11b26e396c | |||
| 94139e6ac6 | |||
| c0cc2432ff | |||
| f86aaa3782 |
38
README.md
38
README.md
@@ -29,12 +29,13 @@ From here on much is controlled by key presses.
|
|||||||
appended to the currently selected chapter, or to the last chapter if none is selected.
|
appended to the currently selected chapter, or to the last chapter if none is selected.
|
||||||
* Press and hold "T" to record a new phrase that is the start of a new paragraph. This adds the "post paragraph" gap to the previous sentence. Otherwise it does the same as "R".
|
* Press and hold "T" to record a new phrase that is the start of a new paragraph. This adds the "post paragraph" gap to the previous sentence. Otherwise it does the same as "R".
|
||||||
* Press and hold "F" to record a "continuation" phrase. This sets the previous phrase's post-gap to be the "short" gap instead of the normal length gap.
|
* Press and hold "F" to record a "continuation" phrase. This sets the previous phrase's post-gap to be the "short" gap instead of the normal length gap.
|
||||||
|
* Press and hold "Y" to record a new phrase that is the start of a new section. This add the "post section" gap to the previous sentence. Otherwise it does the same as "R".
|
||||||
* Press "D" to delete the last phrase you recorded.
|
* Press "D" to delete the last phrase you recorded.
|
||||||
* Press "E" to re-record the currently selected phrase.
|
* Press "E" to re-record the currently selected phrase.
|
||||||
|
|
||||||
Each phrase you record will be briefly analysed using FFT to find the start and end of the audio and set
|
Each phrase you record can be automatically analysed to find the start and end of the audio and set
|
||||||
crop marks appropriately. These can be adjusted in the waveform display when a phrase is selected. You can also
|
crop marks appropriately. These can be adjusted in the waveform display when a phrase is selected. You can also
|
||||||
re-run the analysis using either the default FFT method or using a peak detector method (finding the first and last points
|
re-run the analysis using either FFT or a peak detector method (finding the first and last points
|
||||||
where the audio amplitude rises above the backround noise).
|
where the audio amplitude rises above the backround noise).
|
||||||
|
|
||||||
The phrases also have a "post gap" associated with them. This is the amount of room noise (in milliseconds) to place between
|
The phrases also have a "post gap" associated with them. This is the amount of room noise (in milliseconds) to place between
|
||||||
@@ -53,18 +54,14 @@ edit the text of this ID to identify the recordings. You
|
|||||||
may, for instance, change it to have the same text as the
|
may, for instance, change it to have the same text as the
|
||||||
audio contains.
|
audio contains.
|
||||||
|
|
||||||
To help with this the Haven On-Demand online speech recognition
|
The audio can also be automatically converted to text if you have an suitable command-line
|
||||||
service is integrated with the system and can be used to try and convert the
|
executable that will work. One example is (on Linux) [DeepSpeech](https://github.com/mozilla/DeepSpeech) by Mozilla.
|
||||||
audio into text. Right clicking on a recording brings
|
|
||||||
up a menu which includes the option to try and convert
|
|
||||||
the audio into text. The detected text is then used to
|
|
||||||
replace the current recording ID / text.
|
|
||||||
|
|
||||||
File layout
|
File layout
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
All data is stored in your "storage" directory (specified in Options). Each book (which is a directory named after the
|
All data is stored in your "storage" directory (specified in Options). Each book (which is a directory named after the
|
||||||
title of the book) has an associated XML file (audiobook.abk) and a directory "files" where all the audio (stored as WAV
|
title of the book) has an associated XML file (audiobook.abx) and a directory "files" where all the audio (stored as WAV
|
||||||
files) is placed.
|
files) is placed.
|
||||||
|
|
||||||
When you export the book as MP3 a new folder "export" is created within the book's folder where the MP3 files are placed.
|
When you export the book as MP3 a new folder "export" is created within the book's folder where the MP3 files are placed.
|
||||||
@@ -81,3 +78,26 @@ Building
|
|||||||
5. Build with `ant build`
|
5. Build with `ant build`
|
||||||
6. Run with `java -jar ./AudiobookRecorder.jar`
|
6. Run with `java -jar ./AudiobookRecorder.jar`
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Extra Resources
|
||||||
|
===============
|
||||||
|
|
||||||
|
* DeepSpeech wrapper script
|
||||||
|
|
||||||
|
This is a small script that will convert the audio into a format DeepSpeech likes and call the `deepspeech` executable, removing any extra rubbish from the output. It
|
||||||
|
also requires `sox` to be installed for the audio conversion.
|
||||||
|
|
||||||
|
```
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
ID=$$
|
||||||
|
FILE=$1
|
||||||
|
BINPATH=${HOME}/local/bin
|
||||||
|
MODELS=${HOME}/ds/deepspeech-0.6.1-models
|
||||||
|
|
||||||
|
sox "$FILE" -r 16000 -c 1 -b 16 "/tmp/ds-${ID}.wav"
|
||||||
|
${BINPATH}/deepspeech --model ${MODELS}/output_graph.pbmm --lm ${MODELS}/lm.binary --trie ${MODELS}/trie --audio "/tmp/ds-${ID}.wav" 2>/dev/null
|
||||||
|
rm /tmp/ds-${ID}.wav
|
||||||
|
```
|
||||||
|
|||||||
BIN
deps/json-20190722.jar
LFS
vendored
Normal file
BIN
deps/json-20190722.jar
LFS
vendored
Normal file
Binary file not shown.
@@ -1 +1 @@
|
|||||||
version=0.3.6
|
version=0.3.9
|
||||||
|
|||||||
BIN
resources/uk/co/majenko/audiobookrecorder/icons/close.png
Normal file
BIN
resources/uk/co/majenko/audiobookrecorder/icons/close.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/uk/co/majenko/audiobookrecorder/icons/processing.png
Normal file
BIN
resources/uk/co/majenko/audiobookrecorder/icons/processing.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 489 B |
BIN
resources/uk/co/majenko/audiobookrecorder/icons/queued.png
Normal file
BIN
resources/uk/co/majenko/audiobookrecorder/icons/queued.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 717 B |
@@ -11,6 +11,7 @@ import java.util.Enumeration;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -18,6 +19,7 @@ import java.util.zip.ZipEntry;
|
|||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.awt.FlowLayout;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Desktop;
|
import java.awt.Desktop;
|
||||||
@@ -66,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;
|
||||||
@@ -137,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;
|
||||||
@@ -145,7 +149,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
|
|
||||||
JPanel statusBar;
|
JPanel statusBar;
|
||||||
|
|
||||||
JLabel statusLabel;
|
NoiseFloor noiseFloorLabel;
|
||||||
|
|
||||||
JScrollPane mainScroll;
|
JScrollPane mainScroll;
|
||||||
|
|
||||||
@@ -177,6 +181,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
|
|
||||||
JSpinner postSentenceGap;
|
JSpinner postSentenceGap;
|
||||||
JSpinner gainPercent;
|
JSpinner gainPercent;
|
||||||
|
Timer waveformUpdater = new Timer();
|
||||||
JCheckBox locked;
|
JCheckBox locked;
|
||||||
JCheckBox attention;
|
JCheckBox attention;
|
||||||
JCheckBox rawAudio;
|
JCheckBox rawAudio;
|
||||||
@@ -203,7 +208,8 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
|
|
||||||
public static AudiobookRecorder window;
|
public static AudiobookRecorder window;
|
||||||
|
|
||||||
public Queue<Runnable>speechProcessQueue = null;
|
public Queue<Runnable>processQueue = null;
|
||||||
|
public QueueMonitor queueMonitor = null;
|
||||||
|
|
||||||
void buildToolbar(Container ob) {
|
void buildToolbar(Container ob) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
@@ -369,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) {
|
||||||
@@ -381,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);
|
||||||
|
|
||||||
@@ -419,7 +438,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
Debug.debugEnabled = CLI.isSet("debug");
|
Debug.debugEnabled = CLI.isSet("debug");
|
||||||
Debug.traceEnabled = CLI.isSet("trace");
|
Debug.traceEnabled = CLI.isSet("trace");
|
||||||
|
|
||||||
speechProcessQueue = new ArrayDeque<Runnable>();
|
processQueue = new ArrayDeque<Runnable>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@@ -444,8 +463,11 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
|
|
||||||
Options.loadPreferences();
|
Options.loadPreferences();
|
||||||
|
|
||||||
|
queueMonitor = new QueueMonitor(processQueue);
|
||||||
|
|
||||||
for (int i = 0; i < Options.getInteger("process.threads"); i++) {
|
for (int i = 0; i < Options.getInteger("process.threads"); i++) {
|
||||||
WorkerThread worker = new WorkerThread(speechProcessQueue);
|
WorkerThread worker = new WorkerThread(processQueue, queueMonitor);
|
||||||
|
queueMonitor.addThread(worker);
|
||||||
worker.start();
|
worker.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,11 +551,12 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
if (selectedSentence != null) {
|
if (selectedSentence != null) {
|
||||||
selectedSentence.autoTrimSampleFFT();
|
queueJob(new SentenceJob(selectedSentence) {
|
||||||
sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset());
|
public void run() {
|
||||||
sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing());
|
sentence.autoTrimSampleFFT();
|
||||||
postSentenceGap.setValue(selectedSentence.getPostGap());
|
updateWaveformMarkers();
|
||||||
gainPercent.setValue((int)(selectedSentence.getGain() * 100d));
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -542,11 +565,12 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
if (selectedSentence != null) {
|
if (selectedSentence != null) {
|
||||||
selectedSentence.autoTrimSamplePeak();
|
queueJob(new SentenceJob(selectedSentence) {
|
||||||
sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset());
|
public void run() {
|
||||||
sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing());
|
sentence.autoTrimSamplePeak();
|
||||||
postSentenceGap.setValue(selectedSentence.getPostGap());
|
updateWaveformMarkers();
|
||||||
gainPercent.setValue((int)(selectedSentence.getGain() * 100d));
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -654,8 +678,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
doCutSplit.setEnabled(false);
|
doCutSplit.setEnabled(false);
|
||||||
selectCutMode.setSelected(false);
|
selectCutMode.setSelected(false);
|
||||||
selectSplitMode.setSelected(false);
|
selectSplitMode.setSelected(false);
|
||||||
|
selectedSentence.reloadTree();
|
||||||
bookTreeModel.reload(selectedSentence);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
controlsTop.add(locked);
|
controlsTop.add(locked);
|
||||||
@@ -676,7 +699,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
selectedSentence.setAttentionFlag(false);
|
selectedSentence.setAttentionFlag(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bookTreeModel.reload(selectedSentence);
|
selectedSentence.reloadTree();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -744,10 +767,14 @@ 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.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);
|
||||||
|
|
||||||
buildToolbar(centralPanel);
|
buildToolbar(centralPanel);
|
||||||
|
|
||||||
@@ -931,12 +958,8 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
Debug.trace();
|
Debug.trace();
|
||||||
if (ev.getPropertyName().equals("dividerLocation")) {
|
if (ev.getPropertyName().equals("dividerLocation")) {
|
||||||
if ((bookTreeModel != null) && (book != null)) {
|
if ((bookTreeModel != null) && (book != null)) {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
Debug.trace();
|
||||||
public void run() {
|
book.reloadTree();
|
||||||
Debug.trace();
|
|
||||||
bookTreeModel.reload(book);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -989,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) {
|
||||||
@@ -1039,6 +1064,8 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
|
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
|
Properties props = System.getProperties();
|
||||||
|
props.setProperty("sun.java2d.opengl", "true");
|
||||||
try {
|
try {
|
||||||
config.load(AudiobookRecorder.class.getResourceAsStream("config.txt"));
|
config.load(AudiobookRecorder.class.getResourceAsStream("config.txt"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -1191,10 +1218,11 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
Sentence snt = (Sentence)s.nextElement();
|
Sentence snt = (Sentence)s.nextElement();
|
||||||
if (!snt.isLocked()) {
|
if (!snt.isLocked()) {
|
||||||
if (snt.getId().equals(snt.getText())) {
|
if (snt.getId().equals(snt.getText())) {
|
||||||
Runnable r = snt.getRecognitionRunnable();
|
queueJob(new SentenceJob(snt) {
|
||||||
snt.setQueued();
|
public void run() {
|
||||||
speechProcessQueue.add(r);
|
sentence.doRecognition();
|
||||||
speechProcessQueue.notify();
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1227,7 +1255,11 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
JMenuObject o = (JMenuObject)e.getSource();
|
JMenuObject o = (JMenuObject)e.getSource();
|
||||||
Sentence s = (Sentence)o.getObject();
|
Sentence s = (Sentence)o.getObject();
|
||||||
if (!s.isLocked()) {
|
if (!s.isLocked()) {
|
||||||
s.recognise();
|
queueJob(new SentenceJob(s) {
|
||||||
|
public void run() {
|
||||||
|
sentence.doRecognition();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1304,7 +1336,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
String type = (String)o.getObject2();
|
String type = (String)o.getObject2();
|
||||||
sent.setPostGapType(type);
|
sent.setPostGapType(type);
|
||||||
sent.setPostGap(Options.getInteger("catenation.post-sentence"));
|
sent.setPostGap(Options.getInteger("catenation.post-sentence"));
|
||||||
bookTreeModel.reload(sent);
|
sent.reloadTree();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setGapType.add(gapTypeSentence);
|
setGapType.add(gapTypeSentence);
|
||||||
@@ -1317,7 +1349,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
String type = (String)o.getObject2();
|
String type = (String)o.getObject2();
|
||||||
sent.setPostGapType(type);
|
sent.setPostGapType(type);
|
||||||
sent.setPostGap(Options.getInteger("catenation.short-sentence"));
|
sent.setPostGap(Options.getInteger("catenation.short-sentence"));
|
||||||
bookTreeModel.reload(sent);
|
sent.reloadTree();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setGapType.add(gapTypeContinuation);
|
setGapType.add(gapTypeContinuation);
|
||||||
@@ -1330,7 +1362,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
String type = (String)o.getObject2();
|
String type = (String)o.getObject2();
|
||||||
sent.setPostGapType(type);
|
sent.setPostGapType(type);
|
||||||
sent.setPostGap(Options.getInteger("catenation.post-paragraph"));
|
sent.setPostGap(Options.getInteger("catenation.post-paragraph"));
|
||||||
bookTreeModel.reload(sent);
|
sent.reloadTree();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setGapType.add(gapTypeParagraph);
|
setGapType.add(gapTypeParagraph);
|
||||||
@@ -1343,7 +1375,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
String type = (String)o.getObject2();
|
String type = (String)o.getObject2();
|
||||||
sent.setPostGapType(type);
|
sent.setPostGapType(type);
|
||||||
sent.setPostGap(Options.getInteger("catenation.post-section"));
|
sent.setPostGap(Options.getInteger("catenation.post-section"));
|
||||||
bookTreeModel.reload(sent);
|
sent.reloadTree();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setGapType.add(gapTypeSection);
|
setGapType.add(gapTypeSection);
|
||||||
@@ -1416,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));
|
||||||
@@ -1472,14 +1504,19 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
JMenuObject o = (JMenuObject)e.getSource();
|
JMenuObject o = (JMenuObject)e.getSource();
|
||||||
Chapter chap = (Chapter)o.getObject();
|
Chapter c = (Chapter)o.getObject();
|
||||||
|
for (Enumeration s = c.children(); s.hasMoreElements();) {
|
||||||
|
Sentence snt = (Sentence)s.nextElement();
|
||||||
|
if (!snt.isProcessed()) {
|
||||||
|
queueJob(new SentenceJob(snt) {
|
||||||
|
public void run() {
|
||||||
|
sentence.autoTrimSampleFFT();
|
||||||
|
updateWaveformMarkers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1487,14 +1524,18 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
JMenuObject o = (JMenuObject)e.getSource();
|
JMenuObject o = (JMenuObject)e.getSource();
|
||||||
Chapter chap = (Chapter)o.getObject();
|
Chapter c = (Chapter)o.getObject();
|
||||||
|
for (Enumeration s = c.children(); s.hasMoreElements();) {
|
||||||
ProgressDialog ed = new ProgressDialog("Auto-trimming " + chap.getName());
|
Sentence snt = (Sentence)s.nextElement();
|
||||||
|
if (!snt.isProcessed()) {
|
||||||
AutoTrimThread t = new AutoTrimThread(chap, ed, AutoTrimThread.FFT, AutoTrimThread.NewOnly);
|
queueJob(new SentenceJob(snt) {
|
||||||
Thread nt = new Thread(t);
|
public void run() {
|
||||||
nt.start();
|
sentence.autoTrimSampleFFT();
|
||||||
ed.setVisible(true);
|
updateWaveformMarkers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1502,14 +1543,16 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
JMenuObject o = (JMenuObject)e.getSource();
|
JMenuObject o = (JMenuObject)e.getSource();
|
||||||
Chapter chap = (Chapter)o.getObject();
|
Chapter c = (Chapter)o.getObject();
|
||||||
|
for (Enumeration s = c.children(); s.hasMoreElements();) {
|
||||||
ProgressDialog ed = new ProgressDialog("Auto-trimming " + chap.getName());
|
Sentence snt = (Sentence)s.nextElement();
|
||||||
|
queueJob(new SentenceJob(snt) {
|
||||||
AutoTrimThread t = new AutoTrimThread(chap, ed, AutoTrimThread.Peak, AutoTrimThread.All);
|
public void run() {
|
||||||
Thread nt = new Thread(t);
|
sentence.autoTrimSamplePeak();
|
||||||
nt.start();
|
updateWaveformMarkers();
|
||||||
ed.setVisible(true);
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1517,14 +1560,16 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
JMenuObject o = (JMenuObject)e.getSource();
|
JMenuObject o = (JMenuObject)e.getSource();
|
||||||
Chapter chap = (Chapter)o.getObject();
|
Chapter c = (Chapter)o.getObject();
|
||||||
|
for (Enumeration s = c.children(); s.hasMoreElements();) {
|
||||||
ProgressDialog ed = new ProgressDialog("Auto-trimming " + chap.getName());
|
Sentence snt = (Sentence)s.nextElement();
|
||||||
|
queueJob(new SentenceJob(snt) {
|
||||||
AutoTrimThread t = new AutoTrimThread(chap, ed, AutoTrimThread.FFT, AutoTrimThread.All);
|
public void run() {
|
||||||
Thread nt = new Thread(t);
|
sentence.autoTrimSampleFFT();
|
||||||
nt.start();
|
updateWaveformMarkers();
|
||||||
ed.setVisible(true);
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1597,7 +1642,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();
|
||||||
snt.setLocked(true);
|
snt.setLocked(true);
|
||||||
bookTreeModel.reload(snt);
|
snt.reloadTree();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1610,7 +1655,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();
|
||||||
snt.setLocked(false);
|
snt.setLocked(false);
|
||||||
bookTreeModel.reload(snt);
|
snt.reloadTree();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1658,13 +1703,11 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
Sentence snt = (Sentence)s.nextElement();
|
Sentence snt = (Sentence)s.nextElement();
|
||||||
if (!snt.isLocked()) {
|
if (!snt.isLocked()) {
|
||||||
if (!snt.beenDetected()) {
|
if (!snt.beenDetected()) {
|
||||||
Debug.d("Queueing recognition of", snt.getId());
|
queueJob(new SentenceJob(snt) {
|
||||||
synchronized(speechProcessQueue) {
|
public void run() {
|
||||||
Runnable r = snt.getRecognitionRunnable();
|
sentence.doRecognition();
|
||||||
snt.setQueued();
|
}
|
||||||
speechProcessQueue.add(r);
|
});
|
||||||
speechProcessQueue.notify();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1701,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())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2073,7 +2116,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
if (recording == null) return;
|
if (recording == null) return;
|
||||||
recording.stopRecording();
|
recording.stopRecording();
|
||||||
|
|
||||||
bookTreeModel.reload(book);
|
// book.reloadTree();
|
||||||
|
|
||||||
bookTree.expandPath(new TreePath(((DefaultMutableTreeNode)recording.getParent()).getPath()));
|
bookTree.expandPath(new TreePath(((DefaultMutableTreeNode)recording.getParent()).getPath()));
|
||||||
bookTree.setSelectionPath(new TreePath(recording.getPath()));
|
bookTree.setSelectionPath(new TreePath(recording.getPath()));
|
||||||
@@ -2131,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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2188,8 +2229,12 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
bookTree.setEditable(true);
|
bookTree.setEditable(true);
|
||||||
bookTree.setUI(new CustomTreeUI(mainScroll));
|
bookTree.setUI(new CustomTreeUI(mainScroll));
|
||||||
|
|
||||||
bookTree.setCellRenderer(new BookTreeRenderer());
|
TreeCellRenderer renderer = new BookTreeRenderer();
|
||||||
|
try {
|
||||||
|
bookTree.setCellRenderer(renderer);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
bookTree.setCellRenderer(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
InputMap im = bookTree.getInputMap(JComponent.WHEN_FOCUSED);
|
InputMap im = bookTree.getInputMap(JComponent.WHEN_FOCUSED);
|
||||||
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "startStopPlayback");
|
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "startStopPlayback");
|
||||||
@@ -2283,11 +2328,11 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
} else {
|
} else {
|
||||||
book.setIcon(Icons.book);
|
book.setIcon(Icons.book);
|
||||||
}
|
}
|
||||||
bookTreeModel.reload(book);
|
book.reloadTree();
|
||||||
|
|
||||||
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();
|
||||||
@@ -2319,7 +2364,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
ImageIcon i = new ImageIcon(cf.getAbsolutePath());
|
ImageIcon i = new ImageIcon(cf.getAbsolutePath());
|
||||||
Image ri = Utils.getScaledImage(i.getImage(), 22, 22);
|
Image ri = Utils.getScaledImage(i.getImage(), 22, 22);
|
||||||
book.setIcon(new ImageIcon(ri));
|
book.setIcon(new ImageIcon(ri));
|
||||||
bookTreeModel.reload(book);
|
book.reloadTree();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -2545,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2618,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
|
||||||
}
|
}
|
||||||
@@ -2719,55 +2764,6 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AutoTrimThread implements Runnable {
|
|
||||||
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, int sc) {
|
|
||||||
super();
|
|
||||||
Debug.trace();
|
|
||||||
dialog = e;
|
|
||||||
chapter = c;
|
|
||||||
type = t;
|
|
||||||
scope = sc;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void run() {
|
|
||||||
Debug.trace();
|
|
||||||
|
|
||||||
int numKids = chapter.getChildCount();
|
|
||||||
int kidCount = 0;
|
|
||||||
for (Enumeration s = chapter.children(); s.hasMoreElements();) {
|
|
||||||
kidCount++;
|
|
||||||
dialog.setProgress(kidCount * 2000 / numKids);
|
|
||||||
Sentence snt = (Sentence)s.nextElement();
|
|
||||||
if (scope == NewOnly) {
|
|
||||||
if (snt.isProcessed()) continue;
|
|
||||||
}
|
|
||||||
switch (type) {
|
|
||||||
case FFT:
|
|
||||||
snt.autoTrimSampleFFT();
|
|
||||||
break;
|
|
||||||
case Peak:
|
|
||||||
snt.autoTrimSamplePeak();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.closeDialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ExportThread implements Runnable {
|
class ExportThread implements Runnable {
|
||||||
ProgressDialog exportDialog;
|
ProgressDialog exportDialog;
|
||||||
Chapter chapter;
|
Chapter chapter;
|
||||||
@@ -3503,7 +3499,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
ImageIcon i = new ImageIcon(dest.getAbsolutePath());
|
ImageIcon i = new ImageIcon(dest.getAbsolutePath());
|
||||||
Image ri = Utils.getScaledImage(i.getImage(), 22, 22);
|
Image ri = Utils.getScaledImage(i.getImage(), 22, 22);
|
||||||
book.setIcon(new ImageIcon(ri));
|
book.setIcon(new ImageIcon(ri));
|
||||||
bookTreeModel.reload(book);
|
book.reloadTree();
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -3516,16 +3512,42 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
updateWaveform(false);
|
updateWaveform(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateWaveform(boolean force) {
|
TimerTask waveformUpdaterTask = null;
|
||||||
|
|
||||||
|
synchronized public void updateWaveform(boolean force) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
if (selectedSentence != null) {
|
if (selectedSentence != null) {
|
||||||
if ((!force) && (sampleWaveform.getId() != null) && (sampleWaveform.getId().equals(selectedSentence.getId()))) return;
|
if ((!force) && (sampleWaveform.getId() != null) && (sampleWaveform.getId().equals(selectedSentence.getId()))) return;
|
||||||
sampleWaveform.setId(selectedSentence.getId());
|
|
||||||
if (rawAudio.isSelected()) {
|
synchronized (waveformUpdater) {
|
||||||
sampleWaveform.setData(selectedSentence.getRawAudioData());
|
try {
|
||||||
} else {
|
if (waveformUpdaterTask != null) {
|
||||||
sampleWaveform.setData(selectedSentence.getDoubleAudioData(effectsEnabled));
|
waveformUpdaterTask.cancel();
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
}
|
||||||
|
|
||||||
|
waveformUpdaterTask = new TimerTask() {
|
||||||
|
public void run() {
|
||||||
|
sampleWaveform.setId(selectedSentence.getId());
|
||||||
|
if (rawAudio.isSelected()) {
|
||||||
|
sampleWaveform.setData(selectedSentence.getRawAudioData());
|
||||||
|
} else {
|
||||||
|
sampleWaveform.setData(selectedSentence.getDoubleAudioData(effectsEnabled));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
waveformUpdater.schedule(waveformUpdaterTask, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized public void updateWaveformMarkers() {
|
||||||
|
if (selectedSentence != null) {
|
||||||
|
sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset());
|
||||||
|
sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4175,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) {
|
||||||
@@ -4185,4 +4213,14 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void queueJob(Runnable r) {
|
||||||
|
synchronized(processQueue) {
|
||||||
|
processQueue.add(r);
|
||||||
|
if (r instanceof SentenceJob) {
|
||||||
|
SentenceJob sj = (SentenceJob)r;
|
||||||
|
sj.setQueued();
|
||||||
|
}
|
||||||
|
processQueue.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import java.util.UUID;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import javax.sound.sampled.AudioFormat;
|
import javax.sound.sampled.AudioFormat;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.tree.DefaultTreeModel;
|
import javax.swing.tree.DefaultTreeModel;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
@@ -154,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) {
|
||||||
@@ -217,7 +213,7 @@ public class Book extends BookTreeNode {
|
|||||||
oldDir.renameTo(newDir);
|
oldDir.renameTo(newDir);
|
||||||
name = newName;
|
name = newName;
|
||||||
AudiobookRecorder.window.saveBookStructure();
|
AudiobookRecorder.window.saveBookStructure();
|
||||||
AudiobookRecorder.window.bookTreeModel.reload(this);
|
reloadTree();
|
||||||
Options.set("path.last-book", name);
|
Options.set("path.last-book", name);
|
||||||
Options.savePreferences();
|
Options.savePreferences();
|
||||||
AudiobookRecorder.window.setTitle("AudioBook Recorder :: " + name);
|
AudiobookRecorder.window.setTitle("AudioBook Recorder :: " + name);
|
||||||
@@ -447,4 +443,18 @@ public class Book extends BookTreeNode {
|
|||||||
public void setLocation(File l) {
|
public void setLocation(File l) {
|
||||||
location = l;
|
location = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reloadTree() {
|
||||||
|
Debug.trace();
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
if (AudiobookRecorder.window == null) return;
|
||||||
|
if (AudiobookRecorder.window.bookTreeModel == null) return;
|
||||||
|
try {
|
||||||
|
AudiobookRecorder.window.bookTreeModel.reload(Book.this);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package uk.co.majenko.audiobookrecorder;
|
|||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.GridBagConstraints;
|
import java.awt.GridBagConstraints;
|
||||||
import java.awt.GridBagLayout;
|
import java.awt.GridBagLayout;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JTree;
|
import javax.swing.JTree;
|
||||||
@@ -84,7 +86,8 @@ public class BookTreeRenderer extends DefaultTreeCellRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
JLabel time = new JLabel(Utils.secToTime(s.getLength(), "mm:ss.SSS") + " ");
|
JLabel time = new JLabelFixedWidth(75, " " + Utils.secToTime(s.getLength(), "ss.SSS") + " ");
|
||||||
|
time.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||||
|
|
||||||
ctx.gridx = 0;
|
ctx.gridx = 0;
|
||||||
ctx.gridy = 0;
|
ctx.gridy = 0;
|
||||||
@@ -93,17 +96,18 @@ public class BookTreeRenderer extends DefaultTreeCellRenderer {
|
|||||||
ctx.anchor = GridBagConstraints.LINE_START;
|
ctx.anchor = GridBagConstraints.LINE_START;
|
||||||
p.add(ret, ctx);
|
p.add(ret, ctx);
|
||||||
|
|
||||||
if (s.isDetecting()) {
|
if (s.isProcessing()) {
|
||||||
JLabel eff = new JLabel(" recognising... ");
|
JLabel eff = new JLabel();
|
||||||
|
eff.setIcon(Icons.processing);
|
||||||
ctx.weightx = 0.0d;
|
ctx.weightx = 0.0d;
|
||||||
ctx.gridx = 1;
|
ctx.gridx = 1;
|
||||||
p.add(eff);
|
p.add(eff);
|
||||||
} else if (s.isQueued()) {
|
} else if (s.isQueued()) {
|
||||||
JLabel eff = new JLabel(" queued ");
|
JLabel eff = new JLabel();
|
||||||
|
eff.setIcon(Icons.queued);
|
||||||
ctx.weightx = 0.0d;
|
ctx.weightx = 0.0d;
|
||||||
ctx.gridx = 1;
|
ctx.gridx = 1;
|
||||||
p.add(eff);
|
p.add(eff);
|
||||||
} else if (s.isQueued()) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String effectChain = s.getEffectChain();
|
String effectChain = s.getEffectChain();
|
||||||
@@ -121,7 +125,8 @@ public class BookTreeRenderer extends DefaultTreeCellRenderer {
|
|||||||
ctx.gridx = 3;
|
ctx.gridx = 3;
|
||||||
ctx.anchor = GridBagConstraints.LINE_END;
|
ctx.anchor = GridBagConstraints.LINE_END;
|
||||||
int peak = s.getPeakDB();
|
int peak = s.getPeakDB();
|
||||||
JLabel peakLabel = new JLabel(peak + "dB ");
|
JLabel peakLabel = new JLabelFixedWidth(50, peak + "dB ");
|
||||||
|
peakLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||||
if (peak > 0) {
|
if (peak > 0) {
|
||||||
peakLabel.setForeground(new Color(0xCC, 0x00, 0x00));
|
peakLabel.setForeground(new Color(0xCC, 0x00, 0x00));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,4 +40,7 @@ public class Icons {
|
|||||||
static public final ImageIcon disable = new ImageIcon(Icons.class.getResource("icons/disable-effects.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"));
|
static public final ImageIcon manuscript = new ImageIcon(Icons.class.getResource("icons/manuscript.png"));
|
||||||
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 processing = new ImageIcon(Icons.class.getResource("icons/processing.png"));
|
||||||
|
static public final ImageIcon close = new ImageIcon(Icons.class.getResource("icons/close.png"));
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/uk/co/majenko/audiobookrecorder/JLabelFixedWidth.java
Normal file
38
src/uk/co/majenko/audiobookrecorder/JLabelFixedWidth.java
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package uk.co.majenko.audiobookrecorder;
|
||||||
|
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
|
||||||
|
public class JLabelFixedWidth extends JLabel {
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
Dimension size;
|
||||||
|
public JLabelFixedWidth(int w, String txt) {
|
||||||
|
super(txt);
|
||||||
|
JLabel t = new JLabel(txt);
|
||||||
|
size = t.getPreferredSize();
|
||||||
|
size.width = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JLabelFixedWidth(int w) {
|
||||||
|
super();
|
||||||
|
JLabel t = new JLabel("nothing");
|
||||||
|
size = t.getPreferredSize();
|
||||||
|
size.width = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getPreferredSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getMaximumSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getMinimumSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
51
src/uk/co/majenko/audiobookrecorder/NoiseFloor.java
Normal file
51
src/uk/co/majenko/audiobookrecorder/NoiseFloor.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
110
src/uk/co/majenko/audiobookrecorder/QueueMonitor.java
Normal file
110
src/uk/co/majenko/audiobookrecorder/QueueMonitor.java
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package uk.co.majenko.audiobookrecorder;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
|
|
||||||
|
public class QueueMonitor extends JPanel implements MouseListener {
|
||||||
|
|
||||||
|
ArrayList<WorkerThread> threadList = new ArrayList<WorkerThread>();
|
||||||
|
Queue<Runnable> queue;
|
||||||
|
|
||||||
|
public QueueMonitor(Queue<Runnable> q) {
|
||||||
|
super();
|
||||||
|
queue = q;
|
||||||
|
addMouseListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addThread(WorkerThread t) {
|
||||||
|
threadList.add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void purgeQueue() {
|
||||||
|
Runnable work;
|
||||||
|
synchronized (queue) {
|
||||||
|
while (queue.size() > 0) {
|
||||||
|
work = queue.remove();
|
||||||
|
if (work instanceof SentenceJob) {
|
||||||
|
SentenceJob sj = (SentenceJob)work;
|
||||||
|
sj.setDequeued();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getPreferredSize() {
|
||||||
|
return new Dimension(150 + (24 * threadList.size()), 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());
|
||||||
|
|
||||||
|
for (int i = 0; i < threadList.size(); i++) {
|
||||||
|
WorkerThread t = threadList.get(i);
|
||||||
|
if (t.isRunning()) {
|
||||||
|
g.setColor(new Color(50, 200, 0));
|
||||||
|
} else {
|
||||||
|
g.setColor(new Color(80, 0, 0));
|
||||||
|
}
|
||||||
|
g.fillOval(i * 24 + 4, 4, 22 - 8, 22 - 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
g.setColor(getForeground());
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -33,6 +33,7 @@ import java.io.FileInputStream;
|
|||||||
import javax.sound.sampled.AudioSystem;
|
import javax.sound.sampled.AudioSystem;
|
||||||
import javax.sound.sampled.AudioFileFormat;
|
import javax.sound.sampled.AudioFileFormat;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -65,8 +66,11 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
boolean attention = false;
|
boolean attention = false;
|
||||||
boolean processed = false;
|
boolean processed = false;
|
||||||
boolean isDetected = false;
|
boolean isDetected = false;
|
||||||
boolean detecting = false;
|
|
||||||
boolean queued = false;
|
int state = IDLE;
|
||||||
|
static final int IDLE = 0;
|
||||||
|
static final int QUEUED = 1;
|
||||||
|
static final int PROCESSING = 2;
|
||||||
|
|
||||||
String effectChain = null;
|
String effectChain = null;
|
||||||
|
|
||||||
@@ -218,7 +222,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
if (text.equals("")) text = id;
|
if (text.equals("")) text = id;
|
||||||
|
|
||||||
if ((crossStartOffset == -1) || (crossEndOffset == -1)) {
|
if ((crossStartOffset == -1) || (crossEndOffset == -1)) {
|
||||||
updateCrossings(true);
|
updateCrossings();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runtime <= 0.01d) getLength();
|
if (runtime <= 0.01d) getLength();
|
||||||
@@ -256,26 +260,40 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
CacheManager.removeFromCache(this);
|
CacheManager.removeFromCache(this);
|
||||||
|
|
||||||
if (!id.equals("room-noise")) {
|
if (!id.equals("room-noise")) {
|
||||||
autoTrimSample(true);
|
autoTrimSample();
|
||||||
if (Options.getBoolean("process.sphinx")) {
|
if (Options.getBoolean("process.sphinx")) {
|
||||||
recognise();
|
AudiobookRecorder.window.queueJob(new SentenceJob(this) {
|
||||||
|
public void run() {
|
||||||
|
sentence.doRecognition();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void autoTrimSample() {
|
public void autoTrimSample(boolean ignored) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
autoTrimSample(false);
|
autoTrimSample();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void autoTrimSample(boolean useRaw) {
|
public void autoTrimSample() {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
String tm = Options.get("audio.recording.trim");
|
String tm = Options.get("audio.recording.trim");
|
||||||
if (tm.equals("peak")) {
|
if (tm.equals("peak")) {
|
||||||
autoTrimSamplePeak(useRaw);
|
AudiobookRecorder.window.queueJob(new SentenceJob(this) {
|
||||||
|
public void run() {
|
||||||
|
sentence.autoTrimSamplePeak();
|
||||||
|
AudiobookRecorder.window.updateWaveformMarkers();
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (tm.equals("fft")) {
|
} else if (tm.equals("fft")) {
|
||||||
autoTrimSampleFFT(useRaw);
|
AudiobookRecorder.window.queueJob(new SentenceJob(this) {
|
||||||
|
public void run() {
|
||||||
|
sentence.autoTrimSampleFFT();
|
||||||
|
AudiobookRecorder.window.updateWaveformMarkers();
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
startOffset = 0;
|
startOffset = 0;
|
||||||
crossStartOffset = 0;
|
crossStartOffset = 0;
|
||||||
@@ -284,13 +302,13 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
processed = false;
|
processed = false;
|
||||||
// peak = -1d;
|
// peak = -1d;
|
||||||
}
|
}
|
||||||
|
AudiobookRecorder.window.updateWaveform(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final int FFTBuckets = 1024;
|
public static final int FFTBuckets = 1024;
|
||||||
|
|
||||||
public void autoTrimSampleFFT() {
|
public void autoTrimSampleFFT(boolean ignored) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
autoTrimSampleFFT(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public double bucketDifference(double[] a, double[] b) {
|
public double bucketDifference(double[] a, double[] b) {
|
||||||
@@ -304,16 +322,12 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void autoTrimSampleFFT(boolean useRaw) {
|
public void autoTrimSampleFFT() {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
crossStartOffset = -1;
|
crossStartOffset = -1;
|
||||||
crossEndOffset = -1;
|
crossEndOffset = -1;
|
||||||
double[][] samples;
|
double[][] samples;
|
||||||
if (useRaw) {
|
samples = getProcessedAudioData();
|
||||||
samples = getRawAudioData();
|
|
||||||
} else {
|
|
||||||
samples = getProcessedAudioData();
|
|
||||||
}
|
|
||||||
if (samples == null) {
|
if (samples == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -385,7 +399,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
if (endOffset <= startOffset) endOffset = startOffset + fftSize;
|
if (endOffset <= startOffset) endOffset = startOffset + fftSize;
|
||||||
if (endOffset < 0) endOffset = 0;
|
if (endOffset < 0) endOffset = 0;
|
||||||
if (endOffset >= samples[LEFT].length) endOffset = samples[LEFT].length;
|
if (endOffset >= samples[LEFT].length) endOffset = samples[LEFT].length;
|
||||||
updateCrossings(useRaw);
|
updateCrossings();
|
||||||
intens = null;
|
intens = null;
|
||||||
samples = null;
|
samples = null;
|
||||||
processed = true;
|
processed = true;
|
||||||
@@ -436,21 +450,17 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void autoTrimSamplePeak() {
|
public void autoTrimSamplePeak(boolean ignored) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
autoTrimSamplePeak(false);
|
autoTrimSamplePeak();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void autoTrimSamplePeak(boolean useRaw) {
|
public void autoTrimSamplePeak() {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
crossStartOffset = -1;
|
crossStartOffset = -1;
|
||||||
crossEndOffset = -1;
|
crossEndOffset = -1;
|
||||||
double[][] samples;
|
double[][] samples;
|
||||||
if (useRaw) {
|
samples = getProcessedAudioData();
|
||||||
samples = getRawAudioData();
|
|
||||||
} else {
|
|
||||||
samples = getProcessedAudioData();
|
|
||||||
}
|
|
||||||
if (samples == null) return;
|
if (samples == null) return;
|
||||||
double noiseFloor = AudiobookRecorder.window.getNoiseFloor();
|
double noiseFloor = AudiobookRecorder.window.getNoiseFloor();
|
||||||
noiseFloor *= 1.1;
|
noiseFloor *= 1.1;
|
||||||
@@ -490,7 +500,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
|
|
||||||
if (startOffset < 0) startOffset = 0;
|
if (startOffset < 0) startOffset = 0;
|
||||||
if (endOffset >= samples[LEFT].length) endOffset = samples[LEFT].length-1;
|
if (endOffset >= samples[LEFT].length) endOffset = samples[LEFT].length-1;
|
||||||
updateCrossings(useRaw);
|
updateCrossings();
|
||||||
processed = true;
|
processed = true;
|
||||||
reloadTree();
|
reloadTree();
|
||||||
}
|
}
|
||||||
@@ -597,38 +607,23 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
|
|
||||||
public void updateCrossings() {
|
public void updateCrossings() {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
updateCrossings(false);
|
updateStartCrossing();
|
||||||
}
|
updateEndCrossing();
|
||||||
|
|
||||||
public void updateCrossings(boolean useRaw) {
|
|
||||||
Debug.trace();
|
|
||||||
updateStartCrossing(useRaw);
|
|
||||||
updateEndCrossing(useRaw);
|
|
||||||
runtime = -1d;
|
runtime = -1d;
|
||||||
getLength();
|
getLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateStartCrossing() {
|
public void updateStartCrossing() {
|
||||||
Debug.trace();
|
|
||||||
updateStartCrossing(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateStartCrossing(boolean useRaw) {
|
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
if (crossStartOffset == -1) {
|
if (crossStartOffset == -1) {
|
||||||
crossStartOffset = findNearestZeroCrossing(useRaw, startOffset, 4096);
|
crossStartOffset = findNearestZeroCrossing(startOffset, 4096);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateEndCrossing() {
|
public void updateEndCrossing() {
|
||||||
Debug.trace();
|
|
||||||
updateEndCrossing(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateEndCrossing(boolean useRaw) {
|
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
if (crossEndOffset == -1) {
|
if (crossEndOffset == -1) {
|
||||||
crossEndOffset = findNearestZeroCrossing(useRaw, endOffset, 4096);
|
crossEndOffset = findNearestZeroCrossing(endOffset, 4096);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -694,20 +689,8 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Runnable getRecognitionRunnable() {
|
|
||||||
Runnable r = new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
Debug.d("Starting recognition of", getId());
|
|
||||||
doRecognition();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void doRecognition() {
|
public void doRecognition() {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
detecting = true;
|
|
||||||
queued = false;
|
|
||||||
try {
|
try {
|
||||||
reloadTree();
|
reloadTree();
|
||||||
|
|
||||||
@@ -727,18 +710,10 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
|
|
||||||
setText(res);
|
setText(res);
|
||||||
isDetected = true;
|
isDetected = true;
|
||||||
detecting = false;
|
|
||||||
reloadTree();
|
reloadTree();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
detecting = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void recognise() {
|
|
||||||
Debug.trace();
|
|
||||||
Thread t = new Thread(getRecognitionRunnable());
|
|
||||||
t.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocked(boolean l) {
|
public void setLocked(boolean l) {
|
||||||
@@ -776,18 +751,9 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int findNearestZeroCrossing(int pos, int range) {
|
public int findNearestZeroCrossing(int pos, int range) {
|
||||||
Debug.trace();
|
|
||||||
return findNearestZeroCrossing(false, pos, range);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int findNearestZeroCrossing(boolean useRaw, int pos, int range) {
|
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
double[][] data = null;
|
double[][] data = null;
|
||||||
if (useRaw) {
|
data = getProcessedAudioData();
|
||||||
data = getRawAudioData();
|
|
||||||
} else {
|
|
||||||
data = getProcessedAudioData();
|
|
||||||
}
|
|
||||||
if (data == null) return 0;
|
if (data == null) return 0;
|
||||||
if (data[LEFT].length == 0) return 0;
|
if (data[LEFT].length == 0) return 0;
|
||||||
|
|
||||||
@@ -855,7 +821,6 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
File to = sentence.getFile();
|
File to = sentence.getFile();
|
||||||
Files.copy(from.toPath(), to.toPath());
|
Files.copy(from.toPath(), to.toPath());
|
||||||
|
|
||||||
// sentence.updateCrossings();
|
|
||||||
return sentence;
|
return sentence;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -873,24 +838,15 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
|
|
||||||
public double getPeakValue() {
|
public double getPeakValue() {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
return getPeakValue(false, true);
|
return getPeakValue(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getPeakValue(boolean useRaw) {
|
public double getPeakValue(boolean applyGain) {
|
||||||
Debug.trace();
|
|
||||||
return getPeakValue(useRaw, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getPeakValue(boolean useRaw, boolean applyGain) {
|
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
double oldGain = gain;
|
double oldGain = gain;
|
||||||
gain = 1.0d;
|
gain = 1.0d;
|
||||||
double[][] samples = null;
|
double[][] samples = null;
|
||||||
if (useRaw) {
|
samples = getProcessedAudioData(true, applyGain);
|
||||||
samples = getRawAudioData();
|
|
||||||
} else {
|
|
||||||
samples = getProcessedAudioData(true, applyGain);
|
|
||||||
}
|
|
||||||
gain = oldGain;
|
gain = oldGain;
|
||||||
if (samples == null) {
|
if (samples == null) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -937,7 +893,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
public double normalize(double low, double high) {
|
public double normalize(double low, double high) {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
if (locked) return gain;
|
if (locked) return gain;
|
||||||
double max = getPeakValue(true, false);
|
double max = getPeakValue(false);
|
||||||
double d = 0.708 / max;
|
double d = 0.708 / max;
|
||||||
if (d > 1d) d = 1d;
|
if (d > 1d) d = 1d;
|
||||||
if (d < low) d = low;
|
if (d < low) d = low;
|
||||||
@@ -952,7 +908,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
public double normalize() {
|
public double normalize() {
|
||||||
Debug.trace();
|
Debug.trace();
|
||||||
if (locked) return gain;
|
if (locked) return gain;
|
||||||
double max = getPeakValue(true, false);
|
double max = getPeakValue(false);
|
||||||
double d = 0.708 / max;
|
double d = 0.708 / max;
|
||||||
if (d > 1d) d = 1d;
|
if (d > 1d) d = 1d;
|
||||||
setGain(d);
|
setGain(d);
|
||||||
@@ -1000,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 {
|
||||||
@@ -1095,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() {
|
||||||
@@ -1427,21 +1381,23 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
|
|
||||||
|
|
||||||
String def = AudiobookRecorder.window.getDefaultEffectsChain();
|
String def = AudiobookRecorder.window.getDefaultEffectsChain();
|
||||||
Effect eff = AudiobookRecorder.window.effects.get(def);
|
if ((def != null) && (AudiobookRecorder.window.effects != null)) {
|
||||||
|
Effect eff = AudiobookRecorder.window.effects.get(def);
|
||||||
|
|
||||||
if (effectsEnabled) {
|
if (effectsEnabled) {
|
||||||
if (eff != null) {
|
if (eff != null) {
|
||||||
eff.init(getAudioFormat().getFrameRate());
|
eff.init(getAudioFormat().getFrameRate());
|
||||||
eff.process(processedAudio);
|
eff.process(processedAudio);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (effectChain != null) {
|
if (effectChain != null) {
|
||||||
// Don't double up the default chain
|
// Don't double up the default chain
|
||||||
if (!effectChain.equals(def)) {
|
if (!effectChain.equals(def)) {
|
||||||
eff = AudiobookRecorder.window.effects.get(effectChain);
|
eff = AudiobookRecorder.window.effects.get(effectChain);
|
||||||
if (eff != null) {
|
if (eff != null) {
|
||||||
eff.init(getAudioFormat().getFrameRate());
|
eff.init(getAudioFormat().getFrameRate());
|
||||||
eff.process(processedAudio);
|
eff.process(processedAudio);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1670,9 +1626,11 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
Debug.trace();
|
Debug.trace();
|
||||||
if (id.equals("room-noise")) return;
|
if (id.equals("room-noise")) return;
|
||||||
if (getParent() == null) return;
|
if (getParent() == null) return;
|
||||||
synchronized (AudiobookRecorder.window.bookTreeModel) {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
AudiobookRecorder.window.bookTreeModel.reload(this);
|
public void run() {
|
||||||
}
|
AudiobookRecorder.window.bookTreeModel.reload(Sentence.this);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getPeak() {
|
public double getPeak() {
|
||||||
@@ -1707,20 +1665,31 @@ public class Sentence extends BookTreeNode implements Cacheable {
|
|||||||
return (int)db;
|
return (int)db;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDetecting() {
|
|
||||||
return detecting;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean beenDetected() {
|
public boolean beenDetected() {
|
||||||
return isDetected;
|
return isDetected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isProcessing() {
|
||||||
|
return state == PROCESSING;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isQueued() {
|
public boolean isQueued() {
|
||||||
return queued;
|
return state == QUEUED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProcessing() {
|
||||||
|
state = PROCESSING;
|
||||||
|
reloadTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setQueued() {
|
public void setQueued() {
|
||||||
queued = true;
|
state = QUEUED;
|
||||||
|
reloadTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDequeued() {
|
||||||
|
state = IDLE;
|
||||||
|
reloadTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/uk/co/majenko/audiobookrecorder/SentenceExternalJob.java
Normal file
17
src/uk/co/majenko/audiobookrecorder/SentenceExternalJob.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/uk/co/majenko/audiobookrecorder/SentenceJob.java
Normal file
25
src/uk/co/majenko/audiobookrecorder/SentenceJob.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package uk.co.majenko.audiobookrecorder;
|
||||||
|
|
||||||
|
import java.lang.Runnable;
|
||||||
|
|
||||||
|
public abstract class SentenceJob implements Runnable {
|
||||||
|
protected Sentence sentence;
|
||||||
|
|
||||||
|
public SentenceJob(Sentence s) {
|
||||||
|
sentence = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQueued() {
|
||||||
|
sentence.setQueued();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDequeued() {
|
||||||
|
sentence.setDequeued();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProcessing() {
|
||||||
|
sentence.setProcessing();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void run();
|
||||||
|
}
|
||||||
77
src/uk/co/majenko/audiobookrecorder/VersionChecker.java
Normal file
77
src/uk/co/majenko/audiobookrecorder/VersionChecker.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,9 +5,13 @@ import java.util.Queue;
|
|||||||
public class WorkerThread extends Thread {
|
public class WorkerThread extends Thread {
|
||||||
private static int instance = 0;
|
private static int instance = 0;
|
||||||
private final Queue<Runnable> queue;
|
private final Queue<Runnable> queue;
|
||||||
|
private final QueueMonitor monitor;
|
||||||
|
|
||||||
public WorkerThread(Queue<Runnable> queue) {
|
private boolean running = false;
|
||||||
|
|
||||||
|
public WorkerThread(Queue<Runnable> queue, QueueMonitor mon) {
|
||||||
this.queue = queue;
|
this.queue = queue;
|
||||||
|
monitor = mon;
|
||||||
setName("Worker Thread " + (instance++));
|
setName("Worker Thread " + (instance++));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,8 +34,19 @@ public class WorkerThread extends Thread {
|
|||||||
work = queue.remove();
|
work = queue.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the work item
|
running = true;
|
||||||
|
monitor.repaint();
|
||||||
|
if (work instanceof SentenceJob) {
|
||||||
|
SentenceJob sj = (SentenceJob)work;
|
||||||
|
sj.setProcessing();
|
||||||
|
}
|
||||||
work.run();
|
work.run();
|
||||||
|
if (work instanceof SentenceJob) {
|
||||||
|
SentenceJob sj = (SentenceJob)work;
|
||||||
|
sj.setDequeued();
|
||||||
|
}
|
||||||
|
running = false;
|
||||||
|
monitor.repaint();
|
||||||
}
|
}
|
||||||
catch ( InterruptedException ie ) {
|
catch ( InterruptedException ie ) {
|
||||||
ie.printStackTrace();
|
ie.printStackTrace();
|
||||||
@@ -40,4 +55,8 @@ public class WorkerThread extends Thread {
|
|||||||
}
|
}
|
||||||
Debug.d(getName(), "died");
|
Debug.d(getName(), "died");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isRunning() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user