web-dev-qa-db-ja.com

ライブオーディオストリームjava

MICから別のPCの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();
        }
    }
}
10
user4488923

そこで、マイクに正弦波(または、漠然とした意味で正弦波に似たもの)を入力すると、プログラムは正常に機能します。

したがって、私の具体的な変更は次のとおりです。

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バイトの長さのピースと誤解し、正弦波をボットしましたが、実際には、それが意図したとおりのサウンドを生成しました-特定のピッチで気が遠くなるようなガラガラ音。

これを念頭に置いて、問題がコードに明示的にあるとは思わない。私が最初にチェックするのは、システムがオーディオ用にタップしているラインです。複数のマイクを接続していますか?ウェブカメラマイク、多分?あなたはPulseAudioボリュームコントロールのようなユーティリティをつかんでチェックするかもしれません。マイクの機能をまだ確認していない場合は、確認することもできます。彼らには寿命があります。

オーディオストリームのビットをスクランブルすることはまったく珍しいことではなく、難しいことでもありません。しかし、あなたがそれを行うことができる場所はどこにも見当たりません。

サーバーに送信する前に、プログラムを変更してローカルでサウンドを再生しようとすることも考えられます。そうすれば、少なくとも問題がマイクの前か後かを判断できます。

クライアントとサーバーが異なるサイズのデータ​​バッファーを使用すると、一方が切り捨てられ、一方または両方でアーティファクトが生成される可能性があります。

サーバーのバッファサイズはbyte[] receiveData = new byte[4096];に設定されています

クライアントのバッファサイズは何らかの理由で動的であり、byte[] data = new byte[buffsize];に設定されています

サーバーに一致するように、クライアントのバッファサイズを静的4096に設定します。byte[] data = new byte[4096];

または、両方が同じサイズであることを確認してください...

3
user3674935

だからこれは古い質問ですが、これを解決することは私をいくらか助けました、そして私が見つけたものは他の人を助けるかもしれないと思います..これはあなたが説明した問題を解決した方法です:

私のマシンでは、

boolean bigEndian = true;

boolean bigEndian = false;

ホワイトノイズの問題を解決しました(明らかにバイトオーダーの問題でした)

これが唯一の変更である場合、結果のオーディオのピッチは低くなります。これは、マイク側で2つのチャンネルを収集し、スピーカー側で1つのチャンネルを再生するためです。

これを解決するには、次の行を変更するだけです。

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

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

そして、音声は明確で理解しやすいものでなければなりません

1
Don Joe

まず、クライアントのレコーダーであるオーディオをファイルに書き込むことをお勧めします。これにより、キャプチャされたオーディオに問題がないかどうかを確認できます。 soxなどのユーティリティを使用してPCMをWAVに変換できます。

0
Tim

クライアントとサーバーの両方でオーディオ形式を一致させることが重要です。たとえば、Client.Javaのオーディオ形式を次のように変更します。format = new AudioFormat(sampleRate, 16, 1, true, false);両方のプログラムで同じバッファサイズを使用する必要もあります。

0
Mauricio