写真から素数を文字認識するアプリ作ったよ

作ったもの

目にした数字が素数かどうかって気になってしょうがないですよね。そんなわけで、カメラで撮影した画像中の数字を認識して素数かどうか判定するアプリを作りました。

f:id:aike:20181231182513p:plain

使い方

ファイル選択ボタンを押して判定したいの数字が写っている写真を指定します。このとき「写真を撮る」を選ぶとカメラで撮影した画像を保存せずにそのまま使うこともできます。例として、未読メール数字が表示されているスマホスクリーンショット画像を使ってみます。

f:id:aike:20181231180353j:plain

 

画像を選択したらすぐにサーバに送信され数秒で結果が返ります。1129も83も素数でした!来年は良い年になりそう。ほかにも時計の数字や車のナンバーを撮影して、さっといつでも素数判定することができます。便利。

f:id:aike:20181231183626p:plain

   
 
 

使った技術

例によってReactとCloud Vision APIです。 正しく判定できる最大値は9007199254740991 (Number.MAX_SAFE_INTEGER)、素数判定はこちらのcheck-primeを使っています。シンプルな試し割り法ですが、いくつか試した中で今回の用途にはもっとも安定していて速度も十分でした。

github.com

おしまい。

   

素数レンズ Github  

大晦日ハッカソン2018  

写真から電話番号を文字認識するアプリ作ったよ

作ったもの

カメラで撮影した電話番号画像を認識して電話がかけられるスマホ用Webアプリです。

f:id:aike:20181205145655p:plain

使い方

ファイル選択ボタンを押して電話番号の数字が写っている写真を指定します。このとき「写真を撮る」を選ぶとカメラで撮影した画像を保存せずにそのまま使うこともできます。

f:id:aike:20181205200029p:plain

 

画像を選択したらすぐにサーバに送信され数秒で結果が返ります。画像の中に電話番号と解釈できる文字列があれば画面下にリンクとして表示されます。さまざまなフォントや手書き文字もわりといい感じに認識します。

f:id:aike:20181205201805p:plain

 

リンクをタップすると発信ダイアログが出て電話をかけられます*1。まずは無料お試しセットから。

f:id:aike:20181206093046p:plain

 
 
 
画面下の共有ボタンをタップして「ホーム画面に追加」でアイコンを作っておくとネイティブアプリのようにすぐ起動できて便利。

f:id:aike:20181205200012p:plain

 

ユースケース

たとえばあなたの洗濯機が夜中に水漏れしたとする。しかしサポート業者の電話受付は10時-17時なのだ。そこであなたはとりあえず連絡先の電話番号をメモ代わりに写真に撮っておく。

翌日、勤務先で空き時間に業者へ電話をかけようとして、あなたははたと困る。なんと驚くべきことに、iPhoneは写真ライブラリを見ながら電話をかけることができないのだ。最近のiPadのように2画面分割することもできず、どんなことをしようと電話のキーパッドを表示すると写真が隠れてしまう。これが名高いヒューマンインタフェースガイドラインか、これが世界の選択か。人生に疲れたあなたは天を仰ぎ、思いつく限り呪詛の言葉を叫び続けることだろう。  

* * *

 
写真に撮った電話番号にかけたい場面はわりとあるように思います。上記のような大型家電のサポート、自販機で商品が出てこないトラブル、移動中に気になる看板広告を見かけたとき、テレビCMの電話番号など、いずれも何かの事情でその場では電話できず、カメラでメモしておいて後で連絡するような状況です。そしてこれらは1回かけたらたぶんもう使わない、電話帳に登録するにはちょっと躊躇するような番号です。

以前、困り果ててツイッターで何か良い方法がないか募ってみました。

このとき結城浩さんからいただいたアイデアは、公式メモアプリに電話番号を書くと自動でリンクになることを利用して、メモに貼り付けた写真を見ながら番号をタイプする、というものでした。

