THETAで日本代表を応援しよう！【THETAプラグイン開発】 #thetaplugin

リコーの @kushimoto です。

いきなりですが、[RICOH THETA V](https://theta360.com/ja/about/theta/v.html)は、AndroidベースのOSなので、アプリをインストールすることで機能拡張でき、THETA界隈ではアプリのことをプラグインと呼んでいます。

THETAプラグインをご存じない方は[こちら](https://github.com/ricohapi/theta-api-specs/blob/main/core/products/theta-plugin.md)をご覧ください。
興味を持たれた方は[Twitter](https://twitter.com/thetaplugin)のフォローと[THETAプラグイン開発コミュニティ（Slack）](https://join.slack.com/t/thetadeveloper/shared_invite/enQtNDU5Nzg4MTg4NzA2LTYyOGQ1YjRkMTllY2RhNGE2ZTNhNjIxZDY4MzgyZjYyOWYxYmM4OWE5MjZlM2QwZGQ4ZTIyZDc1MjQ0ODcwZjM)への参加もよろしくお願いします｡

この記事では、THETAで音を鳴らす方法を紹介して、最終的にはみんなで日本代表を応援しようと思います。

## THETAを鳴らす

### API

THETA本体で使われている音を再生するためのAPIが提供されています。

「きゅいんっ」っていうシャッター音とか「ぴぴぴぴぴっ」って強制終了しちゃう音とかを、自由に好きなタイミングで鳴らせるので、THETAファンにはたまりませんね。

APIとしては、以下のインテントが定義されていて、インテントをブロードキャストすることで再生できます。

[Sound Effect](https://github.com/ricohapi/theta-api-specs/blob/main/ricoh-theta-plugin/broadcast-intent.md#sound-effect)

| インテント | 説明 |
|---|---|
| `“com.theta360.plugin.ACTION_AUDIO_SHUTTER”` | シャッター音 |
| `“com.theta360.plugin.ACTION_AUDIO_SH_OPEN”` | 長時間露光撮影開始音 |
| `“com.theta360.plugin.ACTION_AUDIO_SH_CLOSE”` | 長時間露光撮影終了音 |
| `“com.theta360.plugin.ACTION_AUDIO_MOVSTART”` | 動画撮影開始音 |
| `“com.theta360.plugin.ACTION_AUDIO_MOVSTOP”` | 動画撮影終了音 |
| `“com.theta360.plugin.ACTION_AUDIO_SELF”` | セルフタイマー音 |
| `“com.theta360.plugin.ACTION_AUDIO_WARNING”` | 警告音 |

### SDK

プラグインの開発を手助けするためにSDKが用意されています。

[RICOH THETA Plug-in SDK](https://github.com/ricohapi/theta-plugin-sdk)

音以外にもボタンイベントの取得やLEDを制御する機能が含まれています。
SDKの使い方は以下の記事を参考にしてください。

* [【THETAプラグイン開発】THETAでRICOH THETA Plug-in SDKを動かす方法](https://qiita.com/yokazuya/items/cda20420ee0b1b58e03f)
* [【THETAプラグイン開発】THETAプラグインでHello World](https://qiita.com/meronpan/items/fde7d66bed7f31f9e193)（メロンパンの記事）

上記の音系のAPIに対応するメソッドは、SDKの `PluginActivity` クラスで定義されています。

* `notificationAudioShutter()`
* `notificationAudioOpen()`
* `notificationAudioClose()`
* `notificationAudioMovStart()`
* `notificationAudioMovStop()`
* `notificationAudioSelf()`
* `notificationAudioWarning()`

メソッドを呼ぶと音が鳴ります。

## THETAで日本代表を応援する

本題です。

サッカーワールドカップ日本代表、惜しかったですね。

この記事は惜しくも代表戦には間に合いませんでしたが、何かしらの日本代表は常にどこかしらにいるはずなので、THETAを使って日本代表を応援する方法を紹介したいと思います。

打倒[ブブゼラ](https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%96%E3%82%BC%E3%83%A9)！

### 日本古来の応援といえば…

日本人なら応援といえば **三三七拍子** ですよね。知らない人はいないし、ライセンス的にも問題ないし、これしか考えられません。

と、いうことで、今回は **三三七拍子ができるTHETA** を開発します！

打倒[ブブゼラ](https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%96%E3%82%BC%E3%83%A9)！

### コーディング

SDKに含まれているサンプルコードの `MainActivity.java` を修正してみました。
サンプルコードの詳細な説明は[メロンパンの記事](https://qiita.com/meronpan/items/fde7d66bed7f31f9e193)をご覧くださいメロメロン:melon:

```java
package com.theta360.pluginapplication;

import android.os.Bundle;
import android.view.KeyEvent;
import com.theta360.pluginapplication.task.TakePictureTask;
import com.theta360.pluginapplication.task.TakePictureTask.Callback;
import com.theta360.pluginlibrary.activity.PluginActivity;
import com.theta360.pluginlibrary.callback.KeyCallback;
import com.theta360.pluginlibrary.receiver.KeyReceiver;
import com.theta360.pluginlibrary.values.LedColor;
import com.theta360.pluginlibrary.values.LedTarget;

public class MainActivity extends PluginActivity {
    private static final int INTERVAL = 350;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Set a callback when a button operation event is acquired.
        setKeyCallback(new KeyCallback() {
            // ぴっ
            private void pi() throws InterruptedException {
                notificationLedShow(LedTarget.LED3);
                notificationAudioMovStart();
                Thread.sleep(INTERVAL);
                notificationLedHide(LedTarget.LED3);
            }

            // ぴー
            private void pii() throws InterruptedException {
                notificationLedShow(LedTarget.LED4);
                notificationAudioShutter();
                Thread.sleep(INTERVAL);
                notificationLedHide(LedTarget.LED4);
            }

            @Override
            public void onKeyDown(int keyCode, KeyEvent event) {
                if (keyCode == KeyReceiver.KEYCODE_CAMERA) {

                    try {
                        // 三
                        pi();
                        pi();
                        pi();

                        // 休符
                        Thread.sleep(INTERVAL);

                        // 三
                        pi();
                        pi();
                        pi();

                        // 休符
                        Thread.sleep(INTERVAL);

                        // 七
                        pi();
                        pi();
                        pi();
                        pi();
                        pi();
                        pi();
                        pii();

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void onKeyUp(int keyCode, KeyEvent event) {
            }

            @Override
            public void onKeyLongPress(int keyCode, KeyEvent event) {
            }
        });
    }
}
```

簡単に説明しますと、

```java
            @Override
            public void onKeyDown(int keyCode, KeyEvent event) {
                if (keyCode == KeyReceiver.KEYCODE_CAMERA) {
                    try {
                        // 三
                        pi();
                        pi();
                        pi();

                        // 休符
                        Thread.sleep(INTERVAL);

                        // 三
                        pi();
                        pi();
                        pi();

                        // 休符
                        Thread.sleep(INTERVAL);

                        // 七
                        pi();
                        pi();
                        pi();
                        pi();
                        pi();
                        pi();
                        pii();

                        // 休符
                        Thread.sleep(INTERVAL);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
```

ボタンが押されると `onKeyDown()` が呼ばれて、今回はシャッターボタン（`KEYCODE_CAMERA`）が押された場合に三三七拍子します。
大胆にもメインスレッドをヤッて（`Thread.sleep()`させて）いますが、サンプルコードとしての読みやすさを優先しています。

```java
            // ぴっ
            private void pi() throws InterruptedException {
                notificationLedShow(LedTarget.LED3);
                notificationAudioMovStart();
                Thread.sleep(INTERVAL);
                notificationLedHide(LedTarget.LED3);
            }

            // ぴー
            private void pii() throws InterruptedException {
                notificationLedShow(LedTarget.LED4);
                notificationAudioShutter();
                Thread.sleep(INTERVAL);
                notificationLedHide(LedTarget.LED4);
            }
```

実際に音を鳴らしているのはこれらのメソッドです。
音が鳴るだけだとそっけないので、LEDも光らせています。LEDの光らせ方もまた[メロンパンの記事](https://qiita.com/meronpan/items/fde7d66bed7f31f9e193)で説明されていますメロメロン:melon:

### THETAで三三七拍子する

動かしてみるとこんな感じです（↓はYouTubeへのリンクです）。

[![YouTube動画](http://img.youtube.com/vi/r1xFDtPzH0o/0.jpg)](https://www.youtube.com/watch?v=r1xFDtPzH0o)

なんともいえませんね。

## まとめ

この記事では、THETAで音を再生する方法を紹介し、それを使って三三七拍子ができるTHETAを作ってみました。

残念ながら[ブブゼラ](https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%96%E3%82%BC%E3%83%A9)には負けてしまいましたが、もしこの記事が代表戦に間に合っていたら…みんなでTHETAで応援できていたら…もしかしたら…。