本文完整呈現一個基於 Swing 與 Java Sound API 的圖形化節拍器,涵蓋 GUI 佈局、事件監聽、MIDI 序列器編程、音軌構建與播放控制,適合進階 Java 開發者鞏固客戶端開發技能。
JFrame + BorderLayout,左側樂器名稱(BoxLayout.Y_AXIS),右側控制按鈕,中間 16×16 JCheckBox 網格(GridLayout)代表 16 條音軌 × 16 拍。ActionListener 內部類處理 Start/Stop/Tempo± 邏輯。Sequencer、Sequence、Track、MidiEvent 構建標準 MIDI 序列。public void buildGUI() {
theFrame = new JFrame("Cyber BeatBox");
theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel background = new JPanel(new BorderLayout());
background.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
// 右側按鈕盒
Box buttonBox = new Box(BoxLayout.Y_AXIS);
JButton start = new JButton("Start");
start.addActionListener(new MyStartListener());
buttonBox.add(start);
// Stop、Tempo Up/Down 同理...
// 左側樂器名稱
Box nameBox = new Box(BoxLayout.Y_AXIS);
for (String name : instrumentNames) nameBox.add(new Label(name));
// 中間 16x16 CheckBox 網格
GridLayout grid = new GridLayout(16, 16);
grid.setVgap(1); grid.setHgap(2);
mainJPanel = new JPanel(grid);
for (int i = 0; i < 256; i++) {
JCheckBox cb = new JCheckBox();
cb.setSelected(false);
checkboxsList.add(cb);
mainJPanel.add(cb);
}
background.add(BorderLayout.EAST, buttonBox);
background.add(BorderLayout.WEST, nameBox);
background.add(BorderLayout.CENTER, mainJPanel);
theFrame.getContentPane().add(background);
theFrame.pack();
theFrame.setVisible(true);
}
要點:JFrame 必須用 getContentPane().add() 或 setContentPane(),不能直接 frame.add();pack() 根據子組件優先尺寸自動調整窗口大小。
public void setUpMidi() {
try {
sequencer = MidiSystem.getSequencer();
sequencer.open();
sequence = new Sequence(Sequence.PPQ, 4); // 4 ticks per quarter note
track = sequence.createTrack();
sequencer.setTempoInBPM(120);
} catch (Exception e) { e.printStackTrace(); }
}
public void buildTrackAndStart() {
sequence.deleteTrack(track);
track = sequence.createTrack();
for (int i = 0; i < 16; i++) { // 16 條音軌
int[] trackList = new int[16];
int key = instruments[i]; // MIDI note number
for (int j = 0; j < 16; j++) { // 16 拍
JCheckBox cb = checkboxsList.get(j + 16 * i);
trackList[j] = cb.isSelected() ? key : 0;
}
makeTracks(trackList); // 將 note on/off 加入 track
track.add(makeEvent(176, 1, 127, 0, 16)); // Control Change: All Sound Off
}
track.add(makeEvent(192, 9, 1, 0, 15)); // Program Change: 結束標記
sequencer.setSequence(sequence);
sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
sequencer.start();
sequencer.setTempoInBPM(120);
}
public MidiEvent makeEvent(int cmd, int chan, int one, int two, int tick) {
try {
ShortMessage msg = new ShortMessage();
msg.setMessage(cmd, chan, one, two);
return new MidiEvent(msg, tick);
} catch (Exception e) { e.printStackTrace(); return null; }
}
public void makeTracks(int[] list) {
for (int i = 0; i < 16; i++) {
int key = list[i];
if (key != 0) {
track.add(makeEvent(144, 9, key, 100, i)); // Note On
track.add(makeEvent(128, 9, key, 100, i + 1)); // Note Off
}
}
}
cmd 對應 MIDI 狀態字節:144=Note On, 128=Note Off, 176=Control Change, 192=Program Change。Channel 9 為打擊樂專用通道。
// Tempo Up: 每次 +3%
sequencer.setTempoFactor(tempoFactor * 1.03f);
// Tempo Down: 每次 -3%
sequencer.setTempoFactor(tempoFactor * 0.97f);

界面呈現 16 行樂器 × 16 列拍子的棋盤,點擊 Start 即可循環播放勾選的節奏型,支持實時調速。
checkboxsList 狀態序列化為 JSON/Properties,下次啟動恢復。Soundbank 載入 SF2 音色庫,突破 GM 預設音色。MidiSystem.write(sequence, 1, file) 導出 .mid 檔案。JCheckBox 切換,實現即時演奏。MediaPlayer + Canvas 實現現代化 UI 與更低延遲音頻。JFrame.setDefaultCloseOperation(EXIT_ON_CLOSE) 確保關閉窗口釋放 MIDI 資源。sequencer.open() 後必須 close(),否則佔用獨佔音頻設備導致二次啟動失敗。alsa-utils、pulseaudio、soundbank.gm 是否安裝。SwingUtilities.invokeLater(() -> new BeatBox().buildGUI())。完整代碼約 200 行,涵蓋 GUI、事件、MIDI 三大核心知識點,是鞏固 Java 客戶端開發的優秀練手項目。