我一直在研究一个从usb调制解调器调用的java项目。 该应用程序在我的计算机上运行正常,但是当我尝试在较低规格的应用程序上运行时,从PC呼叫的人的音频流完全熄灭,并且在电话上完全听到了。但是应该由电脑用户听到的音频被延迟(3到5秒),伴有白噪声,并且几乎不可能进行对话。
要记住的一些事情:
关于可能出现什么问题或如何解决问题的任何线索?
在我开始新线程后处理音频的类:(进入呼叫音频)
public class SerialVoiceReader implements Runnable{
/** The running. */
private volatile boolean running = true;
/** The in. */
DataInputStream in;
/** The af. */
AudioFormat af;
/** The samples per frame. */
private int samplesPerFrame = 160;
/** The audio buffer size. */
private int audioBufferSize = samplesPerFrame * 2 ; //20ms delay
private String tel;
private String timestamp;
public SerialVoiceReader ( DataInputStream in, AudioFormat af){
this.in = in;
this.af = af;
}
public void run (){
try
{
Info infos = new Info(SourceDataLine.class, af);
SourceDataLine dataLine = (SourceDataLine) AudioSystem.getLine(infos);
dataLine.open(dataLine.getFormat(),audioBufferSize *2);
dataLine.start();
// set the volume up
if (dataLine.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
FloatControl volume = (FloatControl) dataLine.getControl(FloatControl.Type.MASTER_GAIN);
volume.setValue(volume.getMaximum());
}
// get a field from GUI to set as part of the file name
tel = CallGUI.telField.getText();
timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(Calendar.getInstance().getTime());
// save the stream to a file to later set the header and make it .wav format
FileOutputStream fos = new FileOutputStream("Llamadas/" + timestamp + "-" + tel + "-OUT.raw");
// the audio buffer writing (this is the audio that goes out on the call)
while (running){
byte[] buffer = new byte[audioBufferSize];
int offset = 0;
int numRead = 0;
while (running && (offset < buffer.length && (numRead = this.in.read(buffer, offset, buffer.length - offset)) >= 0))
{
offset += numRead;
}
if(running && offset>=0){
dataLine.write(buffer, 0, offset);
fos.write(buffer);
}
}
dataLine.stop();
dataLine.drain();
dataLine.close();
fos.close();
}
catch ( Exception e )
{
}
}
在我开始新线程后处理音频的类:(拨出呼叫音频)
public class SerialVoiceWriter implements Runnable{
/** The running. */
private volatile boolean running = true;
/** The out. */
DataOutputStream out;
/** The af. */
AudioFormat af;
/** The samples per frame. */
private int samplesPerFrame = 160;
/** The audio buffer size. */
private int audioBufferSize = samplesPerFrame * 2; //20ms delay
private String tel;
private String timestamp;
public SerialVoiceWriter ( DataOutputStream out, AudioFormat af, Boolean playMessage)
{
this.out = out;
this.af = af;
}
public void run ()
{
try
{
Info infos = new Info(TargetDataLine.class, af);
TargetDataLine dataLine = (TargetDataLine) AudioSystem.getLine(infos);
dataLine.open(dataLine.getFormat(),audioBufferSize*2 );
dataLine.start();
tel = CallGUI.telField.getText();
timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(Calendar.getInstance().getTime());
FileOutputStream fis = new FileOutputStream("Llamadas/" + timestamp + "-" + tel + "-IN.raw");
while (running){
byte[] audioBuffer = new byte[audioBufferSize];
int offset = 0;
int numRead = 0;
while (running && (offset < audioBuffer.length && (numRead = dataLine.read(audioBuffer, offset, audioBuffer.length - offset)) > 0))
{
offset += numRead;
}
if(running && offset>=0){
this.out.write(audioBuffer);
fis.write(audioBuffer);
}
}
dataLine.flush();
dataLine.stop();
dataLine.close();
fis.close();
dataLine = null;
}
catch (Exception e )
{
}
}
谢谢你的建议
答案 0 :(得分:1)
您需要采取的步骤是:
OutputStream fos = new BufferedOutputStream( new FileOutputStream("Llamadas/" + timestamp + "-" + tel + "-OUT.raw"));
你将获得极大改善的表现。现在的方式是循环的每次迭代都等待缓冲区完成写入磁盘。物理磁盘很慢,而且很多等待。这将需要更多的工作:而不是写入dataLine,然后按顺序写入fos,并行写入它们。它们每个都花费一定的时间将数据写入各自的目的地。如果fos需要X微秒而dataLine需要Y,那么当前代码需要X + Y微秒。如果你并行执行,最终只能等待max(X,Y)。 `
ExecutorService es = Executors.newFixedThreadPool(2);
Callable<Void>[] calls = new Callable[2];
//... your other code here...
if (running && offset >= 0) {
final int finalOffset = offset;
Callable<Void> call1 = new Callable<Void>()
{
@Override
public Void call() throws Exception
{
dataLine.write(buffer, 0, finalOffset);
return null;
}
};
Callable<Void> call2 = new Callable<Void>()
{
@Override
public Void call() throws Exception
{
fos.write(buffer); // or however you need to write.
return null;
}
};
calls[0] = call1;
calls[1] = call2;
List<Callable<Void>> asList = Arrays.asList(calls);
es.invokeAll(asList); // invokeAll will block until both callables have completed.
}
`
答案 1 :(得分:0)
我认为无论造成接近100%CPU的原因都是罪魁祸首。但这并没有真正告诉你任何具体的东西。首先,由于问题在于低端PC上的播放,您可能需要检查该设备上的音频驱动程序是否是最新的。之后,我会考虑优化处理音频接收的代码部分。虽然较旧的PC较低,但我认为它不应该对您要实现的内容有任何问题。我建议您在应用程序运行时运行性能分析器,以查看需要很长时间的内容。
更新:您可以尝试增加audioBufferSize以查看它是否有任何效果,20ms似乎很低。提供的代码仅适用于从PC发送的音频。从手机收到的音频怎么样?