コンテンツにスキップ

式(Expressions)

NODE タブの FX 欄と Expression SIG Node で使う、小さな式言語のリファレンスです。 どの規格にも準拠しない独自の言語で(実装:crates/lemonade-core/src/expr.rs)、演算子は C、関数名は GLSL に合わせています。値はすべて float の 1 種類です。

trig(0) > 0.5 ? 1 : x * phase
mix(x1, x2, smoothstep(0.2, 0.8, low))
mod(floor(beats / 4), 2) == 0 ? v : 0

演算子

上ほど優先度が低い順です。

優先度 演算子 備考
1 a ? b : c 右結合(a ? b : c ? d : e と書ける)
2 \|\| 短絡評価。結果は 1 か 0
3 && 短絡評価。結果は 1 か 0
4 == != float をそのまま比較
5 < <= > >=
6 + - 左結合
7 * / % 左結合。% は C と同じで、結果の符号は左辺に従う
8 単項 - + ! !x は x が 0 なら 1、それ以外は 0

( ) でグループ化できます。真偽値は 1 と 0 で、0 以外は真として扱います。

変数

どこでも使える値(エンジンの値)

名前 内容
time 起動からの秒数
dt 前フレームからの秒数
bpm テンポ
phase 拍内の位置 0..1
beat 拍ごとのパルス(拍の頭で 1、0 に向かって減衰)
beats 累計拍数(連続値。小数部が phase)
level 全体の音量 0..1
onset アタックの強さ
sub low mid high 帯域ごとのレベル 0..1
pi 3.14159…

パラメタの FX 欄だけで使える値

名前 内容
x 接続されているワイヤーを combine(mul / add / min / max / avg)した値。未接続ならスライダーの値
x1〜x4 各ワイヤーの値。未接続なら 0
v スライダーで設定した元の値

Expression Node の中だけで使える値

名前 内容
a b c d Node の入力ポートの値。未接続なら 0

関数

関数 内容
sin(x) cos(x) tan(x) ラジアン
abs(x) sign(x) sign(0) は 0
floor(x) ceil(x) round(x) fract(x) fract(x) = x - floor(x)
sqrt(x) 負の値は 0 として計算
pow(a, b)
min(a, b) max(a, b)
mod(a, b) GLSL と同じで、結果の符号は b に従う(mod(-1, 4) = 3)
step(edge, x) x >= edge なら 1、それ以外は 0
clamp(x, lo, hi)
mix(a, b, t) a + (b - a) * t
smoothstep(e0, e1, x) 引数の順番は GLSL と同じ
hash(x) x から決まる疑似乱数 0..1(同じ x なら同じ値)
trig(i) トリガーパッドの値。0 始まりなので、パッド 1〜4 は trig(0)〜trig(3)
band(i) 帯域 0..3(0 = sub, 1 = low, 2 = mid, 3 = high)
hit(i) 帯域 i(0 = sub, 1 = low, 2 = mid, 3 = high)のアタックがしきい値(AUDIO タブ)を超えた瞬間に 1、減衰して 0
fft(i) FFT のビン 0..63(対数間隔)

trig band fft はインデックスの小数点以下を切り捨て、範囲外のインデックスでは 0 を返します。

構文(EBNF)

式の文法です(ISO 14977 の EBNF:, は連接、| は選択、[ ] は省略可、{ } は 0 回以上の繰り返し)。 expr.rs の再帰下降パーサー(ternary → 優先順位法の binary → unary → primary)と同じ構造で、上の演算子表の優先度・結合性はこの規則から決まります。

expression     = ternary ;
ternary        = or , [ "?" , ternary , ":" , ternary ] ;   (* 右結合 *)
or             = and , { "||" , and } ;                     (* 以下はすべて左結合 *)
and            = equality , { "&&" , equality } ;
equality       = comparison , { ( "==" | "!=" ) , comparison } ;
comparison     = additive , { ( "<" | "<=" | ">" | ">=" ) , additive } ;
additive       = multiplicative , { ( "+" | "-" ) , multiplicative } ;
multiplicative = unary , { ( "*" | "/" | "%" ) , unary } ;
unary          = ( "-" | "+" | "!" ) , unary
               | primary ;
primary        = number
               | call
               | name
               | "(" , ternary , ")" ;
call           = name , "(" , [ ternary , { "," , ternary } ] , ")" ;

字句(トークン)の規則:

number = digit , { digit | "." }
       | "." , digit , { digit | "." } ;     (* 全体が 10 進の小数として読めること *)
