【发布时间】:2021-04-16 00:09:24
【问题描述】:
我正在编写一个函数来使用 TargetDataLine 捕获大约 7.5 秒的音频剪辑。该代码执行并呈现一个“input.wav”文件,但是当我播放它时没有声音。
如本文底部的代码所示,我的方法是执行以下操作:
- 创建 AudioFormat 并获取目标数据线的信息。
- 通过从 AudioSystem 获取线来创建目标数据线。
- 打开并启动 TargetDataLine,它为录制分配系统资源。
- 创建一个辅助线程,通过写入文件来录制音频。
- 启动辅助线程,同时暂停主线程,然后关闭目标数据线以停止记录。
到目前为止我所尝试的:
-
更改音频格式。最初,我使用的是另一个采用文件类型的 AudioFormat 构造函数(其中第一个参数是 AudioFormat.Encoding.PCM_SIGNED 等)。我的采样率为 44100、16 位、2 个通道和另一种格式的小端设置,产生了相同的结果。
-
更改辅助线程和主线程上的命令顺序(即在备用位置执行 TLine.open() 或 start())。
-
检查我的辅助线程确实启动了。
作为参考,我在 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:一些新的测试和结果
-
我的麦克风和扬声器都在使用其他应用程序 - 使用 QuickTimePlayer 录制工作音频。
-
我围绕我的 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。
上面代码的输出是这样的(第一行分别是other和line):
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
- 意识到这一点后,我从混音器中手动加载了所有 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 的音频都是无声的。
为了完整起见,这里是 soundless 和 loadAsBytes 函数:
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