openFrameworksを別スレッドでTwitter連携させる

[Life is Tech ! Advent Calendar 2016](http://qiita.com/advent-calendar/2016/life-is-tech) の24日目の記事です。
[クリエイティブコーディング Advent Calendar 2016](http://qiita.com/advent-calendar/2016/creative-coding)に書くか迷いました。
ということで、あまり初心者向けな丁寧な解説はそこまで載せてないので、またまだまだ修正できるとこがあると思うので書いていただける方編集リクエストお待ちしております。

この記事では、[openFrameworks](http://openframeworks.cc/)と [Twitter Streaming APIs](https://dev.twitter.com/streaming/overview) と [Node.js](https://nodejs.org/ja/) を使って、リアルタイムにTweetを拾ってきてそれの描画みたいなことをしようと思います。

そもそも、この記事で紹介するものはは京大生の @YoshimasaSakuragi  の多大な contribute があり、去年一緒にやった[武蔵野美術大学芸術祭プロジェクションマッピング企画](https://www.youtube.com/watch?v=20rGrjsn8Xo)とかに向けて作ったものです(この時のものを少し修正しました)




<img src="https://media.giphy.com/media/3o6ZtlvGVmhG3XZB8k/giphy.gif" alt="1" width="33%" /><img src="https://media.giphy.com/media/3o6Zt8jYKVPfqAVyFi/giphy.gif" alt="2" width="33%" /><img src="https://media.giphy.com/media/l0HlIC9wmvD0NBTB6/giphy.gif" alt="3" width="33%" />




例えば、[東工大メディア研究会](titamas.net)の[OFFTHERAILSというイベント](https://www.facebook.com/events/198669307207955/)で作ったものとか、大勢の観客がいて気軽にインタラクションみたいなのできるのはTwitterなのではという考えの元に、そういったイベントで使ったりしています。
<img src="https://media.giphy.com/media/l0MYSOAP5uOL4kiek/giphy.gif" alt="1" width="33%" /><img src="https://media.giphy.com/media/l0HlPdGVifRkhYjhC/giphy.gif" alt="2" width="33%" /><img src="https://media.giphy.com/media/l0MYP9Ga0J6DxYLgk/giphy.gif" alt="3" width="33%" />



FacebookのGraph APIと連携するバージョンもそのうち書きたいと思います。


#__<i class="fa fa-apple" aria-hidden="true"></i> 環境__

今回はOSX環境のみの紹介になります。基本的に私の環境が少し古いですが、ほとんど、最新バージョンと互換性があるみたいなので最新に全部持っていけば問題ないかと思います。

| 本記事の環境 | Image | 最新版のURL |
|:-----------|:------------|:------------|
|Xcode 7.3.1      | ![xcode](https://qiita-image-store.s3.amazonaws.com/0/56510/ad720bfc-c7a5-729b-981f-822c49df47da.png) | https://developer.apple.com/download/|
|  OSX 10.11.4  | ![osx](https://qiita-image-store.s3.amazonaws.com/0/56510/23068b0d-ed5c-dd2e-d613-3fb80e12cf8d.png) | https://support.apple.com/ja_JP/downloads/macos|
|  openFrameworks 0.9.2  | ![openFrameworks](https://qiita-image-store.s3.amazonaws.com/0/56510/0cebb483-d1ab-c573-c77f-d74ee447c671.png) | http://openframeworks.cc/ja/download/|
|ofxJSON    |![Screen Shot 2016-12-03 at 12.58.39.png](https://qiita-image-store.s3.amazonaws.com/0/56510/f359fdac-928e-a853-04be-a5fa8059b6a6.png)| https://github.com/jefftimesten/ofxJSON | 
|ofxTrueTypeFontUL2    |![Screen Shot 2016-12-03 at 12.58.39.png](https://qiita-image-store.s3.amazonaws.com/0/56510/fa510b39-b84c-bb35-6668-ce128e62d925.png)| https://github.com/kentaroid/ofxTrueTypeFontUL2 | 
|ofxOpenGLContextScope    |![Screen Shot 2016-12-03 at 12.58.39.png](https://qiita-image-store.s3.amazonaws.com/0/56510/1b26bb3a-0571-37dc-f6d9-7fba0dfb85a0.png)| https://github.com/satoruhiga/ofxOpenGLContextScope | 
|Node.js v4.1.1    |![Screen Shot 2016-12-21 at 10.49.55.png](https://qiita-image-store.s3.amazonaws.com/0/56510/fdb72e97-62b3-28a6-be87-fad3231ed33a.png)| https://nodejs.org/en/ | 
|twit 2.0.1   |![Screen Shot 2016-12-21 at 10.58.00.png](https://qiita-image-store.s3.amazonaws.com/0/56510/0d1cf788-5948-80da-5718-66fd2adcbc12.png)| https://www.npmjs.com/package/twit | 

#<i class="fa fa-twitter" aria-hidden="true"></i> 事前準備

node.jsとStreaming APIs (twitを使うけどkeyとかアクセストークンが必要なので登録)に関しては他の方が色々まとめてくれているので割愛しますが、この準備が以下の前提になっているので参考サイトをリンクしていますのでご準備ください。[twit](https://github.com/ttezel/twit)が便利だと思います。

- http://qiita.com/kokushi_yayoi/items/e4a5682c082acfa3218c
- http://take-she12.hatenablog.com/entry/2016/03/14/120000
- https://elstreet.style/elstreets/332

ここまでで

```streaming.js
var tweet = require('twit');
var fs = require('fs');
var file = 'twitter.json';
var T = new tweet({
  consumer_key: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
  consumer_secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
  access_token: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
  access_token_secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
});

var stream = T.stream('statuses/filter', {track: '#NowPlaying'});
stream.on('tweet', function(tw) {
  fs.writeFile(file, JSON.stringify(tw), null, null);
  fs.readFile(file, 'utf8', function (err, tw) {
    console.log(JSON.stringify(tw));
  });
});
```

みたいな、適当なハッシュタグとかでつぶやかれたもの(今回は`#NowPlaying`)を、
![sample.gif](https://qiita-image-store.s3.amazonaws.com/0/56510/64b94863-f0ed-ece8-eca8-c0228e2c63e9.gif)

のような感じで、

```bash
$ node streaming.js
```

と実行すれば`twitter.json`とかいう名前(任意の名前で大丈夫です)のファイルに書き込むようなもの(今回の場合は`streaming.js`というファイル)を用意できていれば事前準備は完了です。

#__<i class="fa fa-tasks" aria-hidden="true"></i> 作るものの整理(概観)__

以下、クラスの命名とかちゃんとできてないかもしれないので、[この記事](http://qiita.com/magicant/items/8134edf969f9629fa66e)とか参考に、編集リクエストお待ちしております。また、たまに箇条書きの形で貼っているURLは参考になるかも！と思ったリンクです。

##1. Projectの作成
Addonに`ofxJSON`,`ofxTrueTypeFontUL2`,`ofxOpenGLContextScope`を入れてプロジェクトを作成します。`ofxJSON`はJsonを監視したり読み取るため、`ofxTrueTypeFontUL2`は日本語などの描画のため、`ofxOpenGLContextScope`は別スレッドでの画像の読み込みのために必要です。

##2. `TweetData`みたいなクラスを作る
取得したデータをopenFrameworksのプロジェクトで使いやすいようデータに格納しますが、その際のクラスを用意しておきます。

##3. `WStringConverter`の準備
日本語などのマルチバイト文字列を扱うために、`wstring`というクラスを使います、その変換のための`WStringConverter`を準備します。

- http://minus9d.hatenablog.com/entry/20131029/1383057700

##4. `FontManager`の準備
日本語などのマルチバイト文字列を表示するために`ofxTrueTypeFontUL2`を使いますが、その管理をするクラスを作ります。

##5. `TweetJsonReceiver`の準備
`Node.js`で取得し続けているJSONファイルをパースして先ほど用意した`TweetData`みたいなクラスを準備してそれに格納していくみたいなことをします。

- http://www.slideshare.net/tado/itamabi-13-6artsat-api-1-web-api

##6. `JsonReceiveThread`の準備
メインスレッドでJsonから読み取った画像のURLをたたいて画像を取得していたりするとFPSが落ちるので、別スレッドで実行します。そのための`JsonReceiveThread`を用意します。
また、このスレッドで画像の取得をするために`ofxOpenGLContextScope`を使っています。

- http://hideyukisaito.hatenablog.com/entry/2013/10/26/131751

##7. `TwitterStreamingManager`みたいなManagerクラスを作る
上記の、Twitterから取得したJsonファイル読み取ったり、ThreadにしたりをまとめるManagerみたいなものを、用意します。

##8. 取得データを描画する何かを書く
せっかく取得しましたが、ここまででは何も描画しないので、適当に描画するコードを書きます。ここまでの労力の1/10くらいでできます。

#__<i class="fa fa-file-code-o" aria-hidden="true"></i> 実装__

##1. Projectの作成
こんな感じで。
![Screen Shot 2016-12-21 at 09.39.06.png](https://qiita-image-store.s3.amazonaws.com/0/56510/bd77474b-98ca-8d48-9410-26d6a261337c.png)

また、今回は`TwitterStreamingSample`という名前にしましたが、このディレクトリと、`Node.js`で書いた`streaming.js`が含まれるディレクトリ(TweetStreamという名前にしてみました)が同じディレクトリ内にあるようにしておいてください(コード中に出てくるパスの設定のため)。

![Screen Shot 2016-12-21 at 14.38.53.png](https://qiita-image-store.s3.amazonaws.com/0/56510/5cb0a24e-51b0-d797-2b50-78e8def38ee4.png)

##2. `TweetData`みたいなクラスを作る
コードを書きます。特に特別なことはせずに、Twitterから取得できるデータをデータごとに記述し、一度取得すればいいデータなので中身がいろいろ変更になることはなく、コンストラクタ内の引数でそのデータを渡す形をとりました。


```TweetData.hpp
#include "ofMain.h"
class TweetData{
public:
    wstring tweet_text; //twwet contents
    wstring user_name;  //user name
    string twitter_id;  //screen_name @account name
    string icon_url;    //icon image url

    int friends_count;  //number of following
    int statuses_count; //number of tweet
    int followers_count;//number of follower

    ofImage image;      //to store icon image

    TweetData();
    TweetData(const wstring &tweet_text,
                   const wstring &user_name,
                   const string &twitter_id,
                   const string &icon_url,
                   const int &friends_count,
                   const int &statuses_count,
                   const int &followers_count);
};
```

```TweetData.cpp
#include "TweetData.hpp"

//------------------------------------------------------------------------------------
TweetData::TweetData(){

}

//------------------------------------------------------------------------------------
TweetData::TweetData(const wstring &tweet_text,
                               const wstring &user_name,
                               const string &twitter_id,
                               const string &icon_url,
                               const int &friends_count,
                               const int &statuses_count,
                               const int &followers_count){
    cout << twitter_id << endl;  // output console
    this->tweet_text = tweet_text;
    this->user_name = user_name;
    this->twitter_id = twitter_id;
    this->icon_url = icon_url;
    this->friends_count = friends_count;
    this->statuses_count = statuses_count;
    this->followers_count = followers_count;
    this->image.load(this->icon_url);
}
```

ここでは、jsonで取得したtweetに紐づく内容などをまとめて一つのクラスにするためのもので、コンストラクタに引数でデータをあげています。

##3. `WStringConverter`の準備

ここはもう難しいので、気にせずコピペしてみてください。
C++と一概に言っても、windowsとOSX環境でも文字コードなどの違いからいろんなことを機にする必要があります。

```WStringConverter.hpp
//  This was quoted from @YoshimasaSakuragi
#include "ofMain.h"

class WStringConverter{
public:
    static wstring convToWString(string src);
    template <class T>
    static wstring convToUCS4(basic_string<T> src);
};
```

```WStringConverter.cpp
#include "WStringConverter.hpp"

//--------------------------------------------------------------------------------------------
wstring WStringConverter::convToWString(string src) {
#ifdef TARGET_WIN32
    wstring dst = L"";
    typedef codecvt<wchar_t, char, mbstate_t> codecvt_t;

    locale loc = locale("");
    if(!std::has_facet<codecvt_t>(loc))
        return dst;

    const codecvt_t & conv = use_facet<codecvt_t>(loc);

    const std::size_t size = src.length();
    std::vector<wchar_t> dst_vctr(size);

    if (dst_vctr.size() == 0)
        return dst;

    wchar_t * const buf = &dst_vctr[0];

    const char * dummy;
    wchar_t * next;
    mbstate_t state = {0};
    const char * const s = src.c_str();

    if (conv.in(state, s, s + size, dummy, buf, buf + size, next) == codecvt_t::ok)
        dst = std::wstring(buf, next - buf);

    return dst;
#elif defined __clang__
    wstring dst = L"";
    for (int i=0; i<src.size(); ++i)
        dst += src[i];
#if defined(__clang_major__) && (__clang_major__ >= 4)
    dst = convToUCS4<wchar_t>(dst);
#endif
    return dst;
#else
    return convToUCS4<char>(src);
#endif
}


//--------------------------------------------------------------------------------------------
template <class T>
wstring WStringConverter::convToUCS4(basic_string<T> src) {
    wstring dst = L"";
    int size = src.size();
    int index = 0;
    while (index < size) {
        wchar_t c = (unsigned char)src[index];
        if (c < 0x80) {
            dst += (c);
        }else if (c < 0xe0) {
            if (index + 1 < size) {
                dst += (((c & 0x1f) << 6) | (src[index+1] & 0x3f));
                index++;
            }
        }else if (c < 0xf0) {
            if (index + 2 < size) {
                dst += (((c & 0x0f) << 12) | ((src[index+1] & 0x3f) << 6) |
                        (src[index+2] & 0x3f));
                index += 2;
            }
        }else if (c < 0xf8) {
            if (index + 3 < size) {
                dst += (((c & 0x07) << 18) | ((src[index+1] & 0x3f) << 12) |
                        ((src[index+2] & 0x3f) << 6) | (src[index+3] & 0x3f));
                index += 3;
            }
        }else if (c < 0xfc) {
            if (index + 4 < size) {
                dst += (((c & 0x03) << 24) | ((src[index+1] & 0x3f) << 18) |
                        ((src[index+2] & 0x3f) << 12) | ((src[index+3] & 0x3f) << 6) |
                        (src[index+4] & 0x3f));
                index += 4;
            }
        }else if (c < 0xfe) {
            if (index + 5 < size) {
                dst += (((c & 0x01) << 30) | ((src[index+1] & 0x3f) << 24) |
                        ((src[index+2] & 0x3f) << 18) | ((src[index+3] & 0x3f) << 12) |
                        ((src[index+4] & 0x3f) << 6) | (src[index+5] & 0x3f));
                index += 5;
            }
        }
        index++;
    }
    return dst;
}
```

とりあえず、`WStringConverter::convToWString(string str)`で`str`がwstringに変換されると思ってもらえれば大丈夫です。

##4. `FontManager`の準備

日本語などのマルチバイト文字列を表示できるようにしたいので、まず適当なフォントファイルそれを`data/font/hoge.otf`みたいな感じで用意しておきます。
次にFontのFontManagerクラスを作ります。

- http://fontfree.me/

![sc.png](https://qiita-image-store.s3.amazonaws.com/0/56510/211c5bef-e586-d074-0186-2763a051c9ea.png)

配置が終われば、このフォントを使って文字を画面に出力するためのクラスを作ります。FontManagerは不変で毎度インスタンス化する必要はないので、staticなメソッドとしていろいろ定義しています。

- http://www.hyuki.com/dp/dpinfo.html#Monostate


```FontManager.hpp
#include "ofMain.h"
#include "ofxTrueTypeFontUL2.h"

class FontManager{
public:

    FontManager(){};

    static ofxTrueTypeFontUL2 mbigfont;
    static ofxTrueTypeFontUL2 msmallfont;

    static void init();

    static void loadSmallFont(const string &arg,const int &size);
    static void loadBigFont(const string &arg,const int &size);

    static void drawSmallString(const string &arg ,const float &x,const float &y);
    static void drawBigString(const string &arg ,const float &x,const float &y);

    static void drawSmallWString(const wstring &arg ,const float &x,const float &y);
    static void drawBigWString(const wstring &arg ,const float &x,const float &y);
};
```

```FontManager.cpp
#include "FontManager.hpp"

ofxTrueTypeFontUL2 FontManager::msmallfont;
ofxTrueTypeFontUL2 FontManager::mbigfont;

//--------------------------------------------------------------------------------
void FontManager::init(){
    setlocale(LC_CTYPE,"");
    if(msmallfont.isLoaded())return;
    FontManager::loadSmallFont("font/hoge.otf", 12);
    FontManager::loadBigFont("font/hoge.otf", 22);
}

//--------------------------------------------------------------------------------
void FontManager::loadSmallFont(const string &arg,const int &size){
    msmallfont.loadFont(arg, size);
}

//--------------------------------------------------------------------------------
void FontManager::drawSmallString(const string &arg ,const float &x,const float &y){
    msmallfont.drawString(arg, x, y);
}

//--------------------------------------------------------------------------------
void FontManager::drawSmallWString(const wstring &arg ,const float &x,const float &y){
    msmallfont.drawString(arg, x, y);
    msmallfont.useProportional(true);
    msmallfont.useVrt2Layout(true);
}

//--------------------------------------------------------------------------------
void FontManager::loadBigFont(const string &arg,const int &size){
    mbigfont.loadFont(arg, size);
}

//--------------------------------------------------------------------------------
void FontManager::drawBigString(const string &arg ,const float &x,const float &y){
    mbigfont.drawString(arg, x, y);
}

//--------------------------------------------------------------------------------
void FontManager::drawBigWString(const wstring &arg ,const float &x,const float &y){
    mbigfont.drawString(arg, x, y);
    mbigfont.useProportional(true);
    mbigfont.useVrt2Layout(true);
}
```

細かな設定の命令などはこちらのREADMEを参考にしてください。

- https://github.com/kentaroid/ofxTrueTypeFontUL2


##5. `TweetJsonReceiver`の準備

> C++では通常のクラスはコンストラクタの呼び出し(newの使用や非ポインタ、非参照変数の宣言など) 毎に実体が生成されます。
しかしログや重いオブジェクトなど、プログラム実行中に実体は一つのみ生成し、その実体を使いまわしたい場合があります。
この様な場合にシングルトンパターンを適用すると、 実体がプログラム中で一つであることが保証されます。

- http://www.geocities.jp/bleis_tift/cpp/singleton.html

こちらは先ほどの、`FontManager`とは違い、状態が不変ではないので、シングルトンパターンっぽく書いています。先ほどのものはモノステイトパターンっぽいです(namespaceで区切るほうが良いかもしれません)。

```TweetJsonReceiver.hpp
#include "ofxJSONElement.h"
#include "TweetData.hpp"
#include "ofMain.h"
#include "WStringConverter.hpp"

//保持するtweetのデータの最大値
#define DATA_MAX_NUM 50

class TweetJsonReceiver {
public:
    int64_t cachedTweetId;
    ofxJSONElement jsonElement;
    static int data_num;
    static vector<TweetData> tweetData;
    static TweetJsonReceiver &getInstance();

    TweetJsonReceiver(){}
    TweetJsonReceiver &operator=(const TweetJsonReceiver &other){}

    vector<TweetData> getTweetData();
    bool checkUpdateJson();
    void parseJson();
    void init();
    void update();
    bool receive();
};
```

更新を追いかけるため、tweetの固有番号であるidを`checkUpdateJson()`内で毎回確認するような仕組みです。

```TweetJsonReceiver.cpp
#include "TweetJsonReceiver.hpp"

vector<TweetData> TweetJsonReceiver::tweetData;
int TweetJsonReceiver::data_num;

//--------------------------------------------------------------------------------------------
TweetJsonReceiver &TweetJsonReceiver::getInstance() {
    static TweetJsonReceiver instance;
    return instance;
}

//--------------------------------------------------------------------------------------------
vector<TweetData> TweetJsonReceiver::getTweetData(){
    return tweetData;
}

//--------------------------------------------------------------------------------------------
bool TweetJsonReceiver::checkUpdateJson(){
    if(jsonElement["id"].asInt64() != cachedTweetId){
        cachedTweetId = jsonElement["id"].asInt64();
        return true;
    }else{
        return false;
    }
}

//--------------------------------------------------------------------------------------------
void TweetJsonReceiver::init(){
    data_num = 0;
    TweetJsonReceiver::tweetData.clear();
    bool parsingSuccessful = jsonElement.openLocal("../../../TweetStream/twitter.json");//Nodeで取得したJSON
    if (parsingSuccessful){
        cout << "Parsing Successful" << endl;
        cachedTweetId = jsonElement["id"].asInt64();
    }else{
        cout << "Failed to parse JSON" << endl;
    }
}

//--------------------------------------------------------------------------------------------
void TweetJsonReceiver::parseJson(){
    string tmptext = jsonElement["text"].asCString() ;
    string tmpusername = jsonElement["user"]["name"].asCString();
    wstring wtmptext = WStringConverter::convToWString(tmptext);
    wstring wtmpusername = WStringConverter::convToWString(tmpusername);

    tweetData.push_back((TweetData){
        WStringConverter::convToWString(tmptext),
        WStringConverter::convToWString(tmpusername),
        jsonElement["user"]["screen_name"].asCString(),
        jsonElement["user"]["profile_image_url"].asCString(),
        jsonElement["user"]["friends_count"].asInt(),
        jsonElement["user"]["statuses_count"].asInt(),
        jsonElement["user"]["followers_count"].asInt()
    });
    data_num++;
    return;
}


//--------------------------------------------------------------------------------------------
//Jsonの数に変化があればその分をtweetDataに追加
void TweetJsonReceiver::update(){

    //tweetDataが無限にたまらないようにFIFOさせる
    if(TweetJsonReceiver::tweetData.size()>DATA_MAX_NUM){
        TweetJsonReceiver::tweetData.erase(TweetJsonReceiver::tweetData.begin());
        cout << "CLear data" <<endl;
    }
    receive();
}


//--------------------------------------------------------------------------------------------
bool TweetJsonReceiver::receive(){
    bool parsingSuccessful = jsonElement.openLocal("../../../TweetStream/twitter.json");//Nodeで取得したJSON
    if (parsingSuccessful){
        if(checkUpdateJson()){
            parseJson();
            return true;
        }
    }else{
        cout << "Failed to parse JSON" << endl;
    }
    return false;
}
```

`init()`を一度読んであげたのち、`update()`で新しいtweetの情報が更新されていれば`TweetData`型のvectorであるtweetDataに要素が追加されていくようになっています。またコメントでも書きましたが、`TweetJsonReceiver::tweetData`に要素が追加されすぎてメモリが足りなくなることを防ぐため、`update()`内で要素数が増えすぎたら消すようにしています。

##6. `JsonReceiveThread`の準備
先ほど用意した`TweetJsonReceiver`をメインスレッドで呼ぶとFPSが落ちたりあまりいいことがないので、tweetDataへの要素の追加や画像の読み込みなどは、別スレッドで行いましょうということをするための準備をします。
このスレッドを用いない場合はメインスレッドで画像取得なども行うことになり処理速度が劇的に低下します。

```JsonReceiveThread.hpp
#include "ofMain.h"
#include "TweetJsonReceiver.hpp"
#include "ofxOpenGLContextScope.h"

class JsonReceiveThread : public ofThread{

public:
    void threadedFunction(){
        ofxOpenGLContextScope scope;
        while (isThreadRunning()) {
            if(lock()){
                TweetJsonReceiver::getInstance().update();
                unlock();
                sleep(100);
            }
        }
    }
};
```

ヘッダファイルに実装も書いたので、`JsonReceiveThread.cpp`はいじらなくて大丈夫です。(これwindowsの環境だと動かないかも)
このスレッドでは100msに一回`TweetJsonReceiver::getInstance().update();`を読んでいるので、メインスレッドでアップデートしなくても、このスレッドで、tweetDataへの要素の追加や画像の読み込みなどやってくれることになります。
また、`ofxOpenGLContextScope scope`というおまじないがないと、メインスレッド外での画像のallocate的なことができないっぽいのでおまじないだと思って書いておきましょう。

##7. `TwitterStreamingManager`みたいなManagerクラスを作る
今までのを簡単に使えるようにManagerクラスとしてまとめたいと思います、(componentとかの方が名前としてはいいのかな？)
これも実体は一つでいいので、そのことを保証するためにも先ほどのデザインパターンを適用してもいいかもしれません。

```TwitterStreamingManager.hpp
#include "ofMain.h"
#include "TweetJsonReceiver.hpp"
#include "TweetData.hpp"
#include "ofxOpenGLContextScope.h"
#include "JsonReceiveThread.hpp"

class TwitterStreamingManager{
private:
    JsonReceiveThread mJsonReceieveThread;
public:
    void setup();
    void exit();
};
```

```TwitterStreamingManager.cpp
#include "TwitterStreamingManager.hpp"

//------------------------------------------------------------------------------------
void TwitterStreamingManager::setup(){

    //Twitter初期化はメインスレッドで行う
    if(TweetJsonReceiver::tweetData.size()>0)return;
    TweetJsonReceiver::getInstance().init();

    //別スレッドで画像読み込みも含めてデータ取得する
    ofxOpenGLContextScope::setup();
    mJsonReceieveThread.startThread();
}

//------------------------------------------------------------------------------------
void TwitterStreamingManager::exit(){
    mJsonReceieveThread.stopThread();
}
```

`mJsonReceieveThread.startThread()`してしまえば、100msに一回`TweetJsonReceiver::getInstance().update()`されるはずなので大丈夫です。また、`end()`でしっかりスレッドを`stopThread()`してあげましょう。

##8. 取得データを描画する何かを書く
これはもうなんでも大丈夫です。
わかりやすく参考までに`ofApp.h`と`ofApp.cpp`に書いてみようと思います。今回は簡単にこんな感じのものを作ります。リアルタイムで #NowPlaying とつぶやいた人の情報を表示し続けるものです。

![gif](https://media.giphy.com/media/l4JySRAlTFrdcBRgk/giphy.gif)

今回の場合以下のように、`ofApp.h`と`ofApp.cpp`に描画関係の命令を書いていきます。(普通ならクラスとかに切り分けたほうがいいですが、今回は簡単にするため直書きです)

```ofApp.h
#include "ofMain.h"
#include "TwitterStreamingManager.hpp"
#include "FontManager.hpp"

class ofApp : public ofBaseApp{

	public:
		void setup();
		void update();
		void draw();

        void exit();

		void keyPressed(int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y );
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);
		void mouseEntered(int x, int y);
		void mouseExited(int x, int y);
		void windowResized(int w, int h);
		void dragEvent(ofDragInfo dragInfo);
		void gotMessage(ofMessage msg);

    TwitterStreamingManager twitterStreamingManager;
```

TwitterStreamingManagerのインスタンスを一つ用意しておきました。また`exit()`命令を追加しました。

```ofApp.cpp
#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    twitterStreamingManager.setup();
    FontManager::init();
}

//--------------------------------------------------------------
void ofApp::update(){
    ofSetWindowTitle("FPS: " + ofToString(ofGetFrameRate()) +"/  num of element: "+ofToString(TweetJsonReceiver::data_num));
}

//--------------------------------------------------------------
void ofApp::draw(){
    ofBackground(0);
    ofPushMatrix();
    ofTranslate(ofGetWidth()/2-300, ofGetHeight()/2);
    if(TweetJsonReceiver::tweetData.size()>0){
        TweetJsonReceiver::tweetData.back().image.draw(-100,-100,200,200);
        FontManager::drawSmallString("@"+ofToString(TweetJsonReceiver::tweetData.back().twitter_id),150,-60);
        FontManager::drawSmallWString(TweetJsonReceiver::tweetData.back().user_name,150,-40);
        FontManager::drawSmallString("followers_count: "+ofToString(TweetJsonReceiver::tweetData.back().followers_count),150,-20);
        FontManager::drawSmallString("friends_count: "+ofToString(TweetJsonReceiver::tweetData.back().friends_count),150,0);
        FontManager::drawSmallWString(TweetJsonReceiver::tweetData.back().tweet_text,150,20);
    }
    ofPopMatrix();
}

//--------------------------------------------------------------
void ofApp::exit() {
    twitterStreamingManager.exit();
}

//~~以降特に追記しないので省略~~

```

ここでは、特に難しいことはしていません、画面の中央付近にtweetした人のいろんな情報を表示しているだけです。

この描画部分を頑張ってたくさん書くと、最初の例ようなリッチな画面ができます。

#__<i class="fa fa-th-large" aria-hidden="true"></i> 最後に__
最後に今回のサンプルはgithubにあげてますのでそちらも参考にしてみてください。

- https://github.com/Hiroki11x/openFrameworksSample/tree/master/TwitterStreamingSample

今回Twitterに特化して扱ったように書きましたが、ofxJSONは便利で、例えばFaceBook Graph API使って取得した何かとか、とにかくJSONであれば、この記事の__<i class="fa fa-file-code-o" aria-hidden="true"></i> 実装__のところを読めば汎用的だと思うので、大体できると思うので試してみてください。

また、いろいろ修正してくださる方いらっしゃいましたら修正リクエストお願いします🙇