Added LFO ring modulator
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
debug="true"
|
||||
debuglevel="lines,vars,source"
|
||||
encoding="UTF-8"
|
||||
bootclasspath="${bootclass.path}"
|
||||
bootclasspath="/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar"
|
||||
includeAntRuntime="false"
|
||||
deprecation="true"
|
||||
srcdir="src"
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package uk.co.majenko.audiobookrecorder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import javax.swing.tree.*;
|
||||
|
||||
public class Amplifier extends DefaultMutableTreeNode implements Effect {
|
||||
public class Amplifier implements Effect {
|
||||
double gain;
|
||||
public Amplifier() {
|
||||
gain = 1.0d;
|
||||
|
||||
@@ -525,6 +525,7 @@ public class AudiobookRecorder extends JFrame {
|
||||
if (selectedSentence != null) {
|
||||
int i = effectChain.getSelectedIndex();
|
||||
KVPair<String, String> p = effectChain.getItemAt(i);
|
||||
if (p == null) return;
|
||||
selectedSentence.setEffectChain(p.getKey());
|
||||
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.post-gap", Options.get("catenation.post-chapter"));
|
||||
|
||||
loadEffects();
|
||||
buildBook(prefs);
|
||||
|
||||
Options.set("path.last-book", book.getName());
|
||||
@@ -1192,6 +1194,7 @@ public class AudiobookRecorder extends JFrame {
|
||||
JComboBox<KVPair<String, String>> defEff = new JComboBox<KVPair<String, String>>();
|
||||
int selEff = -1;
|
||||
int i = 0;
|
||||
if (effects != null) {
|
||||
for (String k : effects.keySet()) {
|
||||
if (k.equals(defaultEffectChain)) {
|
||||
selEff = i;
|
||||
@@ -1200,6 +1203,7 @@ public class AudiobookRecorder extends JFrame {
|
||||
defEff.addItem(p);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
defEff.setSelectedIndex(selEff);
|
||||
|
||||
@@ -2578,6 +2582,11 @@ System.err.println(format);
|
||||
if (eff != null) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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() {
|
||||
int sel = effectChain.getSelectedIndex();
|
||||
KVPair<String, String> ent = effectChain.getItemAt(sel);
|
||||
while (effectChain.getItemCount() > 0) {
|
||||
effectChain.removeItemAt(0);
|
||||
}
|
||||
@@ -2677,6 +2700,11 @@ System.err.println(format);
|
||||
KVPair<String, String> p = new KVPair<String, String>(k, e.toString());
|
||||
effectChain.addItem(p);
|
||||
}
|
||||
if (ent != null) {
|
||||
setEffectChain(ent.getKey());
|
||||
} else {
|
||||
setEffectChain(defaultEffectChain);
|
||||
}
|
||||
}
|
||||
|
||||
public void setEffectChain(String key) {
|
||||
|
||||
@@ -19,9 +19,8 @@ package uk.co.majenko.audiobookrecorder;
|
||||
//
|
||||
|
||||
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 Highpass = 1;
|
||||
public static final int Bandpass = 2;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package uk.co.majenko.audiobookrecorder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import javax.swing.tree.*;
|
||||
|
||||
public class DelayLine extends DefaultMutableTreeNode implements Effect {
|
||||
public class DelayLine implements Effect {
|
||||
|
||||
ArrayList<DelayLineStore> delayLines;
|
||||
|
||||
|
||||
@@ -8,4 +8,5 @@ public interface Effect {
|
||||
public ArrayList<Effect> getChildEffects();
|
||||
public void dump();
|
||||
public void init(double sr);
|
||||
public String toString();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package uk.co.majenko.audiobookrecorder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import javax.swing.tree.*;
|
||||
|
||||
public class EffectGroup extends DefaultMutableTreeNode implements Effect {
|
||||
public class EffectGroup implements Effect {
|
||||
String name;
|
||||
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;
|
||||
JSpinner cacheSize;
|
||||
|
||||
JSpinner fftThreshold;
|
||||
|
||||
JSpinner etherealIterations;
|
||||
JSpinner etherealAttenuation;
|
||||
JSpinner etherealOffset;
|
||||
@@ -295,6 +297,7 @@ public class Options extends JDialog {
|
||||
rateList = addDropdown(optionsPanel, "Sample rate:", getSampleRateList(), get("audio.recording.samplerate"));
|
||||
bitDepth = addDropdown(optionsPanel, "Sample resolution:", getResolutionList(), get("audio.recording.resolution"));
|
||||
trimMethod = addDropdown(optionsPanel, "Auto-trim method:", getTrimMethods(), get("audio.recording.trim"));
|
||||
fftThreshold = addSpinner(optionsPanel, "FFT threshold:", 0, 100, 1, getInteger("audio.recording.trim.fft"), "");
|
||||
|
||||
addSeparator(optionsPanel);
|
||||
|
||||
@@ -570,6 +573,8 @@ public class Options extends JDialog {
|
||||
defaultPrefs.put("catenation.short-sentence", "100");
|
||||
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.archive", (new File(new File(System.getProperty("user.home"), "Recordings"),"archive")).toString());
|
||||
defaultPrefs.put("path.ffmpeg", "");
|
||||
@@ -704,6 +709,7 @@ public class Options extends JDialog {
|
||||
set("process.haven.apikey", havenApiKey.getText());
|
||||
set("editor.external", externalEditor.getText());
|
||||
set("cache.size", cacheSize.getValue());
|
||||
set("audio.recording.trim.fft", fftThreshold.getValue());
|
||||
|
||||
set("effects.ethereal.offset", etherealOffset.getValue());
|
||||
set("effects.ethereal.iterations", etherealIterations.getValue());
|
||||
|
||||
@@ -87,7 +87,6 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
||||
tempFile = tf;
|
||||
wavFile = wf;
|
||||
format = af;
|
||||
System.err.println(format);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
@@ -271,14 +270,15 @@ System.err.println(format);
|
||||
}
|
||||
}
|
||||
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;
|
||||
for (int i = 0; i < blocks; i++) {
|
||||
if (intens[i] > 0) break;
|
||||
if (intens[i] > limit) break;
|
||||
start = i;
|
||||
}
|
||||
|
||||
@@ -291,9 +291,9 @@ System.err.println(format);
|
||||
if (startOffset >= samples.length) startOffset = samples.length;
|
||||
|
||||
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--) {
|
||||
if (intens[i] > 0) break;
|
||||
if (intens[i] > limit) break;
|
||||
end = i;
|
||||
}
|
||||
|
||||
@@ -318,6 +318,7 @@ System.err.println(format);
|
||||
double[] samples = getProcessedAudioData();
|
||||
if (samples == null) return;
|
||||
double noiseFloor = AudiobookRecorder.window.getNoiseFloor();
|
||||
noiseFloor *= 1.1;
|
||||
|
||||
// Find start
|
||||
for (int i = 0; i < samples.length; i++) {
|
||||
|
||||
Reference in New Issue
Block a user