【问题标题】:Live audio stream java实时音频流 java
【发布时间】:2015-03-23 05:05:03
【问题描述】:

我正在另一台 PC 上实现从 MIC 到 java 服务器的实时流式传输。但我只听到白噪音。

我已经附加了客户端和服务器程序

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

服务器端没有问题。它与 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();
        }
    }
}

【问题讨论】:

    标签: java sockets audio-streaming audio-recording


    【解决方案1】:

    所以,我用正弦波(或者在某种模糊的意义上类似于正弦波的东西)填充麦克风,你的程序运行良好。

    我的具体变化如下:

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

    显然我把它误解为一个 512 字节长的片段,并且搞砸了正弦波,但问题是,它发出的声音正是它本来的意思——在特定音高下发出令人麻木的嘎嘎声。

    p>

    记住这一点,我不怀疑问题出在您的代码中。我要检查的第一件事是您的系统正在利用哪条线路获取音频。您是否连接了多个麦克风?一个网络摄像头麦克风,也许?您可以使用 PulseAudio Volume Control 之类的实用程序进行检查。如果您还没有检查过麦克风的功能,您也可以这样做;它们确实有生命周期。

    对音频流中的比特进行加扰并不罕见,也并不困难;但我没有看到任何地方可以这样做。

    一种想法可能是修改您的程序以尝试在本地播放声音,然后再将其发送到服务器。这样,您至少可以确定问题出在麦克风前还是麦克风后。

    【讨论】:

    • 用 TCP 代替 UDP 怎么样?
    • 嗯,已经做了,但是有很多缺点。 UDP 被设计为 TCP 的轻量级版本,如果您正在处理实时媒体并且必须跟上,这非常棒。但是,它不会预测哪些数据包将首先到达,它们何时到达,或者它们是否会到达那里。并且没有接收确认。这些都是对 TCP 的严重限制,因此当程序可以猜测或插入数据时,UDP 通常更容易。 TCP 通常速度较慢但更可靠。
    • 确切地说,当字节不确定时。对音质做对比比较好,当局域网/互联网拥塞时可能不是好主意。
    【解决方案2】:

    当客户端和服务器使用不同大小的数据缓冲区时,其中一个会被截断,并可能导致其中一个或两个产生伪影。

    您的服务器缓冲区大小设置为byte[] receiveData = new byte[4096];

    由于某种原因,您的客户端缓冲区大小是动态的,并设置为 byte[] data = new byte[buffsize];

    将客户端缓冲区大小设置为静态 4096 以匹配服务器:byte[] data = new byte[4096];

    或者只是确保它们的大小相同......

    【讨论】:

      【解决方案3】:

      所以这是一个老问题,但解决这个问题对我有所帮助,我想我发现的内容可能对其他人有所帮助......这就是我解决你描述的问题的方法:

      在我的机器上,正在改变

      boolean bigEndian = true;
      

      boolean bigEndian = false;
      

      解决了白噪声问题(显然是字节顺序问题)

      如果这是您所做的唯一更改,则生成的音频将具有低音调,这是因为在麦克风一侧您收集 2 个通道,而在扬声器一侧您通过一个通道播放。

      要解决这个问题,只需更改此行:

      format = new AudioFormat(sampleRate, 16, 1, true, false);
      

      format = new AudioFormat(sampleRate, 16, 2, true, false);
      

      然后音频应该清晰易懂

      【讨论】:

        【解决方案4】:

        我建议您首先将客户端上的录音机的音频写入文件。这将使您能够验证捕获的音频是否正常。您可以使用 sox 等实用程序将 PCM 转换为 WAV。

        【讨论】:

          【解决方案5】:

          在客户端和服务器上匹配音频格式很重要,例如将 Client.java 中的音频格式更改为:format = new AudioFormat(sampleRate, 16, 1, true, false); 您还需要在两个程序上使用相同的缓冲区大小。

          【讨论】:

            猜你喜欢
            • 2016-10-13
            • 1970-01-01
            • 2021-09-20
            • 2012-08-05
            • 2013-06-12
            • 2011-05-30
            • 2012-04-03
            • 2012-07-20
            • 1970-01-01
            相关资源
            最近更新 更多