Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ピアノロールマニュアル

ナビゲーション: ガイド | ドキュメントルート

この文書は piano_roll の例に対する長編の解説書です。この例は、16分音符のティックを駆動する Keleusma スクリプトと、Simple DirectMedia Layer 3 を通じて8ボイスのポリフォニック出力を合成する Rust オーディオホストとを結合しています。この例は一度に読み通せる程度に小さく、かつ Keleusma ホストアプリケーション全体で繰り返し現れるパターンを実践する程度に濃密です。

目次

この文書は3つの主要な節を持ち、それぞれ異なる読者に向けられています。

  • 楽曲を作る は、この例またはその応用を通じて再生する新しい .kel の楽曲を書きたい人に向けたものです。読者はメンタルモデル、データセグメントの慣習、ティックごとの本体構造、および利用可能なホストネイティブ関数呼び出しを学びます。
  • 例を発展させる は、この例をより大きなアプリケーションにコピーしたい人に向けたものです。2つの道があります。1つ目はホストループをゲームや音楽エディタなどの別のプログラムに組み込むものです。2つ目は例をそのまま拡張してより本格的なツールに仕上げるものです。両方の道を扱います。
  • 組み込みパターン は、Keleusma を別の制御ループアプリケーションに組み込むためのパターンとしてアーキテクチャを学びたい人に向けたものです。ピアノロールが低リスクの標準例として選ばれたのは、オーディオが監査しやすく、ここで機能するパターンがより厳しい領域にも転用できるためです。

この文書とソースの関係

examples/piano_roll.rs 内のモジュールレベルのドキュメントコメントには、ホストネイティブ関数、パラメータ範囲、デフォルト値、波形コード、およびデータセグメントのスロットレイアウトの権威あるカタログが記載されています。この文書はそのカタログを取り巻く説明を行います。ドックストリングが何が利用可能かを列挙するのに対し、この文書はその使い方となぜそのように構成されたのかを説明します。特定のネイティブの引数の形を調べたい読者はドックストリングを参照すべきです。アーキテクチャを理解したい、または新しい楽曲を書きたい読者はこの文書を読むべきです。

