Skip to content

内蔵シンセサイザー (NativeSynth)

NativeSynth は MIDI を単体で音にします — ダウンロードするサンプルも、同梱する SoundFont も要りません。libsonare に組み込まれているので、MIDI トラックは何もしなくても最初から音が鳴ります。

内部的には、NativeSynth は 7 つの差し替え可能なシンセシスエンジンを備えた 1 台のシンセです。それぞれ音の作り方が異なります。

  • バーチャルアナログ減算合成(定番のシンセリードやパッド)
  • FM(エレクトリックピアノやベル)
  • Karplus-Strong 撥弦(ギターやハープ)
  • モーダル打楽器(マリンバ、ビブラフォン)
  • 加算ドローバーオルガン
  • 膜打楽器(ドラムキット)
  • 拡張導波路アコースティックピアノ

7 つすべてが共通のモジュレーション/エンベロープ/フィルター層を共有するため、挙動は一貫しています。音色を出すには、プリセットを名前で選ぶか、SynthPatch でフィールド単位に組み立てます。鳴らし始めるのにエンジン内部へ触れる必要はありません。

シンセシス用語をまとめて把握

以下のエンジン名は、音色を生成する方式の違いです。最初から全部を知る必要はありません(プリセットを選んで鳴らせば十分です)。それぞれを 1 行で説明します。

  • subtractive(減算合成) — 明るい波形から始め、フィルターで削っていく古典的なアナログシンセの手法です。
  • FM/位相変調 — 1 つのオシレーターのピッチがもう 1 つを変調し、金属的で鐘のような音色を作ります。
  • Karplus-Strong — 撥弦をモデル化する短い遅延ループです。
  • modal(モーダル) — 叩いたバーや鐘をモデル化する、チューニングされた共鳴器のバンクです。
  • additive/drawbar(加算/ドローバー) — ハモンドオルガンのドローバーのように、倍音のサイン波成分を足し合わせます。
  • (拡張)waveguide(導波路) — 振動する弦や管をモデル化する遅延ラインです。

すべてのエンジンのラッパーに共通して登場する用語が 2 つあります。ADSR エンベロープ(attack/decay/sustain/release。1 つのレベルがノートの間どう立ち上がり減衰するか)と、モッドマトリクス(LFO やエンベロープなどのモジュレーションソースを、ピッチやフィルターカットオフなどのターゲットへルーティングする仕組み)です。

MIDI は決して無音にならない

NativeSynth は SoundFont プレイヤーデータ不要な土台でもあります。SF2 経由でプロジェクトをバウンスしたとき、あるプログラム(または SoundFont 全体)が欠けていると、その音は NativeSynth の GM フォールバックバンクへ落ちます。128 種すべての General MIDI プログラムとドラムマップを備えているため、いずれにせよ音は出ます。

NativeSynth の位置づけ

NativeSynth パッチはインストゥルメントです。MIDI 出力先へバインドすると、その出力先へルーティングされたトラックの MIDI が NativeSynth で鳴ります。オフラインでは bounceWithSynthInstrument でバインドし、ライブでは engine.setSynthInstrument でバインドして MIDI 入力を送ります。サンプルベースのマルチサンプル音色が必要なら、代わりに SoundFont プレイヤー を使ってください。

どのノートにも、NativeSynth の中を 1 本のシグナルパスが流れます。MIDI ノートが 7 つのうち 1 つのエンジンを選び、そのエンジンの素の音色が共通ラッパーを通ってからステレオ出力へ届きます。

このページで身につくこと

このページを読むと、次のことができるようになります。

  • 音色に合ったシンセシスエンジンと、適切な名前付きプリセットを選べる。
  • プリセットを出発点に、SynthPatch で個々のフィールドを上書きできる。
  • プリセット名と enum 名を推測せず、ランタイムから実際の名前を取得できる。
  • va: ルーティング接頭辞と、drum-kit の GM ドラムマップを理解できる。
  • bounceWithSynthInstrument でオフライン、setSynthInstrument でライブに MIDI を音声化できる。
  • ある音が NativeSynth で鳴るのか、読み込んだ SoundFont で鳴るのかを判断できる。

