open-audiovisual
搜尋 GitHub

p5.js · 一行

p5.js 一行接上

同一行還能出聲音、開一台鼓機(節奏可以用一行字寫)、聽麥克風,也能把畫布連同聲音錄成影片。每一樣都像讀滑鼠:讀一個變數,寫一個函式。

oav-p5 0.8 · 框架 0.1

現場試玩:範例 15 → 手冊裡的同一章 和 Strudel 一起用 GitHub 上的 p5/ ↗

那一頁的草圖就是普通的 p5.js,第一行是這一行。點 🎹、轉旋鈕、敲打擊墊。🔊、🥁、⏺ 也在同一個面板裡。

現成草圖: 粒子 · OP Configurator ↗ · 麥克風 · 彈出聲音 · 鼓機與小節 · 一行節奏 · 看得見的節奏 · 節奏實驗台 · 錄成影片 · Lag、包絡與示波器 · 會動的數值 · 跟 Strudel 一起 · 音名

任何一份 p5.js 草圖(OpenProcessing、p5 編輯器、你自己的網頁)在最前面加一行,就能用 MIDI 控制器、螢幕上的控制器或手機來彈:

js
document.head.prepend(Object.assign(document.createElement('script'), { src: 'https://openaudiovisual.com/p5/oav-p5.js' }));

在 OpenProcessing 也可以不寫這行,改把網址貼到右側 Libraries 的 +(自訂函式庫)。p5 1.x、2.x 的全域模式都能用, setup() 裡不用呼叫任何東西。帶著這一行、現在就能彈的草圖:範例 15。

像讀滑鼠一樣讀它#

js
function draw() {
  background(knob1 * 255);                 // knob 1, 0..1, like mouseX
  circle(width / 2, height / 2, 50 + knob2 * 300);
}

function notePressed(note, velocity) {     // like keyPressed(): any key, or a pad
  circle(random(width), random(height), velocity * 100);
}
讀值
knob1 … knob8、knob(n)0 到 1,編號照控制器上印的順序
fader1 … fader8、fader(n)0 到 1
pad1 … pad16、pad(n)按著的時候是力度,放開回到 0
note、velocity最後按的鍵(MIDI 編號)和多用力(0 到 1)
noteIsPressed、noteIsDown(60)跟 keyIsPressed、keyIsDown() 一樣
寫這個函式什麼時候被叫
notePressed(note, velocity)、noteReleased(note)按下、放開一個鍵(打擊墊也算,除非你寫了 padPressed)
knobMoved(n, value)、faderMoved(n, value)轉旋鈕、推推桿
padPressed(n, velocity)、padReleased(n)打擊墊按下、放開

旋鈕 1 就是你手上那台的旋鈕 1:控制器設定檔記著每顆旋鈕送哪個訊息(MiniLab 3 的旋鈕 1 是 CC 74, LPD8 是 CC 70)。OAV 沒有設定檔的控制器,你先轉的那顆就是 knob1,再轉的是 knob2。草圖裡自己已經有 note、velocity、knob1 這些名字的話,原本的會留著。

音名#

收音符號碼的地方,也都能直接寫音名(0.8):

js
function draw() {
  if (noteIsDown('C4')) circle(width / 2, height / 2, 100);   // the same as noteIsDown(60)
}

function mousePressed() {
  midi.play('C#4', 0.8);                    // sends note 61, as a key would
}
寫得到
noteNumber('C4')60;給數字就原樣回來,夾在 0 到 127
noteName(61)'C#4';noteName(61, { flats: true }) 是 'Db4',noteName(61, { octave: false }) 是 'C#'
noteFreq('A4')、noteFreq(69)440(赫茲),給自己合成聲音的草圖用;noteFreq(69.5) 是高四分之一音
noteIsDown('C4')、midi.isDown('C4')、midi.play('C#4', 0.8)跟寫 60、61 一樣

音名是字母 A 到 G(大小寫都行)、可有可無的 # 或 b(♯、♭ 也行),再加八度數字:C4、c#4、Db3、F♯2,從 C-1(0)到 G9(127)。C4 是 60,跟課程模組用的科學音高記法一致;有些軟體(Ableton、Yamaha)把 60 叫 C3,從那邊抄來的音名會差一個八度。這也是 Strudel 的寫法(c4 e4 g4,升記號 #,降記號 b)。其他寫法,像 H4、C-2,會停下來並跳出一張一行的卡片,告訴你正確怎麼寫。 notePressed(note, velocity) 拿到的仍然是號碼:noteName(note) 把它寫回音名。

