前言

身為一個喜歡搞創意編程的後端工程師,我一直覺得視覺只是表現形式的一半。真正讓人起雞皮疙瘩的互動裝置,通常都有聲音的參與——想想看博物館裡那些你走過去就會發出聲響的裝置,或是隨著你的動作改變音樂和燈光的互動作品。

要做到這件事,有一個工具我覺得特別適合入門:PureData(Pd)

PureData 是一個開源的視覺化程式語言,專門用來做即時的音訊處理、多媒體互動、和聲光藝術。它不像 p5.js 那樣用文字寫程式,而是用「連線」的方式把音訊處理的模組接在一起——有點像在接樂高。

這篇文章會帶你從安裝開始,一路到做出一個能用感測器控制的簡單聲光互動裝置。


PureData 是什麼?

歷史背景

PureData(通常簡稱 Pd)是由 Miller Puckette 在 1990 年代開發的。他也是 Max/MSP 的原始作者——Max/MSP 是音樂和多媒體領域最知名的商業軟體之一(一年授權要好幾百美金)。

Pd 本質上是 Max 的開源版本。雖然介面比較陽春,但核心功能不輸 Max。很多聲音藝術家、互動設計師、和新媒體藝術家都在用它。

Pd 的版本

  • Pd-vanilla:Miller Puckette 維護的原始版本,功能較基本
  • Purr Data(Pd-l2ork):加了現代化 UI 和更多外部物件
  • Pd-extended:已停止維護,但很多教學還是基於這個版本

建議從 Purr Data 開始,它的 UI 比較友善,而且內建很多實用的外部物件。

安裝

# macOS(用 Homebrew)
brew install --cask purr-data

# Ubuntu/Debian sudo apt-get install purr-data

# Windows # 從官網下載安裝包:https://agraef.github.io/purr-data/


Pd 的基本概念

視覺化程式設計

Pd 的程式不是用文字寫的,而是用物件(Object)連線(Patch Cord) 組成的。

一個基本的 Pd patch 看起來像這樣:

[osc~ 440]      ← 產生 440Hz 的正弦波
    |
[*~ 0.3]        ← 乘以 0.3(降低音量)
    |
[dac~]          ← 輸出到音效卡(Digital-to-Analog Converter)

兩種資料流

Pd 有兩種資料流:

  1. 控制訊號(Control):離散的數值和訊息(整數、浮點數、符號)
  2. 音訊訊號(Audio):連續的音訊串流(通常 44100 samples/sec)

帶有 ~ 的物件處理音訊訊號,不帶的處理控制訊號:

[metro 500]     ← 控制訊號:每 500ms 發送一個 bang
[osc~ 440]      ← 音訊訊號:440Hz 正弦波

基本物件

| 物件 | 功能 | 類型 |
|——|——|——|
| [osc~ freq] | 正弦波振盪器 | 音訊 |
| [noise~] | 白噪音 | 音訊 |
| [phasor~ freq] | 鋸齒波 | 音訊 |
| [*~ value] | 乘法(音量控制) | 音訊 |
| [+~ value] | 加法 | 音訊 |
| [dac~] | 音訊輸出 | 音訊 |
| [adc~] | 音訊輸入(麥克風) | 音訊 |
| [metro ms] | 定時觸發 | 控制 |
| [random max] | 隨機數 | 控制 |
| [print] | 印出值(除錯用) | 控制 |


第一個 Patch:會唱歌的 Pd

讓我們做一個簡單的音調產生器。在 Pd 中,你需要:

  1. 打開 Pd
  2. 按 Ctrl+1 建立新物件
  3. 把以下物件放到畫布上,然後用滑鼠連線

Patch 1:基本音調

