我正在用Java编写音频聊天应用程序,并且我遇到了一些延迟/延迟,如果应用程序暂停运行一段时间,这些延迟/延迟通常会显示出来。
我在下面的示例应用程序中重新创建了该问题。它只是将声音从麦克风循环到扬声器。最初它的行为与预期一致。当您按下按钮并对着麦克风说话时,您会在扬声器中听到一声微小的延迟。但是,如果程序保持运行一段时间(一周),那么该延迟将增加到几秒钟。
我测试了不同的耳机。我使用Java 8,9,10进行了测试,它始终显示相同的行为。我还尝试了drain()和flush()等等,但唯一摆脱延迟的是关闭并重新创建TargetDataLine。但是重新创建该行不是我的应用程序的选项,因为它需要很长时间,并且在重新创建该行时音频不可用。
这是一个已知的限制还是我做错了什么?非常感谢任何见解或想法!
import javax.sound.sampled.*;
import javax.swing.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
/**
* Created by asa on 2018-04-03.
*/
public class Main {
public static void main(String[] args) throws LineUnavailableException {
String title = "";
if (args.length > 0) {
title = args[0];
}
String mixerName = "USB”; // Part of the name of my headset
if (args.length > 1) {
mixerName = args[1];
}
AudioFormat format = new AudioFormat(8000f,
16,
1,
true,
false);
DataLine.Info targetInfo = new TargetDataLine.Info(TargetDataLine.class, format);
TargetDataLine mic = null;
for (Mixer.Info info : AudioSystem.getMixerInfo()) {
if (info.getName().contains(mixerName) && !info.getName().contains("Port")) {
Mixer m = AudioSystem.getMixer(info);
if (m.isLineSupported(targetInfo)) {
mic = (TargetDataLine) m.getLine(targetInfo);
break;
}
}
}
mic.open(format, 1280);
mic.start();
DataLine.Info sourceInfo = new DataLine.Info(SourceDataLine.class, format);
SourceDataLine speaker = null;
for (Mixer.Info info : AudioSystem.getMixerInfo()) {
if (info.getName().contains(mixerName) && !info.getName().contains("Port")) {
Mixer m = AudioSystem.getMixer(info);
if (m.isLineSupported(sourceInfo)) {
speaker = (SourceDataLine) m.getLine(sourceInfo);
break;
}
}
}
speaker.open(format, 8000);
speaker.start();
MicRunnable micRunnable = new MicRunnable(mic, speaker);
new Thread(micRunnable).start();
Frame.show(title, new Frame.PttListener() {
@Override
public void press() {
micRunnable.start();
}
@Override
public void release() {
micRunnable.stop();
}
});
}
private static class MicRunnable implements Runnable {
private final TargetDataLine _mic;
private final SourceDataLine _speaker;
private final Object runLock = new Object();
private volatile boolean running = false;
public MicRunnable(TargetDataLine mic, SourceDataLine speaker) {
_mic = mic;
_speaker = speaker;
}
public void start() {
synchronized (runLock) {
running = true;
runLock.notify();
}
}
public void stop() {
synchronized (runLock) {
running = false;
}
}
@Override
public void run() {
while (true) {
byte[] bytes = new byte[640];
_mic.read(bytes, 0, bytes.length);
if (running) {//tPeakGain(bytes) > 300) {
_speaker.write(bytes, 0, bytes.length);
}
}
}
}
private static class Frame extends JFrame {
interface PttListener {
void press();
void release();
}
private Frame(String title, PttListener listener) {
setTitle(title);
JPanel content = new JPanel();
JButton pttButton = new JButton("PTT");
pttButton.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
listener.press();
}
@Override
public void mouseReleased(MouseEvent e) {
listener.release();
}
});
content.add(pttButton);
setContentPane(content);
setSize(300, 100);
}
public static void show(String title, Frame.PttListener pttListener) {
new Frame(title, pttListener).setVisible(true);
}
}
}