カオスでチャンスオペレーション：ロジスティック写像 編

#はじめに
[カオス(力学系)](https://ja.wikipedia.org/wiki/カオス理論)から[チャンスオペレーション(偶然性の音楽)](https://ja.wikipedia.org/wiki/偶然性の音楽)ができないか？という試みを[Siv3D](https://siv3d.github.io/ja-jp/)で行っています。カオスを用いることで、単純なランダムと違って曲を数式で一意に定義可能で、初期値鋭敏性により偶然性の音楽を生成することができ、有界性による限られた範囲での動きを音階に当てはめやすいというメリットが考えられます。

#ロジスティック写像での例
ここでは[ロジスティック写像](https://ja.wikipedia.org/wiki/ロジスティック写像)のサンプルをご紹介します。
https://twitter.com/hat_a2c/status/1258944697159933952

#使い方
ロジスティック写像は[0,1]の範囲を離散的に移動します。その際、パラメータの値に応じて不動点に収束したり、周期的に振動したり、カオスになったり、ランダムになったりします。

その中でも、このパラメータを調整して3周期軌道にすることで３領域を行き来しながらも不規則にバーストする振る舞いを表現することができます。そこで、その３領域に３和音コードの音を割り当てています。

さらに今回は、４和音コードの関係となる２つの３和音コードを用意することで、４和音コードのアルペジオになるようにしてみました。（不協和音を避けるため基本的には同時発音しない条件としています）
発音についてもロジスティック写像の0.5を境にしてON,OFFを割り当てています。

**参考**：
https://sakkyoku.info/theory/chord-decoration-01/
https://ja.wikipedia.org/wiki/%E5%9B%9B%E5%92%8C%E9%9F%B3
https://note.com/kazmaki_416/n/nf6c4394d954f

#ソースコード
[OpenSiv3D(0.4.3)](https://siv3d.github.io/ja-jp/#opensiv3d-sdk-v043)を前提としています。素人コードなので冗長な部分があるかと思いますが、ご活用頂ければと思います。


```cpp:Main.cpp
# include <Siv3D.hpp>

void func(double n[10], double m)
{
    n[9] = n[8];
    n[8] = n[7];
    n[7] = n[6];
    n[6] = n[5];
    n[5] = n[4];
    n[4] = n[3];
    n[3] = n[2];
    n[2] = n[1];
    n[1] = n[0];
    n[0] = m;
}

LineString funcR(double a, Vec2 p, int d)
{
    Vec2 l1 = Vec2(0.000 * d + p.x, p.y - a * 0.000 * (1 - 0.000) * d);
    Vec2 l2 = Vec2(0.125 * d + p.x, p.y - a * 0.125 * (1 - 0.125) * d);
    Vec2 l3 = Vec2(0.250 * d + p.x, p.y - a * 0.250 * (1 - 0.250) * d);
    Vec2 l4 = Vec2(0.365 * d + p.x, p.y - a * 0.365 * (1 - 0.365) * d);
    Vec2 l5 = Vec2(0.500 * d + p.x, p.y - a * 0.500 * (1 - 0.500) * d);
    Vec2 l6 = Vec2(0.625 * d + p.x, p.y - a * 0.625 * (1 - 0.625) * d);
    Vec2 l7 = Vec2(0.750 * d + p.x, p.y - a * 0.750 * (1 - 0.750) * d);
    Vec2 l8 = Vec2(0.875 * d + p.x, p.y - a * 0.875 * (1 - 0.875) * d);
    Vec2 l9 = Vec2(1.000 * d + p.x, p.y - a * 1.000 * (1 - 1.000) * d);
    return LineString
    {
        l1,l2,l3,l4,l5,l6,l7,l8,l9
    };
}

LineString funcLine(double n[10], Vec2 p , int d)
{
    Vec2 l1  = Vec2(n[9] * d + p.x, p.y - n[8] * d);
    Vec2 l2  = Vec2(n[8] * d + p.x, p.y - n[8] * d);
    Vec2 l3  = Vec2(n[8] * d + p.x, p.y - n[7] * d);
    Vec2 l4  = Vec2(n[7] * d + p.x, p.y - n[7] * d);
    Vec2 l5  = Vec2(n[7] * d + p.x, p.y - n[6] * d);
    Vec2 l6  = Vec2(n[6] * d + p.x, p.y - n[6] * d);
    Vec2 l7  = Vec2(n[6] * d + p.x, p.y - n[5] * d);
    Vec2 l8  = Vec2(n[5] * d + p.x, p.y - n[5] * d);
    Vec2 l9  = Vec2(n[5] * d + p.x, p.y - n[4] * d);
    Vec2 l10 = Vec2(n[4] * d + p.x, p.y - n[4] * d);
    Vec2 l11 = Vec2(n[4] * d + p.x, p.y - n[3] * d);
    Vec2 l12 = Vec2(n[3] * d + p.x, p.y - n[3] * d);
    Vec2 l13 = Vec2(n[3] * d + p.x, p.y - n[2] * d);
    Vec2 l14 = Vec2(n[2] * d + p.x, p.y - n[2] * d);
    Vec2 l15 = Vec2(n[2] * d + p.x, p.y - n[1] * d);
    Vec2 l16 = Vec2(n[1] * d + p.x, p.y - n[1] * d);
    Vec2 l17 = Vec2(n[1] * d + p.x, p.y - n[0] * d);
    Vec2 l18 = Vec2(n[0] * d + p.x, p.y - n[0] * d);
    return LineString
    {
        l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11,l12,l13,l14,l15,l16,l17,l18
    };
}



void Main()
{
    //発音データ
    const Audio pC(GMInstrument::Piano1, PianoKey::C3, 0.5s);
    const Audio pE(GMInstrument::Piano1, PianoKey::E3, 0.5s);
    const Audio pF(GMInstrument::Piano1, PianoKey::F3, 0.5s);
    const Audio pG(GMInstrument::Piano1, PianoKey::G3, 0.5s);

    //ロジスティック写像パラメータ
    double r = 3.858;

    //初期値
    double xo = Random();
    double yo = Random();
    double mo = Random();
    double no = Random();
    double x = xo;
    double y = yo;
    double m = mo;
    double n = no;

   //表示用
    const Font font(20);
    const Font font2(15);
    int d = 200;
    Vec2 px = Vec2(100, 240);
    Vec2 py = Vec2(100, 520);
    Vec2 pm = Vec2(450, 240);
    Vec2 pn = Vec2(450, 520);
    double lx[10] = { xo,xo,xo,xo,xo,xo,xo,xo,xo,xo };
    double ly[10] = { yo,yo,yo,yo,yo,yo,yo,yo,yo,yo };
    double lm[10] = { mo,mo,mo,mo,mo,mo,mo,mo,mo,mo };
    double ln[10] = { no,no,no,no,no,no,no,no,no,no };

    //処理用
    bool play = false; //再生用
    uint64 timer = 0; //更新時間管理用
    bool on = false; //発音判定用
    int count = 0; //発音回数
    int span = 120; //発音間隔[msec]

    while (System::Update())
    {
        if (KeySpace.down()) //スペースキーで起動
        {
            play = true;
            timer = Time::GetMicrosecSinceEpoch();
            count = 1;
            on = true;
        }

        if (play)
        {
            uint64 value = (Time::GetMicrosecSinceEpoch() - timer); //再生時間[usec]
            uint64 next = span * 1000 * count; //発音時間間隔判定[usec]
            if (on)
            {
                on = false;
            }
            else if (value >= next)
            {
                x = r * x * (1.0 - x);
                y = r * y * (1.0 - y);
                m = r * m * (1.0 - m);
                n = r * n * (1.0 - n);
                if ((y >= 0.5 && x < 1.0 / 3.0) || (n <= 0.5 && m < 1.0 / 3.0))
                {
                    pG.playOneShot(0.5);
                }
                else if ((y >= 0.5 && x > 2.0 / 3.0) || (n <= 0.5 && m > 2.0 / 3.0))
                {
                    pC.playOneShot(0.5);
                }
                else if ((y >= 0.5 && x >= 1.0 / 3.0 && x <= 2.0 / 3.0) && !(n <= 0.5 && m >= 1.0 / 3.0 && m <= 2.0 / 3.0))
                {
                    pE.playOneShot(0.5);
                }
                else if (!(y >= 0.5 && x >= 1.0 / 3.0 && x <= 2.0 / 3.0) && (n <= 0.5 && m >= 1.0 / 3.0 && m <= 2.0 / 3.0))
                {
                    pF.playOneShot(0.5);
                }
                else if (y >= 0.5 && x >= 1.0 / 3.0 && x <= 2.0 / 3.0 && n <= 0.5 && m >= 1.0 / 3.0 && m <= 2.0 / 3.0)
                {
                }
                count += 1;
                on = true;
                func(lx, x);
                func(ly, y);
                func(lm, m);
                func(ln, n);
            }
        }

        if (1) //以下、表示用
        {
            font(U"X").drawBase(250, 20);
            font2(U"n+1").drawBase(265, 20);
            font(U" = rX").drawBase(290, 20);
            font2(U"n").drawBase(340, 20);
            font(U"(1-X").drawBase(350, 20);
            font2(U"n").drawBase(395, 20);
            font(U"); r= ", r).drawBase(405, 20);

            font(U"G3").draw(px.x + 28, px.y + 10);
            font(U"E3").draw(px.x + d * 0.5 - 6, px.y + 10);
            font(U"C3").draw(px.x - 37 + d, px.y + 10);
            font(U"初期値 = ", xo).draw(px.x - 72 + d * 0.5, px.y + 32);
            font(U"OFF").draw(py.x + d * 0.25 - 6, py.y + 10);
            font(U"ON").draw(py.x - d * 0.25 - 4 + d, py.y + 10);
            font(U"初期値 = ", yo).draw(py.x - 72 + d * 0.5, py.y + 32);
            font(U"G3").draw(pm.x + 28, pm.y + 10);
            font(U"F3").draw(pm.x + d * 0.5 - 6, pm.y + 10);
            font(U"C3").draw(pm.x - 37 + d, pm.y + 10);
            font(U"初期値 = ", mo).draw(pm.x - 72 + d * 0.5, pm.y + 32);
            font(U"ON").draw(pn.x + d * 0.25 - 6, pn.y + 10);
            font(U"OFF").draw(pn.x - d * 0.25 - 4 + d, pn.y + 10);
            font(U"初期値 = ", no).draw(pn.x - 72 + d * 0.5, pn.y + 32);

            Line(px.x, px.y, px.x + d, px.y - d).draw(1, Palette::White);
            Line(py.x, py.y, py.x + d, py.y - d).draw(1, Palette::White);
            LineString line1 = funcR(r, px, d);
            LineString line2 = funcR(r, py, d);
            line1.drawCatmullRom(1, Palette::White);
            line2.drawCatmullRom(1, Palette::White);
            LineString lineX = funcLine(lx, px, d);
            lineX.draw(1, Palette::Pink);
            LineString lineY = funcLine(ly, py, d);
            lineY.draw(1, Palette::Pink);
            Line(px.x + d * 1.0 / 3.0, px.y, px.x + d * 1.0 / 3.0, px.y - d).draw(LineStyle::SquareDot, 1, Palette::White);
            Line(px.x + d * 2.0 / 3.0, px.y, px.x + d * 2.0 / 3.0, px.y - d).draw(LineStyle::SquareDot, 1, Palette::White);
            Line(py.x + d * 1.0 / 2.0, py.y, py.x + d * 1.0 / 2.0, py.y - d).draw(LineStyle::SquareDot, 1, Palette::White);
            Triangle(px.x + d * xo, px.y, px.x + d * xo - 3, px.y + 7, px.x + d * xo + 3, px.y + 7).draw(Palette::Pink);
            Triangle(py.x + d * yo, py.y, py.x + d * yo - 3, py.y + 7, py.x + d * yo + 3, py.y + 7).draw(Palette::Pink);
            Line(pm.x, pm.y, pm.x + d, pm.y - d).draw(1, Palette::White);
            Line(pn.x, pn.y, pn.x + d, pn.y - d).draw(1, Palette::White);
            LineString line3 = funcR(r, pm, d);
            LineString line4 = funcR(r, pn, d);
            line3.drawCatmullRom(1, Palette::White);
            line4.drawCatmullRom(1, Palette::White);
            LineString lineM = funcLine(lm, pm, d);
            lineM.draw(1, Palette::Skyblue);
            LineString lineN = funcLine(ln, pn, d);
            lineN.draw(1, Palette::Skyblue);
            Line(pm.x + d * 1.0 / 3.0, pm.y, pm.x + d * 1.0 / 3.0, pm.y - d).draw(LineStyle::SquareDot, 1, Palette::White);
            Line(pm.x + d * 2.0 / 3.0, pm.y, pm.x + d * 2.0 / 3.0, pm.y - d).draw(LineStyle::SquareDot, 1, Palette::White);
            Line(pn.x + d * 1.0 / 2.0, pn.y, pn.x + d * 1.0 / 2.0, pn.y - d).draw(LineStyle::SquareDot, 1, Palette::White);
            Triangle(pm.x + d * mo, pm.y, pm.x + d * mo - 3, pm.y + 7, pm.x + d * mo + 3, pm.y + 7).draw(Palette::Skyblue);
            Triangle(pn.x + d * no, pn.y, pn.x + d * no - 3, pn.y + 7, pn.x + d * no + 3, pn.y + 7).draw(Palette::Skyblue);
        }
    }
}
```