【问题标题】:JAVA audio data streaming 0sJAVA音频数据流0s
【发布时间】:2013-10-26 18:18:17
【问题描述】:

我一直在编写一个简短的程序来将音频从线路输入/麦克风重定向到扬声器。我只是在学习其中的大部分内容,但我的工作已经产生了似乎几乎是我想要的工作模型。但是,当我打印 TargetDataLine 缓冲区时,它会打印全 0,就好像它已连接并流式传输一样,但听不到我的输入。在这一点上,我已经研究了大部分sampled 包的内容以及在论坛、教程和其他人的代码中在线提供的内容,并且由于令人失望地缺乏已发布的音频代码,我认为我的学习资源几乎没有用完。因此,如果有人有任何建议或资源,将不胜感激。我认为您不需要任何其他代码,但如果需要,请询问。此代码在我的机器上使用 Eclipse version: 4.3.0.v20130605 编译时没有错误或警告。

这是一个类的方法索引,可以为您节省大部分 200 行代码。

class Stream extends Thread {
    vars
    ...
    Stream()
    setProcessingBuffer()
    setRenderingBuffer()
    bytesToString()
    play()
    pause()
    run() (Override)
}

代码:

package moshi;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;

/**
 * @author KN
 * @version 1.0 Build 1 October 26, 2013
 * 
 *          This concurrent process sets up and controls the streaming of audio
 *          data via input and output buffers.
 * 
 * @see {@link Thread}, {@link AudioSystem}, {@link TargetDataLine},
 *      {@link SourceDataLine}
 */

public class Stream extends Thread {

    /** The {@link AudioFormat} used in encoding/decoding streaming audio data */
    public final static AudioFormat audioFormat = new AudioFormat(48000, 16, 2, true, true);

    /**
     * {@link String} describing the name of the audio device to be used.
     * <p>
     * Example: "Line In", "Microphone"
     */
    private static String INPUT = "Mic";
    /**
     * {@link String} describing the name of the audio device to be used.
     * <p>
     * Example: "Speakers", "Line Out"
     */
    private static String OUTPUT = "Speakers";

    /**
     * {@link #PROCESSING_BUFFER} is a buffer used for receiving audio data
     * 
     * @see TargetDataLine
     */
    private static TargetDataLine PROCESSING_BUFFER;
    /**
     * {@link #RENDERING_BUFFER} is a buffer used for writing audio data
     * 
     * @see SourceDataLine
     */
    private static SourceDataLine RENDERING_BUFFER;
    /** {@link Integer} specifying the buffer sizes in bytes */
    private static int BUFFER_SIZE = 2048;
    /** {@link Byte[]} for holding raw audio data */
    private static byte[] READ_BUFFER = new byte[Stream.BUFFER_SIZE];

    /**
     * Initiates the audio hardware read/write buffers into
     * {@link TargetDataLine}s and {@link SourceDataLine}s respectively.
     * 
     * @see {@link TargetDataLine}, {@link SourceDataLine}
     */
    public Stream() {
        setProcessingBuffer();
        setRenderingBuffer();
    }

    /**
     * Queries input Lines and stores the {@link TargetDataLine} at
     * {@link #PROCESSING_BUFFER}
     * 
     * @see {@link AudioSystem}, {@link Line}, {@link TargetDataLine},
     *      {@link Mixer}
     */
    private void setProcessingBuffer() {
        final Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
        for (final Mixer.Info info : mixerInfos) {
            final Mixer mixer = AudioSystem.getMixer(info);
            final Line.Info[] targetLineInfos = mixer.getTargetLineInfo();
            for (final Line.Info targetLineInfo : targetLineInfos) {
                if (targetLineInfo.getLineClass() == javax.sound.sampled.TargetDataLine.class
                    && info.getName().startsWith(Stream.INPUT)) {
                    try {
                        Stream.PROCESSING_BUFFER = (TargetDataLine) mixer.getLine(targetLineInfo);
                        System.out.println(targetLineInfo.getLineClass() + ": " + info.getName() + " ["
                            + Stream.PROCESSING_BUFFER + "] ");
                    } catch (LineUnavailableException e) {
                        e.printStackTrace();
                    }
                } else {
                }
            }
        }
    }

    /**
     * Queries output Lines and stores the {@link SourceDataLine} at
     * {@link #RENDERING_BUFFER}
     * 
     * @see {@link AudioSystem}, {@link Line}, {@link SourceDataLine},
     *      {@link Mixer}
     */
    private void setRenderingBuffer() {
        final Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
        for (Mixer.Info info : mixerInfos) {
            final Mixer mixer = AudioSystem.getMixer(info);
            final Line.Info[] sourceLineInfos = mixer.getSourceLineInfo();
            for (final Line.Info sourceLineInfo : sourceLineInfos) {
                if (sourceLineInfo.getLineClass() == javax.sound.sampled.SourceDataLine.class
                    && info.getName().startsWith(Stream.OUTPUT)) {
                    try {
                        Stream.RENDERING_BUFFER = (SourceDataLine) mixer.getLine(sourceLineInfo);
                        System.out.println(sourceLineInfo.getLineClass() + ": " + info.getName() + " ["
                            + Stream.RENDERING_BUFFER + "]");
                    } catch (LineUnavailableException e) {
                        e.printStackTrace();
                    }
                } else {
                }
            }
        }
    }

