Compare commits

...

32 Commits

Author SHA1 Message Date
0cfd066f4f Released 0.2.1 2019-12-18 11:31:43 +00:00
217ccb915a Added mono/stereo export option 2019-12-16 15:54:34 +00:00
1dd3e9d86f Add purge backup files menu entry 2019-11-28 14:32:11 +00:00
d3d81d71fe Remove play stop/start 2019-11-28 12:19:31 +00:00
80c110afa9 Added playback block size option 2019-10-13 20:11:07 +01:00
1c08b9a51d Purge cache when changing base effect or reloading effects 2019-09-08 22:37:27 +01:00
4ad30106b6 Move save population to sentence 2019-09-08 22:36:58 +01:00
9100d0e35a Improvements to AGC limiting 2019-09-08 22:36:37 +01:00
b8dea19c30 Make splits continuations and have single playback always 1.00x 2019-09-08 12:30:52 +01:00
671f2b9270 Added adjustable FFT block size 2019-09-07 21:24:46 +01:00
d619fb2f4d Added playback speed option 2019-09-07 20:34:02 +01:00
3eb6704f2f Released 0.2.0 2019-09-05 00:21:04 +01:00
cd24beb8a6 Removed pointless garbage collection calls. Massive speedup 2019-09-05 00:20:29 +01:00
fa287305eb Use 100* integer for gain comparison 2019-09-03 00:12:45 +01:00
3fb656b693 Add small visual gap for paragraphs 2019-09-03 00:12:26 +01:00
f514993525 Added global effects disable and fixed caching problems 2019-09-02 21:44:44 +01:00
81787260c9 Lighten font and make flashing panel alternate red/green 2019-08-27 17:14:17 +01:00
f9ad396228 Added chapter and book level resetting of post gaps 2019-08-25 14:08:25 +01:00
8976f2e359 Add post gap classification 2019-08-25 13:41:46 +01:00
187c3edaf6 Add waveform split and cut functionality 2019-08-14 19:58:06 +01:00
02e85fb354 Released 0.1.9 2019-08-13 14:03:45 +01:00
289834021f Finally fixed pesky memory leak 2019-08-13 14:02:14 +01:00
04fea4acb2 Added race condition debugging and reduced recording buffer size for better responsiveness 2019-08-11 12:51:10 +01:00
54739b0a75 Add Tritex to recording controls 2019-08-11 11:15:55 +01:00
ea5520a729 Fix double apply gain shift in AGC 2019-07-26 14:06:30 +01:00
0a19d8d308 Protect against no effects 2019-07-24 14:17:38 +01:00
4dbe3e23b7 Remove debugging cruft 2019-07-23 12:45:46 +01:00
c43cfc3b69 Improve caching policy 2019-07-23 12:44:24 +01:00
babd3d2052 Release 0.1.8 2019-07-22 23:24:16 +01:00
9464839b4d Turn on JTattoo 2019-07-22 23:24:07 +01:00
bcf7875414 Scrap Sample object and use double[][] instead. Disable processed audio caching - it causes heap overflows 2019-07-22 23:23:03 +01:00
732894a0fb Add Chain effect to connect effects together 2019-07-22 10:43:53 +01:00
34 changed files with 1412 additions and 316 deletions

View File

@@ -1 +1 @@
version=0.1.7 version=0.2.1

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

View File

