オルトプラスエンジニアの日常をお伝えします!

通信量を減らすビット演算

※この記事は「AltPlus Advent Calendar 2016」の19日目の記事です。 こんにちは。オルトプラスの福原です。

昨今誰でも簡単にアプリが作れる環境が整い、手軽に開発も出来るようになってきました。中でもリアルタイムで通信を行い複数人数で遊ぶアプリも増えてきています。 ここで注目しておきたいのは通信量です。いくらネットが普及し光回線設備が整ったとしても無制限ではありません。一度に送受信できる量の限界、光の速度の限界は当然あります。 今現在、スマートフォンではキャリアが設けた通信量の制限にすぐ到達してしまわないように配慮する必要が出てきます。そして運営している側から見た場合、サーバーのデータ転送量を極力下げコストを抑えたいところでもあります。

どのような方法があるか

通信する量を減らすかまたは極力通信を行わなくても済むような設計が必要になると思います。今回は前者の量を減らすという部分について述べてみます。 お手軽な方法はデータ(jsonなど)を圧縮して送る方法ではありますが、今回はもっと根本的な部分から見てみることにします。 ほぼ全ての言語で利用可能なビット演算^1です。

1 バイトの中身

ビット番号 7 6 5 4 3 2 1 0
対応値 128 64 32 16 8 4 2 1
0 0 0 0 1 1 1 0

上記は整数 14 を表します。

例1

4人でデータの送受信を行ったケースを見てみます。 この例ではサーバーで4人のマップ上の X 座標、Y 座標を収集しを4人へ送ることとします。

map00.png

X 座標を float 型 4 バイト y 座標を float 型 4 バイト

1回の送信で

8 bytes x 4 = 32 bytes

4人分で 32 bytes になります。

これを JSON 表記した場合^2

{
  "player1" : { "x" : 350.48, "y" : 190.66 },
  "player2" : { "x" : 80.21, "y" : 200.34 },
  "player3" : { "x" : 128.93, "y" : 260.66 },
  "player4" : { "x" : 288.19, "y" : 90.12 }
}

この例では大体の位置が分れば良い前提として、 左上からマップを 16x16 のブロックで分割しておき、対応する座標から対象となるブロックを xy の座標としてしまいます。

map00.png

{
  "player1_xy" : 107
}

このように他のユーザーの座標も含めると以下のようになります。

{
  "player1_xy" : 107,
  "player2_xy" : 119,
  "player3_xy" : 187,
  "player4_xy" : 76,
}

このデータをさらに短くするためビット演算を使うと以下のように一つの整数にすることができます。

なぜ 1803008844 になったのかと言うと、4つの値をビット演算を用いて1つの 32bit 整数へ入れたため

01101011 (107) << 24 bit 左へシフト = 1795162112
01110111 (119) << 16 bit 左へシフト = 7798784
10111011 (187) << 8 bit 左へシフト = 47872
01001100 (76) はそのまま
ビット位置 24 16 8 0
元の値 107 119 187 76
1進数 01101011 01110111 10111011 01001100
ビット数 8bit 8bit 8bit 8bit

それぞれを左へシフトすることで 01101011 01110111 10111011 01001100 = 計 32 bit 整数 = 1803008844 となります。

結果を JSON 表記した場合[^3]

{
  "player_positions" : 1803008844
}

4人の座標データが一つの 32bit 整数にまとまりました。 この整数を逆に右シフトなどさせてそれぞれのプレイヤーの座標を取り出すことになります。

まとめ

最初の 32bit float 型 8 個のものより、 1/8 ほど通信量を減らせたと言えます。 特にリアルタイムで何度も送受信するケースでは重宝してくるでしょう。 極端な例でしたが適切な要所で扱えればかなりの量が節約できてしまうビット演算でした。

では今回はこの辺りでまた次の機会に。

2016年 有馬記念の予想

※この記事は「AltPlus Advent Calendar 2016」の23日目の記事です。