バンドルされたロスターには 10 曲が含まれています。曲 0 と 1 は、ホストを紹介する 3 チャンネルのコード進行スクリプトです。曲 2 は、ホストのネイティブ ADSR とリトリガーを実演する 5 チャンネルのバッハのプレリュード編曲です。曲 3 は、ニ短調による長尺の 8 チャンネルのボステーマ負荷試験であり、7/8 拍子への転換、全音音階のスナップダウンのジェスチャー、3 つの重ね合わせ技法(デチューンによるステレオユニゾン、デチューンされたオクターブ重ね、ヘ長調での平行音程ハーモナイゼーション)、および 90 BPM から 250 BPM の間をランプするティックごとの BPM 更新を含む、10 セクションのデュアルピークループにわたって、すべてのホストネイティブをアクティブ、非アクティブ、および動的な状態で行使します。曲 4 は 2 つ目のフルマトリクス実演であり、1024 ティックのループボディにわたって 60 BPM から 300 BPM の間で連続的な正弦波変調のもとでテンポを走らせ、一定のニ短調コードのスケルトン上でループカウントに応じて循環する 4 つの反復バリエーション(Awakening、Descent、Malfunction、Apocalypse)を、アルゴリズミックなシャコンヌの様式で提示します。曲 5 はフェーズミュージックの伝統によるミニマリストのプロセス作品であり、8 チャンネルがニ自然短調の同じ 12 音のパターンを異なる進行速度で演奏し、数分から数時間のタイムスケールにわたってドリフトするチャンネル間のカノン的関係を生み出します。曲 6 はト調ドリアンによるポリメトリックカノンであり、4 つのカノン声部が同じ 4 音の主題を共有しながら異なるティックストライド(1 主題位置あたり 4、3、5、7 ティック、4/4、3/4、5/4、7/4 拍子に対応)で進行し、1680 ティックの拍節上位周期において真の 4 声部ポリフォニック対位法を生み出します。曲 7 は微分音のドローン作品であり、8 声部が A2 基音の純正律による倍音列の部分音 1、2、3、5、7、9、11、13 を、12 平均律の MIDI ピッチと整数セント単位のデチューンオフセットを通じて実現し、ホストの set_detune ネイティブを連続的な全スペクトルのピッチ制御メカニズムとして実演します。曲 8 は 108 BPM のハ長調による教科書的な主流ポップソングであり、平行短調のブリッジと最終コーラスに向けた変ニ長調への半音転調を伴い、実装エンジンが実験的な楽曲を扱うのと同じ容易さで従来の商業ポップの作曲を扱うことを実演します。曲 9 はチップチューンのコアを持つ半実験的なループ作品であり、スケール(ハ長調、イ短調、ニ調ドリアン、ニ調フリギアンドミナント)とリード波形(ノコギリ波、矩形波、パルス波、三角波)の 4 かける 4 のマトリクスにわたる 16 の反復バリエーションを提示し、各反復は混乱ゾーン、ブリッジ、転調、および最終コーラスを備えた 12 セクションのポップ形式です。テンポは区分化されたランプと反復ごとに 1 つの連続正弦波混乱ゾーンを通じて 60 から 300 BPM を移動し、フルメタループあたり約 50 分です。完全な実装仕様については docs/extras/SONG_3_SPEC.mddocs/extras/SONG_4_SPEC.mddocs/extras/SONG_5_SPEC.mddocs/extras/SONG_6_SPEC.mddocs/extras/SONG_7_SPEC.mddocs/extras/SONG_8_SPEC.md、および docs/extras/SONG_9_SPEC.md を参照してください。

メタノート

この文書は、Keleusma ホストアプリケーションのための具体的なドキュメント例としても役立ちます。その節の形、散文の深さ、およびマニュアルとソースドックストリングの関係そのものがパターンです。別の領域で Keleusma ホストを構築するチームは、独自のマニュアルに同じ形を採用できます。

作曲論と音楽理論は対象外です。スクリプト作者向けの節の締めくくりでは、プログラム的作曲をさらに追求したい読者のために、いくつかの持続的なカテゴリ名を列挙しています。


楽曲を作る

楽曲は、エントリーポイント loop main(input: Word) -> Word を持つ Keleusma プログラムです。ホストは起動時に一度 main を呼び出し、その後16分音符のティックごとに Vm::resume を呼び出します。スクリプトの本体は現在のティック値に対して実行され、0個以上のホストネイティブ関数を呼び出し、制御を譲ります。イテレーション間でホストアリーナがリセットされるため、イテレーションごとのアリーナ割り当ては、スクリプト作者にコストをかけずに解放されます。

メンタルモデル

スクリプトはオーディオを合成しません。スクリプトはイベントをスケジュールします。各ボイスの合成状態はホストが所有します。スクリプトはホストネイティブ関数呼び出しを通じてその状態に書き込みます。オーディオスレッドはその状態から読み取り、Keleusma 仮想マシンに一切入ることなくサンプルをレンダリングします。

したがって、楽曲は連携して動作する3つの異なる状態から成ります。データセグメントは、ティックをまたいで持続するチャンネルごとの位置カウンタとシーケンサレベルの状態を保持します。ホストボイス状態は、波形、エンベロープ、スピーカーごとの音量などの楽器パラメータを保持します。スクリプト本体は各ティックで、どのボイスを再生、消音、または再構成するかを決定します。

init ブロック

同梱されるすべての楽曲は、state.init という名前のスロットで保護された一回限りのセットアップブロックで loop main 本体を開始します。このスロットは起動時にゼロであり、ホストが楽曲のロードごとにデータセグメントをゼロクリアするため、ホットスワップをまたいでもゼロのままです。init ブロックは、その楽曲のボイス状態を構成するすべてのホストネイティブを呼び出し、その後 state.init を1に設定します。

