diff --git a/resources/uk/co/majenko/audiobookrecorder/icons/mic.png b/resources/uk/co/majenko/audiobookrecorder/icons/mic.png new file mode 100644 index 0000000..86586ba Binary files /dev/null and b/resources/uk/co/majenko/audiobookrecorder/icons/mic.png differ diff --git a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java index 11a8d5b..e5dec77 100644 --- a/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java +++ b/src/uk/co/majenko/audiobookrecorder/AudiobookRecorder.java @@ -85,7 +85,10 @@ public class AudiobookRecorder extends JFrame { Random rng = new Random(); - SourceDataLine play; + SourceDataLine play = null; + + public TargetDataLine microphone = null; + public AudioInputStream microphoneStream = null; public Configuration sphinxConfig; public StreamSpeechRecognizer recognizer; @@ -227,6 +230,9 @@ public class AudiobookRecorder extends JFrame { Options.loadPreferences(); + + execScript(Options.get("scripts.startup")); + CacheManager.setCacheSize(Options.getInteger("cache.size")); setLayout(new BorderLayout()); @@ -951,6 +957,11 @@ public class AudiobookRecorder extends JFrame { if (recording != 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.disableSentence(); @@ -979,6 +990,11 @@ public class AudiobookRecorder extends JFrame { if (recording != 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.disableSentence(); @@ -1026,6 +1042,11 @@ public class AudiobookRecorder extends JFrame { if (recording != 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.disableSentence(); @@ -1656,4 +1677,68 @@ public class AudiobookRecorder extends JFrame { } 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(); + } + } + } } diff --git a/src/uk/co/majenko/audiobookrecorder/Equaliser.java b/src/uk/co/majenko/audiobookrecorder/Equaliser.java index 84ddc0b..599222c 100644 --- a/src/uk/co/majenko/audiobookrecorder/Equaliser.java +++ b/src/uk/co/majenko/audiobookrecorder/Equaliser.java @@ -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) { diff --git a/src/uk/co/majenko/audiobookrecorder/Icons.java b/src/uk/co/majenko/audiobookrecorder/Icons.java index 0255b0e..d7b11a0 100644 --- a/src/uk/co/majenko/audiobookrecorder/Icons.java +++ b/src/uk/co/majenko/audiobookrecorder/Icons.java @@ -27,5 +27,5 @@ public class Icons { 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 eq = new ImageIcon(Icons.class.getResource("icons/eq.png")); - + static public final ImageIcon mic = new ImageIcon(Icons.class.getResource("icons/mic.png")); } diff --git a/src/uk/co/majenko/audiobookrecorder/MainToolBar.java b/src/uk/co/majenko/audiobookrecorder/MainToolBar.java index 3ffd9f6..5e6280f 100644 --- a/src/uk/co/majenko/audiobookrecorder/MainToolBar.java +++ b/src/uk/co/majenko/audiobookrecorder/MainToolBar.java @@ -16,6 +16,7 @@ public class MainToolBar extends JToolBar { JButton playonSentence; JButton stopPlaying; JButton eq; + JToggleButton mic; AudiobookRecorder root; @@ -111,7 +112,24 @@ public class MainToolBar extends JToolBar { 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); diff --git a/src/uk/co/majenko/audiobookrecorder/Options.java b/src/uk/co/majenko/audiobookrecorder/Options.java index 6f6194f..787d8d0 100644 --- a/src/uk/co/majenko/audiobookrecorder/Options.java +++ b/src/uk/co/majenko/audiobookrecorder/Options.java @@ -32,6 +32,8 @@ public class Options extends JDialog { Equaliser equaliser; + JTextArea startupScript; + static HashMap defaultPrefs; static Preferences prefs = null; @@ -282,10 +284,19 @@ public class Options extends JDialog { 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); + + setTitle("Options"); 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.30", "-12.00"); + defaultPrefs.put("scripts.startup", ""); + if (prefs == null) { prefs = Preferences.userNodeForPackage(AudiobookRecorder.class); } @@ -587,6 +600,8 @@ public class Options extends JDialog { set("audio.eq." + i, equaliser.getChannel(i)); } + set("scripts.startup", startupScript.getText()); + savePreferences(); } diff --git a/src/uk/co/majenko/audiobookrecorder/Sentence.java b/src/uk/co/majenko/audiobookrecorder/Sentence.java index b836a17..0e9f0cf 100644 --- a/src/uk/co/majenko/audiobookrecorder/Sentence.java +++ b/src/uk/co/majenko/audiobookrecorder/Sentence.java @@ -36,10 +36,75 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { TargetDataLine line; AudioInputStream inputStream; - Thread recordingThread = 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() { super(""); id = UUID.randomUUID().toString(); @@ -57,72 +122,30 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { } public boolean startRecording() { - AudioFormat format = new AudioFormat( - Options.getInteger("audio.recording.samplerate"), - 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); + if (AudiobookRecorder.window.microphone == null) { + JOptionPane.showMessageDialog(AudiobookRecorder.window, "Microphone not started. Start the microphone first.", "Error", JOptionPane.ERROR_MESSAGE); return false; } - inputStream = new AudioInputStream(line); - try { - line.open(); - } catch (Exception e) { - e.printStackTrace(); - return false; - } + recordingThread = new RecordingThread(getTempFile(), getFile(), Options.getAudioFormat()); - 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; } public void stopRecording() { - try { - inputStream.close(); - inputStream = null; - line.stop(); - line.close(); - line = null; - } catch (Exception e) { - e.printStackTrace(); + recordingThread.stopRecording(); + while (recordingThread.isRunning()) { + try { + Thread.sleep(10); + } catch (Exception e) { + } } - recording = false; + storedAudioData = null; if (!id.equals("room-noise")) { @@ -277,6 +300,14 @@ public class Sentence extends DefaultMutableTreeNode implements Cacheable { 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() { String t = JOptionPane.showInputDialog(null, "Edit Text", text);