@@ -795,7 +795,7 @@ public abstract class AbstractTheme extends MetalTheme {
public FontUIResource getControlTextFont() { public FontUIResource getControlTextFont() {
if (controlFont == null) { if (controlFont == null) {
if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) { if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) {
controlFont = new FontUIResource(DIALOG, Font.BOLD, 14); controlFont = new FontUIResource(DIALOG, Font.PLAIN, 14); // bold
} else { } else {
controlFont = new FontUIResource(DIALOG, Font.PLAIN, 12); controlFont = new FontUIResource(DIALOG, Font.PLAIN, 12);
} }
@@ -806,7 +806,7 @@ public abstract class AbstractTheme extends MetalTheme {
public FontUIResource getSystemTextFont() { public FontUIResource getSystemTextFont() {
if (systemFont == null) { if (systemFont == null) {
if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) { if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) {
systemFont = new FontUIResource(DIALOG, Font.BOLD, 14); systemFont = new FontUIResource(DIALOG, Font.PLAIN, 14); // bold
} else { } else {
systemFont = new FontUIResource(DIALOG, Font.PLAIN, 12); systemFont = new FontUIResource(DIALOG, Font.PLAIN, 12);
} }
@@ -817,7 +817,7 @@ public abstract class AbstractTheme extends MetalTheme {
public FontUIResource getUserTextFont() { public FontUIResource getUserTextFont() {
if (userFont == null) { if (userFont == null) {
if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) { if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) {
userFont = new FontUIResource(DIALOG, Font.BOLD, 14); userFont = new FontUIResource(DIALOG, Font.PLAIN, 14); // bold
} else { } else {
userFont = new FontUIResource(DIALOG, Font.PLAIN, 12); userFont = new FontUIResource(DIALOG, Font.PLAIN, 12);
} }
@@ -828,7 +828,7 @@ public abstract class AbstractTheme extends MetalTheme {
public FontUIResource getMenuTextFont() { public FontUIResource getMenuTextFont() {
if (menuFont == null) { if (menuFont == null) {
if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) { if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) {
menuFont = new FontUIResource(DIALOG, Font.BOLD, 14); menuFont = new FontUIResource(DIALOG, Font.PLAIN, 14); // bold
} else { } else {
menuFont = new FontUIResource(DIALOG, Font.PLAIN, 12); menuFont = new FontUIResource(DIALOG, Font.PLAIN, 12);
} }
@@ -839,9 +839,9 @@ public abstract class AbstractTheme extends MetalTheme {
public FontUIResource getWindowTitleFont() { public FontUIResource getWindowTitleFont() {
if (windowTitleFont == null) { if (windowTitleFont == null) {
if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) { if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) {
windowTitleFont = new FontUIResource(DIALOG, Font.BOLD, 14); windowTitleFont = new FontUIResource(DIALOG, Font.PLAIN, 14); // bold
} else { } else {
windowTitleFont = new FontUIResource(DIALOG, Font.BOLD, 12); windowTitleFont = new FontUIResource(DIALOG, Font.PLAIN, 12); // bold
} }
} }
return windowTitleFont; return windowTitleFont;
@@ -850,7 +850,7 @@ public abstract class AbstractTheme extends MetalTheme {
public FontUIResource getSubTextFont() { public FontUIResource getSubTextFont() {
if (smallFont == null) { if (smallFont == null) {
if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) { if (JTattooUtilities.isLinux() && JTattooUtilities.isHiresScreen()) {
smallFont = new FontUIResource(DIALOG, Font.BOLD, 12); smallFont = new FontUIResource(DIALOG, Font.PLAIN, 12); // bold
} else { } else {
smallFont = new FontUIResource(DIALOG, Font.PLAIN, 10); smallFont = new FontUIResource(DIALOG, Font.PLAIN, 10);
} }

View File

@@ -125,11 +125,11 @@ public class HiFiDefaultTheme extends AbstractTheme {
tooltipForegroundColor = white; tooltipForegroundColor = white;
tooltipBackgroundColor = new ColorUIResource(24, 24, 24); tooltipBackgroundColor = new ColorUIResource(24, 24, 24);
controlFont = new FontUIResource("Dialog", Font.BOLD, 12); controlFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
systemFont = new FontUIResource("Dialog", Font.BOLD, 12); systemFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
userFont = new FontUIResource("Dialog", Font.BOLD, 12); userFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
menuFont = new FontUIResource("Dialog", Font.BOLD, 12); menuFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
windowTitleFont = new FontUIResource("Dialog", Font.BOLD, 12); windowTitleFont = new FontUIResource("Dialog", Font.PLAIN, 12); // bold
smallFont = new FontUIResource("Dialog", Font.PLAIN, 10); smallFont = new FontUIResource("Dialog", Font.PLAIN, 10);
} }

View File

@@ -42,18 +42,18 @@ public class HiFiLookAndFeel extends AbstractLookAndFeel {
private static final Properties giantFontProps = new Properties(); private static final Properties giantFontProps = new Properties();
static { static {
smallFontProps.setProperty("controlTextFont", "Dialog bold 10"); smallFontProps.setProperty("controlTextFont", "Dialog 10"); // bold
smallFontProps.setProperty("systemTextFont", "Dialog bold 10"); smallFontProps.setProperty("systemTextFont", "Dialog 10"); // bold
smallFontProps.setProperty("userTextFont", "Dialog 10"); smallFontProps.setProperty("userTextFont", "Dialog 10");
smallFontProps.setProperty("menuTextFont", "Dialog bold 10"); smallFontProps.setProperty("menuTextFont", "Dialog 10"); // bold
smallFontProps.setProperty("windowTitleFont", "Dialog bold 10"); smallFontProps.setProperty("windowTitleFont", "Dialog 10"); // bold
smallFontProps.setProperty("subTextFont", "Dialog 8"); smallFontProps.setProperty("subTextFont", "Dialog 8");
largeFontProps.setProperty("controlTextFont", "Dialog bold 14"); largeFontProps.setProperty("controlTextFont", "Dialog 14"); // bold
largeFontProps.setProperty("systemTextFont", "Dialog bold 14"); largeFontProps.setProperty("systemTextFont", "Dialog 14"); // bold
largeFontProps.setProperty("userTextFont", "Dialog bold 14"); largeFontProps.setProperty("userTextFont", "Dialog 14"); // bold
largeFontProps.setProperty("menuTextFont", "Dialog bold 14"); largeFontProps.setProperty("menuTextFont", "Dialog 14"); // bold
largeFontProps.setProperty("windowTitleFont", "Dialog bold 14"); largeFontProps.setProperty("windowTitleFont", "Dialog 14"); // bold
largeFontProps.setProperty("subTextFont", "Dialog 12"); largeFontProps.setProperty("subTextFont", "Dialog 12");
giantFontProps.setProperty("controlTextFont", "Dialog 18"); giantFontProps.setProperty("controlTextFont", "Dialog 18");

View File

@@ -25,31 +25,32 @@ public class AGC implements Effect {
return getName(); return getName();
} }
public void process(Sample[] samples) { public void process(double[][] samples) {
gain = 1d; gain = 1d;
for (int i = 0; i < samples.length; i++) { for (int i = 0; i < samples.length; i++) {
double absSampleLeft = Math.abs(samples[i].left) * gain; double absSampleLeft = Math.abs(samples[i][Sentence.LEFT]) * gain;
double absSampleRight = Math.abs(samples[i].right) * gain; double absSampleRight = Math.abs(samples[i][Sentence.RIGHT]) * gain;
double factor = 0.0d;
if (absSampleLeft > ceiling) { if (absSampleLeft > ceiling) {
gain -= attack; factor = -attack;
if (gain < 0) gain = 0;
} }
if (absSampleRight > ceiling) { if (absSampleRight > ceiling) {
gain -= attack; factor = -attack;
if (gain < 0) gain = 0;
} }
if ((absSampleLeft < ceiling) && (absSampleRight < ceiling)) { if ((absSampleLeft < ceiling) && (absSampleRight < ceiling)) {
gain += decay; factor = decay;
if (gain > limit) {
gain = limit;
}
} }
samples[i].left *= gain; gain += factor;
samples[i].right *= gain; if (gain > limit) gain = limit;
if (gain < 0) gain = 0;
samples[i][Sentence.LEFT] *= gain;
samples[i][Sentence.RIGHT] *= gain;
} }
} }

View File

@@ -19,10 +19,10 @@ public class Amplifier implements Effect {
return null; return null;
} }
public void process(Sample[] samples) { public void process(double[][] samples) {
for (int i = 0; i < samples.length; i++) { for (int i = 0; i < samples.length; i++) {
samples[i].left *= gain; samples[i][Sentence.LEFT] *= gain;
samples[i].right *= gain; samples[i][Sentence.RIGHT] *= gain;
} }
} }

View File

@@ -24,13 +24,12 @@ import edu.cmu.sphinx.api.*;
import edu.cmu.sphinx.decoder.adaptation.*; import edu.cmu.sphinx.decoder.adaptation.*;
import edu.cmu.sphinx.result.*; import edu.cmu.sphinx.result.*;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import java.util.concurrent.*;
public class AudiobookRecorder extends JFrame { public class AudiobookRecorder extends JFrame {
// Settings - tweakable // Settings - tweakable
public static final int PLAYBACK_CHUNK_SIZE = 16384;
public static final String SPHINX_MODEL = "resource:/edu/cmu/sphinx/models/en-us/en-us"; public static final String SPHINX_MODEL = "resource:/edu/cmu/sphinx/models/en-us/en-us";
static Properties config = new Properties(); static Properties config = new Properties();
@@ -38,6 +37,11 @@ public class AudiobookRecorder extends JFrame {
String defaultEffectChain = "none"; String defaultEffectChain = "none";
public final static int IDLE = 0;
public final static int RECORDING = 1;
public final static int STOPPING = 2;
public int state = IDLE;
MainToolBar toolBar; MainToolBar toolBar;
JMenuBar menuBar; JMenuBar menuBar;
@@ -54,6 +58,7 @@ public class AudiobookRecorder extends JFrame {
JMenuItem bookNewChapter; JMenuItem bookNewChapter;
JMenuItem bookExportAudio; JMenuItem bookExportAudio;
JMenuItem bookPurgeBackups;
JMenu bookVisitACX; JMenu bookVisitACX;
JMenuItem bookVisitTitle; JMenuItem bookVisitTitle;
JMenuItem bookVisitAudition; JMenuItem bookVisitAudition;
@@ -92,10 +97,14 @@ public class AudiobookRecorder extends JFrame {
JSpinner gainPercent; JSpinner gainPercent;
JCheckBox locked; JCheckBox locked;
JCheckBox attention; JCheckBox attention;
JCheckBox rawAudio;
JButtonSpacePlay reprocessAudioFFT; JButtonSpacePlay reprocessAudioFFT;
JButtonSpacePlay reprocessAudioPeak; JButtonSpacePlay reprocessAudioPeak;
JButtonSpacePlay normalizeAudio; JButtonSpacePlay normalizeAudio;
JToggleButtonSpacePlay selectSplitMode;
JToggleButtonSpacePlay selectCutMode;
JButtonSpacePlay doCutSplit;
JComboBox<KVPair<String,String>> effectChain; JComboBox<KVPair<String,String>> effectChain;
@@ -105,6 +114,8 @@ public class AudiobookRecorder extends JFrame {
SourceDataLine play = null; SourceDataLine play = null;
boolean effectsEnabled = true;
public TargetDataLine microphone = null; public TargetDataLine microphone = null;
public AudioInputStream microphoneStream = null; public AudioInputStream microphoneStream = null;
@@ -202,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(bookNewChapter);
bookMenu.add(bookExportAudio); bookMenu.add(bookExportAudio);
bookMenu.add(bookPurgeBackups);
bookVisitACX = new JMenu("Visit ACX"); bookVisitACX = new JMenu("Visit ACX");
bookMenu.add(bookVisitACX); bookMenu.add(bookVisitACX);
@@ -298,6 +317,7 @@ public class AudiobookRecorder extends JFrame {
window = this; window = this;
try { try {
String clsname = "com.jtattoo.plaf.hifi.HiFiLookAndFeel"; String clsname = "com.jtattoo.plaf.hifi.HiFiLookAndFeel";
UIManager.setLookAndFeel(clsname); UIManager.setLookAndFeel(clsname);
@@ -312,8 +332,6 @@ public class AudiobookRecorder extends JFrame {
Method mth = cls.getMethod("setCurrentTheme", cArg); Method mth = cls.getMethod("setCurrentTheme", cArg);
mth.invoke(cls, p); mth.invoke(cls, p);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -386,7 +404,7 @@ public class AudiobookRecorder extends JFrame {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (selectedSentence != null) { if (selectedSentence != null) {
selectedSentence.autoTrimSampleFFT(); selectedSentence.autoTrimSampleFFT();
sampleWaveform.setData(selectedSentence.getDoubleAudioData()); sampleWaveform.setData(selectedSentence.getDoubleAudioData(effectsEnabled));
sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset()); sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset());
sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing()); sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing());
postSentenceGap.setValue(selectedSentence.getPostGap()); postSentenceGap.setValue(selectedSentence.getPostGap());
@@ -399,7 +417,7 @@ public class AudiobookRecorder extends JFrame {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (selectedSentence != null) { if (selectedSentence != null) {
selectedSentence.autoTrimSamplePeak(); selectedSentence.autoTrimSamplePeak();
sampleWaveform.setData(selectedSentence.getDoubleAudioData()); sampleWaveform.setData(selectedSentence.getDoubleAudioData(effectsEnabled));
sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset()); sampleWaveform.setMarkers(selectedSentence.getStartOffset(), selectedSentence.getEndOffset());
sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing()); sampleWaveform.setAltMarkers(selectedSentence.getStartCrossing(), selectedSentence.getEndCrossing());
postSentenceGap.setValue(selectedSentence.getPostGap()); postSentenceGap.setValue(selectedSentence.getPostGap());
@@ -412,11 +430,28 @@ public class AudiobookRecorder extends JFrame {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (selectedSentence != null) { if (selectedSentence != null) {
selectedSentence.normalize(); selectedSentence.normalize();
sampleWaveform.setData(selectedSentence.getDoubleAudioData()); sampleWaveform.setData(selectedSentence.getDoubleAudioData(effectsEnabled));
} }
} }
}); });
selectSplitMode = new JToggleButtonSpacePlay(Icons.split, "Toggle split mode", new ActionListener() {
public void actionPerformed(ActionEvent e) {
toggleSplitMode();
}
});
selectCutMode = new JToggleButtonSpacePlay(Icons.cut, "Toggle cut mode", new ActionListener() {
public void actionPerformed(ActionEvent e) {
toggleCutMode();
}
});
doCutSplit = new JButtonSpacePlay(Icons.docut, "Perform cut or split", new ActionListener() {
public void actionPerformed(ActionEvent e) {
executeCutOrSplit();
}
});
postSentenceGap = new JSpinner(new SteppedNumericSpinnerModel(0, 5000, 100, 0)); postSentenceGap = new JSpinner(new SteppedNumericSpinnerModel(0, 5000, 100, 0));
postSentenceGap.setPreferredSize(new Dimension(50, 20)); postSentenceGap.setPreferredSize(new Dimension(50, 20));
@@ -438,7 +473,7 @@ public class AudiobookRecorder extends JFrame {
JSpinner ob = (JSpinner)e.getSource(); JSpinner ob = (JSpinner)e.getSource();
if (selectedSentence != null) { if (selectedSentence != null) {
selectedSentence.setGain((Integer)ob.getValue() / 100d); selectedSentence.setGain((Integer)ob.getValue() / 100d);
sampleWaveform.setData(selectedSentence.getDoubleAudioData()); sampleWaveform.setData(selectedSentence.getDoubleAudioData(effectsEnabled));
} }
} }
}); });
@@ -457,6 +492,11 @@ public class AudiobookRecorder extends JFrame {
controlsLeft.add(reprocessAudioPeak); controlsLeft.add(reprocessAudioPeak);
controlsLeft.add(normalizeAudio); controlsLeft.add(normalizeAudio);
rawAudio = new JCheckBox("Raw Audio");
rawAudio.setFocusable(false);
controlsTop.add(rawAudio);
locked = new JCheckBox("Phrase locked"); locked = new JCheckBox("Phrase locked");
locked.setFocusable(false); locked.setFocusable(false);
@@ -476,6 +516,11 @@ public class AudiobookRecorder extends JFrame {
gainPercent.setEnabled(!selectedSentence.isLocked()); gainPercent.setEnabled(!selectedSentence.isLocked());
reprocessAudioFFT.setEnabled(!selectedSentence.isLocked()); reprocessAudioFFT.setEnabled(!selectedSentence.isLocked());
reprocessAudioPeak.setEnabled(!selectedSentence.isLocked()); reprocessAudioPeak.setEnabled(!selectedSentence.isLocked());
selectCutMode.setEnabled(!selectedSentence.isLocked());
selectSplitMode.setEnabled(!selectedSentence.isLocked());
doCutSplit.setEnabled(false);
selectCutMode.setSelected(false);
selectSplitMode.setSelected(false);
bookTreeModel.reload(selectedSentence); bookTreeModel.reload(selectedSentence);
} }
@@ -549,6 +594,10 @@ public class AudiobookRecorder extends JFrame {
} }
}); });
controlsBottom.add(selectSplitMode);
controlsBottom.add(selectCutMode);
controlsBottom.add(doCutSplit);
sampleControl.add(controlsTop, BorderLayout.NORTH); sampleControl.add(controlsTop, BorderLayout.NORTH);
sampleControl.add(controlsLeft, BorderLayout.WEST); sampleControl.add(controlsLeft, BorderLayout.WEST);
sampleControl.add(controlsRight, BorderLayout.EAST); sampleControl.add(controlsRight, BorderLayout.EAST);
@@ -586,8 +635,13 @@ public class AudiobookRecorder extends JFrame {
centralPanel.getActionMap().put("startRecord", new AbstractAction() { centralPanel.getActionMap().put("startRecord", new AbstractAction() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (bookTree.isEditing()) return; if (!getLock()) return;
if (bookTree.isEditing()) {
freeLock();
return;
}
if (getNoiseFloor() == 0) { if (getNoiseFloor() == 0) {
freeLock();
alertNoRoomNoise(); alertNoRoomNoise();
return; return;
} }
@@ -596,8 +650,13 @@ public class AudiobookRecorder extends JFrame {
}); });
centralPanel.getActionMap().put("startRecordShort", new AbstractAction() { centralPanel.getActionMap().put("startRecordShort", new AbstractAction() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (bookTree.isEditing()) return; if (!getLock()) return;
if (bookTree.isEditing()) {
freeLock();
return;
}
if (getNoiseFloor() == 0) { if (getNoiseFloor() == 0) {
freeLock();
alertNoRoomNoise(); alertNoRoomNoise();
return; return;
} }
@@ -606,9 +665,14 @@ public class AudiobookRecorder extends JFrame {
}); });
centralPanel.getActionMap().put("startRecordNewPara", new AbstractAction() { centralPanel.getActionMap().put("startRecordNewPara", new AbstractAction() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (bookTree.isEditing()) return; if (!getLock()) return;
if (bookTree.isEditing()) {
freeLock();
return;
}
if (getNoiseFloor() == 0) { if (getNoiseFloor() == 0) {
alertNoRoomNoise(); alertNoRoomNoise();
freeLock();
return; return;
} }
startRecordingNewParagraph(); startRecordingNewParagraph();
@@ -616,8 +680,13 @@ public class AudiobookRecorder extends JFrame {
}); });
centralPanel.getActionMap().put("startRecordNewSection", new AbstractAction() { centralPanel.getActionMap().put("startRecordNewSection", new AbstractAction() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (bookTree.isEditing()) return; if (!getLock()) return;
if (bookTree.isEditing()) {
freeLock();
return;
}
if (getNoiseFloor() == 0) { if (getNoiseFloor() == 0) {
freeLock();
alertNoRoomNoise(); alertNoRoomNoise();
return; return;
} }
@@ -626,8 +695,13 @@ public class AudiobookRecorder extends JFrame {
}); });
centralPanel.getActionMap().put("startRerecord", new AbstractAction() { centralPanel.getActionMap().put("startRerecord", new AbstractAction() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (bookTree.isEditing()) return; if (!getLock()) return;
if (bookTree.isEditing()) {
freeLock();
return;
}
if (getNoiseFloor() == 0) { if (getNoiseFloor() == 0) {
freeLock();
alertNoRoomNoise(); alertNoRoomNoise();
return; return;
} }
@@ -637,7 +711,9 @@ public class AudiobookRecorder extends JFrame {
centralPanel.getActionMap().put("stopRecord", new AbstractAction() { centralPanel.getActionMap().put("stopRecord", new AbstractAction() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (bookTree.isEditing()) return; if (bookTree.isEditing()) return;
stopLock();
stopRecording(); stopRecording();
freeLock();
} }
}); });
centralPanel.getActionMap().put("deleteLast", new AbstractAction() { centralPanel.getActionMap().put("deleteLast", new AbstractAction() {
@@ -916,6 +992,72 @@ public class AudiobookRecorder extends JFrame {
} }
}); });
JMenu setGapType = new JMenu("Post-gap Type...");
String sentenceText = " Sentence";
String continuationText = " Continuation";
String paragraphText = " Paragraph";
String sectionText = " Section";
if (s.getPostGapType().equals("sentence")) {
sentenceText = "\u2713 Sentence";
} else if (s.getPostGapType().equals("continuation")) {
continuationText = "\u2713 Continuation";
} else if (s.getPostGapType().equals("paragraph")) {
paragraphText = "\u2713 Paragraph";
} else if (s.getPostGapType().equals("section")) {
sectionText = "\u2713 Section";
}
JMenuObject2 gapTypeSentence = new JMenuObject2(sentenceText, s, "sentence", new ActionListener() {
public void actionPerformed(ActionEvent e) {
JMenuObject2 o = (JMenuObject2)e.getSource();
Sentence sent = (Sentence)o.getObject1();
String type = (String)o.getObject2();
sent.setPostGapType(type);
sent.setPostGap(Options.getInteger("catenation.post-sentence"));
bookTreeModel.reload(sent);
}
});
setGapType.add(gapTypeSentence);
JMenuObject2 gapTypeContinuation = new JMenuObject2(continuationText, s, "continuation", new ActionListener() {
public void actionPerformed(ActionEvent e) {
JMenuObject2 o = (JMenuObject2)e.getSource();
Sentence sent = (Sentence)o.getObject1();
String type = (String)o.getObject2();
sent.setPostGapType(type);
sent.setPostGap(Options.getInteger("catenation.short-sentence"));
bookTreeModel.reload(sent);
}
});
setGapType.add(gapTypeContinuation);
JMenuObject2 gapTypeParagraph = new JMenuObject2(paragraphText, s, "paragraph", new ActionListener() {
public void actionPerformed(ActionEvent e) {
JMenuObject2 o = (JMenuObject2)e.getSource();
Sentence sent = (Sentence)o.getObject1();
String type = (String)o.getObject2();
sent.setPostGapType(type);
sent.setPostGap(Options.getInteger("catenation.post-paragraph"));
bookTreeModel.reload(sent);
}
});
setGapType.add(gapTypeParagraph);
JMenuObject2 gapTypeSection = new JMenuObject2(sectionText, s, "section", new ActionListener() {
public void actionPerformed(ActionEvent e) {
JMenuObject2 o = (JMenuObject2)e.getSource();
Sentence sent = (Sentence)o.getObject1();
String type = (String)o.getObject2();
sent.setPostGapType(type);
sent.setPostGap(Options.getInteger("catenation.post-section"));
bookTreeModel.reload(sent);
}
});
setGapType.add(gapTypeSection);
JMenuObject ins = new JMenuObject("Insert phrase above", s, new ActionListener() { JMenuObject ins = new JMenuObject("Insert phrase above", s, new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
@@ -999,6 +1141,7 @@ public class AudiobookRecorder extends JFrame {
menu.add(moveUp); menu.add(moveUp);
menu.add(moveDown); menu.add(moveDown);
menu.add(moveMenu); menu.add(moveMenu);
menu.add(setGapType);
menu.addSeparator(); menu.addSeparator();
menu.add(edit); menu.add(edit);
menu.add(external); menu.add(external);
@@ -1029,26 +1172,36 @@ public class AudiobookRecorder extends JFrame {
JMenuObject peak = new JMenuObject("Auto-trim all (Peak)", c, new ActionListener() { JMenuObject peak = new JMenuObject("Auto-trim all (Peak)", c, new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
JMenuObject o = (JMenuObject)e.getSource(); JMenuObject o = (JMenuObject)e.getSource();
Chapter c = (Chapter)o.getObject(); Chapter chap = (Chapter)o.getObject();
for (Enumeration s = c.children(); s.hasMoreElements();) {
Sentence snt = (Sentence)s.nextElement(); ProgressDialog ed = new ProgressDialog("Auto-trimming " + chap.getName());
if (!snt.isLocked()) {
snt.autoTrimSamplePeak(); AutoTrimThread t = new AutoTrimThread(chap, ed, AutoTrimThread.Peak);
} Thread nt = new Thread(t);
} nt.start();
ed.setVisible(true);
} }
}); });
JMenuObject fft = new JMenuObject("Auto-trim all (FFT)", c, new ActionListener() { JMenuObject fft = new JMenuObject("Auto-trim all (FFT)", c, new ActionListener() {
public void actionPerformed(ActionEvent e) {
JMenuObject o = (JMenuObject)e.getSource();
Chapter chap = (Chapter)o.getObject();
ProgressDialog ed = new ProgressDialog("Auto-trimming " + chap.getName());
AutoTrimThread t = new AutoTrimThread(chap, ed, AutoTrimThread.FFT);
Thread nt = new Thread(t);
nt.start();
ed.setVisible(true);
}
});
JMenuObject resetChapterGaps = new JMenuObject("Reset post gaps", c, new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
JMenuObject o = (JMenuObject)e.getSource(); JMenuObject o = (JMenuObject)e.getSource();
Chapter c = (Chapter)o.getObject(); Chapter c = (Chapter)o.getObject();
for (Enumeration s = c.children(); s.hasMoreElements();) { c.resetPostGaps();
Sentence snt = (Sentence)s.nextElement();
if (!snt.isLocked()) {
snt.autoTrimSampleFFT();
}
}
} }
}); });
@@ -1185,12 +1338,16 @@ public class AudiobookRecorder extends JFrame {
JMenuObject normalizeAll = new JMenuObject("Normalize chapter", c, new ActionListener() { JMenuObject normalizeAll = new JMenuObject("Normalize chapter", c, new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
JMenuObject o = (JMenuObject)e.getSource(); JMenuObject o = (JMenuObject)e.getSource();
Chapter c = (Chapter)o.getObject(); Chapter chap = (Chapter)o.getObject();
for (Enumeration s = c.children(); s.hasMoreElements();) {
Sentence snt = (Sentence)s.nextElement(); ProgressDialog ed = new ProgressDialog("Normalizing " + chap.getName());
snt.normalize();
} NormalizeThread t = new NormalizeThread(chap, ed);
Thread nt = new Thread(t);
nt.start();
ed.setVisible(true);
} }
}); });
@@ -1219,6 +1376,7 @@ public class AudiobookRecorder extends JFrame {
menu.add(convertAll); menu.add(convertAll);
menu.add(normalizeAll); menu.add(normalizeAll);
menu.add(resetChapterGaps);
menu.addSeparator(); menu.addSeparator();
menu.add(moveUp); menu.add(moveUp);
menu.add(moveDown); menu.add(moveDown);
@@ -1303,10 +1461,23 @@ public class AudiobookRecorder extends JFrame {
if (!(book.getName().equals(tit))) { if (!(book.getName().equals(tit))) {
book.renameBook(tit); book.renameBook(tit);
} }
CacheManager.purgeCache();
} }
}); });
menu.add(editData); menu.add(editData);
JMenuObject resetBookGaps = new JMenuObject("Reset all post gaps", book, new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (Enumeration ch = book.children(); ch.hasMoreElements();) {
Chapter chap = (Chapter)ch.nextElement();
chap.resetPostGaps();
}
}
});
menu.add(resetBookGaps);
menu.show(bookTree, e.getX(), e.getY()); menu.show(bookTree, e.getX(), e.getY());
} }
@@ -1384,6 +1555,7 @@ public class AudiobookRecorder extends JFrame {
if (lastLeaf instanceof Sentence) { if (lastLeaf instanceof Sentence) {
Sentence lastSentence = (Sentence)lastLeaf; Sentence lastSentence = (Sentence)lastLeaf;
lastSentence.setPostGap(Options.getInteger("catenation.short-sentence")); lastSentence.setPostGap(Options.getInteger("catenation.short-sentence"));
lastSentence.setPostGapType("continuation");
} }
Sentence s = new Sentence(); Sentence s = new Sentence();
@@ -1434,6 +1606,7 @@ public class AudiobookRecorder extends JFrame {
if (lastLeaf instanceof Sentence) { if (lastLeaf instanceof Sentence) {
Sentence lastSentence = (Sentence)lastLeaf; Sentence lastSentence = (Sentence)lastLeaf;
lastSentence.setPostGap(Options.getInteger("catenation.post-paragraph")); lastSentence.setPostGap(Options.getInteger("catenation.post-paragraph"));
lastSentence.setPostGapType("paragraph");
} }
Sentence s = new Sentence(); Sentence s = new Sentence();
@@ -1483,6 +1656,7 @@ public class AudiobookRecorder extends JFrame {
if (lastLeaf instanceof Sentence) { if (lastLeaf instanceof Sentence) {
Sentence lastSentence = (Sentence)lastLeaf; Sentence lastSentence = (Sentence)lastLeaf;
lastSentence.setPostGap(Options.getInteger("catenation.post-section")); lastSentence.setPostGap(Options.getInteger("catenation.post-section"));
lastSentence.setPostGapType("section");
} }
Sentence s = new Sentence(); Sentence s = new Sentence();
@@ -1532,6 +1706,7 @@ public class AudiobookRecorder extends JFrame {
if (lastLeaf instanceof Sentence) { if (lastLeaf instanceof Sentence) {
Sentence lastSentence = (Sentence)lastLeaf; Sentence lastSentence = (Sentence)lastLeaf;
lastSentence.setPostGap(Options.getInteger("catenation.post-sentence")); lastSentence.setPostGap(Options.getInteger("catenation.post-sentence"));
lastSentence.setPostGapType("sentence");
} }
Sentence s = new Sentence(); Sentence s = new Sentence();
@@ -1648,15 +1823,10 @@ public class AudiobookRecorder extends JFrame {
int i = 0; int i = 0;
for (Enumeration s = c.children(); s.hasMoreElements();) { for (Enumeration s = c.children(); s.hasMoreElements();) {
Sentence snt = (Sentence)s.nextElement(); Sentence snt = (Sentence)s.nextElement();
prefs.setProperty(String.format("%s.sentence.%08d.id", keybase, i), snt.getId()); TreeMap<String, String> settings = snt.getSentenceData();
prefs.setProperty(String.format("%s.sentence.%08d.text", keybase, i), snt.getText()); for (String key : settings.keySet()) {
prefs.setProperty(String.format("%s.sentence.%08d.post-gap", keybase, i), Integer.toString(snt.getPostGap())); prefs.setProperty(String.format("%s.sentence.%08d.%s", keybase, i, key), settings.get(key));
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());
i++; i++;
} }
} }
@@ -1665,13 +1835,14 @@ public class AudiobookRecorder extends JFrame {
try { try {
fos = new FileOutputStream(config); fos = new FileOutputStream(config);
prefs.storeToXML(fos, "Audiobook Recorder Description"); prefs.storeToXML(fos, "Audiobook Recorder Description");
} catch (Exception e) { } catch (Exception ex) {
e.printStackTrace(); ex.printStackTrace();
} }
if (fos != null) { if (fos != null) {
try { try {
fos.close(); fos.close();
} catch (Exception e) { } catch (Exception ex) {
ex.printStackTrace();
} }
} }
} }
@@ -1759,7 +1930,7 @@ public class AudiobookRecorder extends JFrame {
if (n instanceof Sentence) { if (n instanceof Sentence) {
Sentence s = (Sentence)n; Sentence s = (Sentence)n;
selectedSentence = s; selectedSentence = s;
sampleWaveform.setData(s.getDoubleAudioData()); sampleWaveform.setData(s.getDoubleAudioData(effectsEnabled));
sampleWaveform.setMarkers(s.getStartOffset(), s.getEndOffset()); sampleWaveform.setMarkers(s.getStartOffset(), s.getEndOffset());
s.updateCrossings(); s.updateCrossings();
sampleWaveform.setAltMarkers(s.getStartCrossing(), s.getEndCrossing()); sampleWaveform.setAltMarkers(s.getStartCrossing(), s.getEndCrossing());
@@ -1774,6 +1945,11 @@ public class AudiobookRecorder extends JFrame {
gainPercent.setEnabled(!s.isLocked()); gainPercent.setEnabled(!s.isLocked());
reprocessAudioFFT.setEnabled(!s.isLocked()); reprocessAudioFFT.setEnabled(!s.isLocked());
reprocessAudioPeak.setEnabled(!s.isLocked()); reprocessAudioPeak.setEnabled(!s.isLocked());
selectCutMode.setEnabled(!s.isLocked());
selectSplitMode.setEnabled(!s.isLocked());
doCutSplit.setEnabled(false);
selectCutMode.setSelected(false);
selectSplitMode.setSelected(false);
} else { } else {
selectedSentence = null; selectedSentence = null;
sampleWaveform.clearData(); sampleWaveform.clearData();
@@ -1781,6 +1957,11 @@ public class AudiobookRecorder extends JFrame {
gainPercent.setValue(100); gainPercent.setValue(100);
locked.setSelected(false); locked.setSelected(false);
attention.setSelected(false); attention.setSelected(false);
selectCutMode.setEnabled(false);
selectSplitMode.setEnabled(false);
doCutSplit.setEnabled(false);
selectCutMode.setSelected(false);
selectSplitMode.setSelected(false);
} }
} }
}); });
@@ -1822,6 +2003,7 @@ public class AudiobookRecorder extends JFrame {
s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.audition.sentence.%08d.attention", i)))); s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.audition.sentence.%08d.attention", i))));
s.setGain(Utils.s2d(prefs.getProperty(String.format("chapter.audition.sentence.%08d.gain", i)))); s.setGain(Utils.s2d(prefs.getProperty(String.format("chapter.audition.sentence.%08d.gain", i))));
s.setEffectChain(prefs.getProperty(String.format("chapter.audition.sentence.%08d.effect", i))); s.setEffectChain(prefs.getProperty(String.format("chapter.audition.sentence.%08d.effect", i)));
s.setPostGapType(prefs.getProperty(String.format("chapter.audition.sentence.%08d.gaptype", i)));
bookTreeModel.insertNodeInto(s, c, c.getChildCount()); bookTreeModel.insertNodeInto(s, c, c.getChildCount());
} }
@@ -1843,6 +2025,7 @@ public class AudiobookRecorder extends JFrame {
s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.open.sentence.%08d.attention", i)))); s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.open.sentence.%08d.attention", i))));
s.setGain(Utils.s2d(prefs.getProperty(String.format("chapter.open.sentence.%08d.gain", i)))); s.setGain(Utils.s2d(prefs.getProperty(String.format("chapter.open.sentence.%08d.gain", i))));
s.setEffectChain(prefs.getProperty(String.format("chapter.open.sentence.%08d.effect", i))); s.setEffectChain(prefs.getProperty(String.format("chapter.open.sentence.%08d.effect", i)));
s.setPostGapType(prefs.getProperty(String.format("chapter.open.sentence.%08d.gaptype", i)));
bookTreeModel.insertNodeInto(s, c, c.getChildCount()); bookTreeModel.insertNodeInto(s, c, c.getChildCount());
} }
@@ -1870,6 +2053,7 @@ public class AudiobookRecorder extends JFrame {
s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.attention", cno, i)))); s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.attention", cno, i))));
s.setGain(Utils.s2d(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.gain", cno, i)))); s.setGain(Utils.s2d(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.gain", cno, i))));
s.setEffectChain(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.effect", cno, i))); s.setEffectChain(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.effect", cno, i)));
s.setPostGapType(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.gaptype", cno, i)));
bookTreeModel.insertNodeInto(s, c, c.getChildCount()); bookTreeModel.insertNodeInto(s, c, c.getChildCount());
} }
} }
@@ -1892,6 +2076,7 @@ public class AudiobookRecorder extends JFrame {
s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.close.sentence.%08d.attention", i)))); s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.close.sentence.%08d.attention", i))));
s.setGain(Utils.s2d(prefs.getProperty(String.format("chapter.close.sentence.%08d.gain", i)))); s.setGain(Utils.s2d(prefs.getProperty(String.format("chapter.close.sentence.%08d.gain", i))));
s.setEffectChain(prefs.getProperty(String.format("chapter.close.sentence.%08d.effect", i))); s.setEffectChain(prefs.getProperty(String.format("chapter.close.sentence.%08d.effect", i)));
s.setPostGapType(prefs.getProperty(String.format("chapter.close.sentence.%08d.gaptype", i)));
bookTreeModel.insertNodeInto(s, c, c.getChildCount()); bookTreeModel.insertNodeInto(s, c, c.getChildCount());
} }
@@ -1943,14 +2128,14 @@ public class AudiobookRecorder extends JFrame {
public double getNoiseFloor() { public double getNoiseFloor() {
if (roomNoise == null) return 0; if (roomNoise == null) return 0;
Sample[] samples = roomNoise.getDoubleAudioData(); double[][] samples = roomNoise.getDoubleAudioData();
if (samples == null) { if (samples == null) {
return 0; return 0;
} }
double ms = 0; double ms = 0;
for (int i = 0; i < samples.length; i++) { for (int i = 0; i < samples.length; i++) {
if (Math.abs(samples[i].getMono()) > ms) { if (Math.abs((samples[i][Sentence.LEFT] + samples[i][Sentence.RIGHT]) / 2d) > ms) {
ms = Math.abs(samples[i].getMono()); ms = Math.abs((samples[i][Sentence.LEFT] + samples[i][Sentence.RIGHT]) / 2d);
} }
} }
@@ -2000,19 +2185,24 @@ public class AudiobookRecorder extends JFrame {
try { try {
int blockSize = Options.getInteger("audio.playback.blocksize");
AudioFormat sampleformat = s.getAudioFormat(); 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 = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer());
play.open(format); play.open(format);
play.start(); play.start();
play.drain();
bookTree.scrollPathToVisible(new TreePath(s.getPath())); bookTree.scrollPathToVisible(new TreePath(s.getPath()));
data = s.getPCMData(); 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()); sampleWaveform.setPlayMarker(pos / format.getFrameSize());
int l = data.length - pos; int l = data.length - pos;
if (l > PLAYBACK_CHUNK_SIZE) l = PLAYBACK_CHUNK_SIZE; if (l > blockSize) l = blockSize;
play.write(data, pos, l); play.write(data, pos, l);
} }
@@ -2029,6 +2219,7 @@ public class AudiobookRecorder extends JFrame {
play.close(); play.close();
} }
play = null; play = null;
e.printStackTrace();
} }
} }
}); });
@@ -2037,6 +2228,72 @@ public class AudiobookRecorder extends JFrame {
playingThread.start(); playingThread.start();
} }
class NormalizeThread implements Runnable {
ProgressDialog dialog;
Chapter chapter;
public NormalizeThread(Chapter c, ProgressDialog e) {
super();
dialog = e;
chapter = c;
}
@SuppressWarnings("unchecked")
public void run() {
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();
snt.normalize();
}
dialog.closeDialog();
}
}
class AutoTrimThread implements Runnable {
ProgressDialog dialog;
Chapter chapter;
int type;
public final static int FFT = 0;
public final static int Peak = 1;
public AutoTrimThread(Chapter c, ProgressDialog e, int t) {
super();
dialog = e;
chapter = c;
type = t;
}
@SuppressWarnings("unchecked")
public void run() {
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();
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;
@@ -2078,6 +2335,84 @@ public class AudiobookRecorder extends JFrame {
} }
public void playToSelectedSentence() {
if (selectedSentence == null) return;
if (playing != null) return;
if (getNoiseFloor() == 0) {
alertNoRoomNoise();
return;
}
playing = selectedSentence;
playingThread = new Thread(new Runnable() {
public void run() {
Sentence s = playing;
byte[] data;
try {
int blockSize = Options.getInteger("audio.playback.blocksize");
AudioFormat sampleformat = s.getAudioFormat();
AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 2, true, false);
play = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer());
play.open(format);
play.start();
play.drain();
bookTree.scrollPathToVisible(new TreePath(s.getPath()));
data = s.getPCMData(effectsEnabled);
int startPos = 0;
int endPos = data.length / format.getFrameSize();
//foobar
if (selectSplitMode.isSelected()) {
endPos = sampleWaveform.getCutStart() - selectedSentence.getStartCrossing();
if (endPos < 0) endPos = 0;
} else if (selectCutMode.isSelected()) {
startPos = sampleWaveform.getCutStart() - selectedSentence.getStartCrossing();;
endPos = sampleWaveform.getCutEnd() - selectedSentence.getStartCrossing();;
if (startPos < 0) startPos = 0;
if (endPos < 0) endPos = 0;
}
startPos *= format.getFrameSize();
endPos *= format.getFrameSize();
if (startPos > data.length) startPos = data.length;
if (endPos > data.length) endPos = data.length;
for (int pos = startPos; pos < endPos; pos += blockSize) {
sampleWaveform.setPlayMarker(pos / format.getFrameSize());
int l = data.length - pos;
if (l > blockSize) l = blockSize;
play.write(data, pos, l);
}
play.drain();
play.stop();
play.close();
play = null;
playing = null;
} catch (Exception e) {
playing = null;
if (play != null) {
play.drain();
play.stop();
play.close();
}
play = null;
e.printStackTrace();
}
}
});
playingThread.setDaemon(true);
playingThread.start();
}
public void playFromSelectedSentence() { public void playFromSelectedSentence() {
if (selectedSentence == null) return; if (selectedSentence == null) return;
if (playing != null) return; if (playing != null) return;
@@ -2094,11 +2429,16 @@ public class AudiobookRecorder extends JFrame {
try { try {
int blockSize = Options.getInteger("audio.playback.blocksize");
AudioFormat sampleformat = s.getAudioFormat(); 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 = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer());
play.open(format); play.open(format);
play.start(); play.start();
play.drain();
while (playing != null) { while (playing != null) {
bookTree.scrollPathToVisible(new TreePath(s.getPath())); bookTree.scrollPathToVisible(new TreePath(s.getPath()));
@@ -2113,21 +2453,21 @@ public class AudiobookRecorder extends JFrame {
data = getRoomNoise(Utils.s2i(Options.get("catenation.pre-chapter"))); data = getRoomNoise(Utils.s2i(Options.get("catenation.pre-chapter")));
play.write(data, 0, data.length); play.write(data, 0, data.length);
} }
data = s.getPCMData(); data = s.getPCMData(effectsEnabled);
DefaultMutableTreeNode next = s.getNextSibling(); DefaultMutableTreeNode next = s.getNextSibling();
if (next != null) { if (next != null) {
Thread t = new Thread(new Runnable() { Thread t = new Thread(new Runnable() {
public void run() { public void run() {
Sentence ns = (Sentence)next; Sentence ns = (Sentence)next;
ns.getProcessedAudioData(); // Cache it ns.getProcessedAudioData(effectsEnabled); // Cache it
} }
}); });
t.start(); 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()); sampleWaveform.setPlayMarker(pos / format.getFrameSize());
int l = data.length - pos; int l = data.length - pos;
if (l > PLAYBACK_CHUNK_SIZE) l = PLAYBACK_CHUNK_SIZE; if (l > blockSize) l = blockSize;
play.write(data, pos, l); play.write(data, pos, l);
} }
@@ -2163,6 +2503,7 @@ public class AudiobookRecorder extends JFrame {
play.close(); play.close();
} }
play = null; play = null;
e.printStackTrace();
} }
} }
}); });
@@ -2370,6 +2711,7 @@ public class AudiobookRecorder extends JFrame {
try { try {
Files.copy(srcFile.toPath(), dstFile.toPath()); Files.copy(srcFile.toPath(), dstFile.toPath());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
} }
} }
@@ -2497,6 +2839,7 @@ public class AudiobookRecorder extends JFrame {
f.delete(); f.delete();
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
} }
pd.closeDialog(); pd.closeDialog();
} }
@@ -2644,7 +2987,11 @@ public class AudiobookRecorder extends JFrame {
public void updateWaveform() { public void updateWaveform() {
if (selectedSentence != null) { if (selectedSentence != null) {
sampleWaveform.setData(selectedSentence.getDoubleAudioData()); if (rawAudio.isSelected()) {
sampleWaveform.setData(selectedSentence.getRawAudioData());
} else {
sampleWaveform.setData(selectedSentence.getDoubleAudioData(effectsEnabled));
}
} }
} }
@@ -2659,6 +3006,7 @@ public class AudiobookRecorder extends JFrame {
public void loadEffectsFromFolder(File dir) { public void loadEffectsFromFolder(File dir) {
if (dir == null) return; if (dir == null) return;
if (!dir.exists()) return;
File[] files = dir.listFiles(); File[] files = dir.listFiles();
for (File f : files) { for (File f : files) {
if (f.getName().endsWith(".eff")) { if (f.getName().endsWith(".eff")) {
@@ -2715,6 +3063,11 @@ public class AudiobookRecorder extends JFrame {
if (eff != null) { if (eff != null) {
group.addEffect(eff); group.addEffect(eff);
} }
} else if (e.getTagName().equals("chain")) {
Effect eff = (Effect)loadChain(e);
if (eff != null) {
group.addEffect(eff);
}
} else if (e.getTagName().equals("group")) { } else if (e.getTagName().equals("group")) {
Effect eff = (Effect)loadEffectGroup(e); Effect eff = (Effect)loadEffectGroup(e);
if (eff != null) { if (eff != null) {
@@ -2815,6 +3168,11 @@ public class AudiobookRecorder extends JFrame {
if (eff != null) { if (eff != null) {
store.addEffect(eff); store.addEffect(eff);
} }
} else if (ie.getTagName().equals("chain")) {
Effect eff = (Effect)loadChain(ie);
if (eff != null) {
store.addEffect(eff);
}
} else if (ie.getTagName().equals("group")) { } else if (ie.getTagName().equals("group")) {
Effect eff = (Effect)loadEffectGroup(ie); Effect eff = (Effect)loadEffectGroup(ie);
if (eff != null) { if (eff != null) {
@@ -2850,6 +3208,11 @@ public class AudiobookRecorder extends JFrame {
return a; return a;
} }
public Chain loadChain(Element root) {
Chain c = new Chain(root.getAttribute("src"));
return c;
}
public Pan loadPan(Element root) { public Pan loadPan(Element root) {
Pan p = new Pan(Utils.s2d(root.getAttribute("pan"))); Pan p = new Pan(Utils.s2d(root.getAttribute("pan")));
return p; return p;
@@ -2925,4 +3288,123 @@ public class AudiobookRecorder extends JFrame {
public String getDefaultEffectsChain() { public String getDefaultEffectsChain() {
return defaultEffectChain; return defaultEffectChain;
} }
public synchronized boolean getLock() {
if (state == RECORDING) return false;
int counts = 0;
while (state == STOPPING) {
try {
Thread.sleep(100);
} catch (Exception ex) {
ex.printStackTrace();
}
counts++;
if (counts > 100) return false;
}
state = RECORDING;
return true;
}
public void freeLock() {
state = IDLE;
}
public void stopLock() {
state = STOPPING;
}
public void toggleSplitMode() {
selectCutMode.setSelected(false);
if (selectedSentence != null) {
sampleWaveform.setDisplaySplit(selectSplitMode.isSelected());
}
doCutSplit.setEnabled(selectSplitMode.isSelected());
toolBar.enablePlayTo(selectSplitMode.isSelected());
}
public void toggleCutMode() {
selectSplitMode.setSelected(false);
if (selectedSentence != null) {
sampleWaveform.setDisplayCut(selectCutMode.isSelected());
}
doCutSplit.setEnabled(selectCutMode.isSelected());
toolBar.enablePlayTo(selectCutMode.isSelected());
}
public void doCut(int start, int end) {
try {
double[][] samples = selectedSentence.getRawAudioData();
double[][] croppedSamples = new double[samples.length - (end - start)][2];
int a = 0;
for (int i = 0; i < samples.length; i++) {
if ((i < start) || (i > end)) {
croppedSamples[a++] = samples[i];
}
}
selectedSentence.writeAudioData(croppedSamples);
selectedSentence.autoTrimSample();
updateWaveform();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void doSplit(int at) {
try {
Sentence newSentence = selectedSentence.cloneSentence();
Chapter c = (Chapter)selectedSentence.getParent();
int idx = bookTreeModel.getIndexOfChild(c, selectedSentence);
bookTreeModel.insertNodeInto(newSentence, c, idx);
double[][] samples = selectedSentence.getRawAudioData();
double[][] startSamples = new double[at][2];
double[][] endSamples = new double[samples.length - at][2];
int a = 0;
int b = 0;
for (int i = 0; i < samples.length; i++) {
if (i < at) {
startSamples[a++] = samples[i];
} else {
endSamples[b++] = samples[i];
}
}
newSentence.writeAudioData(startSamples);
newSentence.setPostGapType("continuation");
newSentence.setPostGap(Options.getInteger("catenation.short-sentence"));
selectedSentence.writeAudioData(endSamples);
selectedSentence.autoTrimSample();
newSentence.autoTrimSample();
updateWaveform();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void executeCutOrSplit() {
int start = sampleWaveform.getCutStart();
int end = sampleWaveform.getCutEnd();
if (selectCutMode.isSelected()) {
doCut(start, end);
} else if (selectSplitMode.isSelected()) {
doSplit(start);
}
selectCutMode.setSelected(false);
selectSplitMode.setSelected(false);
toolBar.enablePlayTo(false);
doCutSplit.setEnabled(false);
}
public void setEffectsEnabled(boolean b) {
effectsEnabled = b;
System.err.println("Effects Enabled: " + b);
}
} }

View File

@@ -89,24 +89,24 @@ public class Biquad implements Effect {
setPeakGain(peakGainDB); setPeakGain(peakGainDB);
} }
public void process(Sample[] samples) { public void process(double[][] samples) {
lz1 = 0d; lz1 = 0d;
lz2 = 0d; lz2 = 0d;
rz1 = 0d; rz1 = 0d;
rz2 = 0d; rz2 = 0d;
for (Sample in : samples) { for (double[] in : samples) {
double lout = in.left * a0 + lz1; double lout = in[Sentence.LEFT] * a0 + lz1;
lz1 = in.left * a1 + lz2 - b1 * lout; lz1 = in[Sentence.LEFT] * a1 + lz2 - b1 * lout;
lz2 = in.left * a2 - b2 * lout; lz2 = in[Sentence.LEFT] * a2 - b2 * lout;
double rout = in.right * a0 + rz1; double rout = in[Sentence.RIGHT] * a0 + rz1;
rz1 = in.right * a1 + rz2 - b1 * rout; rz1 = in[Sentence.RIGHT] * a1 + rz2 - b1 * rout;
rz2 = in.right * a2 - b2 * rout; rz2 = in[Sentence.RIGHT] * a2 - b2 * rout;
in.left = lout; in[Sentence.LEFT] = lout;
in.right = rout; in[Sentence.RIGHT] = rout;
} }
} }

View File

@@ -206,4 +206,14 @@ public class Book extends DefaultMutableTreeNode {
return out; 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

@@ -3,11 +3,14 @@ package uk.co.majenko.audiobookrecorder;
import javax.swing.*; import javax.swing.*;
import javax.swing.tree.*; import javax.swing.tree.*;
import java.awt.*; import java.awt.*;
import javax.swing.border.*;
public class BookTreeRenderer extends DefaultTreeCellRenderer { public class BookTreeRenderer extends DefaultTreeCellRenderer {
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
JLabel ret = (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); JLabel ret = (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
ret.setIconTextGap(0);
ret.setBorder(new EmptyBorder(0, 0, 0, 0));
if (value instanceof Sentence) { if (value instanceof Sentence) {
Sentence s = (Sentence)value; Sentence s = (Sentence)value;
@@ -39,6 +42,28 @@ public class BookTreeRenderer extends DefaultTreeCellRenderer {
ret.setIcon(icn); ret.setIcon(icn);
String gaptype = s.getPostGapType();
DefaultMutableTreeNode prev = s.getPreviousSibling();
String prevtype = "sentence";
if (prev instanceof Sentence) {
Sentence s2 = (Sentence)prev;
prevtype = s2.getPostGapType();
}
if (prevtype.equals("continuation")) {
ret.setIconTextGap(20);
}
if (gaptype.equals("sentence")) {
ret.setBorder(new EmptyBorder(0, 0, 0, 0));
} else if (gaptype.equals("continuation")) {
ret.setBorder(new EmptyBorder(0, 0, 0, 0));
} else if (gaptype.equals("paragraph")) {
ret.setBorder(new EmptyBorder(0, 0, 7, 0));
} else if (gaptype.equals("section")) {
ret.setBorder(new EmptyBorder(0, 0, 15, 0));
}
} else if (value instanceof Chapter) { } else if (value instanceof Chapter) {
ret.setIcon(Icons.chapter); ret.setIcon(Icons.chapter);
} else if (value instanceof Book) { } else if (value instanceof Book) {

View File

@@ -13,6 +13,9 @@ public class CacheManager {
if (ob.lockedInCache()) { if (ob.lockedInCache()) {
cache.add(ob); cache.add(ob);
} else { } else {
if (ob instanceof Sentence) {
Sentence s = (Sentence)ob;
}
ob.clearCache(); ob.clearCache();
} }
} }
@@ -23,4 +26,19 @@ public class CacheManager {
public static void setCacheSize(int c) { public static void setCacheSize(int c) {
cacheSize = c; cacheSize = c;
} }
public static void removeFromCache(Cacheable c) {
if (c instanceof Sentence) {
Sentence s = (Sentence)c;
}
cache.remove(c);
c.clearCache();
}
public static void purgeCache() {
for (Cacheable c : cache) {
c.clearCache();
}
cache.clear();
}
} }

View File

@@ -0,0 +1,58 @@
package uk.co.majenko.audiobookrecorder;
import java.util.ArrayList;
public class Chain implements Effect {
String target;
public Chain(String t) {
target = t;
}
public Chain() {
target = null;
}
public void process(double[][] samples) {
if (target != null) {
Effect t = AudiobookRecorder.window.effects.get(target);
if (t != null) {
t.process(samples);
}
}
}
public void setTarget(String t) {
target = t;
}
public String getTarget() {
return target;
}
public String toString() {
return "Chain to " + target;
}
public void dump() {
System.out.println(toString());
}
public void init(double sf) {
if (target != null) {
Effect t = AudiobookRecorder.window.effects.get(target);
if (t != null) {
t.init(sf);
}
}
}
public ArrayList<Effect> getChildEffects() {
return null;
}
public String getName() {
return toString();
}
}

View File

@@ -116,13 +116,13 @@ public class Chapter extends DefaultMutableTreeNode {
audioAttributes.setCodec("libmp3lame"); audioAttributes.setCodec("libmp3lame");
audioAttributes.setBitRate(Options.getInteger("audio.export.bitrate")); audioAttributes.setBitRate(Options.getInteger("audio.export.bitrate"));
audioAttributes.setSamplingRate(Options.getInteger("audio.export.samplerate")); 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.setFormat("mp3");
attributes.setAudioAttributes(audioAttributes); attributes.setAudioAttributes(audioAttributes);
AudioFormat sampleformat = AudiobookRecorder.window.roomNoise.getAudioFormat(); AudioFormat sampleformat = AudiobookRecorder.window.roomNoise.getAudioFormat();
AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 1, true, false); AudioFormat format = new AudioFormat(sampleformat.getSampleRate(), 16, 2, true, false);
byte[] data; byte[] data;
int fullLength = 0; int fullLength = 0;
@@ -232,5 +232,21 @@ public class Chapter extends DefaultMutableTreeNode {
return out; return out;
} }
public void resetPostGaps() {
for (Enumeration s = children(); s.hasMoreElements();) {
Sentence snt = (Sentence)s.nextElement();
snt.resetPostGap();
}
}
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,12 +19,12 @@ public class Clipping implements Effect {
return null; return null;
} }
public void process(Sample[] samples) { public void process(double[][] samples) {
for (Sample sample : samples) { for (double[] sample : samples) {
if (sample.left > clip) sample.left = clip; if (sample[Sentence.LEFT] > clip) sample[Sentence.LEFT] = clip;
if (sample.left < -clip) sample.left = -clip; if (sample[Sentence.LEFT] < -clip) sample[Sentence.LEFT] = -clip;
if (sample.right > clip) sample.right = clip; if (sample[Sentence.RIGHT] > clip) sample[Sentence.RIGHT] = clip;
if (sample.right < -clip) sample.right = -clip; if (sample[Sentence.RIGHT] < -clip) sample[Sentence.RIGHT] = -clip;
} }
} }

View File

@@ -0,0 +1,12 @@
package uk.co.majenko.audiobookrecorder;
public class Debug {
static long timestamp;
static void debug(String msg) {
long now = System.currentTimeMillis();
long diff = now - timestamp;
timestamp = now;
System.err.println(String.format("%8d - %s", diff, msg));
}
}

View File

@@ -16,22 +16,28 @@ public class DelayLine implements Effect {
return "Delay Line (" + delayLines.size() + " lines)"; return "Delay Line (" + delayLines.size() + " lines)";
} }
public void process(Sample[] samples) { public void process(double[][] samples) {
Sample[] savedSamples = new Sample[samples.length]; double[][] savedSamples = new double[samples.length][2];
for (int i = 0; i < samples.length; i++) { for (int i = 0; i < samples.length; i++) {
savedSamples[i] = new Sample(samples[i].left, samples[i].right); savedSamples[i][Sentence.LEFT] = samples[i][Sentence.LEFT];
savedSamples[i][Sentence.RIGHT] = samples[i][Sentence.RIGHT];
} }
if (wetOnly) { if (wetOnly) {
for (int i = 0; i < samples.length; i++) { for (int i = 0; i < samples.length; i++) {
samples[i].left = 0d; samples[i][Sentence.LEFT] = 0d;
samples[i].right = 0d; samples[i][Sentence.RIGHT] = 0d;
} }
} }
double[][] subSamples = new double[samples.length][2];
for (int i = 0; i < samples.length; i++) {
subSamples[i][Sentence.LEFT] = savedSamples[i][Sentence.LEFT];
subSamples[i][Sentence.RIGHT] = savedSamples[i][Sentence.RIGHT];
}
for (DelayLineStore d : delayLines) { for (DelayLineStore d : delayLines) {
Sample[] subSamples = new Sample[samples.length];
for (int i = 0; i < samples.length; i++) { for (int i = 0; i < samples.length; i++) {
subSamples[i] = new Sample(savedSamples[i].left, savedSamples[i].right); subSamples[i][Sentence.LEFT] = savedSamples[i][Sentence.LEFT];
subSamples[i][Sentence.RIGHT] = savedSamples[i][Sentence.RIGHT];
} }
d.process(subSamples); d.process(subSamples);
@@ -40,31 +46,31 @@ public class DelayLine implements Effect {
int off = i + d.getSamples(); int off = i + d.getSamples();
if ((off < samples.length) && (off > 0)) { if ((off < samples.length) && (off > 0)) {
Sample ns = mix(samples[off], subSamples[i]); double[] ns = mix(samples[off], subSamples[i]);
samples[off].left = ns.left; samples[off][Sentence.LEFT] = ns[Sentence.LEFT];
samples[off].right = ns.right; samples[off][Sentence.RIGHT] = ns[Sentence.RIGHT];
} }
} }
} }
} }
Sample mix(Sample a, Sample b) { double[] mix(double[] a, double[] b) {
Sample out = new Sample(0, 0); double[] out = new double[2];
if ((a.left < 0) && (b.left < 0)) { if ((a[Sentence.LEFT] < 0) && (b[Sentence.LEFT] < 0)) {
out.left = (a.left + b.left) - (a.left * b.left); out[Sentence.LEFT] = (a[Sentence.LEFT] + b[Sentence.LEFT]) - (a[Sentence.LEFT] * b[Sentence.LEFT]);
} else if ((a.left > 0) && (b.left > 0)) { } else if ((a[Sentence.LEFT] > 0) && (b[Sentence.LEFT] > 0)) {
out.left = (a.left + b.left) - (a.left * b.left); out[Sentence.LEFT] = (a[Sentence.LEFT] + b[Sentence.LEFT]) - (a[Sentence.LEFT] * b[Sentence.LEFT]);
} else { } else {
out.left = a.left + b.left; out[Sentence.LEFT] = a[Sentence.LEFT] + b[Sentence.LEFT];
} }
if ((a.right < 0) && (b.right < 0)) { if ((a[Sentence.RIGHT] < 0) && (b[Sentence.RIGHT] < 0)) {
out.right = (a.right + b.right) - (a.right * b.right); out[Sentence.RIGHT] = (a[Sentence.RIGHT] + b[Sentence.RIGHT]) - (a[Sentence.RIGHT] * b[Sentence.RIGHT]);
} else if ((a.right > 0) && (b.right > 0)) { } else if ((a[Sentence.RIGHT] > 0) && (b[Sentence.RIGHT] > 0)) {
out.right = (a.right + b.right) - (a.right * b.right); out[Sentence.RIGHT] = (a[Sentence.RIGHT] + b[Sentence.RIGHT]) - (a[Sentence.RIGHT] * b[Sentence.RIGHT]);
} else { } else {
out.right = a.right + b.right; out[Sentence.RIGHT] = a[Sentence.RIGHT] + b[Sentence.RIGHT];
} }
return out; return out;

View File

@@ -24,21 +24,21 @@ public class DelayLineStore {
effects = new ArrayList<Effect>(); effects = new ArrayList<Effect>();
} }
public void process(Sample[] samples) { public void process(double[][] samples) {
for (Effect e : effects) { for (Effect e : effects) {
e.process(samples); e.process(samples);
} }
for (Sample sample : samples) { for (double[] sample : samples) {
sample.left *= gain; sample[Sentence.LEFT] *= gain;
sample.right *= gain; sample[Sentence.RIGHT] *= gain;
if (pan < 0) { if (pan < 0) {
double p = 1 + pan; double p = 1 + pan;
sample.right *= p; sample[Sentence.RIGHT] *= p;
} else { } else {
double p = 1 - pan; double p = 1 - pan;
sample.left *= p; sample[Sentence.LEFT] *= p;
} }
} }
} }

View File

@@ -3,7 +3,7 @@ package uk.co.majenko.audiobookrecorder;
import java.util.ArrayList; import java.util.ArrayList;
public interface Effect { public interface Effect {
public void process(Sample[] samples); public void process(double[][] samples);
public String getName(); public String getName();
public ArrayList<Effect> getChildEffects(); public ArrayList<Effect> getChildEffects();
public void dump(); public void dump();

View File

@@ -16,7 +16,7 @@ public class EffectGroup implements Effect {
effects = new ArrayList<Effect>(); effects = new ArrayList<Effect>();
} }
public void process(Sample[] samples) { public void process(double[][] samples) {
for (Effect e : effects) { for (Effect e : effects) {
e.process(samples); e.process(samples);
} }

View File

@@ -27,6 +27,8 @@ public class FlashPanel extends JPanel {
public void setFlash(boolean f) { public void setFlash(boolean f) {
flash = f; flash = f;
col = true;
for (Component o : getComponents()) { for (Component o : getComponents()) {
((JComponent)o).setVisible(!f); ((JComponent)o).setVisible(!f);
} }
@@ -42,7 +44,7 @@ public class FlashPanel extends JPanel {
if (col) { if (col) {
g.setColor(Color.RED); g.setColor(Color.RED);
} else { } else {
g.setColor(Color.BLACK); g.setColor(Color.GREEN);
} }
Dimension d = getSize(); Dimension d = getSize();
g.fillRect(0, 0, d.width, d.height); g.fillRect(0, 0, d.width, d.height);

View File

@@ -33,4 +33,9 @@ public class Icons {
static public final ImageIcon dollar = new ImageIcon(Icons.class.getResource("icons/dollar.png")); static public final ImageIcon dollar = new ImageIcon(Icons.class.getResource("icons/dollar.png"));
static public final ImageIcon attention = new ImageIcon(Icons.class.getResource("icons/attention.png")); static public final ImageIcon attention = new ImageIcon(Icons.class.getResource("icons/attention.png"));
static public final ImageIcon normalize = new ImageIcon(Icons.class.getResource("icons/normalize.png")); static public final ImageIcon normalize = new ImageIcon(Icons.class.getResource("icons/normalize.png"));
static public final ImageIcon split = new ImageIcon(Icons.class.getResource("icons/split.png"));
static public final ImageIcon cut = new ImageIcon(Icons.class.getResource("icons/cut.png"));
static public final ImageIcon docut = new ImageIcon(Icons.class.getResource("icons/do-cut.png"));
static public final ImageIcon playto = new ImageIcon(Icons.class.getResource("icons/play-to.png"));
static public final ImageIcon disable = new ImageIcon(Icons.class.getResource("icons/disable-effects.png"));
} }

View File

@@ -22,8 +22,8 @@ public class LFO implements Effect {
phase = p; phase = p;
} }
public void process(Sample[] samples) { public void process(double[][] samples) {
for (Sample sample : samples) { for (double[] sample : samples) {
double v = Math.sin(phase); double v = Math.sin(phase);
phase += sampleStep; phase += sampleStep;
if (phase > (Math.PI * 2d)) { if (phase > (Math.PI * 2d)) {
@@ -38,8 +38,8 @@ public class LFO implements Effect {
v *= depth; v *= depth;
// Apply it to the sample // Apply it to the sample
sample.left += (sample.left * v); sample[Sentence.LEFT] += (sample[Sentence.LEFT] * v);
sample.right += (sample.right * v); sample[Sentence.RIGHT] += (sample[Sentence.RIGHT] * v);
} }
} }

View File

@@ -14,9 +14,14 @@ public class MainToolBar extends JToolBar {
JButtonSpacePlay recordRoomNoise; JButtonSpacePlay recordRoomNoise;
JButtonSpacePlay playSentence; JButtonSpacePlay playSentence;
JButtonSpacePlay playonSentence; JButtonSpacePlay playonSentence;
JButtonSpacePlay playtoSentence;
JButtonSpacePlay stopPlaying; JButtonSpacePlay stopPlaying;
JButtonSpacePlay eq; JButtonSpacePlay eq;
JToggleButton mic; JToggleButtonSpacePlay mic;
JComboBox<String> playbackSpeed;
JToggleButtonSpacePlay disableEffects;
AudiobookRecorder root; AudiobookRecorder root;
@@ -78,6 +83,14 @@ public class MainToolBar extends JToolBar {
}); });
add(playonSentence); add(playonSentence);
playtoSentence = new JButtonSpacePlay(Icons.playto, "Play sentence to cut", new ActionListener() {
public void actionPerformed(ActionEvent e) {
root.playToSelectedSentence();
}
});
add(playtoSentence);
playtoSentence.setEnabled(false);
stopPlaying = new JButtonSpacePlay(Icons.stop, "Stop playing", new ActionListener() { stopPlaying = new JButtonSpacePlay(Icons.stop, "Stop playing", new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
root.stopPlaying(); root.stopPlaying();
@@ -89,6 +102,7 @@ public class MainToolBar extends JToolBar {
eq = new JButtonSpacePlay(Icons.eq, "Reload Effects", new ActionListener() { eq = new JButtonSpacePlay(Icons.eq, "Reload Effects", new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
root.loadEffects(); root.loadEffects();
CacheManager.purgeCache();
} }
}); });
@@ -118,10 +132,55 @@ public class MainToolBar extends JToolBar {
} }
}); });
add(mic); add(mic);
addSeparator();
disableEffects = new JToggleButtonSpacePlay(Icons.disable, "Disable effects", new ActionListener() {
Color bgCol = null;
public void actionPerformed(ActionEvent e) {
JToggleButton b = (JToggleButton)e.getSource();
if (b.isSelected()) {
root.setEffectsEnabled(false);
bgCol = b.getBackground();
b.setBackground(Color.RED);
} else {
root.setEffectsEnabled(true);
if (bgCol != null) {
b.setBackground(bgCol);
}
}
}
});
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); setFloatable(false);
} }
public void enablePlayTo(boolean b) {
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> rateList;
JComboBox<KVPair> bitDepth; JComboBox<KVPair> bitDepth;
JComboBox<KVPair> trimMethod; JComboBox<KVPair> trimMethod;
JComboBox<KVPair> fftBlockSize;
JComboBox<KVPair> playbackBlockSize;
JTextField storageFolder; JTextField storageFolder;
JTextField archiveFolder; JTextField archiveFolder;
JSpinner preChapterGap; JSpinner preChapterGap;
@@ -34,6 +36,7 @@ public class Options extends JDialog {
JSpinner postSectionGap; JSpinner postSectionGap;
JTextField ffmpegLocation; JTextField ffmpegLocation;
JComboBox<KVPair> bitRate; JComboBox<KVPair> bitRate;
JComboBox<KVPair> channels;
JComboBox<KVPair> exportRate; JComboBox<KVPair> exportRate;
JCheckBox enableParsing; JCheckBox enableParsing;
JSpinner cacheSize; JSpinner cacheSize;
@@ -297,10 +300,12 @@ public class Options extends JDialog {
bitDepth = addDropdown(optionsPanel, "Sample resolution:", getResolutionList(), get("audio.recording.resolution")); bitDepth = addDropdown(optionsPanel, "Sample resolution:", getResolutionList(), get("audio.recording.resolution"));
trimMethod = addDropdown(optionsPanel, "Auto-trim method:", getTrimMethods(), get("audio.recording.trim")); trimMethod = addDropdown(optionsPanel, "Auto-trim method:", getTrimMethods(), get("audio.recording.trim"));
fftThreshold = addSpinner(optionsPanel, "FFT threshold:", 0, 100, 1, getInteger("audio.recording.trim.fft"), ""); 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); addSeparator(optionsPanel);
playbackList = addDropdown(optionsPanel, "Playback device:", getPlaybackMixerList(), get("audio.playback.device")); playbackList = addDropdown(optionsPanel, "Playback device:", getPlaybackMixerList(), get("audio.playback.device"));
playbackBlockSize = addDropdown(optionsPanel, "Playback Block size:", getPlaybackBlockSizes(), get("audio.playback.blocksize"));
addSeparator(optionsPanel); addSeparator(optionsPanel);
storageFolder = addFilePath(optionsPanel, "Storage folder:", get("path.storage"), true); storageFolder = addFilePath(optionsPanel, "Storage folder:", get("path.storage"), true);
archiveFolder = addFilePath(optionsPanel, "Archive folder:", get("path.archive"), 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); ffmpegLocation = addFilePath(optionsPanel, "FFMPEG location:", get("path.ffmpeg"), false);
bitRate = addDropdown(optionsPanel, "Export bitrate:", getBitrates(), get("audio.export.bitrate")); 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")); exportRate = addDropdown(optionsPanel, "Export sample rate:", getSampleRateList(), get("audio.export.samplerate"));
@@ -331,7 +337,7 @@ public class Options extends JDialog {
addSeparator(optionsPanel); addSeparator(optionsPanel);
cacheSize = addSpinner(optionsPanel, "Cache size:", 0, 5000, 1, getInteger("cache.size"), ""); cacheSize = addSpinner(optionsPanel, "Cache size:", 2, 100, 1, getInteger("cache.size"), "");
addSeparator(optionsPanel); addSeparator(optionsPanel);
tabs.add("Options", new JScrollPane(optionsPanel)); tabs.add("Options", new JScrollPane(optionsPanel));
@@ -565,6 +571,8 @@ public class Options extends JDialog {
} else { } else {
defaultPrefs.put("audio.playback.device", ""); 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.pre-chapter", "1000");
defaultPrefs.put("catenation.post-chapter", "1500"); defaultPrefs.put("catenation.post-chapter", "1500");
@@ -580,6 +588,7 @@ public class Options extends JDialog {
defaultPrefs.put("path.ffmpeg", ""); defaultPrefs.put("path.ffmpeg", "");
defaultPrefs.put("audio.export.bitrate", "256000"); defaultPrefs.put("audio.export.bitrate", "256000");
defaultPrefs.put("audio.export.channels", "2");
defaultPrefs.put("audio.export.samplerate", "44100"); defaultPrefs.put("audio.export.samplerate", "44100");
defaultPrefs.put("process.sphinx", "false"); defaultPrefs.put("process.sphinx", "false");
@@ -703,11 +712,14 @@ public class Options extends JDialog {
set("catenation.post-paragraph", postParagraphGap.getValue()); set("catenation.post-paragraph", postParagraphGap.getValue());
set("catenation.post-section", postSectionGap.getValue()); set("catenation.post-section", postSectionGap.getValue());
set("audio.export.bitrate", ((KVPair)bitRate.getSelectedItem()).key); 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("audio.export.samplerate", ((KVPair)exportRate.getSelectedItem()).key);
set("process.sphinx", enableParsing.isSelected()); set("process.sphinx", enableParsing.isSelected());
set("editor.external", externalEditor.getText()); set("editor.external", externalEditor.getText());
set("cache.size", cacheSize.getValue()); set("cache.size", cacheSize.getValue());
set("audio.recording.trim.fft", fftThreshold.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.offset", etherealOffset.getValue());
set("effects.ethereal.iterations", etherealIterations.getValue()); set("effects.ethereal.iterations", etherealIterations.getValue());
@@ -775,12 +787,38 @@ public class Options extends JDialog {
} }
public static KVPair[] getTrimMethods() { public static KVPair[] getTrimMethods() {
KVPair[] pairs = new KVPair[2]; KVPair[] pairs = new KVPair[3];
pairs[0] = new KVPair<String, String>("peak", "Peak Amplitude"); pairs[0] = new KVPair<String, String>("none", "None");
pairs[1] = new KVPair<String, String>("fft", "FFT Analysis"); pairs[1] = new KVPair<String, String>("peak", "Peak Amplitude");
pairs[2] = new KVPair<String, String>("fft", "FFT Analysis");
return pairs; 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() { public static void createEffectChains() {
effectChains = new ArrayList<EffectGroup>(); effectChains = new ArrayList<EffectGroup>();

View File

@@ -19,14 +19,14 @@ public class Pan implements Effect {
return null; return null;
} }
public void process(Sample[] samples) { public void process(double[][] samples) {
for (Sample sample : samples) { for (double[] sample : samples) {
if (pan < 0) { if (pan < 0) {
double p = 1 + pan; double p = 1 + pan;
sample.right *= p; sample[Sentence.RIGHT] *= p;
} else { } else {
double p = 1 - pan; double p = 1 - pan;
sample.left *= p; sample[Sentence.LEFT] *= p;
} }
} }
} }

View File

@@ -1,28 +0,0 @@
package uk.co.majenko.audiobookrecorder;
public class Sample {
public double left;
public double right;
public Sample(double m) {
left = m;
right = m;
}
public Sample(double l, double r) {
left = l;
right = r;
}
public double getLeft() {
return left;
}
public double getRight() {
return right;
}
public double getMono() {
return (left + right) / 2.0;
}
}

View File

@@ -39,6 +39,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
int endOffset = 0; int endOffset = 0;
int crossStartOffset = -1; int crossStartOffset = -1;
int crossEndOffset = -1; int crossEndOffset = -1;
String postGapType = "none";
int sampleSize = -1; int sampleSize = -1;
@@ -58,13 +59,17 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
public void setOverrideText(String s) { overrideText = s; } public void setOverrideText(String s) { overrideText = s; }
public String getOverrideText() { return overrideText; } public String getOverrideText() { return overrideText; }
public static final int LEFT = 0;
public static final int RIGHT = 1;
TargetDataLine line; TargetDataLine line;
AudioInputStream inputStream; AudioInputStream inputStream;
AudioFormat storedFormat = null; AudioFormat storedFormat = null;
double storedLength = -1d; double storedLength = -1d;
Sample[] audioData = null; double[][] audioData = null;
Sample[] processedAudio = null;
double[][] processedAudio = null;
RecordingThread recordingThread; RecordingThread recordingThread;
@@ -90,7 +95,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
try { try {
running = true; running = true;
recording = true; recording = true;
byte[] buf = new byte[AudiobookRecorder.window.microphone.getBufferSize()]; byte[] buf = new byte[1024]; //AudiobookRecorder.window.microphone.getBufferSize()];
FileOutputStream fos = new FileOutputStream(tempFile); FileOutputStream fos = new FileOutputStream(tempFile);
int len = 0; int len = 0;
AudiobookRecorder.window.microphone.flush(); AudiobookRecorder.window.microphone.flush();
@@ -134,7 +139,6 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
} }
} }
public Sentence() { public Sentence() {
super(""); super("");
id = UUID.randomUUID().toString(); id = UUID.randomUUID().toString();
@@ -157,6 +161,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
return false; return false;
} }
CacheManager.removeFromCache(this);
recordingThread = new RecordingThread(getTempFile(), getFile(), Options.getAudioFormat()); recordingThread = new RecordingThread(getTempFile(), getFile(), Options.getAudioFormat());
@@ -173,76 +178,67 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
try { try {
Thread.sleep(10); Thread.sleep(10);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
} }
} }
audioData = null; CacheManager.removeFromCache(this);
storedFormat = null;
storedLength = -1;
if (!id.equals("room-noise")) { if (!id.equals("room-noise")) {
String tm = Options.get("audio.recording.trim"); autoTrimSample(true);
if (tm.equals("peak")) {
autoTrimSamplePeak();
} else if (tm.equals("fft")) {
autoTrimSampleFFT();
}
if (Options.getBoolean("process.sphinx")) { if (Options.getBoolean("process.sphinx")) {
recognise(); recognise();
} }
} }
}
public void autoTrimSample() {
autoTrimSample(false);
}
public void autoTrimSample(boolean useRaw) {
String tm = Options.get("audio.recording.trim");
if (tm.equals("peak")) {
autoTrimSamplePeak(useRaw);
} else if (tm.equals("fft")) {
autoTrimSampleFFT(useRaw);
}
} }
public static final int FFTBuckets = 1024; public static final int FFTBuckets = 1024;
public double[][] getFFTProfile() { public void autoTrimSampleFFT() {
double[] real = new double[FFTBuckets]; autoTrimSampleFFT(false);
double[] imag = new double[FFTBuckets];
Sample[] samples = getProcessedAudioData();
int slices = (samples.length / FFTBuckets) + 1;
double[][] out = new double[slices][];
int slice = 0;
for (int i = 0; i < samples.length; i += FFTBuckets) {
for (int j = 0; j < FFTBuckets; j++) {
if (i + j < samples.length) {
real[j] = samples[i+j].getMono();
imag[j] = 0;
} else {
real[j] = 0;
imag[j] = 0;
}
}
out[slice++] = FFT.fft(real, imag, true);
}
return out;
} }
public void autoTrimSampleFFT() { public void autoTrimSampleFFT(boolean useRaw) {
crossStartOffset = -1; crossStartOffset = -1;
crossEndOffset = -1; crossEndOffset = -1;
Sample[] samples = getProcessedAudioData(); double[][] samples;
if (samples == null) return; if (useRaw) {
samples = getRawAudioData();
} else {
samples = getProcessedAudioData();
}
if (samples == null) {
return;
}
int blocks = samples.length / 4096 + 1; int fftSize = Options.getInteger("audio.recording.trim.blocksize");
int blocks = samples.length / fftSize + 1;
int[] intens = new int[blocks]; int[] intens = new int[blocks];
int block = 0; int block = 0;
for (int i = 0; i < samples.length; i+= 4096) { for (int i = 0; i < samples.length; i+= fftSize) {
double[] real = new double[4096]; double[] real = new double[fftSize];
double[] imag = new double[4096]; double[] imag = new double[fftSize];
for (int j = 0; j < 4096; j++) { for (int j = 0; j < fftSize; j++) {
if (i + j < samples.length) { if (i + j < samples.length) {
real[j] = samples[i+j].getMono(); real[j] = (samples[i+j][LEFT] + samples[i+j][RIGHT]) / 2d;
imag[j] = 0; imag[j] = 0;
} else { } else {
real[j] = 0; real[j] = 0;
@@ -252,14 +248,14 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
double[] buckets = FFT.fft(real, imag, true); double[] buckets = FFT.fft(real, imag, true);
double av = 0; double av = 0;
for (int j = 1; j < 2048; j++) { for (int j = 1; j < fftSize/2; j++) {
av += Math.abs(buckets[j]); av += Math.abs(buckets[j]);
} }
av /= 2047d; av /= (fftSize / 2);
intens[block] = 0; 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]); double d = Math.abs(av - buckets[j]);
if (d > 0.05) { if (d > 0.05) {
intens[block]++; intens[block]++;
@@ -268,6 +264,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
block++; block++;
} }
int limit = Options.getInteger("audio.recording.trim.fft"); int limit = Options.getInteger("audio.recording.trim.fft");
// Find first block with > 1 intensity and subtract one. // Find first block with > 1 intensity and subtract one.
@@ -282,7 +279,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
start = 0; start = 0;
} }
startOffset = start * 4096; startOffset = start * fftSize;
if (startOffset < 0) startOffset = 0; if (startOffset < 0) startOffset = 0;
if (startOffset >= samples.length) startOffset = samples.length; if (startOffset >= samples.length) startOffset = samples.length;
@@ -299,19 +296,30 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
end = blocks - 1; 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 < 0) endOffset = 0;
if (endOffset >= samples.length) endOffset = samples.length; if (endOffset >= samples.length) endOffset = samples.length;
updateCrossings(); updateCrossings(useRaw);
intens = null;
samples = null;
} }
public void autoTrimSamplePeak() { public void autoTrimSamplePeak() {
autoTrimSamplePeak(false);
}
public void autoTrimSamplePeak(boolean useRaw) {
crossStartOffset = -1; crossStartOffset = -1;
crossEndOffset = -1; crossEndOffset = -1;
Sample[] samples = getProcessedAudioData(); double[][] samples;
if (useRaw) {
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;
@@ -319,7 +327,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
// Find start // Find start
for (int i = 0; i < samples.length; i++) { for (int i = 0; i < samples.length; i++) {
startOffset = i; startOffset = i;
if (Math.abs(samples[i].getMono()) > noiseFloor) { if (Math.abs((samples[i][LEFT] + samples[i][RIGHT])/2d) > noiseFloor) {
startOffset --; startOffset --;
if (startOffset < 0) startOffset = 0; if (startOffset < 0) startOffset = 0;
break; break;
@@ -330,27 +338,28 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
startOffset = 0; startOffset = 0;
} }
startOffset -= 4096; int fftSize = Options.getInteger("audio.recording.trim.blocksize");
startOffset -= fftSize;
for (int i = samples.length-1; i >= 0; i--) { for (int i = samples.length-1; i >= 0; i--) {
endOffset = i; endOffset = i;
if (Math.abs(samples[i].getMono()) > noiseFloor) { if (Math.abs((samples[i][LEFT] + samples[i][RIGHT])/2d) > noiseFloor) {
endOffset ++; endOffset ++;
if (endOffset >= samples.length-1) endOffset = samples.length-1; if (endOffset >= samples.length-1) endOffset = samples.length-1;
break; break;
} }
} }
endOffset += 4096; endOffset += fftSize;
if (endOffset <= startOffset) endOffset = startOffset + 4096; if (endOffset <= startOffset) endOffset = startOffset + fftSize;
if (endOffset <= 0) { if (endOffset <= 0) {
endOffset = samples.length-1; endOffset = samples.length-1;
} }
if (startOffset < 0) startOffset = 0; if (startOffset < 0) startOffset = 0;
if (endOffset >= samples.length) endOffset = samples.length-1; if (endOffset >= samples.length) endOffset = samples.length-1;
updateCrossings(); updateCrossings(useRaw);
} }
public String getId() { public String getId() {
@@ -434,19 +443,31 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
} }
public void updateCrossings() { public void updateCrossings() {
updateStartCrossing(); updateCrossings(false);
updateEndCrossing(); }
public void updateCrossings(boolean useRaw) {
updateStartCrossing(useRaw);
updateEndCrossing(useRaw);
} }
public void updateStartCrossing() { public void updateStartCrossing() {
updateStartCrossing(false);
}
public void updateStartCrossing(boolean useRaw) {
if (crossStartOffset == -1) { if (crossStartOffset == -1) {
crossStartOffset = findNearestZeroCrossing(startOffset, 4096); crossStartOffset = findNearestZeroCrossing(useRaw, startOffset, 4096);
} }
} }
public void updateEndCrossing() { public void updateEndCrossing() {
updateEndCrossing(false);
}
public void updateEndCrossing(boolean useRaw) {
if (crossEndOffset == -1) { if (crossEndOffset == -1) {
crossEndOffset = findNearestZeroCrossing(endOffset, 4096); crossEndOffset = findNearestZeroCrossing(useRaw, endOffset, 4096);
} }
} }
@@ -566,6 +587,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
public void clearCache() { public void clearCache() {
audioData = null; audioData = null;
processedAudio = null; processedAudio = null;
storedFormat = null;
storedLength = -1;
} }
public boolean lockedInCache() { public boolean lockedInCache() {
@@ -573,7 +596,16 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
} }
public int findNearestZeroCrossing(int pos, int range) { public int findNearestZeroCrossing(int pos, int range) {
Sample[] data = getProcessedAudioData(); return findNearestZeroCrossing(false, pos, range);
}
public int findNearestZeroCrossing(boolean useRaw, int pos, int range) {
double[][] data = null;
if (useRaw) {
data = getRawAudioData();
} else {
data = getProcessedAudioData();
}
if (data == null) return 0; if (data == null) return 0;
if (data.length == 0) return 0; if (data.length == 0) return 0;
@@ -583,19 +615,19 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
int backwards = pos; int backwards = pos;
int forwards = pos; int forwards = pos;
double backwardsPrev = data[backwards].getMono(); double backwardsPrev = (data[backwards][LEFT] + data[backwards][RIGHT]) / 2d;
double forwardsPrev = data[forwards].getMono(); double forwardsPrev = (data[forwards][LEFT] + data[forwards][RIGHT]) / 2d;
while (backwards > 0 || forwards < data.length-2) { while (backwards > 0 || forwards < data.length-2) {
if (forwards < data.length-2) forwards++; if (forwards < data.length-2) forwards++;
if (backwards > 0) backwards--; if (backwards > 0) backwards--;
if (backwardsPrev >= 0 && data[backwards].getMono() < 0) { // Found one! if (backwardsPrev >= 0 && ((data[backwards][LEFT] + data[backwards][RIGHT]) / 2d) < 0) { // Found one!
return backwards; return backwards;
} }
if (forwardsPrev < 0 && data[forwards].getMono() >= 0) { if (forwardsPrev < 0 && ((data[forwards][LEFT] + data[forwards][RIGHT]) / 2d) >= 0) {
return forwards; return forwards;
} }
@@ -604,8 +636,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
return pos; return pos;
} }
backwardsPrev = data[backwards].getMono(); backwardsPrev = (data[backwards][LEFT] + data[backwards][RIGHT]) / 2d;
forwardsPrev = data[forwards].getMono(); forwardsPrev = (data[forwards][LEFT] + data[forwards][RIGHT]) / 2d;
} }
return pos; return pos;
} }
@@ -647,17 +679,26 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
} }
public double getPeakValue() { public double getPeakValue() {
return getPeakValue(false);
}
public double getPeakValue(boolean useRaw) {
double oldGain = gain; double oldGain = gain;
gain = 1.0d; gain = 1.0d;
Sample[] samples = getProcessedAudioData(); double[][] samples = null;
if (useRaw) {
samples = getRawAudioData();
} else {
samples = getProcessedAudioData();
}
gain = oldGain; gain = oldGain;
if (samples == null) { if (samples == null) {
return 0; return 0;
} }
double ms = 0; double ms = 0;
for (int i = 0; i < samples.length; i++) { for (int i = 0; i < samples.length; i++) {
if (Math.abs(samples[i].getMono()) > ms) { if (Math.abs((samples[i][LEFT] + samples[i][RIGHT]) / 2d) > ms) {
ms = Math.abs(samples[i].getMono()); ms = Math.abs((samples[i][LEFT] + samples[i][RIGHT]) / 2d);
} }
} }
return ms; return ms;
@@ -676,8 +717,10 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
if (g <= 0.0001d) g = 1.0d; if (g <= 0.0001d) g = 1.0d;
if (g == gain) return; if (g == gain) return;
if (gain != g) { int gint = (int)(g * 100d);
clearCache(); int gainint = (int)(gain * 100d);
if (gint != gainint) {
CacheManager.removeFromCache(this);
} }
gain = g; gain = g;
} }
@@ -723,7 +766,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
proc.waitFor(); proc.waitFor();
} catch (Exception e) { } catch (Exception e) {
} }
clearCache(); CacheManager.removeFromCache(Sentence.this);
AudiobookRecorder.window.updateWaveform(); AudiobookRecorder.window.updateWaveform();
} }
} }
@@ -749,7 +792,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
} }
} }
if (backupNumber == -1) { if (backupNumber == -1) {
System.err.println("Out of backup space!"); debug("Out of backup space!");
return; return;
} }
@@ -813,7 +856,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
} }
} }
clearCache(); CacheManager.removeFromCache(Sentence.this);
AudiobookRecorder.window.updateWaveform(); AudiobookRecorder.window.updateWaveform();
} }
} }
@@ -856,18 +899,17 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
e.printStackTrace(); e.printStackTrace();
} }
clearCache(); CacheManager.removeFromCache(this);
AudiobookRecorder.window.updateWaveform(); AudiobookRecorder.window.updateWaveform();
} }
public Sample[] getDoubleDataS16LE(AudioInputStream s, AudioFormat format) throws IOException { public double[][] getDoubleDataS16LE(AudioInputStream s, AudioFormat format) throws IOException {
long len = s.getFrameLength(); long len = s.getFrameLength();
int frameSize = format.getFrameSize(); int frameSize = format.getFrameSize();
int chans = format.getChannels(); int chans = format.getChannels();
int bytes = frameSize / chans;
byte[] frame = new byte[frameSize]; byte[] frame = new byte[frameSize];
Sample[] samples = new Sample[(int)len]; double[][] samples = new double[(int)len][2];
for (long fno = 0; fno < len; fno++) { for (long fno = 0; fno < len; fno++) {
@@ -881,27 +923,166 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
int right = (rh << 8) | rl; int right = (rh << 8) | rl;
if ((left & 0x8000) == 0x8000) left |= 0xFFFF0000; if ((left & 0x8000) == 0x8000) left |= 0xFFFF0000;
if ((right & 0x8000) == 0x8000) right |= 0xFFFF0000; if ((right & 0x8000) == 0x8000) right |= 0xFFFF0000;
samples[(int)fno] = new Sample((double)left / 32768d, (double)right / 32768d); samples[(int)fno][LEFT] = (double)left / 32767d;
samples[(int)fno][RIGHT] = (double)right / 32767d;
} else { } else {
int l = frame[0] >= 0 ? frame[0] : 256 + frame[0]; int l = frame[0] >= 0 ? frame[0] : 256 + frame[0];
int h = frame[1] >= 0 ? frame[1] : 256 + frame[1]; int h = frame[1] >= 0 ? frame[1] : 256 + frame[1];
int mono = (h << 8) | l; int mono = (h << 8) | l;
if ((mono & 0x8000) == 0x8000) mono |= 0xFFFF0000; if ((mono & 0x8000) == 0x8000) mono |= 0xFFFF0000;
samples[(int)fno] = new Sample((double)mono / 32768d); samples[(int)fno][LEFT] = (double)mono / 32767d;
samples[(int)fno][RIGHT] = (double)mono / 32767d;
} }
} }
return samples; return samples;
} }
public Sample[] getDoubleDataS24LE(AudioInputStream s, AudioFormat format) throws IOException { public void writeDoubleDataS16LE(double[][] samples, AudioFormat format) throws IOException {
int chans = format.getChannels();
int frames = samples.length;
byte[] buffer;
if (chans == 2) {
int buflen = frames * 4;
buffer = new byte[buflen];
for (int i = 0; i < frames; i++) {
double left = samples[i][LEFT];
double right = samples[i][RIGHT];
int off = i * 4;
left *= 32767d;
right *= 32767d;
int li = (int)left;
int ri = (int)right;
if (li > 32767) li = 32767;
if (li < -32767) li = -32767;
if (ri > 32767) ri = 32767;
if (ri < -32767) ri = -32767;
buffer[off + 0] = (byte)(li & 0xFF);
buffer[off + 1] = (byte)((li >> 8) & 0xFF);
buffer[off + 2] = (byte)(ri & 0xFF);
buffer[off + 3] = (byte)((ri >> 8) & 0xFF);
}
} else {
int buflen = frames * 2;
buffer = new byte[buflen];
for (int i = 0; i < frames; i++) {
double left = samples[i][LEFT];
double right = samples[i][RIGHT];
double mono = (left + right) / 2d;
int off = i * 2;
mono *= 32767d;
int mi = (int)mono;
if (mi > 32767) mi = 32767;
if (mi < -32767) mi = -32767;
buffer[off + 0] = (byte)(mi & 0xFF);
buffer[off + 1] = (byte)((mi >> 8) & 0xFF);
}
}
backup();
ByteArrayInputStream bis = new ByteArrayInputStream(buffer);
AudioInputStream ais = new AudioInputStream(bis, format, frames);
File wavFile = getFile();
FileOutputStream fos = new FileOutputStream(wavFile);
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, fos);
fos.close();
ais.close();
}
public void writeDoubleDataS24LE(double[][] samples, AudioFormat format) throws IOException {
int chans = format.getChannels();
int frames = samples.length;
byte[] buffer;
if (chans == 2) {
int buflen = frames * 6;
buffer = new byte[buflen];
for (int i = 0; i < frames; i++) {
double left = samples[i][LEFT];
double right = samples[i][RIGHT];
int off = i * 6;
left *= 8388607d;
right *= 8388607d;
int li = (int)left;
int ri = (int)right;
if (li > 8388607) li = 8388607;
if (li < -8388607) li = -8388607;
if (ri > 8388607) ri = 8388607;
if (ri < -8388607) ri = -8388607;
buffer[off + 0] = (byte)(li & 0xFF);
buffer[off + 1] = (byte)((li >> 8) & 0xFF);
buffer[off + 2] = (byte)((li >> 16) & 0xFF);
buffer[off + 3] = (byte)(ri & 0xFF);
buffer[off + 4] = (byte)((ri >> 8) & 0xFF);
buffer[off + 5] = (byte)((ri >> 16) & 0xFF);
}
} else {
int buflen = frames * 3;
buffer = new byte[buflen];
for (int i = 0; i < frames; i++) {
double left = samples[i][LEFT];
double right = samples[i][RIGHT];
double mono = (left + right) / 2d;
int off = i * 3;
mono *= 8388607d;
int mi = (int)mono;
if (mi > 8388607) mi = 8388607;
if (mi < -8388607) mi = -8388607;
buffer[off + 0] = (byte)(mi & 0xFF);
buffer[off + 1] = (byte)((mi >> 8) & 0xFF);
buffer[off + 2] = (byte)((mi >> 16) & 0xFF);
}
}
backup();
ByteArrayInputStream bis = new ByteArrayInputStream(buffer);
AudioInputStream ais = new AudioInputStream(bis, format, frames);
File wavFile = getFile();
FileOutputStream fos = new FileOutputStream(wavFile);
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, fos);
fos.close();
ais.close();
}
public void writeAudioData(double[][] samples) throws IOException {
AudioFormat format = getAudioFormat();
switch (format.getSampleSizeInBits()) {
case 16:
writeDoubleDataS16LE(samples, format);
break;
case 24:
writeDoubleDataS24LE(samples, format);
break;
}
CacheManager.removeFromCache(this);
}
public double[][] getDoubleDataS24LE(AudioInputStream s, AudioFormat format) throws IOException {
long len = s.getFrameLength(); long len = s.getFrameLength();
int frameSize = format.getFrameSize(); int frameSize = format.getFrameSize();
int chans = format.getChannels(); int chans = format.getChannels();
int bytes = frameSize / chans;
byte[] frame = new byte[frameSize]; byte[] frame = new byte[frameSize];
Sample[] samples = new Sample[(int)len]; double[][] samples = new double[(int)len][2];
for (long fno = 0; fno < len; fno++) { for (long fno = 0; fno < len; fno++) {
@@ -918,14 +1099,16 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
int right = (rh << 16) | (rm << 8) | rl; int right = (rh << 16) | (rm << 8) | rl;
if ((left & 0x800000) == 0x800000) left |= 0xFF000000; if ((left & 0x800000) == 0x800000) left |= 0xFF000000;
if ((right & 0x800000) == 0x800000) right |= 0xFF000000; if ((right & 0x800000) == 0x800000) right |= 0xFF000000;
samples[(int)fno] = new Sample((double)left / 8388608d, (double)right / 8388608d); samples[(int)fno][LEFT] = (double)left / 8388607d;
samples[(int)fno][RIGHT] = (double)right / 8388607d;
} else { } else {
int l = frame[0] >= 0 ? frame[0] : 256 + frame[0]; int l = frame[0] >= 0 ? frame[0] : 256 + frame[0];
int m = frame[1] >= 0 ? frame[1] : 256 + frame[1]; int m = frame[1] >= 0 ? frame[1] : 256 + frame[1];
int h = frame[2] >= 0 ? frame[2] : 256 + frame[2]; int h = frame[2] >= 0 ? frame[2] : 256 + frame[2];
int mono = (h << 16) | (m << 8) | l; int mono = (h << 16) | (m << 8) | l;
if ((mono & 0x800000) == 0x800000) mono |= 0xFF000000; if ((mono & 0x800000) == 0x800000) mono |= 0xFF000000;
samples[(int)fno] = new Sample((double)mono / 8388608d); samples[(int)fno][LEFT] = (double)mono / 8388607d;
samples[(int)fno][RIGHT] = (double)mono / 8388607d;
} }
} }
@@ -933,14 +1116,20 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
} }
public void loadFile() { public void loadFile() {
if (audioData != null) return; if (audioData != null) {
return;
}
File f = getFile(); File f = getFile();
try { try {
if (!f.exists()) {
debug("TODO: Race condition: wav file doesn't exist yet");
return;
}
AudioInputStream s = AudioSystem.getAudioInputStream(f); AudioInputStream s = AudioSystem.getAudioInputStream(f);
AudioFormat format = getAudioFormat(); AudioFormat format = getAudioFormat();
Sample[] samples = null; double[][] samples = null;
switch (format.getSampleSizeInBits()) { switch (format.getSampleSizeInBits()) {
case 16: case 16:
@@ -956,17 +1145,30 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
audioData = samples; audioData = samples;
CacheManager.addToCache(this); CacheManager.addToCache(this);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
} }
} }
public Sample[] getProcessedAudioData() { synchronized public double[][] getRawAudioData() {
loadFile(); loadFile();
if (processedAudio != null) return processedAudio; return audioData;
}
synchronized public double[][] getProcessedAudioData() {
return getProcessedAudioData(true);
}
synchronized public double[][] getProcessedAudioData(boolean effectsEnabled) {
loadFile();
if (processedAudio != null) {
return processedAudio;
}
if (audioData == null) return null; if (audioData == null) return null;
processedAudio = new Sample[audioData.length]; processedAudio = new double[audioData.length][2];
for (int i = 0; i < audioData.length; i++) { for (int i = 0; i < audioData.length; i++) {
processedAudio[i] = new Sample(audioData[i].left, audioData[i].right); processedAudio[i][LEFT] = audioData[i][LEFT];
processedAudio[i][RIGHT] = audioData[i][RIGHT];
} }
// Add processing in here. // Add processing in here.
@@ -974,64 +1176,82 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
String def = AudiobookRecorder.window.getDefaultEffectsChain(); String def = AudiobookRecorder.window.getDefaultEffectsChain();
Effect eff = AudiobookRecorder.window.effects.get(def); Effect eff = AudiobookRecorder.window.effects.get(def);
if (eff != null) { if (effectsEnabled) {
eff.init(getAudioFormat().getFrameRate()); if (eff != null) {
eff.process(processedAudio); eff.init(getAudioFormat().getFrameRate());
} 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);
}
} }
} }
} }
// Add final master gain stage // Add final master gain stage
for (int i = 0; i < processedAudio.length; i++) { for (int i = 0; i < processedAudio.length; i++) {
processedAudio[i].left = processedAudio[i].left * gain; processedAudio[i][LEFT] *= gain;
processedAudio[i].right = processedAudio[i].right * gain; processedAudio[i][RIGHT] *= gain;
} }
return processedAudio; return processedAudio;
} }
public Sample[] getDoubleAudioData() { public double[][] getDoubleAudioData() {
return getProcessedAudioData(); return getDoubleAudioData(true);
} }
public Sample[] getCroppedAudioData() { public double[][] getDoubleAudioData(boolean effectsEnabled) {
Sample[] inSamples = getDoubleAudioData(); return getProcessedAudioData(effectsEnabled);
}
public double[][] getCroppedAudioData() {
return getCroppedAudioData(true);
}
public double[][] getCroppedAudioData(boolean effectsEnabled) {
double[][] inSamples = getDoubleAudioData(effectsEnabled);
if (inSamples == null) return null; if (inSamples == null) return null;
updateCrossings(); updateCrossings();
int length = crossEndOffset - crossStartOffset; int length = crossEndOffset - crossStartOffset;
Sample[] samples = new Sample[length]; double[][] samples = new double[length][2];
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
samples[i] = inSamples[crossStartOffset + i]; samples[i][LEFT] = inSamples[crossStartOffset + i][LEFT];
samples[i][RIGHT] = inSamples[crossStartOffset + i][RIGHT];
} }
return samples; return samples;
} }
public byte[] getPCMData() { public byte[] getPCMData() {
Sample[] croppedData = getCroppedAudioData(); return getPCMData(true);
}
public byte[] getPCMData(boolean effectsEnabled) {
double[][] croppedData = getCroppedAudioData(effectsEnabled);
if (croppedData == null) return null; if (croppedData == null) return null;
int length = croppedData.length; int length = croppedData.length;
byte[] pcmData = new byte[length * 4]; byte[] pcmData = new byte[length * 4];
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
double sd = croppedData[i].left * 32768d; double sd = croppedData[i][LEFT] * 32767d;
int si = (int)sd; int si = (int)sd;
if (si > 32767) si = 32767; if (si > 32767) si = 32767;
if (si < -32768) si = -32768; if (si < -32767) si = -32767;
pcmData[i * 4] = (byte)(si & 0xFF); pcmData[i * 4] = (byte)(si & 0xFF);
pcmData[(i * 4) + 1] = (byte)((si & 0xFF00) >> 8); pcmData[(i * 4) + 1] = (byte)((si & 0xFF00) >> 8);
sd = croppedData[i].right * 32768d; sd = croppedData[i][RIGHT] * 32767d;
si = (int)sd; si = (int)sd;
if (si > 32767) si = 32767; if (si > 32767) si = 32767;
if (si < -32768) si = -32768; if (si < -32767) si = -32767;
pcmData[(i * 4) + 2] = (byte)(si & 0xFF); pcmData[(i * 4) + 2] = (byte)(si & 0xFF);
pcmData[(i * 4) + 3] = (byte)((si & 0xFF00) >> 8); pcmData[(i * 4) + 3] = (byte)((si & 0xFF00) >> 8);
} }
@@ -1040,7 +1260,7 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
public void setEffectChain(String key) { public void setEffectChain(String key) {
if ((effectChain != null) && (!effectChain.equals(key))) { if ((effectChain != null) && (!effectChain.equals(key))) {
clearCache(); CacheManager.removeFromCache(this);
} }
effectChain = key; effectChain = key;
} }
@@ -1049,4 +1269,76 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
if (effectChain == null) return "none"; if (effectChain == null) return "none";
return effectChain; return effectChain;
} }
public String getPostGapType() {
return postGapType;
}
public void setPostGapType(String t) {
if (t == null || t.equals("none")) {
if (getPostGap() == Options.getInteger("catenation.short-sentence")) {
t = "continuation";
} else if (getPostGap() == Options.getInteger("catenation.post-paragraph")) {
t = "paragraph";
} else if (getPostGap() == Options.getInteger("catenation.post-section")) {
t = "section";
} else if (getPostGap() == Options.getInteger("catenation.post-sentence")) {
t = "sentence";
} else {
t = "sentence";
}
}
postGapType = t;
}
public void resetPostGap() {
if (postGapType == null) {
postGapType = "sentence";
}
if (postGapType.equals("continuation")) {
setPostGap(Options.getInteger("catenation.short-sentence"));
} else if (postGapType.equals("paragraph")) {
setPostGap(Options.getInteger("catenation.post-paragraph"));
} else if (postGapType.equals("section")) {
setPostGap(Options.getInteger("catenation.post-section"));
} else {
setPostGap(Options.getInteger("catenation.post-sentence"));
}
}
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();
}
}
}
} }

