You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java中同时录制麦克风与扬声器音频并获取对应设备音频?

嘿,我来帮你搞定Java里同时录制麦克风和扬声器音频的问题,还有怎么获取Line-In(麦克风)和Line-Out(扬声器)的音频信号——这俩需求核心都是Java Sound API的使用,不过扬声器录制得注意点系统相关的细节,咱们一步步来:

一、先枚举并获取目标音频设备

不管是录麦克风还是扬声器,第一步得先找到对应的硬件设备。Java Sound API用Mixer类来管理所有音频设备,咱们可以枚举所有可用的混频器,然后筛选出支持输入(Line-In)和输出(Line-Out)的设备。

枚举所有音频设备的代码示例

import javax.sound.sampled.*;

public class AudioDeviceLister {
    public static void main(String[] args) {
        Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
        for (Mixer.Info info : mixerInfos) {
            Mixer mixer = AudioSystem.getMixer(info);
            // 检查是否支持输入线路(TargetDataLine对应麦克风/Line-In)
            Line.Info[] targetLineInfos = mixer.getTargetLineInfo();
            // 检查是否支持输出线路(SourceDataLine对应扬声器/Line-Out)
            Line.Info[] sourceLineInfos = mixer.getSourceLineInfo();
            
            System.out.println("设备名称: " + info.getName());
            System.out.println("描述: " + info.getDescription());
            System.out.println("支持输入线路: " + (targetLineInfos.length > 0));
            System.out.println("支持输出线路: " + (sourceLineInfos.length > 0));
            System.out.println("---");
        }
    }
}

运行这段代码后,你会看到所有设备的信息:比如Windows上的“立体声混音”就是用来捕获扬声器输出的设备,Mac上可能需要先配置Soundflower或者聚合设备,Linux可以用PulseAudio的回环模块。

二、录制麦克风(Line-In)的音频

麦克风属于输入音频设备,用TargetDataLine来捕获它的音频流,步骤很清晰:

步骤1:定义统一的音频格式

首先得确定音频的参数,比如采样率44100Hz、16位深度、立体声/单声道,这些参数要和设备支持的格式匹配:

AudioFormat format = new AudioFormat(
    AudioFormat.Encoding.PCM_SIGNED,
    44100.0f, // 采样率
    16, // 位深
    2, // 声道数(立体声)
    4, // 帧大小(16位*2声道=4字节)
    44100.0f, // 帧速率
    false // 是否大端字节序
);

步骤2:打开输入线路并录制

找到对应的麦克风设备后,打开TargetDataLine,然后读取音频数据到缓冲区,最后保存为WAV文件:

import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;

public class MicRecorder {
    public static void main(String[] args) throws LineUnavailableException, IOException {
        AudioFormat format = new AudioFormat(44100.0f, 16, 2, true, false);
        DataLine.Info lineInfo = new DataLine.Info(TargetDataLine.class, format);
        
        // 这里可以指定具体的麦克风混频器,比如从之前的枚举结果里选
        TargetDataLine line = (TargetDataLine) AudioSystem.getLine(lineInfo);
        line.open(format);
        line.start();
        
        // 保存到WAV文件
        AudioInputStream ais = new AudioInputStream(line);
        AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File("mic_recording.wav"));
        
        // 停止录制的逻辑(比如按回车停止)
        System.in.read();
        line.stop();
        line.close();
        ais.close();
    }
}
三、录制扬声器(Line-Out)的音频

这个稍微麻烦点,因为Java Sound API没有直接捕获系统输出的接口,得用回环捕获的方式:把扬声器输出的音频路由到一个虚拟输入设备里,然后捕获这个虚拟设备的信号。

关键点:找到回环设备

  • Windows:需要启用“立体声混音”设备(在声音设置的录制选项卡,右键显示禁用的设备,然后启用它)
  • Mac:安装Soundflower或者用系统自带的“聚合设备”创建回环
  • Linux:启用PulseAudio的回环模块

找到回环设备后,录制逻辑和麦克风完全一样——只是把目标设备换成回环设备就行,比如修改上面的代码:

