2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JUCEAdvent Calendar 2017

Day 11

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

Last updated at Posted at 2017-12-10

本記事は JUCE Advent Calendar 2017 の12月11日向けに投稿した記事です。

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

作ったもの

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

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

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

スクリーンショット 2017-11-19 16.23.27.jpg

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

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

スクリーンショット 2017-11-19 16.24.55.png

3. GUIを作成する

ProjucerのGUIエディタを利用してGUIを定義するクラスを作成しました。
GUIエディタの使い方については、こちらの記事をご参考ください。
今回は以下のコンポーネントを配置しました。

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

アプリケーションで表示した場合

スクリーンショット 2017-11-19 23.00.47.png

4. Blocksと接続するクラスを継承する

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

"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"関数からリスナーを登録します。

"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プログラムを送信します。

"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から導入された生文字列リテラルです。参考情報

"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+ で接続しました。

"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 公式ドキュメント
BLOCKS SDK 公式ドキュメント

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?