7 つのシンセシスエンジン

各プリセットは 1 つの engineMode を選びます。ラッパー部(フィルター、エンベロープ、LFO、モッドマトリクス、ボディ共鳴、ポリフォニー)は、選択中のどのエンジンの上にも適用されます。エンジン固有の深いパラメータ(FM オペレータスタック、モーダルのモードテーブル、ドローバー設定、キットの各パーツ、ピアノの弦)は、パッチではなく名前付きプリセットの中に収まっています。

subtractive — バーチャルアナログ

オシレーター → フィルター → アンプという古典的なボイスです。デチューンユニゾン、ドリフト、フィルター前段のドライブ、4 種のフィルターモデルにより、太いソウリードから広がりのあるパッドまで作れます。リード・ベース・パッド・プラック、つまりアナログシンセで作りたいものに向きます。プリセット: sinesaw-leadsquare-leadsub-basswarm-pad

SYNTH · SUBTRACTIVEIDLE
音を形づくる — オシレーター・フィルター・エンベロープ

内蔵シンセが A3 を1音レンダリングします。輪郭はその振幅エンベロープ。アタックを上げると音が徐々に立ち上がり、カットオフを下げると音色が暗くなります。再生でパッチを試聴できます。

オシレーター
カットオフ
2200 Hz
アタック
8 ms

「キャラクター」の核はフィルターモデルです。filterModel で 4 種の古典モデルを選べます。

モデル模した音色備考
svfTPT ステートバリアブル(SEM 系)クリーン。filterOutput(lowpass / bandpass / highpass)を選べる唯一のモデル
moog-ladder4 ポールトランジスタラダーゼロディレイフィードバック、飽和ループ、自己発振する
diode-ladderダイオードラダー(VCS3 / TB-303 系)結合段 ZDF、自己発振する
sallen-keyKorg35 ザレンキー(MS-10 / 初期 MS-20)自己発振する

4 種とも、サンプル単位のカットオフ/レゾナンス変調下でも安定しジッパーノイズがなく、自己発振も決定論的です。

SYNTH · SUBTRACTIVEIDLE
ローパスフィルター — カットオフ・レゾナンス・モデル

ノコギリ波をローパスフィルターに通します。シンセで最も象徴的な操作です。カットオフを下げると高次の倍音が削れて音色が暗くなり、レゾナンスを上げるとフィルターがカットオフ付近で共振して「ワウ」がかかります。モデルは模倣するフィルター回路を選びます。SVF はクリーンなまま、ラダーや Sallen-Key はサチュレーションがかかり、高いレゾナンスでは自己発振します。下段のスコープは波形です。再生して聞いてください。

カットオフ
2200 Hz
レゾナンス
3
フィルターモデル

fm — 周波数変調

小さなアルゴリズムテーブルを持つ位相変調オペレータスタック(1 つのオシレーターのピッチがもう 1 つを変調 → 金属的・鐘的な音色)で、指数エンベロープ、フィードバックオペレータ、ベロシティ → インデックス(明るさ)スケーリングを備えます。エレクトリックピアノ・ベル・マレット・クラビ/ハープシコード・ブラス、つまり減算合成が苦手な金属的・鐘的・非整数次倍音の音に向きます。プリセット: e-pianobellbrass

karplus-strong — 撥弦

位相が正確にチューニングされた分数遅延導波路ループ(撥弦をモデル化する短い遅延ループ)に、ピック位置コム、ベロシティ駆動の明るさ、ディケイストレッチ、ノートオフ時のループダンピング(フィンガー/パームミュート)を加えたものです。撥弦・ストローク弦、すなわちギター・ハープ・撥弦系民族楽器に向きます。プリセット: pluckelectric-guitarharp

物理的なモード比(一様バーのグロッケン、深いアーチのマリンバ/ビブラフォン)に合わせたモーダル共鳴バンク(叩いたバーや鐘をモデル化する、チューニングされた共鳴器のバンク)で、マレット硬さのベロシティ重みづけとモードごとのディケイを持ちます。音程のあるマレット楽器、グロッケン・ビブラフォン・マリンバ・シロフォンに向きます。プリセット: marimbaglass

additive — ドローバーオルガン

9 本のハモンドドローバーのピッチ(倍音のサイン波成分を、ドローバー 1 本につき 1 成分として足し合わせる)をステップ状のストップレベルで鳴らし、フリーランの倍音位相とキークリックの接点トランジェントを備えます。オルガン、すなわち持続して倍音成分が豊かなレジストレーションに向きます。プリセット: organ

percussion — 膜打楽器

レイリーの円形膜モードに、下降するストライクピッチのエンベロープとフィルタードノイズを重ねたものです。このエンジンが GM ドラムキット(キック、スネアの胴とスナッピー、タム、ハット、非整数次倍音のリングモードを持つシンバル)を支えます。ワンショットで決定論的です。プリセット: drum-kit

piano — 拡張導波路アコースティックピアノ

データ不要のグランドピアノのスケッチで、ピアノを定義する 4 要素を備えます。剛性弦の分散(鍵盤を上がるほど倍音成分がシャープに伸びる)、非線形フェルトハンマー(強打ほど短く明るい)、2-3 本の微デチューンユニゾン弦、響板共鳴バンクです。アコースティックピアノに向きます。プリセット: acoustic-piano

名前付きプリセットカタログ

NativeSynth は小さな名前付きプリセットカタログを同梱します。プリセット名をハードコードしないでください。ランタイムから synthPresetNames() で一覧し、synthPresetPatch(name) で各プリセットを SynthPatch として確認します。

SYNTH · PRESETIDLE
プリセットカタログ — 1 音で全エンジンを

同じ A3 を 1 音、名前付きプリセット — 合成エンジンごとに 1 つ — で試聴します。パッチは `synthPresetPatch(name)` からそのまま取るので、各プリセットは本来の性格で鳴ります。減算方式のサウリードとウォームパッド、FM のエレピとベル、Karplus-Strong のプラック、モーダルのマリンバ、ドローバーオルガン、物理モデルのグランドピアノ。選ぶたびにエンベロープと波形が変わります。再生すると、同一の 1 音からエンジンごとにどれだけ音が違うかが聴けます。

プリセット
typescript
import { init, synthPresetNames, synthPresetPatch } from '@libraz/libsonare';

await init();

synthPresetNames();
// ['sine', 'saw-lead', 'square-lead', 'sub-bass', 'warm-pad', 'e-piano',
//  'bell', 'brass', 'pluck', 'electric-guitar', 'harp', 'marimba', 'glass',
//  'organ', 'drum-kit', 'acoustic-piano']

const pad = synthPresetPatch('warm-pad');
// { preset: 'warm-pad', engineMode: 'subtractive', waveform: 'saw',
//   unison: 7, detuneCents: 18, cutoffHz: 2800, ampAttackMs: 400, ... }
python
import libsonare as sonare

sonare.synth_preset_names()
# ['sine', 'saw-lead', 'square-lead', 'sub-bass', 'warm-pad', 'e-piano',
#  'bell', 'brass', 'pluck', 'electric-guitar', 'harp', 'marimba', 'glass',
#  'organ', 'drum-kit', 'acoustic-piano']

pad = sonare.synth_preset_patch("warm-pad")
# SynthPatch(preset='warm-pad', engine_mode='subtractive', waveform='saw',
#            unison=7, detune_cents=18.0, cutoff_hz=2800.0, ...)

カタログとエンジンの対応は次のとおりです(各エンジンの感触は 1 行で十分つかめます)。

プリセットエンジン向いている用途
sine saw-lead square-lead sub-bass warm-padsubtractiveリード・ベース・パッド
e-piano bell brassfmエレピ・ベル・ブラス
pluck electric-guitar harpkarplus-strong撥弦
marimba glassmodal音程のあるマレット
organadditiveドローバーオルガン
drum-kitpercussionGM ドラムマップ
acoustic-pianopianoアコースティックピアノ

