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

如何基于javax.sound.sampled在Processing中查询多通道音频输入线路及振幅

嘿,我之前在Processing里做过多通道音频电平可视化,刚好踩过javax.sound.sampled的坑,给你一步步拆解解决方案:

第一步:查询系统可用的音频输入线路数量

javax.sound.sampled里用Mixer表示音频设备,输入线路则对应TargetDataLine接口。我们可以遍历所有系统音频设备,筛选出支持输入线路的设备,就能统计数量了。

直接上Processing里可运行的代码:

import javax.sound.sampled.*;

void setup() {
  size(400, 200);
  
  // 获取所有音频设备的信息
  Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
  int inputLineCount = 0;

  println("=== 可用音频输入线路 ===");
  for (Mixer.Info info : mixerInfos) {
    Mixer mixer = AudioSystem.getMixer(info);
    // 检查当前设备是否支持输入线路
    if (mixer.isLineSupported(new Line.Info(TargetDataLine.class))) {
      inputLineCount++;
      println(inputLineCount + ". " + info.getName() + "\n   描述:" + info.getDescription());
    }
  }
  println("\n共找到 " + inputLineCount + " 条输入线路");
}

void draw() {
  background(0);
  fill(255);
  text("查看控制台输出的输入线路列表", 20, 100);
}

运行后控制台会列出所有可用于输入的音频设备,比如内置麦克风、外接USB声卡等。

第二步:获取每条线路的音频振幅

要获取振幅,需要先打开目标输入线路,读取PCM音频数据,再将字节数据转换成可计算的样本值,最后统计振幅(常用平均振幅或峰值)。

这里给一个支持立体声的完整示例,多通道只需要修改音频格式和样本解析逻辑即可:

import javax.sound.sampled.*;

TargetDataLine activeInputLine;
// 存储最近60帧的振幅数据,用于可视化
float[] channelAmplitudes;
int channelCount = 2; // 立体声,可修改为对应通道数
Thread audioReadThread;

void setup() {
  size(800, 400);
  background(0);
  
  // 选择第一个可用的输入线路(你也可以让用户手动选择)
  Mixer targetMixer = findFirstInputMixer();
  if (targetMixer == null) {
    println("未找到可用音频输入线路");
    exit();
    return;
  }

  // 配置音频格式:44100采样率,16位深度,对应通道数,小端字节序,有符号
  AudioFormat audioFormat = new AudioFormat(44100, 16, channelCount, true, false);
  
  try {
    // 打开并启动输入线路
    activeInputLine = (TargetDataLine) targetMixer.getLine(new Line.Info(TargetDataLine.class));
    activeInputLine.open(audioFormat);
    activeInputLine.start();

    // 初始化振幅存储数组
    channelAmplitudes = new float[channelCount];
    
    // 启动独立线程读取音频数据,避免阻塞Processing的draw循环
    audioReadThread = new Thread(new AudioProcessor());
    audioReadThread.start();
    
  } catch (LineUnavailableException e) {
    e.printStackTrace();
    exit();
  }
}

void draw() {
  background(0);
  stroke(255);
  
  // 绘制每个通道的电平条
  float barWidth = width / channelCount;
  for (int i = 0; i < channelCount; i++) {
    float barHeight = channelAmplitudes[i] * height;
    rect(i * barWidth, height - barHeight, barWidth - 5, barHeight);
    fill(255);
    text("通道 " + (i+1), i * barWidth + 10, height - 10);
    noFill();
  }
}

// 查找第一个支持输入的音频设备
Mixer findFirstInputMixer() {
  Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
  for (Mixer.Info info : mixerInfos) {
    Mixer mixer = AudioSystem.getMixer(info);
    if (mixer.isLineSupported(new Line.Info(TargetDataLine.class))) {
      println("使用输入设备:" + info.getName());
      return mixer;
    }
  }
  return null;
}

// 音频数据处理线程
class AudioProcessor implements Runnable {
  public void run() {
    byte[] buffer = new byte[2048]; // 每次读取的字节数,可根据需求调整
    
    while (!Thread.currentThread().isInterrupted()) {
      int bytesRead = activeInputLine.read(buffer, 0, buffer.length);
      if (bytesRead <= 0) break;
      
      // 计算每个通道的平均振幅
      float[] channelSums = new float[channelCount];
      int samplePerChannel = bytesRead / (2 * channelCount); // 16位样本占2字节
      
      for (int i = 0; i < bytesRead; i += 2 * channelCount) {
        for (int ch = 0; ch < channelCount; ch++) {
          // 将两个字节转换为16位有符号样本值
          short sample = (short)((buffer[i + 1 + 2*ch] << 8) | (buffer[i + 2*ch] & 0xFF));
          // 转换为0-1的振幅比例(16位样本最大绝对值为32767)
          channelSums[ch] += abs(sample) / 32767.0;
        }
      }
      
      // 更新每个通道的平均振幅
      for (int ch = 0; ch < channelCount; ch++) {
        channelAmplitudes[ch] = channelSums[ch] / samplePerChannel;
      }
    }
  }
}

// 程序停止时清理资源
void stop() {
  if (activeInputLine != null) {
    activeInputLine.stop();
    activeInputLine.close();
  }
  if (audioReadThread != null) {
    audioReadThread.interrupt();
  }
  super.stop();
}

一些关键注意点:

  • 如果需要支持更多通道,只需要修改channelCount变量,并确保音频格式的通道数与设备支持的一致。
  • 不同设备支持的音频格式可能不同,你可以通过Mixer.getLineInfo()获取设备支持的所有格式,选择兼容的配置。
  • 振幅计算可以替换为峰值(记录读取周期内的最大样本值),这样视觉反馈会更灵敏。

内容的提问来源于stack exchange,提问作者matt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:12:58