私は現在、主要な要素がリアルタイムで信号処理を行うことができるソフトウェアであるプロジェクトに取り組んでいます。
基本的な考え方/コンセプトは次のとおりです。オーディオ データは、(USB) オーディオ インターフェイス (96 kHz、24 ビットで動作) から継続的にキャプチャされます。次に、「処理チェーン」(「仮想効果チェーン」など)を介して実行され、各要素がデータに対して任意の処理を実行できます。データは、シンクに到達するまで、ある要素から次の要素に渡されます。シンクに流れ込んだデータは、出力のためにオーディオ インターフェイスに送り返されます。
シグナル チェーンは、何らかの「処理」メソッドを実装する「オブジェクト」で構成され、1920 サンプル (20 ms) サイズのデータのチャンクで機能します (ただし、この数は変更される可能性があります)。
「疑似コード」における基本的な考え方。
while (true) {
float[] samples = audio.read(BLOCK_SIZE);
foreach (Effect e in signal_flow)
e.process(samples);
audio.write(samples);
}
オーディオ I/O に ALSA API を使用する最初のプロトタイプが Python で実装されました。3 つの個別のスレッドが開始されます。
最初のブロックは ALSA から継続的に読み取りを行い (これはブロッキング操作です)、データをキューに入れ、次のブロックの読み取りを続けます。(ALSA では、少なくとも 20 ミリ秒ごとに "read(...)" する必要があるため、そのためには非常に軽量なスレッドが必要です。これは、データの読み取りと保存のみを行うため、時間の制約を満たすことができます。)
2 つ目は、このキューからの入力からサンプルを読み取り、シグナル チェーンを介してそれらを配置し、最終的に出力用の別のキューに入れます。
3 番目のスレッドは、この出力キュー (ブロッキング操作) から読み取り、ブロックを ALSA に書き込みます。
ここには基本的に 2 つの「数値」変数があります。1 つはブロック サイズで、もう 1 つは処理を開始する前に出力キューに「プレポスト」するブロックの数です。 "出力バッファのアンダーランニングなし。さらに、これらのことを別のスレッドまたは別のプロセスで行うこともできます。
いくつかの深刻なデバッグ (信号発生器とオシロスコープの両方の使用が含まれます!) の後、私はついにこのアプローチを実行することができました。最大の問題は、ALSA API のバグでした。つまり、「読み書きされるフレーム数」として指定される「period_size」は、実際には読み取られたフレーム数のように見えますが、バイト数は書いた。入力と出力の両方に 1920 (96 kHz のサンプリング レートで 20 ミリ秒のオーディオ) を指定すると、出力の 1/4、無音の 3/4、出力の 1/4 しか得られません。入力と出力の両方に 1920 * 4 = 7680 を指定すると、デバイスのバッファがこの数のフレームを読み取るのに十分な大きさではないというエラーが表示されます。入力に 1920 を指定し、出力に 7680 を指定すると、完全に機能します (また、キューが「オーバーフィル」することもありません)。
それが解決された後も、深刻な問題が 1 つ残っています。それは、レイテンシです。
このプロジェクト全体は基本的に自分のエフェクトを作成するために始めたので、このソフトウェアをステージ/ライブ用のエフェクト ユニットとして動作させたいと考えています。2 つのイベントの間隔が 30 ミリ秒未満の場合、たとえば 2 つのストロボ ライトが 30 ミリ秒間隔で発射されると、人間の脳はどちらが最初でどちらが 2 番目であるかを判断できなくなると読んだことがあります。したがって、30 ミリ秒以下の遅延は、基本的に「瞬時」と認識されます。これは、イベントが「融合」し、「1 つ」として認識されるしきい値です。レイテンシーが非常に低い場合、どのイベントが最初であるかを判断できませんでした。たとえば、ギタリストは、弦を叩いてから PA から音が出たのか、それとも PA から音が出たのかを知ることができなくなりました。つまり、基本的に「ゼロ レイテンシー」のように感じられ、実際のアンプのようになります。
私が現在到達しているレイテンシーは...まあ...まだ正確に測定する機会はありませんでしたが、10分の1秒のオーダーで、200ミリ秒または300ミリ秒です. 行き過ぎです!I/O 用に個別のスレッドとプロセスの両方を試しました (Python では、「グローバル インタープリター ロック」によりスレッドが実際に高速になるかどうかわからないため)。プロセス間通信に関連するオーバーヘッドと、2 つの I/O プロセスが「集中的な」ことを何も行わないという事実。ブロッキング操作を待機し、高速 I/O を実行します。
別の発言として、ユーザーが信号パスを構成し、エフェクト「デバイス」のパラメーターを調整できるように、アプリケーションで Web サーバーを (もちろん別のスレッドまたはプロセスで) ホストする必要があると言いたいです。直感的な方法。
明らかに、レイテンシーを下げる必要があります。私には今、進むべき道がいくつかあります。
私の最初のアイデアは、ALSA を「低遅延」と言われる JACK に置き換えることです。欠点は、ADC/DAC での正確なレベルを制御できなくなることです (JACK は、ハードウェアが何をしようとも、既に [-1.0, 1.0] にスケーリングされている浮動小数点数で常に動作します) およびオーバーサンプリング レート (私がジャックはサンプリングレートを「指示」します-私はもう選択できません-ALSAを使用すると、使用したいサンプリングレートを選択でき、ハードウェアがそれから逸脱した場合、透過的にアップサンプル/ダウンサンプルします)、ブロッキング私/O (非常に優れたパラダイムだと思います) はコールバック メカニズムに置き換えられ、タイミングの制約を満たすのはおそらくはるかに困難です。(JACKで自分で「バッファリング」することはできません。常に一定の時間で処理を行う必要があります。)また、JACK API は、一般的に ALSA API よりも複雑に見えます。おそらくALSAをJACKに置き換えることができますが、ALSAがすでにかなり「低レベル」のAPIであり、JACKが構築されていることを考えると、どれだけ「勝つ」ことができるかわかりませんが、おそらく維持するために非常に具体的な方法で使用しますレイテンシーが低い。正直なところ、ALSA と「下」が原因でその遅延がどの程度なのか、「上位レイヤー」が原因でどの程度の遅延が生じているのか正確にはわかりません。
私は現在、解釈された、ガベージコレクションされた言語を利用しています。これは、リアルタイムの要件には適していません。しかし、代替手段は何ですか?もちろん、リアルタイム アプリケーションについて考えるときは C が思い浮かびますが、重大な欠点があります。
2.1 - ソフトウェアに Web ベースの UI が必要です。Python で Web サーバーを作成するのは簡単です。Python 標準ライブラリにそのためのクラスがあります。C で Web サーバーを作成するのは面倒です。基本的に、「Web」を実行できるCには何もありません。ソケット API だけに基づいてゼロから始める必要があります。幸運を!:-(
2.2 - Python は数値をサポートしています。numpy と scipy があるため、ベクトル データ型、行列乗算、高速フーリエ変換、高速畳み込み、補間 (スプラインなど) があります。これはすべて、ここで目前の問題に非常に役立ちます。C では、これらすべてが失われます。つまり、これを使わずに仕事を終わらせる必要があるか (ほとんどありません)、または C でこれらすべてを自分で再作成する必要があり、これには時間がかかり、エラーが発生します。傾向がある。
2.3 - Python には、マルチプロセッシングとマルチスレッドの「ハイレベル」サポートがあります。同期されたキューを介してプロセスとスレッド間で通信でき、「スレッド プール / レプリケートされたワーカー」スタイルの方法でソリューションを実装できます。C では、POSIX スレッドと、mutice のような低レベルの同期プリミティブに頼る必要があります。(「mutex」の正しい複数形ですか? 「Unix」のように --> 「Unices」?) 繰り返しますが、これには時間がかかり、エラーが発生しやすくなります。
使用する言語に関する提案はありますか? 私がよく知っている言語には、Python、C-Sharp、Java、C、Go、および少しの C++ が含まれます (その言語はまったく必要ありません。低レベルである場合は C を使用し、高レベルである場合は私が使用します)。ガベージコレクションされたインタープリターまたはバイトコードでコンパイルされた言語を使用する)、および一部の Web テクノロジ (特に、UI に関して役立つ AJAX を使用した JavaScript など)。
これらはすべてLinuxディストリビューションで実行する必要があり、できれば依存関係やライブラリをできるだけ少なくする必要があります。したがって、「標準」ライブラリは「エキゾチック」ライブラリよりも優先されます。プラットフォームに依存しないこと、特に Windows でも実行できることはプラスですが、厳密には必須ではありません。
UIが実際の処理(リアルタイムである必要がある場合は「Cで行う必要がある」かもしれません)とは異なる言語で実装されていても問題ありません(Webサーバーに行くことが頭に浮かびます)。これにより、両方が別々のプロセスで実行され、たとえばUnixドメインソケットを介して通信するため、非常に複雑になります。これにより、すべてのデータをバイトストリームとして表現するために前後に渡す必要があります)、したがって、プロセス間通信プロトコルを設計し、このレベルでも多くの解析を行う必要があります。この巨大なオーバーヘッドを回避し、単一の言語で実行する機会はありますか?
他のアイデアはありますか?具体的には、ALSA から JACK に移行することで何か得られることはありますか? まだ検討していない他の選択肢はありますか?
お時間をありがとうございました!