この方法であれば10桁程度の数字を短期記憶する必要がないのでかなり楽になります。とはいえ、全桁打ち直すのはもう少しなんとかならないものか。  
 

最近話題のGoogleレンズは、カメラ映像や写真からテキストをはじめかなり高度な各種情報の画像認識をするようです。iPhoneでもGoogleフォトアプリの一機能として使えるみたいです。ただ、そのためにiOS標準とは異なる写真ライブラリ管理アプリを入れるのも大仰な気がします。*2  
 
つまり、ぼくらがほしいのは電話番号だけ認識するGoogleレンズの単機能版なのです。そんなわけで「電話番号専用Googleレンズもどき(怒られそうなので仮称)」、略して「電話レンズ(仮)」を作りました。  
 
 

使った技術

Reactは、まあなんというか勉強中なのでとりあえず使ってみた感じ。機能を追加するたびに複雑になっていくアプリを、DOMっぽい単位でコンポーネント化することで考えることをシンプルに保つことができるのは慣れると快適ですね。慣れるまでに3年かかりましたが。

Cloud Vision APIは、Google機械学習技術で画像に何が写っているかを認識してくれるめちゃくちゃすごいAPI。今回のアプリはCloud Vision APIにガワをつけただけの200行くらいのシンプルなプログラムです。 APIに送る処理はこの辺の記事を参考にコピペ。ぼくの人生だいたいコピペ。  

    let body = {
      requests: [
        {image: {content: base64string}, features: [{type: 'TEXT_DETECTION'}]}
      ]
    };
    let xhr = new XMLHttpRequest();
    const url = 'https://vision.googleapis.com/v1/images:annotate?key='+key;
    xhr.open('POST', url, true);
    xhr.setRequestHeader('Content-Type', 'application/json');
    const p = new Promise((resolve, reject) => {
      xhr.onreadystatechange = () => {
        if (xhr.readyState !== XMLHttpRequest.DONE) return;
        if (xhr.status >= 400)
          return reject({message: `Failed with ${xhr.status}:${xhr.statusText}`});
        resolve(JSON.parse(xhr.responseText));
      };
    })
    xhr.send(JSON.stringify(body));

 

認識結果はJSONで返ってきます。断片的な言葉の切れ端が多いので、そこから意味のある単語を見つけ出すのが腕の見せどころです。ここでは、最初に正規表現を使って、数字、ハイフン、カッコ、スペースが並んでいたら電話番号っぽい文字列と判断して抽出して、次に抽出した文字列からさらに数字だけ抜き出して10〜11桁なら電話番号と最終判断しています。

    const arr_annotations = json.responses[0].textAnnotations;
    const tel_numbers = [];
    arr_annotations.forEach((item) => {
      // 高速化のために短すぎる文字列はここで無視する
      if (item.description.length >= 10) {        
        const str = item.description;
        // 数字、ハイフン、カッコ、スペースの連続を電話番号とみなす
        const match_str = str.match(/[\d\-() ]+/g);
        // 電話番号が存在するか
        if (match_str !== null) {
          // 電話番号が複数存在する場合を考慮してループ
          match_str.forEach((item) => {
            // ハイフン、カッコ、スペースを削除して数字のみを得る
            const num = item.replace(/[-() ]/g, '');
            // 数字のみで10〜11桁のものを電話番号として配列にする
            if ((num.length >= 10) && (num.length <= 11)) {
              tel_numbers.push({str:item, num:num});
            }
          });
        }
      }
    });

 

数字の頭に“tel:"という文字列をくっつけてリンクにすると電話をかけるリンクになります。

    const hash = {};
    tel_numbers.forEach(item => {
      // 重複チェック
      if (!hash[item.num]) {
        hash[item.num] = true;
        const parent = document.querySelector('#resultarea');
        const li = document.createElement('li');
        const a = document.createElement('a');
        a.innerText = item.str;
        a.href = 'tel:' + item.num;
        li.appendChild(a);
        parent.appendChild(li);
      }
    });