init ブロックは、楽器パラメータが構成されるスクリプト内の唯一の場所です。チャンネルは無効な状態で始まります。init ブロックは楽曲が使用するチャンネルを有効化し、その波形、エンベロープ、音量を構成します。init ブロックで言及されていないチャンネルは無効なままで、音を出しません。

データセグメントの慣習

ホストはデータセグメントに23個のスロットを予約します。最初の7スロットはシーケンサレベルの状態を保持します。残りの16スロットは、8ボイスのチャンネル全数に対するチャンネルごとの位置カウンタと残りティックカウンタです。

スロット0、init は、上記で説明した一回限りのセットアップゲートです。

スロット1、loop_count は、進行がラップしたときにスクリプトによって加算されます。楽曲はこれを使用して、後続のループでの動作を変化させます。初回のみのイントロセクションは、loop_count がゼロのときにのみ実行できます。フェードアウトは、loop_count が選択された閾値に達すると開始できます。移調は奇数ループごとに適用できます。

スロット2、section は、楽曲セクションのポインタです。複数部構成を持つ楽曲は、これを使用して現在どのセクションがアクティブかを追跡します。値0は最初のセクションを、1は次のセクションを、という具合に表します。スクリプトはこの値を読み取って正しい音符テーブルにディスパッチします。

スロット3から6、user0 から user3 は、ホストが関知しない状態のための汎用スロットです。適切な用途には、乱数シード、移調オフセット、チャンネルごとのミュートマスク、フィルパターンセレクタ、その他楽曲が追跡する必要のあるものが含まれます。

スロット7から14は idx: [Word; 8]、8ボイスのチャンネル全数に対するチャンネルごとの位置カウンタを保持します。スロット15から22は rem: [Word; 8]、対応するチャンネルごとの残りティックカウンタを保持します。スクリプトは、ch[0, 8) の範囲の Word である場合、インデックス付き配列形式 state.idx[ch] または state.rem[ch] を通じて各カウンタにアクセスします。コンパイラは、基盤となるフラットスロット領域に対する境界チェック付きのインデックス付き読み書きを発行します。範囲外のインデックスは、別のカウンタを静かにアドレス指定するのではなくトラップします。すべてのチャンネルを走査する必要のあるスクリプトは、for ch in 0..8 { ... state.idx[ch] ... } を使用でき、コンパイラは Value::Array を具体化することなく、そのイテレーションを直接的なインデックス付きスロット読み取りに落とし込みます。

データセグメントは、スキーマレベルではホスト所有、意味レベルではスクリプト所有です。ホストはスロットを予約してゼロクリアします。各スロットが何を意味するかはスクリプトが決定します。上記の慣習は同梱されるすべての楽曲によって守られ、スキーマが一覧全体で一貫して保たれます。

ティックごとの本体構造

init ブロックの後、各チャンネルブロックは同じ形に従います。スクリプトは、チャンネルの残りティックカウンタがゼロかどうかを確認します。ゼロの場合、チャンネルの音符テーブルで次の音符を調べ、その音符が休符かどうかに基づいて host::play または host::silence を呼び出し、残りティックカウンタを音符の長さに設定し、チャンネルの位置カウンタを進めます。そうでない場合、スクリプトは残りティックカウンタをデクリメントします。

このパターンはティックごとのコストを有界に保ちます。各ティックは、一定数のネイティブ関数呼び出しと少量のデータセグメント算術を実行します。Keleusma が言語レベルで提供する有界ステップ保証は、全体を通じて成り立ちます。

シーケンサ状態の扱い

