来自套接字的“无尽”AudioInputStream
Posted
技术标签:
【中文标题】来自套接字的“无尽”AudioInputStream【英文标题】:"Endless" AudioInputStream from socket 【发布时间】:2016-02-18 01:40:44 【问题描述】:我在从 Socket 创建 AudioInputStream 时遇到问题。 以下是重要部分:
public class SoundStream extends Thread
private int port;
private String IP;
private Socket socket;
private SoundObject soundObject;
private OpenAL openAL;
private Source source;
private boolean run = true;
public SoundStream(int port, String IP, SoundObject soundObject)
this.soundObject = soundObject;
this.port = port;
this.IP = IP;
public void run()
try
this.socket = new Socket(this.IP, this.port);
this.openAL = new OpenAL();
catch (Exception e)
e.printStackTrace();
this.mainCycleMethod();
private void mainCycleMethod()
while (run)
this.soundObject.blockAndWait();
switch (this.soundObject.getAndResetEvent())
case 0:
this.run = false;
this.close();
break;
case 1:
this.setPitch();
break;
case 2:
this.closeSource();
this.play();
break;
case 3:
this.pause(true);
break;
case 4:
this.pause(false);
break;
private BufferedInputStream getInputStream() throws Exception
return new BufferedInputStream(socket.getInputStream());
private void setPitch()
if(this.source != null)
try
this.source.setPitch(this.soundObject.getPitch());
catch (ALException e)
e.printStackTrace();
private void play()
try
AudioInputStream audioInputStream = new AudioInputStream(this.getInputStream(), this.soundObject.getAudioFormat(), Audiosystem.NOT_SPECIFIED);
// AudioInputStream audioInputStream_tmp = AudioSystem.getAudioInputStream(this.getInputStream());
// AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(this.soundObject.getAudioFormat(), audioInputStream_tmp);
this.source = openAL.createSource(audioInputStream);
this.source.setGain(1f);
this.source.play();
catch (Exception ex)
ex.printStackTrace();
private void close()
this.closeSource();
this.openAL.close();
try
this.socket.close();
catch (IOException e)
e.printStackTrace();
private void closeSource()
if(this.source!=null)
this.source.close();
private void pause(boolean pause)
if(this.source != null)
try
if (pause)
this.source.pause();
else
this.source.play();
catch (ALException ex)
ex.printStackTrace();
public class SoundObject extends AbstractEventObject
public AudioFormat getAudioFormat()
boolean signed = false;
//true,false
boolean bigEndian = false;
//true,false
return new AudioFormat(this.frequency, this.bits, this.channels, signed, bigEndian);
.
.
.
.
此代码在这一行抛出 UnsupportedAudioFileException:
AudioInputStream audioInputStream_tmp = AudioSystem.getAudioInputStream(this.getInputStream());
但是,当我使用此代码时:
AudioInputStream audioInputStream = new AudioInputStream(this.getInputStream(), this.soundObject.getAudioFormat(), 100000);
它播放声音,但仅在将这 100000 个样本帧加载到音频输入流之后。播放完所有 100000 帧后,它就完成了。
如果我可以在第一次 AudioInputStream 初始化期间直接将 AudioFormat 作为参数传递,我想我会解决这个问题,但这似乎是不可能的。 我正在从服务器接收音频格式规范。
我认为一种可能的解决方案是创建一个数据线,我可以将其作为参数传递给 AudioInputStream 构造函数。但是我不确定如何将数据从套接字直接获取到数据线。我知道一个使用无限循环的解决方案,它读取数据并将它们写入数据线。但是好像很浪费。有没有更直接的方法?
我希望可以使用java-openAL库来解决,因为我需要改变速度,我希望我不必自己做。
谢谢
【问题讨论】:
作为第一步,您可以尝试使用AudioInputStream audioInputStream = new AudioInputStream(this.getInputStream(), this.soundObject.getAudioFormat(), AudioSystem.NOT_SPECIFIED);
看看会发生什么。
它不能解决我的问题,因为它被 openAL.createSource(audioInputStream) 方法阻止了。它可能会等待整个 InputStream 完成。谢谢
什么是openAL
?你能展示完整的源代码吗(可能是minimal reproducible example)?
我修改了我的帖子,现在有完整的代码。 openAL 只是 OpenAL 类的初始化。 @罗马
在哪里可以找到OpenAL
类的源代码?
【参考方案1】:
我终于解决了这个问题。事实证明,java-openAL 内置了流式支持,但它不在 GitHub 上的文档中,所以我一开始没有注意到。 Source 类中有一个createOutputStream 方法,它返回OutputStream。您可以将字节直接写入 OutputStream。
这是我的代码:
在这个 sn-p 中我初始化 OpenAL:
public void run()
try
this.socket = new Socket(this.IP, this.port);
this.openAL = new OpenAL();
catch (Exception ex)
Log.severe(ex.toString());
this.mainCycleMethod();
这是我在 InputStream 可用时调用的播放方法:
private void play()
try
this.source = openAL.createSource();
this.outputWriter = new OutputWriter(this.socket.getInputStream(), this.source, this.soundObject.getAudioFormat());
this.source.setGain(1f);
this.outputWriter.start();
catch (Exception ex)
Log.severe(ex.toString());
您必须使用不带参数的 createSource 方法,它返回 Source 的新实例。不要在源上调用 play 方法,它由 SourceOutputStream 类处理,该实例由 createOutputStream 方法返回。手动调用 play 方法没有任何问题,但是当缓冲区为空时,我这样做的体验很糟糕。基本上,当您开始将数据流式传输到 OpenAL 时,它不会在稍后开始播放。
这是我的 OutputWriter 代码,它负责将字节从 InputStream 传递到 OutputStream:
package cz.speechtech.sound;
import org.urish.openal.ALException;
import org.urish.openal.Source;
import javax.sound.sampled.AudioFormat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Created by honza on 16.12.15.
*/
public class OutputWriter extends Thread
private InputStream inputStream;
private OutputStream outputStream;
private int STREAMING_BUFFER_SIZE = 24000;
private int NUMBER_OF_BUFFERS = 4;
private boolean run = true;
public OutputWriter(InputStream inputStream, Source source, AudioFormat audioFormat)
this.inputStream = inputStream;
try
this.outputStream = source.createOutputStream(audioFormat, this.NUMBER_OF_BUFFERS, 1024);
catch (ALException e)
e.printStackTrace();
public void run()
byte[] buffer = new byte[this.STREAMING_BUFFER_SIZE];
int i;
try
Thread.sleep(1000); // Might cause problems
while (this.run)
i = this.inputStream.read(buffer);
if (i == -1) break;
outputStream.write(buffer, 0, i);
catch (IOException e)
e.printStackTrace();
catch (InterruptedException e)
e.printStackTrace();
public synchronized void stopRunning()
this.run = false;
try
this.outputStream.close();
catch (IOException e)
e.printStackTrace();
祝你有美好的一天。
【讨论】:
以上是关于来自套接字的“无尽”AudioInputStream的主要内容,如果未能解决你的问题,请参考以下文章