Complete overhaul of recording to allow better recording from microphones that require a warmup and self-calibration period
This commit is contained in:
BIN
resources/uk/co/majenko/audiobookrecorder/icons/mic.png
Normal file
BIN
resources/uk/co/majenko/audiobookrecorder/icons/mic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 588 B |
@@ -85,7 +85,10 @@ public class AudiobookRecorder extends JFrame {
|
|||||||
|
|
||||||
Random rng = new Random();
|
Random rng = new Random();
|
||||||
|
|
||||||
SourceDataLine play;
|
SourceDataLine play = null;
|
||||||
|
|
||||||
|
public TargetDataLine microphone = null;
|
||||||
|
public AudioInputStream microphoneStream = null;
|
||||||
|
|
||||||
public Configuration sphinxConfig;
|
public Configuration sphinxConfig;
|
||||||
public StreamSpeechRecognizer recognizer;
|
public StreamSpeechRecognizer recognizer;
|
||||||
@@ -227,6 +230,9 @@ public class AudiobookRecorder extends JFrame {
|
|||||||
|
|
||||||
|
|
||||||
Options.loadPreferences();
|
Options.loadPreferences();
|
||||||
|
|
||||||
|
execScript(Options.get("scripts.startup"));
|
||||||
|
|
||||||
CacheManager.setCacheSize(Options.getInteger("cache.size"));
|
CacheManager.setCacheSize(Options.getInteger("cache.size"));
|
||||||
|
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
@@ -951,6 +957,11 @@ public class AudiobookRecorder extends JFrame {
|
|||||||
if (recording != null) return;
|
if (recording != null) return;
|
||||||
if (book == null) return;
|
if (book == null) return;
|
||||||
|
|
||||||
|
if (microphone == null) {
|
||||||
|
JOptionPane.showMessageDialog(this, "Microphone not started. Start microphone first.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
toolBar.disableBook();
|
toolBar.disableBook();
|
||||||
toolBar.disableSentence();
|
toolBar.disableSentence();
|
||||||
|
|
||||||
@@ -979,6 +990,11 @@ public class AudiobookRecorder extends JFrame {
|
|||||||
if (recording != null) return;
|
if (recording != null) return;
|
||||||
if (book == null) return;
|
if (book == null) return;
|
||||||
|
|
||||||
|
if (microphone == null) {
|
||||||
|
JOptionPane.showMessageDialog(this, "Microphone not started. Start microphone first.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
toolBar.disableBook();
|
toolBar.disableBook();
|
||||||
toolBar.disableSentence();
|
toolBar.disableSentence();
|
||||||
|
|
||||||
@@ -1026,6 +1042,11 @@ public class AudiobookRecorder extends JFrame {
|
|||||||
if (recording != null) return;
|
if (recording != null) return;
|
||||||
if (book == null) return;
|
if (book == null) return;
|
||||||
|
|
||||||
|
if (microphone == null) {
|
||||||
|
JOptionPane.showMessageDialog(this, "Microphone not started. Start microphone first.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
toolBar.disableBook();
|
toolBar.disableBook();
|
||||||
toolBar.disableSentence();
|
toolBar.disableSentence();
|
||||||
|
|
||||||
@@ -1656,4 +1677,68 @@ public class AudiobookRecorder extends JFrame {
|
|||||||
}
|
}
|
||||||
equaliserWindow.setVisible(true);
|
equaliserWindow.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean enableMicrophone() {
|
||||||
|
AudioFormat format = new AudioFormat(
|
||||||
|
Options.getInteger("audio.recording.samplerate"),
|
||||||
|
16,
|
||||||
|
Options.getInteger("audio.recording.channels"),
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
Mixer.Info mixer = Options.getRecordingMixer();
|
||||||
|
|
||||||
|
microphone = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
microphone = AudioSystem.getTargetDataLine(format, mixer);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
microphone = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (microphone == null) {
|
||||||
|
JOptionPane.showMessageDialog(AudiobookRecorder.window, "Sample format not supported", "Error", JOptionPane.ERROR_MESSAGE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
microphoneStream = new AudioInputStream(microphone);
|
||||||
|
|
||||||
|
try {
|
||||||
|
microphone.open();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
microphone = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
microphone.start();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disableMicrophone() {
|
||||||
|
try {
|
||||||
|
microphoneStream.close();
|
||||||
|
microphone.stop();
|
||||||
|
microphone.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execScript(String s) {
|
||||||
|
if (s == null) return;
|
||||||
|
String[] lines = s.split("\n");
|
||||||
|
|
||||||
|
for (String line : lines) {
|
||||||
|
try {
|
||||||
|
Process p = Runtime.getRuntime().exec(line);
|
||||||
|
p.waitFor();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,8 +48,21 @@ public class Equaliser extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
JButton def = new JButton("Set as default");
|
||||||
|
def.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
for (int i = 0; i < 31; i++) {
|
||||||
|
Options.set("audio.eq." + i, channels[i].getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
add(smooth, BorderLayout.SOUTH);
|
JPanel buttons = new JPanel();
|
||||||
|
buttons.setLayout(new FlowLayout());
|
||||||
|
buttons.add(smooth);
|
||||||
|
buttons.add(def);
|
||||||
|
|
||||||
|
add(buttons, BorderLayout.SOUTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getChannel(int c) {
|
public float getChannel(int c) {
|
||||||
|
|||||||
@@ -27,5 +27,5 @@ public class Icons {
|
|||||||
static public final ImageIcon spinner2 = new ImageIcon(Icons.class.getResource("icons/spinner2.png"));
|
static public final ImageIcon spinner2 = new ImageIcon(Icons.class.getResource("icons/spinner2.png"));
|
||||||
static public final ImageIcon spinner3 = new ImageIcon(Icons.class.getResource("icons/spinner3.png"));
|
static public final ImageIcon spinner3 = new ImageIcon(Icons.class.getResource("icons/spinner3.png"));
|
||||||
static public final ImageIcon eq = new ImageIcon(Icons.class.getResource("icons/eq.png"));
|
static public final ImageIcon eq = new ImageIcon(Icons.class.getResource("icons/eq.png"));
|
||||||
|
static public final ImageIcon mic = new ImageIcon(Icons.class.getResource("icons/mic.png"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public class MainToolBar extends JToolBar {
|
|||||||
JButton playonSentence;
|
JButton playonSentence;
|
||||||
JButton stopPlaying;
|
JButton stopPlaying;
|
||||||
JButton eq;
|
JButton eq;
|
||||||
|
JToggleButton mic;
|
||||||
|
|
||||||
AudiobookRecorder root;
|
AudiobookRecorder root;
|
||||||
|
|
||||||
@@ -111,7 +112,24 @@ public class MainToolBar extends JToolBar {
|
|||||||
|
|
||||||
add(eq);
|
add(eq);
|
||||||
|
|
||||||
|
addSeparator();
|
||||||
|
|
||||||
|
mic = new JToggleButton(Icons.mic);
|
||||||
|
mic.setToolTipText("Enable/disable microphone");
|
||||||
|
mic.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
JToggleButton b = (JToggleButton)e.getSource();
|
||||||
|
if (b.isSelected()) {
|
||||||
|
if (!root.enableMicrophone()) {
|
||||||
|
b.setSelected(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.disableMicrophone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add(mic);
|
||||||
|
|
||||||
|
|
||||||
setFloatable(false);
|
setFloatable(false);
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ public class Options extends JDialog {
|
|||||||
|
|
||||||
Equaliser equaliser;
|
Equaliser equaliser;
|
||||||
|
|
||||||
|
JTextArea startupScript;
|
||||||
|
|
||||||
static HashMap<String, String> defaultPrefs;
|
static HashMap<String, String> defaultPrefs;
|
||||||
static Preferences prefs = null;
|
static Preferences prefs = null;
|
||||||
|
|
||||||
@@ -282,10 +284,19 @@ public class Options extends JDialog {
|
|||||||
equaliser.setChannel(i, Options.getFloat("audio.eq." + i));
|
equaliser.setChannel(i, Options.getFloat("audio.eq." + i));
|
||||||
}
|
}
|
||||||
|
|
||||||
tabs.add("EQ", equaliser);
|
tabs.add("Default EQ", equaliser);
|
||||||
|
|
||||||
|
JPanel startScript = new JPanel();
|
||||||
|
startScript.setLayout(new BorderLayout());
|
||||||
|
startupScript = new JTextArea(get("scripts.startup"));
|
||||||
|
startScript.add(startupScript, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
tabs.add("Startup Script", startScript);
|
||||||
|
|
||||||
add(tabs, BorderLayout.CENTER);
|
add(tabs, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setTitle("Options");
|
setTitle("Options");
|
||||||
|
|
||||||
setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
|
setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
|
||||||
@@ -482,6 +493,8 @@ public class Options extends JDialog {
|
|||||||
defaultPrefs.put("audio.eq.29", "-11.00");
|
defaultPrefs.put("audio.eq.29", "-11.00");
|
||||||
defaultPrefs.put("audio.eq.30", "-12.00");
|
defaultPrefs.put("audio.eq.30", "-12.00");
|
||||||
|
|
||||||
|
defaultPrefs.put("scripts.startup", "");
|
||||||
|
|
||||||
if (prefs == null) {
|
if (prefs == null) {
|
||||||
prefs = Preferences.userNodeForPackage(AudiobookRecorder.class);
|
prefs = Preferences.userNodeForPackage(AudiobookRecorder.class);
|
||||||
}
|
}
|
||||||
@@ -587,6 +600,8 @@ public class Options extends JDialog {
|
|||||||
set("audio.eq." + i, equaliser.getChannel(i));
|
set("audio.eq." + i, equaliser.getChannel(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set("scripts.startup", startupScript.getText());
|
||||||
|
|
||||||
savePreferences();
|
savePreferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,10 +36,75 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
|||||||
TargetDataLine line;
|
TargetDataLine line;
|
||||||
AudioInputStream inputStream;
|
AudioInputStream inputStream;
|
||||||
|
|
||||||
Thread recordingThread = null;
|
|
||||||
|
|
||||||
int[] storedAudioData = null;
|
int[] storedAudioData = null;
|
||||||
|
|
||||||
|
RecordingThread recordingThread;
|
||||||
|
|
||||||
|
static class RecordingThread implements Runnable {
|
||||||
|
|
||||||
|
boolean running = false;
|
||||||
|
boolean recording = false;
|
||||||
|
|
||||||
|
File tempFile;
|
||||||
|
File wavFile;
|
||||||
|
|
||||||
|
AudioFormat format;
|
||||||
|
|
||||||
|
public RecordingThread(File tf, File wf, AudioFormat af) {
|
||||||
|
tempFile = tf;
|
||||||
|
wavFile = wf;
|
||||||
|
format = af;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
running = true;
|
||||||
|
recording = true;
|
||||||
|
byte[] buf = new byte[AudiobookRecorder.window.microphone.getBufferSize()];
|
||||||
|
FileOutputStream fos = new FileOutputStream(tempFile);
|
||||||
|
int len = 0;
|
||||||
|
AudiobookRecorder.window.microphone.flush();
|
||||||
|
int nr = 0;
|
||||||
|
while (recording) {
|
||||||
|
nr = AudiobookRecorder.window.microphoneStream.read(buf, 0, buf.length);
|
||||||
|
len += nr;
|
||||||
|
fos.write(buf, 0, nr);
|
||||||
|
}
|
||||||
|
nr = AudiobookRecorder.window.microphoneStream.read(buf, 0, buf.length);
|
||||||
|
len += nr;
|
||||||
|
fos.write(buf, 0, nr);
|
||||||
|
fos.close();
|
||||||
|
|
||||||
|
FileInputStream fis = new FileInputStream(tempFile);
|
||||||
|
AudioInputStream ais = new AudioInputStream(fis, format, len / format.getFrameSize());
|
||||||
|
fos = new FileOutputStream(wavFile);
|
||||||
|
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, fos);
|
||||||
|
fos.close();
|
||||||
|
ais.close();
|
||||||
|
fis.close();
|
||||||
|
|
||||||
|
tempFile.delete();
|
||||||
|
|
||||||
|
recording = false;
|
||||||
|
running = false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
running = false;
|
||||||
|
recording = false;
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRunning() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopRecording() {
|
||||||
|
recording = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public Sentence() {
|
public Sentence() {
|
||||||
super("");
|
super("");
|
||||||
id = UUID.randomUUID().toString();
|
id = UUID.randomUUID().toString();
|
||||||
@@ -57,72 +122,30 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean startRecording() {
|
public boolean startRecording() {
|
||||||
AudioFormat format = new AudioFormat(
|
if (AudiobookRecorder.window.microphone == null) {
|
||||||
Options.getInteger("audio.recording.samplerate"),
|
JOptionPane.showMessageDialog(AudiobookRecorder.window, "Microphone not started. Start the microphone first.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||||
16,
|
|
||||||
Options.getInteger("audio.recording.channels"),
|
|
||||||
true,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
Mixer.Info mixer = Options.getRecordingMixer();
|
|
||||||
|
|
||||||
line = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
line = AudioSystem.getTargetDataLine(format, mixer);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line == null) {
|
|
||||||
JOptionPane.showMessageDialog(AudiobookRecorder.window, "Sample format not supported", "Error", JOptionPane.ERROR_MESSAGE);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inputStream = new AudioInputStream(line);
|
|
||||||
|
|
||||||
try {
|
recordingThread = new RecordingThread(getTempFile(), getFile(), Options.getAudioFormat());
|
||||||
line.open();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
line.start();
|
Thread rc = new Thread(recordingThread);
|
||||||
|
rc.setDaemon(true);
|
||||||
|
rc.start();
|
||||||
|
|
||||||
File audioFile = getFile();
|
|
||||||
|
|
||||||
recordingThread = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
AudioSystem.write(inputStream, AudioFileFormat.Type.WAVE, audioFile);
|
|
||||||
} catch (Exception e) {
|
|
||||||
inputStream = null;
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
recordingThread.setDaemon(true);
|
|
||||||
|
|
||||||
recordingThread.start();
|
|
||||||
|
|
||||||
recording = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopRecording() {
|
public void stopRecording() {
|
||||||
try {
|
recordingThread.stopRecording();
|
||||||
inputStream.close();
|
while (recordingThread.isRunning()) {
|
||||||
inputStream = null;
|
try {
|
||||||
line.stop();
|
Thread.sleep(10);
|
||||||
line.close();
|
} catch (Exception e) {
|
||||||
line = null;
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
recording = false;
|
|
||||||
storedAudioData = null;
|
storedAudioData = null;
|
||||||
|
|
||||||
if (!id.equals("room-noise")) {
|
if (!id.equals("room-noise")) {
|
||||||
@@ -277,6 +300,14 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable {
|
|||||||
return new File(b, id + ".wav");
|
return new File(b, id + ".wav");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public File getTempFile() {
|
||||||
|
File b = new File(AudiobookRecorder.window.getBookFolder(), "files");
|
||||||
|
if (!b.exists()) {
|
||||||
|
b.mkdirs();
|
||||||
|
}
|
||||||
|
return new File(b, id + ".wax");
|
||||||
|
}
|
||||||
|
|
||||||
public void editText() {
|
public void editText() {
|
||||||
String t = JOptionPane.showInputDialog(null, "Edit Text", text);
|
String t = JOptionPane.showInputDialog(null, "Edit Text", text);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user