我试图弄清楚Windows中是否播放任何类型的声音(通过任何应用程序).如果有什么东西在某处发出声音,我想知道它!
按照文档后,我已经找到了如何获得机器上的混音器列表,以及这些混音器的行 - 如果我理解正确的话,那就是混音器的输入/输出.
但是,我遇到的问题是我不知道如何从线路获取所需的数据.
我看到的唯一具有音量级概念的界面是DataLine.这个问题是我无法弄清楚什么返回实现数据线接口的对象.
列举所有混音器和线路:
public static void printMixers() {
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) {
System.out.println(linfo);
}
}
catch (LineUnavailableException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Run Code Online (Sandbox Code Playgroud)
该代码枚举并显示我机器上的所有音频设备.从那,其中一个不应该Lines包含某种播放级数据?
哦,你想找到音量?好吧,并非所有硬件都支持它,但这里是你如何获得数据线.
public static SourceDataLine getSourceDataLine(Line.Info lineInfo){
try{
return (SourceDataLine) AudioSystem.getLine(lineInfo);
}
catch(Exception ex){
ex.printStackTrace();
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
然后只需调用SourceDataLine.getLevel()来获取卷.我希望这有帮助.
注意:如果声音来自JVM之外或不通过JavaSound API,则此方法将无法检测到声音,因为JVM无法访问与SourceDataLine等效的操作系统.
更新:经过进一步研究,大多数系统都没有实现getLevel().所以我根据这个论坛讨论手动实现了这个方法:https://community.oracle.com/message/5391003
以下是课程:
public class Main {
public static void main(String[] args){
MicrophoneAnalyzer mic = new MicrophoneAnalyzer(FLACFileWriter.FLAC);
System.out.println("HELLO");
mic.open();
while(true){
byte[] buffer = new byte[mic.getTargetDataLine().getFormat().getFrameSize()];
mic.getTargetDataLine().read(buffer, 0, buffer.length);
try{
System.out.println(getLevel(mic.getAudioFormat(), buffer));
}
catch(Exception e){
System.out.println("ERROR");
e.printStackTrace();
}
}
}
public static double getLevel(AudioFormat af, byte[] chunk) throws IOException{
PCMSigned8Bit converter = new PCMSigned8Bit(af);
if(chunk.length != converter.getRequiredChunkByteSize())
return -1;
AudioInputStream ais = converter.convert(chunk);
ais.read(chunk, 0, chunk.length);
long lSum = 0;
for(int i=0; i<chunk.length; i++)
lSum = lSum + chunk[i];
double dAvg = lSum / chunk.length;
double sumMeanSquare = 0d;
for(int j=0; j<chunk.length; j++)
sumMeanSquare = sumMeanSquare + Math.pow(chunk[j] - dAvg, 2d);
double averageMeanSquare = sumMeanSquare / chunk.length;
return (Math.pow(averageMeanSquare,0.5d));
}
}
Run Code Online (Sandbox Code Playgroud)
我使用的方法仅适用于8bitPCM,因此我们必须将编码转换为使用这两个类的编码.这是一般的抽象转换器类.
import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
abstract class AbstractSignedLevelConverter
{
private AudioFormat srcf;
public AbstractSignedLevelConverter(AudioFormat sourceFormat)
{
srcf = sourceFormat;
}
protected AudioInputStream convert(byte[] chunk)
{
AudioInputStream ais = null;
if(AudioSystem.isConversionSupported( AudioFormat.Encoding.PCM_SIGNED,
srcf))
{
if(srcf.getEncoding() != AudioFormat.Encoding.PCM_SIGNED)
ais = AudioSystem.getAudioInputStream(
AudioFormat.Encoding.PCM_SIGNED,
new AudioInputStream(new ByteArrayInputStream(chunk),
srcf,
chunk.length * srcf.getFrameSize()));
else
ais = new AudioInputStream(new ByteArrayInputStream(chunk),
srcf,
chunk.length * srcf.getFrameSize());
}
return ais;
}
abstract public double convertToLevel(byte[] chunk) throws IOException;
public int getRequiredChunkByteSize()
{
return srcf.getFrameSize();
}
}
Run Code Online (Sandbox Code Playgroud)
这是8BitPCM的一个
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
public class PCMSigned8Bit extends AbstractSignedLevelConverter
{
PCMSigned8Bit(AudioFormat sourceFormat)
{
super(sourceFormat);
}
public double convertToLevel(byte[] chunk) throws IOException
{
if(chunk.length != getRequiredChunkByteSize())
return -1;
AudioInputStream ais = convert(chunk);
ais.read(chunk, 0, chunk.length);
return (double)chunk[0];
}
}
Run Code Online (Sandbox Code Playgroud)
这适用于TargetDataLine,它可能在您的用例中不起作用,但您可以围绕SourceDataLine构建一个包装器并使用它来正确实现这些方法.希望这会有所帮助.