允许 Java 应用程序在录制音频时继续 运行
Allow Java application to continue running while recording audio
我正在尝试嵌入一些我找到的代码(原始 here),用于将音频输入录制和保存到 Java swing 应用程序中。我的问题是我希望录音 运行 与应用程序正在做的其他事情同时进行,但实际上,应用程序会暂停,直到录音完成。我怎样才能防止这种情况发生?
这是产生问题的我的代码的简化版本。显示"Now recording"的行直到录音完成后才出现。
import javax.sound.sampled.*;
import java.io.*;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.File;
import javafx.embed.swing.JFXPanel;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Example extends JPanel {
protected static final long serialVersionUID = 1L;
// whether we've started recording
private boolean startedRecording;
// record duration, in milliseconds
static final long RECORD_TIME = 5000; // 5 seconds
// format of audio file
AudioFileFormat.Type fileType = AudioFileFormat.Type.WAVE;
// path of the wav file
File wavFile;
// the line from which audio data is captured
TargetDataLine line;
public Example( String output_fn ) {
wavFile = new File(output_fn);
startedRecording = false;
setPreferredSize(new Dimension(100,100));
createPanel();
}
public static JFXPanel createPanel() {
return new JFXPanel();
}
/**
* Defines an audio format
*/
AudioFormat getAudioFormat() {
float sampleRate = 16000;
int sampleSizeInBits = 8;
int channels = 2;
boolean signed = true;
boolean bigEndian = true;
AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits,
channels, signed, bigEndian);
return format;
}
/**
* Captures the sound and record into a WAV file
*/
public void start() {
try {
AudioFormat format = getAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
// checks if system supports the data line
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line not supported");
System.exit(0);
}
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.start(); // start capturing
System.out.println("In utils.Recorder: Start capturing...");
AudioInputStream ais = new AudioInputStream(line);
System.out.println("In utils.Recorder: Start recording...");
// start recording
AudioSystem.write(ais, fileType, wavFile);
} catch (LineUnavailableException ex) {
ex.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
repaint();
}
/**
* Closes the target data line to finish capturing and recording
*/
public void finish() {
line.stop();
line.close();
System.out.println("In utils.Recorder: Finished");
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
// start recording
if ( !startedRecording ) {
startedRecording = true;
Thread stopper = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(RECORD_TIME);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
Example.this.finish();
}
});
stopper.start();
this.start();
}
// display message
g2.drawString("Now recording", 50, 50);
}
public static void main(String[] args) {
final Example eg = new Example("TestRecordAudio.wav");
JFrame f = new JFrame();
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.add(eg, BorderLayout.CENTER);
f.pack();
f.setVisible(true);
f.repaint();
}
}
您正在 AWT Event Dispatch Thread 上调用 start
从而阻止任何其他事情发生。您需要在不同的线程上调用您的音频调用代码。
你永远不应该 运行 长时间 运行 Swing 线程上的任务,除非你想面对死亡的灰色屏幕
画法是用来画画的!永远不要在 paint 方法中执行任何其他操作,这根本不合适,而且是一个非常非常糟糕的主意。
创建 JButton
(或 JToggleButton
)并将其用于 start/stop 录音。
使用line.stop()
和line.close()
停止录制。
Swing 是单线程环境,在事件调度线程上下文中执行的任何长 运行 或阻塞代码将阻止它处理事件队列,使它看起来像您的程序已挂起, 因为它有。
您可以使用 SwingWorker
,但由于您在录制时不尝试更新 UI,因此使用 Thread
[= 会更容易20=]
看看
- Concurrency in Swing
- How to Use Buttons, Check Boxes, and Radio Buttons
- How to Write an Action Listeners
了解更多详情
更新示例
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestRecord {
public static void main(String[] args) {
new TestRecord();
}
public TestRecord() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
private JToggleButton recordButton;
protected static final AudioFileFormat.Type FILE_TYPE = AudioFileFormat.Type.WAVE;
private TargetDataLine line;
public TestPane() {
setLayout(new GridBagLayout());
recordButton = new JToggleButton("Record");
recordButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (recordButton.isSelected()) {
startRecording();
recordButton.setText("Stop");
} else {
stopRecording();
recordButton.setText("Record");
}
}
});
add(recordButton);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void stopRecording() {
if (line != null) {
line.stop();
line.close();
line = null;
}
}
protected void startRecording() {
if (line == null) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
AudioFormat format = getAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
// checks if system supports the data line
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line not supported");
System.exit(0);
}
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.start(); // start capturing
System.out.println("In utils.Recorder: Start capturing...");
AudioInputStream ais = new AudioInputStream(line);
System.out.println("In utils.Recorder: Start recording...");
// start recording
System.out.println("Is recoding");
AudioSystem.write(ais, FILE_TYPE, new File("Test.wav"));
} catch (LineUnavailableException ex) {
ex.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
System.out.println("Recording is done");
}
});
t.start();
}
}
protected AudioFormat getAudioFormat() {
float sampleRate = 16000;
int sampleSizeInBits = 8;
int channels = 2;
boolean signed = true;
boolean bigEndian = true;
AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits,
channels, signed, bigEndian);
return format;
}
}
}
(注意:我无法测试它,因为我没有可用的线路来支持录音......显然)
我正在尝试嵌入一些我找到的代码(原始 here),用于将音频输入录制和保存到 Java swing 应用程序中。我的问题是我希望录音 运行 与应用程序正在做的其他事情同时进行,但实际上,应用程序会暂停,直到录音完成。我怎样才能防止这种情况发生?
这是产生问题的我的代码的简化版本。显示"Now recording"的行直到录音完成后才出现。
import javax.sound.sampled.*;
import java.io.*;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.File;
import javafx.embed.swing.JFXPanel;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Example extends JPanel {
protected static final long serialVersionUID = 1L;
// whether we've started recording
private boolean startedRecording;
// record duration, in milliseconds
static final long RECORD_TIME = 5000; // 5 seconds
// format of audio file
AudioFileFormat.Type fileType = AudioFileFormat.Type.WAVE;
// path of the wav file
File wavFile;
// the line from which audio data is captured
TargetDataLine line;
public Example( String output_fn ) {
wavFile = new File(output_fn);
startedRecording = false;
setPreferredSize(new Dimension(100,100));
createPanel();
}
public static JFXPanel createPanel() {
return new JFXPanel();
}
/**
* Defines an audio format
*/
AudioFormat getAudioFormat() {
float sampleRate = 16000;
int sampleSizeInBits = 8;
int channels = 2;
boolean signed = true;
boolean bigEndian = true;
AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits,
channels, signed, bigEndian);
return format;
}
/**
* Captures the sound and record into a WAV file
*/
public void start() {
try {
AudioFormat format = getAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
// checks if system supports the data line
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line not supported");
System.exit(0);
}
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.start(); // start capturing
System.out.println("In utils.Recorder: Start capturing...");
AudioInputStream ais = new AudioInputStream(line);
System.out.println("In utils.Recorder: Start recording...");
// start recording
AudioSystem.write(ais, fileType, wavFile);
} catch (LineUnavailableException ex) {
ex.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
repaint();
}
/**
* Closes the target data line to finish capturing and recording
*/
public void finish() {
line.stop();
line.close();
System.out.println("In utils.Recorder: Finished");
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
// start recording
if ( !startedRecording ) {
startedRecording = true;
Thread stopper = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(RECORD_TIME);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
Example.this.finish();
}
});
stopper.start();
this.start();
}
// display message
g2.drawString("Now recording", 50, 50);
}
public static void main(String[] args) {
final Example eg = new Example("TestRecordAudio.wav");
JFrame f = new JFrame();
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.add(eg, BorderLayout.CENTER);
f.pack();
f.setVisible(true);
f.repaint();
}
}
您正在 AWT Event Dispatch Thread 上调用 start
从而阻止任何其他事情发生。您需要在不同的线程上调用您的音频调用代码。
你永远不应该 运行 长时间 运行 Swing 线程上的任务,除非你想面对死亡的灰色屏幕
画法是用来画画的!永远不要在 paint 方法中执行任何其他操作,这根本不合适,而且是一个非常非常糟糕的主意。
创建 JButton
(或 JToggleButton
)并将其用于 start/stop 录音。
使用line.stop()
和line.close()
停止录制。
Swing 是单线程环境,在事件调度线程上下文中执行的任何长 运行 或阻塞代码将阻止它处理事件队列,使它看起来像您的程序已挂起, 因为它有。
您可以使用 SwingWorker
,但由于您在录制时不尝试更新 UI,因此使用 Thread
[= 会更容易20=]
看看
- Concurrency in Swing
- How to Use Buttons, Check Boxes, and Radio Buttons
- How to Write an Action Listeners
了解更多详情
更新示例
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestRecord {
public static void main(String[] args) {
new TestRecord();
}
public TestRecord() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
private JToggleButton recordButton;
protected static final AudioFileFormat.Type FILE_TYPE = AudioFileFormat.Type.WAVE;
private TargetDataLine line;
public TestPane() {
setLayout(new GridBagLayout());
recordButton = new JToggleButton("Record");
recordButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (recordButton.isSelected()) {
startRecording();
recordButton.setText("Stop");
} else {
stopRecording();
recordButton.setText("Record");
}
}
});
add(recordButton);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void stopRecording() {
if (line != null) {
line.stop();
line.close();
line = null;
}
}
protected void startRecording() {
if (line == null) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
AudioFormat format = getAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
// checks if system supports the data line
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line not supported");
System.exit(0);
}
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.start(); // start capturing
System.out.println("In utils.Recorder: Start capturing...");
AudioInputStream ais = new AudioInputStream(line);
System.out.println("In utils.Recorder: Start recording...");
// start recording
System.out.println("Is recoding");
AudioSystem.write(ais, FILE_TYPE, new File("Test.wav"));
} catch (LineUnavailableException ex) {
ex.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
System.out.println("Recording is done");
}
});
t.start();
}
}
protected AudioFormat getAudioFormat() {
float sampleRate = 16000;
int sampleSizeInBits = 8;
int channels = 2;
boolean signed = true;
boolean bigEndian = true;
AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits,
channels, signed, bigEndian);
return format;
}
}
}
(注意:我无法测试它,因为我没有可用的线路来支持录音......显然)