View File

@@ -63,4 +63,17 @@ public class Utils {
} }
} }
} }
static long millis = System.currentTimeMillis();
public static void report(String tag) {
long t = System.currentTimeMillis();
long d = t - millis;
millis = t;
System.err.println(String.format("%10d - %10s : %8d | %8d | %8d", d, tag,
Runtime.getRuntime().totalMemory(),
Runtime.getRuntime().maxMemory(),
Runtime.getRuntime().freeMemory()
));
}
} }

View File

@@ -9,7 +9,7 @@ import javax.sound.sampled.*;
public class Waveform extends JPanel implements MouseListener, MouseMotionListener { public class Waveform extends JPanel implements MouseListener, MouseMotionListener {
Sample[] samples = null; double[][] samples = null;
int leftMarker = 0; int leftMarker = 0;
int rightMarker = 0; int rightMarker = 0;
@@ -21,6 +21,11 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
int leftAltMarker = 0; int leftAltMarker = 0;
int rightAltMarker = 0; int rightAltMarker = 0;
int cutEntry = 0;
int cutExit = 0;
boolean displayCut = false;
boolean displaySplit = false;
int dragging = 0; int dragging = 0;
int step = 1; int step = 1;
@@ -92,7 +97,7 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
double lmax = 0; double lmax = 0;
for (int o = 0; o < step; o++) { for (int o = 0; o < step; o++) {
double sample = samples[offset + (n * step) + o].getMono(); double sample = (samples[offset + (n * step) + o][Sentence.LEFT] + samples[offset + (n * step) + o][Sentence.RIGHT]) / 2d;
if (sample >= 0) { if (sample >= 0) {
have += sample; have += sample;
hcnt++; hcnt++;
@@ -133,7 +138,7 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
g.drawLine(n, (int)(h/2 + lave), n, (int)(h/2 - have)); g.drawLine(n, (int)(h/2 + lave), n, (int)(h/2 - have));
} }
g.setColor(new Color(255, 0, 0, 32)); g.setColor(new Color(255, 0, 0, 64));
g.fillRect(0, 0, (leftAltMarker - offset)/step, h); g.fillRect(0, 0, (leftAltMarker - offset)/step, h);
g.fillRect((rightAltMarker - offset)/step, 0, (num - rightAltMarker) / step , h); g.fillRect((rightAltMarker - offset)/step, 0, (num - rightAltMarker) / step , h);
@@ -145,6 +150,19 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
g.drawLine((leftMarker - offset)/step, 0, (leftMarker - offset)/step, h); g.drawLine((leftMarker - offset)/step, 0, (leftMarker - offset)/step, h);
g.drawLine((rightMarker - offset)/step, 0, (rightMarker - offset)/step, h); g.drawLine((rightMarker - offset)/step, 0, (rightMarker - offset)/step, h);
if (displayCut || displaySplit) {
g.setColor(new Color(0, 255, 255));
g.drawLine((cutEntry - offset)/step, 0, (cutEntry - offset)/step, h);
}
if (displayCut) {
g.setColor(new Color(0, 255, 255));
g.drawLine((cutExit - offset)/step, 0, (cutExit - offset)/step, h);
g.setColor(new Color(0, 255, 255, 80));
g.fillRect((cutEntry - offset)/step, 0, ((cutExit - offset) - (cutEntry - offset))/step , h);
}
g.setColor(new Color(0, 255, 255)); g.setColor(new Color(0, 255, 255));
for (int i = 0; i < h; i += 2) { for (int i = 0; i < h; i += 2) {
g.drawLine((playMarker - offset) / step, i, (playMarker - offset) / step, i); g.drawLine((playMarker - offset) / step, i, (playMarker - offset) / step, i);
@@ -189,9 +207,11 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
repaint(); repaint();
} }
public void setData(Sample[] s) { public void setData(double[][] s) {
samples = s; samples = s;
playMarker = 0; playMarker = 0;
displayCut = false;
displaySplit = false;
repaint(); repaint();
} }
@@ -221,6 +241,19 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
dragging = 2; dragging = 2;
return; return;
} }
if (displayCut || displaySplit) {
if ((x >= ((cutEntry - offset)/step) - 10) && (x <= ((cutEntry - offset)/step) + 10)) {
dragging = 3;
return;
}
}
if (displayCut) {
if ((x >= ((cutExit - offset)/step) - 10) && (x <= ((cutExit - offset)/step) + 10)) {
dragging = 4;
return;
}
}
} }
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
@@ -251,6 +284,19 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
return; return;
} }
if (displayCut || displaySplit) {
if ((x >= ((cutEntry - offset)/step) - 10) && (x <= ((cutEntry - offset)/step) + 10)) {
setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
return;
}
}
if (displayCut) {
if ((x >= ((cutExit - offset)/step) - 10) && (x <= ((cutExit - offset)/step) + 10)) {
setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
return;
}
}
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
@@ -270,6 +316,18 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
if (rightMarker < leftMarker) { if (rightMarker < leftMarker) {
rightMarker = leftMarker; rightMarker = leftMarker;
} }
} else if (dragging == 3) {
cutEntry = (x * step) + offset;
if (displayCut) {
if (cutEntry > cutExit) {
cutEntry = cutExit;
}
}
} else if (dragging == 4) {
cutExit = (x * step) + offset;
if (cutExit < cutEntry) {
cutExit = cutEntry;
}
} }
repaint(); repaint();
@@ -299,4 +357,33 @@ public class Waveform extends JPanel implements MouseListener, MouseMotionListen
playMarker = leftAltMarker + m; playMarker = leftAltMarker + m;
repaint(); repaint();
} }
public void setDisplayCut(boolean c) {
displayCut = c;
displaySplit = false;
if (displayCut) {
int d = rightMarker - leftMarker;
cutEntry = leftMarker + (d / 3);
cutExit = leftMarker + (d / 3 * 2);
}
repaint();
}
public void setDisplaySplit(boolean c) {
displayCut = false;
displaySplit = c;
if (displaySplit) {
int d = rightMarker - leftMarker;
cutEntry = leftMarker + (d / 2);
}
repaint();
}
public int getCutStart() {
return cutEntry;
}
public int getCutEnd() {
return cutExit;
}
} }