loop_count を使用する楽曲は、チャンネル0の位置カウンタと同じ境界でスロットを加算すべきです。なぜなら、チャンネル0は通常最も長いパートを保持するためです。チャンネル0の位置カウンタがゼロにラップすると、その楽曲は1つの完全な進行を完了しています。インクリメントはラップの直後に行われます。

section を使用する楽曲は、楽曲作者が定義するセクション境界でスロットを進めるべきです。セクションは小節数、特定の loop_count 値、または手動のスケジュールに結び付けられる場合があります。その後 section の読み取りがチャンネルごとの音符テーブルの検索を駆動し、各セクションが独自の進行を持てるようになります。

ホットスワップと楽曲名のアナウンス

ホストは、host::song_name を通じてロードごとに一度、楽曲のタイトルをアナウンスします。init ブロックは静的な文字列リテラルでこのネイティブを呼び出します。同じ文字列での後続の呼び出しはホストによって静かに無視されます。ホットスワップのたびに、ホストは追跡している名前をクリアするため、次の楽曲は無条件にアナウンスされます。

プログラム的作曲のためのリソース

作曲理論と音楽的実践はこの文書の対象外です。プログラム的作曲をさらに追求したい読者は、チップチューンコミュニティが維持するトラッカーモジュールのドキュメント、アルゴリズム作曲に関する概説、および Music Macro Language のドキュメントを参照するとよいでしょう。これらの伝統はいずれも長い歴史と活発なコミュニティを持ち、この文書が及ばない深さを提供できます。


例を発展させる

この節は、この例を自身のプロジェクトに取り込みたい Rust ホスト開発者に向けたものです。2つの道を扱います。1つ目はこの例をより大きなアプリケーションに組み込むものです。2つ目はこの例をより本格的なツールに拡張するものです。この2つの道は関心事の大部分を共有しており、まとめて扱います。

main と run の分離

この例は、アプリケーションの外装を組み込み可能なホストループから分離しています。main 関数はコマンドライン引数の解析やその他のプロセスレベルの関心事を担います。run 関数は、Keleusma 仮想マシンの構築、オーディオデバイスのオープン、ホストネイティブ関数の登録、およびティック・アンド・イールドのサイクルの駆動という、実際のホスト作業を担います。

この例を別のプログラムに組み込む開発者は、run の本体を自身のホストコードにコピーします。この関数は現在引数を取りません。楽曲一覧、アリーナ容量、デフォルトテンポ、または代替のホストネイティブ登録を受け入れるように拡張することは局所的な変更です。

この例をより本格的なツールに拡張する開発者は、run をそのまま保ち、main を成長させます。開始楽曲の選択、代替テンポ、または別のオーディオデバイスのためのコマンドラインフラグは、run に触れることなく main に着地します。この2つの関数は別々に保たれるため、ソースを読む組み込み者は、どの部分をコピーしどの部分を破棄すべきかを認識できます。

ネイティブ登録の境界

register_natives 関数は、この例が提供するすべてのホスト・スクリプト間の橋渡しを担います。各エントリは、共有状態をキャプチャするクロージャを伴う別々の vm.register_native_closure 呼び出しです。このパターンは意図的に冗長です。読者は任意のネイティブを、その名前からその効果まで2回の読みでたどることができます。

本番のホストは、おそらくこれをマクロまたは登録ヘルパーを通じて短縮するでしょう。組み込みガイドで説明されている同梱の register_library トレイトが、そのステップのためにサポートされている抽象です。この例では、データフローがページ上で明示的に保たれるように、それを使用していません。

演習への案内

この例のモジュールレベルのドキュメントコメントは、例が製品ではなく例のままであるように意図的に省かれた10の実質的な機能を列挙しています。このリストには、トレモロ、フィルタエンベロープ、ディレイ、リバーブ、アルペジオ、ポリフォニックボイス割り当て、サンプル再生、周波数変調合成、ウェーブテーブル合成、リアルタイムビジュアライザ、および Musical Instrument Digital Interface 入力についての、おおまかな Rust 側のコード行数見積もりが記載されています。この例を拡張する開発者は、これらのいずれかを出発点として選べます。見積もりはおおまかであり、正確な数を約束するためではなく作業を範囲付けするためのものです。

