java 实时音频流java
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28122097/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Live audio stream java
提问by
I am implementing live streaming from MIC to java server at another PC. But I am only hearing a white noise.
我正在另一台 PC 上实现从 MIC 到 Java 服务器的实时流式传输。但我只听到白噪音。
I have attached both client and server program
我已经附加了客户端和服务器程序
Client:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
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;
public class Mic
{
public byte[] buffer;
private int port;
static AudioInputStream ais;
public static void main(String[] args)
{
TargetDataLine line;
DatagramPacket dgp;
AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
float rate = 44100.0f;
int channels = 2;
int sampleSize = 16;
boolean bigEndian = true;
InetAddress addr;
AudioFormat format = new AudioFormat(encoding, rate, sampleSize, channels, (sampleSize / 8) * channels, rate, bigEndian);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line matching " + info + " not supported.");
return;
}
try
{
line = (TargetDataLine) AudioSystem.getLine(info);
int buffsize = line.getBufferSize()/5;
buffsize += 512;
line.open(format);
line.start();
int numBytesRead;
byte[] data = new byte[buffsize];
addr = InetAddress.getByName("127.0.0.1");
DatagramSocket socket = new DatagramSocket();
while (true) {
// Read the next chunk of data from the TargetDataLine.
numBytesRead = line.read(data, 0, data.length);
// Save this chunk of data.
dgp = new DatagramPacket (data,data.length,addr,50005);
socket.send(dgp);
}
}catch (LineUnavailableException e) {
e.printStackTrace();
}catch (UnknownHostException e) {
// TODO: handle exception
} catch (SocketException e) {
// TODO: handle exception
} catch (IOException e2) {
// TODO: handle exception
}
}
}
and the server side is no issue. It is running perfectly with android client AudioRecord.
服务器端没有问题。它与android客户端AudioRecord完美运行。
Server:
import java.io.ByteArrayInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
public class Server {
AudioInputStream audioInputStream;
static AudioInputStream ais;
static AudioFormat format;
static boolean status = true;
static int port = 50005;
static int sampleRate = 44100;
static DataLine.Info dataLineInfo;
static SourceDataLine sourceDataLine;
public static void main(String args[]) throws Exception
{
System.out.println("Server started at port:"+port);
DatagramSocket serverSocket = new DatagramSocket(port);
/**
* Formula for lag = (byte_size/sample_rate)*2
* Byte size 9728 will produce ~ 0.45 seconds of lag. Voice slightly broken.
* Byte size 1400 will produce ~ 0.06 seconds of lag. Voice extremely broken.
* Byte size 4000 will produce ~ 0.18 seconds of lag. Voice slightly more broken then 9728.
*/
byte[] receiveData = new byte[4096];
format = new AudioFormat(sampleRate, 16, 1, true, false);
dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
sourceDataLine.open(format);
sourceDataLine.start();
//FloatControl volumeControl = (FloatControl) sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
//volumeControl.setValue(1.00f);
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
ByteArrayInputStream baiss = new ByteArrayInputStream(receivePacket.getData());
while (status == true)
{
serverSocket.receive(receivePacket);
ais = new AudioInputStream(baiss, format, receivePacket.getLength());
toSpeaker(receivePacket.getData());
}
sourceDataLine.drain();
sourceDataLine.close();
}
public static void toSpeaker(byte soundbytes[]) {
try
{
System.out.println("At the speaker");
sourceDataLine.write(soundbytes, 0, soundbytes.length);
} catch (Exception e) {
System.out.println("Not working in speakers...");
e.printStackTrace();
}
}
}
采纳答案by Michael Eric Oberlin
So, I filled the microphone with a sine wave (or something which, in some vague sense, resembles a sine wave), and your program works fine.
所以,我用正弦波(或某种模糊的意义上类似于正弦波的东西)填充麦克风,并且您的程序运行良好。
My specific changes were thus:
我的具体变化是这样的:
package audioclient;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import javax.sound.sampled.*;
public class Mic {
public byte[] buffer;
private int port;
static AudioInputStream ais;
public static void main(String[] args) {
TargetDataLine line;
DatagramPacket dgp;
AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
float rate = 44100.0f;
int channels = 2;
int sampleSize = 16;
boolean bigEndian = true;
InetAddress addr;
AudioFormat format = new AudioFormat(encoding, rate, sampleSize, channels, (sampleSize / 8) * channels, rate, bigEndian);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line matching " + info + " not supported.");
return;
}
try {
line = (TargetDataLine) AudioSystem.getLine(info);
//TOTALLY missed this.
int buffsize = line.getBufferSize() / 5;
buffsize += 512;
line.open(format);
line.start();
int numBytesRead;
byte[] data = new byte[buffsize];
/*
* MICK's injection: We have a buffsize of 512; it is best if the frequency
* evenly fits into this (avoid skips, bumps, and pops). Additionally, 44100 Hz,
* with two channels and two bytes per sample. That's four bytes; divide
* 512 by it, you have 128.
*
* 128 samples, 44100 per second; that's a minimum of 344 samples, or 172 Hz.
* Well within hearing range; slight skip from the uneven division. Maybe
* bump it up to 689 Hz.
*
* That's a sine wave of shorts, repeated twice for two channels, with a
* wavelength of 32 samples.
*
* Note: Changed my mind, ignore specific numbers above.
*
*/
{
final int λ = 16;
ByteBuffer buffer = ByteBuffer.allocate(λ * 2 * 8);
for(int j = 0; j < 2; j++) {
for(double i = 0.0; i < λ; i++) {
System.out.println(j + " " + i);
//once for each sample
buffer.putShort((short)(Math.sin(Math.PI * (λ/i)) * Short.MAX_VALUE));
buffer.putShort((short)(Math.sin(Math.PI * (λ/i)) * Short.MAX_VALUE));
}
}
data = buffer.array();
}
addr = InetAddress.getByName("127.0.0.1");
try(DatagramSocket socket = new DatagramSocket()) {
while (true) {
for(byte b : data) System.out.print(b + " ");
// Read the next chunk of data from the TargetDataLine.
// numBytesRead = line.read(data, 0, data.length);
for(int i = 0; i < 64; i++) {
byte b = data[i];
System.out.print(b + " ");
}
System.out.println();
// Save this chunk of data.
dgp = new DatagramPacket(data, data.length, addr, 50005);
for(int i = 0; i < 64; i++) {
byte b = dgp.getData()[i];
System.out.print(b + " ");
}
System.out.println();
socket.send(dgp);
}
}
} catch (LineUnavailableException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
// TODO: handle exception
} catch (SocketException e) {
// TODO: handle exception
} catch (IOException e2) {
// TODO: handle exception
}
}
}
Obviously I misinterpreted it as a 512-byte-long piece and botched the sine wave, but the thing is, it produced exactly the sound that it was meant to--a mind-numbing rattle at a specific pitch.
很明显,我将它误解为 512 字节长的片段并弄坏了正弦波,但事实是,它产生的声音正是它想要的——在特定音高下发出令人麻木的嘎嘎声。
This in mind, I don't suspect that the problem is explicitly in your code. The first thing I would check is which line your system is tapping for audio. Do you have multiple microphones hooked up? A webcam mic, maybe? You might grab a utility like PulseAudio Volume Control to check. If you haven't already checked on the functionality of your microphone, you might do that too; they do have a lifespan on them.
考虑到这一点,我不怀疑问题出在您的代码中。我要检查的第一件事是您的系统正在窃听哪条线路以获取音频。您是否连接了多个麦克风?也许是网络摄像头麦克风?您可能会使用 PulseAudio Volume Control 之类的实用程序进行检查。如果您还没有检查过麦克风的功能,您也可以这样做;它们确实有使用寿命。
It isn't uncommon at all to scramble the bits in an audio stream, nor is it difficult; but I don't see anywhere where you could be doing that.
对音频流中的比特进行加扰并不少见,也并不困难;但我看不到任何地方可以这样做。
One thought might be to modify your program to attempt to play the sound locally, before sending it over to the server. That way, you can at least determine if the problem is pre- or post-Mic.
一种想法可能是修改您的程序以尝试在本地播放声音,然后再将其发送到服务器。这样,您至少可以确定问题是在麦克风前还是麦克风后。
回答by user3674935
When client and server use data buffers of different sizes one will get truncated and may cause one or both to produce artifacts.
当客户端和服务器使用不同大小的数据缓冲区时,一个会被截断,并可能导致其中之一或两者产生工件。
Your server buffer size is set to byte[] receiveData = new byte[4096];
您的服务器缓冲区大小设置为 byte[] receiveData = new byte[4096];
Your client buffer size is for some reason dynamic, and set to byte[] data = new byte[buffsize];
由于某种原因,您的客户端缓冲区大小是动态的,并设置为 byte[] data = new byte[buffsize];
Set client buffer size to a static 4096 to match the server: byte[] data = new byte[4096];
将客户端缓冲区大小设置为静态 4096 以匹配服务器: byte[] data = new byte[4096];
Or just make sure they are both the same size...
或者只是确保它们的大小相同......
回答by Don Joe
So this is an old question but solving this helped me somewhat and I suppose what I found might help others so.. this is how I solved the issues you described:
所以这是一个老问题,但解决这个问题对我有所帮助,我想我的发现可能对其他人有帮助,所以..这就是我解决您描述的问题的方式:
On my machine, changing
在我的机器上,改变
boolean bigEndian = true;
to
到
boolean bigEndian = false;
solved the white noise issue (it was obviously a byte order issue)
解决了白噪声问题(显然是字节顺序问题)
If this is the only change you make, the resulting audio is going to have a low pitch, this is due to the fact that on the Mic side you collect 2 channels and on the Speaker side you play through one channel.
如果这是您所做的唯一更改,则生成的音频将具有低音调,这是因为在麦克风侧您收集 2 个通道,而在扬声器侧您通过一个通道播放。
To solve that simply change this line:
要解决这个问题,只需更改这一行:
format = new AudioFormat(sampleRate, 16, 1, true, false);
to
到
format = new AudioFormat(sampleRate, 16, 2, true, false);
And then the audio should be clear and understandable
然后音频应该清晰易懂
回答by Mauricio
It is important to match the audio format on both client and server, for instance change the one in Client.java to: format = new AudioFormat(sampleRate, 16, 1, true, false);
You also need to use the same buffer size on both programs.
匹配客户端和服务器上的音频格式很重要,例如将 Client.java 中的格式更改为:format = new AudioFormat(sampleRate, 16, 1, true, false);
您还需要在两个程序上使用相同的缓冲区大小。
回答by Tim
I suggest that you first write to a file the audio that is recorder on the client. This will enable you to verify if the captured audio is OK. You can convert the PCM to WAV using utilities like sox.
我建议您首先将客户端上的录音机音频写入文件。这将使您能够验证捕获的音频是否正常。您可以使用诸如 sox 之类的实用程序将 PCM 转换为 WAV。