【问题标题】:Checking the level of audio playback in a Mixer's Line?检查混音器线路中的音频播放电平?
【发布时间】:2014-03-01 21:10:58
【问题描述】:

我正在尝试确定 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();
        }
    }

该代码枚举并显示我机器上的所有音频设备。从那以后,其中一个Lines 不应该包含某种播放级别数据吗?

【问题讨论】:

  • 考虑添加一个 Audio 或 Javasound 标签来提醒有这种特殊兴趣的用户!

标签: java


【解决方案1】:

哦,您想查找音量吗?好吧,不是所有的硬件都支持它,但这里是你如何获得数据线。

    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 构建一个包装器并使用它来正确实现这些方法。希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-23
    • 1970-01-01
    • 1970-01-01
    • 2022-08-23
    相关资源
    最近更新 更多