web audio APIを用いたiOS6 safari対応の仮想音源位置の移動があんまり上手くいかなかった話
こんなん作りました。以上です。
ではなく。ちゃんと解説します。
ざっくり言うと、「chromeとiOS6 safariで動く、仮想音源位置の移動体験」です。
使い方はれあどめ読んでくださいな。
web audio API使ってます。日本語記事少ないしリファレンス読めないし大変だったー。
Web Audio API
Web Audio API のスペック (日本語訳) - miura off
説明のためにこのアプリのjsを部分要素に分解しますと
- 音源位置の移動
- タッチ位置の検出
- selectの選択値から仮想音源位置の操作を行う面(水平面、正中面、冠状面)を変更する
- canvasに人の頭アイコンを描画する
になります。で、今回1.しか説明しません。他はそんなに面白くないので。
ざっくり言うと、web audio APIってのは、nodeを繋ぎ合わせて音に色んな効果とかをつけるもんだ、っていう解釈してます。やり始めたの最近なんで違ってるかも知れないですが。
Web Audio API 解説 - 01.前説 | g200kg Music & Software
ここが非常に詳しかったです。オシレーターやゲイン操作、オーディオバッファやパナーのnodeをwebkitAudioContextから作って順番にconnectしていって、最後はcontext.destinationにconnectしてnoteOnすると音が鳴る、と。言ってしまえばそれだけです。僕の現時点での知識では。
実際のコードで言うと
var ctx = new webkitAudioContext(), src = ctx.createBufferSource(), pnr = ctx.createPanner(), node = ctx.createJavaScriptNode(1024, 1, 1);
でnode作って
src.noteOn(0); src.connect(node); node.connect(pnr); pnr.connect(ctx.destination);
でつないで、noteOnで再生。各nodeのメソッドなりメンバいじって聴こえを変える、と。
ちなみに止めるときは
src.disconnect(); node.disconnect(); pnr.disconnect();
ってやってる。noteOffってメソッドあるし、そっちでもできそうな気はしている。
で、肝心の音を作ってる部分
node.onaudioprocess = function(e) { var out = e.outputBuffer.getChannelData(0), rslt = [], len = out.length; for (var i=0;i<len;i++) { rslt.push(out[i] = Math.random()); } return rslt; };
forの中で、返り値の配列の中に乱数入れてホワイトノイズ作ってます。ただ、ちなみにこの部分
iOS6 の Web Audio API を使う - 音の鳴るブログ
こちらを参考にしていてまだ調査中という酷い状況に…
ちゃんと調べてここで報告しますすみません
で、仮想音源位置を動かしてる部分ですが、web audio APIのpannerNode(コード中ではpnr)にsetPositionというなんともそのまんまなメソッドが用意されています。
このメソッド、引数が3つ(x,y,z)で、値域は-∞から+∞です。(0,0,0)が聴取者の位置で、3次元座標をイメージすると分かりやすいと思います。
ちなみに両耳を貫く左右がx軸、頭部を脳天から首に向かって貫く上下がy軸、後頭部から鼻を貫く前後がz軸です。この値をタッチまたはクリック位置によって操作して、仮想的に音源位置を動かしています。
まとめ
- web audio APIで用意されているnodeの中から必要なものをwebkitAudioContextから生成する。
- 音源を作る
- 各種nodeをconnectでつなぎ、最後をcontext.destinationにconnectする
- context.noteOn()で再生する
- pannerNodeのsetPositionで仮想音源位置を簡単に操作できる
あんまりまとまってないな…。でもこれに関してはまだまだ書きたいこと(iOS6 safari対応時のつまずき、対応してみて分かったこと、pannerNode自体に考えられる問題)があるので別記事にします。
お腹減った…
佐藤フィルターで使ったあれこれの話(Youtube Player API その1)
先日、デザイナーの佐藤ねじさんからお話をいただいてこんなもの作った
ねじさんが得意な一発ネタ系サイトで、内容としては「横浜駅周辺で"佐藤さ~ん!"と呼びかけて、どのくらいの人数が振り向くか」を動画で撮影して公開するというなんともいい感じにバカおもろい企画だった
かく言う自分も佐藤なんだが、わけあって当日の撮影には参加できなかった、無念
で、このサイト、公開日に縛り(3/10=佐藤の日)があり、かつページのシンプルなデザインとは打って変わって手の込んだ実装となったために、公開日直前は死にかけた。
そのメモ(主にYoutube Player API)を残しておく。ちなみにYoutube Player APIのリファレンスはこちら(YouTube JavaScript Player API リファレンス - YouTube — Google Developers)
<script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script> <script type="text/javascript"> var params = {allowScriptAccess: 'always'}; var attrs = {id: 'myMov'}; swfobject.embedSWF('http://www.youtube.com/v/FidK-hgq_qY?loop=1&controls=0&enablejsapi=1&playerapiid=mov1&version=3&rel=0&disablekb=1&start=0&autoplay=0&showinfo=0&iv_load_policy=3','elmMov1', '640', '360', '8', null, null, params, attrs); </script> <div id="elmMov1" class="videoElm"></div>
とりあえずこれだけあれば動画は表示される。はず。
解説していくと、まずswfobject.jsのincludeが必須。また、embed時に渡すパラメータとして「allowScriptAccess: 'always'」が必要。そんでattrに、操作するplayerのidを渡しておく。↑の例だと、myMovがplayerのオブジェクトとなり、こっからメソッド叩いて再生とか一時停止とかする。
次にsefobjectのembedSWFで再生する動画のURLと各種パラメータを指定する。大事なのは
enablejsapi=1 playerapiid=mov1 disablekb=1
この辺。上から説明すると
- enablejsapi=1
そのまんま。youtube player APIによる操作を有効にする
- playerapiid=mov1
値は任意。動画の読み込みが完了した際にonYouTubePlayerReadyという名前で関数を宣言しておくと、関数が実行されその引数にこのplayerapiidがセットされる。佐藤フィルターでは、ここでセットしたidの数字からattrのidを決定し、操作している。
- disablekb=1
そこまで重要ではないが、youtubeが実装しているキーボードショートカット(スペースキーで一時停止/再生とか)を拒否することができる
ふー疲れた。あんまり長くすると読み手も疲れるからね(言い訳)。続きは次回書くことにしましょう。今回はまだ、ただembedしただけになってるので、次回はjs側から再生を制御するところについて。多分あと1回で終わると思います。ではではー
ネガティブかも知れないけれど、トップダウン型の開発をやめようという話
フロントに限定した話かも知れないし自分に落ち度があったので
手元にあるハイスペック端末で検証しながらアプリを作って、ロースペック端末用に機能削減版を作るんじゃなくて
対応すべき端末の一番下で検証を始めて、ハイスペック端末には演出を出し分けるような開発をしてみようという宣言。
ネガティブかも知れないけど、まだまだ経験浅くて見立ても悪いからこうやっていこう。
趣味なら別にどうだっていいけどそうじゃないからね。
慣れるまでは、トップダウン型の開発をやめてボトムアップ型の開発をしよう、ネガティブかも知れないけど。
チラシの裏でもTwitterでも良かったんだけど、ログしやすいからこっちで
ネガティブかも知れないけど
全然ないけどsassの自作mixin
はい、今週もお疲れ様でした。というわけで、sassの自作mixinをずらずらっと書いていく
position一括指定
基本的にleftとかtopとかってabsoluteのときしか書かないんだけど、absolute指定した時点でleftかtopは確実に書くし、right、bottomも場合によっちゃ書くから、そのために複数行書くのめんどくさいよねってとこから作ったやつ。
mixin
@mixin pos($top: 0, $right: 0, $bottom: 0, $left: 0) { position: absolute; @if $left != '' { left: $left; } @if $top != '' { top: $top; } @if $right != '' { right: $right; } @if $bottom != '' { bottom: $bottom; } }
scss
.hoge { @include pos(10px,5px,10px,5px); }
.hoge { position: absolute; left: 5px; top: 10px; right: 5px; bottom: 10px; }
前述の通り、top、left等で指定するのはposition: absoluteのときがほとんどだからabsoluteも一緒に宣言してる。便利。ちなみにこの、top、right、bottom、leftっていう引数の順番は、marginとかpaddingに合わせてあるから覚えやすい。
ベンダープレフィックス一括指定
大体compassで用意されてるけど、たまたま用意されてなかったりするときに使う、ベンダープレフィックス一括指定
mixin
$prefixs: -webkit-, -moz-, -ms-, -o-, ''; @mixin addPrefix($prop, $val) { @each $prefix in $prefixs { #{$prefix}#{$prop}: $val; } }
scss
.hoge { @include addPrefix(border-radius,15px); }
.hoge { -webkit-border-radius: 15px; -moz-border-radius: 15px; -ms-border-radius: 15px; -o-border-radius: 15px; border-radius: 15px; }
始めにprefixの配列を宣言して、引数にもらったプロパティの接頭辞に全部ベンダープレフィックスつけるだけ。あんま使ってない。
スプライトシートのbackground-position自動計算
スプライトシートでコマアニメ作るとき、今までは各状態のbackground-positionを手と頭で計算して書いてたけど、そこ自動化できることに気付いたから書いた
mixin
@mixin posSprite($imgpath, $length, $label) { @for $i from 1 through $length { &.#{$label}#{$i} { background-position: 0 -#{image-height(#{$imgpath})/$length * ($i - 1)}; } } }
scss
.hoge { @include posSprite('icon_suika.jpg',3,suika); }
.hoge.suika1 { background-position: 0 -0px; } .hoge.suika2 { background-position: 0 -60px; } .hoge.suika3 { background-position: 0 -120px; }
第一引数にスプライトシートのパス、第二匹数がコマ数、第三引数が各状態の接頭辞になるラベル。ちなみにこのmixinはスプライトシートが縦長の場合しか想定していない。スマホとかは読み込める画像の高さとか幅に制限があったはずだから横長にも拡張する必要があるかも。ちなみに便利なのは、わざわざ画像のサイズを調べなくても、image-height(又はwidth)(画像パス)で画像の高さ、幅を取得できることだ。sassすごい。もちろんだけど、指定したパスに画像がないと取得できない。ちなみに今回使ってる画像はアイコンの萃香ちゃん(180px*180px)です
テキストの背景画像置換(retina、ボタンマウスオーバー対応)
文言画像を置くときの方法は2種類あると思う
- 例1
html
<h1><img src="maintitle.png" alt="メインタイトルです" width="200" height=50" /></h1>
- 例2
html
<h1>メインタイトルです</h1>
scss
h1 { width: 200px; height: 200px; text-indent: -9999em; overflow: hidden; white-space:nowrap; background-image: url('maintitle.png'); background-repeat: no-repeat; background-position: 0% 0%; }
てな感じで。同様に、ボタン画像を置くときも上記のような二択があると思っていて、SEO的にどのくらい差が出るかは正直わからん。以前までは例1で書いて、ボタン画像の:hoverのときはbackground-imageのパスを書き換えてたんだけど、その場合プリロードしないとhover時に最初のリクエストが走るから一瞬ボタンが消えたようになって非常に嫌なので今は例2で書いて、ボタンの通常とhover時をスプライトで書き出してhover時はbackground-positionを書き換えるようにしている。で、例2のscssのコードは正直同じようなのばかりで書くのめんどくさいからmixinにした。
mixin
@mixin replaceImg($imgpath,$flagSP,$flagBtn){ overflow: hidden; white-space:nowrap; text-indent: -9999em; background-image: url(#{$imgDir}#{$imgpath}); background-repeat: no-repeat; background-position: center top; @if $flagSP == true { width: image-width(#{$imgpath})/2; height: image-height(#{$imgpath})/2; background-size: image-width(#{$imgpath})/2 image-height(#{$imgpath})/2; } @else { width: image-width(#{$imgpath}); @if $flagBtn == true { height: image-height(#{$imgpath})/2; a { width: 100%; height: 100%; display: block; } &:hover { background-position: center bottom; } } height: image-height(#{$imgpath}); } }
scss
.hoge { @include replaceImg('icon_suika.jpg',false,true); }
.hoge { overflow: hidden; white-space: nowrap; text-indent: -9999em; background-image: url(icon_suika.jpg); background-repeat: no-repeat; background-position: center top; width: 180px; height: 90px; } body .hoge a { width: 100%; height: 100%; display: block; } body .hoge:hover { background-position: center bottom; }
第一引数が画像パス、第二匹数がスマホかどうかのフラグ(retina対応すべきか否か)、第三引数が、それがボタンかどうかのフラグ。ちなみに、ボタンの場合はスプライトシートが縦長かつ、通常とhover時の高さが同じ必要があるという制約がある。その内記事書くけど、compass sprite-mapとかでその辺は解決してけたらなぁと思う(CSS Sprite Helpers for Compass | Compass Documentation)。
で、解説すると、
overflow: hidden; white-space:nowrap; text-indent: -9999em; background-repeat: no-repeat; background-position: center top;
ここは固定で入る。画像パスからbackground-imageとwidth、heightを設定し、もしflagSPが立ってたらサイズを半分にしてbackground-sizeも半分で書く。もしflagBtnが立ってたら、高さを半分にして中に置いてある(であろう)aタグを幅高さいっぱいにして、hover時にbackground-positionをずらす設定をする。
ふー疲れた……自分のメモがてらとはいえ、誰かが読んでくれるようになると嬉しいしモチベーションになるんだろうなぁ。
というわけで次は、やっと脱jQueryしたらメソッドが貧弱過ぎたのでちょこちょこ自作していくぜ、classManager編です。そのうちcompass sprite-mapも書きます。ではー
compassのconfig.rbとか自作mixinとかの話
はてな記法ってなんだよくそがあああああああああああああ!!!!!!!!!!!
どうもかるねです。sass/scssのプラグイン集だったり、プラグインの自作ができるcompassのお話です。
- config.rb
compass createするとsass,cssディレクトリと一緒に作られるこのconfig.rbをメモがてら書いて解説していきますよ
http_path = "/" css_dir = "css" sass_dir = "scss" images_dir = "img" javascripts_dir = "js" cache = false output_style = (environment == :production) ? :compressed : :expanded line_comments = (environment == :production) ? false : true
最近変えたのは
output_style = (environment == :production) ? :compressed : :expanded line_comments = (environment == :production) ? false : true
ここ。そんなに変わんないけどね。最初から解説していくと
http_path = "/"
いきなりいまいちわかってない。公式ドキュメントには「The path to the project when running within the web server. Defaults to "/".」ってなってるけどここ変えても特に不具合が起きたことがないし情報が少ない。誰か教えて
css_dir = "css" sass_dir = "scss" images_dir = "img" javascripts_dir = "js"
config.rbから見た各コンテンツ群のディレクトリ。
cache = false
コンパイル時に作られる.sass-cacheディレクトリを作らない設定。別にあってもなくてもいいんだけどたまに間違えてプロジェクトのリポジトリにコミットしちゃうから書いてる。
output_style = (environment == :production) ? :compressed : :expanded line_comments = (environment == :production) ? false : true
三項演算子については後述。
output_styleはコンパイルによって出力されるcssの見た目を決定する。取りうる値は4つ、expanded、nested、compact、compressed。後に行くほど可読性が下がる。expandedとnestedはほぼ同じ、compactはセレクタとそれに対応するスタイルを1行で記述し、compressedは改行もスペースもない。
line_commentsはbooleanをとり、出力されるcssにコメントを付加するか否かを決める。trueにすると、出力されたcssの各要素に、それがどのsass/scssファイルの何行目をコンパイルしたコードかというコメントが付加される。
開発時にデバッグするときはよくコンパイルされたcssを読むので、expandedかつline_commentsありでコンパイルしているが、本番時はできる限り容量を削りたいので、compressedかつline_commentsなしでコンパイルしたい。ただ、そのためにconfig.rbを書き換えるのは面倒なので、前述の三項演算子を使う。
変数environmentはproduction(本番)とdevelopment(開発)の2つの値をとり、コンパイル時の引数から指定できる。ちなみにこのconfig.rbの場合、開発時は
compass watch -e development
でコンパイルしてexpandedかつline_commentsありのcssを出力し、本番時は
compass watch -e production
でコンパイルしてcompressedかつline_commentsなしのcssを出力する。
他にも色々便利オプションありそうなので、ご存知の方はコメントで殴りに来てください。
あ、自作mixin………いいや次回にしよう。
環境の話
いや技術的なネタはあるんだけどね。小分けにしないと何の記事かわかんなくなるかなと思って。今回は開発環境の話です。似たような境遇の人がいたら訳に立てればいいし、「◯◯使った方がいいよ」とかあるなら突っ込んで欲しいです。
- OS:windows 7
→MBA欲しい
- ターミナル:mintty
→かゆいところに手が届かない。特筆すべきいいとことかない。誰か代わり教えて欲しい
- エディタ:Emacs
→大学で使ってた名残で。会社で前いた部署はvimが圧倒的に多かったり、今いる部署はsublime textが流行ってたりで、どこに行っても肩身が狭い。
- html:html-helper-mode + ZenCoding
→全然カスタマイズしてない。Zenだけに。早いとこ自分テンプレ作りたい。
- css:scss + compass + scss-mode
→言わずもがな。cssの便利記法と、そのプラグイン集みたいなやつ。
- js:js2-mode
→特になし。jQueryからフロントを始めるという温いところから入ったので今必死に生js書いてる。なんかjQuery使ってると風当たり強いので、自分が使うメソッドばっか集めた軽量jQueryもどき作りたい。あとgruntも早く覚えたい
フロント始めたときは開発と言えばlinuxだったから必死にvmで環境作ろうとしてたけど、フロントである以上photoshopとかでadobeの恩恵を受ける必要があったので渋々winでの環境を作った。まぁ早いとこMBA買えばいいんだけど、winにもいいところはあるので(エロゲとかエロゲとかエロゲとか)、なんとか効率的にUbuntuたんで開発できる方法を模索中である
最近作ったものとか、これから作りたいものとか
危ない危ない。あれだけ始めたいってほざいてたブログが危うく記事1個で闇に葬り去られるとこだった。
技術的なことじゃ全然ないけど、とりあえず自分の手がかかって世に出てきたものとか書いとこうと思う。業務外のやつ
- すごいWeb(http://sugoiweb.nezihiko.com/)
佐藤ねじさん(http://nezihiko.com/)からお話頂いてお手伝いしたシリーズ1つ目。デザインが洗練されてるし中身はぶっ飛んでるけど俺がやった実装は普通というなんともオチない感じ。テーマは「すごいすごいって言ってるけどその評価ができない、見た人に評価が委ねられる」だったかな、ねじさんはすごく頭の良い方だから俺には難しかった。
htmlのクラス名とかファイル名に「sugoi_」って接頭辞をつけまくってはいるけど中身で遊べなかったなー。未熟だった頃だし、勿体無い。でも980はてブついてる。すごい。
- NO Tポイント(http://not.nezihiko.com/)
佐藤ねじs(ryシリーズ2つ目。これはすごいWebと違ってネタを盛り込みづらかったからさくっと実装した覚えがある。公開してからの初速は遅かったけど公開日の夕方にGIGAZINEに載って(http://gigazine.net/news/20130121-no-t-mark/)「やばい、怒られちゃう」って言われたのが懐かしい。
以上です。え、以上だよ以上。なんだよ、ねじさんの手伝いしかしてないってか。ごめんなさい。なのでこれからやりたいことも書くよ
- 絵師さんの個人サイトを無償でかっこよくおしゃれにしたい
東方Projectのゲームが好きです。それ以外にもアニメやらゲームが好きで、絵師さんの個人サイトなんかを見に行ってニヤニヤしてるんだが、かっこ良くてオシャレなデザインのサイトは少ない。そりゃイラストレーターなんだから仕方ないと思う。けど自分は絵が描けないからそういう人たちすごいと思うし、もっとお洒落に着飾って活動して、夢を追いかけて欲しいなぁと思うので、もしできることなら「もっとサイトのデザインをよくしたいなぁ」と思ってる人にデザインされたサイトを提供したいと思った。協力してくれるデザイナーさんがいるので、今鋭意企画中である。そのうち宣伝とかしたい。
- 機械学習とか
長くなってきたからざっくり言うと、すももちゃんが欲しいんですよ、ちょびっツの。あと、サイコパスのタコみたいなやつ。わかんなかったらググってください。若しくは察してください。
そろそろ技術的な記事書かないとほんとに中身すっからかんになるな