【问题标题】:Java TargetDataLine not picking up any audio?Java TargetDataLine 没有拾取任何音频?
【发布时间】:2021-04-16 00:09:24
【问题描述】:

我正在编写一个函数来使用 TargetDataLine 捕获大约 7.5 秒的音频剪辑。该代码执行并呈现一个“input.wav”文件,但是当我播放它时没有声音。

如本文底部的代码所示,我的方法是执行以下操作:

  1. 创建 AudioFormat 并获取目标数据线的信息。
  2. 通过从 AudioSystem 获取线来创建目标数据线。
  3. 打开并启动 TargetDataLine,它为录制分配系统资源。
  4. 创建一个辅助线程,通过写入文件来录制音频。
  5. 启动辅助线程,同时暂停主线程,然后关闭目标数据线以停止记录。

到目前为止我所尝试的:

  1. 更改音频格式。最初,我使用的是另一个采用文件类型的 AudioFormat 构造函数(其中第一个参数是 AudioFormat.Encoding.PCM_SIGNED 等)。我的采样率为 44100、16 位、2 个通道和另一种格式的小端设置,产生了相同的结果。

  2. 更改辅助线程和主线程上的命令顺序(即在备用位置执行 TLine.open() 或 start())。

  3. 检查我的辅助线程确实启动了。

作为参考,我在 Mac OS Big Sur 上使用 IntelliJ。