搭配 OP Configurator#

OP Configurator 3000(OPC,OpenProcessing 內建)幫變數取名字、定範圍。頁面上有這一行,控制器就照宣告順序接上這些變數,不用一個一個對應:

js
OPC.slider('radius', 60, 10, 300);   // knob 1
OPC.slider('speed', 1, 0, 5, 0.1);   // knob 2
OPC.toggle('trails', true);          // pad 1 flips it

旋鈕依序接 slider,推桿接在旋鈕後面,打擊墊接 toggle 和 button。轉旋鈕時 OPC 面板上的滑桿跟著動, parameterChanged() 照常會被叫。OpenProcessing 的 OPC 面板自己也能學實體控制器(面板上的 MIDI 開關),一看到它的對應在動,實體控制器就交給它,這一行繼續接螢幕上的控制器和手機(這兩個 OpenProcessing 看不到)。 OPC 變數不要取 p5 已經有的函式名稱(hue、fill),p5 1.x 會撞名。

一顆旋鈕只做一件事。 草圖自己也直接讀 knob1 的話,旋鈕 1 就歸草圖:OPC 的第一條拉桿不再跟著它(改在 OPC 面板上拉),其他旋鈕照接,並跳一次提示說明。🎹 面板的第一行就是接線表,例如「接線:旋鈕1 → knob1(草圖;radius 改用滑桿) · 旋鈕2 → speed(OPC)」。

麥克風#

同一行也接得上麥克風,草圖讀它的方式一樣:讀變數。

js
function draw() {
  background(230, 40, 10);
  fill(200 + bass * 160, 70, 100);                      // bass: the low end, 0..1
  circle(width / 2, height / 2, 50 + micLevel * 300);   // micLevel: how loud, 0..1
}

function beatDetected(level) {                          // like keyPressed(): a beat just landed
  // flash, spawn, jump to the next scene
}
讀值
micLevel0 到 1,聽起來多大聲:房間本身的底噪是 0,說話大約 0.4–0.6,大喊接近 1
bass、mid、treble0 到 1,20–250 Hz、250–2000 Hz、2000–8000 Hz 三段的能量
beat每個拍子有一格是 true,像按著滑鼠時的 mouseIsPressed
micIsOn麥克風有沒有在跑
startMic()、stopMic()用程式開、關(回傳 promise)
micSensitivity(2)靈敏度變兩倍;micSensitivity() 回傳現在的值
寫這個函式什麼時候被叫
beatDetected(level)一個拍子:音量比房間剛才的樣子突然跳起來(level 0 到 1)
kickDetected(level)、snareDetected(level)、hatDetected(level)低、中、高頻段各自打一下(20–120 Hz、150–800 Hz、6–14 kHz)

草圖開口要,它才開。 頁面載入時什麼都不會要。草圖第一次讀到 micLevel、bass、mid、treble、beat,或呼叫 startMic(),瀏覽器才會問你要不要給麥克風;答案回來之前,每個值都是 0。只寫了 beatDetected()、什麼值都沒讀的草圖,會在第一次點擊或按鍵時開。🎹 面板裡的 🎤 按鈕可以手動開關、顯示音量;瀏覽器擋下麥克風時,它會寫出來(從網址列左邊重新允許之後,再按一次)。

聲音:彈什麼就聽到什麼#

js
function setup() {
  createCanvas(windowWidth, windowHeight);
  startSound('piano');          // every key is heard: a keyboard, the 🎹 on screen, a phone
}
呼叫/讀值
startSound(instrument)開聲音(預設 'piano');開著的時候再叫一次就換樂器
stopSound()關聲音
soundIsOn現在彈下去有沒有聲音

樂器來自 @openav/sound:piano(Salamander 平台鋼琴,第一次要載 2 MB 的取樣,載入中先用電鋼琴頂著,載不到就一直用電鋼琴)、epiano、organ、strings、choir、harp、marimba、vibraphone、xylophone、 glockenspiel、music-box,還有 synth/… 一系列合成器。🎹 面板上的 🔊 可以開關聲音,打開後有樂器選單和載入進度。

鼓機#

js
function setup() {
  startDrums('breakbeat', 110);   // a 16-step grid on screen; click a cell to change the beat
}

function kickDetected(level) {    // the microphone's own callback
  // swell, flash, spawn
}

