Compare commits

...

21 Commits

Author SHA1 Message Date
2064184093 Release 0.4.4 2021-06-03 10:40:34 +01:00
2e6ea5eecc Fixes to effect and tree synchronisation 2021-04-17 14:46:36 +01:00
0dde64e5fc Merge branch 'master' of github.com:MajenkoProjects/AudiobookRecorder 2021-01-22 14:13:38 +00:00
28a3326a35 Improved archive routine 2021-01-22 14:13:27 +00:00
df6b893171 Merge pull request #27 from MajenkoProjects/dependabot/maven/launch4j/maven/com.thoughtworks.xstream-xstream-1.4.15
Bump xstream from 1.4.14-java7 to 1.4.15 in /launch4j/maven
2021-01-22 04:22:43 -08:00
ead577521d Normalized all file paths 2021-01-22 11:55:38 +00:00
dependabot[bot]
cc1c76bd47 Bump xstream from 1.4.14-java7 to 1.4.15 in /launch4j/maven
Bumps [xstream](https://github.com/x-stream/xstream) from 1.4.14-java7 to 1.4.15.
- [Release notes](https://github.com/x-stream/xstream/releases)
- [Commits](https://github.com/x-stream/xstream/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-21 17:03:20 +00:00
b74f1aedb4 Bump xtream version 2020-11-16 20:58:51 +00:00
67d1e787ee Merge pull request #26 from MajenkoProjects/dependabot/maven/launch4j/maven/com.thoughtworks.xstream-xstream-1.4.13-java7
Bump xstream from 1.4.10-java7 to 1.4.13-java7 in /launch4j/maven
2020-11-16 20:56:19 +00:00
11ca88ee38 Merge pull request #25 from MajenkoProjects/dependabot/maven/launch4j/maven/org.apache.ant-ant-1.9.15
Bump ant from 1.8.2 to 1.9.15 in /launch4j/maven
2020-11-16 20:56:09 +00:00
dependabot[bot]
ae4e3db0d7 Bump xstream from 1.4.10-java7 to 1.4.13-java7 in /launch4j/maven
Bumps [xstream](https://github.com/x-stream/xstream) from 1.4.10-java7 to 1.4.13-java7.
- [Release notes](https://github.com/x-stream/xstream/releases)
- [Commits](https://github.com/x-stream/xstream/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-16 20:41:30 +00:00
dependabot[bot]
4314790271 Bump ant from 1.8.2 to 1.9.15 in /launch4j/maven
Bumps ant from 1.8.2 to 1.9.15.

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-14 18:42:19 +00:00
9c58276119 More improvements to gain curve 2020-09-01 19:02:43 +01:00
16380a9752 Some improvements to gain curve calculations 2020-08-31 17:09:03 +01:00
b523d80c25 Move normalize chapter to queue system 2020-08-25 17:10:39 +01:00
34d1f504c0 Released 0.4.3 2020-07-06 19:44:21 +01:00
d4a64d4e72 Automate gain profile generation 2020-07-06 18:17:49 +01:00
44201a0bbb Merge pull request #24 from MajenkoProjects/dependabot/maven/launch4j/maven/commons-beanutils-commons-beanutils-1.9.4
Bump commons-beanutils from 1.7.0 to 1.9.4 in /launch4j/maven
2020-07-01 11:23:38 +01:00
dependabot[bot]
1d19cd4c98 Bump commons-beanutils from 1.7.0 to 1.9.4 in /launch4j/maven
Bumps commons-beanutils from 1.7.0 to 1.9.4.

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-01 10:22:57 +00:00
4c89c70b05 Merge pull request #23 from MajenkoProjects/dependabot/maven/launch4j/maven/com.thoughtworks.xstream-xstream-1.4.10-java7
Bump xstream from 1.4.8 to 1.4.10-java7 in /launch4j/maven
2020-07-01 11:22:36 +01:00
dependabot[bot]
33eb219904 Bump xstream from 1.4.8 to 1.4.10-java7 in /launch4j/maven
Bumps [xstream](https://github.com/x-stream/xstream) from 1.4.8 to 1.4.10-java7.
- [Release notes](https://github.com/x-stream/xstream/releases)
- [Commits](https://github.com/x-stream/xstream/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2020-06-30 23:31:32 +00:00
12 changed files with 393 additions and 106 deletions

2
dist/linux/stub vendored
View File

@@ -5,6 +5,6 @@ java=java
if test -n "$JAVA_HOME"; then
java="$JAVA_HOME/bin/java"
fi
java_args=-Xmx1g
java_args=-Xmx8g
exec "$java" $java_args -jar $MYSELF "$@"
exit 1

View File

@@ -57,7 +57,7 @@
<groupId>commons-collections</groupId>
</exclusion>
</exclusions>
<version>1.7.0</version>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
@@ -138,12 +138,12 @@
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.8</version>
<version>1.4.15</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.8.2</version>
<version>1.9.15</version>
</dependency>
</dependencies>
@@ -509,4 +509,4 @@
</profiles>
</project>
</project>

View File

@@ -1 +1 @@
version=0.4.2
version=0.4.4

View File

@@ -185,6 +185,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
JToggleButtonSpacePlay selectCutMode;
JButtonSpacePlay doCutSplit;
JToggleButtonSpacePlay editGainCurve;
JButtonSpacePlay autoCreatePoints;
JButtonSpacePlay refreshSentence;
@@ -203,6 +204,8 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
public Queue<Runnable>processQueue = null;
public QueueMonitor queueMonitor = null;
boolean effectsUpdating = false; // crude lock
void buildToolbar(Container ob) {
Debug.trace();
toolBar = new MainToolBar(this);
@@ -449,9 +452,20 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
}
});
autoCreatePoints = new JButtonSpacePlay(Icons.normalize, "Create peak points", new ActionListener() {
public void actionPerformed(ActionEvent e) {
Debug.trace();
if (selectedSentence != null) {
selectedSentence.autoAddPeakGainPoints();
updateWaveform(true);
}
}
});
editGainCurve = new JToggleButtonSpacePlay(Icons.normalize, "Edit gain curve", new ActionListener() {
public void actionPerformed(ActionEvent e) {
Debug.trace();
autoCreatePoints.setEnabled(editGainCurve.isSelected());
sampleWaveform.setDisplayGainCurve(editGainCurve.isSelected());
}
});
@@ -625,16 +639,20 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
public void actionPerformed(ActionEvent e) {
Debug.trace();
Debug.d(e);
if (effectsUpdating) return;
if (selectedSentence != null) {
int i = effectChain.getSelectedIndex();
KVPair<String, String> p = effectChain.getItemAt(i);
if (p == null) return;
System.err.println("I want to select effect " + p.getKey());
selectedSentence.setEffectChain(p.getKey());
updateWaveform(true);
}
}
});
controlsBottom.add(autoCreatePoints);
autoCreatePoints.setEnabled(false);
controlsBottom.add(editGainCurve);
controlsBottom.addSeparator();
controlsBottom.add(selectSplitMode);
@@ -809,8 +827,6 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
locked.setSelected(s.isLocked());
attention.setSelected(s.getAttentionFlag());
setEffectChain(s.getEffectChain());
postSentenceGap.setEnabled(!s.isLocked());
gainPercent.setEnabled(!s.isLocked());
reprocessAudioFFT.setEnabled(!s.isLocked());
@@ -1675,12 +1691,17 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
JMenuObject o = (JMenuObject)e.getSource();
Chapter chap = (Chapter)o.getObject();
ProgressDialog ed = new ProgressDialog("Normalizing " + chap.getName());
NormalizeThread t = new NormalizeThread(chap, ed);
Thread nt = new Thread(t);
nt.start();
ed.setVisible(true);
for (Enumeration s = c.children(); s.hasMoreElements();) {
Sentence snt = (Sentence)s.nextElement();
if (!snt.isLocked()) {
queueJob(new SentenceJob(snt) {
public void run() {
sentence.normalize();
sentence.autoAddPeakGainPoints();
}
});
}
}
}
});
@@ -2332,7 +2353,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
book.save();
// Add the book to the tree
loadXMLBookStructure(new File(book.getLocation(), "audiobook.abx"));
loadXMLBookStructure(book.getLocation("audiobook.abx"));
updateOpenBookList();
} catch (Exception ex) {
ex.printStackTrace();
@@ -2430,40 +2451,6 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
playingThread.start();
}
class NormalizeThread implements Runnable {
ProgressDialog dialog;
Chapter chapter;
public NormalizeThread(Chapter c, ProgressDialog e) {
super();
Debug.trace();
dialog = e;
chapter = c;
}
@SuppressWarnings("unchecked")
public void run() {
Debug.trace();
int numKids = chapter.getChildCount();
int kidCount = 0;
double lastGain = -1;
double variance = Options.getInteger("audio.recording.variance") / 100d;
for (Enumeration s = chapter.children(); s.hasMoreElements();) {
kidCount++;
dialog.setProgress(kidCount * 2000 / numKids);
Sentence snt = (Sentence)s.nextElement();
if (lastGain == -1) {
lastGain = snt.normalize();
} else {
lastGain = snt.normalize(lastGain - variance, lastGain + variance);
}
}
dialog.closeDialog();
}
}
class ExportThread implements Runnable {
ProgressDialog exportDialog;
Chapter chapter;
@@ -2827,6 +2814,11 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
public void run() {
Debug.trace();
try {
pd.setMessage("Purging backups...");
book.purgeBackups();
pd.setMessage("Archiving book...");
String name = book.getName();
File storageDir = new File(Options.get("path.storage"));
File bookDir = book.getLocation();
@@ -2913,6 +2905,8 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
zos.flush();
zos.close();
pd.setMessage("Cleaning up...");
while (fileList.size() > 0) {
File f = fileList.remove(fileList.size() - 1);
f.delete();
@@ -2926,7 +2920,7 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
public void archiveBook(Book book) {
Debug.trace();
int r = JOptionPane.showConfirmDialog(this, "This will stash the current book away\nin the archives folder in a compressed\nform. The existing book files will be deleted\nand the book closed.\n\nAre you sure you want to do this?", "Archive Book", JOptionPane.OK_CANCEL_OPTION);
int r = JOptionPane.showConfirmDialog(this, "This will stash the current book away\nin the archives folder in a compressed\nform. All backups will be purged, the existing book files will be deleted\nand the book closed.\n\nAre you sure you want to do this?", "Archive Book", JOptionPane.OK_CANCEL_OPTION);
if (r == JOptionPane.OK_OPTION) {
@@ -2937,17 +2931,23 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
Thread t = new Thread(runnable);
t.start();
pd.setVisible(true);
closeBook(book);
closeBook(book, false);
}
}
public void closeBook(Book b) {
closeBook(b, true);
}
public void closeBook(Book b, boolean save) {
if (selectedBook == b) {
setSelectedBook(null);
setSelectedSentence(null);
setSelectedChapter(null);
}
saveBook(b);
if (save) {
saveBook(b);
}
bookTreeModel.removeNodeFromParent(b);
updateOpenBookList();
}
@@ -3111,8 +3111,8 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
public void updateEffectChains(TreeMap<String, EffectGroup> effs) {
Debug.trace();
int sel = effectChain.getSelectedIndex();
KVPair<String, String> ent = effectChain.getItemAt(sel);
effectsUpdating = true;
System.err.println("Updating effect chains");
while (effectChain.getItemCount() > 0) {
effectChain.removeItemAt(0);
}
@@ -3124,15 +3124,40 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
KVPair<String, String> p = new KVPair<String, String>(k, e.toString());
effectChain.addItem(p);
}
if (ent != null) {
setEffectChain(ent.getKey());
// } else {
// setEffectChain(getBook().getDefaultEffect());
if (selectedSentence != null) {
String key = selectedSentence.getEffectChain();
for (int i = 0; i < effectChain.getItemCount(); i++) {
KVPair<String, String> p = effectChain.getItemAt(i);
if (p.getKey().equals(key)) {
effectChain.setSelectedIndex(i);
effectsUpdating = false;
return;
}
}
}
effectsUpdating = false;
}
public void showSelectedEffectChain(String key) {
Debug.trace();
effectsUpdating = true;
System.err.println("Looking for effect " + key + "...");
for (int i = 0; i < effectChain.getItemCount(); i++) {
KVPair<String, String> p = effectChain.getItemAt(i);
System.err.println(" Testing " + p.getKey() + "...");
if (p.getKey().equals(key)) {
System.err.println(" Found it.");
effectChain.setSelectedIndex(i);
effectsUpdating = false;
return;
}
}
effectsUpdating = false;
}
public void setEffectChain(String key) {
Debug.trace();
System.err.println("Setting effect " + key);
for (int i = 0; i < effectChain.getItemCount(); i++) {
KVPair<String, String> p = effectChain.getItemAt(i);
if (p.getKey().equals(key)) {
@@ -3255,8 +3280,8 @@ public class AudiobookRecorder extends JFrame implements DocumentListener {
}
newSentence.writeAudioData(startSamples);
newSentence.setPostGapType("continuation");
newSentence.setPostGap(Options.getInteger("catenation.short-sentence"));
newSentence.setPostGapType("sentence");
newSentence.setPostGap(Options.getInteger("catenation.post-sentence"));
selectedSentence.writeAudioData(endSamples);
selectedSentence.autoTrimSample();

View File

@@ -78,6 +78,21 @@ public class Biquad implements Effect {
setPeakGain(peakGainDB);
}
// Special single channel version for wave profile processing
public void process(double[] samples) {
Debug.trace();
lz1 = 0d;
lz2 = 0d;
for (int i = 0; i < samples.length; i++) {
double lout = samples[i] * a0 + lz1;
lz1 = samples[i] * a1 + lz2 - b1 * lout;
lz2 = samples[i] * a2 - b2 * lout;
samples[i] = lout;
}
}
public void process(double[][] samples) {
Debug.trace();
lz1 = 0d;

View File

@@ -472,13 +472,14 @@ public class Book extends BookTreeNode {
public void onSelect(BookTreeNode target) {
Debug.trace();
AudiobookRecorder.setSelectedBook(this);
AudiobookRecorder.window.setTitle("AudioBook Recorder :: " + name);
if (target == this) {
AudiobookRecorder.setSelectedChapter(null);
AudiobookRecorder.setSelectedSentence(null);
}
AudiobookRecorder.window.setBookNotes(notes);
AudiobookRecorder.window.noiseFloorLabel.setNoiseFloor(getNoiseFloorDB());
// AudiobookRecorder.window.updateEffectChains(effects);
AudiobookRecorder.window.updateEffectChains(effects);
TreeNode p = getParent();
if (p instanceof BookTreeNode) {
BookTreeNode btn = (BookTreeNode)p;
@@ -501,6 +502,11 @@ public class Book extends BookTreeNode {
return location;
}
public File getLocation(String dir) {
Debug.trace();
return new File(location, dir);
}
public void setLocation(File l) {
Debug.trace();
location = l;
@@ -588,9 +594,7 @@ public class Book extends BookTreeNode {
Debug.trace();
effects = new TreeMap<String,EffectGroup>();
loadEffectsFromFolder(new File(Options.get("path.storage"), "System"));
if (location != null) {
loadEffectsFromFolder(location);
}
loadEffectsFromFolder(getLocation());
}
public void loadEffectsFromFolder(File dir) {

View File

@@ -8,15 +8,18 @@ public class CacheManager {
static int cacheSize = 10;
public static void addToCache(Cacheable c) {
Debug.trace();
while (cache.size() >= cacheSize) {
Cacheable ob = cache.remove(0);
if (ob.lockedInCache()) {
cache.add(ob);
} else {
if (ob instanceof Sentence) {
Sentence s = (Sentence)ob;
if (ob != null) {
if (ob.lockedInCache()) {
cache.add(ob);
} else {
if (ob instanceof Sentence) {
Sentence s = (Sentence)ob;
}
ob.clearCache();
}
ob.clearCache();
}
}
@@ -28,6 +31,7 @@ public class CacheManager {
}
public static void removeFromCache(Cacheable c) {
Debug.trace();
if (c instanceof Sentence) {
Sentence s = (Sentence)c;
}

View File

@@ -172,12 +172,7 @@ public class Chapter extends BookTreeNode {
Book book = getBook();
File bookRoot = new File(Options.get("path.storage"), book.getName());
if (!bookRoot.exists()) {
bookRoot.mkdirs();
}
File export = new File(bookRoot, "export");
File export = book.getLocation("export");
if (!export.exists()) {
export.mkdirs();
}

View File

@@ -60,7 +60,9 @@ public class Options extends JDialog {
JSpinner shortSentenceGap;
JSpinner postParagraphGap;
JSpinner postSectionGap;
JSpinner maxGainVariance;
JSpinner rmsLow;
JSpinner rmsHigh;
JCheckBox autoNormalize;
JTextField ffmpegLocation;
JComboBox<KVPair> bitRate;
JComboBox<KVPair> channels;
@@ -358,7 +360,9 @@ public class Options extends JDialog {
trimMethod = addDropdown(optionsPanel, "Auto-trim method:", getTrimMethods(), get("audio.recording.trim"), "None: don't auto-trim. FFT: Compare the FFT profile of blocks to the room noise profile and trim silent blocks, Peak: Look for the start and end rise and fall points");
fftThreshold = addSpinner(optionsPanel, "FFT threshold:", 0, 100, 1, getInteger("audio.recording.trim.fft"), "", "This specifies the difference (in hundredths) between the power of FFT buckets in a sample block compared to the overall power of the same FFT bucket in the room noise. Raising this number makes the FFT trimming less sensitive.");
fftBlockSize = addDropdown(optionsPanel, "FFT Block size:", getFFTBlockSizes(), get("audio.recording.trim.blocksize"), "How large an FFT block should be when processing. Larger values increase sensitivity but at the epxense of resolution.");
maxGainVariance = addSpinner(optionsPanel, "Maximum gain variance:", 0, 100, 1, getInteger("audio.recording.variance"), "", "This is how much the gain is allowed to vary by from phrase to phrase when normalizing an entire chapter.");
rmsLow = addSpinner(optionsPanel, "Target RMS (low):", -100, 0, 1, getInteger("audio.recording.rms.low"), "", "When normalizing this is the lowest target average RMS to aim for");
rmsHigh = addSpinner(optionsPanel, "Target RMS (high):", -100, 0, 1, getInteger("audio.recording.rms.high"), "", "When normalizing this is the highest target average RMS to aim for");
autoNormalize = addCheckBox(optionsPanel, "Enable automatic normalization", getBoolean("process.normalize"), "This will automatically normalize each phrase after recording");
addSeparator(optionsPanel);
@@ -599,7 +603,9 @@ public class Options extends JDialog {
defaultPrefs.put("catenation.post-section", "3000");
defaultPrefs.put("audio.recording.trim.fft", "10");
defaultPrefs.put("audio.recording.variance", "10");
defaultPrefs.put("audio.recording.rms.low", "-22");
defaultPrefs.put("audio.recording.rms.high", "-20");
defaultPrefs.put("process.normalize", "true");
defaultPrefs.put("path.storage", (new File(System.getProperty("user.home"), "Recordings")).toString());
defaultPrefs.put("path.archive", (new File(new File(System.getProperty("user.home"), "Recordings"),"archive")).toString());
@@ -736,7 +742,9 @@ public class Options extends JDialog {
set("editor.external", externalEditor.getText());
set("cache.size", cacheSize.getValue());
set("audio.recording.trim.fft", fftThreshold.getValue());
set("audio.recording.variance", maxGainVariance.getValue());
set("audio.recording.rms.low", rmsLow.getValue());
set("audio.recording.rms.high", rmsHigh.getValue());
set("process.normalize", autoNormalize.isSelected());
if (fftBlockSize.getSelectedItem() != null) set("audio.recording.trim.blocksize", ((KVPair)fftBlockSize.getSelectedItem()).key);
if (playbackBlockSize.getSelectedItem() != null) set("audio.playback.blocksize", ((KVPair)playbackBlockSize.getSelectedItem()).key);

View File

@@ -103,6 +103,8 @@ public class Sentence extends BookTreeNode implements Cacheable {
double[][] processedAudio = null;
double[] fftProfile = null;
double[] waveProfile = null;
TreeMap<Integer, Double> gainPoints = null;
@@ -233,6 +235,11 @@ public class Sentence extends BookTreeNode implements Cacheable {
peak = Utils.s2d(Book.getTextNode(root, "peak", "-1.000"));
isDetected = Utils.s2b(Book.getTextNode(root, "detected"));
if (sampleSize == 0) {
loadFile();
autoTrimSample();
}
gainPoints = new TreeMap<Integer, Double>();
Element gp = Book.getNode(root, "gainpoints");
if (gp != null) {
@@ -332,6 +339,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
public void run() {
sentence.autoTrimSamplePeak();
AudiobookRecorder.window.updateWaveformMarkers();
if (Options.getBoolean("process.normalize")) sentence.normalize();
}
});
} else if (tm.equals("fft")) {
@@ -339,6 +347,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
public void run() {
sentence.autoTrimSampleFFT();
AudiobookRecorder.window.updateWaveformMarkers();
if (Options.getBoolean("process.normalize")) sentence.normalize();
}
});
} else {
@@ -449,6 +458,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
updateCrossings();
intens = null;
samples = null;
waveProfile = null;
processed = true;
reloadTree();
}
@@ -575,7 +585,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
Debug.d("Get file for", id);
Book book = getBook();
if (book == null) return null;
File b = new File(book.getLocation(), "files");
File b = book.getLocation("files");
if (!b.exists()) {
b.mkdirs();
}
@@ -584,7 +594,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
public File getTempFile() {
Debug.trace();
File b = new File(getBook().getLocation(), "files");
File b = getBook().getLocation("files");
if (!b.exists()) {
b.mkdirs();
}
@@ -791,13 +801,16 @@ public class Sentence extends BookTreeNode implements Cacheable {
public void clearCache() {
Debug.trace();
audioData = null;
waveProfile = null;
processedAudio = null;
storedFormat = null;
}
public boolean lockedInCache() {
Debug.trace();
return id.equals("room-noise");
if (id.equals("room-noise")) return true;
if (isProcessing()) return true;
return false;
}
public int findNearestZeroCrossing(int pos, int range) {
@@ -940,6 +953,10 @@ public class Sentence extends BookTreeNode implements Cacheable {
}
public void setGain(double g) {
setGain(g, false);
}
public void setGain(double g, boolean batch) {
Debug.trace();
if (g <= 0.0001d) g = 1.0d;
if (g == gain) return;
@@ -950,7 +967,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
if (gint != gainint) {
refreshAllData();
peak = -1;
reloadTree();
if (!batch) reloadTree();
}
}
@@ -962,27 +979,38 @@ public class Sentence extends BookTreeNode implements Cacheable {
public double normalize(double low, double high) {
Debug.trace();
if (locked) return gain;
double max = getPeakValue(false);
double d = 0.708 / max;
if (d < low) d = low;
if (d > high) d = high;
setGain(d);
int targetLow = Options.getInteger("audio.recording.rms.low");
int targetHigh = Options.getInteger("audio.recording.rms.high");
long ts = System.currentTimeMillis();
while ((int)getRMS() < targetLow) {
if (System.currentTimeMillis() - ts > 10000) {
System.err.println("Aborted gain boost: took too long");
return gain;
}
setGain(gain + 0.1);
if (gain >= 10.0d) break;
}
ts = System.currentTimeMillis();
while ((int)getRMS() > targetHigh) {
if (System.currentTimeMillis() - ts > 10000) {
System.err.println("Aborted gain cut: took too long");
return gain;
}
setGain(gain - 0.1);
}
refreshAllData();
peak = -1;
getPeak();
reloadTree();
return d;
return gain;
}
public double normalize() {
Debug.trace();
if (locked) return gain;
double max = getPeakValue(false);
double d = 0.708 / max;
setGain(d);
peak = -1;
getPeak();
reloadTree();
return d;
return normalize(0, 0);
}
class ExternalEditor implements Runnable {
@@ -1455,11 +1483,11 @@ public class Sentence extends BookTreeNode implements Cacheable {
synchronized public double[][] getProcessedAudioData(boolean effectsEnabled, boolean applyGain) {
Debug.trace();
Book book = getBook();
loadFile();
if (processedAudio != null) {
return processedAudio;
}
Book book = getBook();
loadFile();
if (audioData == null) return null;
processedAudio = new double[2][audioData[LEFT].length];
@@ -1486,7 +1514,12 @@ public class Sentence extends BookTreeNode implements Cacheable {
eff = book.effects.get(effectChain);
if (eff != null) {
eff.init(getAudioFormat().getFrameRate());
eff.process(processedAudio);
// There is a chance another thread could cripple the audio data cache
// so we'll just ignore any errors here.
try {
eff.process(processedAudio);
} catch (Exception ex) {
}
}
}
}
@@ -1730,6 +1763,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
Debug.trace();
AudiobookRecorder.setSelectedSentence(this);
AudiobookRecorder.window.setSentenceNotes(notes);
AudiobookRecorder.window.showSelectedEffectChain(getEffectChain());
TreeNode p = getParent();
if (p instanceof BookTreeNode) {
BookTreeNode btn = (BookTreeNode)p;
@@ -1832,6 +1866,7 @@ public class Sentence extends BookTreeNode implements Cacheable {
peak = -1d;
sampleSize = -1;
audioData = null;
waveProfile = null;
processedAudio = null;
fftProfile = null;
CacheManager.removeFromCache(this);
@@ -1868,6 +1903,10 @@ public class Sentence extends BookTreeNode implements Cacheable {
}
public void adjustGainPoint(Integer loc, Double adj) {
adjustGainPoint(loc, adj, true);
}
public void adjustGainPoint(Integer loc, Double adj, boolean reload) {
if (gainPoints == null) {
gainPoints = new TreeMap<Integer, Double>();
return;
@@ -1876,7 +1915,9 @@ public class Sentence extends BookTreeNode implements Cacheable {
if (gp == null) return;
gp += adj;
gainPoints.put(loc, gp);
refreshAllData();
if (reload) {
refreshAllData();
}
}
public double[] calculateGains() {
@@ -1901,7 +1942,10 @@ public class Sentence extends BookTreeNode implements Cacheable {
double ystep = diff / (double)range;
for (int x = 0; x < range; x++) {
y += ystep;
gains[x1 + x] = y;
try {
gains[x1 + x] = y;
} catch (Exception ex) {
}
}
x1 = x2;
}
@@ -1937,6 +1981,24 @@ public class Sentence extends BookTreeNode implements Cacheable {
return rms;
}
public boolean isClipping(int start, int end) {
double[][] samples = getProcessedAudioData();
if (samples == null) {
return false;
}
for (int i = start; i <= end; i++) {
if (Math.abs(samples[LEFT][i]) > 0.708) {
return true;
}
if (Math.abs(samples[RIGHT][i]) > 0.708) {
return true;
}
}
return false;
}
public boolean isClipping() {
if (clipping > 0) {
if (clipping == 1) return false;
@@ -1950,15 +2012,177 @@ public class Sentence extends BookTreeNode implements Cacheable {
clipping = 1;
for (int i = 0; i < samples[LEFT].length; i++) {
if (samples[LEFT][i] > 0.708) {
if (Math.abs(samples[LEFT][i]) > 0.708) {
clipping = 2;
return true;
}
if (samples[RIGHT][i] > 0.708) {
if (Math.abs(samples[RIGHT][i]) > 0.708) {
clipping = 2;
return true;
}
}
return false;
}
public double mix(double a, double b) {
return (a + b) / 2d;
}
final int window = 500;
public double[] getWaveProfile() {
if (waveProfile != null) return waveProfile;
double[][] samples = getProcessedAudioData();
if (samples[LEFT].length == 0) return null;
waveProfile = new double[samples[LEFT].length];
double rt = 0;
int nsamp = samples[LEFT].length;
int nbuckets = nsamp / window;
double[] buckets = new double[nbuckets + 1];
for (int i = 0; i < nsamp; i++) {
double sval = Math.abs(mix(samples[LEFT][i], samples[RIGHT][i]));
int bnum = i / window;
if (sval > buckets[bnum]) buckets[bnum] = sval;
}
for (int i = 0; i < nsamp; i++) {
waveProfile[i] = buckets[i / window];
}
return waveProfile;
}
int findBiggestPeak() {
double[][] samples = getProcessedAudioData();
int pos = 0;
double peak = 0;
if (samples == null) {
System.err.println("Um.... no samples...?");
return -1;
}
for (int i = 0; i < samples[LEFT].length; i++) {
if (Math.abs(samples[LEFT][i]) > peak) {
peak = Math.abs(samples[LEFT][i]);
pos = i;
}
if (Math.abs(samples[RIGHT][i]) > peak) {
peak = Math.abs(samples[RIGHT][i]);
pos = i;
}
}
return pos;
}
int findPreviousZero(int offset) {
double[] profile = getWaveProfile();
int pos = offset;
while (pos > 0) {
if (profile[pos] < 0.05) return pos - (window/2);
pos--;
}
return -1;
}
int findNextZero(int offset) {
double[] profile = getWaveProfile();
int pos = offset;
while (pos < profile.length) {
if (profile[pos] < 0.05) return pos + (window / 2);
pos++;
}
return -1;
}
int findNearestGainPoint(int pos) {
int closest = Integer.MAX_VALUE;
int cpos = -1;
if (gainPoints == null) return -1;
for (Integer loc : gainPoints.keySet()) {
int diff = pos - loc;
if (diff < 0) diff = 0 - diff;
if (diff < closest) {
closest = diff;
cpos = loc;
}
}
return cpos;
}
public void autoAddPeakGainPoints() {
long ts = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - ts > 10000) {
System.err.println("Terminated: running too long!");
return;
}
processedAudio = null;
double[][] samples = getProcessedAudioData();
int pos = findBiggestPeak();
if (pos == -1) return;
System.err.println("Biggest peak: " + pos);
int closest = findNearestGainPoint(pos);
if (closest >= 0) {
int diff = closest - pos;
if (diff < 0) diff = 0 - diff;
if (diff < 10) {
System.err.println("Readjusting location: " + closest + " - diff = " + diff);
pos = closest;
}
}
if ((Math.abs(samples[LEFT][pos]) < 0.708) && (Math.abs(samples[RIGHT][pos]) < 0.708)) {
System.err.println("No more peaks");
refreshAllData();
return;
}
int start = findPreviousZero(pos);
int end = findNextZero(pos);
if (start == -1) {
System.err.println("Unable to find previous zero");
return;
}
if (end == -1) {
System.err.println("Unable to find next zero");
return;
}
addGainPoint(start, 1d);
addGainPoint(pos, 1d);
addGainPoint(end, 1d);
double val = 1d;
while (isClipping(pos - 10, pos + 10)) {
adjustGainPoint(pos, -0.05, false);
val -= 0.05d;
if (val < 0.1d) {
System.err.println("Aborting: gain too low");
break;
}
processedAudio = null; // Force a refresh
}
System.err.println("Result: " + gainPoints.get(pos));
try {
Thread.sleep(1);
} catch (Exception ex) {
}
}
}
}

View File

@@ -56,6 +56,7 @@ public class VersionChecker implements Runnable {
if (Utils.s2i(installedVersion) >= Utils.s2i(availableVersion)) return;
JButton upgrade = new JButton("A new version is available.");
upgrade.setFocusable(false);
upgrade.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
Utils.browse(website);

View File

@@ -99,6 +99,7 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
if (sentence != null) {
double[][] samples = sentence.getDoubleAudioData(true);
double[] waveProfile = sentence.getWaveProfile();
int num = samples[Sentence.LEFT].length;
step = num / zoomFactor / w;
@@ -117,9 +118,13 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
double lave = 0;
double lmax = 0;
double prof = 0;
for (int o = 0; o < step; o++) {
if (offset + (n * step) + o >= samples[Sentence.LEFT].length) break;
double sample = (samples[Sentence.LEFT][offset + (n * step) + o] + samples[Sentence.RIGHT][offset + (n * step) + o]) / 2d;
double asamp = waveProfile[offset + (n * step) + o];
if (asamp > prof) prof = asamp;
if (sample >= 0) {
have += sample;
hcnt++;
@@ -158,6 +163,12 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
g.drawLine(n, (int)(h/2 + lmax), n, (int)(h/2 - hmax));
g.setColor(new Color(0, 100, 255));
g.drawLine(n, (int)(h/2 + lave), n, (int)(h/2 - have));
if (prof < 0.05d) {
g.setColor(new Color(0, 255, 0));
} else {
g.setColor(new Color(0, 100, 0));
}
g.drawLine(n, (int)(h/2 - prof * scale), n, (int)(h/2 - prof * scale));
}
g.setColor(new Color(255, 0, 0, 64));