おしまい。

   

電話レンズ Github  

クソアプリ Advent Calendar 2018  
 
 
 

STEINS;GATE ELITE - PS4

STEINS;GATE ELITE - PS4

 

*1:連続して利用すると「このWebサイトから自動的に電話をかけることは禁止されています」というダイアログが出ることがありますが、その場合は「通話を許可」すれば発信できます

*2:追記:この記事を投稿した3日後にiOS向けGoogleレンズの提供を開始したと公式アナウンスがありました https://japan.cnet.com/article/35130001/ 頑張って作ったアプリが3日で役目を終える気持ちを皆さんちょっとでいいので想像してみてください

パンはパンでも体の向きによらない3Dパンがでーきた

WebAudio Web MIDI API Advent Calendar 2016の19日目です。

 

せっかちな人のためのデモページリンク。スマホ+イヤホンで聞いてグルグル歩き回ってください。
http://aikelab.net/apan/

 

Web Audio APIでは、左右だけでなく3Dのパンが用意されています。つまり音の定位を三次元的に配置することができます。
さて、せっかく3Dパンなので、スマホの加速度センサーを使って、聴く人の体の向きによらず空間的に音が配置されるようにしてみましょう。つまり、たとえばイヤホンで音楽を聴きながら横を向くと、それまで左右から聞こえていた音が前後から聞こえてくる、というようなことです。

 

ソースコードはこんな感じになります。
まず何はなくともオーディオコンテキストの作成。

    var ctx = new (window.AudioContext || window.webkitAudioContext)();

Playerは自作のBufferSourceNodeラッパーです。詳しくはplayer.jsを見てください。

    var player = new Player(ctx, 'sound/apan.mp3');

チャンネルスプリッターノードでステレオソースの左chと右chを別々に取り出します。

    var splitter = ctx.createChannelSplitter(2);

パンナーノードで左chの音と右chの音を三次元空間に配置します。

    var panL = ctx.createPanner();
    panL.panningModel = "HRTF";
    panL.setPosition(-1, 0, 0);

    var panR = ctx.createPanner();
    panR.panningModel = "HRTF";
    panR.setPosition(1, 0, 0);

ノード同士の接続はこんな感じ。

    // player ----splitter
    //               ---- PanL ---- destination
    //               ---- PanR ---- destination
    player.connect(splitter);
    splitter.connect(panL, 0);
    splitter.connect(panR, 1);
    panL.connect(ctx.destination);
    panR.connect(ctx.destination);

パン回転用のsetAngle関数です。角度をラジアンで受け取ってパンナーノードの位置をY軸を中心に回転します。

    var setAngle = function(theta) {
        var sn = Math.sin(theta);
        var cs = Math.cos(theta);
        panL.setPosition(-cs, 0, sn);
        panR.setPosition(cs, 0, -sn);
    } 

加速度センサーのイベント処理。加速度センサーはalpha、beta、gammaの3つの値で表現され、スマホを縦持ちしたときのY軸回転はalphaに対応します。加速度センサーの値は度なのでラジアンに変換してから前述のsetAngle関数に渡しています。

    window.addEventListener("deviceorientation", function(e){
        if (e.alpha) {
            setAngle(e.alpha * Math.PI / 180);
        }
    });

 

デモページはこちらです。
http://aikelab.net/apan/
スマホ+イヤホンで再生します。
こんな姿勢でスマホを持ったままグルグル歩き回ってください。


ソースコードはこちら。
https://github.com/aike/AcceleroPanner

