FL StudioとJavaScriptでサウンドプログラミング
FL Studioにはオーディオ編集ツールとして評判の高いEdisonが付属しています。VST版も販売されているので単品で購入すれば他社のDAWでも使用できるようです。
あまり知られていないことですがEdisonはプラグインを読み込んでオーディオ編集機能を拡張することができます。さらに知られていないことですが、このプラグインはスクリプト言語を使って簡単に書くことができます。スクリプト言語としてはPascal、Basicの他にみんな大好きJavaScriptも使えます。
そんなわけで今回はJavaScriptを使って簡単なサウンド加工プラグインを作ってみます。
■編集ツールプラグインのメリット
DTMのプラグインというとVST Effectのようなものがよく使われますが、VSTはリアルタイムで音を加工するためにプログラミング上非常に厳しい時間的制約があり、あまり重い処理はできません。また、10秒後の音を10秒前の音に混ぜるような時間を遡る処理も不可能です。
これに対しEdisonのような編集ツールのプラグインは、リアルタイムで演奏中の音にかけることはできませんが、その代わり上記のような制約にとらわれずに自由にプログラムできます。
■プログラミング概要
普通にインストールすると、以下の場所に簡単なリファレンスのテキストファイルが格納されています。
C:\Program Files\Image-Line\FL Studio 9\Plugins\Fruity\Effects\Edison\Data\Scripts\Reference.txt
同じフォルダ以下に実際のプラグインスクリプトやサンプルプログラムがあるので、これらを見ればプログラムの方法は大体わかります。
基本は、EditorSanpleオブジェクトに対してGetSampleAt関数で、ある瞬間のサンプルを取得、加工した後にSetSampleAt関数で書き込むという流れです。CreateScriptDialog関数でパラメータ入力ダイアログを表示することもできます。
■今回作るツール
簡単な例として元の音の前にリバース音をくっつける処理を書いてみます。
まず、オリジナル音としてこんな感じのクラッシュシンバルの音を用意します。
リバース加工するとこんな感じ。
リバース音の後ろにオリジナル音をくっつけるとリリースが自然になり、シンバルロールっぽく使い勝手の良い音になります。これをプログラムで一発で作成できるようにします。
ただし、元の音はアタックが少し遅く、音量が最大になる前のわずかな時間があります。これをそのまま反転貼り付けすると真ん中に微妙な谷ができてしまいます。
そのためサンプル音を最初から最後までスキャンして音量がピークの位置を検出し、それ以前の部分を削除してから反転貼り付けします。このへんがリアルタイムでは絶対にできない編集ツールならではの処理です。
余計なアタックを削除したあとはオリジナル音を前から順番に読んで、貼り付け位置の後ろから順番に貼り付けていくだけです。注意点としては、たとえばステレオ音源ならLとRの2チャンネルになっているので、EditorSample.NumChansのチャンネル数を見て全チャンネル分の加工やコピペをする必要があるということです。
■プログラムの保存と実行
ソースコードの1行目に以下のヘッダ情報を書くことでJavaScriptのプログラムとして認識されます。
script "(スクリプトの名前)" language "javascript";
ソースは拡張子.edscriptをつけて以下のフォルダ以下に保存します。
C:\Program Files\Image-Line\FL Studio 9\Plugins\Fruity\Effects\Edison\Data\Scripts\
「MyScript」のような自作スクリプト用のフォルダを掘っておくとわかりやすいと思います。文字コードはUTF-8で保存すると日本語コメントも大丈夫のようです。
実行はツールボタンをクリックして表示されるメニューからおこないます。
→ Run script → フォルダ名 → プログラム名
ReverseAndRelease.edscript
script "Reverse and Release" language "javascript"; // 最大音量の位置を検出 function DetectPeak(x1, x2) { var max = 0.0; var pos = 0; for (var n = x1; n <= x2; n++) { var x = n - x1; if (x % 10000 == 0) ProgressMsg("Processing", x, x2 - x1); for (var c = 0; c < EditorSample.NumChans; c++) { var s = Math.abs(EditorSample.GetSampleAt(n, c)); if (s > max) { max = s; pos = n; } } } return pos; } // src位置のサンプルのリバースをdst位置にペースト function ReverseCopy(src, dst, length) { for (var n = 0; n <= length; n++) { if (n % 10000 == 0) ProgressMsg("Processing", n, length); var spos = src + n; var dpos = dst + length - n; for (var c = 0; c < EditorSample.NumChans; c++) EditorSample.SetSampleAt(dpos, c, EditorSample.GetSampleAt(spos, c)); } } var x1 = 0; var x2 = 0; // 選択位置の取得(無選択時はサンプル全体が対象) Editor.GetSelectionS(x1, x2); // ピーク位置の検出 var peak = DetectPeak(x1, x2); // サンプルが読み込まれていないとき誤動作を防止するための条件文 if (peak > 0) { // 最初からピーク位置まで削除 EditorSample.DeleteFromTo(x1, peak); // 選択位置の再取得 Editor.GetSelectionS(x1, x2); // 選択範囲と同じ長さの無音領域をインサート EditorSample.InsertSilence(x1, x2); // 無音領域に反転しながらペースト ReverseCopy(x2 + 1, x1, x2 - x1); }
こんな感じで、サウンドファイルの読み込みやフォーマット解析など面倒な部分はEdison側にまかせてサウンド処理部分のみをシンプルに書くことができます。処理自体は普通のJavaScriptであり、豊富な既存ライブラリもそのまま使えるので、たとえばフィボナッチ数列にしたがってフィードパック時間が変化するディレイ、など思いついたエフェクトをさくっと実現できて楽しいです。