public static void captureAudio() {
        try {
            AudioFormat f = new AudioFormat(22050, 8, 1, false, false);
            DataLine.Info secure = new DataLine.Info(TargetDataLine.class, f);
            if (!AudioSystem.isLineSupported(secure)) {
                System.err.println("Unsupported Line");
            }
            TargetDataLine tLine = (TargetDataLine)AudioSystem.getLine(secure);
            System.out.println("Starting recording...");
            tLine.open(f);
            tLine.start();
            File writeTo = new File("input.wav");
            Thread t = new Thread(){
                    public void run() {
                        try {
                            AudioInputStream is = new AudioInputStream(tLine);
                            AudioSystem.write(is, AudioFileFormat.Type.WAVE, writeTo);
                        } catch(IOException e) {
                            System.err.println("Encountered system I/O error in recording:");
                            e.printStackTrace();
                        }
                    }
            };
            t.start();
            Thread.sleep(7500);
            tLine.stop();
            tLine.close();
            System.out.println("Recording has ended.");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

更新 1:一些新的测试和结果

  1. 我的麦克风和扬声器都在使用其他应用程序 - 使用 QuickTimePlayer 录制工作音频。

  2. 我围绕我的 TargetDataLines 是什么以及与它们的交易做了很多测试。我运行了以下代码:

public static void main(String[] args) {
AudioFormat f = new AudioFormat(48000, 16, 2, true, false);
        //DataLine.Info inf = new DataLine.Info(SourceDataLine.class, f);
        try {
            TargetDataLine line = AudioSystem.getTargetDataLine(f);
            DataLine.Info test = new DataLine.Info(TargetDataLine.class, f);
            TargetDataLine other = (TargetDataLine)AudioSystem.getLine(test);
            String output = line.equals(other) ? "Yes" : "No";
            if (output.equals("No")) {
                System.out.println(other.toString());
            }
            System.out.println(line.toString());
            System.out.println("_______________________________");
            for (Mixer.Info i : AudioSystem.getMixerInfo()) {
                Line.Info[] tli = AudioSystem.getMixer(i).getTargetLineInfo();
                if (tli.length != 0) {
                   Line comp = AudioSystem.getLine(tli[0]);
                   System.out.println(comp.toString() + ":" +i.getName());
                   if (comp.equals(line) || comp.equals(other)) {
                       System.out.println("The TargetDataLine is from " + i.getName());
                   }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
}

长话短说,我收到的 TargetDataLine TargetDataLine line = AudioSystem.getTargetDataLine(f);TargetDataLine other = (TargetDataLine)AudioSystem.getLine(new DataLine.Info(TargetDataLine.class, f)); 是不同的,而且,不匹配与我系统的混音器关联的任何 TargetDataLines。

上面代码的输出是这样的(第一行分别是otherline):

com.sun.media.sound.DirectAudioDevice$DirectTDL@cc34f4d
com.sun.media.sound.DirectAudioDevice$DirectTDL@17a7cec2
_______________________________
com.sun.media.sound.PortMixer$PortMixerPort@79fc0f2f:Port MacBook Pro Speakers
com.sun.media.sound.PortMixer$PortMixerPort@4d405ef7:Port ZoomAudioDevice
com.sun.media.sound.DirectAudioDevice$DirectTDL@3f91beef:Default Audio Device
com.sun.media.sound.DirectAudioDevice$DirectTDL@1a6c5a9e:MacBook Pro Microphone
com.sun.media.sound.DirectAudioDevice$DirectTDL@37bba400:ZoomAudioDevice
  1. 意识到这一点后,我从混音器中手动加载了所有 TargetDataLine,并尝试用它们各自录制音频,看看是否有任何声音。

我使用以下方法收集了所有的 TargetDataLines:

public static ArrayList<Line.Info> allTDL() {
        ArrayList<Line.Info> all = new ArrayList<>();
        for (Mixer.Info i : AudioSystem.getMixerInfo()) {
            Line.Info[] tli = AudioSystem.getMixer(i).getTargetLineInfo();
            if (tli.length != 0) {
                for (int f = 0; f < tli.length; f += 1) {
                    all.add(tli[f]);
                }
            }
        }
        return all;
    }

我的捕获/录制音频方法保持不变,除了将格式切换为AudioFormat f = new AudioFormat(48000, 16, 2, true, false);,将录制时间更改为 5000 毫秒,并将方法标题写入为public static void recordAudio(Line.Info inf),以便我可以单独加载每个 TargetDataLine 及其信息.

然后我执行以下代码来旋转 TargetDataLines:

public static void main(String[] args) {
   for (Line.Info inf : allTDL()) {
            recordAudio(inf);
            try {
                Thread.sleep(5000);
            } catch(Exception e) {
                e.printStackTrace();
            }
            if (!soundless(loadAsBytes("input.wav"))) {
                System.out.println("The recording with " + inf.toString() + " has sound!");
            }
            System.out.println("The last recording with " + inf.toString() + " was soundless.");
        }

    }
}

输出是这样的:

Recording...
Was unable to cast com.sun.media.sound.PortMixer$PortMixerPort@506e1b77 to a TargetDataLine.
End recording.
The last recording with SPEAKER target port was soundless.
Recording...
Was unable to cast com.sun.media.sound.PortMixer$PortMixerPort@5e9f23b4 to a TargetDataLine.
End recording.
The last recording with ZoomAudioDevice target port was soundless.
Recording...
End recording.
The last recording with interface TargetDataLine supporting 8 audio formats, and buffers of at least 32 bytes was soundless.
Recording...
End recording.
The last recording with interface TargetDataLine supporting 8 audio formats, and buffers of at least 32 bytes was soundless.
Recording...
End recording.
The last recording with interface TargetDataLine supporting 14 audio formats, and buffers of at least 32 bytes was soundless.

TL;DR 每个 TargetDataLine 的音频都是无声的。 为了完整起见,这里是 soundlessloadAsBytes 函数:

 public static byte[] loadAsBytes(String name) {
        assert name.contains(".wav");
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        File retrieve = new File("src/"+ name);
        try {
            InputStream input = AudioSystem.getAudioInputStream(retrieve);

            int read;
            byte[] b = new byte[1024];
            while ((read = input.read(b)) > 0) {
                out.write(b, 0, read);
            }
            out.flush();
            byte[] full = out.toByteArray();
            return full;

        } catch(UnsupportedAudioFileException e) {
            System.err.println("The File " + name + " is unsupported on this system.");
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("Input-Output Exception on retrieval of file " + name);
            e.printStackTrace();
        }
        return null;

    }

  static boolean soundless(byte[] s) {
        if (s == null) {
            return true;
        }
        for (int i = 0; i < s.length; i += 1) {
            if (s[i] != 0) {
                return false;
            }
        }
        return true;
    }

除了不允许 Java 访问音频线的操作系统怪癖之外,我不确定此时可能是什么问题,但我不知道如何解决这个问题 - 查看系统偏好设置没有任何明显的方式来允许访问。我认为它可能必须使用终端命令来完成,但也不确定我必须在那里执行哪些命令。

【问题讨论】:

  • 我在“macos bigsur java audio”上进行了搜索,发现了有关音频问题的帖子。我没有彻底了解结果,但下面的链接列举了几个安装步骤,涉及到他们的应用程序的音频设置(3、4、5)。 help.uaudio.com/hc/en-us/articles/… 也许这将是一个很好的线索,用于搜索和设置哪些系统设置。 BigSur 似乎确实给许多人带来了挑战。您为解决这个问题所做的工作给我留下了深刻的印象和感激。
  • support.apple.com/en-us/HT209175 这些系统设置是否可以通过 Java 访问?身份证。 ProTools 很头疼! pro-tools-expert.com/production-expert-1/2020/10/21/…
  • 我目前正在设置 Universal Audio 并检查这些设置!如果这方面有任何新内容,我会更新。图像中的系统设置是我检查过的,但 Java 不在列出的应用程序中,而且窗口不允许用户添加新应用程序很遗憾!与此同时,我在 Ask Different 上发表了一篇文章,看看是否还有其他值得寻找的 Mac 设置
  • 你玩过这些文件吗??在一些音频编辑器中打开它们??依靠错误程序的其他部分来告诉您程序是否工作并不总是一种安全的技术 - 在 Win 10 中对我来说很好
  • @gpasch 是的,我做的第一件事是在 QuickTime 音频播放器中打开音频文件并播放它们以检查是否有声音

标签: java intellij-idea audio signal-processing wav


【解决方案1】:

我在您显示的代码中没有发现任何错误。我还没有尝试在我的系统上测试它。 (Linux、Eclipse)

在我看来,您的代码与this tutorial 非常匹配。作者Nam Ha Minh对回答问题格外认真。如果他的版本也对您不适用,您可以尝试他的确切代码示例并咨询他。

但首先,生成的 .wav 文件的大小是多少?文件大小是否与您记录期间的预期数据量相匹配?如果是这样,您确定您有来自麦克风的数据吗? Nam 有another code example,其中录制的声音被逐步读取并放入内存中。基本上,不是使用AudioInputStream 作为AudioSystem.write 方法的参数,而是在AudioInputStream 上执行多个read 方法调用并直接检查传入数据。这可能有助于解决问题是否发生在流程的传入和传出部分。

我对格式不够了解,无法知道 Mac 的处理方式是否不同。我很惊讶您将格式设置为 unsigned。出于我的有限目的,我坚持使用“CD 质量立体声”并在所有关键时刻都使用签名 PCM。

编辑:根据反馈,问题似乎在于传入的行没有返回数据。从查看其他类似的教程来看,似乎有几个人在他们的 Mac 系统上遇到了同样的问题。

首先要验证:您的麦克风是否可以与其他应用程序一起使用?

至于接下来的步骤,我会尝试验证所选的行。可以枚举/检查暴露给 java 的行。教程Accessing Audio System Resources 提供了一些有关如何执行此操作的基本信息。看起来AudioSystem.getMixerInfo() 将返回可以检查的可用混音器列表。也许AudioSystem.getTargetLineInfo() 会更重要。

我想当您获得TargetDataLine 时使用的默认LinePort 可能不是运行麦克风的那个。如果某个特定的线路或端口是您需要的,那么可以通过overridden getTargetDataLine method 明确指定它。

我了解到可能需要处理一个安全策略。我不完全理解代码,但如果这是问题所在,Exception 大概会被抛出。也许 MacO 有新的安全措施,以防止外部程序偷偷打开麦克风线?

如果您确实解决了这个问题,请务必发布答案并将其标记为已解决。对于很多人来说,这似乎是一个活生生的问题。

【讨论】:

  • 您好菲尔,谢谢您的回复!我将查看其他代码示例,看看我是否遇到了同样的问题。至于生成的 .wav 文件的大小,当我使用比上面代码中更多的标准设置(44100 采样率、PCM_SIGNED、2 通道等)时,我得到的音频文件大小为 1.3 MB(2.1 on盘)。传统智慧说 .WAVE 是每分钟 10MB,所以 10 * 7.5/60 = 1.25 MB 似乎在球场上
  • 作为更新:我在我的计算机上单独运行了备用代码示例,看看是否会成功,结果相同(生成了正确长度的音频文件,但没有声音) .我也尝试过切换格式(AIFF-C 而不是 WAVE),但无济于事
  • 有趣。我刚才在看他的教程上的问题,似乎其他Mac用户也出现了同样的问题,他没有回答这方面的问题。此外,cmets 部分似乎出现故障。我将编辑我的答案以添加一些额外的建议。
  • 感谢您添加的建议!我用结果更新了我的问题 - 将研究音频线路上的操作系统保护
猜你喜欢
  • 1970-01-01
  • 2016-02-15
  • 2012-12-11
  • 2012-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-08
  • 2022-11-14
相关资源
最近更新 更多