こんにちはこんにちは、shohojiです。

今週末は競馬ファンお馴染みのG1レース「有馬記念」が開催されますね。
有馬記念は、今年活躍した馬たちが一堂に会し、今年最後の活躍馬を決めるグランプリレースとも呼ばれる大きなレースです。

ダービーズキングの伝説やダービーゲート、ダービーロードなど、数々の競馬ゲームのを企画や開発をしてきたこの私が、そんなビッグレースがあるのに無視できるはずはありませんので・・・
今回は有馬記念の予想記事を書いてみようかと思います。

とはいえ、この記事は、QiitaのAltPlus Advent Calendar 2016の23日目の記事
ここでは技術的な話を書く必要があるとのことなので
今回は私が予想するのではなく、プログラムで有馬記念を予想してみようと思います。

なにで予想する?

競馬予想をする上で、様々なアプローチはあるとは思うのですが
今回は出走馬の過去のレースの走破タイムから予想してみようと思います。

とりあえず、実際の競馬ではレース展開、相手関係との駆け引きなどもあるので単純にタイムが早ければ強いというわけでもないのですが 競馬の専門誌などでも、ベストタイムが掲載されていたりするぐらいには重要な要素です。
普通に考えて、平均的にタイムが速い馬の方がポテンシャルは高いはず。
今回は出走馬の過去のレースから、それぞれの平均タイムを算出して、順位付けしてみます。

とはいえ、例えば距離が違う、斤量が軽ければ速くなるなど、いろんな条件で結果が変化するのが競馬。
単純に平均タイムを出しても全く意味のない数字になります。
なので今回は、タイムの比較ができるように、過去のレースが、全て今回の有馬記念と同じく、3歳以上の重賞、2500m、良馬場の条件で開催されていたとしたらどんなタイムだったのか?
というタイムの置き換えを行った上で、平均タイムを算出してみます。

どうやってタイムの置き換えをするか?

  1. タイムが変動する大きな要素でグループ化
    • 距離

    • 馬場状態(良、稍重、重、不良)

    • レースのクラス

    • レースの年齢条件(2歳、3歳、それ以外)

    • 競馬場

    で過去5年のログをグルーピングし、それぞれの条件の平均勝ちタイムを算出します。
    その中から「有馬記念」の中山、2500m、重賞、良馬場を「基準タイム」とします。

  2. 各馬の出走ログから、走ったレースと同じ条件の平均勝ちタイムと、基準タイムとの「タイム差」を算出

  3. 出走馬の実際の走破タイムに、タイム差を反映し「想定タイム」を生成

  4. 各馬の想定タイムから「平均タイム」を算出

実際には馬には距離適性とかもあるので、距離があまり違うレースや古すぎるレースを参考にしても仕方ないので
出走馬の過去2年半(2年前の秋の戦績から)の2000m〜3000mまでの芝のレースだけを対象にします。

予想タイムは?

だらだらと長くなるのもあれなので、それらを考慮して実際に平均タイム(予想タイム)を計算してみた結果がこれです。

f:id:shohojiap:20161222143434p:plain

前走、クラシック三冠最後のレース、菊花賞を制覇した「サトノダイヤモンド」がタイム1位となりました。

実際の競馬でもおそらく2番人気になりそうな馬が1位になりましたね、おそらく1番人気になるであろう、サブちゃんこと北島三郎さんの所有馬「キタサンブラック」は0.3秒差の5位、上位人気の馬はタイム的な裏付けもありそうですね

2位〜4位のタイム上位に入った、「ミッキークイーン」、「サムソンズプライド」、「アドマイヤデウス」などは穴馬ですが、タイム的には狙って見ても面白いのかもしれません。

ちなみに6位の「シュヴァルグラン」はハマの大魔神こと佐々木主浩さんの所有馬です。