メーヴェ型飛行具のプロジェクトに参加した話

 メーヴェを見にいこうと思ったのは、2014年の夏、5年勤めた会社を辞めて、さてこれからどうしようかというときだった。八谷和彦さんのOpenSkyプロジェクトが北海道で初めて公開テスト飛行をおこなうということを聞いて、特急スーパーカムイに乗って、たきかわスカイパークまでいくことにした。ところが、現地に到着したくらいから天候がくずれ、その日のフライトは中止になった。運もついていない。
 
 その代わり展示された機体を間近でつぶさに観察することができた。M-02Jという正式名称もそのとき知った。離れたところから見ると白くなめらかな機体の曲線に目を奪われるものの、細部はホームセンターにありそうな部材だったり、自転車用のパーツのようなものをベルクロや補修テープでくくりつけた無骨さ。ナウシカというより、どちらかというとこれはハウルだ。アクセル制御にいたっては音楽用ミキサーのフェーダーで、0dBまで上げるとジェットエンジンが吠える。時代考証が無茶苦茶なテクノロジーの塊だった。
 

 

 
 1週間後、八谷さんからメッセージが届いた。
「先日のカメラを機体に取り付けて撮影しませんか?」
 公開飛行がおこなわれるはずだった日、空の端から現れて反対側の空へ消えていくM-02Jを撮影しようと、特殊な全天球動画カメラを持っていったのだ。それを地上からではなく機上から撮影できるとは。
 
 早速、次の週チームに合流した。朝7時、メンバー全員を集めてブリーフィング。驚いたことにすぐに機体の移動補助や飛行記録といった責任ある役割をまかされる。後から知ったことだが、他のメンバーも初参加や2回目などが多かったようだ。
 ブリーフィングが終わると次はエンジンの試動。万が一のときのために、少し離れたところから消火器を手に待機する。アニメ調の機体から獰猛な燃焼音が響く。普段はグライダーとプロペラ機しかいない、のどかな飛行場で、ジェットエンジンは異次元の音だ。地鳴りのようなサブソニックと高速回転する金属音。イヤーマフをしていても頭蓋骨に響きわたる音に意識がゆっくり遠のいていくような気分になる。いまだに実感のわきづらい光景もあいまって現実とSFとが少しずつ融けていくような感覚を味わっていた。
 

 
 たった2年前なのに全天球動画撮影環境は今とかなり異なる。専用のカメラなどではなく、6台のGoProカメラを3Dプリンタで製作したリグで固定したものだ。これらをWi-Fi接続のリモコンから操作する。たいていひとつくらい電波が届かずリモコンに反応しないので目視確認して手動で録画開始する。数を重ねるうちに、手鏡を使って確認したり、映画用のカチンコアプリで合図を入れたり、とノウハウがたまっていく。
 
 撮影した映像はその日のうちにスティッチング。テスト飛行は朝9時には終わるので、昼の空き時間や就寝前の夜間、6本の動画を専用ソフトでひとつに合成する。作業してわかったことだが、複数カメラ映像の合成加工は、至近距離から地平線まで映り込む空撮にはあまり向いていないようだ。パイロットに合わせて合成すると遠い景色の輪郭がずれてしまい、かといって遠距離を合わせると今度は翼端の映像がずれる。もっともつなぎめが目立たないポイントを時間をかけて探していく。全フライト分を翌日に確認できるよう作業をするとなるとけっこう忙しい。
 
 動画の確認もノートPCで見るだけでは十分伝わらないので、あるときはOculus Riftを持ち込み、あるときはハコスコと自作アプリで即日確認できるようにした。
 

  
 そのシーズンの最終日、ドローンの操縦を任された。ドローン撮影はチームとしても初めてだったのであまり効率よく撮影することはできなかったが、それでもワンカット、M-02Jが飛び立つ様子を上空からとらえることができた。*1
 
 このプロジェクトに参加する中、とても興味深く見ていたのは、八谷さんのプロジェクト運営、チーム運営方法だった。ボランティアベースのコミュニティということで、オープンソースプロジェクトのような、ゆるいつながりに近い。とはいえ、なにしろ航空機なので大きなお金も動くしミスがあったら人が死ぬ。そういった作業の一端を、初めて会ったばかりの大学生にもどんどん担当させていく。
 ボランティアベースだとスパルタ式にはいかない。丁寧な言葉で明確に指示をする。搭乗姿勢になった後は細かい指示は出せないので、メンバーそれぞれ自律的に動く必要がある。さらには700mほど離れたテイクオフ側とランディング側で2つのチームが独立して正しく機能する必要がある。八谷さんは、メンバーのスキルを見極めた上で全面的に信頼して担当を依頼する。それでも経験が浅いとミスはあるので、要所には関東から呼んだベテランスタッフを配置する。 
 また、利用させてもらう各種施設やコラボレーション先と付き合うときも、一方的な立場になるのではなく、相手側にもメリットのあるようにして、良い関係を長く続けられるようにする。周りの人たちとお互いにリスペクトした関係性を、わずかな期間で実にたくみに作り上げていってしまう。そのあたりの立ち回り方の秘密について何度か本人に聞いてみたのだが「偶然」とか、「出会いを大切に」とか、いつも普通の言葉でかわされてしまう。いや、それ絶対普通のスキルではないと思う。たぶん、それができれば今頃みんな、空を飛んでる。
 
 M-02Jは地面に映る影が非現実的なくらい美しい。この影を見る限り、八谷さんは完全に鳥だ。僕がこのプロジェクトに参加して撮影した全天球映像は、ハコスコアプリの「きみはテト2」で見ることができるので、ぜひ自分の目で確認してほしい。





   
 そんなOpenSkyプロジェクトであるが、2015年以降は参加できていない。時間的に都合がつかなかった、というのが直接的な理由ではある。本当の理由は、八谷さんを見て、なにかひとつ自分の大きなプロジェクトを始めたくなってしまったからだ。人間とコンピュータが融け合うSFを実現するプロジェクト。そのために働きながら大学院に入りなおし、土日はすべて研究の時間にあてるようにした。なにもかも手探りの状態なので、10年以上かかるかもしれない。
 
 OpenSkyプロジェクトの方は、今年無事目標だった場周飛行を成功させていた。クラウドファンディング支援者向けのテストフライト報告会が開かれ、その会場で久しぶりに八谷さんとお会いすることができた。プロジェクトは僕が参加していたときにくらべて、さらに加速していた。エンジン出力が倍になり、ずっと鋭角に上昇していた。ナウシカのテーマ曲の生演奏に乗せて、石狩平野のはるか上空で八谷さんは自由自在に飛んでいた。
 

 


