gpt4 book ai didi

Java TargetDataLine 没有接收到任何音频?

转载 作者:行者123 更新时间:2023-12-05 06:06:46 26 4
gpt4 key购买 nike

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

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

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

到目前为止我尝试了什么:

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

  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();
}
}

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

上面代码的输出是这样的(第一行分别是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毫秒,并将方法 header 编写为 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 访问音频线路的操作系统怪癖之外,我不太确定此时可能出现的问题,但我不知道如何解决这个问题 - 查看系统偏好设置' 允许访问的任何明显方式。我认为这可能必须使用终端命令来完成,但也不确定我必须在那里执行哪些命令。

最佳答案

我没有发现您显示的代码有任何问题。我还没有尝试在我的系统上测试它。 (Linux, Eclipse)

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

但首先,生成的 .wav 文件的大小是多少?文件大小是否与您录制期间的预期数据量相匹配?如果是这样,您确定您的麦克风有数据传入吗?南有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 有新的安全措施,以防止外部程序偷偷打开麦克风线?

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

关于Java TargetDataLine 没有接收到任何音频?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65660266/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com