使用 Java 混合器混合两个音频流

Posted

技术标签:

【中文标题】使用 Java 混合器混合两个音频流【英文标题】:Mixing two streams of audio using Java mixer 【发布时间】:2016-10-19 20:36:20 【问题描述】:

我想使用 java 混合器混合两个音频流,并将输出写入文件。

为此,我通过读取麦克风混音器的 TargetDataLines 来捕获两个麦克风的输入:

    //microphone TargetDataLines
    TargetDataLine finalIn = null;
    TargetDataLine finalIn2 = null;

    Mixer.Info[] mixerInfos = Audiosystem.getMixerInfo();
    for (Mixer.Info info: mixerInfos)

        Mixer m = AudioSystem.getMixer(info);
        Line.Info[] lineInfos = m.getSourceLineInfo();
        System.out.println("****mixer name:" + info.getName());
        if(info.getName().equals("C920 [plughw:1,0]"))
            finalIn=(TargetDataLine) m.getLine(m.getTargetLineInfo()[0]);
        if(info.getName().equals("C920_1 [plughw:3,0]"))
            finalIn2=(TargetDataLine) m.getLine(m.getTargetLineInfo()[0]);

        for (Line.Info lineInfo:lineInfos)
            System.out.println ("\n source line info: "+lineInfo);
        
        lineInfos = m.getTargetLineInfo();
        for (Line.Info lineInfo:lineInfos)
            System.out.println ("target line info: "+lineInfo);
        

    

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

    finalIn.open(format,2000);
    finalIn.start();
    finalIn2.open(format,2000);
    finalIn2.start();

    int numBytesRead;
    byte[] targetData = new byte[finalIn.getBufferSize() / 5];

    int numBytesRead2;
    byte[] targetData2 = new byte[finalIn2.getBufferSize() / 5];

    while (true) 
            numBytesRead = finalIn.read(targetData,0,targetData.length);
            numBytesRead2 = finalIn2.read(targetData2, 0, targetData2.length);

从麦克风捕获的结果数据将位于targetDatatargetData2。现在我想将这两个包含从麦克风接收到的声音样本的字节数组混合到一个新的字节数组中我想为此使用另一个 Java 混合器

为此,我使用以下代码创建了两个 SourceDataLine 和一个 TargetDataLine:

        DataLine.Info targetInfo = new DataLine.Info(TargetDataLine.class, format);
        DataLine.Info sourceInfo = new DataLine.Info(SourceDataLine.class, format);
        SourceDataLine sourceLine = (SourceDataLine) AudioSystem.getLine(sourceInfo);
        SourceDataLine sourceLine2 = (SourceDataLine) AudioSystem.getLine(sourceInfo);

        sourceLine.open(format,2000);
        sourceLine.start();

        sourceLine2.open(format,2000);
        sourceLine2.start();

        TargetDataLine targetLine = (TargetDataLine)        AudioSystem.getLine(targetInfo);
        targetLine.open(format,2000);
        targetLine.start();

然后在“while”里面我会补充:

    byte[] result = new byte[finalIn.getBufferSize() / 5];

    while (true) 
            numBytesRead = finalIn.read(targetData,0,targetData.length);
            numBytesRead2 = finalIn2.read(targetData2, 0, targetData2.length);

            sourceLine.write(targetData, 0, numBytesRead);
            sourceLine.write(targetData2, 0, numBytesRead2);
            targetLine.read(result, 0, numBytesRead2);
    

但是,虽然声音实际上是通过我的耳机正确播放的,但结果数组仍然是空的。 我怎么解决这个问题?我做错了什么?

【问题讨论】:

您要存储结果还是播放它?如果你玩它,你有另一个声卡吗?目标行是捕获不写所以结果不会被存储;你需要明确你到底想做什么;你有很多声卡吗??直到你得到两个麦克风,你都可以 如需尽快获得更好的帮助,请发帖minimal reproducible example 或Short, Self Contained, Correct Example。 我的 2 个网络摄像头和 1 个声卡上有两个麦克风。我需要从我的程序中捕捉到我的扬声器的信号。我想捕获与该混音器关联的目标线。 我完全不明白其中的原理:你想混合两个输入;但是混合器会进行混合;为什么不让声音通过并捕捉混音器?这是一项无用的任务或助长黑客行为的东西;我不明白你想要实现什么 - 【参考方案1】:

我想知道您是否对 Java Mixer 的功能有误解。由于我作为音频工程师的背景经验,我认为这门课的名字很糟糕。在我看来,混音器是您组合各种音轨的地方。这不是 Java Mixer 所做的。

我认为要做你想做的事,你需要并行阅读你的两条线。对于每个,您将读取一个字节缓冲区,并且必须将字节转换为 PCM 数据。然后,您可以将 PCM 数据相加,然后将其转换回字节并输出。

有一个很好的教程展示了如何获取单个字节/帧here。在页面的下方,有一个代码示例。注意评论的位置:

// Here, do something useful with the audio data that's 
// now in the audioBytes array...

此示例从 AudioInputStream 读取,但从 TargetDataLines 获取单个字节的技术并没有那么不同。当然,您将从两行而不是从单个 AIS 读取。而您要做的“有用的事情”就是在每帧的基础上并行添加 PCM 数据。

对音频数据求和后,您可以使用some code I wrote 并将其发布到另一个站点,该站点采用 PCM 流并将其写为 wav 文件。希望该页面上的讨论足够清晰,您可以理解。

【讨论】:

这很有启发性。我设法使用您提供的链接编写了一个简单的混音器。但是,我想知道我是否可以从 Java 访问一个硬件混音器来为我进行音频混音。 似乎有道理,但我从来没有这样做过。我计划最终尝试为我在下个月编写的 java 合成器编写 MIDI 输入读取功能。这将是我第一次尝试处理默认 SourceDataLine 和 TargetDataLine 以外的行。

以上是关于使用 Java 混合器混合两个音频流的主要内容,如果未能解决你的问题,请参考以下文章

FFMpeg;混合两个音频流时出错

使用 OpenGL 混合音频

我们如何使用 mediaRecorder 将画布流与音频流混合

我应该使用线程编程来混合 2 个音频流吗?

在 iOS 上混合和均衡多个压缩音频流

使用不同的起点条目快速混合两个音频文件