下のロールは1つの3声フレーズをシーケンスし、bounceWithSynthInstrument(presetName, …) でバウンスします。楽器セレクタは4つのエンジン — acoustic-pianopiano)、e-pianofm)、harpkarplus-strong)、marimbamodal)— をまたぐので、同じ音符がそれぞれのエンジンの性格を帯びるのが聞き取れます。

MIDI · PIANO ROLLIDLE
MIDI のフレーズ — 同じ音符を、どの楽器でも

旋律・和音・低音の3声フレーズをピアノロールで表示します。音符はそのまま、楽器を切り替えると、まったく同じ MIDI をエンジンが別の内蔵音色でバウンスします。テンポを動かせばシーケンス全体が速く・遅くなります。再生でフレーズを試聴でき、再生ヘッドは音声に追従します。

楽器
テンポ
100 BPM

va: ルーティング接頭辞

プリセット名には va: 接頭辞を付けられます(例: va:saw-leadva:e-piano)。この接頭辞はプリセット名を受け取るすべての場所 — synthPresetPatchbounceWithSynthInstrumentsetSynthInstrument — で受け付けられ、接頭辞なしと同じパッチに解決されます。これは「この出力先はバーチャルアナログの NativeSynth を鳴らす」と印を付けるためにホストが使うルーティング規約で、シンセは検索前に取り除きます。

drum-kit プリセットと GM ドラムマップ

drum-kitpercussion エンジンを選び、入ってくる MIDI ノートを General MIDI ドラムマップへ割り当てます。ノート番号をピッチとして扱うのではなく、ノート 36 はキック、ノート 38 はアコースティックスネア、というように対応づけます。ドラムパターンのノートを drum-kit をバインドした出力先へ送ると、各ノートが対応するパーツを鳴らします。

SynthPatch オブジェクト

SynthPatch は「プリセット + あなたの調整」と考えてください。ベース(名前付き preset。省略すると既定の減算合成 init パッチ)から始まり、設定した各フィールドがそのベースを上書きします。フィールドを省けば、ベース値がそのまま残ります。

0 は「0 に設定」ではなく「ベースを保つ」

注意してください。ampSustain: 0 と書いてもサスティンは無音になりません。プリセットのサスティンがそのまま保たれます。このオブジェクト全体で、0(または省略)は「ベース値を保つ」を意味し、非ゼロ値が上書きします(可聴域にクランプ)。enum フィールドは「保つ」に 'default' を使います。

したがって、数値フィールドを文字どおりの 0 に強制することはできません。(固定された C ABI にはフィールドごとの「設定済みか」フラグがないため、0 が「未変更」を意味するしかないからです。)フィールドの範囲の下限、あるいはその近くの値がどうしても必要なら、代わりに最小の非ゼロ値を使ってください。たとえば ampSustain: 0.001 ならベースを上書きでき、実質的に無音になります。

もう 1 つのルールとして、空でない modRoutings 配列は、ベースのモッドマトリクスへ追加するのではなく、まるごと置き換えます。

パッチは、すべてのエンジンが共有するラッパー部を公開します。

セント・ベロシティ・キートラッキング

  • セント(cent) — 半音の 1/100 です。100 セントでピアノの 1 鍵分、1200 で 1 オクターブです。ピッチやデチューンの量はセントで表します。
  • ベロシティ — ノートをどれだけ強く弾いたか(0〜127)です。プリセットはこれで明るさや音量を制御します。
  • キートラッキング — フィルターカットオフなどのパラメータを、鍵盤を上がるほどノートの音高に追従させることです。
グループフィールド
オシレーターengineModewaveformunison(1-7)、detuneCentsdriftCentsdrive(0-1)
フィルターfilterModelfilterOutput(SVF のみ)、cutoffHzresonanceQkeyTrack(0-1)、envToCutoffCentsvelToCutoffCents
アンプエンベロープampAttackMsampDecayMsampSustainampReleaseMs
フィルターエンベロープfilterAttackMsfilterDecayMsfilterSustainfilterReleaseMs
LFO とグライドlfoRateHzlfoToPitchCentslfo2RateHzglideMs
ボディ共鳴bodynone / guitar / violin / wood-tube)、bodyMix(0-1)
ステレオと出力stereoSpread(0-1)、gain(リニア)、polyphony(1-64)、busDrive(0-1)
モッドマトリクスmodRoutings(最大 8 本)
バインディング(JS のみ)destinationId(既定 0

ポリフォニーは同時に鳴らせるノート数、ボイスは鳴っている 1 つのノートで、ボイススティールは足りなくなったとき最も古いノートを止めることです。)

LFO 2 にはルーティングが必要

2 つの LFO は挙動が異なります。LFO 1(lfoRateHz + lfoToPitchCents)はピッチへ固定配線されており、単独でビブラートを生みます。LFO 2 はマトリクス経由専用で、modRoutings のエントリが source: 'lfo2' で送り先を指定するまで、lfo2RateHz を設定しても何も起きません。

モッドルーティング{ source, destination, depth } です。モッドマトリクスにより、エンベロープ・LFO・ベロシティ・キートラッキング・モッドホイール・シード付きのボイスごとランダムソースが、ピッチ・フィルターカットオフ・音量・パンを変調できます。depth はソースが最大振れたときの destination 単位です。

body フィールドは NativeSynth のボディ/フォルマント共鳴層 — 楽器の物理的な筐体がもつ共鳴的なキャラクターです。アコースティックギター・ハープ・木製マレットはボディを持ちますが、ソリッドボディのエレキは意図的に持ちません。そうした音色では bodynone のままにします。

ピッチベンド・MPE・コントローラーリセット

NativeSynth はピッチベンドメッセージに反応し、ベンドレンジは RPN 0(ピッチベンドレンジの標準パラメータ。Data Entry で設定)に従います。MIDI の Reset All Controllers メッセージを送ると、ベンドレンジを含めて既定値へ戻ります。これらは通常の MIDI イベントで駆動します。ピッチベンドイベント(オフラインなら Project.midiPitchBend(...) など)と、ストリーム中の RPN 0/Data Entry/リセットの各 CC です。

さらにノートごとの表現にも対応します。MPE スタイルのノートごとピッチベンドとノートごと(ポリフォニック)プレッシャーは、チャンネル全体ではなく該当するボイスを個別に動かすため、重なるノートが独立してグライドやスウェルできます。ライブのコントロールチェンジはフル解像度でデコードされます。14 ビット CC ペアと RPN/NRPN 値は微細バイトを保ったまま反映され、MIDI 2.0 のノートベロシティは 7 ビットに丸めず 16 ビットのフル領域で整形されます。

enum 名テーブル

各 enum フィールドは、名前文字列または C の序数のどちらも受け付けます。名前と序数がずれないよう、synthEnumTables() でランタイムから正規のテーブルを取得してください。

typescript
import { init, synthEnumTables } from '@libraz/libsonare';

await init();
synthEnumTables();
// {
//   engineModes:      ['default', 'subtractive', 'fm', 'karplus-strong',
//                      'modal', 'additive', 'percussion', 'piano'],
//   waveforms:        ['default', 'sine', 'saw', 'square', 'triangle', 'noise'],
//   filterModels:     ['default', 'svf', 'moog-ladder', 'diode-ladder', 'sallen-key'],
//   filterOutputs:    ['default', 'lowpass', 'bandpass', 'highpass'],
//   bodyTypes:        ['default', 'none', 'guitar', 'violin', 'wood-tube'],
//   modSources:       ['none', 'amp-env', 'filter-env', 'lfo1', 'lfo2',
//                      'velocity', 'key-track', 'mod-wheel', 'random'],
//   modDestinations:  ['none', 'pitch-cents', 'cutoff-cents', 'amp-gain', 'pan-units'],
// }

同じ配列は名前付き定数(SYNTH_ENGINE_MODESSYNTH_OSC_WAVEFORMSSYNTH_FILTER_MODELSSYNTH_FILTER_OUTPUTSSYNTH_BODY_TYPESSYNTH_MOD_SOURCESSYNTH_MOD_DESTINATIONS)としてもエクスポートされます。多くのテーブルでインデックス 0 は 'default'(ベース値を保つ)で、modSources / modDestinations は代わりに 'none' を使います。

