简体   繁体   English

允许Java应用程序在录制音频时继续运行

[英]Allow Java application to continue running while recording audio

I am trying to embed some code I found (original here ) for recording and saving audio input into a Java swing application. 我正在尝试嵌入一些我发现的代码( 在此原始),用于记录音频输入并将其保存到Java swing应用程序中。 My problem is that I want the audio recording to run concurrently with other things the application is doing, but in fact, the application pauses until the audio recording is finished. 我的问题是我希望音频记录与应用程序正在做的其他事情同时运行,但是实际上,应用程序会暂停直到音频记录完成。 How can I prevent this from happening? 如何防止这种情况发生?

Here's a simplified version of my code that produces the issue. 这是产生问题的代码的简化版本。 The line that displays "Now recording" does not appear until after the audio recording is finished. 直到音频录制完成,才会显示“正在录制”行。

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();
    }
}

Paint methods are for painting! 绘画方法是绘画! Never perform any other actions within the paint method, it's simply inappropriate and a really, really bad idea. 永远不要在paint方法内执行任何其他操作,这是不适当的,而且是一个非常非常糟糕的主意。

Create a JButton (or JToggleButton ) and use this to start/stop the recording. 创建一个JButton (或JToggleButton ),并使用它来开始/停止录制。

Use line.stop() and line.close() to stop the recording. 使用line.stop()line.close()停止录制。

Swing is a single thread environment, any long running or blocking code which is executed within the context of the Event Dispatching Thread will prevent it from processing the Event Queue, making it look like your program has hung, because it has. Swing是一个单线程环境,在事件调度线程的上下文中执行的任何长时间运行或阻塞的代码都将阻止它处理事件队列,从而使其看起来像您的程序已挂起。

You could use a SwingWorker , but since you're not trying to update the UI while you're recording, it would be easier to use a Thread 您可以使用SwingWorker ,但是由于您在录制时不尝试更新UI,因此使用Thread会更容易

Take a look at 看一眼

for more details 更多细节

Updated with example 更新了示例

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;
        }
    }

}

(nb: I've not been able to test it, as I don't have a line available to support recording...apparently) (nb:我无法对其进行测试,因为我显然没有可用的线路来支持录音...)

You're calling start on the AWT Event Dispatch Thread thus blocking anything else from happening. 您正在AWT事件调度线程上调用start ,从而阻止了其他任何事情的发生。 You need to call your audio calling code on a different thread. 您需要在其他线程上调用音频呼叫代码。

You should never run long running tasks on the Swing thread unless you want to face the grey screen of death 除非要面对死亡的灰屏,否则切勿在Swing线程上运行长时间运行的任务

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM