Added ethereal voice effect

This commit is contained in:
2018-10-29 15:33:25 +00:00
parent f5c0d5bbfc
commit 175281537c
4 changed files with 218 additions and 4 deletions

View File

@@ -77,6 +77,7 @@ public class AudiobookRecorder extends JFrame {
JSpinner postSentenceGap;
JCheckBox locked;
JCheckBox attention;
JCheckBox ethereal;
JButtonSpacePlay reprocessAudioFFT;
JButtonSpacePlay reprocessAudioPeak;
@@ -443,6 +444,27 @@ public class AudiobookRecorder extends JFrame {
controlsTop.add(attention);
ethereal = new JCheckBox("Ethereal voice");
ethereal.setFocusable(false);
ethereal.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JCheckBox c = (JCheckBox)e.getSource();
if (c.isSelected()) {
if (selectedSentence != null) {
selectedSentence.setEthereal(true);
}
} else {
if (selectedSentence != null) {
selectedSentence.setEthereal(false);
}
}
bookTreeModel.reload(selectedSentence);
}
});
controlsTop.add(ethereal);
controlsTop.add(Box.createHorizontalGlue());
controlsTop.add(new JLabel("Post gap:"));
controlsTop.add(postSentenceGap);
@@ -1015,6 +1037,7 @@ public class AudiobookRecorder extends JFrame {
JMenuItem editData = new JMenuItem("Edit Data...");
editData.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JTabbedPane tabs = new JTabbedPane();
BookInfoPanel info = new BookInfoPanel(
book.getName(),
book.getAuthor(),
@@ -1022,7 +1045,38 @@ public class AudiobookRecorder extends JFrame {
book.getComment(),
book.getACX()
);
int r = JOptionPane.showConfirmDialog(AudiobookRecorder.this, info, "Edit Book", JOptionPane.OK_CANCEL_OPTION);
tabs.add("Data", info);
JPanel effects = new JPanel();
effects.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
effects.add(new JLabel("Ethereal Iterations:"), c);
c.gridx = 1;
JSpinner ethIt = new JSpinner(new SteppedNumericSpinnerModel(1, 10, 1, book.getInteger("effects.ethereal.iterations")));
effects.add(ethIt, c);
c.gridx = 0;
c.gridy++;
effects.add(new JLabel("Ethereal Attenuation:"), c);
c.gridx = 1;
JSpinner ethAt = new JSpinner(new SteppedNumericSpinnerModel(0, 100, 1, book.getInteger("effects.ethereal.attenuation")));
effects.add(ethAt, c);
c.gridx = 0;
c.gridy++;
effects.add(new JLabel("Ethereal Offset:"), c);
c.gridx = 1;
JSpinner ethOf = new JSpinner(new SteppedNumericSpinnerModel(0, 2000, 10, book.getInteger("effects.ethereal.offset")));
effects.add(ethOf, c);
c.gridx = 0;
c.gridy++;
tabs.add("Effects", effects);
int r = JOptionPane.showConfirmDialog(AudiobookRecorder.this, tabs, "Edit Book", JOptionPane.OK_CANCEL_OPTION);
if (r != JOptionPane.OK_OPTION) return;
String tit = info.getTitle();
@@ -1031,6 +1085,10 @@ public class AudiobookRecorder extends JFrame {
String com = info.getComment();
String acx = info.getACX();
book.set("effects.ethereal.iterations", (Integer)ethIt.getValue());
book.set("effects.ethereal.attenuation", (Integer)ethAt.getValue());
book.set("effects.ethereal.offset", (Integer)ethOf.getValue());
book.setAuthor(aut);
book.setGenre(gen);
book.setComment(com);
@@ -1297,6 +1355,9 @@ public class AudiobookRecorder extends JFrame {
File config = new File(bookRoot, "audiobook.abk");
Properties prefs = new Properties();
prefs.setProperty("effects.ethereal.iterations", book.get("effects.ethereal.iterations"));
prefs.setProperty("effects.ethereal.offset", book.get("effects.ethereal.offset"));
prefs.setProperty("effects.ethereal.attenuation", book.get("effects.ethereal.attenuation"));
prefs.setProperty("book.name", book.getName());
prefs.setProperty("book.author", book.getAuthor());
@@ -1330,6 +1391,7 @@ public class AudiobookRecorder extends JFrame {
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.ethereal", keybase, i), snt.getEthereal() ? "true" : "false");
i++;
}
}
@@ -1379,7 +1441,7 @@ public class AudiobookRecorder extends JFrame {
public void buildBook(Properties prefs) {
book = new Book(prefs.getProperty("book.name"));
book = new Book(prefs, prefs.getProperty("book.name"));
book.setAuthor(prefs.getProperty("book.author"));
book.setGenre(prefs.getProperty("book.genre"));
@@ -1439,6 +1501,7 @@ public class AudiobookRecorder extends JFrame {
postSentenceGap.setValue(s.getPostGap());
locked.setSelected(s.isLocked());
attention.setSelected(s.getAttentionFlag());
ethereal.setSelected(s.getEthereal());
postSentenceGap.setEnabled(!s.isLocked());
reprocessAudioFFT.setEnabled(!s.isLocked());
@@ -1449,6 +1512,7 @@ public class AudiobookRecorder extends JFrame {
postSentenceGap.setValue(0);
locked.setSelected(false);
attention.setSelected(false);
ethereal.setSelected(false);
}
}
});
@@ -1488,6 +1552,7 @@ public class AudiobookRecorder extends JFrame {
s.setEndOffset(Utils.s2i(prefs.getProperty(String.format("chapter.audition.sentence.%08d.end-offset", i))));
s.setLocked(Utils.s2b(prefs.getProperty(String.format("chapter.audition.sentence.%08d.locked", i))));
s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.audition.sentence.%08d.attention", i))));
s.setEthereal(Utils.s2b(prefs.getProperty(String.format("chapter.audition.sentence.%08d.ethereal", i))));
bookTreeModel.insertNodeInto(s, c, c.getChildCount());
}
@@ -1507,6 +1572,7 @@ public class AudiobookRecorder extends JFrame {
s.setEndOffset(Utils.s2i(prefs.getProperty(String.format("chapter.open.sentence.%08d.end-offset", i))));
s.setLocked(Utils.s2b(prefs.getProperty(String.format("chapter.open.sentence.%08d.locked", i))));
s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.open.sentence.%08d.attention", i))));
s.setEthereal(Utils.s2b(prefs.getProperty(String.format("chapter.open.sentence.%08d.ethereal", i))));
bookTreeModel.insertNodeInto(s, c, c.getChildCount());
}
@@ -1531,7 +1597,8 @@ public class AudiobookRecorder extends JFrame {
s.setStartOffset(Utils.s2i(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.start-offset", cno, i))));
s.setEndOffset(Utils.s2i(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.end-offset", cno, i))));
s.setLocked(Utils.s2b(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.locked", cno, i))));
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.setEthereal(Utils.s2b(prefs.getProperty(String.format("chapter.%04d.sentence.%08d.ethereal", cno, i))));
bookTreeModel.insertNodeInto(s, c, c.getChildCount());
}
}
@@ -1552,6 +1619,7 @@ public class AudiobookRecorder extends JFrame {
s.setEndOffset(Utils.s2i(prefs.getProperty(String.format("chapter.close.sentence.%08d.end-offset", i))));
s.setLocked(Utils.s2b(prefs.getProperty(String.format("chapter.close.sentence.%08d.locked", i))));
s.setAttentionFlag(Utils.s2b(prefs.getProperty(String.format("chapter.close.sentence.%08d.attention", i))));
s.setEthereal(Utils.s2b(prefs.getProperty(String.format("chapter.close.sentence.%08d.ethereal", i))));
bookTreeModel.insertNodeInto(s, c, c.getChildCount());
}

View File

@@ -29,8 +29,12 @@ public class Book extends DefaultMutableTreeNode {
float[] eqChannels = new float[31];
public Book(String bookname) {
Properties prefs;
public Book(Properties p, String bookname) {
super(bookname);
prefs = p;
name = bookname;
equaliser = new Equaliser();
AudiobookRecorder.window.setTitle("AudioBook Recorder :: " + name);
@@ -164,4 +168,22 @@ public class Book extends DefaultMutableTreeNode {
public AudioFormat getAudioFormat() {
return new AudioFormat(getSampleRate(), getResolution(), getChannels(), true, false);
}
public String get(String key) {
if (prefs.getProperty(key) == null) { return Options.get(key); }
return prefs.getProperty(key);
}
public Integer getInteger(String key) {
if (prefs.getProperty(key) == null) { return Options.getInteger(key); }
return Utils.s2i(prefs.getProperty(key));
}
public void set(String key, String value) {
prefs.setProperty(key, value);
}
public void set(String key, Integer value) {
prefs.setProperty(key, "" + value);
}
}

View File

@@ -34,6 +34,10 @@ public class Options extends JDialog {
JCheckBox enableParsing;
JSpinner cacheSize;
JSpinner etherealIterations;
JSpinner etherealAttenuation;
JSpinner etherealOffset;
JTextField havenApiKey;
Equaliser equaliser;
@@ -319,6 +323,26 @@ public class Options extends JDialog {
startupScript = new JTextArea(get("scripts.startup"));
startScript.add(startupScript, BorderLayout.CENTER);
tabs.add("Startup Script", startScript);
JPanel effects = new JPanel();
effects.setLayout(new GridBagLayout());
constraint.gridx = 0;
constraint.gridy = 0;
constraint.gridwidth = 1;
constraint.gridheight = 1;
etherealOffset = addSpinner(effects, "Ethereal Voice Offset", 0, 2000, 10, getInteger("effects.ethereal.offset"), "ms");
etherealIterations = addSpinner(effects, "Ethereal Voice Iterations", 1, 10, 1, getInteger("effects.ethereal.iterations"), "");
etherealAttenuation = addSpinner(effects, "Ethereal Voice Attenuation", 0, 100, 1, getInteger("effects.ethereal.attenuation"), "%");
tabs.add("Effects", effects);
add(tabs, BorderLayout.CENTER);
setTitle("Options");
@@ -525,6 +549,10 @@ public class Options extends JDialog {
defaultPrefs.put("scripts.startup", "");
defaultPrefs.put("effects.ethereal.offset", "50");
defaultPrefs.put("effects.ethereal.iterations", "3");
defaultPrefs.put("effects.ethereal.attenuation", "50");
if (prefs == null) {
prefs = Preferences.userNodeForPackage(AudiobookRecorder.class);
}
@@ -631,6 +659,10 @@ public class Options extends JDialog {
set("process.haven.apikey", havenApiKey.getText());
set("cache.size", cacheSize.getValue());
set("effects.ethereal.offset", etherealOffset.getValue());
set("effects.ethereal.iterations", etherealIterations.getValue());
set("effects.ethereal.attenuation", etherealAttenuation.getValue());
for (int i = 0; i < 31; i++) {
set("audio.eq." + i, equaliser.getChannel(i));
}

View File

@@ -69,6 +69,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
RecordingThread recordingThread;
boolean effectEthereal = false;
static class RecordingThread implements Runnable {
boolean running = false;
@@ -594,6 +596,8 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
bytesToRead--;
}
}
data = postProcessData(data);
return data;
} catch (Exception e) {
@@ -602,6 +606,69 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
return null;
}
byte[] postProcessData(byte[] data) {
if (effectEthereal) {
AudioFormat format = getAudioFormat();
int frameSize = format.getFrameSize();
int channels = format.getChannels();
int bytesPerChannel = frameSize / channels;
int frames = data.length / frameSize;
int byteNo = 0;
double fpms = (double)format.getFrameRate() / 1000d;
double doubleOffset = fpms * (double) AudiobookRecorder.window.book.getInteger("effects.ethereal.offset");
int offset = (int)doubleOffset;
double attenuation = 1d - ((double)AudiobookRecorder.window.book.getInteger("effects.ethereal.attenuation") / 100d);
int copies = AudiobookRecorder.window.book.getInteger("effects.ethereal.iterations");
byte[] out = new byte[data.length];
for (int i = 0; i < frames; i++) {
if (channels == 1) {
int l = data[i * frameSize] >= 0 ? data[i * frameSize] : 256 + data[i * frameSize];
int h = data[(i * frameSize) + 1] >= 0 ? data[(i * frameSize) + 1] : 256 + data[(i * frameSize) + 1];
int sample = (h << 8) | l;
if ((sample & 0x8000) == 0x8000) sample |= 0xFFFF0000;
double sampleDouble = (double)sample;
int used = 0;
for (int j = 0; j < copies; j++) {
if (i + (j * offset) < frames) {
used++;
int lx = data[(i + (j * offset)) * frameSize] >= 0 ? data[(i + (j * offset)) * frameSize] : 256 + data[(i + (j * offset)) * frameSize];
int hx = data[((i + (j * offset)) * frameSize) + 1] >= 0 ? data[((i + (j * offset)) * frameSize) + 1] : 256 + data[((i + (j * offset)) * frameSize) + 1];
int futureSample = (hx << 8) | lx;
if ((futureSample & 0x8000) == 0x8000) futureSample |= 0xFFFF0000;
double futureDouble = (double)futureSample;
for (int k = 0; k < copies; k++) {
futureDouble *= attenuation;
}
sampleDouble = mix(sampleDouble, futureDouble);
}
}
sample = (int)sampleDouble;
if (sample > 32767) sample = 32767;
if (sample < -32768) sample = -32768;
out[i * frameSize] = (byte)(sample & 0xFF);
out[(i * frameSize) + 1] = (byte)((sample & 0xFF00) >> 8);
} else {
return data;
}
}
return out;
} else {
return data;
}
}
public void recognise() {
AudiobookRecorder.window.havenQueue.submit(Sentence.this);
}
@@ -834,4 +901,29 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
ex.printStackTrace();
}
}
public void setEthereal(boolean e) {
effectEthereal = e;
}
public boolean getEthereal() {
return effectEthereal;
}
public double mix(double a, double b) {
double z;
double fa, fb, fz;
fa = a + 32768d;
fb = b + 32768d;
if (fa < 32768d && fb < 32768d) {
fz = (fa * fb) / 32768d;
} else {
fz = (2d * (fa + fb)) - ((fa * fb) / 32768d) - 65536d;
}
z = fz - 32768d;
return z;
}
}