【问题标题】:Java Record Mic to Byte Array and Play soundJava 将麦克风录制到字节数组并播放声音
【发布时间】:2014-11-06 00:53:48
【问题描述】:

我想用 Java 制作一个实时语音聊天程序,但我对用 Java 录制/播放声音一无所知,所以在谷歌的帮助下,我想我已经能够从我的麦克风录制到一个字节数组以下:

AudioFormat format = new AudioFormat(8000.0f, 16, 1, true, true);
TargetDataLine microphone;
try{
    microphone = AudioSystem.getTargetDataLine(format);

    DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
    microphone = (TargetDataLine)AudioSystem.getLine(info);
    microphone.open(format);

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    int numBytesRead;
    byte[] data = new byte[microphone.getBufferSize()/5];
    microphone.start();

    int bytesRead =0;

    try{
        while(bytesRead<100000){ //Just so I can test if recording my mic works...
            numBytesRead = microphone.read(data, 0, data.length);
            bytesRead = bytesRead + numBytesRead;
        //    System.out.println(bytesRead);
            out.write(data, 0, numBytesRead);
        }
    catch(Exception e){
        e.printStackTrace();
    }
    microphone.close();

catch(LineUnavailibleException e){
    e.printStackTrace();
}

所以现在,据我了解,如果我调用 out.toByteArray();,我应该得到一个我刚刚从麦克风录制的声音的字节数组。 (我运行上面没有错误,但无法证明它是否真的记录了,因为我不想将它输出到文件中并且没有这样做)

现在,如果以上是正确的,那么下面就是我遇到问题的地方: 我现在想播放我刚刚创建的字节数组......(在我的真实程序中,我会通过我已经能够做到的 Java 套接字将字节发送到我的“接收程序”,但现在我只是想制作一个小程序来记录麦克风并播放它)。为了播放字节数组中的声音信息,我遵循了这个:http://www.wikijava.org/wiki/Play_a_wave_sound_in_Java 并想出了以下内容:(位于上面的mic.close()之后)

try{
    DataLine.Info info2 = DataLine.Info(SourceDataLine.class, format);
    SourceDataLine dataLine = (SourceDataLine)AudioSystem.getLine(info2);
    int bufferSize = 2200;
    soundLine.open(format, bufferSize);
    soundLine.start();
    AudioInputStream audioInputStream = null;

    InputStream input = new ByteArrayInputStream(out.toByteArray());
    audioInputStream = AudioSystem.getAudioInputStream(input);

    ...

其余部分几乎是从这个链接的 playSound.java 复制粘贴的: http://www.wikijava.org/wiki/Play_a_wave_sound_in_Java

当我运行上面的代码时......录制似乎工作正常,但我收到以下错误:

javax.sound.sampled.UnsupportedAudioFileException: could not get audio input stream from input stream

对于这一行audioInputStream = AudioSystem.getAudioInputStream(input);

根据我有限的知识,我假设这是因为我弄乱了录制方法,我需要某种“音频格式标题?” https://ccrma.stanford.edu/courses/422/projects/WaveFormat/(我以为我不需要这样的东西,因为我从未保存到文件中,只是将其全部保存为字节数组),或者我完全误解了 java 的 AudioInputStream 如何读取和解析数据......

这是我第一次在 Java 中使用任何与声音相关的东西,所以如果我完全误解并扼杀了这段代码,我深表歉意(是的,我知道代码看起来很糟糕而且杂乱无章,但我只是想让它工作) ...我在 Google/StackOverflow 上尝试了多次搜索,并且能够找到一个非常相似的问题:

java byte array play sound

但它也没有得到答复(唯一的答案是将其保存到文件中,但我们都希望将其直接作为字节数组流式传输,而不会成为文件)

我所知道的:

可以使用 TargetDataLine 录制音频,并录制麦克风,可以使用 ByteArrayOutputStream 将其输出到 Byte Array

音频可以保存到文件中并通过使用 AudioInputStream 读取文件和 SourceDataLine 播放数据来播放。

如果我想写一个文件,我可以使用 AudioSystem.write(new AudioInputStream(microphone), AudioFileFormat.Type.WAVE, new File("recording.wav"); //我已经通过替换 while 进行了测试用这条线循环,它记录得很好(除了它永远不会停止,所以我不得不手动终止它),但我不希望这样,因为输出到文件意味着不可能通过套接字将它发送到另一端实时。

我不知道的/我的问题:

如何使用 Java 录制从麦克风录制的音频并将其流式传输到另一台计算机,该计算机可以以尽可能少的延迟播放(非常像类似于 Skype 的语音聊天)。

在此先感谢您提供的任何帮助或可以为我指明正确方向的人。另外,如果有人知道更简单的方法,请也告诉我。

【问题讨论】:

  • 我确实看到了那个问题,但是因为他们的代码非常小,我不知道他们用什么技术来展示。例如,我不知道线路或缓冲区用于/它们是什么。你能解释一下哪一行是/如何启动,以及 ByteBuffer 是做什么的吗?
  • 我可以稍后给出更详细的答案,但现在查看关于音频 I/O 的 java 教程(有多个页面)docs.oracle.com/javase/tutorial/sound/accessing.html
  • 我已经看过教程但我只是第一次浏览它,我一定会尽快详细检查它,但我也很想得到你的答案。谢谢。

标签: java sockets audio microphone


【解决方案1】:

编辑: 这是与以下相同想法的稍微好一点的版本,可以在您录制时直接播放

AudioFormat format = new AudioFormat(8000.0f, 16, 1, true, true);
    TargetDataLine microphone;
    SourceDataLine speakers;
    try {
        microphone = AudioSystem.getTargetDataLine(format);

        DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
        microphone = (TargetDataLine) AudioSystem.getLine(info);
        microphone.open(format);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int numBytesRead;
        int CHUNK_SIZE = 1024;
        byte[] data = new byte[microphone.getBufferSize() / 5];
        microphone.start();

        int bytesRead = 0;
        DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
        speakers = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
        speakers.open(format);
        speakers.start();
        while (bytesRead < 100000) {
            numBytesRead = microphone.read(data, 0, CHUNK_SIZE);
            bytesRead += numBytesRead;
            // write the mic data to a stream for use later
            out.write(data, 0, numBytesRead); 
            // write mic data to stream for immediate playback
            speakers.write(data, 0, numBytesRead);
        }
        speakers.drain();
        speakers.close();
        microphone.close();
    } catch (LineUnavailableException e) {
        e.printStackTrace();
    } 

请耐心等待,因为这真的很粗糙,但它会通过扬声器播放录制的音频;

为了让它听起来更好,您需要添加线程,并优化输入/输出流。

http://www.developer.com/java/other/article.php/1579071/Java-Sound-Getting-Started-Part-2-Capture-Using-Specified-Mixer.htm

package audio;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

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.SourceDataLine;
import javax.sound.sampled.TargetDataLine;

public class AudioTest {

    public static void main(String[] args) {

        AudioFormat format = new AudioFormat(8000.0f, 16, 1, true, true);
        TargetDataLine microphone;
        AudioInputStream audioInputStream;
        SourceDataLine sourceDataLine;
        try {
            microphone = AudioSystem.getTargetDataLine(format);

            DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
            microphone = (TargetDataLine) AudioSystem.getLine(info);
            microphone.open(format);

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int numBytesRead;
            int CHUNK_SIZE = 1024;
            byte[] data = new byte[microphone.getBufferSize() / 5];
            microphone.start();

            int bytesRead = 0;

            try {
                while (bytesRead < 100000) { // Just so I can test if recording
                                                // my mic works...
                    numBytesRead = microphone.read(data, 0, CHUNK_SIZE);
                    bytesRead = bytesRead + numBytesRead;
                    System.out.println(bytesRead);
                    out.write(data, 0, numBytesRead);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            byte audioData[] = out.toByteArray();
            // Get an input stream on the byte array
            // containing the data
            InputStream byteArrayInputStream = new ByteArrayInputStream(
                    audioData);
            audioInputStream = new AudioInputStream(byteArrayInputStream,format, audioData.length / format.getFrameSize());
            DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
            sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
            sourceDataLine.open(format);
            sourceDataLine.start();
            int cnt = 0;
            byte tempBuffer[] = new byte[10000];
            try {
                while ((cnt = audioInputStream.read(tempBuffer, 0,tempBuffer.length)) != -1) {
                    if (cnt > 0) {
                        // Write data to the internal buffer of
                        // the data line where it will be
                        // delivered to the speaker.
                        sourceDataLine.write(tempBuffer, 0, cnt);
                    }// end if
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            // Block and wait for internal buffer of the
            // data line to empty.
            sourceDataLine.drain();
            sourceDataLine.close();
            microphone.close();
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }
}

【讨论】:

  • 请注意,在全部录制完成之前,这不会播放任何音频
  • 您也可以在读取循环期间直接写入 sourceDataLine 的输入流
  • 感谢它完美运行(两个示例),但我有最后一个问题,我说话和播放之间有大约 0.5-1 秒的延迟(即时播放示例,更接近到我想做的事情)...我知道必须有,但是有没有办法将延迟降到最低?
  • 总会有延迟,但您可以尝试使块大小更小,以便在输出之前从麦克风读取的数据更少。输出缓冲区中必须有东西才能写入
  • 我认为如果你循环浏览 Mixer Info,你可以找到所有不同的可用目标源;在这里查看答案:stackoverflow.com/questions/12863081/…
猜你喜欢
  • 1970-01-01
  • 2013-02-02
  • 1970-01-01
  • 1970-01-01
  • 2014-11-23
  • 1970-01-01
  • 1970-01-01
  • 2011-08-09
  • 1970-01-01
相关资源
最近更新 更多