続、Lightpad Blockで電光掲示板を作った話

本記事は [JUCE Advent Calendar 2017](https://qiita.com/advent-calendar/2017/juce) の12月11日向けに投稿した記事です。

[前日の記事](https://qiita.com/COx2/private/2b48d6d4a3ce6493f590)にて、Lightpad Blockで電光掲示板を作った話について紹介しました。
本日はそれの続きとして、Lightpad BlockとJUCEライブラリと連携することで、Lightpad Blockで実行するプログラム(Littlefoot言語)を動的に変更した事例についても紹介します。

## 作ったもの

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">アドカレ用のネタの実験中。Ligthpad Blockに転送するLittlefootのソースコードをホストアプリ内でメタプログラミングで生成することで、電光掲示板の文字をアプリで変更してみる。<a href="https://twitter.com/hashtag/JUCE?src=hash&amp;ref_src=twsrc%5Etfw">#JUCE</a> <a href="https://twitter.com/hashtag/ROLI?src=hash&amp;ref_src=twsrc%5Etfw">#ROLI</a> <a href="https://twitter.com/hashtag/BLOCKS?src=hash&amp;ref_src=twsrc%5Etfw">#BLOCKS</a> <a href="https://t.co/7BmKdjEe1W">pic.twitter.com/7BmKdjEe1W</a></p>&mdash; COx2 ))))@ (@CO_CO_) <a href="https://twitter.com/CO_CO_/status/932224160503422977?ref_src=twsrc%5Etfw">2017年11月19日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## ROLI Blocksについて

英国ROLI社が開発・販売しているBlocksシリーズは、プログラマブルな楽器インターフェースです。
製品サイト: https://www.mi7.co.jp/products/roli/blocks/

+ 5Dタッチセンシティブ・コントロール・サーフェス
+ 15 x 15 LEDマトリクス
+ 94 mm x 94 mm x 20 mm / 250g
+ DNAコネクター8基搭載
+ 充電式バッテリー内蔵
+ USB-C端子（MIDI出力/電源供給）
+ USBおよびBluetoothによるMIDI互換（DAWでも使用可能）

[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/2gon6Vf79ak/0.jpg)](http://www.youtube.com/watch?v=2gon6Vf79ak)

## 1. 新規プロジェクト作成

本記事では、Projucerの"GUI Application"テンプレートから新規プロジェクトを作成します。

![スクリーンショット 2017-11-19 16.23.27.jpg](https://qiita-image-store.s3.amazonaws.com/0/93566/c4424e47-627f-8f91-0b56-29fae30694d2.jpeg)


## 2. juce::Blocksモジュールを追加する

Blocksと連携するAPIを利用するには、juce::Blocksモジュールを追加する必要があります。
Projucerのプロジェクト設定画面において、[Modules]の設定ボタン（歯車のアイコン）をクリックし、
[Add a module] → [Global JUCE modules path] → [juce_blocks_basics] をクリックしてモジュールを追加します。


<img width="1056" alt="スクリーンショット 2017-11-19 16.24.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/93566/653b50b0-a190-0253-7b76-bdb08232c90d.png">


## 3. GUIを作成する

ProjucerのGUIエディタを利用してGUIを定義するクラスを作成しました。
GUIエディタの使い方については、[こちらの記事](https://qiita.com/COx2/private/95d2c89962cf9ddef841)をご参考ください。
今回は以下のコンポーネントを配置しました。

+ 文字列を入力するテキストエディタ―コンポーネント
+ BlocksにLittlefootプログラムを送信するトリガーボタン
+ Bluetooth接続ダイアログを表示するボタン(iOS/Android用)
+ Blocksのステータスを表示するラベル

<img width="1050" alt="スクリーンショット 2017-11-19 18.30.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/93566/302fa1eb-e1da-9a6d-9877-2262fc3beadf.png">

#### アプリケーションで表示した場合
<img width="712" alt="スクリーンショット 2017-11-19 23.00.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/93566/3daa6433-96a3-5215-7d2c-b0145edc613a.png">


## 4. Blocksと接続するクラスを継承する
TopologySource::Listenerクラスを継承することでBlocksからのコールバックを受け取ることが出来ます。
今回はGUIコンポーネントのクラスにTopologySource::Listenerクラスを継承しました。
内部変数としてBlocksの接続状態を監視するPhysicalTopologySourceクラスのインスタンスを追加します。

```cpp
"SignBoardManager.h"

class SignBoardManager  : public Component,
                          public TopologySource::Listener,
                          public Button::Listener
{
public:
    //==============================================================================
    SignBoardManager ();
    ~SignBoardManager();

    //==============================================================================
    //[UserMethods]     -- You can add your own custom methods in this section.
    void topologyChanged() override;    // TopologySource::Listenerクラスに定義されたインターフェース
                                        // Blocksの接続状態が変更されたことをトリガーとしてコールバック関数として実行される

    void setSignBoardToBlocks(String text);
    void setSignBoardProgram(Block& block, String text);
    void detachActiveBlock();
    //[/UserMethods]

~~中略~~

private:
    //[UserVariables]   -- You can add your own custom variables in this section.
    PhysicalTopologySource topologySource;    // Blocksの接続状態を監視するインスタンス
    Block::Ptr activeBlock;                   // 現在接続状態にあるBlocksへのポインタ
    //[/UserVariables]
~~中略~~

}
```

また、実装コードでは以下のようにtopologySource変数からコールバックを受け取れるよう、"addListener"関数からリスナーを登録します。

```cpp
"SignBoardManager.cpp"

SignBoardManager::SignBoardManager ()
{
    //[Constructor_pre] You can add your own custom stuff here..
    topologySource.addListener (this);       // PhysicalTopologySourceクラスからコールバックを受けるようにリスナーを登録
    //[/Constructor_pre]
~~中略~~
}

```

## 5. BlocksにLittlefootプログラムを送信する処理

先ず、Blocksとの接続が確立したことをトリガーとしてデフォルトのプログラムを送る処理を実装します。
Blocksとの接続が確立したら、Block::setProgram(Program* newProgram)を実行することで、BlocksにLittlefootプログラムを送信します。


```cpp
"SignBoardManager.cpp"

// Blocksとの接続状態に変化が生じた場合に実行されるコールバック関数
void SignBoardManager::topologyChanged()
{
    // すでに接続があった場合にはactiveBlockにnullを代入して、接続解除したものとみなす
    if (activeBlock != nullptr)
        detachActiveBlock();

    auto blocks = topologySource.getCurrentTopology().blocks;
    for (auto b : blocks)
    {
        if (b->getType() == Block::Type::lightPadBlock)
        {
            activeBlock = b;

            // 接続が確立された際にデフォルトのプログラムを送信
            setSignBoardProgram (*activeBlock, "Hello Blocks");

            // Statusラベルに接続したBlocksの状態を表示する
            label->setText("Status: "
                           + activeBlock->getDeviceDescription()
                           + (String)activeBlock->getBatteryLevel(),
                           dontSendNotification);
            break;
        }
    }
}


void SignBoardManager::detachActiveBlock()
{
    activeBlock = nullptr;
}

void SignBoardManager::setSignBoardToBlocks(String text)
{
    if(activeBlock == nullptr)
        return;

    setSignBoardProgram(*activeBlock, text);
}

void SignBoardManager::setSignBoardProgram(Block& block, String text)
{
    // 接続されたBlocksに新しいプログラムを送信する
    block.setProgram(new SignBoardProgram(block, text));
}

```

## 6. Blocksに送信するプログラムを定義する

Block::Programクラスを継承したクラスは、Blocksにプログラムを送信する関数"Block::setProgram(Program* newProgram)"の引数として渡すことができるようになります。
今回はBlock::Programクラスを継承する"SignBoardProgram"クラスを定義し、"Program::getLittleFootProgram()"関数をオーバーライドしてLittlefootプログラムを返す処理を実装します。

ちなみに、R"littlefoot(~~~)littlefoot"で囲まれたブロックは、C++11から導入された生文字列リテラルです。[参考情報](https://cpprefjp.github.io/lang/cpp11/raw_string_literals.html)

```cpp
"SignBoardProgram.h"

class SignBoardProgram : public Block::Program
{
public:
    SignBoardProgram (Block& b, String t) : Program (b), sendingtext(t){}
    
    String getLittleFootProgram() override
    {
        sendingtext = sendingtext.toUpperCase();
    
        return  // Littlefootプログラム本体（juce::String型の文字列）
        R"littlefoot(
        
        #heapsize: 256
        
        //==============================================================================
        int newTouchColor;
        int touchColor0;

        ~~中略~~
        
        void repaint()
        {
            clearDisplay();
            
            drawPressureMap();
            fadePressureMap();
            
            updateBar();
            
            if(moveSwitch)
            {
                basePostionX = updateBasePosition(basePostionX);
            }

　          showCharactor(0x4a, touchColor0, basePostionX+0, basePostionY+0); //J
            showCharactor(0x55, touchColor1, basePostionX+5, basePostionY+0); //U
　          showCharactor(0x43, touchColor2, basePostionX+10,basePostionY+0); //C
　          showCharactor(0x45, touchColor3, basePostionX+15,basePostionY+0); //E

　          showCharactor(0x4a, touchColor0, basePostionX+25,basePostionY+0); //J
　          showCharactor(0x41, touchColor4, basePostionX+30,basePostionY+0); //A
　          showCharactor(0x50, touchColor1, basePostionX+35,basePostionY+0); //P
　          showCharactor(0x41, touchColor4, basePostionX+40,basePostionY+0); //A
            showCharactor(0x4e, touchColor2, basePostionX+45,basePostionY+0); //N
        }

        ~~中略~~
        
        )littlefoot"
            ) //String
        );
    }
    ~~中略~~
}

```


## 7. Littlefootプログラムを動的に変更する方法

"SignBoardProgram"クラスの関数"getLittleFootProgram()"は返りの型がString型であることからも解るように、Littlefoot言語のプログラムはString型の文字列そのものです。
ですので、文字列を変更してしまえば、Littlefootプログラムを動的に変更することが出来ます。
"juce::String"型は operator+ によって二つの文字列を接続することが出来ますので、動的に変更したい箇所の前後をString型で切り離し、変更後のString文字列を operator+ で接続しました。

```cpp
"SignBoardProgram.h"

String getLittleFootProgram() override
    {
        sendingtext = sendingtext.toUpperCase();
        
        return String(
        String(
        R"littlefoot(
        
        #heapsize: 256
        
        //==============================================================================
        int newTouchColor;
        ~~中略~~
            
            if(moveSwitch)
            {
                basePostionX = updateBasePosition(basePostionX);
            }
            
            )littlefoot"
            )
           
            + generateCharacterInstruction(sendingtext)  //動的に変更する箇所. juce::String型の文字列として挿入する

            + String(
            R"littlefoot(
        }
        
        void initialise()
        {

        ~~中略~~
        
        )littlefoot"
            ) //String
        );

    }

    ~~中略~~

private:
   String generateCharacterInstruction(String text){
       String result = "";
       for(int i=0; i<text.length(); i++){
           result +=
           String("showCharactor("
                  + (String)getASCII(sendingtext.getCharPointer()[i])
                  + ", touchColor" + (String)(i%5)
                  + ", basePostionX + " + (String)(i*5)
                  + ", basePostionY + 0);"
                  );
       }
       return result;
   }

   int getASCII(char ch){
        if     (ch == 'A')
            return 0x41;

         ~~中略~~

   }

```
 

## 参考情報
[juce::Blocks API 公式ドキュメント](https://juce.com/doc/group__juce__blocks__basics)
[BLOCKS SDK 公式ドキュメント](http://developer.roli.com/documentation/the_standalone_blocks_sdk.html)