データセグメント拡張の注意点

すべての楽曲スクリプトで宣言される data state ブロックはデータセグメントスキーマを定義し、一覧内のすべての楽曲は同じスキーマを宣言しなければなりません。スクリプトはレイアウトを宣言します。ホストは空の initial_data ベクタを replace_module に渡すため、セグメントはスワップのたびにゼロに再初期化されます。data state スキーマが現在ロードされているものと異なる楽曲は、ホストが replace_module_unchecked を選択しない限り、replace_module のスキーマハッシュチェックによってホットスワップ時に拒否されます。

推奨は、いずれの楽曲を書く前にデータセグメントスキーマを事前に確定することです。ホスト作者と楽曲作者は、シーケンサ状態、チャンネルごとのカウンタ、およびアプリケーション固有の記録管理にどのスロットが必要かを協働して決めます。スキーマが定まれば、すべての楽曲はそれを対象にします。

しかし、プロジェクト途中での変更は起こります。ホスト作者は、新しいシーケンサ機能をサポートするためにスロットを追加する必要があるかもしれません。そのコストはホスト作者にとっては小さいものの、すでに書かれたすべての楽曲にとっては無視できないものです。なぜなら、各楽曲の data state ブロックを新しいスキーマに一致するように更新しなければならないためです。緩和策には、スキーマ変更をより広範なコンテンツ改訂と一致するようにスケジュールすること、スキーマの成長がスキーマレベルではなくそれらのスロット内で起こるように寛大な user スロットを前もって予約すること、およびスキーマバージョンをどこか目に見える場所に記録することが含まれます。すべての data state ブロックの先頭にバージョン刻印のコメントを付けるのは1つのアプローチです。

Cargo フィーチャの要件

ピアノロールの例は、ビルドに sdl3-example Cargo フィーチャを必要とします。このフィーチャは Simple DirectMedia Layer 3 依存を取り込み、cmake で SDL3 をソースからビルドします。例の Cargo.toml 内の required-features 宣言は compileverifysdl3-example を列挙しており、最初の2つはデフォルトで有効です。したがってビルドコマンドは cargo run --release --example piano_roll --features sdl3-example です。

静的な文字列リテラル(同梱楽曲が host::song_name 呼び出しで使用)は V0.2.0 では無条件です。廃止された V0.1.x の text cargo フィーチャはもはや存在しません。異なるオーディオバックエンドを持つ、この例から派生したホストは、sdl3-example の要件を自身のバックエンドが必要とするものに置き換えます。


組み込みパターン

この節は、Keleusma を別の制御ループアプリケーションに組み込むための参考としてこの例を学びたい開発者に向けたものです。オーディオが選ばれた領域であるのは、オーディオが監査しやすく、リアルタイムの期限のプレッシャーがほとんどの開発者にとって馴染み深いためです。ここで機能するパターンは、期限の逸失や状態の破損のコストが著しく高くなり得る他の制御ループにも転用できます。

なぜこの例が標準例として選ばれたか

ピアノロールは Keleusma ホストの全表面を実践します。エントリーポイントとして Stream ブロックを使用します。データセグメントを通じてティックをまたいで状態を持続させます。loop main を通じて決定論的ステップのイテレーションを実行します。ホットコードスワップを乗り切ります。2つのスレッド、1つは Keleusma 仮想マシンを実行し、もう1つは異なるレートで出力をレンダリングするスレッドを協調させます。スクリプトの論理的なイベントとホストの物理的な状態を橋渡しするためにホストネイティブ関数を使用します。

これらのパターンはいずれもオーディオに固有ではありません。同じアーキテクチャは、ホスト所有の状態に対して規則的な周期でイベントをスケジュールする、任意のレートで実行される制御ループに役立ちます。