// 枚举时找到回环设备的Info,比如名称包含“立体声混音”
Mixer.Info loopbackMixerInfo = null;
Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
for (Mixer.Info info : mixerInfos) {
    if (info.getName().contains("立体声混音")) {
        loopbackMixerInfo = info;
        break;
    }
}

// 用回环设备打开TargetDataLine
Mixer mixer = AudioSystem.getMixer(loopbackMixerInfo);
TargetDataLine line = (TargetDataLine) mixer.getLine(lineInfo);
四、同时录制麦克风和扬声器

要同时录两个设备,需要开两个独立的线程分别处理麦克风和扬声器的捕获,然后可以选择:

  1. 分别保存为两个独立的音频文件
  2. 把两个音频流混合成一个立体声文件(比如左声道麦克风,右声道扬声器)

双线程录制+混合的示例框架

import javax.sound.sampled.*;
import java.io.File;

public class DualRecorder {
    private static final AudioFormat FORMAT = new AudioFormat(44100.0f, 16, 2, true, false);
    private static TargetDataLine micLine;
    private static TargetDataLine loopbackLine;
    private static final int BUFFER_SIZE = FORMAT.getFrameSize() * 1024;
    
    public static void main(String[] args) throws Exception {
        // 初始化麦克风线路
        DataLine.Info micInfo = new DataLine.Info(TargetDataLine.class, FORMAT);
        micLine = (TargetDataLine) AudioSystem.getLine(micInfo);
        micLine.open(FORMAT);
        
        // 初始化回环线路(扬声器)
        Mixer.Info loopbackInfo = findLoopbackMixer();
        Mixer loopbackMixer = AudioSystem.getMixer(loopbackInfo);
        DataLine.Info loopbackLineInfo = new DataLine.Info(TargetDataLine.class, FORMAT);
        loopbackLine = (TargetDataLine) loopbackMixer.getLine(loopbackLineInfo);
        loopbackLine.open(FORMAT);
        
        // 启动两个录制线程
        Thread micThread = new Thread(new MicRecorderRunnable());
        Thread loopbackThread = new Thread(new LoopbackRecorderRunnable());
        micThread.start();
        loopbackThread.start();
        
        // 等待用户输入停止
        System.out.println("按回车停止录制...");
        System.in.read();
        
        // 停止录制
        micLine.stop();
        micLine.close();
        loopbackLine.stop();
        loopbackLine.close();
    }
    
    private static Mixer.Info findLoopbackMixer() {
        // 这里根据系统返回对应的回环设备Info,比如Windows的“立体声混音”
        Mixer.Info[] infos = AudioSystem.getMixerInfo();
        for (Mixer.Info info : infos) {
            if (info.getName().contains("立体声混音")) {
                return info;
            }
        }
        throw new RuntimeException("未找到回环设备,请检查系统音频设置");
    }
    
    static class MicRecorderRunnable implements Runnable {
        @Override
        public void run() {
            byte[] buffer = new byte[BUFFER_SIZE];
            while (micLine.isRunning()) {
                int bytesRead = micLine.read(buffer, 0, buffer.length);
                // 这里可以将麦克风数据写入左声道,和回环数据混合后保存
                // 注意需要同步两个线程的缓冲区,避免时间偏移
            }
        }
    }
    
    static class LoopbackRecorderRunnable implements Runnable {
        @Override
        public void run() {
            byte[] buffer = new byte[BUFFER_SIZE];
            while (loopbackLine.isRunning()) {
                int bytesRead = loopbackLine.read(buffer, 0, buffer.length);
                // 这里可以将回环数据写入右声道,和麦克风数据混合后保存
            }
        }
    }
}

注意事项

  • 音频格式必须统一:麦克风和扬声器的录制格式要完全一致,否则混合会出现杂音
  • 线程同步:两个录制线程的缓冲区需要同步,避免时间偏移导致音画不同步
  • 权限问题:Mac和Windows都需要给程序授予音频录制权限,否则会报错
  • 设备兼容性:不同系统的回环设备名称不一样,最好让用户手动选择设备,不要硬编码名称

内容的提问来源于stack exchange,提问作者Ha Thi Hong

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:06:56