*1:H ZETTRIOのMV「Wonderful Flight」で使われているドローン映像はこのとき撮影したものだそうです https://www.youtube.com/watch?v=-mlI_hZAS6U

ブラウザでMMLを演奏するChrome拡張作った

ブラウザでMML(Music Macro Language)の文字列を選択してCtrl-Cを押すと音楽が流れるChrome拡張を作りました。
Googleウェブストアからインストールできます。
https://goo.gl/0UJQcT
ソースはこちら
https://github.com/aike/TSSCCjs
実行例
http://youtube.com/watch?v=o-IzrXaqIbM


今から10年くらい前、ネットの掲示板上でMMLによる音楽が盛り上がったことがありました。添付ファイルなどではなく、掲示板のテキストとして直接貼られた10行程度のMMLでも驚くほどリッチな楽曲が再現できるため、いかに短いMMLでリアルな音楽を表現するか、技術の競い合いのような文化が生まれていました。

 

当時、再生ツールとして使われていたのが、keimさんが作られたTSS Clipboard Player(TSSCPまたはTCP)というWindowsMMLプレイヤーです。これは、とよしまさん制作のT'SoundSystem(TSS)という、PSG、ファミコンMSXFM音源などの音を再現できるサウンドドライバを利用したものでした。Ctrl-CでクリップボードMMLをコピーするだけですぐに音楽が再生されるTSSCPのあまりの使いやすさと、TSSの音の良さに支えられて、掲示板上のMMLは大変盛り上がったようです。(自分は後から知った口なので、当事者ではないんですが)

 