状態の分離

この例は状態を2つの領域に分割します。ホスト所有の領域は、オーディオボイス、マスター音量、ティック間隔、および楽曲名の重複排除キャッシュを保持します。この状態は同期プリミティブの背後にある Rust 型に存在します。オーディオスレッドがそれを読み取ります。スクリプトはホストネイティブ関数呼び出しを通じてそれに書き込みます。

スクリプト所有の領域は、チャンネルごとの位置カウンタ、ループカウント、セクションポインタ、およびアプリケーション固有のユーザースロットを保持します。この状態は Keleusma データセグメントに存在します。スクリプトはそれを直接読み書きします。ホストはロードのたびにそれをゼロクリアします。

この分離には原則があります。ホスト所有の状態は、Keleusma 仮想マシン呼び出しを行うことなくホストのホットパスが読み取る必要のあるすべてです。スクリプト所有の状態は、その意味論をホストが関知することなく、スクリプトがイテレーションをまたいで推論する必要のあるすべてです。

この分離は一般化されます。Keleusma プログラムが何が起こるべきかを決定し、Rust スレッドがその決定を実行する任意の制御ループは、状態を同じように分割すべきです。スクリプトの不変条件はデータセグメントに存在します。ホストの不変条件は、適切な同期の背後にある Rust 型に存在します。ネイティブ関数の境界が唯一の橋渡しです。その橋渡しは有界で、型付けされ、監査可能であり、これらは真剣なホストがそのアプリケーションの他のすべての境界に望むのと同じ性質です。

ティック・アンド・イールドの境界

スクリプトはオーディオサンプルごとではなく、16分音符のティックごとに一度制御を譲ります。この決定は意図的でした。

サンプルレートでのイールドは細かすぎます。毎秒4万8000サンプルでは、スクリプトはイールドごとに約20マイクロ秒の予算しか持たず、これはジッタから逃れるのが難しく、メインスレッドでも実行されるホスト作業に余裕を残しません。

非常に粗い粒度のイールドも間違っています。それはスワップ、再起動、再構成の機会の間にホストに長い間隔を残し、その間隔中にホストが受け取ったあらゆる入力は、現在のティック境界ではなく次のティック境界に着地することになります。毎分120拍での16分音符のティックは、イールド間で125ミリ秒に着地します。これはスクリプトの作業にとって快適な予算であり、ホットスワップとコマンド処理にとって許容できる遅延です。

一般的な規則は明快です。ティックレートは、スクリプトが決定を行う最も高い有意な周波数であるべきです。10ミリ秒ごとに決定を行う制御ループは、1ミリ秒ごとでも100ミリ秒ごとでもなく、10ミリ秒ごとに制御を譲るべきです。誤った粒度を選ぶホストは、遅延か予算のプレッシャーのいずれかで代償を払います。

ホットスワップの意味論

ホストは、仮想マシンが VmState::Reset 状態にあるときにのみ Vm::replace_module を呼び出します。Reset 状態は Stream ブロックのイテレーション間の境界です。その時点でスクリプトのスタックは空であり、データセグメントが唯一の生きているスクリプト所有の状態です。

ホストは、新しいゼロ初期化されたベクタを replace_module に渡すことでデータセグメントをリセットします。ホストはまた、スワップを発行する前にホスト所有のボイス状態と楽曲名の重複排除キャッシュをリセットします。したがって、入ってくるスクリプトの init ブロックは、両領域のクリーンな状態に対して実行されます。

関連する原則は、ホットコードスワップは、アプリケーションの不変条件が有界でホストが読み取り可能な領域に存在する場合にのみ安全である、ということです。Keleusma は、スワップが Reset 境界で起こることを要求することでこれを強制します。音楽以外の領域に Keleusma を組み込むホストアプリケーションは、同じ制約を尊重すべきです。スワップを乗り切る必要のあるあらゆる状態はデータセグメントに属し、ホストは同じ境界でホスト所有の領域をリセットして、入ってくるスクリプトが古い状態を観測しないようにすべきです。