[440(                    ← 訊息框:點擊會送出 440
   |
[osc~]                   ← 正弦波振盪器
   |
[*~ 0.2]                 ← 降低音量,保護耳朵!
   |
[dac~]                   ← 輸出到喇叭

點擊 [440( 訊息框,你應該會聽到一個 A4 音調。

Patch 2:可調頻率的音調

[hslider]                ← 水平滑桿(0~127)
   |
[* 10]                   ← 乘以 10,範圍變成 0~1270
   |
[+ 100]                  ← 加 100,範圍變成 100~1370 Hz
   |
[osc~]
   |
[*~ 0.2]
   |
[dac~]

現在你可以用滑桿控制頻率了。

Patch 3:簡單的合成器

[notein]                 ← 接收 MIDI 輸入
|      |
|   [mtof]               ← MIDI 音符轉頻率
|      |
|   [osc~]
|      |
|   [*~]─────┐
|      |      |
[/ 127]       |           ← 力度轉 0~1 的音量
   |          |
   [sig~]─────┘           ← 控制訊號轉音訊訊號
      |
   [dac~]

這個 patch 接收 MIDI 鍵盤的輸入,把 MIDI 音符轉成頻率,力度轉成音量。如果你有 MIDI 鍵盤,接上去就能彈了。


音訊處理基礎

濾波器

濾波器是聲音設計的核心工具:

[noise~]                 ← 白噪音(包含所有頻率)
   |
[lop~ 500]               ← Low-pass filter,只讓 500Hz 以下通過
   |                         → 聲音變得「悶悶的」
[*~ 0.3]
   |
[dac~]

常用的濾波器:

  • [lop~]:低通(Low-pass),讓低頻通過
  • [hip~]:高通(High-pass),讓高頻通過
  • [bp~]:帶通(Band-pass),只讓特定頻率範圍通過
  • [vcf~]:電壓控制濾波器,可以即時改變截止頻率

延遲效果

[adc~]                   ← 麥克風輸入
   |
[delwrite~ myDelay 1000] ← 寫入 1 秒的延遲緩衝
   |
[delread~ myDelay 300]   ← 讀取 300ms 前的聲音
   |
[*~ 0.5]                 ← 延遲訊號的音量
   |
[+~]─────────[adc~]      ← 混合原始和延遲訊號
   |
[dac~]

包絡線(Envelope)

讓聲音有「起音-持續-消退」的變化,而不是突然開關:

[bang(                   ← 觸發
   |
[vline~]                 ← 包絡線產生器
   |                       接收 "目標值 時間ms" 的訊息
[*~]─────[osc~ 440]
   |
[dac~]

--- 觸發包絡線的訊息序列 --- [0, 1 10, 0.7 100, 0 500( ← 立刻到 0 ← 10ms 升到 1(Attack) ← 100ms 降到 0.7(Decay/Sustain) ← 500ms 降到 0(Release)


感測器輸入:Arduino 整合

這裡是事情開始變得有趣的地方——把實體感測器接上 Pd。

硬體需求

  • Arduino(Uno、Nano 都可以)
  • 各種感測器:超音波距離感測器、光敏電阻、加速度計等
  • 幾條杜邦線

Arduino 端:Firmata 協議

最簡單的方式是在 Arduino 上燒錄 Firmata 韌體。Firmata 是一個標準化的通訊協議,讓電腦可以直接讀寫 Arduino 的腳位:

// Arduino IDE: File → Examples → Firmata → StandardFirmata
// 直接上傳就好,不需要改任何程式碼

Pd 端:Pduino

Pd 有一個外部物件叫 Pduino,可以跟 Firmata 通訊:

[open /dev/ttyUSB0(      ← 打開 Arduino 的序列埠
   |
[arduino]                 ← Pduino 物件
   |
[route analog]            ← 篩選類比輸入
   |
[route 0]                 ← 篩選第 0 號類比腳位(A0)
   |
[/ 1023]                  ← 正規化到 0~1
   |
[* 1000]                  ← 映射到 0~1000 Hz
   |
[+ 100]                   ← 偏移到 100~1100 Hz
   |
[osc~]
   |
[*~ 0.2]
   |
[dac~]

這樣你就能用一個旋鈕(接在 A0)來控制音調了。

另一種方式:Serial 直接通訊

如果不想用 Firmata,也可以自己寫 Arduino 程式,用 Serial 送資料:

// Arduino 端
const int SENSOR_PIN = A0;

void setup() { Serial.begin(9600); }

void loop() { int value = analogRead(SENSOR_PIN);

// 送出格式:sensor_id value; Serial.print("0 "); Serial.print(value); Serial.println(";");

delay(20); // 50 Hz 更新率 }

--- Pd 端 ---
[comport 0 9600]          ← 開啟序列埠
   |
[zl group 32]             ← 收集字元直到分隔符
   |
[list trim]               ← 清理
   |
[route 0]                 ← 篩選感測器 ID
   |
[unpack f]                ← 解包數值
   |
... 連到音訊處理 ...

聲光同步:Pd + 視覺

方法 1:Pd + GEM

GEM(Graphics Environment for Multimedia)是 Pd 內建的 OpenGL 圖形函式庫:

[gemhead]                 ← GEM 的渲染起點
   |
[color 1 0 0]             ← 紅色
   |
[sphere 1]                ← 畫一個球
   |
[gemwin]                  ← GEM 視窗

--- 用音訊控制視覺 --- [osc~ 2] ← 2Hz 的正弦波 | [snapshot~] ← 音訊轉控制訊號 | [* 2] | [+ 1] | [size $1 $1 $1( ← 控制球的大小 | [gemhead]

方法 2:Pd + Processing/p5.js(OSC 通訊)

我個人比較推薦的做法是:Pd 處理聲音,p5.js 處理視覺,兩者用 OSC 通訊

OSC(Open Sound Control)是一個網路通訊協議,專門為即時音樂/多媒體設計:

--- Pd 端:送出 OSC 訊息 ---
[osc~ 440]
   |
[env~]                   ← 追蹤音量包絡
   |
[oscformat /audio/level] ← 打包成 OSC 訊息
   |
[oscnet_send localhost 9000]  ← 送到 p5.js
// p5.js 端:接收 OSC 訊息(使用 osc.js 函式庫)
const osc = new OSC();
osc.open({ port: 9000 });

let audioLevel = 0;

osc.on('/audio/level', (message) => { audioLevel = message.args[0]; });

function draw() { background(20);

// 根據音訊音量改變視覺 let size = map(audioLevel, 0, 1, 50, 400); let hue = map(audioLevel, 0, 1, 200, 360);

fill(hsl(${hue}, 80%, 60%)); noStroke(); circle(width/2, height/2, size); }

這樣你就能做到:感測器 → Arduino → Pd(聲音處理)→ OSC → p5.js(視覺化) 的完整互動流程。


實作:互動聲光裝置

讓我們把以上的知識整合,設計一個簡單的互動裝置:

概念

  • 超音波感測器偵測觀眾的距離
  • 距離近 → 高頻音調 + 亮色
  • 距離遠 → 低頻音調 + 暗色
  • 加上延遲和混響效果,讓聲音有空間感

Arduino 端

// 超音波感測器 HC-SR04
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;

void setup() { Serial.begin(115200); pinMode(TRIG_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT); }

void loop() { // 發射超音波 digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2); digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIG_PIN, LOW);

// 計算距離(cm) long duration = pulseIn(ECHO_PIN, HIGH); float distance = duration * 0.034 / 2.0;

// 限制在有效範圍 distance = constrain(distance, 5, 200);

// 送出資料 Serial.println(distance);

delay(50); }

Pd Patch(概念描述)

--- 距離輸入 ---
[comport 0 115200]
   |
[atof]                    ← 字串轉浮點數
   |
[clip 5 200]              ← 限制範圍
   |
[smooth 20]               ← 平滑化(自訂 abstraction)
   |                |
   |            [oscformat /distance]
   |                |
   |            [oscnet_send localhost 9000]  ← 送給 p5.js
   |
--- 頻率映射 ---
[scale 5 200 800 100]     ← 距離 5-200cm → 頻率 800-100Hz
   |
[osc~]                    ← 正弦波
   |
--- 音量映射 ---
[scale 5 200 0.4 0.05]    ← 近的時候大聲,遠的時候小聲
   |
[*~]
   |
--- 延遲效果 ---
[delwrite~ delay1 500]
[delread~ delay1 200]
   |
[*~ 0.3]                  ← 延遲音量
   |
[+~]                      ← 混合原始和延遲
   |
--- 簡單混響 ---
[rev3~ 80 60 70 0.2]      ← Pd 內建的混響
   |
[*~ 0.7]
   |
[dac~]

p5.js 端

// 接收距離資料,產生對應的視覺
let distance = 100;
let particles = [];

function setup() { createCanvas(windowWidth, windowHeight);

// 連接 OSC(需要 WebSocket bridge) connectOSC(); }

function draw() { background(20, 30);

// 根據距離產生粒子 let rate = map(distance, 5, 200, 10, 1); for (let i = 0; i < rate; i++) { particles.push({ x: width / 2 + random(-50, 50), y: height / 2 + random(-50, 50), vx: random(-2, 2), vy: random(-3, -1), life: 255, size: map(distance, 5, 200, 15, 3), hue: map(distance, 5, 200, 0, 240), }); }

// 更新和繪製粒子 for (let i = particles.length - 1; i >= 0; i--) { let p = particles[i]; p.x += p.vx; p.y += p.vy; p.life -= 3;

if (p.life <= 0) { particles.splice(i, 1); continue; }

noStroke(); fill(hsla(${p.hue}, 80%, 60%, ${p.life / 255})); circle(p.x, p.y, p.size); }

// 中心圓:大小隨距離變化 let centerSize = map(distance, 5, 200, 300, 50); let centerHue = map(distance, 5, 200, 0, 240); fill(hsla(${centerHue}, 70%, 50%, 0.3)); circle(width / 2, height / 2, centerSize); }


學習路線建議

第一階段:聲音基礎(1-2 週)

  1. 安裝 Purr Data
  2. 做出基本的音調產生器
  3. 理解控制訊號和音訊訊號的差別
  4. 試玩濾波器和延遲效果

第二階段:MIDI 和互動(2-3 週)

  1. 接上 MIDI 鍵盤(或用虛擬 MIDI)
  2. 做一個簡單的合成器
  3. 加上包絡線(ADSR)
  4. 試試序列器(Sequencer)

第三階段:感測器整合(2-3 週)

  1. Arduino + Firmata + Pduino
  2. 超音波感測器控制音調
  3. 光敏電阻控制濾波器
  4. 加速度計控制效果參數

第四階段:聲光同步(2-4 週)

  1. 學會 OSC 通訊
  2. Pd + p5.js 的整合
  3. 做一個完整的互動裝置
  4. 展覽!

小結

PureData 是一個低調但強大的工具。它可能沒有 Ableton Live 那麼漂亮,沒有 Max/MSP 那麼精緻,但它是免費的、開源的、跨平台的,而且社群已經活躍了超過 25 年。

對一個做視覺創作的後端工程師來說,加入聲音的維度會讓你的作品有質的飛躍。視覺只能看,但聲光結合的裝置可以讓人沉浸

而且老實說,Pd 的「接線」方式對工程師來說其實很直覺——它就像是把函式呼叫和資料流視覺化了。如果你能理解 Unix pipe,你就能理解 Pd。

延伸閱讀