その後、別の方による偽TCPというツールも出たりしましたが、基本的にTSSCPMMLデファクトスタンダードとなって、その上に拡張機能を追加したものになっています。

 

月日は流れ2016年、Web Musicハッカソン#5で、いまやWeb Audio/Web MIDIのキーマンである、とよしまさんにお会いすることができたので、
「TSSのWeb Audio版ってないの?」
と聞いてみたら、
あるよ
とのこと。つまり、OSにかかわらずブラウザのJavaScriptMMLを再生できる!

 

「じゃあ、クリップボードAPIMML演奏するツールもあるの?」
MMLの方言がいろいろ違うし、それはまだなくて……」
え、そうなの? MMLの方言くらいなんとかなるでしょ、ってことで作りはじめたのがこのツールです。つまりTSSCPのWeb版クローンです。

 

実装してみたら、意外と大変で。ブラウザのクリップボードAPIはセキュリティのために、コピー操作時点でのクリップボードの内容が取得できないことが判明。ぐぬぬ
まあ、キー操作と選択文字は取れるので、Ctrl-Cを押されたときに選択されている文字を取得すれば同じことはできるか。*1

 

MMLも難関です。
やるべきことはTSSCP形式のMMLをTSS形式のMMLに変換してTSSに渡すだけだし、どうせMMLの方言といっても、不等号の向きとかそんな程度だろうと思ってたら全然違いました。TSSCPMMLは圧縮効率を重視したさまざまな拡張がされていて、行の終端は改行ではなくセミコロン、#A〜#Zまでマクロ定義、#FMマクロでFMアルゴリズムの柔軟な定義、といったTSSにない機能を持っています。

 

たとえばTSSCPMMLは以下のようなものです。

#TITLE <sample>;t150;#A=%3v15o5cdefg;#FMB(A);A;A;

これを、TSSで再生できるようにするには以下のようなMMLに変換する必要があります。#FMマクロは、MMLの中でもさらに別のシンタックスを持った言語と考えられ、専用のパーサを別途用意する必要がありました。

#TITLE <sample>
#CHANNEL 3
#A t150
#B %3 @i0,0@o1,0v15o5cdefg
#C %3 @i3,0@o0,0v15o5cdefg

 

また、通常のマクロも、A(7)とすると、5度(7セミトーン)上に移調してマクロ展開するなど、高度な機能があり、これらを忠実に実装しなくては当時のMMLを同じ音で再生することができません。
さらに、偽TCPの拡張命令も難題です、これはTSSでは再生不可能な機能を実装していたりするので拡張コマンドを無視するしかないのですが、MMLは命令同士がセパレータで分割されておらず、また命令の文字数も1〜数文字とまちまちであるため、無視するにしても、ここからここまでが拡張コマンド、というように認識する必要があります。

 

結局、簡易的な文字列処理ではなく、しっかりパースして抽象構文木を生成してから変換するちょっとしたトランスパイラのプログラムになりました。

 

だいたいここまでで、当時のMMLは再生できるようになりましたが、まだ同じ音色になりません。よく調べてみると、JS版TSSのバグで発音数制限があったり、JS版ではmlコマンドがサポートされていなかったり、ということあったので、JS版TSSのソースに手を入れて調整した結果、ほぼ同じ音が出るようになりました。*2

 

非互換部分は以下のとおりです。
・偽TCPの拡張コマンドは非対応
・JS版TSSでは固定波形テーブル音源が未実装のため、%5の音源は無音になる
・oコマンドやlコマンドを未指定時の値が違うっぽい
・Panが左右逆かも
・文法エラーのあるMMLの解釈

 

再生確認用のサンプルMMLも書いてみたので、Chrome拡張をインストールして聞いてみてください。