オフラインでレンダー: bounceWithSynthInstrument

MIDI アレンジを音声化するには、NativeSynth インストゥルメントを MIDI 出力先へバインドしてバウンスします。プリセット名の文字列、SynthPatch、またはそのどちらかの配列を渡して、複数の出力先を一度にバインドできます。配列を渡すと、各 SynthPatchdestinationId(既定 0)でバインドする MIDI 出力先を選べます。たとえば [{ preset: 'saw-lead', destinationId: 0 }, { preset: 'drum-kit', destinationId: 1 }] なら、1 回のレンダー呼び出しで 2 つの出力先へ供給します。destinationId は JS のバインディング用の便宜機能で、NativeSynth パッチそのものの一部ではありません(Python では出力先を別の引数として渡します)。明示的に空の配列(または undefinednull)を渡すとバインディングは 0 件になります。レンダーはプロジェクト・オプション・パッチが固定なら決定論的です。

typescript
import { init, Project } from '@libraz/libsonare';

await init();

const project = new Project();
project.setSampleRate(48000);

// MIDI クリップ 1 つ: 出力先 0 へルーティングした 2 拍の C4 ノート
const { trackId, clipId } = project.addMidiClip(0, 4);
project.setTrackMidiDestination(trackId, 0);
project.setMidiEvents(clipId, [
  Project.midiNoteOn(0, 0, 0, 60, 100),
  Project.midiNoteOff(2, 0, 0, 60, 0),
]);

try {
  // 名前付きプリセットを出力先 0 へバインドしてステレオでレンダー
  const audio = project.bounceWithSynthInstrument('va:saw-lead', {
    totalFrames: 48000,
    numChannels: 2,
  });
  // audio はインターリーブの Float32(frames * channels)。無音ではない
} finally {
  project.delete();   // WASM ハンドルは GC されない — 必ず解放する
}
python
import libsonare as sonare

project = sonare.Project()
project.set_sample_rate(48000)

track_id, clip_id = project.add_midi_clip(0, 4)
project.set_track_midi_destination(track_id, 0)
project.set_midi_events(clip_id, [
    sonare.Project.midi_note_on(0, 0, 0, 60, 100),
    sonare.Project.midi_note_off(2, 0, 0, 60, 0),
])

# 名前付きプリセットを出力先 0 へバインドしてレンダー -> (frames, channels) float32
audio = project.bounce_with_synth_instrument(
    "va:saw-lead", total_frames=48000, num_channels=2,
)
project.close()
bash
sonare project bounce --in song.json -o synth.wav --synth va:saw-lead
sonare project synth-presets            # NativeSynth プリセットカタログを一覧

カスタマイズするには、名前の代わりに SynthPatch を渡します。プリセットを出発点に上書きしてください。

typescript
const audio = project.bounceWithSynthInstrument(
  {
    preset: 'warm-pad',
    cutoffHz: 1200,                // プリセットの 2800 Hz より暗く
    resonanceQ: 3,
    modRoutings: [{ source: 'lfo1', destination: 'cutoff-cents', depth: 600 }],
  },
  { totalFrames: 48000, numChannels: 2 },
);

totalFrames を 0 のままにすると、アレンジとパッチのリリーステイルから長さを自動導出します。未知のプリセット名は例外を投げます。bounceWith* が共有するチャンネル・サンプルレート・レイテンシなどは プロジェクトバウンス を参照してください。

ライブでレンダー: setSynthInstrument + MIDI 入力

対話的な再生では、RealtimeEngine の出力先にシンセをバインドして MIDI を送ります。次のスニペットは制御スレッドだけで動き(AudioWorklet 不要)、非ゼロのサンプルを生成します。

typescript
import { init, RealtimeEngine } from '@libraz/libsonare';

await init();

const engine = new RealtimeEngine(48000, 128);
try {
  engine.setSynthInstrument('va:saw-lead', 7);   // 出力先 7 へバインド
  engine.pushMidiNoteOn(7, 0, 0, 60, 100);       // 出力先, グループ, チャンネル, ノート, ベロシティ

  const out = engine.process([new Float32Array(128), new Float32Array(128)]);
  // out[0] / out[1] がレンダー済みのステレオブロック。無音ではない

  engine.midiInstrumentCount();                   // 1
} finally {
  engine.destroy();   // ネイティブハンドルを解放
}

実際のアプリでは pushMidiNoteOn / pushMidiNoteOff / pushMidiCc をライブキーボードから駆動するか、エンジン所有の MIDI 入力ソースを有効にしてイベントを到着順に送ります。詳しくは MIDI 入力 を参照してください。setSynthInstrument はプリセット名や SynthPatchbounceWithSynthInstrument とまったく同じように解決するため、オフラインで作り込んだ音色がライブでも同一に鳴ります。

NativeSynth と SoundFont フォールバック

NativeSynth は SoundFont プレイヤーの下にあるセーフティネットです。bounceWithSf2Instrument でレンダーする(またはライブで SF2 をバインドする)と、libsonare はアレンジが実際に鳴らす各 (channel, bank, program) を解決します。

  • 読み込んだ SoundFont がそのプログラムをカバーしていれば、そのノートは SF2 から鳴ります(GS バリエーションとドラムフォールバックを含む)。
  • そうでなければ — SoundFont を一切読み込んでいない場合も含めて — そのノートは NativeSynth の GM フォールバックバンク(128 種すべてのプログラムとドラムマップ)で鳴ります。

レンダー前にプログラムごとのバックエンドを確認するには soundFontManifest() を使います。最初に使われる順に、各プログラムについて 'sf2''synth' を報告します。

typescript
project.loadSoundFont(sf2Bytes);
const manifest = project.soundFontManifest();
// [{ channel, bank, program, backend: 'sf2' | 'synth', presetName }, ...]

GM フォールバックバンクが常に存在するため、データがないという理由で MIDI が無音になることはありません。SF2 データの読み込みやチャンネル/プログラムごとの解決は SoundFont プレイヤー を参照してください。

レシピ

1 つのプロジェクトで全エンジンを試聴

同じ MIDI クリップを、エンジンごとに 1 プリセットでバウンスして各ボイスを聴き比べます。

typescript
const project = new Project();
project.setSampleRate(48000);
const { trackId, clipId } = project.addMidiClip(0, 4);
project.setTrackMidiDestination(trackId, 0);
project.setMidiEvents(clipId, [
  Project.midiNoteOn(0, 0, 0, 60, 100),
  Project.midiNoteOff(2, 0, 0, 60, 0),
]);
try {
  for (const preset of ['saw-lead', 'e-piano', 'electric-guitar',
                         'marimba', 'organ', 'acoustic-piano']) {
    const audio = project.bounceWithSynthInstrument(preset, { totalFrames: 48000 });
    // 各プリセットの音声をレンダー/確認
  }
} finally {
  project.delete();
}
GM ドラムマップでドラムパターンを鳴らす

ドラムノート(キック 36、スネア 38、ハット 42、...)を drum-kit をバインドした出力先へ送ります。

typescript
project.setMidiEvents(clipId, [
  Project.midiNoteOn(0, 0, 9, 36, 110),   // キック
  Project.midiNoteOff(1, 0, 9, 36, 0),
  Project.midiNoteOn(0, 0, 9, 38, 100),   // スネア
  Project.midiNoteOff(1, 0, 9, 38, 0),
]);
const audio = project.bounceWithSynthInstrument('drum-kit', { totalFrames: 24000 });

各ノートはピッチとしてではなく、対応する GM のパーツを鳴らします。

LFO ワブルを足したカスタムパッチ

warm-pad を出発点に、フィルターを暗くし、LFO 1 でカットオフをゆらします。

typescript
const audio = project.bounceWithSynthInstrument(
  {
    preset: 'warm-pad',
    cutoffHz: 1200,
    resonanceQ: 3,
    lfoRateHz: 6,
    modRoutings: [{ source: 'lfo1', destination: 'cutoff-cents', depth: 600 }],
  },
  { totalFrames: 48000, numChannels: 2 },
);

空でない modRoutings は、プリセットのモッドマトリクスをまるごと置き換えます。

関連