さあ果たして予想タイムと実際のレース結果はどの程度リンクするのでしょうか?
有馬記念は12/25日の15時25分発走予定です。

Unity with VOCALOIDを触ってみる

※この記事は「Altplus Advent Calendar 2016」の22日目の記事です。

こんにちは。

どうせやるならと思いまして、昨年から気になっていたUnity with VOCALOIDを触ってみます。

そもそもUnity with VOCALOIDっていったい何?という話ですが、 「VOCALOIDを用いてUnityで音声の合成ができる」という、とても夢が広がる技術だそうで、発表があってからずっと気になってはいたものの、感覚的にはイマイチ盛り上がってないのかなーと感じています。(どうでしょう?)

でも、動的に歌声を生成することが出来るなんてすごいじゃないですか。もうちょっと盛り上がっても良いと思うんですよね。 ということで紹介してみます。

それでは早速やっていきたいと思いますが、 といっても、正直順を追って説明できるレベルのものではなかったので、ヤマハ公式サイトに置いてある、「サンプルプロジェクト3種類同梱パック」の「CreateSequence demo」を元に改変して今回のネタとしました。 とりあえず軽くポイントとなる箇所だけ説明します。

エンジンの起動とモードの設定

まずはエンジンを起動します。staticなYVFクラスを取り回す感じです。プラットフォーム毎にnamespaceが違うので、クラスのインポートの部分がこんな感じに。。。

#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
using Yamaha.VOCALOID.Windows;
#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
using Yamaha.VOCALOID.OSX;
#elif UNITY_IOS
using Yamaha.VOCALOID.iOS;
#endif
using UnityEngine;

public class Hoge : MonoBehaviour {
    void Start () {
        YVF.YVFStartup("personal", Application.streamingAssetsPath + "/VOCALOID/DB_ini");
    }    
}

起動後、モードの設定をします。 大きく分けて

  • Playback合成モード
  • Realtime合成モード

の2つのモードがあるようです。

ざっくり言うと、Playbackの方が、事前に用意したデータで発声、Realtimeの方が、その瞬間に合成して発声するものの様です。 Playback合成モードは最初に出来た曲があって、それをいじったりするという時に使う。(VSQXファイルを再生できる模様) Realtime合成モードは、たとえばボタンを押すなどした時にリアルタイムで音声を発声させたい時に使う。という感じでしょうか?

// PlayBack合成モードの場合。引数はエンジンのインスタンス数で、並列で歌声合成処理を実行する必要がない場合は1を設定する。
YVF.YVFResult result = YVF.YVFSetStaticSetting(engineCount);
// Realtime合成モードの場合。512はバッファサイズ。
YVF.YVFResult result = YVF.YVFRealtimeSetStaticSetting(YVF.YVFRealtimeMode.Mode512);

今回改変したサンプルはシーケンスを作って再生するというものなので、PlayBackモードになっています。

ノートの編集

実はこの間に色々あるのですが、ごめんなさい。省略します。 今回肝となる、ノートの編集部分です。ノートを編集し、シーケンスを作成→再生する。という流れになります。 ノートに音階や長さ、発生する単語などを設定します。

// ノートの編集.
List<VocaloidNote> melody = new List<VocaloidNote>();
VocaloidNote nt = new VocaloidNote();