#TITLE <TSSCCjs's Theme by aike CC-BY>;
t165;#A=l4dr8d16e16f2.r8gf8e8f8e.d.ec.d.efr8f16g16a2.r8b-a8g8d8fr8fr8fgr8gr8
;#B=d.a2r8g.c+2r8c+.d.ef2.rd.a2r8g.c2r8c+.g.df2.r;#C=l4ar8a16g16a2.r8ga<d8c.
>a.<c>a.a+.<c>a.b-16<c16d2.r8c.>a+ar8ar8a;#D=a.d8>a8.<d8.f8er8e8e8ef8g.a8a+8
ga+8;#E=dgfdgdfd>a<fd>a<f>a<d>a<cgecgcec>a<ec>a<e>a<c>a<dgfdgdfdcgecgcec>b-<
fd>b-<f>b-<d>b-;#F=fgfde.f8g.r8efg8aa+8ac+defgfde.f8g.r8efg8aa+8d2.r;#G=l4d.
>a.<d>b-r8b-.<dc.>g.<c>ar8ar8a<d.>a.<dcr8cr8c>b-r8b-r8b-a16r16a16r16a16r16a1
6r16<e>a<;#H=l16[dagfdfga][c+agfc+fga][eagfefga][dagfdfga];#I=[4arararar]<[d
rdrdrdr]frfrfrfrgrgrgrgr;#J=AeAa+BB;#K=C<cr8cr8c>C<c+r8c+r8c+Da2.rDa.d2r8Da2
.rDa.d2r8;#L=l8Ea<ec>a<e>a<c>a<Ea<ec+>a<e>a<c+>al4<FF>;#M=GGHHHH;#N=l8[8r1]I
[16r1];#O=%3v10ml3q6s5;#P=%3v10mp1,4,1,2,0k1q6s5;#Q=%3q10s3;#FMC3(B2(A));Oo6
$J;Po6$J;Qp1o6v6$J;#FMC3(B2(A));Oo5p2$K;Po5p2$K;Qp2o5v6p2$K;#FMC3(B2(A));Oo5
$L;Po5$L;Qo5v4$L;#FMC4(B2(A));%3o4l4v15$q0s1o3M;%3o4l4v13$q0s3o3M;%3o4l4v4$q
13s1o3M;o6v4q4s4$N;o6v4q4s4k5$N;o6v2q4s4r8.$N;o6v2q4s4k5r8.$N;o8%2q0s50l16v8
$[16arararaaarararaa][16araaaraaaraaaraa];#R=[7rara]ral16s20;%2q0l4o3s12,18v
8$Rarrraaaal4s12Raaaaaaaal4s12Rarrraaaal4s12Raaaaaaaal4s12;%3o4v15s1,-15q1l1
6$[16crrrrrcrcrrrrrcr][16crccrcrccrccrcrc];

*1:そういうわけでClipboard Playerとは名づけてません

*2:いずれも本家にプルリクエストして反映してもらいました

UnityでサイドチェーンしてSEとBGMの自動バランス調整する話

最近のUnityは音楽制作用DAWのような機能が充実してて、試しにサイドチェーンできるか調べたらできたのでメモ。
 
サイドチェーンというのは音圧上げないと死ぬ病の人たちが発明した音楽用コンプレッサーの使い方で、キック(バスドラム)の音が鳴った瞬間、それ以外のシンセやベースなどの音量を自動的に下げる、というものです。これにより音圧をギリギリまで上げつつ、キックの音をしっかり目立たせ、バックの音にもンッアッンッアッというリズムを生み出すという一粒で三度美味しいワザです。
 
ゲーム制作でいうと、SEやセリフの音が鳴るときにBGMを自動的に下げるというような、よくある状況に応用できます。
 
早速やってみましょう。
 
UnityのWindowメニューからAudio Mixerを選択します。大丈夫、あなたのPersonal(無償)版でもできますから。
 