function drumHit(lane, velocity) {   // every hit: 'kick', 'snare', 'hat' or 'clap'
}
讀值
drumsAreOn鼓機有沒有在打
drumStep0 到 15,剛打到第幾格
drumBar1、2、3……,從 startDrums() 開始數第幾小節
drumBpm速度(格子上的滑桿也會改它)
drumPhase0 到 1,這一小節走到哪,連續的、照聽到的時間算:rotate(drumPhase * TWO_PI) 一小節轉一圈
beatPhase0 到 1,這一拍(四分音符)走到哪:1 - beatPhase 每一拍一開始最大,慢慢縮到下一拍
startDrums(pattern, bpm)、stopDrums()開始(打著的時候再叫一次就換節奏、換速度)、停
寫這個函式什麼時候被叫
drumHit(lane, velocity)每打一下,每一種鼓都叫(一行節奏裡的碎音鈸、疊音鈸、中鼓也是)
kickDetected(level)、snareDetected(level)、hatDetected(level)大鼓;小鼓、拍手或鼓邊;鈸(閉鈸、開鈸、踩鈸)
beatDetected(level)大鼓或小鼓,每一格最多一次(beat 也會有一格是 true)
rhythmChanged(src, how)那一行換了:startDrums()、點格子、打字、↶(0.7)

寫給麥克風的草圖,換成鼓機照樣動。 鼓機每打一下,叫的就是麥克風會叫的那幾個函式,所以麥克風草圖不用改就跟著鼓機動。草圖開了鼓機之後,只是寫了 kickDetected() 不會在第一次點擊時去開麥克風(有讀 micLevel 的草圖還是會問,那時兩邊都會叫它)。

畫面再重,鼓都不歪。 鼓是照音訊時鐘提前排好的:Web Worker 裡的心跳每 25 毫秒醒一次,把接下來 0.1 秒內要打的鼓,照精確的時間交給鼓組。用鼓組自己的輸出量(draw() 每一格故意忙 30 毫秒):大鼓到大鼓的間隔,差異是 0.000 毫秒; 0.5 版在畫面輪到那一格才打,差異是 15 毫秒。回呼還是在畫面的某一格叫,叫的是聲音聽得到的那一格(排定的時間加上瀏覽器回報的輸出延遲),在 draw() 之前,順序跟以前一樣。

一行節奏#

js
function setup() {
  startDrums("bd*4, [~ sd]*2, hh*8", 110);   // a kick on every beat, a snare on 2 and 4, eight hats
}

不是節奏名稱的字串,就當成一行節奏;節奏名稱、自己寫的格子、數字,行為跟以前一模一樣。這一行字會顯示在格子上方,正在響的那個字會亮起來、再淡掉。

寫意思例
一個字一種鼓:bd 大鼓、sd 小鼓、hh 鈸、oh 開鈸、cp 拍手、rim 鼓邊、lt mt ht 中鼓、cr 碎音鈸、rd 疊音鈸、cb 牛鈴"bd sd bd sd"
~休息"bd ~ sd ~"
空白幾個字平分一小節"bd hh sd hh"
[ ]一組擠進一格"bd [sd sd]"
*n在那一格打 n 下"hh*8"
,再疊一行"bd*4, hh*8"
< >每小節輪一個"<bd*4 [bd ~ bd ~]>"
(k,n)n 格裡平均打 k 下"bd(3,8)"

完整的寫法在 @openav/pattern,跟鼓機一起從 oav-p5.js 旁邊載入。格子上沒有那一軌的鼓(碎音鈸、疊音鈸、中鼓、開鈸)會多一列;不在 16 格上的音(三連音)畫在它落在那一格裡的位置。startDrums("…") 寫錯了會跳一張卡(照頁面語言)說第幾個字、為什麼、怎麼改,鼓機先打 'four on floor';只寫 'x...x...'(格子的一軌)會提醒你放進 { kick: … }。

