55208


お世話になります。  
BASSというオーディオライブラリで三角波の再生を行いたいと考えております。  
[How can I generate a triangle wave with
"BASS_SampleCreate"?](http://www.un4seen.com/forum/?topic=18528.msg129941#msg129941)を参考にC++のソースコードをC#で書き直そうとしています。  
C#には「Buffer.BlockCopy」というのがあり、データをコピーできそうなことはわかったのですが、「srcOffset」、「dstOffset」、「count」はどのように指定すればよいのでしょうか。  
そもそもC++コード内の

[code]

        memcpy(&data[a * period], data, period * sizeof(*data));
    
[/code]

というのがいまいち理解できていません。  
C++のコードは全くといっていいくらい読んだことがないため、的外れなことを書いていたら申し訳ないのですが、何かアドバイスを頂けないでしょうか。

## C++のソースコード

[code]

    int period = 64;
    int cycles = 100 * 440;
    short *data = new short[cycles * period];
    int a;
    int period4 = period / 4;
    for (a = 0; a < period4; a++) {
        data[a] = 32767 * a / period4;
        data[period4 + a] = 32767 - data[a];
        data[2 * period4 + a] = -data[a];
        data[3 * period4 + a] = -32767 + data[a];
    }
    for (a = 1; a < cycles; a++)
        memcpy(&data[a * period], data, period * sizeof(*data));
    delete[] data;
    
[/code]

## 現状のC#のソースコード

[code]

    int period = 64;
    int cycles = 100 * 440;
    short[] data = new short[cycles * period];
    int a;
    int period4 = period / 4;
    for (a = 0; a < period4; a++){
    data[a] = 32767 * a / period4;
    data[period4 + a] = 32767 - data[a];
    data[2 * period4 + a] = -data[a];
    data[3 * period4 + a] = -32767 + data[a];
    }
    for (a = 1; a < cycles; a++){
    // ここでつまずいています
    }
    
[/code]


CC BY-SA 4.0
2019-05-22T02:40:39.237
55154
2019-05-24T02:02:51.963
2019-05-23T17:06:52.813
3605
29034
question
c# c++
memcpyをC#で利用するには


元のソースコードが間違っています。

[code]

     short *data = new float[cycles * period];
    
[/code]

この部分、`float`型配列でメモリ確保を行っているにもかかわらず、`short*`として扱っています。このため、コンパイラーは`float`実数ではなく`short`整数と見なしてコード生成してしまいます。  
その結果、

[code]

    memcpy(data + a * period * 2, data, period * 2);
    
[/code]

も、`short`整数としてアドレス計算してしまうことになります。また[`memcpy`](https://ja.cppreference.com/w/c/string/byte/memcpy)の第３引数は（扱うポインターのデータ型に依らず）コピーを行うバイト数を指定することになります。`period
* 2`とのことですが、これは`sizeof(short) == 2`を想定したコードでしょうか？
この点を気を付ける必要があります。[`std::copy_n`](https://ja.cppreference.com/w/cpp/algorithm/copy_n)であれば要素数を指定できますので、こちらを使用することをお勧めします。

元コードを修正されたということですが、問題自体は何も変わっていません。

[code]

    memcpy(data + a * period * 2, data, period * 2);
    // ↓
    memcpy(&data[a * period * 2], &data[0], period * 2);
    
[/code]

というコードであり、`data`配列の`0`番目以降の値を`data`配列の`a * period * 2`番目以降の位置へ`period *
2`バイトコピーする（ただしこのサイズが`data`配列の何要素分に相当するかは不明）、という意味です。

* * *

以上、元のソースコードが間違っており、コピーを行うサイズが不明確な点を指摘した上で、C#でのコピー方法ですが…  
C# / .NET
Frameworkではガベージコレクタがメモリ管理を行っており、プログラム実行中にメモリ移動が発生し得る仕様となっています。このため、基本的にはポインターを扱っていません。配列と配列オフセットで要素を指定することになります。

  * `std::copy_n`相当の要素数指定 [`Array.Copy`](https://docs.microsoft.com/ja-jp/dotnet/api/system.array.copy?view=netframework-4.8)
  * `memcpy`相当のバイト数指定 [`Buffer.BlockCopy`](https://docs.microsoft.com/ja-jp/dotnet/api/system.buffer.blockcopy?view=netframework-4.8)

が用意されています。


CC BY-SA 4.0
2019-05-22T03:26:30.907
55156
2019-05-22T06:53:28.473
2019-05-22T06:53:28.473
4236
4236
55154
answer


直接の回答ではないのですが、「三角波の再生」とご質問に明示されたので、正しいC++側のコードを示しておきたいと思います。

サンプルに16-bit符号付整数を使う場合。

[code]

    int period = 64; // <- 1周期分の要素数
    int seconds = 100; // <- データの秒数
    int frequency = 440; // <- 再生音の周波数(Hz)
    int cycles = seconds * frequency;
    short *data = new short[cycles * period]; // <- `short *`なら`new short[...]`
    //1周期分(`period`個の要素)に三角波データを埋める
    int period4 = period / 4;
    for( int a = 0; a < period4; ++a ) {
        //-32767...32767 の三角波データの作成
        data[a] = 32767 * a / period4;
        data[period4 + a] = 32767 - data[a];
        data[2 * period4 + a] = -data[a];
        data[3 * period4 + a] = -32767 + data[a];
    }
    //残り(`cycles-1`周期分)は最初の1周期のデータをコピーする
    for( int c = 1; c < cycles; ++c ) { // <- 1つ目のforとは異なった種類の値なんで無理に変数を使い回すべきでない
        memcpy(data + c * period, data, period * sizeof(short)); // <- `data +`の後は要素単位、第3引数はバイト単位
    }
    
    //...`delete[]`する前に`data`を使うコードがあるはず?
    
    delete[] data;
    
[/code]

サンプルに32-bit浮動小数点数を使う場合。

[code]

    int period = 64; // <- 1周期分の要素数
    int seconds = 100; // <- データの秒数
    int frequency = 440; // <- 再生音の周波数(Hz)
    int cycles = seconds * frequency;
    float *data = new float[cycles * period]; // <- `float *`なら`new float[...]`
    //1周期分(`period`個の要素)に三角波データを埋める
    int period4 = period / 4;
    for( int a = 0; a < period4; ++a ) {
        //-1.0f...1.0f の三角波データの作成
        data[a] = (float)a / (float)period4;
        data[period4 + a] = 1.0f - data[a];
        data[2 * period4 + a] = -data[a];
        data[3 * period4 + a] = -1.0f + data[a];
    }
    //残り(`cycles-1`周期分)は最初の1周期のデータをコピーする
    for( int c = 1; c < cycles; ++c ) { // <- 1つ目のforとは異なった種類の値なんで無理に変数を使い回すべきでない
        memcpy(data + c * period, data, period * sizeof(float)); // <- `data +`の後は要素単位、第3引数はバイト単位
    }
    
    //...`data`を使うコード
    
    delete[] data;
    
[/code]

* * *

「BASSというオーディオライブラリ」のC#バインディングがどうなっているのよくわからないので、肝心の「memcpyのやり方」をC#に置き換えることはやっていませんが、コメントの内容をよく読んで、何をするための`memcpy`なのかが理解できれば、sayuriさんのご回答と合わせて正しい処理が書けるのではないかと思います。

ポイントとしては「`short`使いたいのか`float`使いたいのかはっきりしてよ」と言うところでしょうか。

* * *

追記

上の`short`の場合でいうと、最初の`for( int a...
)`のループが完了した時点で、`data`の指す配列の中身はこんな感じになっているわけです。

[code]

    +32767     o
              o o
             o   o
            o     o
     0     o-------o-------+-------+-------+-------+-------+...............+-------+-------|
                    o     o^               ^               ^               ^               ^
                     o   o |               |               |               |               |
                      o o  |               |               |               |               |
    -32767             o   |               |               |               |               |
                           period*1        period*2        period*3      period*(cycles-1) period*cycles
    
[/code]

後は同じことなんで、それをコピーして、

[code]

    +32767     o               o               o                               o
              o o             o o             o o                             o o
             o   o           o   o           o   o           .               o   o
            o     o         o     o         o     o         .               o     o
     0     o-------o-------o-------o-------o-------o-------o...............o-------o-------|
                    o     o^        o     o^        o     o^              .^        o     o^
                     o   o |         o   o |         o   o |             . |         o   o |
                      o o  |          o o  |          o o  |               |          o o  |
    -32767             o   |           o   |           o   |               |           o   |
                           period*1        period*2        period*3      period*(cycles-1) period*cycles
    
[/code]

ってな感じにしたいわけです。

どこからどこまでの範囲をどうコピーすればいいのか、`memcpy`の動作やC++のポインタ演算が完全には理解できていなくても、配列にどんなデータを埋めていけば良いのかがわかれば、おおよそはわかると思うんですが…。

思考停止して機械的な書き換えをしようと思わず、何をするコードなのか理解するようにして下さい。


CC BY-SA 4.0
2019-05-22T11:47:56.700
55171
2019-05-22T14:58:11.653
2019-05-22T14:58:11.653
13972
13972
55154
answer


[c](/questions/tagged/c "'c' のタグが付いた質問を表示")/[c++](/questions/tagged/c%2b%2b
"'c++' のタグが付いた質問を表示") の `mem***()`
は「メモリ上の物理的配置」を強く意識しないと使えない代物です。他言語はこの辺を故意に隠ぺいしている関係で [c](/questions/tagged/c
"'c' のタグが付いた質問を表示") 独特のものといってもいいでしょう。元コードを [c#](/questions/tagged/c%23 "'c#'
のタグが付いた質問を表示") に移植する際においては `memcpy()` 部分は元コードを参考にせず OOPer
さんの示した「元コードが何をやっているか」を理解した上で書き直すのが手早いっす。元コードの `memcpy` 部分の詳しい解説は
[c#](/questions/tagged/c%23 "'c#' のタグが付いた質問を表示") で何をしなければならないかの理解の邪魔になるので省略。

「何をするコード」なのか  
\- 三角波の１周期分を `[data[0]...data[period])` の半開区間に格納しています。  
\- それを複写すれば「複数周期」になるわけですよね。

ってことは、やらなきゃならないことは `data` の部分データの複写で、

複写元：配列 `data` の位置 `0` 長さ `period` の区間  
複写先：配列 `data` の位置 `period*1`  
複写先：配列 `data` の位置 `period*2` ... 以下同文

ここまでわかったらすでに提示されている「配列の複写」 [`Array.Copy`](https://docs.microsoft.com/ja-
jp/dotnet/api/system.array.copy) を使うのが手早そうです。 MSDN 解説によると４種類の `Copy`
が可能だとわかりました。今回やりたいのは配列内要素を同一配列内の違う位置にコピーなので、４種類のうちどれが適切なのかを解説読んで探します。

今回単に `int` を使えば十分なので引数に `Int64` を使うものは不要。  
複写先を `0` 位置固定にするのは不適。  
ということで `Array.Copy(Array sourceArray, int sourceIndex, Array destinationArray,
int destinationIndex, int length);` が最適だと判明します。

あと [c#](/questions/tagged/c%23 "'c#' のタグが付いた質問を表示")
は型が縮小する方向への変換はキャストの明示を要求する仕様であること、何がしたいのかもっと直観的に読めるように直しておきたいので

[code]

    for (int i = 0; i < period4; ++i)
    {
        int val = 32767 * i / period4;
        data[0 * period4 + i] = (short)(val);
        data[1 * period4 + i] = (short)(32767 - val);
        data[2 * period4 + i] = (short)(-val);
        data[3 * period4 + i] = (short)(-32767 + val);
    }
    for (int i = 1; i < cycles; ++i)
    {
        Array.Copy(data, 0, data, i * period, period);
    }
    
[/code]

ってことになりそうです。 `0 * period4`
とか無駄っぽいですが、最近のコンパイラは十分賢いので最適化してくれますし、してくれなくても初期化１回だけなんだったらキニシナイ。

# 宿題なんだったらそろそろ期限切れだろうし  
# SO 的には「もっと考えてね」よりは直接の回答になってるほうがよいと判断


CC BY-SA 4.0
2019-05-24T00:43:17.013
55208
2019-05-24T02:02:51.963
2019-05-24T02:02:51.963
8589
8589
55154
answer