全然ミキサーっぽくない表示が出て若干不安になりますが、勇気を出して「+」印をクリックし、ミキサー名を入力します。ぼくはこれまでの人生でミキサーに名前をつけるような経験は一度もしたことがなかったので少しとまどいましたが、無難に「mixer」としました。


 

一歩前進しました。でもまだミキサーっぽくないのでGroupsのMasterを選択した状態で「+」をクリックしてトラックを追加します。トラック名はつけたことがあるので迷いません。「SE」と「BGM」にしました。


 

Audio Sourceをふたつ作ります。AudioClipとしては数小節分のキックのサンプリングとシンセのサンプリングループを用意します。サンプリングループは自分で作ると大変なので買ってきました。お金って便利。
 
キックの方はOutputを「SE(mixer)」に、シンセの方はOutputを「BGM(mixer)」にします。



 
サイドチェーンをかけずにそのまま混ぜて鳴らすとこんな感じになります。ふーん。

Download
 
サイドチェーンの登場です。
トラックの下の方の「Add ..」をクリックし、SEは「Send」、BGMは「Duck Volume」を選択します。ミキサーっぽくなってきた。


 
SendのReceiveを「BGM\Duck Volume」、Send levelを最大の「0.00 dB」に設定します。


 
Duck VolumeのThresholdやRelease Timeをこんな感じに設定します。Thresholdは左の方にするほど強くかかるので、最初は強めにして効果を耳で確かめてから丁度いいところまで上げていくと分かりやすいかもしれません。


 
サイドチェーンをかけて鳴らすとこんな感じ。キックがしっかり聞こえて、シンセがンッアッンッアッですね。

Download
 
次に別の音をトリガーにしてみます。
ユニティちゃんのボイスを公式サイトからダウンロードしてきました。
 
普通に混ぜるとこんな感じ。もごもご。

Download
© Unity Technologies Japan/UCL
  
これもキックと同じようにOutputを「SE(mixer)」にします。


 
Duck VolumeのThresholdはさっきより強め。ユニティちゃんが一言しゃべるたびにンッアッってならないようにRelease Timeは長めにします。


 
できた。

Download
© Unity Technologies Japan/UCL

 
ね?簡単でしょ。

OSCをMacやWindowsやLinuxやRasPiから送るシンプルツール作った

OSC(Open Sound Control)を気軽に送りたくなることってあるじゃないですか。ラズベリーパイからPCへ簡単な通知を送ったりとか。どこのご家庭にもある雰囲気メガネを光らせたいときとか。
 
でもなかなかシンプルなツールが見つからなくて、開発環境入れてコンパイルする必要があったり、音楽や映像を扱う大きなソフトの一機能だったりして、ちょっと気軽な感じがしないものばかりで。そんなわけで、コンパイル不要で実行ファイルひとつだけでOSCメッセージを送れるコマンドを作りました。
 
http://github.com/aike/oscer

 
使い方は簡単。コマンド名の後にホスト名、ポート番号、OSCメッセージを指定するだけ。
 
実行例

oscer localhost 10000 /dummy

 
引数にはInt32、Float32、Stringが指定できます。

oscer 192.168.1.10 12000 /hello 10
oscer 192.168.1.10 13000 /world 3.14
oscer 192.168.1.10 12000 /hello world

 
IPv6も対応。たぶん。

oscer fe80::1%lo0 11000 /foo


golangで書いたので、クロスコンパイルも簡単。
MacWin32Win64Linux32Linux64LinuxARM(Raspberry Pi)用にビルドしておきました。golangは初めて使いましたが生ソケット通信みたいなアプリでも完璧にWrite once, run anywhereなのはびっくり。実行側はgolangをはじめランタイムライブラリ的なものが一切不要なのも嬉しいです。
 
そんなわけで良いOSCライフを。
 
2016.06.13 追記
受信側もほしくなったので受信機能もつけました。

oscer receive 10000