Added LFO ring modulator
This commit is contained in:
@@ -25,7 +25,7 @@
|
|||||||
debug="true"
|
debug="true"
|
||||||
debuglevel="lines,vars,source"
|
debuglevel="lines,vars,source"
|
||||||
encoding="UTF-8"
|
encoding="UTF-8"
|
||||||
bootclasspath="${bootclass.path}"
|
bootclasspath="/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar"
|
||||||
includeAntRuntime="false"
|
includeAntRuntime="false"
|
||||||
deprecation="true"
|
deprecation="true"
|
||||||
srcdir="src"
|
srcdir="src"
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package uk.co.majenko.audiobookrecorder;
|
package uk.co.majenko.audiobookrecorder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import javax.swing.tree.*;
|
|
||||||
|
|
||||||
public class Amplifier extends DefaultMutableTreeNode implements Effect {
|
public class Amplifier implements Effect {
|
||||||
double gain;
|
double gain;
|
||||||
public Amplifier() {
|
public Amplifier() {
|
||||||
gain = 1.0d;
|
gain = 1.0d;
|
||||||
|
|||||||
@@ -525,6 +525,7 @@ public class AudiobookRecorder extends JFrame {
|
|||||||
if (selectedSentence != null) {
|
if (selectedSentence != null) {
|
||||||
int i = effectChain.getSelectedIndex();
|
int i = effectChain.getSelectedIndex();
|
||||||
KVPair<String, String> p = effectChain.getItemAt(i);
|
KVPair<String, String> p = effectChain.getItemAt(i);
|
||||||
|
if (p == null) return;
|
||||||
selectedSentence.setEffectChain(p.getKey());
|
selectedSentence.setEffectChain(p.getKey());
|
||||||
updateWaveform();
|
updateWaveform();
|
||||||
}
|
}
|
||||||
@@ -707,6 +708,7 @@ public class AudiobookRecorder extends JFrame {
|
|||||||
prefs.setProperty("chapter.close.pre-gap", Options.get("catenation.pre-chapter"));
|
prefs.setProperty("chapter.close.pre-gap", Options.get("catenation.pre-chapter"));
|
||||||
prefs.setProperty("chapter.close.post-gap", Options.get("catenation.post-chapter"));
|
prefs.setProperty("chapter.close.post-gap", Options.get("catenation.post-chapter"));
|
||||||
|
|
||||||
|
loadEffects();
|
||||||
buildBook(prefs);
|
buildBook(prefs);
|
||||||
|
|
||||||
Options.set("path.last-book", book.getName());
|
Options.set("path.last-book", book.getName());
|
||||||
@@ -1192,13 +1194,15 @@ public class AudiobookRecorder extends JFrame {
|
|||||||
JComboBox<KVPair<String, String>> defEff = new JComboBox<KVPair<String, String>>();
|
JComboBox<KVPair<String, String>> defEff = new JComboBox<KVPair<String, String>>();
|
||||||
int selEff = -1;
|
int selEff = -1;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (String k : effects.keySet()) {
|
if (effects != null) {
|
||||||
if (k.equals(defaultEffectChain)) {
|
for (String k : effects.keySet()) {
|
||||||
selEff = i;
|
if (k.equals(defaultEffectChain)) {
|
||||||
|
selEff = i;
|
||||||
|
}
|
||||||
|
KVPair<String, String> p = new KVPair<String, String>(k, effects.get(k).toString());
|
||||||
|
defEff.addItem(p);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
KVPair<String, String> p = new KVPair<String, String>(k, effects.get(k).toString());
|
|
||||||
defEff.addItem(p);
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defEff.setSelectedIndex(selEff);
|
defEff.setSelectedIndex(selEff);
|
||||||
@@ -2578,6 +2582,11 @@ System.err.println(format);
|
|||||||
if (eff != null) {
|
if (eff != null) {
|
||||||
group.addEffect(eff);
|
group.addEffect(eff);
|
||||||
}
|
}
|
||||||
|
} else if (e.getTagName().equals("lfo")) {
|
||||||
|
Effect eff = (Effect)loadLFO(e);
|
||||||
|
if (eff != null) {
|
||||||
|
group.addEffect(eff);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2653,6 +2662,11 @@ System.err.println(format);
|
|||||||
if (eff != null) {
|
if (eff != null) {
|
||||||
store.addEffect(eff);
|
store.addEffect(eff);
|
||||||
}
|
}
|
||||||
|
} else if (ie.getTagName().equals("lfo")) {
|
||||||
|
Effect eff = (Effect)loadLFO(ie);
|
||||||
|
if (eff != null) {
|
||||||
|
store.addEffect(eff);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2668,7 +2682,16 @@ System.err.println(format);
|
|||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LFO loadLFO(Element root) {
|
||||||
|
double f = Utils.s2d(root.getAttribute("frequency"));
|
||||||
|
double d = Utils.s2d(root.getAttribute("depth"));
|
||||||
|
double p = Utils.s2d(root.getAttribute("phase"));
|
||||||
|
return new LFO(f, d, p);
|
||||||
|
}
|
||||||
|
|
||||||
public void updateEffectChains() {
|
public void updateEffectChains() {
|
||||||
|
int sel = effectChain.getSelectedIndex();
|
||||||
|
KVPair<String, String> ent = effectChain.getItemAt(sel);
|
||||||
while (effectChain.getItemCount() > 0) {
|
while (effectChain.getItemCount() > 0) {
|
||||||
effectChain.removeItemAt(0);
|
effectChain.removeItemAt(0);
|
||||||
}
|
}
|
||||||
@@ -2677,6 +2700,11 @@ System.err.println(format);
|
|||||||
KVPair<String, String> p = new KVPair<String, String>(k, e.toString());
|
KVPair<String, String> p = new KVPair<String, String>(k, e.toString());
|
||||||
effectChain.addItem(p);
|
effectChain.addItem(p);
|
||||||
}
|
}
|
||||||
|
if (ent != null) {
|
||||||
|
setEffectChain(ent.getKey());
|
||||||
|
} else {
|
||||||
|
setEffectChain(defaultEffectChain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEffectChain(String key) {
|
public void setEffectChain(String key) {
|
||||||
|
|||||||
@@ -19,9 +19,8 @@ package uk.co.majenko.audiobookrecorder;
|
|||||||
//
|
//
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import javax.swing.tree.*;
|
|
||||||
|
|
||||||
public class Biquad extends DefaultMutableTreeNode implements Effect {
|
public class Biquad implements Effect {
|
||||||
public static final int Lowpass = 0;
|
public static final int Lowpass = 0;
|
||||||
public static final int Highpass = 1;
|
public static final int Highpass = 1;
|
||||||
public static final int Bandpass = 2;
|
public static final int Bandpass = 2;
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package uk.co.majenko.audiobookrecorder;
|
package uk.co.majenko.audiobookrecorder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import javax.swing.tree.*;
|
|
||||||
|
|
||||||
public class DelayLine extends DefaultMutableTreeNode implements Effect {
|
public class DelayLine implements Effect {
|
||||||
|
|
||||||
ArrayList<DelayLineStore> delayLines;
|
ArrayList<DelayLineStore> delayLines;
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ public interface Effect {
|
|||||||
public ArrayList<Effect> getChildEffects();
|
public ArrayList<Effect> getChildEffects();
|
||||||
public void dump();
|
public void dump();
|
||||||
public void init(double sr);
|
public void init(double sr);
|
||||||
|
public String toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package uk.co.majenko.audiobookrecorder;
|
package uk.co.majenko.audiobookrecorder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import javax.swing.tree.*;
|
|
||||||
|
|
||||||
public class EffectGroup extends DefaultMutableTreeNode implements Effect {
|
public class EffectGroup implements Effect {
|
||||||
String name;
|
String name;
|
||||||
ArrayList<Effect> effects;
|
ArrayList<Effect> effects;
|
||||||
|
|
||||||
|
|||||||
66
src/uk/co/majenko/audiobookrecorder/LFO.java
Normal file
66
src/uk/co/majenko/audiobookrecorder/LFO.java
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package uk.co.majenko.audiobookrecorder;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class LFO implements Effect {
|
||||||
|
|
||||||
|
double phase;
|
||||||
|
double frequency;
|
||||||
|
double depth;
|
||||||
|
double sampleRate;
|
||||||
|
double sampleStep;
|
||||||
|
|
||||||
|
public LFO(double f, double d) {
|
||||||
|
frequency = f;
|
||||||
|
depth = d;
|
||||||
|
phase = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LFO(double f, double d, double p) {
|
||||||
|
frequency = f;
|
||||||
|
depth = d;
|
||||||
|
phase = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double process(double sample) {
|
||||||
|
double v = Math.sin(phase);
|
||||||
|
phase += sampleStep;
|
||||||
|
if (phase > (Math.PI * 2d)) {
|
||||||
|
phase -= (Math.PI * 2d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Make it between 0 and 1.
|
||||||
|
// v = 1d + v;
|
||||||
|
// v /= 2d;
|
||||||
|
|
||||||
|
// Multiply it by the gain factor
|
||||||
|
v *= depth;
|
||||||
|
|
||||||
|
// Apply it to the sample
|
||||||
|
sample += (sample * v);
|
||||||
|
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() { return "Low Frequency Oscillator (" + frequency + " Hz, " + (depth * 100d) + "%)"; }
|
||||||
|
|
||||||
|
public ArrayList<Effect> getChildEffects() { return null; }
|
||||||
|
|
||||||
|
public void dump() {
|
||||||
|
System.out.println(getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(double sr) {
|
||||||
|
sampleRate = sr;
|
||||||
|
|
||||||
|
// Number of samples that make up one cycle of the LFO
|
||||||
|
double oneCycle = sampleRate / frequency;
|
||||||
|
|
||||||
|
// Amount to increase each step
|
||||||
|
sampleStep = (Math.PI * 2d) / oneCycle;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() { return getName(); }
|
||||||
|
|
||||||
|
}
|
||||||
@@ -37,6 +37,8 @@ public class Options extends JDialog {
|
|||||||
JCheckBox enableParsing;
|
JCheckBox enableParsing;
|
||||||
JSpinner cacheSize;
|
JSpinner cacheSize;
|
||||||
|
|
||||||
|
JSpinner fftThreshold;
|
||||||
|
|
||||||
JSpinner etherealIterations;
|
JSpinner etherealIterations;
|
||||||
JSpinner etherealAttenuation;
|
JSpinner etherealAttenuation;
|
||||||
JSpinner etherealOffset;
|
JSpinner etherealOffset;
|
||||||
@@ -295,6 +297,7 @@ public class Options extends JDialog {
|
|||||||
rateList = addDropdown(optionsPanel, "Sample rate:", getSampleRateList(), get("audio.recording.samplerate"));
|
rateList = addDropdown(optionsPanel, "Sample rate:", getSampleRateList(), get("audio.recording.samplerate"));
|
||||||
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"), "");
|
||||||
|
|
||||||
addSeparator(optionsPanel);
|
addSeparator(optionsPanel);
|
||||||
|
|
||||||
@@ -569,6 +572,8 @@ public class Options extends JDialog {
|
|||||||
defaultPrefs.put("catenation.post-sentence", "1000");
|
defaultPrefs.put("catenation.post-sentence", "1000");
|
||||||
defaultPrefs.put("catenation.short-sentence", "100");
|
defaultPrefs.put("catenation.short-sentence", "100");
|
||||||
defaultPrefs.put("catenation.post-paragraph", "2000");
|
defaultPrefs.put("catenation.post-paragraph", "2000");
|
||||||
|
|
||||||
|
defaultPrefs.put("audio.recording.trim.fft", "10");
|
||||||
|
|
||||||
defaultPrefs.put("path.storage", (new File(System.getProperty("user.home"), "Recordings")).toString());
|
defaultPrefs.put("path.storage", (new File(System.getProperty("user.home"), "Recordings")).toString());
|
||||||
defaultPrefs.put("path.archive", (new File(new File(System.getProperty("user.home"), "Recordings"),"archive")).toString());
|
defaultPrefs.put("path.archive", (new File(new File(System.getProperty("user.home"), "Recordings"),"archive")).toString());
|
||||||
@@ -704,6 +709,7 @@ public class Options extends JDialog {
|
|||||||
set("process.haven.apikey", havenApiKey.getText());
|
set("process.haven.apikey", havenApiKey.getText());
|
||||||
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("effects.ethereal.offset", etherealOffset.getValue());
|
set("effects.ethereal.offset", etherealOffset.getValue());
|
||||||
set("effects.ethereal.iterations", etherealIterations.getValue());
|
set("effects.ethereal.iterations", etherealIterations.getValue());
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
|||||||
tempFile = tf;
|
tempFile = tf;
|
||||||
wavFile = wf;
|
wavFile = wf;
|
||||||
format = af;
|
format = af;
|
||||||
System.err.println(format);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -271,14 +270,15 @@ System.err.println(format);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
block++;
|
block++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find first block with > 0 intensity and subtract one.
|
int limit = Options.getInteger("audio.recording.trim.fft");
|
||||||
|
|
||||||
|
// Find first block with > 1 intensity and subtract one.
|
||||||
|
|
||||||
int start = 0;
|
int start = 0;
|
||||||
for (int i = 0; i < blocks; i++) {
|
for (int i = 0; i < blocks; i++) {
|
||||||
if (intens[i] > 0) break;
|
if (intens[i] > limit) break;
|
||||||
start = i;
|
start = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,9 +291,9 @@ System.err.println(format);
|
|||||||
if (startOffset >= samples.length) startOffset = samples.length;
|
if (startOffset >= samples.length) startOffset = samples.length;
|
||||||
|
|
||||||
int end = blocks - 1;
|
int end = blocks - 1;
|
||||||
// And last block with > 0 intensity and add one.
|
// And last block with > 1 intensity and add one.
|
||||||
for (int i = blocks-1; i >= 0; i--) {
|
for (int i = blocks-1; i >= 0; i--) {
|
||||||
if (intens[i] > 0) break;
|
if (intens[i] > limit) break;
|
||||||
end = i;
|
end = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,6 +318,7 @@ System.err.println(format);
|
|||||||
double[] samples = getProcessedAudioData();
|
double[] samples = getProcessedAudioData();
|
||||||
if (samples == null) return;
|
if (samples == null) return;
|
||||||
double noiseFloor = AudiobookRecorder.window.getNoiseFloor();
|
double noiseFloor = AudiobookRecorder.window.getNoiseFloor();
|
||||||
|
noiseFloor *= 1.1;
|
||||||
|
|
||||||
// Find start
|
// Find start
|
||||||
for (int i = 0; i < samples.length; i++) {
|
for (int i = 0; i < samples.length; i++) {
|
||||||
|
|||||||
Reference in New Issue
Block a user