我试图弄清楚Windows中(通过任何应用程序)是否正在播放任何声音。如果某处正在发出噪音,我想知道!

在阅读了文档之后,我发现了如何在机器上获取混音器列表以及这些混音器的行-如果我理解正确的话,它们就是用于混音器输入/输出的内容。

但是,我遇到的问题是我不知道如何从生产线上获取所需的数据。

我看到的唯一具有音量级别概念的接口(interface)是DataLine。这样做的问题是我不知道是什么返回了实现数据线接口(interface)的对象。

列举所有的混音器和线:

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

该代码枚举并显示了我机器上的所有音频设备。由此看来,这些Lines中的其中一个不应该包含某种回放级别数据吗?

最佳答案

哦,你想找到音量?好吧,并不是所有的硬件都支持它,但是这就是您获得数据线的方式。

    public static SourceDataLine getSourceDataLine(Line.Info lineInfo){
    try{
        return (SourceDataLine) AudioSystem.getLine(lineInfo);
    }
    catch(Exception ex){
        ex.printStackTrace();
        return null;
    }
}

然后只需调用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));
    }
}

我使用的方法仅适用于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();
    }
}

这是一个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];
    }




}

这是针对TargetDataLine的,在您的用例中可能无法使用,但是您可以围绕SourceDataLine构建包装器,并使用该包装器正确实现这些方法。希望这会有所帮助。

10-04 13:18