1

我正在尝试使用 SourceDataLine 编写 2 个不同的缓冲区(缓冲区 A 和 B),以同时播放声音。但它不断在缓冲区 A 和缓冲区 B 之间切换,我是否需要在将缓冲区写入我的 SourceDataLine 之前将它们合并在一起,或者有没有办法同步播放它们?

class PlayThread extends Thread {
    byte[] buffer = new byte[2 * 1024];

    @Override
    public void run() {
        try {
            while (true) {
                DatagramPacket receive = new DatagramPacket(buffer, buffer.length);
                mDatagramSocket.receive(receive);
                mSourceDataLine.write(receive.getData(), 0, receive.getData().length);

                System.out.println("Received!");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

我有 2 个带有不同传入缓冲区的 PlayThread 实例。下面是初始化 SourceDataLine 的函数。

private void init() {
    try {
        DataLine.Info sourceDataLineInfo = new DataLine.Info(
                SourceDataLine.class, audioFormat);
        DataLine.Info targetDataLineInfo = new DataLine.Info(
                TargetDataLine.class, audioFormat);

        Mixer.Info[] mixerInfo = AudioSystem.getMixerInfo();

        Mixer mixer = AudioSystem.getMixer(mixerInfo[3]);
        mSourceDataLine = (SourceDataLine) AudioSystem
                .getLine(sourceDataLineInfo);
        mTargetDataLine = (TargetDataLine) mixer.getLine(targetDataLineInfo);

        mSourceDataLine.open(audioFormat, 2 * 1024);
        mSourceDataLine.start();

        mTargetDataLine.open(audioFormat, 2 * 1024);
        mTargetDataLine.start();
    } catch (LineUnavailableException ex) {
        ex.printStackTrace();
    }
}

谢谢你。

4

1 回答 1

1

你绝对必须合并它们。想象一下从两个线程将数字写入文件:

123456...
123456...

可能变成

11234235656...

这是发生在你身上的事情。

另一个问题是您需要在数据从网络传入时对其进行缓冲,否则您可能会丢弃它。您至少需要两个线程——一个用于阅读,一个用于播放以使其正常工作。但是,在您的情况下,每个输入数据包流使用一个读取器线程可能会更好。(请参阅我的演讲幻灯片:http ://blog.bjornroche.com/2011/11/slides-from-fundamentals-of-audio.html我特别有一张关于从 http 流式传输的幻灯片,这也与此处相关)

因此,不要使用多个 PlayThreads,而是创建多个 ReaderThreads,它们等待数据然后写入某种类型的缓冲区(PipedInput并且PipedOutputStream适用于 Java)。然后您需要另一个线程从缓冲区读取数据,然后将组合数据写入流。

这留下了如何组合数据的原始问题。答案是没有单一的答案,但通常最简单的正确方法是逐个样本对数据进行平均。但是,具体如何执行取决于您的数据格式,您的代码不包含该格式。假设它是 big-endian 16 位整数,您需要将传入的原始数据转换为 short,平均 short,然后将平均 short 转换回字节。

使用and最容易完成bytetoshort转换。DataInputStreamDataOutputStream

于 2012-11-05T22:32:46.900 回答