string jsonData = "{\"noteArray\" : [{\"noteLength\" : 240,\"noteNumber\" : 76,\"lyricStr\" : \"じ\"},";
jsonData += "{\"noteLength\" : 240,\"noteNumber\" : 76,\"lyricStr\" : \"ん\"},";
jsonData += "{\"noteLength\" : 240,\"noteNumber\" : 76,\"lyricStr\" : \"ぐ\"},";
jsonData += "{\"noteLength\" : 240,\"noteNumber\" : 76,\"lyricStr\" : \"る\"},";
jsonData += "{\"noteLength\" : 720,\"noteNumber\" : 76,\"lyricStr\" : \"べ\"},";
jsonData += "{\"noteLength\" : 240,\"noteNumber\" : 76,\"lyricStr\" : \"る\"},";
jsonData += "{\"noteLength\" : 240,\"noteNumber\" : 76,\"lyricStr\" : \"じ\"},";
jsonData += "{\"noteLength\" : 240,\"noteNumber\" : 76,\"lyricStr\" : \"ん\"},";
jsonData += "{\"noteLength\" : 240,\"noteNumber\" : 76,\"lyricStr\" : \"ぐ\"},";
jsonData += "{\"noteLength\" : 240,\"noteNumber\" : 76,\"lyricStr\" : \"る\"},";
jsonData += "{\"noteLength\" : 720,\"noteNumber\" : 76,\"lyricStr\" : \"べ\"},";
jsonData += "{\"noteLength\" : 240,\"noteNumber\" : 76,\"lyricStr\" : \"る\"},";
jsonData += "{\"noteLength\" : 480,\"noteNumber\" : 76,\"lyricStr\" : \"す\"},";
jsonData += "{\"noteLength\" : 480,\"noteNumber\" : 79,\"lyricStr\" : \"ず\"},";
jsonData += "{\"noteLength\" : 720,\"noteNumber\" : 72,\"lyricStr\" : \"が\"},";
jsonData += "{\"noteLength\" : 240,\"noteNumber\" : 74,\"lyricStr\" : \"な\"},";
jsonData += "{\"noteLength\" : 720,\"noteNumber\" : 76,\"lyricStr\" : \"る\"}";
jsonData += "]}";

NoteArrayModel noteArrayData = JsonUtility.FromJson<NoteArrayModel>(jsonData);

int StartTime = preTick;
for ( int i = 0; i < noteArrayData.noteArray.Count; i++ ) {
    nt.ClearNote();
    nt.Note.noteTime = StartTime;
    nt.Note.noteLength = noteArrayData.noteArray[i].noteLength;
    nt.Note.noteNumber = noteArrayData.noteArray[i].noteNumber;
    nt.Note.lyricStr = noteArrayData.noteArray[i].lyricStr;
    StartTime += nt.Note.noteLength;
    melody.Add( new VocaloidNote(nt) );
}

今回、最低限で音階と長さと言葉のみ設定しています。ノートの登録は楽してjsonにしちゃいました。 こういうとき、unityのJsonUtilityは便利ですね。 NoteArrayModelというモデルクラスを作って、jsonデータをデシリアライズして、ぐるぐる回してます。 リファレンスにC4=72とありましたので、音階はそれに合わせて設定しました。 ド=72 レ=74 ミ=76 ファ=77 ソ=78 ラ=80 シ=81 ド=82 こんな感じかと思います。

歌声合成の実行

最終的に歌声を合成して実行します。 ソースコードについては説明できるだけの知見を得ることが出来なかったので割愛します。 サンプルソースコードやセミナーの資料にかなり詳しく書いてありますので、そちらを参照しましょう。

できたもの

windows版実行ファイル

Mac版実行ファイル

リポジトリはこちら

*Unityのバージョンは5.5です

*VOCALOID SDKのバージョンは1.3.0.0です

*動作には別途VOCALOID SDKをダウンロードし、インポートしてください。

http://business.vocaloid.com/unitysdk/download/

所感

サンプルの改変とはいえ、結構簡単に音声合成が出来るのは魅力です。 今回触りませんでしたが、音色などもかなり細く設定できるようになっています。 ゆっくりしていってね!でおなじみのSoftTalkの上位版みたいなものはサクッと作れそうな感じでした。

そして、以下感じた問題点です。

  • なにをやっているのかを読み解くコストが結構高い
  • モバイルでのパフォーマンスが少し心配
  • 現状、利用できる音声ライブラリがユニティちゃんのみ

これらが解消されていくと、より広がっていける。かもしれないですね。

以上です。ではまたどこかで。