需求澄清
原文件內容為 Perl 變數作用域範例,與標題「Java 顯示聲響波形圖」完全不符。本文提供正確的 Java 音頻波形繪製方案,涵蓋 PCM 數據讀取、波形計算、Swing/JavaFX 繪製、大數據優化。
核心流程
- 解碼音頻:MP3/WAV → PCM (signed 16-bit little-endian) 陣列。
- 降採樣/峰值提取:螢幕像素有限(如 1000px),需將幾十萬採樣點壓縮為每像素一個
min/max 對。
- 繪製:對稱繪製正負振幅,或單向填充面積圖。
依賴庫推薦
- TarsosDSP:純 Java 音頻解碼、特徵提取(Maven:
be.tarsos:dsp:2.5)。
- JLayer / MP3SPI:舊款 MP3 解碼。
- JavaFX Media:內建解碼,但格式支援有限。
- FFmpeg + ProcessBuilder:最強相容性,解碼為 WAV 再讀取。
完整 Swing 波形面板實作
package com.example.audio;
import javax.sound.sampled.*;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Path2D;
import java.io.File;
import java.io.IOException;
public class WaveformPanel extends JPanel {
private short[] pcmData; // 原始 PCM 數據
private int[] minMaxCache; // 降採樣後的 [min, max] 陣列,長度 = 寬度 * 2
private float zoom = 1.0f; // 水平縮放
private int startSample = 0; // 可見區域起始採樣點
public void loadAudio(File file) throws UnsupportedAudioFileException, IOException {
try (AudioInputStream ais = AudioSystem.getAudioInputStream(file)) {
AudioFormat fmt = ais.getFormat();
// 強制轉為 16-bit PCM
if (fmt.getEncoding() != AudioFormat.Encoding.PCM_SIGNED || fmt.getSampleSizeInBits() != 16) {
ais = AudioSystem.getAudioInputStream(
new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, fmt.getSampleRate(), 16,
fmt.getChannels(), fmt.getChannels() * 2, fmt.getSampleRate(), false), ais);
}
byte[] bytes = ais.readAllBytes();
pcmData = new short[bytes.length / 2];
for (int i = 0; i < pcmData.length; i++) {
pcmData[i] = (short) ((bytes[i*2+1] << 8) | (bytes[i*2] & 0xFF)); // little-endian
}
buildCache();
repaint();
}
}
/** 建立降採樣快取:每像素對應一組 min/max */
private void buildCache() {
if (pcmData == null) return;
int w = Math.max(1, getWidth());
minMaxCache = new int[w * 2];
int samplesPerPx = Math.max(1, pcmData.length / w);
for (int px = 0; px < w; px++) {
int start = px * samplesPerPx;
int end = Math.min(start + samplesPerPx, pcmData.length);
short min = 32767, max = -32768;
for (int i = start; i < end; i++) {
short v = pcmData[i];
if (v < min) min = v;
if (v > max) max = v;
}
minMaxCache[px*2] = min;
minMaxCache[px*2+1] = max;
}
}
@Override protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (minMaxCache == null) return;
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(new Color(30, 144, 255)); // DodgerBlue
int h = getHeight();
int mid = h / 2;
float scale = (h * 0.45f) / 32768f; // 留 10% 邊距
Path2D.Float path = new Path2D.Float();
int w = getWidth();
// 上半部(正振幅)
path.moveTo(0, mid);
for (int px = 0; px < w; px++) {
int max = minMaxCache[px*2+1];
float y = mid - max * scale;
path.lineTo(px, y);
}
// 下半部(負振幅,鏡像)
for (int px = w-1; px >= 0; px--) {
int min = minMaxCache[px*2];
float y = mid - min * scale; // min 為負,y > mid
path.lineTo(px, y);
}
path.closePath();
g2.fill(path);
g2.draw(path);
// 中央軸
g2.setColor(Color.GRAY);
g2.drawLine(0, mid, w, mid);
}
@Override public Dimension getPreferredSize() {
return new Dimension(800, 200);
}
// 主程式測試
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame("Audio Waveform");
WaveformPanel panel = new WaveformPanel();
f.add(panel);
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.setVisible(true);
// 載入測試檔案
try { panel.loadAudio(new File("test.wav")); }
catch (Exception e) { e.printStackTrace(); }
});
}
}
大檔案優化策略
| 策略 | 說明 |
| 記憶體映射 | 用 FileChannel.map() 讀取大檔,避免全量載入堆內存。 |
| 多解析度快取 | 預計算多層級 min/max 金字塔(如 1x, 2x, 4x...),縮放時 O(1) 切換。 |
| 虛擬化繪製 | 僅繪製可見區域採樣點,配合 AffineTransform 平移縮放。 |
| 背景執行緒解碼 | SwingWorker 或 JavaFX Task 非阻塞載入,載入中顯示進度條。 |
JavaFX 版本優勢
- 硬體加速渲染(Canvas / Path),大數據流暢度遠超 Swing。
- 內建
AudioSpectrumListener 可即時繪製頻譜/波形。
- CSS 樣式化、動畫原生支援。
// JavaFX 簡易波形繪製片段
Canvas canvas = new Canvas(800, 200);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.DODGERBLUE);
for (int px = 0; px < width; px++) {
double max = cache[px*2+1] / 32768.0;
double min = cache[px*2] / 32768.0;
double mid = height/2;
gc.fillRect(px, mid - max*mid*0.9, 1, (max-min)*mid*0.9);
}
常見格式支援對照
| 格式 | Java Sound | TarsosDSP | FFmpeg |
| WAV (PCM) | ✅ 原生 | ✅ | ✅ |
| MP3 | ❌ 需 MP3SPI | ✅ | ✅ |
| AAC/M4A | ❌ | ❌ | ✅ |
| FLAC | ❌ | ❌ | ✅ |
| OGG Vorbis | ❌ | ✅ | ✅ |