從 strudel.cc 複製來的也能用:startDrums('s("bd*4, [~ sd]*2").fast(2)') 打的是 [bd*4, [~ sd]*2]*2。讀得懂 s("…")、sound("…")("、'、` 都行;好幾個 $: s(…) 會疊在一起)、.fast(n)、.slow(n);setcpm(110/4) 或 setcps(…) 在 startDrums() 沒給速度時拿來當速度。.bank("…") 先略過(OAV 用自己合成的鼓組),鏈上其他的(.jux(rev)、 .every(…)、.gain(…))會跳一張卡說 OAV 還不支援、請按「在 Strudel 打開」;其餘照打。

一行節奏的寫法來自 TidalCycles/Strudel(strudel.cc),OAV 是依公開文件獨立實作。

格子和那一行互相改#

0.7 起,格子上方那一行一直都在(節奏名稱也會顯示它的格子寫成一行的樣子:'four on floor' 就是 bd*4, [~ sd]*2, hh*8),兩個方向都能改:

js
function rhythmChanged(src, how) {   // how: 'code' · 'cell' · 'type' · 'undo' · 'menu'
  line = src;                        // e.g. draw the line on the canvas, so a recording shows it
}

OAVp5.drums.src 是正在打的那一行,OAVp5.drums.edit("…") 像打字一樣換一行,OAVp5.drums.undo() 回上一版。按鍵換節奏、上一步、開到 Strudel 的草圖:p5/examples/rhythm-lab/。

把節奏畫在畫布上#

鼓機卡是網頁的一部分、不在畫布上,所以錄影錄不到它。drawRhythm() 把節奏畫在畫布上:

js
function draw() {
  background(0);
  drawRhythm(20, 20, 360, 90, 'grid');          // the sixteen steps, a playhead
  drawRhythm(20, 130, 300, 300, 'circle');      // a ring of 16 per drum, a hand turned by drumPhase
  drawRhythm(340, 130, 300, 300, 'spiral', { bars: 4, colors: { kick: '#ff7b54' } });   // the last 4 bars, wound outwards
}

drawRhythm(x, y, 寬, 高, 樣式, 選項):樣式是 'grid'、'circle' 或 'spiral';每一下在聽到的那一刻亮起來(跟 drumPhase 同一個時鐘),三連音畫在它落下的位置。選項:colors({ kick: '#f66', … },p5 認得的顏色都行)、color (全部同一色)、off(空的格子)、playhead(顏色,或 false 不畫)、bg、bars(螺旋幾小節)。在 push()/pop() 裡用 p5 自己的圖形畫,2D 和 WEBGL、p5 1.x 和 2.x 都行;WEBGL 的 (0, 0) 在畫布正中間: drawRhythm(-width / 2, -height / 2, width, height, 'circle')。不給位置就畫在底部一條('grid')或正中間一個正方形。三種一起用的草圖:p5/examples/rhythm-visual/。

時間軸:小節與 cue#

鼓機也是時鐘:一小節十六格,從 startDrums() 開始數。宣告第幾小節要發生什麼:

js
function setup() {
  startDrums('breakbeat', 110);
  cue(1, 'calm', 12);             // bar 1, and again every 12 bars
  cue(5, 'bloom', 12);
  cue(9, 'storm', 12);
}

function cueFired(name, bar) {    // or read cueName in draw()
  look = SCENES[name];
}
呼叫/讀/定義
cue(bar, name, every)第 bar 小節(1 是第一小節,4.5 是第 4 小節的一半);有給 every 就每隔那麼多小節再來一次
cueName最近一次觸發的 cue(第一次之前是 '')
cueFired(name, bar)到了某個 cue 的那一小節
barStarted(bar)、stepStarted(step, bar)每一小節的第一格;每一格

同樣的 cue() 叫兩次不會多一個,所以放在 draw() 裡也可以。同一格上先照宣告順序觸發 cue,再來 barStarted、stepStarted、那一格的鼓。只要時鐘不要聲音:startDrums({})。全部用上的草圖:p5/examples/drums/;一行節奏加 drumPhase、beatPhase:p5/examples/rhythm/;把節奏畫在畫布上:p5/examples/rhythm-visual/。

數值隨時間:lag、env、speed、spring、trail#

旋鈕和 micLevel 是跳著變的;好看的是它在兩次跳動之間怎麼動。TouchDesigner 用 CHOP 做這件事;這裡有同樣的運算子,在 draw() 裡一個一行,不用宣告;秒就是秒(30 fps 和 144 fps 畫出同一條曲線):

js
circle(width / 2, height / 2, 50 + lag(micLevel, 0.03, 0.4) * 300);   // a VU meter: opens at once, closes slowly
scale(1 + env('kick', 0.01, 0.3));                                     // the drum machine: every kick swells it, gone in 0.3 s
rotate(spring(knob1, 100, 6) * TWO_PI);                                // flick a knob: it overshoots and settles

第一行像 VU 表:一出聲立刻撐開、安靜下來慢慢收。第二行:鼓機每打一下大鼓就撐開一下,0.3 秒收回。第三行:旋鈕一甩,會衝過頭再彈回來停下。

TouchDesigneroav-p5
Lag CHOP(Lag 1/Lag 2)lag(value, up, down)跟著 value 走:往上用 up 秒、往下用 down 秒(不給 down 就等於 up;預設 0.1)
Trigger CHOP(attack、decay)env('kick', attack, decay)、trigger('boom')0..1:每個事件之後 attack 秒升到 1,decay 秒回到 0(預設 0.01、0.3)。事件:'kick' 'snare' 'hat' 'beat'(麥克風或鼓機,跟 kickDetected 聽到的是同一套)、'note'(任何琴鍵)、'bar' 'step'(鼓機)、cue 的名字、草圖自己的 trigger('boom');也可以給開關:env(mouseIsPressed, …)
Speed CHOPspeed(rate)每秒加 rate:rotate(speed(TWO_PI)) 一秒轉一圈
Spring CHOPspring(value, stiffness, damping)像掛在彈簧上的東西一樣跟著走(100、6:彈兩三下;damping 20:不彈)
Trail CHOPtrail(value, seconds)最近 seconds 秒(2)的值,一個陣列,每秒 60 個,舊的在前、最新的在最後
Slope CHOPslope(value)每秒變化多少
Count CHOPcount(beat)、count(beat, 4)、count('kick')打開過幾次;給 4 就 0 1 2 3 0 … 循環;給名字:那個事件到現在幾次
CHOP 的檢視器scope(value, 'label')頁面角落的小示波器(見下面)
drawTrail(values, x, y, w, h)同樣的曲線畫在畫布上,作品裡看得到、錄影也錄得到

秒數跟 TouchDesigner 一樣。 lag 的秒數就是 Lag CHOP 的秒數:大約是跟上一次跳動 90% 所需的時間,所以這裡的 lag(x, 0.1) 動起來就像 Lag CHOP 填 0.1(Lag CHOP 文件)。底下是一個時間常數為「秒數 ÷ 2.3(ln 10)」的單極平滑;@openav/mapping 的 smooth 仍照它原本 63% 的時間常數。attack、decay 兩邊都是長度。

不用宣告。 draw() 裡第一個 lag() 記第一個 lag 的值、第二個記第二個,每一幀都一樣;spring()、speed()、trail() … 各自分開數。把它放進 if,或放在長度會變的迴圈裡,順序就會錯開:畫面上會有一張提示說明,之後 oav-p5 改成照「寫在哪一行」對應。最穩的是給它名字,最後一個參數放字串:lag(p.size, 0.1, 0.5, 'p' + p.id)。有名字的在哪裡呼叫都是同一個,draw()、setup()、 mousePressed() 都行;沒名字的在 draw() 外面沒有東西可以對,直接回傳原值(speed 回 0、trail 回 []),也會有提示。 env()、trigger()、count('kick') 是跟著事件走的,在哪裡都能用。草圖自己有 speed 或 count 變數的話照用自己的; OAVp5.chop.speed(…) 是同一個函式換個地方拿。

看得見的值。 scope(value, 'label') 會把 value 原樣回傳,所以可以直接包住一段算式: circle(x, y, scope(lag(micLevel, 0.03, 0.4), 'lag') * 300)。每個標籤是一條線,畫在 🎹 對面角落的小示波器裡:最近 4 秒和現在的值;旁邊再放一個 scope(micLevel, 'mic'),就看得到 lag 做了什麼。它在網頁上、不在畫布上:不會錄進影片和 📷,也不會擋到點擊; hints=off 或 scope=off 就不顯示。要畫進作品裡:stroke(255); drawTrail(trail(micLevel, 3), 0, height - 100, width, 100) (或 drawTrail(micLevel, …),它自己記 2 秒):框的底是 0、頂是 1(h 後面可以給 lo、hi),用草圖的 stroke()(noStroke() 時用白色)。

數學在 @openav/chop,第一次呼叫時從這支檔案旁邊載入;載好之前(幾幀)每個函式都直接回傳原值。參數意義參考 TouchDesigner 官方文件(Derivative),程式由 OAV 自行實作。原值和 lag 後並排、配 scope 的範例:p5/examples/lag/;用 env、spring、speed、count、slope 做一個大鼓撐開的圓盤(只靠 startDrums(),不用麥克風):p5/examples/chop/。

錄影:畫面和聲音一起錄#

js
function keyPressed() {
  if (key === 'r') recordingIsOn ? stopRecording() : startRecording();   // startRecording(10): ten seconds, then it stops
}

function recordingSaved(info) {   // info.name, info.seconds, info.bytes, info.format ('mp4' or 'webm')
}
呼叫/讀/定義
startRecording()、startRecording(秒數)、startRecording({ seconds, fps, mic, name, canvas, bitrate })錄下畫布和作品的聲音;給秒數就錄那麼久自動停;一段最長 5 分鐘。canvas 指定要錄哪個畫布(p5 的畫布或 createGraphics(),預設是頁面上最大的 p5 畫布),bitrate 是每秒位元數(預設依畫布大小和幀率算:4 到 16 Mb/s,幀率高過 30 時最高 24 Mb/s)
stopRecording()停止,檔案自動下載
recordingIsOn現在有沒有在錄
recordingSaved(info)存好了:name、seconds、bytes、format、width、height、audio、sources、warnings、blob、url

🎹 面板上的 ⏺ 不寫程式也能開始、停止;錄影中 🎹 按鈕上有紅點和時間(點紅點就停)。📷 把畫布存成一張 PNG。

三種輸入#

輸入怎麼用
實體控制器Chrome、Edge、Firefox 的 Web MIDI,瀏覽器問一次就記住
螢幕上的控制器按 🎹 打開 <oav-controller>,接上哪台控制器就換成哪台
手機面板上的 📱 顯示 QR code,手機遙控頁的 MIDI 分頁就能彈這份草圖

三種輸入進來都是同一串 MIDI,草圖分不出是誰彈的(想知道的話看 midi.last.source)。Safari 和 iPad 沒有 Web MIDI,螢幕上的控制器和手機照樣能用。草圖自己寫了 Web MIDI(navigator.requestMIDIAccess、WebMidi.js)的話,螢幕和手機會變成多出來的一個輸入,名字叫 OAV Virtual MIDI。

別的程式是第四種輸入(0.7)。程式送進 OAV Virtual MIDI 輸出的東西——Strudel 的 .midi('OAV Virtual MIDI')——會在它標的那個時刻到草圖(midi.last.source 是 virtual-out);外部 MIDI 時鐘推動 drumPhase、drumBar 和鼓機; link=<房間> 把另一個分頁、另一台電腦的程式接進這份草圖。全部寫在和 Strudel 一起用。

出狀況的時候#

彈的時候沒人會去看 OpenProcessing 畫布下面的 console,所以出狀況會直接寫在畫面上:🎹 旁邊一張小卡片,不擋畫布中間,用瀏覽器的語言,同一則只跳一次,按 × 關掉。每張都寫三件事:發生什麼、為什麼、下一步按哪裡,console 也同步寫一樣的話。

?oav_hints=off(或 window.OAV_P5 = { hints: 'off' })只寫在 console;OAVp5.hints 列出說過的提示。

🩺 體檢。 🎹 面板上的 🩺(或 OAVp5.check())把版本、頁面、瀏覽器、麥克風、MIDI、聲音、鼓機、錄影(這裡能錄 MP4 還是 WebM、上一段)、接線、草圖自己的名字、目前為止的提示整理成一張卡,截圖貼到 Discord 就能遠端看;有「複製文字」按鈕,也連到完整的裝置檢查。

在 Hydra 裡用#

Hydra 有自己的載入函式;同一個檔在那裡也能用,Hydra 的箭頭函式每一格都會重讀變數:

js
await loadScript('https://openaudiovisual.com/p5/oav-p5.js')
osc(10, 0.1, () => knob1 * 2).out()                     // knob 1 sets the colour offset
js
osc(10, 0.1, () => 1 + micLevel * 3).out()              // the microphone, asked for when this line first runs

note、velocity、knob1 … knob8、fader1 …、pad1 …、drumStep,還有 🎹 螢幕上的控制器,都一樣在。 Hydra 不是 p5,沒有可以定義的 notePressed() 回呼,改用訂閱: midi.on('notePressed', (note, velocity) => { … }) 或 OAVp5.drums.on('hit', (lane, velocity) => { … })(都回傳一個取消訂閱的函式)。測試用的是 hydra-synth 1.4.0,也就是編輯器跑的那個函式庫(p5/test/hydra-smoke.cjs)。

細節#

↑