並行性の選択

この例は、オーディオスレッドとメインスレッド間で共有される単一の Mutex<[Voice; 8]> を使用します。ロックはオーディオコールバックごとに1回のスナップショットコピーのために取得されます。競合ウィンドウはマイクロ秒単位です。

この選択は明快さのために行われました。読者は1回の読みでデータフローをたどります。このパターンは、ロックが競合点となる数百のボイスを持つホストでは通用しないでしょう。その領域で動作する本番のホストは、ボイスごとのアトミック型、ロックフリーキュー、またはトリプルバッファ構成のいずれかに移行するでしょう。

一般的な規則は、期限の予算を満たす最も単純な同期プリミティブを選ぶことです。より複雑なプリミティブへの昇格は、プロファイリングが競合を示すときに正当化できます。抽象的なスケーラビリティの懸念だけでは正当化されません。より単純なプリミティブはデータフローを可視のまま保ち、これは例にとってより重要であり、設計段階で認められる以上に本番でもしばしばより重要です。

ネイティブ登録

この例は、起動時にすべてのネイティブ関数を一度登録します。Keleusma 仮想マシンは、いずれのスクリプトがロードされる前にのみネイティブ関数の登録を受け付けます。したがって登録の境界は、ホストの初期化とホストの運用の間の境界です。

異なるスクリプトに異なるネイティブ関数セットを見せたいホストは、単一の仮想マシンインスタンス内でそれを行うことはできません。利用可能な選択肢は、上位集合を登録してスクリプトがどれを呼ぶかを選べるようにすること、複数の仮想マシンインスタンスを使用すること、またはスクリプト変更の間でホストを再ロードすることです。この例は最初の選択肢を取ります。すべての楽曲は完全なネイティブ関数の表面を見て、楽曲は必要な部分集合を使用します。

トレードオフは、後でネイティブ関数を追加すると、ロードされているすべてのスクリプトが、その新しい関数を使用する場合には再コンパイルを必要とすることです。このトレードオフは、スクリプト一覧が事前に既知でネイティブが早期に安定するホストにとっては許容できます。ネイティブの表面が真に動的なホストは、複数仮想マシンのパターンを検討すべきです。

リセットの慣習

ホストがリセットを所有します。スクリプトは自身をリセットしません。ホストが新しいモジュールをロードするとき、ホストはデータセグメントをクリアし、ホスト所有のボイス状態をクリアし、その他のホスト側のロードごとのキャッシュをクリアします。その後スクリプトの init ブロックが、スクリプトが必要とする値を書き込みます。

この慣習はスクリプトを単純に保ちます。スクリプト作者は、以前の楽曲がステートマシンを予期しない構成に残した場合のための防御的なコードを書きません。ホストがクリーンな状態を保証し、スクリプト作者はその保証を信頼できます。

一般的な原則は、リセットはホストの責任である、ということです。それをスクリプトに押し付けるのが適切なのは、ホストがどの状態をクリアすべきか判断できない場合のみであり、これは実際にはまれです。ほとんどのホスト側の状態は既知の形と既知のリセット値を持ち、ホストはそれを直接クリアできます。

締めくくり

ピアノロールの特定のオペコードは他の領域には転用できません。それらを取り巻くパターンは転用できます。状態の分離、ティック・アンド・イールドの規律、Reset で境界付けられたホットスワップ、単純な同期、ホスト所有のリセット、フラグ付き init ブロックを通じた一回限りのスクリプト初期化は、すべて別の領域の組み込み者が直接採用できるこの例の特徴です。この例は、読者が各パターンを独立して吸収し、それらを別のアプリケーションに合うホストへと組み立てられるように、大きさと形が整えられています。