【发布时间】:2016-12-14 23:58:28
【问题描述】:
tl;dr 对于未来的读者,使用 Java 或 C#不可能(目前)录制实时音频。使用 C++,因为它提供了大量的音频 api。
我的目标是让当前的声音在 Windows 机器上播放,并像图形音频可视化器一样分析声音(获取音量属性和 Hz(低音和高音))。当我说当前声音时,我的意思是如果要播放 Youtube 视频或 Spotify 歌曲,该程序会读取该音频输出。我无意播放声音,而是实时捕捉并可视化它。
在尝试这样做时,我阅读了如何build an audio waveform display 并涉及如何将音频文件转换为字节数组(一行)。这无济于事,因为它不会得到当前的声音。我还阅读了如何capture audio 和this java accessing sound tutorial,它们都没有回答我的问题,因为它们都需要加载歌曲文件。
我根本不明白这一点。我完全一无所知,任何帮助将不胜感激。
编辑:我环顾四周,second answer from this source 让我得出结论:我可以找到所有的音频设备,看看哪个正在发出声音。我不知道那之后该怎么办。
编辑2(再次编辑):通过试验和环顾四周,我在下面编写了这段代码。我认为这让我朝着我想要的方向前进,但我不知道如何完成它。
Mixer.Info[] mixers = AudioSystem.getMixerInfo();
for (Mixer.Info mixerInfo : mixers) {
Mixer mixer = AudioSystem.getMixer(mixerInfo);
try {
mixer.open();
Line.Info[] lines = mixer.getTargetLineInfo();
for (Line.Info linfo : lines) {
Line line = AudioSystem.getLine(linfo);
//here I'm opening the line, but I don't know how to grab data
line.open();
}
} catch (LineUnavailableException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
我使用了这个来源:Checking The Level of Audio-Playback in a mixers line,但我不想检查所有正在播放音量的线路,我只需要用户默认的混音器,获取该线路,并能够分析数据。
编辑3:我试过了:
//creating a format for getting sound
float sampleRate = 8000;
int sampleSizeInBits = 16;
int channels = 2;
boolean signed = true;
boolean bigEndian = true;
AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits, channels,
signed, bigEndian);
//creating a line based off of the format
DataLine.Info info = new DataLine.Info( TargetDataLine.class, format);
TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
//opening and starting that line
line.open(format);
line.start();
while (conditionIsTrue){
//here, I don't know what to put as the parameters.
//Had I known, I don't know how I would get to analyze the data
line.read();
}
我认为我使用上面的代码走在正确的道路上,但我不知道如何提取声音并找到 bpm、低音、高音等。
编辑 4:这是一篇有趣的文章:Real-time low latency audio processing in Java。这并没有涉及哪些类以及如何实际实现它,但它提供了一些见解。
编辑 5:@AndrewThompson 使用基于您的链接的这段代码,我能够迭代可用的源代码和目标行。
Mixer.Info[] mixers = AudioSystem.getMixerInfo();
for (Mixer.Info mixerInfo : mixers) {
Mixer mixer = AudioSystem.getMixer(mixerInfo);
try {
mixer.open();
Line.Info[] sourceLines = mixer.getSourceLineInfo();
Line.Info[] targetLine = mixer.getTargetLineInfo();
for (Line.Info sourceLinfo : sourceLines) {
System.out.println(sourceLinfo );
}
for (Line.Info targetLinefo : targetLine) {
System.out.println(targetLinefo);
}
} catch (LineUnavailableException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
输出如下:
interface SourceDataLine supporting 8 audio formats, and buffers of at least 32 bytes
interface Clip supporting 8 audio formats, and buffers of at least 32 bytes
interface SourceDataLine supporting 8 audio formats, and buffers of at least 32 bytes
interface Clip supporting 8 audio formats, and buffers of at least 32 bytes
interface SourceDataLine supporting 8 audio formats, and buffers of at least 32 bytes
interface Clip supporting 8 audio formats, and buffers of at least 32 bytes
HEADPHONE target port
SPEAKER target port
然后我创建了一个方法来获取所有行的声级,如下所示:
private static void getVolumeOfAllLines() {
Mixer.Info[] mixers = AudioSystem.getMixerInfo();
for (Mixer.Info mixerInfo : mixers) {
Mixer mixer = AudioSystem.getMixer(mixerInfo);
try {
mixer.open();
Line.Info[] lines = mixer.getSourceLineInfo();
for (Line.Info linfo : lines) {
DataLine line = (DataLine)AudioSystem.getLine(linfo);
if(line != null)
System.out.println(line.getLevel());
}
} catch (LineUnavailableException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
-in 尝试查找当前行播放的声音,表示音量较高。这返回:
-1.0
-1.0
-1.0
-1.0
-1.0
-1.0
没有进展。
新代码:
private static void debug(){
Mixer.Info[] mixers = AudioSystem.getMixerInfo();
for (Mixer.Info mixerInfo : mixers) {
Mixer mixer = AudioSystem.getMixer(mixerInfo);
try {
mixer.open();
Line.Info[] lines = mixer.getTargetLineInfo();
AudioFormat format = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
44100,
16, 2, 4,
44100, false);
AudioFormat[] tdl = AudioSystem.getTargetFormats(AudioFormat.Encoding.PCM_SIGNED, format);
for (Line.Info linfo : lines) {
//Line line = AudioSystem.getLine(linfo);
TargetDataLine line = null;
DataLine.Info info = new DataLine.Info(TargetDataLine.class,
format); // format is an AudioFormat object
if (!AudioSystem.isLineSupported(info))
{
System.out.println("line not supported:" + line );
}
try
{
line = (TargetDataLine) AudioSystem.getLine(info); //error
line.open(format);
System.out.println("line opened:" + line);
line.start();
byte[] buffer = new byte[1024];
int ii = 0;
int numBytesRead = 0;
while (ii++ < 100) {
// Read the next chunk of data from the TargetDataLine.
numBytesRead = line.read(buffer, 0, buffer.length);
System.out.println("\nnumBytesRead:" + numBytesRead);
if (numBytesRead == 0) continue;
// following is a quickie test to see if content is only 0 vals
// present in the data that was read.
for (int i = 0; i < 16; i++)
{
if (buffer[i] != 0)
System.out.print(".");
else
System.out.print("0");
}
}
} catch (LineUnavailableException ex) {
ex.printStackTrace();
//...
}
}
} catch (LineUnavailableException e) {
e.printStackTrace();
}
}
}
【问题讨论】:
-
我不确定这是否总是可行的。 一些计算机具有“立体声混音”输入,其作用类似于记录输出的麦克风。
-
看看这里:ganeshtiwaridotcomdotnp.blogspot.com/search/label/… 如何从录制的音频/保存的 .wave 文件中提取 PCM 阵列
-
感谢您的链接。虽然它很有帮助,但我正在寻找一种获取实时音频的方法,而不是存储在 .wmv、mp3 等中的音频文件。感谢您的帮助!
-
具体代码看github.com/gtiwari333/…!你可以玩这个类并添加逻辑来实时处理音频!
-
"我如何获得当前的声音输出及其属性?" 首先使用
MediaTypes中的MediaTypes源迭代可用的声音线this answer.. 注意,如前所述,某些 PC / 操作系统 / 版本的操作系统 / 声卡将允许 Java 进入播放声音,而其他则不会。这是一个非常偶然的事件。
标签: java audio output javasound