Compare commits

...

11 Commits

9 changed files with 173 additions and 43 deletions

View File

@@ -1 +1 @@
version=0.2.0
version=0.2.1

View File

@@ -47,6 +47,7 @@ public class AGC implements Effect {
gain += factor;
if (gain > limit) gain = limit;
if (gain < 0) gain = 0;
samples[i][Sentence.LEFT] *= gain;
samples[i][Sentence.RIGHT] *= gain;

View File

@@ -30,8 +30,6 @@ public class AudiobookRecorder extends JFrame {
// Settings - tweakable
public static final int PLAYBACK_CHUNK_SIZE = 1024;
public static final String SPHINX_MODEL = "resource:/edu/cmu/sphinx/models/en-us/en-us";
static Properties config = new Properties();
@@ -60,6 +58,7 @@ public class AudiobookRecorder extends JFrame {
JMenuItem bookNewChapter;
JMenuItem bookExportAudio;
JMenuItem bookPurgeBackups;
JMenu bookVisitACX;
JMenuItem bookVisitTitle;
JMenuItem bookVisitAudition;
@@ -214,8 +213,16 @@ public class AudiobookRecorder extends JFrame {
}
});
bookPurgeBackups = new JMenuItem("Purge Backups");
bookPurgeBackups.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
book.purgeBackups();
}
});
bookMenu.add(bookNewChapter);
bookMenu.add(bookExportAudio);
bookMenu.add(bookPurgeBackups);
bookVisitACX = new JMenu("Visit ACX");
bookMenu.add(bookVisitACX);
@@ -1454,6 +1461,8 @@ public class AudiobookRecorder extends JFrame {
if (!(book.getName().equals(tit))) {
book.renameBook(tit);
}
CacheManager.purgeCache();
}
});
menu.add(editData);
@@ -1814,16 +1823,10 @@ public class AudiobookRecorder extends JFrame {
int i = 0;
for (Enumeration s = c.children(); s.hasMoreElements();) {
Sentence snt = (Sentence)s.nextElement();
prefs.setProperty(String.format("%s.sentence.%08d.id", keybase, i), snt.getId());
prefs.setProperty(String.format("%s.sentence.%08d.text", keybase, i), snt.getText());
prefs.setProperty(String.format("%s.sentence.%08d.post-gap", keybase, i), Integer.toString(snt.getPostGap()));
prefs.setProperty(String.format("%s.sentence.%08d.start-offset", keybase, i), Integer.toString(snt.getStartOffset()));
prefs.setProperty(String.format("%s.sentence.%08d.end-offset", keybase, i), Integer.toString(snt.getEndOffset()));
prefs.setProperty(String.format("%s.sentence.%08d.locked", keybase, i), snt.isLocked() ? "true" : "false");
prefs.setProperty(String.format("%s.sentence.%08d.attention", keybase, i), snt.getAttentionFlag() ? "true" : "false");
prefs.setProperty(String.format("%s.sentence.%08d.gain", keybase, i), String.format("%.8f", snt.getGain()));
prefs.setProperty(String.format("%s.sentence.%08d.effect", keybase, i), snt.getEffectChain());
prefs.setProperty(String.format("%s.sentence.%08d.gaptype", keybase, i), snt.getPostGapType());
TreeMap<String, String> settings = snt.getSentenceData();
for (String key : settings.keySet()) {
prefs.setProperty(String.format("%s.sentence.%08d.%s", keybase, i, key), settings.get(key));
}
i++;
}
}
@@ -2182,8 +2185,12 @@ public class AudiobookRecorder extends JFrame {
try {
int blockSize = Options.getInteger("audio.playback.blocksize");
AudioFormat sampleformat = s.getAudioFormat();
AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 2, true, false);
float sampleRate = sampleformat.getSampleRate();
// sampleRate *= toolBar.getPlaybackSpeed();
AudioFormat format = new AudioFormat(sampleRate, 16, 2, true, false);
play = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer());
play.open(format);
@@ -2192,10 +2199,10 @@ public class AudiobookRecorder extends JFrame {
bookTree.scrollPathToVisible(new TreePath(s.getPath()));
data = s.getPCMData(effectsEnabled);
for (int pos = 0; pos < data.length; pos += PLAYBACK_CHUNK_SIZE) {
for (int pos = 0; pos < data.length; pos += blockSize) {
sampleWaveform.setPlayMarker(pos / format.getFrameSize());
int l = data.length - pos;
if (l > PLAYBACK_CHUNK_SIZE) l = PLAYBACK_CHUNK_SIZE;
if (l > blockSize) l = blockSize;
play.write(data, pos, l);
}
@@ -2344,6 +2351,8 @@ public class AudiobookRecorder extends JFrame {
try {
int blockSize = Options.getInteger("audio.playback.blocksize");
AudioFormat sampleformat = s.getAudioFormat();
AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 2, true, false);
@@ -2375,10 +2384,10 @@ public class AudiobookRecorder extends JFrame {
if (startPos > data.length) startPos = data.length;
if (endPos > data.length) endPos = data.length;
for (int pos = startPos; pos < endPos; pos += PLAYBACK_CHUNK_SIZE) {
for (int pos = startPos; pos < endPos; pos += blockSize) {
sampleWaveform.setPlayMarker(pos / format.getFrameSize());
int l = data.length - pos;
if (l > PLAYBACK_CHUNK_SIZE) l = PLAYBACK_CHUNK_SIZE;
if (l > blockSize) l = blockSize;
play.write(data, pos, l);
}
@@ -2420,8 +2429,12 @@ public class AudiobookRecorder extends JFrame {
try {
int blockSize = Options.getInteger("audio.playback.blocksize");
AudioFormat sampleformat = s.getAudioFormat();
AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 2, true, false);
float sampleRate = sampleformat.getSampleRate();
sampleRate *= toolBar.getPlaybackSpeed();
AudioFormat format = new AudioFormat(sampleRate, 16, 2, true, false);
play = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer());
play.open(format);
play.start();
@@ -2451,10 +2464,10 @@ public class AudiobookRecorder extends JFrame {
});
t.start();
}
for (int pos = 0; pos < data.length; pos += PLAYBACK_CHUNK_SIZE) {
for (int pos = 0; pos < data.length; pos += blockSize) {
sampleWaveform.setPlayMarker(pos / format.getFrameSize());
int l = data.length - pos;
if (l > PLAYBACK_CHUNK_SIZE) l = PLAYBACK_CHUNK_SIZE;
if (l > blockSize) l = blockSize;
play.write(data, pos, l);
}
@@ -2470,12 +2483,6 @@ public class AudiobookRecorder extends JFrame {
play.write(data, 0, data.length);
playing = null;
} else {
play.drain();
play.stop();
play.close();
play.open(format);
play.start();
play.drain();
data = getRoomNoise(s.getPostGap());
play.write(data, 0, data.length);
}
@@ -3369,6 +3376,9 @@ public class AudiobookRecorder extends JFrame {
}
newSentence.writeAudioData(startSamples);
newSentence.setPostGapType("continuation");
newSentence.setPostGap(Options.getInteger("catenation.short-sentence"));
selectedSentence.writeAudioData(endSamples);
selectedSentence.autoTrimSample();
newSentence.autoTrimSample();

View File

@@ -206,4 +206,14 @@ public class Book extends DefaultMutableTreeNode {
return out;
}
public void purgeBackups() {
for (Enumeration o = children(); o.hasMoreElements();) {
Object ob = (Object)o.nextElement();
if (ob instanceof Chapter) {
Chapter c = (Chapter)ob;
c.purgeBackups();
}
}
}
}

View File

@@ -34,4 +34,11 @@ public class CacheManager {
cache.remove(c);
c.clearCache();
}
public static void purgeCache() {
for (Cacheable c : cache) {
c.clearCache();
}
cache.clear();
}
}

View File

@@ -116,7 +116,7 @@ public class Chapter extends DefaultMutableTreeNode {
audioAttributes.setCodec("libmp3lame");
audioAttributes.setBitRate(Options.getInteger("audio.export.bitrate"));
audioAttributes.setSamplingRate(Options.getInteger("audio.export.samplerate"));
audioAttributes.setChannels(2); //new Integer(2));
audioAttributes.setChannels(Options.getInteger("audio.export.channels")); //new Integer(2));
attributes.setFormat("mp3");
attributes.setAudioAttributes(audioAttributes);
@@ -239,4 +239,14 @@ public class Chapter extends DefaultMutableTreeNode {
}
}
public void purgeBackups() {
for (Enumeration o = children(); o.hasMoreElements();) {
Object ob = (Object)o.nextElement();
if (ob instanceof Sentence) {
Sentence s = (Sentence)ob;
s.purgeBackups();
}
}
}
}

View File

@@ -19,6 +19,8 @@ public class MainToolBar extends JToolBar {
JButtonSpacePlay eq;
JToggleButtonSpacePlay mic;
JComboBox<String> playbackSpeed;
JToggleButtonSpacePlay disableEffects;
AudiobookRecorder root;
@@ -100,6 +102,7 @@ public class MainToolBar extends JToolBar {
eq = new JButtonSpacePlay(Icons.eq, "Reload Effects", new ActionListener() {
public void actionPerformed(ActionEvent e) {
root.loadEffects();
CacheManager.purgeCache();
}
});
@@ -128,6 +131,7 @@ public class MainToolBar extends JToolBar {
}
}
});
add(mic);
@@ -152,7 +156,20 @@ public class MainToolBar extends JToolBar {
add(disableEffects);
addSeparator();
add(new JLabel("Playback speed: "));
playbackSpeed = new JComboBox<String>(new String[] {
"0.75x",
"1.00x",
"1.25x",
"1.50x",
"1.75x"
});
playbackSpeed.setFocusable(false);
playbackSpeed.setSelectedIndex(1);
add(playbackSpeed);
setFloatable(false);
}
@@ -161,4 +178,9 @@ public class MainToolBar extends JToolBar {
playtoSentence.setEnabled(b);
}
public float getPlaybackSpeed() {
int v = playbackSpeed.getSelectedIndex();
return 0.75f + (0.25f * v);
}
}