name   = ( letter | "_" ) , { letter | digit | "_" } ;
letter = "A" | … | "Z" | "a" | … | "z" ;     (* ASCII のみ *)
digit  = "0" | … | "9" ;
  • 空白(スペース・タブ・改行)はトークンの間で無視されます。sin (x) も sin(x) と同じです。
  • 2 文字の演算子(|| && == != <= >=)は 2 文字のまとまりとして先に読まれます。|、&、= 単独は使えません。
  • number は数字と . を続く限り読んでから数値にするので、1.2.3 はエラーです。1. は 1 と同じです。
  • name の直後に ( があれば関数呼び出し(call)です。関数名は下の関数表のもので、引数の数が合わないとエラーになります。
  • それ以外の name は、その場所だけで使える値(FX 欄の x x1..x4 v、Expression Node の a..d など)、エンジンの値の順に探し、どちらにもなければエラーです。
  • 式全体が空、または expression の後に余りのトークンがあるとエラーです。
  • 比較も左結合なので、a < b < c は (a < b) < c(0 か 1 と c の比較)になります。
  • 単項演算子は二項演算子より強く結びつきます。-x * y は (-x) * y、!a == b は (!a) == b です。

C / GLSL と違うところ

  • 数値は 1、0.5、.5 の形だけです。1e3 のような指数表記、16 進数、true / false は使えません。
  • 代入、ビット演算、vec2 などのベクトル型、コメントはありません。
  • 名前は大文字と小文字を区別します。

エラーと結果の扱い

  • 知らない名前、引数の数の誤り、構文エラーは、書いた時点で NODE タブにエラー位置付きで表示されます。その間、パラメタはスライダーの値を使います(Expression Node は 0 を出力)。
  • 0 除算などで結果が NaN や無限大になった場合は 0 になります。
  • パラメタに入る値は、最後にそのパラメタの範囲に clamp されます。int のパラメタは四捨五入、bool のパラメタは 0 以外なら true になります。

例

やりたいこと 式 備考
パッド 1 でフラッシュ trig(0) > 0.5 ? 1 : 0.2
キックでフラッシュ v + hit(1) low 帯域のヒット
拍に合わせてパルス v + beat * 0.5
低音で量を変える mix(v, 1, low)
2 拍で 1 回転 fract(beats / 2) * 360 - 180 Transform rotate など
ワイヤーの値をスケール x * 32 LFO (0..1) → Blur radius
LFO をパッドでゲート x1 * x2 LFO → wire 1、Trigger → wire 2(IN ×2 の mul と同じ)
2 本のワイヤーをクロスフェード mix(x1, x2, x3) wire 3 がフェーダー
1 小節おきにオン mod(floor(beats / 4), 2) == 0 ? v : 0
拍ごとにランダム hash(floor(beats))

テキストのテンプレート

文字列のパラメタ(Text の text、Image / Video の file、font file など)の FX 欄と、STR の Format Node では、上の式を {…} に埋め込んだテンプレートを書きます(実装:crates/lemonade-core/src/template.rs)。

/clips/{floor(beats) % 4:.0}.mp4
{v} · {bpm:.0} BPM
{x1}/{x2}-{floor(beats) % 4:02}.png
  • {…} の外はそのまま文字として出力されます。{{ と }} は { と } 1 文字になります。
  • {名前} が文字列の変数ならその文字列が入ります。
  • FX 欄:x(つないだ STR ワイヤーを順に連結したもの。未接続ならパラメタの値)、x1..x4(各ワイヤー。未接続は空文字)、v(パラメタの値)
  • Format Node:s1、s2
  • それ以外は数値の式として評価されます。使える名前は、エンジンの値と関数、Format Node では入力の a..d です。
  • 最後の : の後に書式を付けられます。空白を入れずに書いてください(a ? b : c の : と区別するためです)。
書式 意味 例(値 7.456)
.2 小数 2 桁 7.46
03 3 文字になるまで先頭を 0 で埋める 7.456(小数点以下は既定のまま)
05.1 0 埋めで 5 文字、小数 1 桁 007.5
4 4 文字になるまで先頭を空白で埋める 7.456
なし 必要な桁だけ 7.456
  • IN のワイヤーだけで FX 欄が空なら、つないだ文字列を順に連結したものがそのまま値になります。
  • テンプレートのエラーは NODE タブに表示されます。その間は連結したワイヤー(なければパラメタの値)が使われます。
  • 例:Image の file を File List の path につなぐと、index を拍で進めるだけでスライドショーになります。Video の file を拍ごとに変えると、そのたびにデコーダーを起動し直します。1 拍に 1 回程度にしてください。

テンプレートの構文

template = { literal | "{{" | "}}" | "}" | hole } ;
literal  = character - ( "{" | "}" ) , { character - ( "{" | "}" ) } ;
hole     = "{" , body , "}" ;            (* body に { と } は書けない *)
body     = text-name
         | expression , [ ":" , format ] ;
format   = [ digits ] , [ "." , digits ] ;  (* 空でないこと。数字と . だけ *)
digits   = digit , { digit } ;
  • {{ は {、}} は }、対になっていない } はそのまま } 1 文字になります。閉じていない { と、穴の中の { はエラーです。
  • body を前後の空白を除いて比べ、文字列の変数の名前(x、x1..x4、v、s1、s2 など)と一致すれば text-name です。
  • それ以外は、最後の : で分け、後ろが format として読めればそれが書式、読めなければ(空白を含む、など)body 全体を expression として読みます。そのため {a ? b : c} はそのまま三項演算子になります。 逆に、三項演算子の最後の項が数字だけのときは : の後に空白を入れてください({a ? b : 2})。{a ? b :2} だと 2 が書式と読まれ、a ? b が不完全な式としてエラーになります。
  • format の幅が 0 で始まると 0 埋め、それ以外は空白で埋めます。