5

オーディオ ストリームを録音する最適な方法を見つける必要があります。低レベルのコードは既に C++ で作成しており、その一部を C# にインターフェースしています。

だから私は浮動小数点数の配列の配列 - オーディオ信号 - を与える C++ コールバックを持っています。現時点では、私の C++ ライブラリは wav 形式でファイルに直接データを記録しており、記録が終了したときに C# アプリに通知するだけです。

しかし、UI 側でのインタラクティブ性、たとえば「無限」のプログレス バー、記録されるデータの量、キャンセル ボタンなどをもっと増やしたいと思っています。私は .NET と C# のメモリ管理についてほとんど知らないので、効率的に作成する方法がわかりません。

データを内部に配置して、後で配列のようにアクセスできる、C# に高速でサイズ変更可能なコンテナーはありますか?

また、波形イメージを作成したいと思います。私はすでにこれらのことをC++で行っていますが、どういうわけか、メッセージを書きすぎたり、オブジェクトを転送したりするという考えは好きではありません.

まとめると、次のようになります。

  1. 私はいくつかのことを行う C++ アンマネージ コールバックを持っており、データを処理したら C# メソッドを呼び出したいと思います。C プロトタイプは次のようになります。

    void プロセス (float **signal, int n); (通常は [2][n] - ステレオ用)

    C#に相当するものは何ですか?そのC++コールバックからどのように呼び出すのですか?

  2. 連続ストリームを書き込み ( mem.put(float[][] data, int size) など)、配列として、または他の簡単な方法で読み取るのに最適なクラスは何ですか (たとえば、そこから wav ファイルを保存するため、または波形ビットマップを作成します)

  3. C# で実行すると、パフォーマンスが大幅に低下しますか? (C#関数などを呼び出すマネージC ++ラッパー...そしておそらくいくつかのDSPのもの)または私はただの偏執症ですか?:)

乾杯、

パブロックス

私の解決策

わかりました私はそれをそのように解決しました:

私のC++ヘッダーファイルでは、転送構造を取得しました:

public ref struct CVAudio {
   public:
    float *left;
    float *right;
    int length;
   };

次に、マネージ C++ クラスで次のように宣言しました。

delegate void       GetAudioData([In, Out] CVAudio^ audio);

次に、オーディオを初期化するメソッドへの引数として使用できます。

void                initializeSoundSystem(void *HWnd, GetAudioData ^audio);

そのデリゲートには、次の C プロトタイプもあります。

typedef void (CALLBACK *GETAUDIODATA)(CVAudio ^a);

内部 C++ クラスで次のように使用されます。

void                initializeSoundSystem(HWND HWnd, GETAUDIODATA audio);

次に、最初のメソッドの本体は次のとおりです。

void VDAudio::initializeSoundSystem(void *HWnd, GetAudioData ^audio) 
{
    HWND h = (HWND) HWnd;

    acb = audio;
    pin_ptr<GetAudioData ^> tmp = &audio;

    IntPtr ip = Marshal::GetFunctionPointerForDelegate(audio);
    GETAUDIODATA cb = static_cast<GETAUDIODATA>(ip.ToPointer());

    audioObserver->initializeSoundSystem(h, cb);
}

audioObserver の本体は、そのコールバックをオブジェクトに格納し、オーディオ関連の処理を行うだけです。

コールバックは、次のように処理メソッドで呼び出されます。

            VDAudio^ a = gcnew VDAudio();
            a->left = VHOST->Master->getSample()[0]; //returns left channel float*
            a->right = VHOST->Master->getSample()[1];
            a->length = length;
            (*callback)(a);

そして、C# デリゲートの本体:

public void GetSamples(CVAudio audio)
{
    unsafe
    {

        float* l = (float*)audio.left;
        float* r = (float*)audio.right;

        if (l != null)
        {

            SamplePack sample = new SamplePack();

            sample.left = new float[audio.length];
            sample.right = new float[audio.length];

            IntPtr lptr = new IntPtr((void*)l);
            IntPtr rptr = new IntPtr((void*)r);

            Marshal.Copy(lptr, sample.left, 0, audio.length);
            Marshal.Copy(rptr, sample.right, 0, audio.length);

            this.Dispatcher.Invoke(new Action(delegate()
            {
                GetSamples(sample);
            }));
        }
    }
}

したがって、おそらくそれは最善のコードではありません-私にはわかりません。それが機能しているとしか言えず、漏れていないようです:)

4

1 に答える 1

2

質問 1: アンマネージ C シグネチャに一致する C# デリゲートを C++ に渡したいと考えています。その後、これが C 関数ポインターであるかのようにコールバックできます。例えば、

delegate void ASIOCallback(IntPtr signal, int n);

次に、シグナル メモリをマネージド バッファーに手動でマーシャリングするか、アンマネージド バッファーにコピーする必要があります。どちらもMarshal クラスのメソッドを使用して実行されます。これは質問 2 につながります。

質問 2: いくつかの選択肢があります。主に、データをマネージド メモリとアンマネージド メモリのどちらに保存するかによって決定されます。アンマネージ メモリに保存すると、アンマネージ コードとのやり取りが容易になり、理論的には、データのコピー数を減らすことでより効率的になる可能性があります。ただし、(配列、ジェネリック コレクションMemoryStreamなどを介して) マネージド メモリに格納すると、マネージド コードからのアクセスがはるかに簡単になります。

質問 3: パフォーマンスに関して考慮する必要がある 2 つの考慮事項は、生の数値計算とデータの複製です。一般に、C# の数値演算は C/C++ よりも遅くはありません。このタイプのパフォーマンスを比較するための優れた情報源が Web 上に複数あります。データのコピーに関する限り、これにより問題がさらに発生する可能性があると思います。データのコピーに費やされる時間と、アプリケーションが使用する余分なメモリの両方が原因です。マネージ メモリとアンマネージ メモリの両方にデータのコピーが必要かどうかを検討する必要があります。これは、C# や C++ でどれだけアクセスしたいかによって決まると思います。

于 2011-10-01T17:33:39.027 に答える