    /**
     * Takes in an array of bytes and returns a String object representation of
     * the data
     * 
     * @param array
     *            The byte array to be converted
     * @return The string object representation of a byte array
     */
    private static String bytesToString(byte[] array) {
        String toString = "";
        for (byte currentByte : array) {
            toString += currentByte;
        }
        return toString;
    }

    /**
     * Opens buffers {@link #PROCESSING_BUFFER} and {@link #RENDERING_BUFFER}
     * for reading/writing
     */
    public static void play() {
        try {
            if (!Stream.PROCESSING_BUFFER.isOpen()) {
                Stream.PROCESSING_BUFFER.open(Stream.audioFormat, Stream.BUFFER_SIZE);
            }
            if (!Stream.RENDERING_BUFFER.isOpen()) {
                Stream.RENDERING_BUFFER.open(Stream.audioFormat, Stream.BUFFER_SIZE);
                Stream.RENDERING_BUFFER.start();
            }

            while (Stream.RENDERING_BUFFER.isOpen()) {
                Stream.PROCESSING_BUFFER.read(Stream.READ_BUFFER, 0, Stream.BUFFER_SIZE);
                System.out.println(Stream.bytesToString(Stream.READ_BUFFER));
                Stream.RENDERING_BUFFER.write(Stream.READ_BUFFER, 0, Stream.BUFFER_SIZE);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Stops buffers {@link #PROCESSING_BUFFER} and {@link #RENDERING_BUFFER}
     * from reading/writing
     */
    public static void pause() {
        if (Stream.PROCESSING_BUFFER.isOpen()) {
            Stream.PROCESSING_BUFFER.close();
        }
        if (Stream.RENDERING_BUFFER.isOpen()) {
            Stream.RENDERING_BUFFER.stop();
            Stream.RENDERING_BUFFER.close();
        }
    }

    /** {@inheritDoc} */
    @Override
    public void run() {
    }
}

输出:

interface javax.sound.sampled.TargetDataLine: Microphone (Realtek High Defini [com.sun.media.sound.DirectAudioDevice$DirectTDL@2f57d162] 
interface javax.sound.sampled.SourceDataLine: Speakers (Realtek High Definition Audio) [com.sun.media.sound.DirectAudioDevice$DirectSDL@79b7d13e]
0000000000000000000000000000000000000000000000000000000000000...... And a lot more of that

【问题讨论】:

  • 如果缓冲区全为零,这意味着它要么没有任何内容,要么音频数据的幅度全为零(也没有)。您应该做的第一件事是检查来自read 的返回值,因为它会判断是否从流中实际读取了任何内容。如果它正在从流中读取,但流全为零,则可能是设备问题。确保麦克风没有静音,检查设备设置,如果可能,尝试使用不同的麦克风等。Java Sound 基本上只是进出之间的独轮车,因此 Java 内部不会出错。
  • 是的,我只是不确定从这里到哪里去。至于read,它被读入(byte[]) Stream.READ_BUFFER,然后在play() 中作为一串0 打印在输出上。方法bytesToString(byte[] array) 看起来像转换数据正常吗,因为它用于打印数据?
  • 如前所述,您必须使用来自read 的返回值,它经常不会填满整个字节数组,在这种情况下,它看起来可能返回 0 - 不读任何东西。
  • 包含 0 的流可能对数据没有任何意义。原始数字数组初始化为 0。从读取中找出返回值是什么。你的 IO 逻辑也应该是int bread = read(b, 0, b.length); write(b, 0, bread);。因为如果 b.length 不是样本帧的整数会发生什么? docs.oracle.com/javase/7/docs/api/javax/sound/sampled/…
  • ^ 实际上我猜这还不够,因为它们都会抛出 IllegalArgumentException,实际上你应该根据 AudioFormat 计算缓冲区大小。虽然我现在假设它不会抛出异常,因为格式可能是 16 位的。但无论如何,您需要找出从 read 方法返回的内容。

标签: java audio audio-streaming javasound


【解决方案1】:

让我们看看:

Stream.PROCESSING_BUFFER.read(Stream.READ_BUFFER, 0, Stream.BUFFER_SIZE);

如果一切正常,这将读取数据,直到缓冲区已满,但在您的情况下,一切都不好,实际上它什么也没有读取,您可以通过检查 read 返回的值来判断:

int numRead = Stream.PROCESSING_BUFFER.read(Stream.READ_BUFFER, 0, Stream.BUFFER_SIZE);

numRead 为 0,Stream.READ_BUFFER 中没有任何内容。

为了确保您输出您需要使用的已读取数据:

Stream.RENDERING_BUFFER.write(Stream.READ_BUFFER, 0, numRead);

你什么都没读是因为你还没有启动TargetDataLine,你需要:

if (!Stream.PROCESSING_BUFFER.isOpen()) {
    Stream.PROCESSING_BUFFER.open(Stream.audioFormat, Stream.BUFFER_SIZE);
    Stream.PROCESSING_BUFFER.start();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-23
    • 2019-10-28
    • 2011-09-29
    • 2012-08-15
    • 1970-01-01
    • 2017-06-09
    • 2013-06-19
    • 2014-10-23
    相关资源
    最近更新 更多