View File

@@ -24,6 +24,8 @@ public class Options extends JDialog {
JComboBox<KVPair> rateList;
JComboBox<KVPair> bitDepth;
JComboBox<KVPair> trimMethod;
JComboBox<KVPair> fftBlockSize;
JComboBox<KVPair> playbackBlockSize;
JTextField storageFolder;
JTextField archiveFolder;
JSpinner preChapterGap;
@@ -34,6 +36,7 @@ public class Options extends JDialog {
JSpinner postSectionGap;
JTextField ffmpegLocation;
JComboBox<KVPair> bitRate;
JComboBox<KVPair> channels;
JComboBox<KVPair> exportRate;
JCheckBox enableParsing;
JSpinner cacheSize;
@@ -297,10 +300,12 @@ public class Options extends JDialog {
bitDepth = addDropdown(optionsPanel, "Sample resolution:", getResolutionList(), get("audio.recording.resolution"));
trimMethod = addDropdown(optionsPanel, "Auto-trim method:", getTrimMethods(), get("audio.recording.trim"));
fftThreshold = addSpinner(optionsPanel, "FFT threshold:", 0, 100, 1, getInteger("audio.recording.trim.fft"), "");
fftBlockSize = addDropdown(optionsPanel, "FFT Block size:", getFFTBlockSizes(), get("audio.recording.trim.blocksize"));
addSeparator(optionsPanel);
playbackList = addDropdown(optionsPanel, "Playback device:", getPlaybackMixerList(), get("audio.playback.device"));
playbackBlockSize = addDropdown(optionsPanel, "Playback Block size:", getPlaybackBlockSizes(), get("audio.playback.blocksize"));
addSeparator(optionsPanel);
storageFolder = addFilePath(optionsPanel, "Storage folder:", get("path.storage"), true);
archiveFolder = addFilePath(optionsPanel, "Archive folder:", get("path.archive"), true);
@@ -318,6 +323,7 @@ public class Options extends JDialog {
ffmpegLocation = addFilePath(optionsPanel, "FFMPEG location:", get("path.ffmpeg"), false);
bitRate = addDropdown(optionsPanel, "Export bitrate:", getBitrates(), get("audio.export.bitrate"));
channels = addDropdown(optionsPanel, "Export channels:", getChannelCountList(), get("audio.export.channels"));
exportRate = addDropdown(optionsPanel, "Export sample rate:", getSampleRateList(), get("audio.export.samplerate"));
@@ -565,6 +571,8 @@ public class Options extends JDialog {
} else {
defaultPrefs.put("audio.playback.device", "");
}
defaultPrefs.put("audio.recording.trim.blocksize", "4096");
defaultPrefs.put("audio.playback.blocksize", "4096");
defaultPrefs.put("catenation.pre-chapter", "1000");
defaultPrefs.put("catenation.post-chapter", "1500");
@@ -580,6 +588,7 @@ public class Options extends JDialog {
defaultPrefs.put("path.ffmpeg", "");
defaultPrefs.put("audio.export.bitrate", "256000");
defaultPrefs.put("audio.export.channels", "2");
defaultPrefs.put("audio.export.samplerate", "44100");
defaultPrefs.put("process.sphinx", "false");
@@ -703,11 +712,14 @@ public class Options extends JDialog {
set("catenation.post-paragraph", postParagraphGap.getValue());
set("catenation.post-section", postSectionGap.getValue());
set("audio.export.bitrate", ((KVPair)bitRate.getSelectedItem()).key);
set("audio.export.channels", ((KVPair)channels.getSelectedItem()).key);
set("audio.export.samplerate", ((KVPair)exportRate.getSelectedItem()).key);
set("process.sphinx", enableParsing.isSelected());
set("editor.external", externalEditor.getText());
set("cache.size", cacheSize.getValue());
set("audio.recording.trim.fft", fftThreshold.getValue());
set("audio.recording.trim.blocksize", ((KVPair)fftBlockSize.getSelectedItem()).key);
set("audio.playback.blocksize", ((KVPair)playbackBlockSize.getSelectedItem()).key);
set("effects.ethereal.offset", etherealOffset.getValue());
set("effects.ethereal.iterations", etherealIterations.getValue());
@@ -782,6 +794,31 @@ public class Options extends JDialog {
return pairs;
}
public static KVPair[] getFFTBlockSizes() {
KVPair[] pairs = new KVPair[8];
pairs[0] = new KVPair<String, String>("1024", "1024");
pairs[1] = new KVPair<String, String>("2048", "2048");
pairs[2] = new KVPair<String, String>("4096", "4096");
pairs[3] = new KVPair<String, String>("8192", "8192");
pairs[4] = new KVPair<String, String>("16384", "16384");
pairs[5] = new KVPair<String, String>("32768", "32768");
pairs[6] = new KVPair<String, String>("65536", "65537");
pairs[7] = new KVPair<String, String>("131072", "131072");
return pairs;
}
public static KVPair[] getPlaybackBlockSizes() {
KVPair[] pairs = new KVPair[8];
pairs[0] = new KVPair<String, String>("1024", "1024");
pairs[1] = new KVPair<String, String>("2048", "2048");
pairs[2] = new KVPair<String, String>("4096", "4096");
pairs[3] = new KVPair<String, String>("8192", "8192");
pairs[4] = new KVPair<String, String>("16384", "16384");
pairs[5] = new KVPair<String, String>("32768", "32768");
pairs[6] = new KVPair<String, String>("65536", "65537");
pairs[7] = new KVPair<String, String>("131072", "131072");
return pairs;
}
public static void createEffectChains() {
effectChains = new ArrayList<EffectGroup>();

View File

@@ -31,7 +31,7 @@ import java.util.Timer;
public class Sentence extends DefaultMutableTreeNode implements Cacheable {
String text;
String id;
int postGap;
@@ -225,17 +225,18 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
return;
}
int fftSize = Options.getInteger("audio.recording.trim.blocksize");
int blocks = samples.length / 4096 + 1;
int blocks = samples.length / fftSize + 1;
int[] intens = new int[blocks];
int block = 0;
for (int i = 0; i < samples.length; i+= 4096) {
double[] real = new double[4096];
double[] imag = new double[4096];
for (int i = 0; i < samples.length; i+= fftSize) {
double[] real = new double[fftSize];
double[] imag = new double[fftSize];
for (int j = 0; j < 4096; j++) {
for (int j = 0; j < fftSize; j++) {
if (i + j < samples.length) {
real[j] = (samples[i+j][LEFT] + samples[i+j][RIGHT]) / 2d;
imag[j] = 0;
@@ -247,14 +248,14 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
double[] buckets = FFT.fft(real, imag, true);
double av = 0;
for (int j = 1; j < 2048; j++) {
for (int j = 1; j < fftSize/2; j++) {
av += Math.abs(buckets[j]);
}
av /= 2047d;
av /= (fftSize / 2);
intens[block] = 0;
for (int j = 2; j < 4096; j += 2) {
for (int j = 2; j < fftSize; j += 2) {
double d = Math.abs(av - buckets[j]);
if (d > 0.05) {
intens[block]++;
@@ -278,7 +279,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
start = 0;
}
startOffset = start * 4096;
startOffset = start * fftSize;
if (startOffset < 0) startOffset = 0;
if (startOffset >= samples.length) startOffset = samples.length;
@@ -295,9 +296,9 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
end = blocks - 1;
}
endOffset = end * 4096;
endOffset = (end+1) * fftSize;
if (endOffset <= startOffset) endOffset = startOffset + 4096;
if (endOffset <= startOffset) endOffset = startOffset + fftSize;
if (endOffset < 0) endOffset = 0;
if (endOffset >= samples.length) endOffset = samples.length;
updateCrossings(useRaw);
@@ -337,7 +338,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
startOffset = 0;
}
startOffset -= 4096;
int fftSize = Options.getInteger("audio.recording.trim.blocksize");
startOffset -= fftSize;
for (int i = samples.length-1; i >= 0; i--) {
endOffset = i;
@@ -348,9 +350,9 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
}
}
endOffset += 4096;
endOffset += fftSize;
if (endOffset <= startOffset) endOffset = startOffset + 4096;
if (endOffset <= startOffset) endOffset = startOffset + fftSize;
if (endOffset <= 0) {
endOffset = samples.length-1;
}
@@ -1308,4 +1310,35 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
public void debug(String txt) {
Debug.debug(String.format("%s: %s", id, txt));
}
public TreeMap<String, String> getSentenceData() {
TreeMap<String, String> out = new TreeMap<String, String>();
out.put("id", getId());
out.put("text", getText());
out.put("post-gap", Integer.toString(getPostGap()));
out.put("start-offset", Integer.toString(getStartOffset()));
out.put("end-offset", Integer.toString(getEndOffset()));
out.put("locked", isLocked() ? "true" : "false");
out.put("attention", getAttentionFlag() ? "true" : "false");
out.put("gain", String.format("%.8f", getGain()));
out.put("effect", getEffectChain());
out.put("gaptype", getPostGapType());
return out;
}
public void purgeBackups() {
File whereto = getFile().getParentFile();
String name = getFile().getName();
File[] files = whereto.listFiles();
for (File f : files) {
String fn = f.getName();
if (fn.startsWith("backup-") && fn.endsWith("-" + name)) {
f.delete();
}
}
}
}