<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7864262528611545747</id><updated>2024-12-27T16:10:32.919+09:00</updated><category term="プログラム"/><category term="golang"/><category term="Debian"/><category term="Go言語"/><category term="JavaScript"/><category term="PC"/><category term="Android"/><category term="音楽"/><category term="セルスタンバイ"/><category term="Cocos2d-JS"/><category term="HTML"/><category term="CSS"/><category term="Python"/><category term="WebSocket"/><category term="jQuery"/><category term="Flash"/><title type='text'>ブローヴちゃん</title><subtitle type='html'>ヴローブちゃんでもブローブちゃんでもヴローヴちゃんでもなく、ブローヴちゃん。</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://bl.oov.ch/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>111</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-3723637921818178496</id><published>2018-06-20T22:51:00.000+09:00</published><updated>2018-06-20T22:51:16.372+09:00</updated><title type='text'>GV-HUVC を買った</title><content type='html'>&lt;a href=&quot;http://www.iodata.jp/product/av/capture/gv-huvc/index.htm&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;http://www.iodata.jp/product/av/capture/gv-huvc/index.htm&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
USB 3.1 Gen1 接続で、USB Video Class 対応の HDMI キャプチャーデバイス。&lt;br /&gt;
専用のドライバーを必要としないので Windows 以外でも動作し、メーカーからのドライバーや専用ソフトの継続的な更新を必要としないため OS の更新などでデバイスが使えなくなる心配が薄い。&lt;br /&gt;
ただ USB 3.1 Gen1 が実質的に必須でAMD製チップセットなど非対応なものもあるようなので、そのあたりでトラブルが起きる可能性は少なくない。&lt;br /&gt;
&lt;br /&gt;
この手の HDMI キャプチャーデバイスでは近年では&amp;nbsp;AVerMedia 製のデバイスが特にメジャーだと思うが、専用のドライバーやソフトウェアがあるタイプの製品はあまり好きではないので、今回は GV-HUVC を買うことにした。&lt;br /&gt;
AVerMedia の UVC 対応デバイスは&lt;a href=&quot;http://www.4gamer.net/games/234/G023421/20180606058/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;7月に出るらしい&lt;/a&gt;。&lt;br /&gt;
&lt;br /&gt;
ffmpeg でフォーマットを列挙すると以下の通りで、MJPEG には非対応。&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; ffmpeg -list_options true -f dshow -i video=&quot;I-O DATA GV-HUVC, Video Capture USB&quot;:audio=&quot;@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{4CFE616A-B674-474C-B539-907085A96B08}&quot;
ffmpeg version N-91317-gb41b6b3234 Copyright (c) 2000-2018 the FFmpeg developers
  built with gcc 7.3.0 (GCC)
  configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-bzlib --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth
  libavutil      56. 18.102 / 56. 18.102
  libavcodec     58. 20.102 / 58. 20.102
  libavformat    58. 17.100 / 58. 17.100
  libavdevice    58.  4.101 / 58.  4.101
  libavfilter     7. 25.100 /  7. 25.100
  libswscale      5.  2.100 /  5.  2.100
  libswresample   3.  2.100 /  3.  2.100
  libpostproc    55.  2.100 / 55.  2.100
[dshow @ 0000022f7989ac80] DirectShow video device options (from video devices)
[dshow @ 0000022f7989ac80]  Pin &quot;繧ｭ繝｣繝励メ繝｣&quot; (alternative pin name &quot;0&quot;)
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1920x1080 fps=6 max s=1920x1080 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1920x1080 fps=6 max s=1920x1080 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=800x600 fps=6 max s=800x600 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=800x600 fps=6 max s=800x600 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1024x768 fps=6 max s=1024x768 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1024x768 fps=6 max s=1024x768 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1152x864 fps=6 max s=1152x864 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1152x864 fps=6 max s=1152x864 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1280x960 fps=6 max s=1280x960 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1280x960 fps=6 max s=1280x960 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1280x1024 fps=6 max s=1280x1024 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1280x1024 fps=6 max s=1280x1024 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1440x900 fps=6 max s=1440x900 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1440x900 fps=6 max s=1440x900 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1600x900 fps=6 max s=1600x900 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1600x900 fps=6 max s=1600x900 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1600x1200 fps=6 max s=1600x1200 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1600x1200 fps=6 max s=1600x1200 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1680x1050 fps=6 max s=1680x1050 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1680x1050 fps=6 max s=1680x1050 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1280x720 fps=6 max s=1280x720 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1280x720 fps=6 max s=1280x720 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=720x480 fps=6 max s=720x480 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=720x480 fps=6 max s=720x480 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=720x576 fps=6 max s=720x576 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=720x576 fps=6 max s=720x576 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1280x800 fps=6 max s=1280x800 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1280x800 fps=6 max s=1280x800 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1360x768 fps=6 max s=1360x768 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1360x768 fps=6 max s=1360x768 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1366x768 fps=6 max s=1366x768 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1366x768 fps=6 max s=1366x768 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=640x360 fps=6 max s=640x360 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=640x360 fps=6 max s=640x360 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=768x576 fps=6 max s=768x576 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=768x576 fps=6 max s=768x576 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=856x480 fps=6 max s=856x480 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=856x480 fps=6 max s=856x480 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=960x540 fps=6 max s=960x540 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=960x540 fps=6 max s=960x540 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1024x576 fps=6 max s=1024x576 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1024x576 fps=6 max s=1024x576 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1920x1200 fps=6 max s=1920x1200 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=1920x1200 fps=6 max s=1920x1200 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=640x480 fps=6 max s=640x480 fps=60.0002
[dshow @ 0000022f7989ac80]   pixel_format=yuyv422  min s=640x480 fps=6 max s=640x480 fps=60.0002
[dshow @ 0000022f7989ac80] DirectShow audio only device options (from audio devices)
[dshow @ 0000022f7989ac80]  Pin &quot;Capture&quot; (alternative pin name &quot;Capture&quot;)
[dshow @ 0000022f7989ac80]   min ch=1 bits=8 rate= 11025 max ch=2 bits=16 rate= 44100
    Last message repeated 22 times&lt;/code&gt;&lt;/pre&gt;
レイテンシーはそれなりにあるので、アクション性の高いゲームのキャプチャーなどを行う場合には HDMI スプリッターは必須だと思われる。&lt;br /&gt;
キャプチャーした映像には品質的な問題はない。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/3723637921818178496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/3723637921818178496'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2018/06/gv-huvc.html' title='GV-HUVC を買った'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-923019332529879848</id><published>2018-03-04T06:09:00.001+09:00</published><updated>2018-03-04T06:09:24.161+09:00</updated><title type='text'>拡張編集と Shift_JIS</title><content type='html'>PSDToolKit を作っていて「ダメ文字」問題と向き合うことになったので、思い出がてら記録しておく。&lt;br /&gt;
&lt;br /&gt;
拡張編集は多くの箇所で MBCS 文字列を使っていて Lua スクリプトでも Shift_JIS が使われるので、例えば
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;A = &quot;サシスセソ&quot;&lt;/code&gt;&lt;/pre&gt;
みたいなスクリプトはそのまま記述されると上手く動かないことが容易に予想できる。&lt;br /&gt;
&lt;br /&gt;
ところが、実際にはテキストオブジェクトに
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?mes(&quot;サシスセソ&quot;)?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
と書くと上手く動き、
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?dofile(&quot;test.lua&quot;)?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
として、test.lua の中に同じ内容を書くと、こちらは動かない。&lt;br /&gt;
&lt;br /&gt;
内部で何が起きているのかを知るのは簡単で、ダブルクォートの代わりに
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?mes([[サシスセソ]])?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
このようにすると、画面には
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;サシスセソ\&lt;/code&gt;&lt;/pre&gt;
と表示される。&lt;br /&gt;
&lt;br /&gt;
つまり、拡張編集では拡張編集が用意したダイアログ内に入力されたテキストに対しては独自にダメ文字対策を行っていて、その対策方法は恐らく単純に「ダメ文字を探して \ を足す」という形で行っているので、実際にエスケープする必要があるかどうかに関係なくエスケープしようとした結果、画面に「サシスセソ\」と出た。&lt;br /&gt;
&lt;br /&gt;
この方法は変なことをしようとしなければ概ね上手く動くので大抵のケースでは問題にならないし、誤ってエスケープされたものも余分な \ を消せば復元できるので大きな問題にはならないはずだったが、PSDToolKit の特殊な事情により厄介な問題に発展した。&lt;br /&gt;
&lt;br /&gt;
やりたかったことは、文字列をクリップボードにコピーしておいて、拡張編集のダイアログや *.lua ファイル内に貼り付けて使う、というだけの処理だった。&lt;br /&gt;
&lt;br /&gt;
何が問題なのかというと、コピーした時点ではどちらに貼り付けようとしているのかがわからないので、貼り付けられた場所次第で得られる文字列が変わってしまう。&lt;br /&gt;
最初は「こっちからの場合は \ を消して……」みたいな場当たり的な措置でお茶を濁そうとしたのだが、置換処理が結構な回数必要になるし、気をつけて扱わないとすぐ事故る勢いだったのでこれはまずいと思ってアプローチを変えることにした。&lt;br /&gt;
&lt;br /&gt;
対策は単純に「ダメ文字が出てこないようにする」というだけ。&lt;br /&gt;
&lt;br /&gt;
処理の都合上、元々文字列では Unicode の範囲全てを扱える必要がありエスケープ処理を行うロジックを作っていたので、それにダメ文字用の対策も加えることによって事なきを得た。&lt;br /&gt;
&lt;br /&gt;
上手く動くようになってよかったなあ。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/923019332529879848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/923019332529879848'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2018/03/shiftjis.html' title='拡張編集と Shift_JIS'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-3942299840625435102</id><published>2017-07-11T00:11:00.000+09:00</published><updated>2018-01-11T01:49:35.748+09:00</updated><title type='text'>「CeVIO Creative Studio」のフォント</title><content type='html'>&lt;a href=&quot;http://cevio.jp/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;CeVIO Creative Studio&lt;/a&gt; のパッケージやサイトなど、様々な場所で使われている&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTXnPbmjPh5gBfWaDkrmeHiittPEedG9hPLdA_2hjXNfjKg4TXQap45RK-nM7RNoyNrRLkcH34mFA9U-lLWW9GQ2Luck_KGSK04JMQRV7Dvs5gOSjft601nP1KDabJwRQi7ubDSvsqw0s/s1600/couiu_canjino_logo.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;369&quot; data-original-width=&quot;468&quot; height=&quot;252&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTXnPbmjPh5gBfWaDkrmeHiittPEedG9hPLdA_2hjXNfjKg4TXQap45RK-nM7RNoyNrRLkcH34mFA9U-lLWW9GQ2Luck_KGSK04JMQRV7Dvs5gOSjft601nP1KDabJwRQi7ubDSvsqw0s/s320/couiu_canjino_logo.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;こういう感じのロゴ&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
このロゴ、今までちゃんと見ずに漠然と「futura」辺りのフォントを使っていると勝手に思っていたんだけど、真面目に見てみると i の上の点が丸くないし、C の形すら違う。&lt;br /&gt;
&lt;br /&gt;
何だろうと思って気になって調べてみたら、これは「URW Gothic」というフォントだった。&lt;br /&gt;
（※追記：系譜も含めて考えると実際に使われているのはもしかしたら ITC Avant Garde Gothic かもしれないけど、自分には全然区別がつかなかった）&lt;br /&gt;
&lt;br /&gt;
このフォントは &lt;a href=&quot;https://www.ghostscript.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;ghostscript&lt;/a&gt; の &lt;a href=&quot;http://git.ghostscript.com/?p=urw-core35-fonts.git;a=tree&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;urw-core35-fonts.git&lt;/a&gt; という git リポジトリに登録されているのだが、ライセンスは特例付きの AGPLv3 となっており、ライセンスの範囲内で自由に使うことができる。&lt;br /&gt;
「URWGothic-Book.otf」や「URWGothic-Book.ttf」が目的のフォントなので、興味があればお試しください。&lt;br /&gt;
&lt;br /&gt;
追記:&lt;br /&gt;
もう１つ、&lt;a href=&quot;http://cevio.jp/product/ccs/index.html&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;このページの左上&lt;/a&gt;にある Project CeVIO の部分は r とか c の形状から察するに&amp;nbsp;&lt;a href=&quot;https://typekit.com/fonts/handel-gothic&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Handel Gothic&lt;/a&gt; をベースに o や e を自作したり、j を J をベースに差し替えたり、CeVIO の部分は横長にした上で太さが極端にならないように修正したものじゃないだろうか、という感じで疑っているんだけど、もし仮にそうだとすると同じフォントを使ってもすぐに同じものを再現できなくて面白くない感じに。&lt;br /&gt;
&lt;br /&gt;
追記 2018-01-11:&lt;br /&gt;
&lt;a href=&quot;https://twitter.com/oovch/status/949978171394310145&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;jamバンドについても調べた&lt;/a&gt;。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/3942299840625435102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/3942299840625435102'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2017/07/cevio-creative-studio.html' title='「CeVIO Creative Studio」のフォント'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTXnPbmjPh5gBfWaDkrmeHiittPEedG9hPLdA_2hjXNfjKg4TXQap45RK-nM7RNoyNrRLkcH34mFA9U-lLWW9GQ2Luck_KGSK04JMQRV7Dvs5gOSjft601nP1KDabJwRQi7ubDSvsqw0s/s72-c/couiu_canjino_logo.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-8554026963500930300</id><published>2016-08-29T07:21:00.001+09:00</published><updated>2020-06-15T06:42:17.649+09:00</updated><title type='text'>［絵を描く人向け］PSDTool の使い方</title><content type='html'>&lt;a href=&quot;http://www.nicovideo.jp/watch/sm28712861&quot; target=&quot;_blank&quot;&gt;先日投稿した動画&lt;/a&gt;は主に「公開されている PSD ファイルを使う人」にスポットを当てたものだったので、今回は「PSD ファイルを公開する人」に向けた &lt;a href=&quot;https://oov.github.io/psdtool/&quot; target=&quot;_blank&quot;&gt;PSDTool&lt;/a&gt; の使い方をブログで紹介しようと思います。&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
ターゲットは絵を公開するために PSD ファイルを扱ったことがあるが、動画制作には詳しくない人です。&lt;br /&gt;
動画制作においては使うツールや設定、内容などによって最適な画像の用意方法が変わってきますが、PSD ファイルの時点で無難な状態にしておくと色々なケースに対応しやすいので、この文書ではベストオブ無難を目指します。&lt;br /&gt;
&lt;br /&gt;
この文書で PSDTool が単なる PSD ファイル表示ツールではないことを少しでもお伝えできれば幸いです。&lt;br /&gt;
ちなみに、全体的にはそんなに目新しい情報はありません。&lt;br /&gt;
&lt;br /&gt;
なお、この文書では解説のため&lt;a href=&quot;http://stringliteral.web.fc2.com/&quot; target=&quot;_blank&quot;&gt;零度の青空 air&lt;/a&gt; さんの「立ち絵 2」をお借りしています。&lt;br /&gt;
&lt;h2&gt;
&lt;b&gt;[はじめに] なぜ PSDTool を使うと嬉しいのか&lt;/b&gt;&lt;/h2&gt;
ざっくりと言えば、恐らくこの辺が PSDTool を使うべき大きな理由だと思います。&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;表情などのパーツの切り替えがしやすいから&lt;/li&gt;
&lt;li&gt;PNG ファイルで保存しやすいから&lt;/li&gt;
&lt;li&gt;ブラウザだけで PSD ファイルをいい感じに読み込めるから&lt;/li&gt;
&lt;/ol&gt;
この文書ではこれらのメリットを最大限に活用するためのテクニックみたいなものを紹介していきます。&lt;br /&gt;
&lt;h2&gt;
目次&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#psdtool-psd&quot;&gt;表情などのパーツを切り替えしやすくするコツ&lt;/a&gt;&lt;br /&gt;PSDTool で扱いやすい PSD ファイルを作る方法&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#psdtool-png&quot;&gt;PNG ファイルで保存するコツ&lt;/a&gt;&lt;br /&gt;効率よく PNG ファイルをたくさん作る方法&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#psdtool-load&quot;&gt;ブラウザで PSD ファイルを読み込むコツ&lt;/a&gt;&lt;br /&gt;ダウンロードせず PSD ファイルを開く方法&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;psdtool-psd&quot;&gt;
1. 表情などのパーツの切り替えをしやすくするコツ&lt;/h2&gt;
立ち絵の場合、色々な表情が必要になることが多いと思います。&lt;br /&gt;
顔全体とか、眉毛、目、口などのパーツ毎とか、やり方は様々ですが、色々な表情パターンのレイヤーを PSD ファイルに入れることになると思います。&lt;br /&gt;
&lt;br /&gt;
以下の画像は単純にレイヤーを並べた時の例と、これを PSDTool で開いた時のスクリーンショットです。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGLocDjFOtyeZGRS3g5fbQuN2QnAkr_bi_Zi-jciNXLnES429JBh2n40RG2j6WstBcIa3PRso6WhZBnwa9fdhaHIZs0u30P53JMtr08te03oHYwcCzTcbOyVjzqr-r57hwlg6A-nGmdXk/s1600/screen1.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;186&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGLocDjFOtyeZGRS3g5fbQuN2QnAkr_bi_Zi-jciNXLnES429JBh2n40RG2j6WstBcIa3PRso6WhZBnwa9fdhaHIZs0u30P53JMtr08te03oHYwcCzTcbOyVjzqr-r57hwlg6A-nGmdXk/s400/screen1.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;そのまま縦に並べているので、PSDTool でもレイヤーは縦に並ぶ&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
PSDTool にはレイヤー構造で厳守すべきルールは存在しないため、基本的にはこのままでも読み込めます。&lt;br /&gt;
しかし PSDTool で使いやすい形に整理することはできるので、その方法を紹介していきます。&lt;br /&gt;
&lt;h4&gt;
1.1 ［準備編］PSDTool で読み込めるのはどんな PSD ファイル？&lt;/h4&gt;
PSDTool は基本的な機能のみを使っている PSD ファイルと PSB ファイル全般が読み込めます。&lt;br /&gt;
Photoshop でしか使えないような機能の大半には対応していません。&lt;br /&gt;
カラーモードは RGB カラーのみ対応しているので、CMYK カラーのものなどは読み込めません。&lt;br /&gt;
&lt;br /&gt;
機能としては基本的なレイヤー合成と、クリッピングマスクと、レイヤーマスクが使えます。&lt;br /&gt;
レイヤー合成とクリッピングマスクだけなら FireAlpaca で読めて、レイヤー合成とレイヤーマスクだけなら GIMP で読めるので、使う機能をこの辺だけにしておくとフリーウェアで扱う時も安心です。&lt;br /&gt;
&lt;br /&gt;
ただし、次に紹介する内容でも触れますが、PSDTool はフォルダーレイヤーの使用を前提としています。&lt;br /&gt;
そのため&lt;span style=&quot;color: red;&quot;&gt;フォルダーレイヤーを作れないソフトから PSD ファイルを書き出すことは非推奨&lt;/span&gt;です。&lt;br /&gt;
（書き出した PSD ファイルが使えないわけではなく、便利な機能が使えないからです）&lt;br /&gt;
&lt;br /&gt;
オススメは Windows と Mac の両方に対応していて動作が軽快な &lt;a href=&quot;http://firealpaca.com/&quot; target=&quot;_blank&quot;&gt;FireAlpaca&lt;/a&gt; です。
&lt;br /&gt;
&lt;h4&gt;
1.2 レイヤーをフォルダーで分けよう&lt;/h4&gt;
まずはお絵かきソフトでフォルダーレイヤーを使って、項目ごとに整理してみましょう。&lt;br /&gt;
整理して保存した PSD ファイルを PSDTool で読み込んだのが、以下のスクリーンショットです。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUCBXL8EgxMa5XGWLb6M21WenlTzOY810n83lTYGTtO69PUz_EfQ6zkbzvmWX-rbTLc-fL5_zabtjVV9cmZB_QtohO9yp2cjHs3tmhWxes08e6zqjpEgw8beV2wr-CYcfZrOndQUR4Fp4/s1600/screen2.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;185&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUCBXL8EgxMa5XGWLb6M21WenlTzOY810n83lTYGTtO69PUz_EfQ6zkbzvmWX-rbTLc-fL5_zabtjVV9cmZB_QtohO9yp2cjHs3tmhWxes08e6zqjpEgw8beV2wr-CYcfZrOndQUR4Fp4/s400/screen2.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;フォルダーに入れたレイヤーは、PSDTool 上でもフォルダーの中にある&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
このように PSD ファイル側でレイヤーをフォルダーに入れれば、PSDTool でもフォルダーに入ります。&lt;br /&gt;
これは見た目を整理するだけでなく、次に紹介する機能を使うための重要なステップです。&lt;br /&gt;
&lt;h4&gt;
1.3 同時に表示しないレイヤーの切り替えをもっと使いやすくしよう&lt;/h4&gt;
フォルダーに入れたレイヤーをよく見てみると、恐らく「くち_1」「くち_2」「くち_3」「くち_4」「くち_5」を同時に表示したい事はほとんどありません。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYF7hu_XPQNIN8HEau5cdlWkusbj34cDxeVmRY76UCG0Pwh86xbF_CrH1slm2uMfLtk7aA9O4e23YSHhqlzQJGz5MGtYP0YF4TtrMyiCbBb2sP05G4oa2GnXnJP1Hnfproo-vycDLCJN8/s1600/screen3.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;185&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYF7hu_XPQNIN8HEau5cdlWkusbj34cDxeVmRY76UCG0Pwh86xbF_CrH1slm2uMfLtk7aA9O4e23YSHhqlzQJGz5MGtYP0YF4TtrMyiCbBb2sP05G4oa2GnXnJP1Hnfproo-vycDLCJN8/s400/screen3.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;全部にチェックが入ると表示がぐちゃぐちゃになっちゃうよね&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
こういう「同時に表示したくない」または「切り替えるように表示したい」というレイヤーは、&lt;a href=&quot;https://oov.github.io/psdtool/manual.html#original-feature-asterisk&quot; target=&quot;_blank&quot;&gt;PSDTool の独自拡張機能&lt;/a&gt;を使う絶好の機会です。&lt;br /&gt;
使い方は、&lt;span style=&quot;color: red;&quot;&gt;単にレイヤーの名前の先頭に半角の「*」を付けるだけ&lt;/span&gt;です。&lt;br /&gt;
&lt;br /&gt;
レイヤーの名前を変更して PSDTool で読み込んだのが、以下のスクリーンショットです。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCPT7XyuSBQQ4hffHoc27viHJdQis_2uIt1SGcukYBwg0_zP3bN14EwKsvAbf0-7adJNjFt5tlrA-VCkGGEuPObPaxmYJVm_lOr9XRMPLWJ5_VZ0gMTMtPKWCIpzjy3ezcJ1Y88rWtnlw/s1600/screen4.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;185&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCPT7XyuSBQQ4hffHoc27viHJdQis_2uIt1SGcukYBwg0_zP3bN14EwKsvAbf0-7adJNjFt5tlrA-VCkGGEuPObPaxmYJVm_lOr9XRMPLWJ5_VZ0gMTMtPKWCIpzjy3ezcJ1Y88rWtnlw/s400/screen4.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;チェックボックスじゃないから、１クリックで表示の切り替えができる&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
半角の「*」を付けるだけで、PSDTool ではチェックボックスがラジオボタンに変わります。&lt;br /&gt;
ラジオボタンにすると同時にレイヤーが表示されないだけではなく、今までは「くち_1を非表示に」→「くち_2を表示に」と&lt;span style=&quot;color: red;&quot;&gt;2回クリックしないと切り替えられなかったのが、１回のクリックでできるように&lt;/span&gt;なりました。&lt;br /&gt;
しかも&lt;span style=&quot;color: red;&quot;&gt;キーボードの上下キーで切り替えることもできます&lt;/span&gt;。&lt;br /&gt;
&lt;br /&gt;
なお、レイヤーの名前を変えただけなので、これで他のソフトで読み込めなくなることはありません。&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;補足ですが、ラジオボタンはフォルダー単位でグループ化されているので、例えば右目と左目でそれぞれ１つずつ選択したい時は「右目」「左目」でそれぞれフォルダーを作って、パーツをその中に入れてください。&lt;/span&gt;&lt;br /&gt;
&lt;h4&gt;
1.4 消す必要がないレイヤーやフォルダーはチェックボックスを隠そう&lt;/h4&gt;
多くの場合、眉毛、目、口などは、パターンの中からどれか一つを&lt;span style=&quot;color: red;&quot;&gt;必ず表示&lt;/span&gt;します。&lt;br /&gt;
（もちろんコミカルな表情など、消すことで味が出るパターンもあると思いますが）&lt;br /&gt;
&lt;br /&gt;
現時点では PSDTool の画面でフォルダーレイヤーにはチェックボックスがあり、このチェックを外すと子階層のパーツを消せるため、目や口がない状態にもできます。&lt;br /&gt;
これを試しにチェックボックス自体を隠すことで消せなくしてみましょう。&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;レイヤーの名前の先頭に半角の「!」を付ける&lt;/span&gt;と&lt;a href=&quot;https://oov.github.io/psdtool/manual.html#original-feature-exclamation&quot; target=&quot;_blank&quot;&gt;PSDTool の独自拡張機能&lt;/a&gt;でチェックボックスがなくなります。&lt;br /&gt;
実際に「!」を付けた PSD ファイルを PSDTool で表示したのが、以下のスクリーンショットです。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3pZwqt5aUWF1Z27qsbikm3MAqZpw10gqJ6IyC7JzLpS6WD66UJhqISs49Z1HSKM4DLeIk7wWpgNKtMSt0iYm7oacr1NBxy6tkuHZ9dZHvflygV7jxkunkVcHwsWV5iihDOKNZx8-aRGc/s1600/screen5.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;148&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3pZwqt5aUWF1Z27qsbikm3MAqZpw10gqJ6IyC7JzLpS6WD66UJhqISs49Z1HSKM4DLeIk7wWpgNKtMSt0iYm7oacr1NBxy6tkuHZ9dZHvflygV7jxkunkVcHwsWV5iihDOKNZx8-aRGc/s320/screen5.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;フォルダーからチェックボックスが消えました&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
触る必要がないチェックボックスを隠せたので、変更できる項目が減り画面がシンプルになりました。&lt;br /&gt;
ちなみにこの機能はフォルダーだけではなく、レイヤーで使うこともできます。&lt;br /&gt;
&lt;br /&gt;
これもレイヤーの名前を変えただけなので、PSD ファイルを他のソフトで開く時には影響はありません。&lt;br /&gt;
&lt;h4&gt;
1.5 「表情などのパーツの切り替えをしやすくするコツ」のまとめ&lt;/h4&gt;
これで読み込んだ PSD ファイルの表情を変えるのが簡単になりました。&lt;br /&gt;
しかもこれをやっておけば、「シンプルビュー」を作るのもとても簡単になります。&lt;br /&gt;
&lt;br /&gt;
この他にも、例えば&lt;a href=&quot;https://oov.github.io/psdtool/manual.html#original-feature-flip&quot; target=&quot;_blank&quot;&gt;左右反転などをした時だけレイヤーを自動で切り替える機能&lt;/a&gt;もあります。&lt;br /&gt;
普通、画像を左右反転するとテキストやアクセサリー、髪型なども逆向きになってしまいますが、予め逆向きの時に表示するためのレイヤーを準備しておけば、左右反転時に自動的に逆向き用レイヤーになります。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://oov.github.io/psdtool/#load:https%3A%2F%2Foov.github.io%2Fpsdtool%2Fimg%2Fim5467479.psd&quot; target=&quot;_blank&quot;&gt;PSDTool のトップページにあるサンプル&lt;/a&gt;は、左右反転すると髪の結び目が自動で切り替わります。&lt;br /&gt;
&lt;a href=&quot;https://oov.github.io/psdtool/&quot; target=&quot;_blank&quot;&gt;トップページ&lt;/a&gt;から PSD ファイルもダウンロードできるので、使い方の参考にして下さい。&lt;br /&gt;
その他、ニコニコ静画にも左右反転に対応した立ち絵が投稿されています。すごい。&lt;br /&gt;
&lt;br /&gt;
&lt;iframe frameborder=&quot;0&quot; height=&quot;176&quot; scrolling=&quot;no&quot; src=&quot;https://ext.seiga.nicovideo.jp/thumb/im5463692&quot; style=&quot;border: solid 1px #888;&quot; width=&quot;312&quot;&gt;&lt;a href=&quot;https://seiga.nicovideo.jp/seiga/im5463692&quot;&gt;結月ゆかり立ち絵素材&lt;/a&gt;&lt;/iframe&gt;&lt;iframe frameborder=&quot;0&quot; height=&quot;176&quot; scrolling=&quot;no&quot; src=&quot;https://ext.seiga.nicovideo.jp/thumb/im5463697&quot; style=&quot;border: solid 1px #888;&quot; width=&quot;312&quot;&gt;&lt;a href=&quot;https://seiga.nicovideo.jp/seiga/im5463697&quot;&gt;弦巻マキ立ち絵素材&lt;/a&gt;&lt;/iframe&gt;&lt;iframe frameborder=&quot;0&quot; height=&quot;176&quot; scrolling=&quot;no&quot; src=&quot;https://ext.seiga.nicovideo.jp/thumb/im5696905&quot; style=&quot;border: solid 1px #888;&quot; width=&quot;312&quot;&gt;&lt;a href=&quot;https://seiga.nicovideo.jp/seiga/im5696905&quot;&gt;東北ずん子立ち絵素材&lt;/a&gt;&lt;/iframe&gt;&lt;iframe frameborder=&quot;0&quot; height=&quot;176&quot; scrolling=&quot;no&quot; src=&quot;https://ext.seiga.nicovideo.jp/thumb/im6009953&quot; style=&quot;border: solid 1px #888;&quot; width=&quot;312&quot;&gt;&lt;a href=&quot;https://seiga.nicovideo.jp/seiga/im6009953&quot;&gt;さとうささら立ち絵素材&lt;/a&gt;&lt;/iframe&gt;&lt;iframe frameborder=&quot;0&quot; height=&quot;176&quot; scrolling=&quot;no&quot; src=&quot;https://ext.seiga.nicovideo.jp/thumb/im6007806&quot; style=&quot;border: solid 1px #888;&quot; width=&quot;312&quot;&gt;&lt;a href=&quot;https://seiga.nicovideo.jp/seiga/im6007806&quot;&gt;【立ち絵】CeVIOのささらさん&lt;/a&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
なお左右反転以外なら&lt;a href=&quot;https://seiga.nicovideo.jp/clip/1704637&quot; target=&quot;_blank&quot;&gt;ニコニコ静画には PSDTool の「*」「!」を使用していただいているイラストがたくさんあります&lt;/a&gt;ので、実際に作る上でレイヤー構造の参考になると思います。&lt;br /&gt;
&lt;h4&gt;
1.番外編 依存関係があるレイヤー切り替えはどうする？&lt;/h4&gt;
例えばポーズと服がそれぞれ2種類ずつある場合、上で紹介した手順に従うと多分こうなります。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix0Enl1yE8K0A1xqhmg1DH8GACdNKBHE4fGQkBnTdvbrOpy2TCcXDSOXsZBEJ6sD5tY9ftVK9TpfcvKvgXnKP_RRQNiSu9wOAiyYZj1Vf9RADvlrV61qlX6x9UKD4bTeNdT_I0qwJS-0I/s1600/screen8.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix0Enl1yE8K0A1xqhmg1DH8GACdNKBHE4fGQkBnTdvbrOpy2TCcXDSOXsZBEJ6sD5tY9ftVK9TpfcvKvgXnKP_RRQNiSu9wOAiyYZj1Vf9RADvlrV61qlX6x9UKD4bTeNdT_I0qwJS-0I/s1600/screen8.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;ポーズのパターン数だけ服装にもパターンが必要になるから……&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
これでもそこそこ使いやすいとは思いますが、「体が『ポーズ1』で、服装が『制服(ポーズ2)』」みたいに、体と服で別のポーズを選ぶこともできてしまいます。&lt;br /&gt;
こういう時は、ポーズ毎でまとめると変な組み合わせになるのを避けることもできます。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3O3p_0YkLx-5HBPy_v7WSKin2iUK6TS_zuSH9Upv_DXebO73G1Y7OYAKd81pUWMn5rwj_aECQ6BJzhpo8CarPgzvusKIxrtrKxerRQTY70UNAWfHMO-XyN4yRN3X0uU9yumKjyNz6wjY/s1600/screen9.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3O3p_0YkLx-5HBPy_v7WSKin2iUK6TS_zuSH9Upv_DXebO73G1Y7OYAKd81pUWMn5rwj_aECQ6BJzhpo8CarPgzvusKIxrtrKxerRQTY70UNAWfHMO-XyN4yRN3X0uU9yumKjyNz6wjY/s1600/screen9.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;ポーズが異なる組み合わせが避けられるようになった&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
パズルみたいな感じですが、工夫によって変な組み合わせを避けられました。&lt;br /&gt;
変な組み合わせが作れないようにすると「一瞬合ってるように見えるけど、よく見るとキャミソールの肩紐が浮いてる」みたいな見落としがちなミスを回避することができます。&lt;br /&gt;
&lt;br /&gt;
ちなみに、もしここから更に服を脱がせられるようにしたい場合、「*制服」「*私服」を「服」フォルダーを作って入れるか、「*服なし」という空白レイヤーを服装の選択肢として追加するといいと思います。&lt;br /&gt;
&lt;br /&gt;
もうひとつ複雑なパターンも考えてみます。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsPDgDUVukVKCaNKjwmk8ZTAshhrnrF_hNCuaFs8u9x_hl9tiArSyf7-gQLLdH0TMWEa9N_5i02ddv-RSRNMkSDx1hhazV_3gax35ARRACk88Q4jlQu1eHwRhbJm_qVBGjGbfMvOVNX-A/s1600/screen10.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsPDgDUVukVKCaNKjwmk8ZTAshhrnrF_hNCuaFs8u9x_hl9tiArSyf7-gQLLdH0TMWEa9N_5i02ddv-RSRNMkSDx1hhazV_3gax35ARRACk88Q4jlQu1eHwRhbJm_qVBGjGbfMvOVNX-A/s320/screen10.png&quot; width=&quot;84&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;ブログに載せるには縦長過ぎる…&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
右腕と左腕がそれぞれ「上げ」と「下げ」で選べるパターンです。&lt;br /&gt;
これは例えば腕ごとにまとめようとすると重ね合わせ順が変わってしまったりするので、現状の PSDTool でこれ以上エレガントに表現するのは結構難しいと思います。&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: #999999; font-size: xx-small;&quot;&gt;（一応この辺を補助するための機能もアイディアとしては考えているものの、機能が必要になる状況の複雑さ、扱いの難しさ、機能を使うユーザーの少なさなどを鑑みて今のところ実装を保留中……）&lt;/span&gt;
&lt;br /&gt;
&lt;br /&gt;
とはいえ、これは「!」を活用しているので PSDTool で見ると案外そんなに複雑ではありません。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgelgJqI0imD3jQIJBg7mDLmM9uS5hq4-wYfIWg3inR2btS95R-PpED-LUTqCW63CJfUJn-9DrdvVHTJSTtUp221J3ZxQi5jKPjXet9X7aNIc5JqsqusMlQUYGal18DuDGvcEcnr-RtgCQ/s1600/screen11.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgelgJqI0imD3jQIJBg7mDLmM9uS5hq4-wYfIWg3inR2btS95R-PpED-LUTqCW63CJfUJn-9DrdvVHTJSTtUp221J3ZxQi5jKPjXet9X7aNIc5JqsqusMlQUYGal18DuDGvcEcnr-RtgCQ/s320/screen11.png&quot; width=&quot;66&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;クリックできる場所は少なめ&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
その他ゴリ押しに近いアプローチとしては、この場合それぞれの服装の「両腕下げ」「左腕上げ」「右腕上げ」「両腕上げ」が上手く用意できればいいだけなので、それぞれのパターンに合わせたお気に入りを作って、エクスポートしたお気に入りデータを PSD ファイルと一緒に配布するという手段もあります。&lt;br /&gt;
つまりお気に入りを読み込んでもらうことで、破綻した組み合わせを避けようというアイディアです。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7sTHfTNGxfz1gKKvvWCZQMge95D0nJTY_vp_L-UpJI8KRmIpSKSWqNllTaGDudY__oKmEfJQiOoDjE-BPRHbhUzMq_bqTEQWeim1e0Yxr-kwiG-u5QjTi40ccSaH0pTZ1T66CF5p47y0/s1600/screen11.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7sTHfTNGxfz1gKKvvWCZQMge95D0nJTY_vp_L-UpJI8KRmIpSKSWqNllTaGDudY__oKmEfJQiOoDjE-BPRHbhUzMq_bqTEQWeim1e0Yxr-kwiG-u5QjTi40ccSaH0pTZ1T66CF5p47y0/s320/screen11.png&quot; width=&quot;216&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;全パターンのお気に入りを作って、フィルターで服装と腕関係にチェックをつけておく&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
このようにフィルター機能を使って影響範囲を絞っておけば、&lt;span style=&quot;color: red;&quot;&gt;表情を変えずに服装とポーズだけをまとめて一発切り替え&lt;/span&gt;という使い方ができます。&lt;br /&gt;
フィルターの使い方については下の方でも少し紹介しています。&lt;br /&gt;
&lt;h2 id=&quot;psdtool-png&quot;&gt;
2. PNG ファイルで保存するコツ&lt;/h2&gt;
「PNG ファイルで保存しやすくするツールなのに保存にコツがあるのか？」と思うかもしれません。&lt;br /&gt;
意外とあります、より効率的になるヤツが。&lt;br /&gt;
&lt;h4&gt;
2.1 画像を１枚１枚手動で保存しない&lt;/h4&gt;
PSDTool では画面上部にある「.png」というボタンを押すと画像を保存できますが、基本的には&lt;a href=&quot;http://www.nicovideo.jp/watch/sm28712861&quot; target=&quot;_blank&quot;&gt;動画&lt;/a&gt;で紹介しているように、&lt;span style=&quot;color: red;&quot;&gt;１枚１枚ボタンを押して保存するより、お気に入りに登録してエクスポートする方が効率的&lt;/span&gt;です。&lt;br /&gt;
&lt;br /&gt;
これなら左右反転バージョンを改めて保存しなおしたり、縮小率を変えたバージョンも一斉に保存できるし、ファイル名はブックマークにつけた名前で保存されるので、いちいちリネームする必要もありません。&lt;br /&gt;
&lt;br /&gt;
特に左右反転で専用のレイヤーに切り替わる場合、外部ツールの左右反転では正しい結果が得られません。&lt;br /&gt;
&lt;h4&gt;
2.2 フィルターを活用する&lt;/h4&gt;
フィルターは&lt;a href=&quot;http://www.nicovideo.jp/watch/sm28712861&quot; target=&quot;_blank&quot;&gt;動画&lt;/a&gt;ではシンプルビュー用の機能みたいな扱いでしたが、実際には単独でも使える機能です。
&lt;br /&gt;
以下は&amp;nbsp;&lt;a href=&quot;https://oov.github.io/psdtool/manual.html&quot; target=&quot;_blank&quot;&gt;PSDTool のマニュアル&lt;/a&gt;で使われているフィルター機能の紹介動画です。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; class=&quot;YOUTUBE-iframe-video&quot; data-thumbnail-src=&quot;https://i.ytimg.com/vi/-L_IzlyGxNM/0.jpg&quot; frameborder=&quot;0&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/-L_IzlyGxNM?feature=player_embedded&quot; width=&quot;560&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;br /&gt;
このように、お気に入りを読み込んだ時の適用範囲を、フィルターで絞ることができます。&lt;br /&gt;
&lt;br /&gt;
この動画のように口の様々なパターンのお気に入りをフィルター越しに用意しておくと、例えば「レイヤータブで目を変更」→「お気に入りをエクスポート」という手順を繰り返すことで色々な目のパターンで口の様々なパターンを書き出すことができます。&lt;br /&gt;
（しかし、もっと大量に書き出したい場合は後述する方法でもっと適したものがあります）&lt;br /&gt;
&lt;h4&gt;
2.3 シンプルビューを活用する&lt;/h4&gt;
シンプルビューを使うと表情の切り替えなどが便利になりますが、たくさんの PNG ファイルを作る時にも使える便利な機能でもあります。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; class=&quot;YOUTUBE-iframe-video&quot; data-thumbnail-src=&quot;https://i.ytimg.com/vi/F1qPRdpZHd4/0.jpg&quot; frameborder=&quot;0&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/F1qPRdpZHd4?feature=player_embedded&quot; width=&quot;560&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;br /&gt;
「表情などのパーツの切り替えをしやすくするコツ」の内容に従ってレイヤーの名前に「*」とか「!」をつけておけば、この動画のように&lt;span style=&quot;color: red;&quot;&gt;シンプルビューは1分未満で準備できます&lt;/span&gt;。&lt;br /&gt;
&lt;br /&gt;
シンプルビューには&lt;a href=&quot;http://www.nicovideo.jp/watch/sm28712861&quot; target=&quot;_blank&quot;&gt;動画&lt;/a&gt;で紹介したように「パーツ毎に PNG ファイルを書き出す」といった使い方の他に、&lt;span style=&quot;color: red;&quot;&gt;シンプルビューで表現可能な全てのパターンを PNG で書き出す&lt;/span&gt;という過激な使い方があります（2016-05-01 実装）。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjE8gAz-kNlv4oT51_-pexWr7S66zgvHo18-JHJAqO8q2_6RXvtFdGBSdwiFvA0O5SkXn1GgdgzKvDUis0lSx8dhgLOyZhFJz3XD0mCULf9nKXMbHAw7wnQA6nlNIOyxueoJ2lisGCLoa4/s1600/screen6.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjE8gAz-kNlv4oT51_-pexWr7S66zgvHo18-JHJAqO8q2_6RXvtFdGBSdwiFvA0O5SkXn1GgdgzKvDUis0lSx8dhgLOyZhFJz3XD0mCULf9nKXMbHAw7wnQA6nlNIOyxueoJ2lisGCLoa4/s400/screen6.png&quot; width=&quot;391&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;使うにはシンプルビューが使える状態でこれを選ぶだけ&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
これを使うと存在し得る全てのパターンを書き出します。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJvgEfuqApDibMw6lKQGz9q9vusqCaR03Oj7IteUNb8k02euZKj4gf9bBS6GLygkEk1uHdxCKdAQaY04T2SKkCsay8eFyeJ05Kv2nsjkwGG6qUnUQcuUAkO6OogF2tRjXUGv7lKrx-CYQ/s1600/screen7.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJvgEfuqApDibMw6lKQGz9q9vusqCaR03Oj7IteUNb8k02euZKj4gf9bBS6GLygkEk1uHdxCKdAQaY04T2SKkCsay8eFyeJ05Kv2nsjkwGG6qUnUQcuUAkO6OogF2tRjXUGv7lKrx-CYQ/s400/screen7.png&quot; width=&quot;303&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;こんな感じで、合成済みパターンが大量に書き出されます（今回は250パターン）&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
しかし、&lt;span style=&quot;color: red;&quot;&gt;パーツ数が多いと総容量が数GBレベル、書き出し所要時間も1時間を超えるスケールに割と簡単に発展&lt;/span&gt;します。5GB 程度までは動作確認しましたが、そんなに凄いパターン数を書き出しても使う側も大変です。&lt;br /&gt;
そういう時は必要に応じてシンプルビューで選べる数を減らして、現実的な数に調整しましょう。&lt;br /&gt;
例えば20分程度で終わるセットを数回に分けて書き出すとか、中断しやすい感じでやると安全です。&lt;br /&gt;
&lt;h4&gt;
2.4 「PNG ファイルで保存するコツ」のまとめ&lt;/h4&gt;
PNG ファイルを作る色々なアプローチを紹介しましたが、全パターン網羅した PNG ファイルは大抵容量が膨らみすぎるので程々にしておくのが良さそうです。&lt;br /&gt;
&lt;br /&gt;
ちなみに PSDTool は
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;絵を描かない人でも PSD ファイルが扱いやすくなるように&lt;/li&gt;
&lt;li&gt;絵を描く人が頑張って PNG ファイル化しなくて済むように&lt;/li&gt;
&lt;/ul&gt;
という両側面から労力を削減するために作っています。&lt;br /&gt;
PNG ファイルを作る作業は絵を描かない人でもできますが、あなたの絵はあなたにしか描くことができないので、絵を描くことに時間を割いたほうが建設的だろうというコンセプトに基いて制作されています。
&lt;br /&gt;
&lt;h2 id=&quot;psdtool-load&quot;&gt;
3.ブラウザで PSD ファイルを読み込むコツ&lt;/h2&gt;
&lt;h4&gt;
3.1 ダイレクト読み込み機能を試そう&lt;/h4&gt;
自身のサイトで PSD ファイルを配布しているなら、&lt;a href=&quot;https://oov.github.io/psdtool/manual.html#supported-filetype-direct&quot; target=&quot;_blank&quot;&gt;ダイレクト読み込み機能&lt;/a&gt;も使えるかもしれません。&lt;br /&gt;
ダイレクト読み込み機能とは、要するにこういうのです。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://goo.gl/1RxjAz&quot; target=&quot;_blank&quot;&gt;http://goo.gl/1RxjAz&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
リンクをクリックすると、特にダウンロードなどしたことがないファイルを開いた状態になります。&lt;br /&gt;
&lt;br /&gt;
ダイレクト読み込み機能の準備方法は&lt;a href=&quot;https://oov.github.io/psdtool/manual.html#supported-filetype-direct&quot; target=&quot;_blank&quot;&gt;マニュアル&lt;/a&gt;や&lt;a href=&quot;http://www.nicovideo.jp/watch/sm28712861&quot; target=&quot;_blank&quot;&gt;動画&lt;/a&gt;でも紹介していますが、
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://s.oov.ch/blog/20160825_test.zip&quot;&gt;http://s.oov.ch/blog/20160825_test.zip&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
例えばこういう URL で zip ファイルを配布するなら
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://s.oov.ch/blog/20160825_test.html&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;http://s.oov.ch/blog/20160825_test.html&lt;/a&gt;&lt;br /&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;（HTML ファイルをダウンロードする場合は右クリックメニューからどうぞ）&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
同じ場所にこんな感じの HTML ファイルも設置します（ファイル名は任意）。すると、
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://oov.github.io/psdtool/#load:xd:http%3A%2F%2Fs.oov.ch%2Fblog%2F20160825_test.html&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;http://oov.github.io/psdtool/#load:xd:http%3A%2F%2Fs.oov.ch%2Fblog%2F20160825_test.html&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
この長い URL で直接 zip ファイルに入った PSD ファイルを開くことができます。&lt;br /&gt;
ちょっと URL が長くて扱いにくい場合は &lt;a href=&quot;https://goo.gl/&quot; target=&quot;_blank&quot;&gt;https://goo.gl/&lt;/a&gt; で短縮しても使えます。&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://goo.gl/1RxjAz&quot; target=&quot;_blank&quot;&gt;http://goo.gl/1RxjAz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
この URL の後ろに + を付ければ&lt;a href=&quot;http://goo.gl/1RxjAz+&quot; target=&quot;_blank&quot;&gt;アクセス解析もできます&lt;/a&gt;。&lt;br /&gt;
PSD ファイルは配布用に用意したものをそのまま流用できるし、実質的に必要になる追加作業は HTML ファイルを編集して上げるだけです。&lt;br /&gt;
ただ zip ファイルのサイズを小さくしないと待ち時間が長くなるので、大きなファイルはオススメしません。&lt;br /&gt;
&lt;br /&gt;
というわけで、ダイレクト読み込み機能を使えばダウンロード用のリンクとファイルを開くためのリンクを並べて設置したりできるので、「とりあえず」のクリックで開いてもらうこともできます。&lt;br /&gt;
&lt;br /&gt;
なお、設置された HTML ファイルは
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;HTML ファイルをブラウザで開いた時にダイレクト読み込み用の URL を伝えるため&lt;/li&gt;
&lt;li&gt;PSDTool に PSD ファイルの内容を転送するため&lt;/li&gt;
&lt;/ul&gt;
この2つの目的で使用されます。&lt;br /&gt;
そのため、URL が正しくても HTML ファイルが削除されていると開けなくなります。&lt;br /&gt;
&lt;h2&gt;
全体のまとめ&lt;/h2&gt;
PSD ファイルでできる工夫は「*」と「!」、PSDTool でできる工夫はシンプルビューを使うことです。&lt;br /&gt;
細かい話は色々あるけどざっくり言うとそういうことです。&lt;br /&gt;
&lt;h4&gt;
もしお気に入りが消えてしまったら&lt;/h4&gt;
PSDTool のお気に入りは、&lt;span style=&quot;color: red;&quot;&gt;PSD ファイルの内容が少しでも変わると別のファイルとして認識されてお気に入りが真っ白になります&lt;/span&gt;が、慌てずに「お気に入りのインポート」機能を使うと、最近使ったお気に入りなら別のファイル用のものでも手動で読み込むことができます。&lt;br /&gt;
&lt;br /&gt;
ただしお気に入りはレイヤーの名前を基準に判定しているので、以前とレイヤーの名前が変わっていると、その周辺部分は上手く読み込まれなくなるでしょう。&lt;br /&gt;
&lt;br /&gt;
もし構造自体は同じで単純にレイヤーの名前が変わっただけなら、お気に入りをエクスポートしてテキストエディタで開いて、名前を置換して、お気に入りをインポートし直すことで対応することも一応できます。&lt;br /&gt;
&lt;br /&gt;
このように PSDTool は「完成した PSD ファイルを触るためのツール」という側面があるので、お絵かきソフトと PSDTool の両方で並行して同じ PSD ファイルを使う時は、お気に入り機能はあまり触らないのがオススメです。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/8554026963500930300'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/8554026963500930300'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2016/08/psdtool.html' title='［絵を描く人向け］PSDTool の使い方'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGLocDjFOtyeZGRS3g5fbQuN2QnAkr_bi_Zi-jciNXLnES429JBh2n40RG2j6WstBcIa3PRso6WhZBnwa9fdhaHIZs0u30P53JMtr08te03oHYwcCzTcbOyVjzqr-r57hwlg6A-nGmdXk/s72-c/screen1.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-6863328385174836529</id><published>2016-08-19T07:18:00.002+09:00</published><updated>2016-10-28T04:39:48.404+09:00</updated><title type='text'>ZIP ファイルに内容の似た PNG ファイルを大量に突っ込む話</title><content type='html'>2016年から&amp;nbsp;&lt;a href=&quot;https://oov.github.io/psdtool/&quot; target=&quot;_blank&quot;&gt;PSDTool&lt;/a&gt; というツールを作っている。&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
これは端的に説明すると PSD ファイルをブラウザで読み込んで、レイヤーごとにバラして保存したり、レイヤーの表示状態を切り替えた画像をたくさん保存したりするためのツール。&lt;br /&gt;
&lt;br /&gt;
PSDTool にはブラウザのブックマークに似た機能があり、レイヤーの表示／非表示の切り替えを行った状態を保存しておくことができる。&lt;br /&gt;
更にエクスポート機能を使うことで登録した項目を PNG ファイルで ZIP ファイルに詰め込むことができる。&lt;br /&gt;
&lt;br /&gt;
メインの用途は古典的な立ち絵イラストを、各種ツールで扱いやすい形式、構造に変換すること。&lt;br /&gt;
（ここでいう古典的とは、モーフィングのような機能を含まないという意味）&lt;br /&gt;
立ち絵を使うツール側で高度な画像合成ができるなら PSD のレイヤーごとに書き出すのもいいが、合成済みの画像を得られた方が便利なケースも当然多い。&lt;br /&gt;
&lt;br /&gt;
合成済みの画像をたくさん ZIP ファイルに保存する場合、表情だけがちょっと違うような、似た内容の画像をたくさん保存することになる。これを ZIP にそのまま突っ込むのは扱いやすい反面ファイルサイズが全然縮まないので、色々考えてみた話。&lt;br /&gt;
&lt;br /&gt;
要するに、こういう感じのファイルセットを効率よく圧縮したい。&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyGTGj4McxmLK_Ne57wJe7fgNpNy2Mxue0SlsA_gbMenH9efiezb6xLJ5l1y61U-bRMFYzOnrNS14MhgVzIUyrGqcnDQhTyYbhz7IAp_ue-fgR-lzA0LZTXJucuMeL8zhr7gzBv6tpfB4/s1600/manypng.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyGTGj4McxmLK_Ne57wJe7fgNpNy2Mxue0SlsA_gbMenH9efiezb6xLJ5l1y61U-bRMFYzOnrNS14MhgVzIUyrGqcnDQhTyYbhz7IAp_ue-fgR-lzA0LZTXJucuMeL8zhr7gzBv6tpfB4/s1600/manypng.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;(C)&amp;nbsp;&lt;a href=&quot;http://stringliteral.web.fc2.com/&quot; target=&quot;_blank&quot;&gt;零度の青空 air&lt;/a&gt;&amp;nbsp;&lt;span style=&quot;color: #cccccc;&quot;&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;|&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;独自拡張機能対応ありがとうございます&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
ちなみに PSDTool 上で「&lt;a href=&quot;https://oov.github.io/psdtool/manual.html#feature-layer-tree&quot; target=&quot;_blank&quot;&gt;シンプルビュー&lt;/a&gt;」を使えるようにして、エクスポートで「シンプルビューの全パターンをエクスポート」を選ぶと、このようにたくさんのパターンの PNG ファイルを簡単に得ることができる。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;ZIP ファイルに内容が似た PNG ファイルをたくさん入れた時の圧縮効率の悪さ&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
まず、ZIP ファイルにちょっとだけ内容が違う PNG ファイルをたくさん入れた時の問題点を整理する。&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;ZIP ファイルではファイル毎に圧縮するので、2つのファイルが似ているかは考慮されない。&lt;br /&gt;例えば圧縮後に 2KiB になるファイルを2つ ZIP に入れれば 4KiB ぐらいになるし、10個入れれば全部同じ内容のファイルでも 20KiB ぐらいになる。&lt;/li&gt;
&lt;li&gt;ZIP ファイルで最も一般的な圧縮メソッドである &lt;a href=&quot;https://ja.wikipedia.org/wiki/Deflate&quot; target=&quot;_blank&quot;&gt;Deflate&lt;/a&gt; は LZ77 のウィンドウサイズが最大でも 32KiB と小さい。&lt;br /&gt;だからもし ZIP ファイルで PNG ファイル同士が似ていることが考慮できたとしても、ファイルが 32KiB を超えると似た場所を上手く見つけられなくなる。&lt;/li&gt;
&lt;li&gt;画像が既に PNG ファイルとして圧縮されているので、表示した時に見た目がほぼ一緒でもファイルのデータ自体には結構差があって、似ているかどうかの判断が難しい。&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
ZIP ファイルが縮まない原因として大きいのはこの辺だと思う。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;どのような対処方法が考えられるか&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
1 や 2 の問題は 7z ファイルなどのような新しいファイル形式や、TAR 形式のように複数のファイルを1つのファイルにまとめてから圧縮するような形式である程度解決する。&lt;/div&gt;
&lt;div&gt;
しかし汎用的なコンテナフォーマットとしては&amp;nbsp;ZIP ほど普及していないので、OS を問わずツールを準備せず簡単に展開できるというレベルではなく、ZIP に比べて取っ付きにくい。&lt;/div&gt;
&lt;div&gt;
自作プログラムで展開するケースを考えても、色々な言語で扱いやすい実装があることが多い ZIP に比べ普及率や速度の面で扱いづらいし、TAR だと上から圧縮すると任意のファイルへランダムアクセスもしづらい。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
3 の問題は無圧縮の画像データにすればデータ的に似ている場所の検出は簡単になるけど、例えばほとんど内容が同じ BMP ファイルを 7z ファイルに大量に入れてあるとしたら、一度に全てを展開したりするとちょっとした&lt;a href=&quot;https://ja.wikipedia.org/wiki/%E9%AB%98%E5%9C%A7%E7%B8%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E7%88%86%E5%BC%BE&quot; target=&quot;_blank&quot;&gt;高圧縮ファイル爆弾&lt;/a&gt;になりうるので危険が危ない。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
自分でアーカイブフォーマットなどを自作するのももちろん考えたが、世の中にあるツールが全て使えないことになるのでこれもまたつらい。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
そんなこんなで ZIP や PNG でもっと縮める方法を考えた末、ちょっとしたアイディアが浮かぶ。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;Tiled 用の形式で書き出す&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT0BwO3sOOYuNU9CQt_AD_Bxjv2219NsnFP8586KdU4mSqrhEYFgBdC-uaol11iVpWIiNZ8fRZ2iHRtAKfSxC7VaW1e_VhKOkk41F2sUWY45Lae6_hzRn4uyhq_E8w4k1pA3fhxQZiCiE/s1600/tiled.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;275&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT0BwO3sOOYuNU9CQt_AD_Bxjv2219NsnFP8586KdU4mSqrhEYFgBdC-uaol11iVpWIiNZ8fRZ2iHRtAKfSxC7VaW1e_VhKOkk41F2sUWY45Lae6_hzRn4uyhq_E8w4k1pA3fhxQZiCiE/s400/tiled.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;(C) &lt;a href=&quot;http://stringliteral.web.fc2.com/&quot; target=&quot;_blank&quot;&gt;零度の青空 air&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;
&lt;div&gt;
&lt;a href=&quot;http://www.mapeditor.org/&quot; target=&quot;_blank&quot;&gt;Tiled&lt;/a&gt; はマップエディタで、ドラクエみたいなマップとか、マリオのステージ構成とか、ああいうタイル状に並べたデータを作るためのツール。これ用のデータとして立ち絵を書き出す試み。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
例えば立ち絵画像2枚をそれぞれ 16x16 のマスで切り分けていくと、表情とか変化があった部分はマスの内容が異なるけど、ほとんどのマスは同じ内容になるのが容易に想像できる。&lt;/div&gt;
&lt;div&gt;
ほとんどのマスが同じということは、多くの場所でタイルチップの使い回しができる。&lt;/div&gt;
&lt;div&gt;
立ち絵画像1枚だけで考えても、余白の透明部分などはかなりの場所で使い回しができる。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
こうして PNG や ZIP のまま Tiled 形式を上手く使って、似た部分を効率よく格納できるようになった。&lt;/div&gt;
&lt;div&gt;
画像を元の形に復元するためには当然タイルを並べないといけないが、レイヤー合成ほど複雑で多彩な処理は必要としないし、レンダリング済み画像だから元画像のレイヤー数が多くても表示の際にメモリ消費や描画負荷が増えることはない。&lt;br /&gt;
タイルチップを並べた画像1枚に相当なバリエーションのデータが詰まっているため消費メモリ面で見ても結構アドバンテージがあるように思う。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
PNG ファイルを ZIP ファイルに詰めた場合大体 &lt;code&gt;82MB&lt;/code&gt; ぐらいになるデータが PNG + TSX&amp;nbsp;+ TMX で&amp;nbsp;&lt;code&gt;1.24MB&lt;/code&gt;&amp;nbsp;まで縮み、今回は 512x512 のテクスチャに全てのタイルが収まった。&lt;br /&gt;
TMX の書き出し方などはテスト段階なので最終的にはサイズが変わるかもしれないが、なかなかの結果だ。&lt;br /&gt;
&lt;br /&gt;
まあこの方法で書き出せるようになっても需要はほとんどなさそうだけど。&lt;br /&gt;
この機能は準備ができたらそのうち公開する予定。&lt;br /&gt;
&lt;br /&gt;
---- 2016-08-21 追記&lt;br /&gt;
&lt;br /&gt;
処理を整理し別の PSD ファイルでも試してみたところ、PNG ファイルを ZIP ファイルに詰めた場合 &lt;code&gt;1.22GB&lt;/code&gt; になるデータが PNG&amp;nbsp;+ TMX の ZIP で &lt;code&gt;18MB&lt;/code&gt;、ZIP ではなく 7z に入れると &lt;code&gt;534KB&lt;/code&gt; まで縮んだ。&lt;br /&gt;
テストに使った PSD ファイルが &lt;code&gt;826KB&lt;/code&gt; で ZIP に入れると &lt;code&gt;539KB&lt;/code&gt;、7z に入れると &lt;code&gt;291KB&lt;/code&gt; なのでなかなかの健闘ぷり。&lt;br /&gt;
&lt;br /&gt;
---- 2016-10-28 追記&lt;br /&gt;
&lt;br /&gt;
ここで得られた知見を元に改めて独自のアーカイブフォーマットを試作してみたところ、上記のテストで &lt;code&gt;1.24MB&lt;/code&gt; だったものは &lt;code&gt;234KB&lt;/code&gt;、追記で書いた &lt;code&gt;18MB&lt;/code&gt; のものは &lt;code&gt;733KB&lt;/code&gt; に縮めることができた。&lt;br /&gt;
自分が作った専用ビューワ以外で開けないので今のところ実用性はアレではある。&lt;/div&gt;
&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/6863328385174836529'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/6863328385174836529'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2016/08/zip.html' title='ZIP ファイルに内容の似た PNG ファイルを大量に突っ込む話'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyGTGj4McxmLK_Ne57wJe7fgNpNy2Mxue0SlsA_gbMenH9efiezb6xLJ5l1y61U-bRMFYzOnrNS14MhgVzIUyrGqcnDQhTyYbhz7IAp_ue-fgR-lzA0LZTXJucuMeL8zhr7gzBv6tpfB4/s72-c/manypng.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-5983675434540784002</id><published>2016-04-27T06:05:00.001+09:00</published><updated>2016-04-27T09:58:37.037+09:00</updated><title type='text'>Go言語の -buildmode=c-archive と Lazarus でのスタティックリンク（Windows/Linux）</title><content type='html'>&lt;a href=&quot;http://bl.oov.ch/2016/04/go-windows-buildmodec-archive.html&quot; target=&quot;_blank&quot;&gt;前回の記事&lt;/a&gt;では Windows で &lt;code&gt;-buildmode=c-archive&lt;/code&gt; を試した。今回はそれと同じことを Ubuntu 上でも行ってみて、最終的に同じソースコードで両方の環境でコンパイルできるところまで持ち込む。
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;最初の一歩&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
前回の話を踏まえると、Lazarus(Free Pascal Compiler) で &lt;code&gt;go build -buildmode=c-archive&lt;/code&gt; でコンパイルしたバイナリをリンクしようとする時は、概ね以下のようにすると最低限処理が動くようになるはずだった。&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;FPC のリンカだと上手く動かないので Use external linker オプションを付ける必要がある&lt;br /&gt;→ プロジェクトオプションのコンパイラオプション → Custom Options から -Xe を追加&lt;/li&gt;
&lt;li&gt;ライブラリのファイル名が lib*.a でなければ上手く認識させられない&lt;br /&gt;→ Go言語側で main パッケージのディレクトリ名を libhoge という感じにしておく&lt;/li&gt;
&lt;li&gt;初期化処理が呼ばれないので自分で呼ぶ必要がある&lt;br /&gt;→ &lt;code&gt;_rt0_amd64_linux_lib&lt;/code&gt; を呼びたい&lt;b&gt;（呼べなかった）&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
というわけで、_rt0_amd64_linux_lib が呼べなくて最初に躓いた。&lt;br /&gt;
&lt;br /&gt;
もう少し詳しく見てみると、Windows では &lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-bash&quot;&gt;nm libhoge.a | grep _lib&lt;/pre&gt;
などとすると&amp;nbsp;&lt;code&gt;_rt0_amd64_windows_lib&lt;/code&gt; は外部シンボルとして定義されているのだが、Ubuntu で同じことをすると&amp;nbsp;&lt;code&gt;_rt0_amd64_linux_lib&lt;/code&gt; は内部シンボルとして定義されていた。そして、FPC で &lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-pascal&quot;&gt;procedure _rt0_amd64_windows_lib(); cdecl; external;&lt;/pre&gt;
みたいな形で定義したものは外部シンボルでなければ参照できず、このままでは無理、という状況。&lt;br /&gt;
&lt;br /&gt;
「この問題を解決するためにどうするのがいいか」という以前にそもそもこの辺あまり詳しくないので、わかる範囲で少し調べながら対応方法を検討した。&lt;br /&gt;
&lt;br /&gt;
その上で一番正しいっぽい道のりは多分「FPC 側でオブジェクトファイルの .init_array / .ctor セクションを参照して初期化処理を実行する」みたいな感じだと思うのだが、ちょっと話のスケールが大きい上に既存の振る舞いとの互換性の問題もありそうで手を出しにくいので、この方向は諦めた。&lt;br /&gt;
もし実現させるならプログラム固有のコードじゃなくてランタイム側のコードを変える話になりそう。&lt;br /&gt;
&lt;br /&gt;
次に考えたのは「Go言語側でのビルド時に Windows では外部シンボルになっているんだから、挙動が違う原因を突き止めて両方外部シンボルにできれば解決するのでは？」という方向性。&lt;br /&gt;
しかし、そもそも外部シンボルになっているものを &lt;code&gt;nm -g libhoge.a&lt;/code&gt; で列挙してみると、むしろ Windows 側が不必要な部分まで外部シンボルになってしまっているのではないか、と思えるぐらいに差があったので、将来的には逆に Windows 側で多くが内部シンボルになる方が正しそうな予感がして、このアプローチもやめた（だから挙動が違う原因もわからないまま）。&lt;br /&gt;
&lt;br /&gt;
最終的に選んだアプローチは「&lt;code&gt;_rt0_amd64_linux_lib&lt;/code&gt; を外部シンボル化する方法を探る」という感じ。&lt;br /&gt;
Linux 側で &lt;code&gt;nm -g libhoge.a&lt;/code&gt; した時に出てくる cgo 関係の外部シンボルを見る限り、&lt;a href=&quot;https://golang.org/src/runtime/cgo/callbacks.go#L22&quot; target=&quot;_blank&quot;&gt;この辺り&lt;/a&gt;で使われている機能を上手く利用すれば外部シンボルにできそうだった。&lt;br /&gt;
最初はGo言語のソースコードの src/runtime 辺りにファイルを追加して、全体を make し直して使えるようになることを確認していたのだが、もう少し調べてみると別に src/runtime に置く必要は全然なかったようなので、&lt;a href=&quot;https://github.com/oov/rt0&quot; target=&quot;_blank&quot;&gt;github.com/oov/rt0&lt;/a&gt; という、コードが全然ないパッケージを作った。&lt;br /&gt;
darwin とか試してないものもあるが、取りあえず&amp;nbsp;&lt;code&gt;_rt0_GOARCH_GOOS_lib&lt;/code&gt; が現状定義されているものに関しては全部揃えておいた。&lt;br /&gt;
&lt;br /&gt;
これで、&lt;code&gt;-buildmode=c-archive&lt;/code&gt; を使う main パッケージ辺りで&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-go&quot;&gt;import _ &quot;github.com/oov/rt0&quot;&lt;/pre&gt;
だけで外部シンボルになるようになった。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;初期化処理を正しく呼ぶ&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Windows の時はパラメータを特に定義せずに呼んでいたのだが、同じように Linux で &lt;code&gt;_rt0_amd64_linux_lib&lt;/code&gt; を呼んでみると、プログラムの起動時に&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-txt&quot;&gt;runtime: kernel page size (134593276374883) is larger than runtime page size (4096)&lt;/pre&gt;
という、なんか値の差が大きすぎて何かが根本的に噛み合ってなさそうな気配のするエラーメッセージが出て上手く動かなかった。&lt;br /&gt;
Go 1.6.2 で同じことをしてもエラーメッセージが出ずに実行されたのだが、&lt;a href=&quot;https://github.com/golang/go/commit/8ce844e88ed8c16bef7febea05b003b50bd0609e&quot; target=&quot;_blank&quot;&gt;つい最近追加された部分だったらしい&lt;/a&gt;。&lt;br /&gt;
&lt;br /&gt;
そこで&amp;nbsp;&lt;a href=&quot;https://golang.org/src/runtime/rt0_linux_amd64.s&quot; target=&quot;_blank&quot;&gt;rt0_linux_amd64.s&lt;/a&gt;&amp;nbsp;のコードを読んでみると、tip の rt0_windows_amd64.s とは違い DI と SI には本来 argc と argv が入っているらしいことがわかった。呼び出し規約を確認してみると2つの引数が渡されてきた時の形なので「あ、これ要するに main 関数じゃん！」などとやっと気付いた勘の悪い自分であったが、結果的に&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-pascal&quot;&gt;procedure _rt0_amd64_linux_lib(argc: Longint; argv: PPChar); cdecl; external;

initialization
  _rt0_amd64_linux_lib(argc, argv);&lt;/pre&gt;
という形に辿り着くことができたので良しとしよう。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnAO97nMg_OJAL3fXuOvZ4ik9zAnLJX9L9-OjIDCjSjyqvo6chKMhxtRq73QYGdTcdywmwhPEJjvo33FeUQzZX8cA7ElA9Pe1hCkzeP-b2grumUrdyj3vVwzgP0VryegoSPvLWYKUUyEI/s1600/screen.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;164&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnAO97nMg_OJAL3fXuOvZ4ik9zAnLJX9L9-OjIDCjSjyqvo6chKMhxtRq73QYGdTcdywmwhPEJjvo33FeUQzZX8cA7ElA9Pe1hCkzeP-b2grumUrdyj3vVwzgP0VryegoSPvLWYKUUyEI/s320/screen.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;動いたー　やったー&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
前回の記事で&amp;nbsp;SIGFPE で止まっていた問題は Windows でもいつからか再現しなくなってしまったので、Linux 上でも起こるかどうかなどは謎。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;処理をまとめる&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
これで Windows と Linux で同じ地点まで辿り着いたので、あとは同じソースコードで動くようにしたい。&lt;br /&gt;
&lt;br /&gt;
まずはGo言語側。前回より少しメモリコピー回数がマシになっている。&lt;br /&gt;
OS によって処理が分かれている部分も特にないのであまり見どころはない。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-go&quot;&gt;package main

// #include &amp;lt;string.h&amp;gt;
import &quot;C&quot;

import (
 &quot;bytes&quot;
 &quot;image/png&quot;
 &quot;os&quot;
 &quot;unsafe&quot;

 &quot;github.com/oov/psd&quot;
 _ &quot;github.com/oov/rt0&quot;
)

//export PSD2PNG
func PSD2PNG(filename *C.char, buf *unsafe.Pointer, ln *C.int) C.int {
 file, err := os.Open(C.GoString(filename))
 if err != nil {
  return 1
 }
 defer file.Close()

 img, _, err := psd.Decode(file, &amp;amp;psd.DecodeOptions{SkipLayerImage: true})
 if err != nil {
  return 2
 }

 var b bytes.Buffer
 pe := png.Encoder{CompressionLevel: png.BestSpeed}
 err = pe.Encode(&amp;amp;b, img)
 if err != nil {
  return 3
 }

 *buf = C.malloc(C.size_t(b.Len()))
 C.memcpy(*buf, unsafe.Pointer(&amp;amp;b.Bytes()[0]), C.size_t(b.Len()))
 *ln = C.int(b.Len())
 return 0
}

func main() {}&lt;/pre&gt;
そして Lazarus 側。初期化処理用のシンボル名は環境毎に変わるので、まずそれに対応する処理が必要。&lt;br /&gt;
あとは Windows の時はリンクするファイルが増えるのでそれも ifdef で囲っておく。&lt;br /&gt;
Windows では argc / argv を参照していないが、呼び出し規約的に動作に影響はないはずなので処理は分けてない。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-pascal&quot;&gt;unit psdtest;

{$mode objfpc}{$H+}

interface

function PSD2PNG(Filename: PChar; Buf: PPointer; Len: PInteger): Integer;
  cdecl; external Name &#39;PSD2PNG&#39;;
procedure Cfree(P: Pointer); cdecl; external Name &#39;free&#39;;

implementation

{$linklib libpsdtest.a}

{$ifdef WINDOWS}
  {$linklib libntdll.a}
  {$linklib libws2_32.a}
  {$linklib libadvapi32.a}
  {$linklib libwinmm.a}
  {$linklib libkernel32.a}
  {$linklib libmsvcrt.a}
{$endif}

const
  ARCH = {$ifdef CPU386}&#39;386&#39;{$endif}{$ifdef CPUAMD64}&#39;amd64&#39;{$endif}{$ifdef CPUARM}&#39;arm&#39;{$endif}{$ifdef CPUAARCH64}&#39;arm64&#39;{$endif};
  OS = {$ifdef WINDOWS}&#39;windows&#39;{$endif}{$ifdef LINUX}&#39;linux&#39;{$endif}{$ifdef DARWIN}&#39;darwin&#39;{$endif};

procedure GoInit(argc: longint; argv: PPChar); cdecl;
  external Name &#39;_rt0_&#39; + ARCH + &#39;_&#39; + OS + &#39;_lib&#39;;

initialization
  GoInit(argc, argv);

end.&lt;/pre&gt;
まあ Lazarus 側はこのままだと扱いにくいので、もうちょっと使いやすくしたのがこちら。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-pascal&quot;&gt;unit psdtest;

{$mode objfpc}{$H+}

interface

uses
  Classes;

function PSD2PNG(FileName: string): TStream;

implementation

uses
  SysUtils;

function PSD2PNG(Filename: PChar; Buf: PPointer; Len: PInteger): integer;
  cdecl; external Name &#39;PSD2PNG&#39;;
procedure Cfree(P: Pointer); cdecl; external Name &#39;free&#39;;

{$linklib libpsdtest.a}

{$ifdef WINDOWS}
  {$linklib libntdll.a}
  {$linklib libws2_32.a}
  {$linklib libadvapi32.a}
  {$linklib libwinmm.a}
  {$linklib libkernel32.a}
  {$linklib libmsvcrt.a}
{$endif}

const
  ARCH = {$ifdef CPU386}&#39;386&#39;{$endif}{$ifdef CPUAMD64}&#39;amd64&#39;{$endif}{$ifdef CPUARM}&#39;arm&#39;{$endif}{$ifdef CPUAARCH64}&#39;arm64&#39;{$endif};
  OS = {$ifdef WINDOWS}&#39;windows&#39;{$endif}{$ifdef LINUX}&#39;linux&#39;{$endif}{$ifdef DARWIN}&#39;darwin&#39;{$endif};

procedure GoInit(argc: longint; argv: PPChar); cdecl;
  external Name &#39;_rt0_&#39; + ARCH + &#39;_&#39; + OS + &#39;_lib&#39;;

// --------------------------------

type
  TCMemoryStream = class(TCustomMemoryStream)
  public
    constructor Create(const P: Pointer; Sz: PtrInt);
    destructor Destroy; override;
  end;

constructor TCMemoryStream.Create(const P: Pointer; Sz: PtrInt);
begin
  inherited Create;
  SetPointer(P, Sz);
end;

destructor TCMemoryStream.Destroy;
begin
  Cfree(Memory);
  inherited Destroy;
end;

function PSD2PNG(FileName: string): TStream;
var
  Errno, Size: Integer;
  Buf: Pointer;
begin
  Errno := PSD2PNG(PChar(FileName), @Buf, @Size);
  if Errno &amp;lt;&amp;gt; 0 then
     raise Exception.Create(&#39;cannot convert psd to png: &#39;+FileName);
  try
    Result := TCMemoryStream.Create(Buf,Size);
  except
    Cfree(Buf);
  end;
end;

initialization
  GoInit(argc, argv);

end.&lt;/pre&gt;
ファイル名を string、戻り値を TStream にすることでフレンドリーに使える形になった。 TCustomMemoryStream を継承したストリームを作る事で無駄なメモリコピーも抑制できて一石二鳥。&lt;br /&gt;
入力もファイル名の代わりに TStream にできると思うけど、巨大なファイルをメモリの固まりじゃなくてストリームとして綺麗に渡すのは結構面倒な話になりそうなのでここでは触れないでおく。&lt;br /&gt;
&lt;br /&gt;
使う側は
&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-pascal&quot;&gt;procedure TForm1.Button1Click(Sender: TObject);
var
  S: TStream;
begin
  if not OpenDialog1.Execute then
    Exit;
  S := PSD2PNG(OpenDialog1.FileName);
  try
    Image1.Picture.LoadFromStream(S);
  finally
    S.Free;
  end;
end;&lt;/pre&gt;
みたいな普通な感じで使えて、これで両方の環境でコンパイルできるソースコードになった。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/5983675434540784002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/5983675434540784002'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2016/04/go-buildmodec-archive-lazarus.html' title='Go言語の -buildmode=c-archive と Lazarus でのスタティックリンク（Windows/Linux）'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnAO97nMg_OJAL3fXuOvZ4ik9zAnLJX9L9-OjIDCjSjyqvo6chKMhxtRq73QYGdTcdywmwhPEJjvo33FeUQzZX8cA7ElA9Pe1hCkzeP-b2grumUrdyj3vVwzgP0VryegoSPvLWYKUUyEI/s72-c/screen.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-2075308102775918836</id><published>2016-04-05T08:18:00.000+09:00</published><updated>2016-04-27T06:06:13.752+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="golang"/><category scheme="http://www.blogger.com/atom/ns#" term="Go言語"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><title type='text'>Go言語の Windows 版で -buildmode=c-archive を使う</title><content type='html'>安定版のリリースはまだ先だけど、tip で Windows 上での -buildmode=c-archive が&lt;a href=&quot;https://github.com/golang/go/commit/ed8f0e5c33269de2f950d33ab7d50554b13f336e&quot; target=&quot;_blank&quot;&gt;動くようになった&lt;/a&gt;らしい。早速試してみることにする。
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;1. 安定版のGo言語をインストール&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
tip のGo言語をビルドするために、まず安定版をインストールする。&lt;br /&gt;
これは普通に&lt;a href=&quot;https://golang.org/dl/&quot; target=&quot;_blank&quot;&gt;インストーラー版&lt;/a&gt;を使ってインストールするだけでいい。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;2. GCC をインストール&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
後々のために入れる。&lt;br /&gt;
&lt;a href=&quot;https://build.golang.org/&quot; target=&quot;_blank&quot;&gt;build.golang.org &lt;/a&gt;で動いている Windows のビルダーは &lt;a href=&quot;https://github.com/golang/winstrap&quot; target=&quot;_blank&quot;&gt;winstrap&lt;/a&gt; で環境を構築しているようなのだが、&lt;a href=&quot;https://github.com/golang/winstrap/blob/master/winstrap.go#L27&quot; target=&quot;_blank&quot;&gt;ここを見る&lt;/a&gt;限りでは &lt;a href=&quot;http://tdm-gcc.tdragon.net/&quot; target=&quot;_blank&quot;&gt;TDM-GCC&lt;/a&gt; を使っているようなので、これを入れることにする。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;3. Git をインストール&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
このご時世インストールしてない人はいないと思うけども。&lt;br /&gt;
これも&lt;a href=&quot;https://git-for-windows.github.io/&quot; target=&quot;_blank&quot;&gt;インストーラ&lt;/a&gt;から。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;4. tip 版のGo言語をビルド&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
ここまでの段階でインストーラによって環境変数 &lt;code&gt;GOROOT&lt;/code&gt; が設定されていると思うので、コマンドプロンプトを開いて &lt;kbd&gt;go version&lt;/kbd&gt; を打てば安定版のGo言語のバージョンが表示されるはず。&lt;br /&gt;
この安定版を最新版のビルドに使うことを示すために、&lt;kbd&gt;set GOROOT_BOOTSTRAP=%GOROOT%&lt;/kbd&gt; などとして環境変数に入れておく。&lt;br /&gt;
そして適当な深すぎないディレクトリで &lt;kbd&gt;git clone https://github.com/golang/go/&lt;/kbd&gt; でGo言語をクローンして、&lt;kbd&gt;go\src\all.bat&lt;/kbd&gt; を実行すればビルドは始まる。&lt;br /&gt;
&lt;br /&gt;
ビルドが完了したら、今度は最新版をGo言語を使うために &lt;kbd&gt;set GOROOT=clone先&lt;/kbd&gt; として &lt;code&gt;GOROOT&lt;/code&gt; を書き換えて、更に &lt;kbd&gt;set PATH=%GOROOT%\bin;%PATH%&lt;/kbd&gt; として &lt;code&gt;PATH&lt;/code&gt; も再優先で通しておく。&lt;br /&gt;
&lt;br /&gt;
改めて &lt;kbd&gt;go version&lt;/kbd&gt; して &lt;samp&gt;go version devel&lt;/samp&gt; という感じでバージョン情報が変わっていたら準備完了。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;5. テストプログラムを作る&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
c-archive モードを使うためのテストプログラムを作ってみる。&lt;br /&gt;
適当なディレクトリを作って、その中にこんな感じのファイルを作成する。&lt;br /&gt;
今回は &lt;kbd&gt;carchivetest&lt;/kbd&gt; というディレクトリの中に &lt;kbd&gt;main.go&lt;/kbd&gt; というファイル名で作成した。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-go&quot;&gt;package main

import (
 &quot;C&quot;
 &quot;log&quot;
)

//export testInt
func testInt() int {
 log.Println(&quot;testInt&quot;)
 return 42
}

//export testStr
func testStr() *C.char {
 log.Println(&quot;testStr&quot;)
 return C.CString(&quot;OK&quot;)
}

func main() {
 log.Println(&quot;Hello world&quot;)
}&lt;/pre&gt;
&lt;code&gt;//export&lt;/code&gt; から始まるコメント行はエクスポートすることを明示するアノテーションなので必ず必要。&lt;br /&gt;
文字列を返したい場合、そのまま &lt;code&gt;string&lt;/code&gt; を渡すと &lt;code&gt;GoString&lt;/code&gt; 型になるけど、&lt;code&gt;C.CString&lt;/code&gt; で変換すると &lt;code&gt;char&lt;/code&gt; ポインタで渡すこともできる。この場合受け取った側で &lt;code&gt;free&lt;/code&gt; する必要がある。&lt;br /&gt;
ちなみに文字列を返さない場合など、この例では &lt;code&gt;import &quot;C&quot;&lt;/code&gt; がなくてもコンパイルはできるものの、その場合C言語側から呼ぶ時に必要になるヘッダーファイルが生成されないし多分エクスポートも動かない気がするので&amp;nbsp;&lt;code&gt;import &quot;C&quot;&lt;/code&gt; は必須だと思う。&lt;br /&gt;
なお、ここでは&amp;nbsp;&lt;code&gt;main&lt;/code&gt; で &lt;kbd&gt;Hello world&lt;/kbd&gt; を出力するようなコードを書いているが、main は無視されるはずなので実際には出力されない。&lt;br /&gt;
&lt;br /&gt;
main.go があるディレクトリで &lt;kbd&gt;go build -buildmode=c-archive&lt;/kbd&gt; とすると、carchivetest.h carchivetest.a というファイルが出力される。&lt;br /&gt;
&lt;br /&gt;
次にC言語側のプログラムを作る。&lt;br /&gt;
生成されたヘッダーファイルを使って、エクスポートされた関数を呼び出す。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-c&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &quot;carchivetest.h&quot;

int main(int argc, char *argv[]) {
 printf(&quot;main\n&quot;);
 printf(&quot;testInt -&amp;gt; %lld\n&quot;, testInt());
 char *r = testStr();
 printf(&quot;testStr -&amp;gt; %s\n&quot;, r);
 free(r);
}&lt;/pre&gt;
特に意外性のない普通のプログラム。&lt;br /&gt;
これをコンパイルするには &lt;code&gt;gcc main.c carchivetest.a -lntdll -lws2_32&lt;/code&gt; という感じにする。&lt;br /&gt;
&lt;br /&gt;
これで a.exe が生成されているので、実行すると以下のような出力が得られる。
&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;main
2016/04/04 18:30:16 testInt
testInt -&amp;gt; 42
2016/04/04 18:30:16 testStr
testStr -&amp;gt; OK&lt;/pre&gt;
予定通り&amp;nbsp;&lt;code&gt;&quot;Hello world&quot;&lt;/code&gt;&amp;nbsp;は出力されず、それぞれに書いた出力は正しく出力されている。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;6. テストプログラムをもっと作る&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
せっかく Windows 上で使えるようになったんだし、GUI プログラムでも試してみようと思う。&lt;br /&gt;
今度は GCC じゃなくて Lazarus を使ってプログラムを作ってみる。&lt;br /&gt;
Lazarus は 1.6 の 64bit 版をインストール（Go言語も 64bit 版を使っていたから）。&lt;br /&gt;
&lt;br /&gt;
ある程度試してみたところ、以下のような感じにすると動いた。&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;FPC のリンカだと上手く動かないので Use external linker オプションを付ける必要がある&lt;br /&gt;→ プロジェクトオプションのコンパイラオプション → Custom Options から&amp;nbsp;&lt;kbd&gt;-Xe&lt;/kbd&gt; を追加した&lt;/li&gt;
&lt;li&gt;ライブラリのファイル名が lib*.a でなければ上手く認識させられなかった&lt;br /&gt;→ 単純に carchivetest.a を libcarchivetest.a をリネームした&lt;/li&gt;
&lt;li&gt;TDM-GCC に付属している libntdll.a libws2_32.a libadvapi32.a libkernel32.a libmsvcrt.a にリンクする必要があるようなので、ライブラリパスを追加する&lt;br /&gt;→ プロジェクトオプションのコンパイラオプション → パス のライブラリにパスを追加&lt;/li&gt;
&lt;li&gt;Go言語側の初期化処理のために &lt;code&gt;initialization&lt;/code&gt; 句辺りで &lt;code&gt;_rt0_amd64_windows_lib&lt;/code&gt; を呼ばなければいけない&lt;br /&gt;→ 呼ばない場合エクスポートされた関数を呼び出しても処理が返ってこなくなる&lt;/li&gt;
&lt;li&gt;文字列をC言語のサンプルプログラムのように受け取る場合、&lt;code&gt;free&lt;/code&gt; もC言語のランタイム側で定義されているのを呼ばないと多分駄目&lt;br /&gt;→ 定義して呼べるようにしておく&lt;/li&gt;
&lt;/ul&gt;
以上の点を踏まえた上で、carchivetest を呼ぶためのユニットファイルは概ね以下の様な形になった。
&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-pascal&quot;&gt;unit carchivetest;

{$mode objfpc}{$H+}

interface

function testInt(): Int64; cdecl; external name &#39;testInt&#39;;
function testStr(): PChar; cdecl; external name &#39;testStr&#39;;
procedure Cfree(P: Pointer); cdecl; external name &#39;free&#39;;

implementation

{$linklib libcarchivetest.a}
{$linklib libntdll.a}
{$linklib libws2_32.a}
{$linklib libadvapi32.a}
{$linklib libkernel32.a}
{$linklib libmsvcrt.a}

procedure _rt0_amd64_windows_lib(); cdecl; external;

initialization
  _rt0_amd64_windows_lib();

end.&lt;/pre&gt;
新規プロジェクトからアプリケーションを選び新規プロジェクトを作成し、更にファイルメニューから新規ユニットを加えて上記のユニットファイルを作成。&lt;br /&gt;
このユニットファイルを Unit1 の uses 句に加え、ボタンを一個配置し、そのボタンをダブルクリックし、追加されたコードを以下の様に書き加える。
&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-pascal&quot;&gt;procedure TForm1.Button1Click(Sender: TObject);
var
  P: PChar;
begin
  P := testStr();
  Caption := IntToStr(testInt()) + &#39; / &#39; + P;
  Cfree(P);
end;&lt;/pre&gt;
これでコンパイル・実行して、ボタンを押した時のスクリーンショットが以下のもの。&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2BZ7vXIO0Qbvb_UKPxVEyOlF4C91tULxthtf7AAg47mLwfKHjYAnI5oghMWcSUimYbtvoWA_7Bzw61Z64TjZ-fDu7yawvkMzFyq-jvynrEn8UQbjv7fF25otoldHiHANCsPvQcFgzv8U/s1600/screenshot.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2BZ7vXIO0Qbvb_UKPxVEyOlF4C91tULxthtf7AAg47mLwfKHjYAnI5oghMWcSUimYbtvoWA_7Bzw61Z64TjZ-fDu7yawvkMzFyq-jvynrEn8UQbjv7fF25otoldHiHANCsPvQcFgzv8U/s1600/screenshot.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Caption にGo言語側から渡した値が正しく表示されている&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;b&gt;7. テストプログラムをもっともっと作る&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
ここまで動くようになったら、もう少し発展的な実験もしてみたい。&lt;br /&gt;
最近Go言語用の &lt;a href=&quot;https://github.com/oov/psd&quot; target=&quot;_blank&quot;&gt;Photoshop 形式の画像読み込みライブラリを作った&lt;/a&gt;ので、これを使って画像データを読み込んでみる。&lt;br /&gt;
&lt;br /&gt;
改めて &lt;kbd&gt;libpsdtest&lt;/kbd&gt; というディレクトリを作成し、その中に以下のファイルを &lt;kbd&gt;main.go&lt;/kbd&gt; として作成。
&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-go&quot;&gt;package main

import (
 &quot;C&quot;

 &quot;bytes&quot;
 &quot;image&quot;
 &quot;image/png&quot;
 &quot;os&quot;

 _ &quot;github.com/oov/psd&quot;
)

//export PSD2PNG
func PSD2PNG(filename *C.char, buf **C.char, ln *int, errno *int) {
 file, err := os.Open(C.GoString(filename))
 if err != nil {
  *errno = 1
  return
 }
 defer file.Close()

 img, _, err := image.Decode(file)
 if err != nil {
  *errno = 2
  return
 }

 var b bytes.Buffer
 err = png.Encode(&amp;amp;b, img)
 if err != nil {
  *errno = 3
  return
 }

 *buf = C.CString(b.String())
 *ln = b.Len()
 *errno = 0
}

func main() {}&lt;/pre&gt;
プログラムとしてはやはり単純。&lt;br /&gt;
Go言語側で戻り値を複数返した場合、Cのヘッダーファイル上で構造体が定義され、それが返されるような形になる。しかし FPC 側でそれを正しく受け取る方法がよくわからないので、今回は素直にポインタを複数渡すことにした。&lt;br /&gt;
&lt;br /&gt;
これを呼ぶためのユニットファイルは以下の様な形。
&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-pascal&quot;&gt;unit psdtest;

{$mode objfpc}{$H+}

interface

procedure PSD2PNG(Filename: PChar; Buf: PPointer; Len: PInteger; Errno: PInteger);
  cdecl; external Name &#39;PSD2PNG&#39;;
procedure Cfree(P: Pointer); cdecl; external Name &#39;free&#39;;

implementation

uses
  Math;

{$linklib libpsdtest.a}
{$linklib libntdll.a}
{$linklib libws2_32.a}
{$linklib libadvapi32.a}
{$linklib libkernel32.a}
{$linklib libmsvcrt.a}

procedure _rt0_amd64_windows_lib(); cdecl; external;

initialization
  SetExceptionMask([exInvalidOp, exPrecision]);
  _rt0_amd64_windows_lib();

end.&lt;/pre&gt;
目新しい点は SetExceptionMask なのだが、これは後述する。&lt;br /&gt;
&lt;br /&gt;
そしてまた新規プロジェクトに今度は TOpenDialog と TImage と TButton をフォーム上に配置して、ボタンのクリックハンドラを以下のように実装。
&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-pascal&quot;&gt;procedure TForm1.Button1Click(Sender: TObject);
var
  Buf: Pointer;
  Len, Errno: integer;
  MS: TMemoryStream;
begin
  if not OpenDialog1.Execute then
    Exit;

  PSD2PNG(PChar(OpenDialog1.FileName), @Buf, @Len, @Errno);
  if Errno &amp;lt;&amp;gt; 0 then
    Exit;
  try
    MS := TMemoryStream.Create;
    try
      MS.WriteBuffer(Buf^, Len);
      MS.Position := 0;
      Image1.Picture.LoadFromStream(MS);
    finally
      MS.Free;
    end;
  finally
    Cfree(Buf);
  end;
end;&lt;/pre&gt;
実行させた結果がこちら。&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBoCwDkqR6LO1l_fWP-_el47qupX2tlHb8b_7KZ-Mt_xkhuRg8s5XfAqc89bpS0hu_bPjLZGIV-uvBI06zxptmAyFwyBRSkYjRQ9su4hXC4-Azlu74nFWgG4FmHF2CuDilAH9Jw2sYSWA/s1600/psd.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;236&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBoCwDkqR6LO1l_fWP-_el47qupX2tlHb8b_7KZ-Mt_xkhuRg8s5XfAqc89bpS0hu_bPjLZGIV-uvBI06zxptmAyFwyBRSkYjRQ9su4hXC4-Azlu74nFWgG4FmHF2CuDilAH9Jw2sYSWA/s320/psd.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;後ろの FireAlpaca で読み込んでいるのと同じ PSD ファイルを読み込んで表示している&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
しかしプログラムを走らせて色々な PSD ファイルを読み込ませていたところ、どうも &lt;a href=&quot;https://golang.org/src/runtime/mgcsweep.go#L372&quot; target=&quot;_blank&quot;&gt;runtime.deductSweepCredit&lt;/a&gt; の中で SIGFPE で動作が停止してしまうことがたまにあった。&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTEG9Imj2tL6OZ9UqGObn1IOa4A87r4SaHJXoz9TCjJDVrUCfYOF1vFvhFyXw1n2r9MyD7aiiFdelTekzcKxDFbDUAVtl1Cmbphlw39r7BOHcF1y531aU1TKMFsX1QSK3h43pUJtuhLN8/s1600/debugger.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;241&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTEG9Imj2tL6OZ9UqGObn1IOa4A87r4SaHJXoz9TCjJDVrUCfYOF1vFvhFyXw1n2r9MyD7aiiFdelTekzcKxDFbDUAVtl1Cmbphlw39r7BOHcF1y531aU1TKMFsX1QSK3h43pUJtuhLN8/s320/debugger.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;止まるときは毎回ここらしい&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
ここだとすると383行目で &lt;code&gt;int64&lt;/code&gt; に変換している辺りの部分でコケているっぽいことになりそうだけど、ちょっと状況がよくわからない。&lt;code&gt;exInvalidOp&lt;/code&gt; をマスクすれば止まらなくなるものの、そもそもなんで止まるのか、普通にGo言語で実行している時は起きているのか、そのへんがよくわからない。&lt;br /&gt;
そういう状態で自分の手には余る感じなので問題は解決できておらず、ひとまずマスクして回避するために &lt;code&gt;SetExceptionMask([exInvalidOp, exPrecision])&lt;/code&gt; を入れている、というわけ。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://s.oov.ch/blog/20160405_psd2png.7z&quot; target=&quot;_blank&quot;&gt;この PSD2PNG プログラムのソースコードとコンパイル済みバイナリはこちら&lt;/a&gt;。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/2075308102775918836'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/2075308102775918836'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2016/04/go-windows-buildmodec-archive.html' title='Go言語の Windows 版で -buildmode=c-archive を使う'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2BZ7vXIO0Qbvb_UKPxVEyOlF4C91tULxthtf7AAg47mLwfKHjYAnI5oghMWcSUimYbtvoWA_7Bzw61Z64TjZ-fDu7yawvkMzFyq-jvynrEn8UQbjv7fF25otoldHiHANCsPvQcFgzv8U/s72-c/screenshot.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-2129282934559909178</id><published>2016-02-17T11:26:00.001+09:00</published><updated>2019-05-12T18:20:45.610+09:00</updated><title type='text'>ブラウザから別のソフトに画像をドラッグ＆ドロップ</title><content type='html'>Windows におけるブラウザでの OLE Drag &amp;amp; Drop に関するメモ書き。&lt;br /&gt;
&lt;br /&gt;
ブラウザで表示している img タグをドラッグアンドドロップで他のソフトに持ち込むことが出来たりすることがある。この仕組みでブラウザからファイルを受け付けたい場合、受け取り側では主に&amp;nbsp;RegisterDragDrop で IDropTarget インターフェイスを登録しておき、呼ばれたら適宜必要な形式で抽出したりすることになる。&lt;br /&gt;
&lt;br /&gt;
ただし、この周辺の挙動はブラウザごとにまちまちであるため、それぞれに対応が必要になる。&lt;br /&gt;
ここではケースごとに各ブラウザでどのように振る舞われているのかを簡単にまとめておく。&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;src に URL を指定した img タグの場合&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
いわゆる普通の img タグの場合。&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Microsoft Internet Explorer 11.63.10586.0&lt;/b&gt;&lt;br /&gt;以下のフォーマットが利用できる。&lt;br /&gt;&lt;br /&gt;CF_TEXT&lt;br /&gt;CF_DIB&lt;br /&gt;CF_UNICODETEXT&lt;br /&gt;CF_HDROP&lt;br /&gt;RegisterClipboardFormat(&quot;HTML Format&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;DragContext&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;UniformResourceLocatorW&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;msSourceUrl&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;UntrustedDragDrop&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;DragImageBits&quot;)&lt;br /&gt;&lt;br /&gt;CF_TEXT / CF_UNICODETEXT / RegisterClipboardFormat(&quot;UniformResourceLocatorW&quot;) は画像の URL が入っている。&lt;br /&gt;RegisterClipboardFormat(&quot;msSourceUrl&quot;) は画像の URL ではなく、ドラッグ元のページの URL らしきものが格納される。&lt;br /&gt;RegisterClipboardFormat(&quot;UntrustedDragDrop&quot;) は&lt;a href=&quot;https://msdn.microsoft.com/ja-jp/library/windows/desktop/bb776902%28v=vs.85%29.aspx?f=255&amp;amp;MSPPError=-2147217396#CFSTR_UNTRUSTEDDRAGDROP&quot; target=&quot;_blank&quot;&gt;この辺に解説がある&lt;/a&gt;。&lt;br /&gt;RegisterClipboardFormat(&quot;HTML Format&quot;) は HTML が画像タグの他にいくつかのタグがセットになった断片で格納されている。HTML の前にヘッダ情報がテキストで付いていて、恐らくこれを利用すると img タグのみを抜き出すこともできるのだと思われる。contentEditable なエレメントで利用される形式のような気がする。&lt;br /&gt;RegisterClipboardFormat(&quot;DragContext&quot;) は未調査。&lt;br /&gt;CF_DIB /&amp;nbsp;RegisterClipboardFormat(&quot;DragImageBits&quot;) には未テストであるものの恐らくデコード済みのビットマップデータが取得可能だと思う。&lt;br /&gt;CF_HDROP からはデコードされていない画像へのパスが取得できる。&lt;br /&gt;&lt;br /&gt;ただし、IE には保護モードがあり、これが ON の場合フォーマットの列挙はできるが読み取ろうとすると API 呼び出しでエラーが起こる。これに関しては&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/bb250462%28v=vs.85%29.aspx?f=255&amp;amp;MSPPError=-2147217396&quot; target=&quot;_blank&quot;&gt;この辺に解説がある&lt;/a&gt;ようだけど細かい部分に関しては未調査。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Microsoft Edge 25.10586.0.0 EdgeHTML 13.10586&lt;/b&gt;&lt;br /&gt;以下のフォーマットが利用できる。&lt;br /&gt;&lt;br /&gt;CF_TEXT&lt;br /&gt;CF_DIB&lt;br /&gt;CF_UNICODETEXT&lt;br /&gt;CF_HDROP&lt;br /&gt;RegisterClipboardFormat(&quot;HTML Format&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;Preferred DropEffect&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;UniformResourceLocatorW&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;msSourceUrl&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;UntrustedDragDrop&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;msSourceTopLevelWindow&quot;)&lt;br /&gt;&lt;br /&gt;IE と概ね同じで、RegisterClipboardFormat(&quot;Preferred DropEffect&quot;) と&amp;nbsp;RegisterClipboardFormat(&quot;msSourceTopLevelWindow&quot;) が増えて&amp;nbsp;RegisterClipboardFormat(&quot;DragContext&quot;) がなくなった。&lt;br /&gt;格納されている内容も IE と同じで、かつ Edge には保護モードがない。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Google Chrome&amp;nbsp;48.0.2564.103&lt;/b&gt;&lt;br /&gt;以下のフォーマットが利用できる。&lt;br /&gt;&lt;br /&gt;CF_TEXT&lt;br /&gt;CF_UNICODETEXT&lt;br /&gt;RegisterClipboardFormat(&quot;FileContents&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;FileGroupDescriptorW&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;HTML Format&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;UniformResourceLocator&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;DragContext&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;UniformResourceLocatorW&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/html&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;DragImageBits&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;chromium/x-renderer-taint&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/x-moz-url&quot;)&lt;br /&gt;&lt;br /&gt;CF_TEXT / CF_UNICODETEXT / RegisterClipboardFormat(&quot;UniformResourceLocator&quot;) / RegisterClipboardFormat(&quot;UniformResourceLocatorW&quot;) / RegisterClipboardFormat(&quot;text/x-moz-url&quot;) は全て URL が取得できる。&lt;br /&gt;RegisterClipboardFormat(&quot;HTML Format&quot;) は IE と同じフォーマットのデータが取得できる。RegisterClipboardFormat(&quot;text/html&quot;) も似たようなデータだが、これは純粋に掴んだ画像タグがそのまま手に入る。&lt;br /&gt;とりあえず&amp;nbsp;RegisterClipboardFormat(&quot;FileContents&quot;) を使うと画像の元データが手に入る。RegisterClipboardFormat(&quot;FileContents&quot;) のデータを読み取る時は&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/bb776902.aspx#CFSTR_FILECONTENTS&quot; target=&quot;_blank&quot;&gt;ここにある&lt;/a&gt;ように&amp;nbsp;FORMATETC 構造体の LINDEX は -1 ではなく 0 から始まるインデックス番号にしなければいけないようだ。また、ファイル名として使うべき名前を&amp;nbsp;RegisterClipboardFormat(&quot;FileGroupDescriptorW&quot;) から知ることができ、複数のファイルがドラッグされる場合は本来ここでの登場順とインデックス番号が対応するものと思われる。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Firefox 44.0.1&lt;/b&gt;&lt;br /&gt;以下のフォーマットが利用できる。&lt;br /&gt;&lt;br /&gt;CF_DIB&lt;br /&gt;CF_UNICODETEXT&lt;br /&gt;CF_HDROP&lt;br /&gt;CF_MAX&lt;br /&gt;RegisterClipboardFormat(&quot;FileContents&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;FileGroupDescriptorW&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;HTML Format&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;Preferred DropEffect&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;DragContext&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;UniformResourceLocatorW&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/html&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;DragImageBits&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;application/x-moz-file-promise-url&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/_moz_htmlinfo&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;application/x-moz-file-promise-dest-filename&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/_moz_htmlcontext&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;application/x-moz-nativeimage&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/x-moz-url&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/x-moz-url-data&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/x-moz-url-desc&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/uri-list&quot;)&lt;br /&gt;&lt;br /&gt;RegisterClipboardFormat(&quot;application/x-moz-nativeimage&quot;) は読み取り時にエラーが起きるので何が入っているのか確認することはできなかった。&lt;br /&gt;CF_UNICODETEXT /&amp;nbsp;RegisterClipboardFormat(&quot;UniformResourceLocatorW&quot;) / RegisterClipboardFormat(&quot;application/x-moz-file-promise-url&quot;) / RegisterClipboardFormat(&quot;text/x-moz-url-data&quot;) /&amp;nbsp;RegisterClipboardFormat(&quot;text/x-moz-url-desc&quot;) /&amp;nbsp;RegisterClipboardFormat(&quot;text/uri-list&quot;) には大体画像の URL っぽいものが入っている。&lt;br /&gt;RegisterClipboardFormat(&quot;HTML Format&quot;) /&amp;nbsp;RegisterClipboardFormat(&quot;text/html&quot;) は Chrome と同じデータ。&lt;br /&gt;RegisterClipboardFormat(&quot;text/_moz_htmlcontext&quot;) には HTML の断片。&lt;br /&gt;CF_DIB / CF_MAX / RegisterClipboardFormat(&quot;DragImageBits&quot;) はビットマップっぽいデータ。&lt;br /&gt;CF_HDROP は元データではなく、元データが BMP ファイルに変換されたもののパスが渡されてくるため、元データが欲しい場合にこれを優先的に処理してはいけない。&lt;br /&gt;未変換の画像データが欲しい場合はやはり Chrome と同じように&amp;nbsp;RegisterClipboardFormat(&quot;FileContents&quot;) と&amp;nbsp;RegisterClipboardFormat(&quot;FileGroupDescriptorW&quot;) を使うのが望ましい。&lt;br /&gt;ただし一つ注意点があって、Firefox では FORMATETC 構造体で&amp;nbsp;TYMED_HGLOBAL を要求して&amp;nbsp;GetData を呼んでも STGMEDIUM 構造体は&amp;nbsp;TYMED_ISTREAM で返ってくる。そのため手抜きで戻ってきたものは全部 TYMED_HGLOBAL だと決め打ちして実装すると Firefox で上手く動かなくなると思われる。あと IStream::Clone は未実装らしい。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;b&gt;src に&amp;nbsp;RFC 2397&amp;nbsp;Data URI scheme&amp;nbsp;を使用した img タグの場合&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
最近だいぶ浸透しつつある&amp;nbsp;data:image/png;base64,... みたいな奴。&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Microsoft Internet Explorer 11.63.10586.0&lt;/b&gt;&lt;br /&gt;以下のフォーマットが利用できる。&lt;br /&gt;&lt;br /&gt;CF_TEXT&lt;br /&gt;CF_DIB&lt;br /&gt;CF_UNICODETEXT&lt;br /&gt;RegisterClipboardFormat(&quot;HTML Format&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;DragContext&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;msSourceUrl&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;DragImageBits&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;UntrustedDragDrop&quot;)&lt;br /&gt;&lt;br /&gt;URL を示す&amp;nbsp;RegisterClipboardFormat(&quot;UniformResourceLocatorW&quot;) がない。&lt;br /&gt;色々なソフトで URL として受け付けることはこの時点で諦めそうな気配がする。&lt;br /&gt;ただし CF_TEXT / CF_UNICODETEXT には Data URI scheme 形式のテキストが入ってくるので、画像データを取得すること自体は普通に可能。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Microsoft Edge 25.10586.0.0 EdgeHTML 13.10586&lt;/b&gt;&lt;br /&gt;以下のフォーマットが利用できる。&lt;br /&gt;&lt;br /&gt;CF_TEXT&lt;br /&gt;CF_DIB&lt;br /&gt;CF_UNICODETEXT&lt;br /&gt;RegisterClipboardFormat(&quot;HTML Format&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;Preferred DropEffect&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;msSourceUrl&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;UntrustedDragDrop&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;msSourceTopLevelWindow&quot;)&lt;br /&gt;&lt;br /&gt;こちらも大体 IE と同じ。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Google Chrome 48.0.2564.103&lt;/b&gt;&lt;br /&gt;以下のフォーマットが利用できる。&lt;br /&gt;&lt;br /&gt;CF_TEXT&lt;br /&gt;CF_UNICODETEXT&lt;br /&gt;RegisterClipboardFormat(&quot;FileContents&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;FileGroupDescriptorW&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;HTML Format&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;UniformResourceLocator&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;DragContext&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;UniformResourceLocatorW&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/html&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/x-moz-url&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;DragImageBits&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;chromium/x-renderer-taint&quot;)&lt;br /&gt;&lt;br /&gt;Chrome は比較的普通のファイルと同じように扱えるよう配慮された挙動をする。&lt;br /&gt;画像データは&amp;nbsp;RegisterClipboardFormat(&quot;FileContents&quot;) で普通に取り出せるし、RegisterClipboardFormat(&quot;FileGroupDescriptorW&quot;) には&amp;nbsp;AQK0eAs+oHYKAAAAAElFTkSuQmCC.png のように Base64 エンコードされている画像データの最後の方に .png をつけたファイル名で格納されている。&lt;br /&gt;また、CF_TEXT / CF_UNICODETEXT /&amp;nbsp;RegisterClipboardFormat(&quot;UniformResourceLocator&quot;) /&amp;nbsp;RegisterClipboardFormat(&quot;UniformResourceLocatorW&quot;) /&amp;nbsp;RegisterClipboardFormat(&quot;text/x-moz-url&quot;) などのように URL が格納されているものには Data URI scheme の画像データが格納されている。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Firefox 44.0.1&lt;/b&gt;&lt;br /&gt;以下のフォーマットが利用できる。&lt;br /&gt;&lt;br /&gt;CF_UNICODETEXT&lt;br /&gt;RegisterClipboardFormat(&quot;FileContents&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;FileGroupDescriptorW&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;HTML Format&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;DragContext&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;UniformResourceLocatorW&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/html&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/x-moz-url&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/x-moz-url-data&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/x-moz-url-desc&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/uri-list&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/_moz_htmlcontext&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;text/_moz_htmlinfo&quot;)&lt;br /&gt;RegisterClipboardFormat(&quot;DragImageBits&quot;)&lt;br /&gt;&lt;br /&gt;Firefox の&amp;nbsp;RegisterClipboardFormat(&quot;FileContents&quot;) は画像ではなく、Data URI scheme へのインターネットショートカット（ファイル名は Untitled.URL）がデータ本体になっているため、これを抜き出してはいけない。&lt;br /&gt;CF_UNICODETEXT や URL が格納されている形式に関しては普通に Data URI scheme でデータが格納されているため、このデータをデコードすることで画像データを取得するのがいいと思われる。&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;b&gt;結局どのようにデータを取得すべきか&lt;/b&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
ドロップ元のプログラムが何なのかを知らずに処理できなければいけないので、概ね以下の順序で取得するプログラムを書くのが取りあえずいいかなと思う。&lt;br /&gt;
なお TYMED_HGLOBAL でデータを要求しても&amp;nbsp;TYMED_ISTREAM で返ってくることが実際にあったので、戻ってくる値が TYMED_HGLOBAL であることを想定して実装してはいけない。&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;RegisterClipboardFormat(&quot;FileContents&quot;) があるなら読んでみて、GIF / JPEG / PNG / WEBP など対応可能な形式ならこれをデータとして受け付ける。形式の認識はファイルの先頭数バイトを見れば&lt;br /&gt;GIF: GIF87a または GIF89a&lt;br /&gt;JPEG: \xff\xd8\xff&lt;br /&gt;PNG: \x89PNG\x0d\x0a\x1a\x0a&lt;br /&gt;WEBP: RIFF????WEBP&lt;br /&gt;などなど、ある程度は判断できるはず。&lt;br /&gt;WebP は一般的な RIFF ヘッダなので ???? にはサイズ情報が入る。&lt;br /&gt;もしファイル名も必要な場合は&amp;nbsp;RegisterClipboardFormat(&quot;FileGroupDescriptorW&quot;) を参照してファイル名を取得する。&lt;br /&gt;ブラウザ以外からもドロップを受け付けるつもりの場合は ANSI 版である RegisterClipboardFormat(&quot;FileGroupDescriptor&quot;) からのファイル名の取得も恐らく試みるべきだろう。&lt;/li&gt;
&lt;li&gt;CF_HDROP があるなら読んでみて、対応可能な形式ならこれをデータとして受け付ける。&lt;br /&gt;これに対応した時点で一般的な色々なソフトからのドロップにも対応できるようになる。&lt;br /&gt;なお、例えば&amp;nbsp;&lt;a href=&quot;https://sevenzip.osdn.jp/&quot; target=&quot;_blank&quot;&gt;7zip&lt;/a&gt; で圧縮済みデータを閲覧中にファイルをドロップしようとした時など、DragEnter で CF_HDROP を渡してくるのにも関わらず、ファイルがまだ実在しないというパターンもある（DragEnter ではなく Drop の時にはファイルはある）。&lt;br /&gt;このような実装になっているのは大容量の圧縮ファイルのドラッグ時のレスポンスを良くするためだと思うが、実際お手本実装とも言える Windows 標準のエクスプローラはこの手のものでも正常に動作するので、対応する価値はあるように思う。&lt;br /&gt;この手のソフトから受け付けることも考慮する場合は拡張子からのファイルタイプ判断が必要になる。&lt;/li&gt;
&lt;li&gt;CF_UNICODETEXT または CF_TEXT があるなら読んでみて、内容が data: で始まる場合は Data URI scheme からのデータ抽出を試みる。&lt;a href=&quot;https://ja.wikipedia.org/wiki/Data_URI_scheme#.E6.9B.B8.E5.BC.8F&quot; target=&quot;_blank&quot;&gt;Wikipedia にも少し書いてある&lt;/a&gt;けど、仕様としては Base64 以外にパーセントエンコーディングが使用されている可能性がある。&lt;br /&gt;ただ画像データをエンコードする際、データの肥大化を考えるとまともなプログラムなら Base64 以外選択肢は実質的にはないと思えるので、Base64 のみ対応でもあまり問題ないかも知れない。&lt;br /&gt;これに対応すると副産物として当然ただのテキストの範囲選択ドロップでも Data URI scheme の形式なら画像として読み込めるようになる。&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
&lt;a href=&quot;http://s.oov.ch/software/oledd_project1_20190512.7z&quot; target=&quot;_blank&quot;&gt;http://s.oov.ch/software/oledd_project1_20190512.7z&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
概ね上記の順番に従って実装したサンプルプログラム。&lt;br /&gt;
&lt;a href=&quot;http://www.lazarus-ide.org/&quot; target=&quot;_blank&quot;&gt;Lazarus&lt;/a&gt; 2.0.0 で作ってある。&lt;br /&gt;
(2019-05-12 追記: CF_HDROP の処理に関するバグを修正)&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
もう少しマイナーなエッジケースも存在するかもしれないが、知っている範囲では今のところ良好。&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/2129282934559909178'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/2129282934559909178'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2016/02/blog-post.html' title='ブラウザから別のソフトに画像をドラッグ＆ドロップ'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-5350576897673194104</id><published>2015-12-31T21:50:00.001+09:00</published><updated>2015-12-31T21:50:19.889+09:00</updated><title type='text'>2015年の一人反省会</title><content type='html'>今年もやったこととかやらなかったこととか色々あったと思うけど、手を出した中でも中途半端な状態で投げ出していて自分でも勿体ないなあと感じているのは、夏の少し前ぐらいに&amp;nbsp;&lt;a href=&quot;http://www.sinsy.jp/&quot; target=&quot;_blank&quot;&gt;Sinsy&lt;/a&gt; を触って遊んでいた奴。&lt;br /&gt;
&lt;br /&gt;
触って遊んでいたと言っても Web インターフェイスから音声を生成していたのわけではなくて、プログラムから叩いて遊んでみていた。&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;img border=&quot;0&quot; height=&quot;283&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipXyBzmj4WK5xO9zCC2otqd2BOh28i4_KWwQz-15FHRtksBYT5X1R4DwyaDn99BWno5njailsGg4Sytpd4INa7QHfyToKkUCNAwCPBp50kT64c44Lps29cDsCvlraCtrOftuTee3EcwJg/s320/c.png&quot; width=&quot;320&quot; /&gt; &lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJeYLfut14QZg3xaFg3eGE0b1fFaGH3e-rybcg3h14lfs8zSvI2lTILBPVJz3Hp22NIVM0IeGjLCpfZQkJmRidq-hT0U2oCz0e3AV1pY3gVR-5ZZkdVFwl_9yKfD5Hpvg_tYaY9EpKiyA/s320/c2.png&quot; width=&quot;200&quot; /&gt;&lt;/div&gt;
&lt;br /&gt;
触り始めた発端は単純で、CeVIO のピアノロールでガイド表示を有効にしている時に 1/32 だと描画内容からは想像ができないレベルで重いので、バグレポートついでに「そんなに重くなるのか？」というのを自分で実装して試してみようということで始めた。&lt;br /&gt;
ただ C# はまともに触ったことがないし、取りあえず C++ で &lt;a href=&quot;http://www.juce.com/&quot; target=&quot;_blank&quot;&gt;JUCE&lt;/a&gt; を使って作業を始めた。&lt;br /&gt;
JUCE も触ったことはなかったものの、API を眺めたことぐらいはあったので精神的な障壁が低めだった。&lt;br /&gt;
&lt;br /&gt;
CeVIO で表示している相当の情報を Sinsy から貰ってきて描画するだけだから専門的な知識がなくても必要な関数をちょちょいと呼べばいけるだろうと高をくくっていたのだが、これが思いの外手間がかかった。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;外部リソースを埋め込めるようにする&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
スクリーンショットでもわかるように、最初から Android もテスト環境として試していた。&lt;br /&gt;
クロスプラットフォームで動くプログラムを作る場合外部リソースの読み込みは結構環境ごとに差があってあまり触れたくない領域で、実際 JUCE でもソースコードに埋め込む手段が標準で用意されているのでそれを使いたかったのだが、ソースコードを少し読んでみると Sinsy::setLanguages とか Sinsy::loadVoices などのインターフェイスが引数にファイル名などを受け取る形しかなくリソースをソースコードに埋め込むことができなかった。&lt;br /&gt;
&lt;br /&gt;
だからまず最初に std::istream からリソースを読み込めるようにオーバーロードを追加して、Sinsy を使う上で外部ファイルに全く依存せずに実行可能な環境を整えることから始めた。&lt;br /&gt;
&lt;br /&gt;
「C ならともかく C++ なら抽象化されたストリームが使えるのに何故そんな構造に」とか「辞書として言語ごとに shift_jis/euc_jp/utf_8 とか文字コードを複数用意するつもりっぽいけど、言語と文字コードは本来別の問題だから『UTF-8 でよこせ』っていう仕様にした方が後々良くない？」とかいくつか疑問があったけど、多分色々事情もあるだろうしそれ自体もまた別の問題だから取りあえず見なかったことにした。&lt;br /&gt;
（特にストリームの話に関しては内部で使われている C の hts_engine API にまで話が及んでいてもう少し面倒臭い話になっている）&lt;br /&gt;
&lt;br /&gt;
ともあれ変更によって全てのリソースの埋め込めるようになった。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;なんかデバッグビルドが重くて辛い&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
試しに Wave ファイルを書き出して出力のテストをしてみていたのだが、デバッグビルドでは Wave 書き出し完了までやたら遅いし、リリースビルドでも CeVIO と比べると何となく遅いような感じがした。&lt;br /&gt;
なぜだろうかと思って少し調べてみたら、どうやら hts_engine API の HTS_alloc_matrix など複数の箇所でループ内で HTS_calloc を大量に呼ぶコードがあり、こういう箇所がかなり足を引っ張っているようだった。&lt;br /&gt;
デバッグビルドでは格段に重くなるのもリーク検出のためかと思うと合点がいく。&lt;br /&gt;
&lt;br /&gt;
HTS_calloc の実装を見てみると #ifdef FESTIVAL でライブラリがあるなら&lt;a href=&quot;http://www.festvox.org/docs/speech_tools-2.4.0/walloc_8c_source.html&quot; target=&quot;_blank&quot;&gt;多分これ&lt;/a&gt;の safe_wcalloc を呼んで、ないなら素直に malloc を呼ぶというコードになっていた。そしてその #ifdef が終わった後には確保した領域を memset で 0 埋めしている。&lt;br /&gt;
このコードだと safe_wcalloc が呼ばれた場合には無駄に memset してしまうし、なぜライブラリがない場合に calloc じゃなくて malloc なんだろうかと、またいくつか疑問はあった。&lt;br /&gt;
&lt;br /&gt;
でも取りあえず1番大きな問題は HTS_calloc の大量呼び出しで、必要なければ頻繁に行うべきではないのは自明なので必要な量のメモリをサイズ計算して一度で全部確保するように変更した。&lt;br /&gt;
こうすると当然開放側でも HTS_free を一度呼べば全て開放できるし。&lt;br /&gt;
&lt;br /&gt;
何箇所かを似たようなアプローチで書き換えてデバッグビルドが耐えられる程度の速度になった。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;表示周りを作るためには拍子から&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
今度は楽譜データを管理する内部構造と、それに伴う GUI 周りを JUCE と共に作り始めた。&lt;br /&gt;
&lt;br /&gt;
最初に手を付けるべきなのは拍子を扱う機構だろうと思い、いわゆるコンダクタートラックを考え始めた。&lt;br /&gt;
というのも、当初の目的ではガイドの線がどの程度の負荷で描画できるのか、というのを試してみることだったので、まずは途中で拍子が変わることに対応できなければいけないと思ったから。&lt;br /&gt;
&lt;br /&gt;
キーと拍子をまとめて扱うクラス（位置は小節単位での絶対位置として管理）を作って、std::deque に突っ込むだけで即席なコンダクタートラックは完成した。&lt;br /&gt;
&lt;br /&gt;
厳密に楽譜を再現する場合にはキーを扱うのに位置情報が小節単位の精度では上手くいかないはずではあるのだが（小節の途中でキーが変わる楽譜もあるから）、キー情報自体ピアノロールに対してはかなり些末な情報と思えるので気にしないことにした。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;ピアノロールの左側にある鍵盤表示&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
この時点でノート情報が存在しないピアノロールをレンダリングするには最低限必要なデータが揃ったので、JUCE で GUI を考え始めた。とはいえ特別オリジナリティのあるインターフェイスにはせず CeVIO に似たところを攻めるだけでいいので、&lt;a href=&quot;http://www.juce.com/doc/classViewport&quot; target=&quot;_blank&quot;&gt;Viewport&lt;/a&gt; と &lt;a href=&quot;http://www.juce.com/doc/classSlider&quot; target=&quot;_blank&quot;&gt;Slider&lt;/a&gt; を組み合わせたコンポーネントでピアノロールのメイン領域を準備しつつ、まずは左側にある鍵盤描画に着手し始めた。&lt;br /&gt;
&lt;br /&gt;
ところで、ピアノロールの鍵盤描画は思っていたより様々なアプローチがあるようでとても面白い。&lt;br /&gt;
ここではピアノロールが回転していることを踏まえ鍵盤の縦の高さのことを「幅」と呼ぶことにする。&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://flstudio-vst.blogspot.jp/2012/06/using-note-colors-in-piano-roll.html&quot; target=&quot;_blank&quot;&gt;FLStudio&lt;/a&gt; や &lt;a href=&quot;https://www.cakewalk.com/Documentation?product=SONAR%20X3&amp;amp;language=3&amp;amp;help=EditingMIDI.03.html&quot; target=&quot;_blank&quot;&gt;SONAR&lt;/a&gt; のインターフェイスでは白鍵の幅がバラバラになっている。&lt;br /&gt;常に黒鍵の中央に白鍵の分かれ目があるものとして扱う場合、こういう鍵盤が生まれる。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/File:B_r_Vocaloid2_Editor.jpg&quot; target=&quot;_blank&quot;&gt;VOCALOID&lt;/a&gt; やこっちの &lt;a href=&quot;http://www.wildpluckings.com/fl-studio-basics/&quot; target=&quot;_blank&quot;&gt;FLStudio&lt;/a&gt; では白鍵も黒鍵も完全に同じ幅として描画している。&lt;br /&gt;鍵盤らしさはないが、ピアノロールのノート表示との相性を考えるとこれも一つの答えだと思う。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://kaymusic-online.com/index.php?%EF%BC%95%EF%BC%9A%E3%83%94%E3%82%A2%E3%83%8E%E3%83%AD%E3%83%BC%E3%83%AB%E3%81%A7%E3%81%AE%E9%9F%B3%E5%85%A5%E5%8A%9B%E3%80%80%E3%81%9D%E3%81%AE%EF%BC%92&quot; target=&quot;_blank&quot;&gt;Domino&lt;/a&gt; や &lt;a href=&quot;http://www.ssw.co.jp/products/ssw/win/sswlt9w/in-depth/midi/index.html&quot; target=&quot;_blank&quot;&gt;Singer Song Writer&lt;/a&gt;(&lt;a href=&quot;http://www.ssw.co.jp/products/ability/in-depth/midi/index.html&quot; target=&quot;_blank&quot;&gt;ABILITY&lt;/a&gt;)、そして &lt;a href=&quot;http://milkcocoa.org/2015/06/02/cevio%E3%81%A8vocaloid%E3%80%81%E3%81%A9%E3%81%A1%E3%82%89%E3%81%8C%E5%84%AA%E3%82%8C%E3%81%9F%E3%83%90%E3%83%BC%E3%83%81%E3%83%A3%E3%83%AB%E3%82%B7%E3%83%B3%E3%82%AC%E3%83%BC%E3%81%AA%E3%81%AE/&quot; target=&quot;_blank&quot;&gt;CeVIO&lt;/a&gt; では恐らく正確な幅で描画している。&lt;br /&gt;Studio One も 3 からはこの形式になったようだ（以前は白鍵バラバラ方式）。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5iH0rZvwSI1guWDGg7RXtp4fpBa8IVOVnyzIV6GnrkZR1pkbHAcVsii3njErzTPUsLgqg2MkwEsKi8MYEsTgJlLEovvm-He2YCAx3XAdXgwThvTLh3RkEX5gjsy3Wk-DXYafULcAKNU0/s1600/c3.png&quot; imageanchor=&quot;1&quot; target=&quot;_blank&quot;&gt;Cubase は個性的な方式を採用&lt;/a&gt;している。&lt;br /&gt;鍵盤描画は正確な描画なのだが、ノートが置かれるエリアでは白鍵が連続するEF、BCの間に余白を挿入することで、白鍵だけを並べた時に等間隔になるようにしている。なおメリットは不明。&lt;/li&gt;
&lt;/ul&gt;
上記一覧で正確な描画としてまとめた中でも恐らく実際には更に方式は細分化されていて、Domino は本当に正確で、他はちょっとだけズルをしているんじゃないかと思う。&lt;br /&gt;
（実際には拡大率とか描画時の座標丸め込みとかの関係で判断難しいのかもしれないけど）&lt;br /&gt;
&lt;br /&gt;
というのも、白鍵を1オクターブの中で配置すると7個で、黒鍵は12等分にしたエリアの 2, 4, 7, 9, 11 番目、ノート配置エリアも同じく12等分したエリアに配置されることになる。&lt;br /&gt;
すると、1オクターブを12等分したノート配置エリアと鍵盤の白鍵の線の位置が一致するのがBC間の一箇所だけになり、EF間の線はかなり惜しい位置にあるものの一致しない（1.0/12.0*5.0 = 0.41666666666666663, 1.0/7.0*3.0 = 0.42857142857142855）。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXYA0QlZzKLED3E-F72eEPyv3uaj8zgZz7RAKRVaQ4zx6G3CkaDyM7ewe4UnOz4MVqWWJUeZyoS0EtjRNCCRvcmuWYKGMru5qoRXBlfZbWtd9YfkHgmAG-iFPzS0fJ7yaa8ZUttjeb9OI/s1600/d.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXYA0QlZzKLED3E-F72eEPyv3uaj8zgZz7RAKRVaQ4zx6G3CkaDyM7ewe4UnOz4MVqWWJUeZyoS0EtjRNCCRvcmuWYKGMru5qoRXBlfZbWtd9YfkHgmAG-iFPzS0fJ7yaa8ZUttjeb9OI/s1600/d.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;文章だとややこしいけど、要するにこういうことだ&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
正確に描画した結果ずれているように見えるのも面白い話ではあるのだが、この合いそうで合わないモヤモヤ感を解消するためには、1オクターブ内で白鍵の幅をCDE と EFGAB で別に扱う必要がある。&lt;br /&gt;
&lt;br /&gt;
まず1オクターブの幅を12で割り、それをノート配置エリアと黒鍵の幅とする。&lt;br /&gt;
そして CDE の幅を 黒鍵の幅*5/3、EFGAB の幅を 黒鍵の幅*7/4 とすれば揃うようになる。&lt;br /&gt;
この幅の差は凄く小さいので見た目上ほとんど正確に見える。&lt;br /&gt;
&lt;br /&gt;
そういう感じでピアノロールの左側にある目安程度の鍵盤の描画にはちょっとしたドラマがあると感じつつ、鍵盤の描画処理を完成させて次の段階へ進むのだった。&lt;br /&gt;
&lt;br /&gt;
ノート配置エリアの描画に関しては特に面白い話はなくて、拍子の変更情報を読みつつ線を引くだけだった。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;ノートの配置&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
まずトラックデータを格納する機構から。&lt;br /&gt;
基本的なアプローチはコンダクタートラックと似た感じになるが、位置情報を PPQN 単位での絶対位置で保持して（4分音符を例えば 480 などにする）、それを基底クラスとし、継承してノートイベントを作る。&lt;br /&gt;
また、コンダクタートラックにまだテンポ情報がないので、同じクラスを継承してテンポイベントも作る。&lt;br /&gt;
これらもやっぱり std::deque に突っ込むだけでそれぞれのトラックとしては基本的には完成。&lt;br /&gt;
&lt;br /&gt;
ところで位置情報に関しては MIDI ファイルの構造を知っているとデルタタイムを参考に相対位置で管理したくなる部分でもあるのだが、絶対位置から相対位置を求める場合は自分のひとつ前との差を見れば済むのに対し、相対位置から絶対位置を求めるには自分より前の全てのイベントを足さないと結果が出せないので、効率を考えると絶対位置で保持するほうがいいだろうと思い、この構造にした。&lt;br /&gt;
絶対位置を符号付き 32bit 整数に格納しても PPQN が 480 の時 2147483647/1920 ≒ 1118481 小節までは進めるし、実際に使う上で困ることはほとんどない。&lt;br /&gt;
&lt;br /&gt;
描画に関しては表示だけなら特に難しいことはなくて、画面外のものを描画しないとか基本的なところを抑えておけばパフォーマンスも問題ないようだった。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Sinsy からタイミング情報などを取得&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
ノート情報を Sinsy に叩き込んで CeVIO の TMG パネルで表示するようなデータを貰おうと思ったら、どうやらその辺のデータを外側に持ち出す機構がなさそうだったので、手を入れる必要があった。&lt;br /&gt;
同じようにやはりピッチやボリュームデータを取り出す仕組みも見当たらないし、取り出す機能がないという事は当然書き換えたデータを代わりに渡す機構もないし、必要になる機能が色々足りない。&lt;br /&gt;
元々ビブラート関係の仕組みはオープンソース版の Sinsy には多分入っていないんだろうとは思うのでその辺は良いとしても、このままではデータの入出力のストリーミングも出来ないし、Sinsy 側での内部処理を理解しなくてもいけるだろうというのは幻想でしかなかったのが浮き彫りになりつつあり、この頃から気分的に盛り下がってくる。&lt;br /&gt;
&lt;br /&gt;
あと元のライブラリとの差分が増えてくるとバージョンアップを取り込む時のマージ作業も面倒くさくなっていくのであまりやりたくない作業でもあった。&lt;br /&gt;
とはいえ、Sinsy や hts_engine API は変更前にローカルの git リポジトリに最初にコミットして、そこから変更分をコミットするようにしているので全部手作業でやるよりはかなりマシなはずではある。&lt;br /&gt;
&lt;br /&gt;
少し変更することでタイミング情報は取れたので、ピアノロール上へのオーバーレイ表示にも取り組み始める。&lt;br /&gt;
小節単位で表示しているピアノロールに実時間ベースで来るタイミングデータをオーバーレイ表示するのは地味に面倒な部分ではあるものの、テンポ情報を見ながら愚直に計算する以外の方法が見つからないのでそのように作った。&lt;br /&gt;
&lt;br /&gt;
タイミング情報が描画できればピッチ情報も近からず遠からずみたいな感じなので同じように描画処理を作った。JUCE の Graphics::strokePath を使うとアンチエイリアシングもされて、線も滑らかに。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;作業に一旦の区切り&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
ここまでの時点でノートの追加、範囲選択、移動、削除などはできるものの歌詞入力は未実装（日本語入力周りの関係で手間が掛かりそうだったから）、ピッチの書き換えや反映を未実装、ボリュームの取得や書き換えや反映を未実装、オーディオデータの再生も未実装。&lt;br /&gt;
この辺りでだいぶ興味が薄れてきてノート編集周りの挙動を気持ちよくなるようにしたりとかしながら、段々ソースコードを触らなくなっていった。&lt;br /&gt;
&lt;br /&gt;
ここからは未着手なので推測だけども、ピアノロールで位置を指定して再生するためには波形の任意の位置からの再生ができなければいけない。&lt;br /&gt;
例えば10分ぐらいのシーケンスがあって、最初の5秒が欲しい場合は頭のほうだけを処理したいし、シーケンスの中央辺りを編集した時は頭の方を処理し直す必要がなさそうに思えるし、こういう部分で不要な処理を削り取りたい。&lt;br /&gt;
今回のプログラムはノートを変更した時など再生対象になるシーケンスが確定したタイミングで裏で別スレッドで生成を始めておいてシームレスに更新する仕組みにはしているけど、ラグ自体は解消しないからタイミング次第では改善しないし、VST とかでの連携を視野に入れると再生開始時に少し音が出ないとか微妙な挙動にもなりそうだし、この辺をもう少し上手く動くようにしたかった。&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
そのため Sinsy で任意の位置からのストリーミング再生をする方法を探っていたのだが、HtsEngine::synthesize の中で使われている&amp;nbsp;HTS_Engine_get_generated_speech はその名の通り既に生成済みの波形データをコピーしてくるだけなので、これではシーケンス全体のサイズに関わらず一定に近い処理量で任意位置から再生することはできそうになかった。&lt;br /&gt;
&lt;br /&gt;
どうやら肝になるのはその前に使われる HTS_Engine_synthesize_from_strings が呼ぶ HTS_Label_load_from_strings と HTS_Engine_synthesize で、負荷のバランスを見た限りでは HTS_Label_load_from_strings は軽くはないけど HTS_Engine_synthesize に比べれば所要時間は短い。&lt;br /&gt;
なので重要そうなのは HTS_Engine_synthesize で行われる3パスの波形生成処理で、ここを細かい単位で複数回に分けて実行できるように、更に可能であれば途中からの実行、無理でも前回からの変更差分のみを最低限の範囲で再処理できるようにしたかった。&lt;br /&gt;
&lt;br /&gt;
そう思って少しずつソースコードを読んでみたりはしたものの、基本的に必要となる前提知識が足りないというか無いので各処理がどんな役割があり変わるとどうなるのかがわからず、たいへん手を出しにくい（実際出していない）。&lt;br /&gt;
&lt;br /&gt;
そういうわけで今回は自分にとっては大きな課題を残したまま放置することになった。&lt;br /&gt;
基本的な部分の知識を持っていないが故に瑣末な問題が気になるんだろうとも思った。&lt;br /&gt;
&lt;br /&gt;
そんな感じで色々な部分が大量に残っているおもちゃ以下のプログラムなのだが、それでも当初の目的だった「ガイド表示の線が増えると重いのか」というのに関してはやっぱりそんなに重くならないということで自分の中では落ち着きつつある。&lt;br /&gt;
ただ少なくとも JUCE のレンダラでは線を引く時に素直に引くとアンチエイリアシングの影響で地味に重くて、代わりに fillRect で細長い長方形を描くと速度が出ることがわかった。&lt;br /&gt;
CeVIO は点線で描画してるけどそれで重くなるのだろうか。その辺は調べていないので謎。&lt;br /&gt;
&lt;br /&gt;
ちゃんと記録取ってなかったので正確なところはわからないものの、ここまでで作業開始から多分1～2ヶ月程度経っている。ライブラリの仕様がわからない状態から始めた割には良く出来たほうかな、とは思っている部分も少しあるが、&lt;a href=&quot;http://mari-on.appspot.com/&quot; target=&quot;_blank&quot;&gt;MARI-ON&lt;/a&gt; を作った時は1ヶ月で完成まで辿り着いているので良く出来てないような気もする。色々な面で違っててあんまり比較にはならないけど。&lt;br /&gt;
&lt;br /&gt;
JUCE は標準の状態では日本語が豆腐になるし Android 対応はこの時点では実験的な状態でもちろん日本語は入力できないし、問題は少なくなかったので実は結構その辺にもじわじわと時間を取られた。&lt;br /&gt;
しかも OpenGL ベースのレンダラを使わないと Android では描画速度も結構厳しくて、使うとそれはそれで他の問題も起こるし、楽譜に対する入力効率も含めやはりPCとスマホやタブレットで同じプログラムの流用は使い物にならなそうなのが実情かなぁというぼんやりとした印象だった。&lt;br /&gt;
&lt;br /&gt;
あと Sinsy のオープンソース版は他のプログラムから呼んで使うためのものというよりはやっぱり技術デモという感じなのかな、というのがちょっと触ってみた感じでの印象だった。&lt;br /&gt;
&lt;br /&gt;
少し前に JUCE は 4.x 系も出たし、年末に Sinsy と hts_engine API もバージョンアップしたみたいなので、また気が向いたら弄くり回してみようと思う。&lt;br /&gt;
&lt;br /&gt;
2015年の一人反省会は大体こんな感じ。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/5350576897673194104'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/5350576897673194104'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2015/12/2015.html' title='2015年の一人反省会'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipXyBzmj4WK5xO9zCC2otqd2BOh28i4_KWwQz-15FHRtksBYT5X1R4DwyaDn99BWno5njailsGg4Sytpd4INa7QHfyToKkUCNAwCPBp50kT64c44Lps29cDsCvlraCtrOftuTee3EcwJg/s72-c/c.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-2955058682383238566</id><published>2015-12-06T23:54:00.000+09:00</published><updated>2015-12-08T17:54:13.069+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="golang"/><category scheme="http://www.blogger.com/atom/ns#" term="Go言語"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><title type='text'>Go 言語用 SQL クエリービルダーを作ってみた話</title><content type='html'>これは Go Advent Calendar 2015 とは何の関係もない記事です。&lt;br /&gt;
&lt;br /&gt;
Go 言語用の SQL クエリービルダーとしては &lt;a href=&quot;https://github.com/elgris/golang-sql-builder-benchmark&quot; target=&quot;_blank&quot;&gt;https://github.com/elgris/golang-sql-builder-benchmark&lt;/a&gt; でベンチマークされているものが有名どころのようなのだが、SQLite に対応した純粋なクエリービルダーはなさそうだったのと、「自分で作ってみるのも勉強になるかな」と思い、自前の SQL クエリービルダーを作ってみることにした。&lt;br /&gt;
（あとこのベンチマークは dbr が 2.0 になる前のものっぽいので内容も少し古い）&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://github.com/oov/q&quot; target=&quot;_blank&quot;&gt;https://github.com/oov/q&lt;/a&gt;&lt;br /&gt;
実際に作ったのはこちら。&lt;br /&gt;
&lt;br /&gt;
パッケージ名に1文字はちょっと短すぎると自分でも思うけど、必要なら好きな名前を当てればいいし大きな問題ではないだろうと思ってそのまま進めることにした。&lt;br /&gt;
実際のコードでは &lt;code&gt;q, args := builder.ToSQL()&lt;/code&gt; みたいなノリで &lt;code&gt;q&lt;/code&gt; を使いたくなるだろうけど、&lt;code&gt;SelectBuilder.ToSQL&lt;/code&gt;&amp;nbsp;で出力してしまえばもうパッケージとしての &lt;code&gt;q&lt;/code&gt; にアクセスする必要性は薄いし、また続きのコードでアクセスしたい場合は &lt;code&gt;q&lt;/code&gt; という変数名はちょっと可読性が微妙だろうという気もするので問題ない。という言い訳も自分にした。&lt;br /&gt;
&lt;br /&gt;
パッケージの話の前に作ろうと思うに至った話を少し。&lt;br /&gt;
&lt;br /&gt;
元々作ろうと思った発端としては &lt;a href=&quot;https://github.com/oov/sqruct/blob/master/cmd/sqruct/example/sqruct.yml&quot; target=&quot;_blank&quot;&gt;YAML に Go の構造体と SQL とのマッピング情報を書いて&lt;/a&gt;、Go のソースコードを&lt;a href=&quot;https://godoc.org/github.com/oov/sqruct/cmd/sqruct/example/mdl&quot; target=&quot;_blank&quot;&gt;自動生成する&lt;/a&gt;ようなツールを試しに作ってみていた時の話になる。&lt;br /&gt;
この手のツールも似たようなものが結構あるのだが、Go の構造体にタグをゴリゴリ書いていくとか、使う前にコードでマッピング情報を追加するとか、使い方を覚える自信があまりなくて個人的には結構精神的な障壁が大きかった。&lt;br /&gt;
そういう感じで既存のは実際に使うところまでは気が乗ってこなくて、あと作ると勉強になりそうというのもあって自作してみることにしたのだった。&lt;br /&gt;
最初は &lt;a href=&quot;https://github.com/jmoiron/sqlx&quot; target=&quot;_blank&quot;&gt;github.com/jmoiron/sqlx&lt;/a&gt;&amp;nbsp;を使っていたけど途中で必要ないかもと思って使うのやめたりとか&lt;a href=&quot;https://github.com/oov/sqruct/blob/master/gen/tpl.go&quot; target=&quot;_blank&quot;&gt;ソース生成用テンプレートがカオスになった&lt;/a&gt;りしつつなんとか基本的な動作はできたが、生成されたコード内で SQL は文字列で直書きされるようになっていて、ちょっとだけ条件を加えたクエリーを投げるような柔軟性が持たせられなかった。&lt;br /&gt;
「なるほど、これは後からの改変に耐えられるようにクエリービルダー的なものを経由しないとダメかな？」と思ったところで今回のパッケージを作るに至った。&lt;br /&gt;
クエリービルダーもこのジェネレータと同じパッケージに含めようか迷ったのだが、一緒にした時のメリットが使う時に import 文がまとまる以外特に思いつかず、本質的には別のパッケージなのでやめた。&lt;br /&gt;
&lt;br /&gt;
あと、&lt;a href=&quot;http://bl.oov.ch/2015/11/go-docker.html&quot; target=&quot;_blank&quot;&gt;前回の記事&lt;/a&gt;で &lt;a href=&quot;https://github.com/oov/dockertest&quot; target=&quot;_blank&quot;&gt;Docker コンテナを使うテストを書きやすくするためのパッケージ&lt;/a&gt;を作ったんだけども、これもこの辺の話に関連していて MySQL、PostgreSQL、SQLite での動作を確認するためになるべく手間が減らせるように作ったのだった。&lt;br /&gt;
（しかし肝心のテストはかなり薄い）&lt;br /&gt;
&lt;br /&gt;
今回のパッケージの話に戻す。&lt;br /&gt;
&lt;br /&gt;
q を作る上でのテーマは、短く書くためだけにショートハンドメソッドなどをガツガツ増やしたりせず、なるべく素直な形に実装にしようというところ。とはいえやり過ぎると SQL を書くのにクッソ面倒くさくて結局使わなくなるだろうし、色々考えて良い落とし所を探ったつもりではある。&lt;br /&gt;
本当はあんまり &lt;code&gt;reflect&lt;/code&gt; とか &lt;code&gt;type switch&lt;/code&gt; とかも使いたくなかったけど、いくつかやむを得ず使った。でもあまり欲張らなかったので、&lt;code&gt;import&lt;/code&gt; するのは &lt;code&gt;fmt&lt;/code&gt; と &lt;code&gt;reflect&lt;/code&gt; だけで済んだ。&lt;br /&gt;
&lt;br /&gt;
使用例としては大体こんな感じ。&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;user := q.T(&quot;user&quot;)
sel := q.Select().From(
&amp;nbsp; &amp;nbsp; user,
).Column(
&amp;nbsp; &amp;nbsp; user.C(&quot;id&quot;),
&amp;nbsp; &amp;nbsp; user.C(&quot;name&quot;),
).Where(
&amp;nbsp; &amp;nbsp; q.Eq(user.C(&quot;age&quot;), 18),
)
fmt.Println(sel) // SELECT &quot;user&quot;.&quot;id&quot;, &quot;user&quot;.&quot;name&quot; FROM &quot;user&quot; WHERE &quot;user&quot;.&quot;age&quot; = ? [18]&lt;/code&gt;&lt;/pre&gt;
簡単に説明すると、&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SELECT&lt;/code&gt; 文をつくるには &lt;code&gt;q.Select&lt;/code&gt;&lt;br /&gt;&lt;code&gt;q.Select(&quot;SELECT SQL_NO_CACHE&quot;)&lt;/code&gt; みたいなことも一応できる&lt;/li&gt;
&lt;li&gt;テーブルを作るには &lt;code&gt;q.T&lt;/code&gt;&lt;br /&gt;クエリービルダーにも同じ名前のメソッドがあり、サブクエリーを FROM に書きたい場合などに使える&lt;/li&gt;
&lt;li&gt;カラムを作るには &lt;code&gt;q.C&lt;/code&gt; か、&lt;code&gt;Table.C&lt;/code&gt; か、&lt;code&gt;Expression.C&lt;/code&gt;&lt;br /&gt;&lt;code&gt;q.C&lt;/code&gt; の場合は &lt;code&gt;&quot;column&quot; table.C&lt;/code&gt; の場合は &lt;code&gt;&quot;table&quot;.&quot;column&quot;&lt;/code&gt; のような形になる&lt;/li&gt;
&lt;li&gt;JOIN にも対応&lt;br /&gt;テーブルにあるメソッドを呼ぶことで使用可能&lt;/li&gt;
&lt;/ul&gt;
といった感じで、他のクエリービルダーに比べて文字列で直接指定できる部分は結構少ないはず。&lt;br /&gt;
そのためテーブル名やカラム名に関してはキッチリエスケープされる。&lt;br /&gt;
&lt;br /&gt;
ただしこれだけだとパッケージに準備されていない色々なことができないので、SQL の破片を直書きできる &lt;code&gt;q.Unsafe&lt;/code&gt; もあり、概ね &lt;code&gt;fmt.Print&lt;/code&gt; と同じ使い勝手で以下のように使うことができる。
&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;&lt;code&gt;user := q.T(&quot;user&quot;, &quot;u&quot;)
age := user.C(&quot;age&quot;)
sel := q.Select().From(user).Column(
&amp;nbsp; &amp;nbsp; q.C(q.Unsafe(`SUM(CASE WHEN (`, age, ` &amp;gt;= 13)AND(`, age, ` &amp;lt;= 19) THEN 1 ELSE 0 END)`), &quot;teen&quot;),
&amp;nbsp; &amp;nbsp; q.C(q.Unsafe(`SUM(CASE WHEN `, age, ` &amp;gt;= 20 THEN 1 ELSE 0 END)`), &quot;adult&quot;),
)
fmt.Println(sel) // SELECT SUM(CASE WHEN (&quot;u&quot;.&quot;age&quot; &amp;gt;= 13)AND(&quot;u&quot;.&quot;age&quot; &amp;lt;= 19) THEN 1 ELSE 0 END) AS &quot;teen&quot;, SUM(CASE WHEN &quot;u&quot;.&quot;age&quot; &amp;gt;= 20 THEN 1 ELSE 0 END) AS &quot;adult&quot; FROM &quot;user&quot; AS &quot;u&quot; []&lt;/code&gt;&lt;/pre&gt;
※ただしこの例で出てくる CASE 文は本当は &lt;code&gt;q.Unsafe&lt;/code&gt; を使わなくても組むこともできる。&lt;br /&gt;
&lt;br /&gt;
このように文字列が直接 SQL として書き出されるため外部からの入力を直接埋め込むのは大変危険なのだが、その場合には &lt;code&gt;q.V&lt;/code&gt; で外部からの入力を囲うことで、プレースホルダーに置換させることもできる。&lt;br /&gt;
&lt;br /&gt;
このような構造にしておいたら外部ツールで解析して危なそうな箇所を見つけられるかな？　と妄想している。文字列リテラルでも &lt;code&gt;Expression&lt;/code&gt; でもないものを渡してる箇所を検出するみたいな感じで。&lt;br /&gt;
簡単にやるなら &lt;code&gt;grep&lt;/code&gt; で &lt;code&gt;q.Unsafe&lt;/code&gt; を探すのでもいいけど。&lt;br /&gt;
&lt;br /&gt;
あと &lt;a href=&quot;https://godoc.org/github.com/oov/q#pkg-index&quot; target=&quot;_blank&quot;&gt;godoc で目次がある程度読みやすくなるよう&lt;/a&gt;に考えて作ってみた。&lt;br /&gt;
&lt;code&gt;Function&lt;/code&gt; タイプのように実質的に中身が一緒でも、敢えて別名にすることで一覧を整理したりとか。&lt;br /&gt;
英語は全然できないんだけど English &amp;gt; Broken English &amp;gt; Non-English Language という話を見かけて「確かにな」と思ったのでそういう方針で、あと Example を沢山用意することで言語を超えて意図を伝えようというのも試みている。&lt;br /&gt;
Go 言語の場合 Example を書くことで出力内容の検証もやってくれるようになるので一石二鳥だった。&lt;br /&gt;
&lt;br /&gt;
小さいパッケージなので話としては大体こんな感じ。&lt;br /&gt;
今後も &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;INSERT&lt;/code&gt; 文辺りへの対応とか、あと日時周りの計算とかもプラットフォーム依存しないように使えるメソッド増やせるといいかもなあ。詳しくないけど。&lt;br /&gt;
&lt;br /&gt;
まだ作ってみたという段階で、これから発端のプロジェクトで使うことでドッグフーディングしていく。そこで使った感触を元にまたドラスティックな変更をするかも知れないけど、ひとまず現状こんな感じ。&lt;br /&gt;
&lt;br /&gt;
---- 2015-12-08 追記&lt;br /&gt;
&lt;br /&gt;
全然まだテストしてないけど UPDATE と DELETE も一応組めるようになった。&lt;br /&gt;
あと type switch を減らせそうなところを減らした。&lt;br /&gt;
プライベートな型とかインターフェイスとかメソッドも結構 public にしてみたので、足りない関数とか諸機能を外部のパッケージで作って組み合わせて使うようなやり方もいけるかも知れない。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/2955058682383238566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/2955058682383238566'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2015/12/go-sql.html' title='Go 言語用 SQL クエリービルダーを作ってみた話'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-856883338510672377</id><published>2015-11-19T07:38:00.000+09:00</published><updated>2015-12-07T03:11:29.735+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="golang"/><category scheme="http://www.blogger.com/atom/ns#" term="Go言語"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><title type='text'>Go 言語で Docker を使うテストを書く時</title><content type='html'>日記。&lt;br /&gt;
Docker コンテナ内で立ち上げたサーバーに繋ぐテストを書く時の話。&lt;br /&gt;
テスト開始時にコンテナを立ち上げて、テストして、終了時に落とすみたいな奴。&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;コマンドや API を叩いてコンテナの実行が始まった直後に繋ぎに行くと、まだコンテナ内で &lt;code&gt;listen&lt;/code&gt; が始まっていないのが原因で失敗する、ということがしばしば起こる。&lt;br /&gt;
この時の対応策は色々あるだろうけども、１番安直な方法は恐らく「成功するまで&amp;nbsp;&lt;code&gt;sleep&lt;/code&gt;&amp;nbsp;とリトライを繰り返す」という類のものだと思う。&lt;br /&gt;
（コンテナ自体が不安定な場合は起動失敗してないか調べるのも必要そう）&lt;br /&gt;
&lt;br /&gt;
この sleep の部分を「&lt;code&gt;netstat&lt;/code&gt;&amp;nbsp;の出力で&amp;nbsp;&lt;code&gt;listen&lt;/code&gt; しているか調べる」という、これまた安直な方法で実現すれば、汎用的に使えるリトライ不要な手段になるのではないだろうか？　と思い、少し作ってみることにした。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/oov/dockertest&quot; target=&quot;_blank&quot;&gt;https://github.com/oov/dockertest&lt;/a&gt;&lt;br /&gt;
コードの一部は&lt;a href=&quot;https://ericchiang.github.io/go/tests/testing/docker/2015/09/14/test-with-docker.html&quot; target=&quot;_blank&quot;&gt;この記事&lt;/a&gt;を参考にさせてもらった。&lt;br /&gt;
&lt;br /&gt;
パッケージを作る前に手作業で試してみた時点で「コンテナ内に &lt;code&gt;netstat&lt;/code&gt;&amp;nbsp;自体が存在しない」という理由で上手くいかず/proc/net/tcp などを見に行くようにアプローチを変えざるを得なかったものの、Docker Hub にあるオフィシャルの MySQL, PostgreSQL, Redis, MongoDB と、dnsmasq(&lt;a href=&quot;https://hub.docker.com/r/andyshinn/dnsmasq/&quot; target=&quot;_blank&quot;&gt;andyshinn/dnsmasq&lt;/a&gt;) に繋ぐのを試した限りでは概ね成功した。&lt;br /&gt;
&lt;br /&gt;
概ね、というのは PostgreSQL は &lt;code&gt;listen&lt;/code&gt; が始まった直後だと &lt;code&gt;&quot;the database system is starting up&quot;&lt;/code&gt; と言われて接続に失敗することがあり、結局リトライする処理が必要になったため。&lt;br /&gt;
他のプログラムでも設定などによっては &lt;code&gt;listen&lt;/code&gt; したあと更に準備時間が必要なケースもあるかも知れないので、結果的には思惑ほど全て上手くは行かなかったことになる。ちょっと残念。&lt;br /&gt;
&lt;br /&gt;
環境変数 &lt;code&gt;DOCKER_HOST&lt;/code&gt; の有無も考慮して動くようにしたおかげでテストに手を加えなくても Windows 環境でも Docker Quickstart Terminal とか boot2docker を経由してテストを実行できるし、パッケージとしては作ってよかったと思う。&lt;br /&gt;
&lt;br /&gt;
ところで、普段 Go 言語を使って遊んでいるのは自宅サーバの Ubuntu で作曲とかに使うマシンは Windows なんだけど、このテストを両方の環境で実行したら思っていたより速度差が大きかった。&lt;br /&gt;
&lt;br /&gt;
CPU: Intel Atom D510 1.66GHz / Memory: 4GB / OS: Ubuntu 14.04&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;=== RUN &amp;nbsp; Example_dnsmasq
--- PASS: Example_dnsmasq (2.98s)
=== RUN &amp;nbsp; Example_mongoDB
--- PASS: Example_mongoDB (4.61s)
=== RUN &amp;nbsp; Example_mySQL
--- PASS: Example_mySQL (55.38s)
=== RUN &amp;nbsp; Example_postgreSQL
--- PASS: Example_postgreSQL (17.30s)
=== RUN &amp;nbsp; Example_redis
--- PASS: Example_redis (2.46s)&lt;/pre&gt;
CPU: Intel Core i7-4790K 4.00GHz / Memory: 32GB / OS: Windows 10&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;=== RUN &amp;nbsp; Example_dnsmasq
--- PASS: Example_dnsmasq (1.08s)
=== RUN &amp;nbsp; Example_mongoDB
--- PASS: Example_mongoDB (0.87s)
=== RUN &amp;nbsp; Example_mySQL
--- PASS: Example_mySQL (6.91s)
=== RUN &amp;nbsp; Example_postgreSQL
--- PASS: Example_postgreSQL (4.49s)
=== RUN &amp;nbsp; Example_redis
--- PASS: Example_redis (0.96s)&lt;/pre&gt;
&lt;br /&gt;
普段の環境で毎回1分近く掛かるのは結構辛いし、テスト開始時にコンテナを起動するか、名前をつけておいた起動済みコンテナを利用するのか自動で判断する仕組みが必要かもなぁ、と思ったのだった。&lt;br /&gt;
&lt;br /&gt;
---- 追記 2015-11-20&lt;br /&gt;
&lt;br /&gt;
起動済みのコンテナを再利用する仕組みを追加したところ、遅い環境でも1秒程度で終わるようになった。&lt;br /&gt;
名前しかチェックしていないのでイメージやパラメータが違う状態で起動しているのも利用できてしまったりするけど、厳密にやり過ぎても使いにくいかも知れないのでその辺りは緩めにしておくことにした。&lt;br /&gt;
同じパラメータで起動したい時は &lt;code&gt;&lt;a href=&quot;https://godoc.org/github.com/oov/dockertest#Container.RunArgs&quot; target=&quot;_blank&quot;&gt;Container.RunArgs&lt;/a&gt;&lt;/code&gt;&amp;nbsp;を利用するとコピペできて便利。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/856883338510672377'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/856883338510672377'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2015/11/go-docker.html' title='Go 言語で Docker を使うテストを書く時'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-4558901820327077671</id><published>2015-10-16T23:14:00.000+09:00</published><updated>2015-10-16T23:15:38.145+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="golang"/><category scheme="http://www.blogger.com/atom/ns#" term="Go言語"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><title type='text'>goji + goth で OAuth ログイン</title><content type='html'>ただの日記。&lt;br /&gt;
&lt;br /&gt;
何となく &lt;a href=&quot;https://golang.org/&quot; target=&quot;_blank&quot;&gt;Go 言語&lt;/a&gt;で &lt;a href=&quot;https://goji.io/&quot; target=&quot;_blank&quot;&gt;goji&lt;/a&gt; を使って OAuth クライアントとして外部サービスにログインして戻ってくる流れを作ってみようと思ったんだけど、実際書き始めてみると思っていたよりも迷ったので一応書き残しておく。&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;b&gt;認証用ライブラリの選択&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
まず便利そうなパッケージをいくつかピックアップしてきた。&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/stretchr/gomniauth&quot; target=&quot;_blank&quot;&gt;github.com/stretchr/gomniauth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/markbates/goth&quot; target=&quot;_blank&quot;&gt;github.com/markbates/goth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dghubble/gologin&quot; target=&quot;_blank&quot;&gt;github.com/dghubble/gologin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
取りあえず自分がよく使う Twitter と Google+ アカウントでのログインを使いたいので、OAuth 1.x に対応していない gomniauth を除外。&lt;br /&gt;
次に gologin を見てみると、これは &lt;a href=&quot;https://godoc.org/golang.org/x/net/context&quot; target=&quot;_blank&quot;&gt;golang.org/x/net/context&lt;/a&gt;&amp;nbsp;を活用したつくりになっていて結構良さそうに見える。ただ複数のサービスにログインできるようにするためには何気にコード量が多くなりそうなのが若干気になる。&lt;br /&gt;
最後に goth を見てみると、サンプルコードひとつで複数サービスへのログインを想定した作りになっていた。&lt;br /&gt;
&lt;br /&gt;
今回はサンプルコードが何となく想像していた構造に近そうな goth にしてみることにした。&lt;br /&gt;
コードが短いのは結局のところ取り捨て選択に寄るもので細かい融通が利かないことも少なくないので、やりたいことや相性によって良くも悪くもなるので判断が難しい。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;サンプルコードを元に goji 用に書き直す&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/markbates/goth/blob/master/examples/main.go&quot; target=&quot;_blank&quot;&gt;これ&lt;/a&gt;を単純に goji で置き換える形で書いてみたのがこちら。この段階ではまだ動いてない。
&lt;br /&gt;
&lt;div&gt;
&lt;script src=&quot;https://gist.github.com/oov/613a3c3e7973c4a7fb4f/98100792a84ad47b60f168df7bed677c39595403.js&quot;&gt;&lt;/script&gt;&lt;/div&gt;
動かすと「you must select a provider」というメッセージが表示されるので、URL から「twitter」などのプロバイダ名が取得できていないことが容易に推測できる。&lt;br /&gt;
まあ書き直している時点で動かなくなることが想定済みだったけども。&lt;br /&gt;
&lt;br /&gt;
じゃあそもそもプロバイダ名をどうやって取得しているのか？　というのをまず確認することにした。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/markbates/goth/blob/master/gothic/gothic.go#L161&quot; target=&quot;_blank&quot;&gt;https://github.com/markbates/goth/blob/master/gothic/gothic.go#L161&lt;/a&gt;&lt;br /&gt;
該当の処理はここで、&lt;code&gt;*http.Request&lt;/code&gt; から URL のクエリ部分を抜き出し &lt;code&gt;&quot;provider&quot;&lt;/code&gt; あるいは &lt;code&gt;&quot;:provider&quot;&lt;/code&gt; という名前のパラメータを取得しようとしていた。&lt;br /&gt;
先頭にコロンがついたものは元々のコードで使われていた &lt;a href=&quot;https://github.com/gorilla/pat&quot; target=&quot;_blank&quot;&gt;github.com/gorilla/pat&lt;/a&gt; が独自に追加しているパラメータで、&lt;code&gt;&quot;/auth/{provider}&quot;&lt;/code&gt; のようなパターンでハンドラを登録しておくと追加されるらしい。&lt;br /&gt;
&lt;br /&gt;
そのすぐ上にある &lt;code&gt;GetProviderName&lt;/code&gt; を外部から差し替える事でこの振る舞いを変えることもできるようだ。&lt;br /&gt;
ところで goji では &lt;code&gt;&quot;/auth/:provider/callback&quot;&lt;/code&gt; のようなパターンでハンドラ登録をしておくと&amp;nbsp;&lt;a href=&quot;https://godoc.org/github.com/zenazn/goji/web#C&quot; target=&quot;_blank&quot;&gt;web.C.URLParams&lt;/a&gt; から URL のパラメータを取得できる仕組みになっているのだが、&lt;code&gt;GetProviderName&lt;/code&gt; には &lt;code&gt;*http.Request&lt;/code&gt; しか渡されないためそのままでは取得できず、微妙に意思の疎通ができていない感じになっている。&lt;br /&gt;
&lt;br /&gt;
この時点で比較的アクロバティックでない手段として考えついたのは以下の様なものだった。&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GetProviderName&lt;/code&gt; を差し替えて、自力で URL のパスからプロバイダ名を抽出する&lt;/li&gt;
&lt;li&gt;pat の仕様に準拠するように、独自にパラメータを追加する&lt;/li&gt;
&lt;li&gt;URL を &lt;code&gt;/auth?provider=twitter&lt;/code&gt; のような形に変更する&lt;/li&gt;
&lt;/ul&gt;
候補としては一番最後のが愚直ながらも収まりの良さそうな解決方法ではあるのだが、この場合コールバック先 URL にも同じように GET パラメータを付与させておく必要があり、ユーザーがログイン成功後こちらに戻ってくるためにはログイン先のサービス側で GET パラメータの連結をしてもらわないといけなくなる。&lt;br /&gt;
それに失敗してログインできないような大手サービスがあるとはあまり思えないものの、可能なら避けたい気もするので今回は選ばなかった。&lt;br /&gt;
&lt;br /&gt;
次に、pat に合わせる形で済ませると簡単そうかと思ったのでこれを考えてみた。&lt;br /&gt;
というのも最初は「もしかして &lt;code&gt;req.URL.Query().Get(&quot;provider&quot;)&lt;/code&gt; を &lt;code&gt;Set&lt;/code&gt; に変えるだけで簡単に割り当ても出来るのでは？」と思ったのだが、どうやらこれは &lt;code&gt;Query&lt;/code&gt; のタイミングで&lt;a href=&quot;https://golang.org/src/net/url/url.go?s=22523:22551#L836&quot; target=&quot;_blank&quot;&gt;毎回 &lt;code&gt;URL.RawQuery&lt;/code&gt; から生成されている&lt;/a&gt;ようなので、直接 &lt;code&gt;RawQuery&lt;/code&gt; を書き換えないといけないようだ。&lt;br /&gt;
そうなると &quot;Raw&quot; Query なのに実際には改ざん済みという状況を作らないといけなくて、個人的にこの仕様はあまり好きではないので除外した。&lt;br /&gt;
（もちろん、もし &lt;code&gt;Set&lt;/code&gt; で割り当てができたとしても、結果としては &lt;code&gt;RawQuery&lt;/code&gt; とは異なる状態を持つか &lt;code&gt;RawQuery&lt;/code&gt; も伴って変更されるかどちらかだと思うので、どちらにしても受け入れがたいものではあった）&lt;br /&gt;
&lt;br /&gt;
結局最後に残ったのは URL を自分で切り分けて検出するパターン。&lt;br /&gt;
しかしこれを自力でやるとせっかく goji がパースしてくれていたのに改めてパースし直さなければいけなくて全体的に見ると二度手間で、goji を使っている意味が薄れている感じがしてこれも気に入らないところではある。
&lt;br /&gt;
&lt;div&gt;
&lt;script src=&quot;https://gist.github.com/oov/613a3c3e7973c4a7fb4f/91dbfea6dd7eb4311351628041ebba9e3854c0b6.js&quot;&gt;&lt;/script&gt;&lt;/div&gt;
ひとまずこれで動くようにはなった。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;セッション周辺の挙動も気になる&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
環境変数に必要なデータを加えて &lt;code&gt;PORT=3000 SESSION_SECRET=hoge TWITTER_KEY=fuga TWITTER_SECRET=moge go run main.go&lt;/code&gt; と実行させることで、取りあえず Twitter でのログインに成功するようになった。&lt;br /&gt;
しかしログイン後 Cookie を覗いてみると 500 バイトぐらいのデータが入りっぱなしだ。そのうち消えるとはいえ不始末感が拭えない。&lt;br /&gt;
また、セッションにはどうやら &lt;a href=&quot;https://github.com/gorilla/sessions&quot; target=&quot;_blank&quot;&gt;github.com/gorilla/sessions&lt;/a&gt; を使用していて、これを使っている場合は&lt;a href=&quot;http://www.gorillatoolkit.org/pkg/context#ClearHandler&quot; target=&quot;_blank&quot;&gt;後始末が必要&lt;/a&gt;なのに元のサンプルコードでは特にやっていない。&lt;br /&gt;
「なんでやっていないんだろう」と一瞬考えたものの、確か &lt;a href=&quot;https://github.com/gorilla/mux&quot; target=&quot;_blank&quot;&gt;github.com/gorilla/mux&lt;/a&gt; を使った時は自動でやってくれているはずなので、例で使われていた pat でも内部的にやっているんだろうと&lt;a href=&quot;https://github.com/gorilla/pat/blob/master/pat.go#L75&quot; target=&quot;_blank&quot;&gt;思った&lt;/a&gt;。&lt;br /&gt;
&lt;br /&gt;
内部でセッションに何を使っているのかは説明などで触れられていないので pat 以外で使おうとした際に後始末が忘れられそうだし、先述のプロバイダ名取得も含めつくりが pat に合わせてありそうな部分が見受けられるので、pat に依存気味でサンプルコードとしてはあまり良くないような気がしてきた。&lt;br /&gt;
&lt;br /&gt;
とはいえ気付いてしまえばこれは単純に &lt;code&gt;context.ClearHandler&lt;/code&gt; を goji の Middleware として追加するだけでいけるのでいいのだが、クッキーにデータが残りっぱなしになるのはあまりエレガントな対策方法が思いつかない。&lt;br /&gt;
sessions にも丸ごと削除する機能はなく、試しに中身のデータを消してみてもクッキー自体は残っていた。&lt;br /&gt;
もちろん &lt;a href=&quot;https://golang.org/pkg/net/http/#SetCookie&quot; target=&quot;_blank&quot;&gt;http.SetCookie&lt;/a&gt; で直接消しに行く手はあるものの、外部から無理矢理叩いてる感じはとても美しくない。&lt;br /&gt;
取りあえず丸ごと消すのは諦めて、&lt;a href=&quot;https://gist.github.com/oov/613a3c3e7973c4a7fb4f/bc738527d623adf0560818019029a7ae82f9a2ba&quot; target=&quot;_blank&quot;&gt;後始末をやるようにだけしたのがこちら&lt;/a&gt;。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;思っていたよりも綺麗に収まらなかった&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
pat を goji に変えた結果思っていたよりも修正項目が多かったのだが、これは恐らく goth での処理を更に単純化するために用意された gothic の柔軟性が今ひとつだった（≒実質的に pat を前提としたコードになっていた）ことに起因するものだろうと感じた。&lt;br /&gt;
セッションの後始末の話も単純にクッキーの読み書きとかなら必要なさそうだし、URL からのパラメータ抽出ももっと柔軟にできた方がいい。&lt;br /&gt;
例えば goji 以外でも &lt;a href=&quot;https://github.com/julienschmidt/httprouter&quot; target=&quot;_blank&quot;&gt;github.com/julienschmidt/httprouter&lt;/a&gt; とか &lt;a href=&quot;https://github.com/dimfeld/httptreemux&quot; target=&quot;_blank&quot;&gt;github.com/dimfeld/httptreemux&lt;/a&gt; とかのように、&lt;code&gt;*http.Request&lt;/code&gt; 以外に URL からのデータ格納するケースは他にもあるわけだし。&lt;br /&gt;
&lt;br /&gt;
その辺を踏まえて改めて仕切りなおしてみることにした。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;自家製 gothic&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
ということで、もう少し柔軟に使えそうな gothic の代わりになるものを試しに作ってみた。&lt;br /&gt;
goth の構造が綺麗に整っていたので gothic の差し替えはとても綺麗にできた。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/oov/gothic&quot; target=&quot;_blank&quot;&gt;https://github.com/oov/gothic&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
主な変更点は以下のとおり。&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GetProviderName&lt;/code&gt; を廃止し、プロバイダ名は直接渡す形に変更&lt;/li&gt;
&lt;li&gt;セッションの代わりに &lt;a href=&quot;https://github.com/gorilla/securecookie&quot; target=&quot;_blank&quot;&gt;github.com/gorilla/securecookie&lt;/a&gt; を使用し、後始末を不要に&lt;/li&gt;
&lt;li&gt;使い終わったクッキーはその場で破棄&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GetState&lt;/code&gt; を廃止し OAuth 2.0 の &lt;code&gt;state&lt;/code&gt; パラメータを内部で検証&lt;/li&gt;
&lt;/ul&gt;
これによって自分が使おうとした際に気になった部分は一掃できた。&lt;br /&gt;
改めて &lt;a href=&quot;https://github.com/oov/gothic/blob/master/examples/goji.go&quot; target=&quot;_blank&quot;&gt;goji と組み合わせて使った場合のサンプルはこちら&lt;/a&gt;。&lt;br /&gt;
&lt;br /&gt;
わかりやすい変化としては引数が増えたことによって &lt;code&gt;gothic.BeginAuthHandler&lt;/code&gt; が &lt;a href=&quot;https://golang.org/pkg/net/http/#HandlerFunc&quot; target=&quot;_blank&quot;&gt;http.HandlerFunc&lt;/a&gt; ではなくなったので名前を &lt;code&gt;gothic.BeginAuth&lt;/code&gt; に変更して、ついでに &lt;code&gt;error&lt;/code&gt; を返すように変えたのでエラー処理が増えてサンプルコードではこの辺が少し冗長に見えるようになった。&lt;br /&gt;
ただ元々の gothic でも &lt;code&gt;gothic.CompleteUserAuth&lt;/code&gt; に関しては戻り値を返していてハンドラにできていなかったし、個人的にはこの変更で両方の足並みが揃った感じがしていい。&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;&lt;/code&gt;
&lt;code&gt;gothic.BeginAuth&lt;/code&gt; で &lt;code&gt;error&lt;/code&gt; を返すようにしたのはハンドラではなく普通の関数ならエラーは処理せず返すのが自然だと思ったからで、内部で勝手に処理されると自前のエラーページなども表示できないし、エラーが起きたこと自体に気付けない（無視できることと気付けないことには大きな隔たりがある）し、結構微妙な問題があると思っている。&lt;br /&gt;
この変更をせずとも &lt;code&gt;gothic.GetAuthURL&lt;/code&gt; で取得した URL にリダイレクトするコードを各自で書けば同じようにエラー処理はできたけど、パッケージを使う人のほとんどはリダイレクト先 URL が取得できたらその後はリダイレクトしたいわけで、エラー処理が必要になっても &lt;code&gt;gothic.BeginAuth&lt;/code&gt; の存在意義は十分にあるように思う。&lt;br /&gt;
&lt;br /&gt;
ところで実は元々は&amp;nbsp;&lt;code&gt;web.C&lt;/code&gt; にガッツリ依存させて gojic って名前にする冗談で作り始めたんだけど、作業している途中でプロバイダ名を単に引数で渡すようにするだけで素晴らしい柔軟性があることに気付いて最終的にこのような形に落ち着いた。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;その他&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Twitter でのログイン時に &lt;code&gt;authorize&lt;/code&gt; じゃなくて &lt;code&gt;authenticate&lt;/code&gt; を使いたかったものの対応していなかったのだが、これは仕様を変えなくても追加だけでスムーズにいけそうだったので PR を投げてマージしてもらった。&lt;br /&gt;
&lt;br /&gt;
あと、ついここ最近の流れで &lt;code&gt;http.Request&lt;/code&gt; に &lt;a href=&quot;https://godoc.org/golang.org/x/net/context#Context&quot; target=&quot;_blank&quot;&gt;golang.org/x/net/context.Context&lt;/a&gt;&amp;nbsp;を持たせてはどうか、という話も&lt;a href=&quot;https://groups.google.com/forum/#!topic/golang-dev/cQs1z9LrJDU&quot; target=&quot;_blank&quot;&gt;持ち上がっている&lt;/a&gt;ので、これの流れ次第でまたちょっとルータやライブラリに動きがあるかも知れない。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/4558901820327077671'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/4558901820327077671'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2015/10/goji-goth-oauth.html' title='goji + goth で OAuth ログイン'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-1690952136593679816</id><published>2015-07-17T15:41:00.000+09:00</published><updated>2015-07-20T02:56:23.581+09:00</updated><title type='text'>CeVIO が軽くなった話</title><content type='html'>&lt;a href=&quot;http://cevio.jp/&quot; target=&quot;_blank&quot;&gt;CeVIO&lt;/a&gt; Creative Studio S でソングトラック編集中重かった部分が v3.2 でかなり改善された話です。&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
少し前の出来事になりますが、私 oov は ニコニコ超会議2015 の JOYSOUND ブースにて行われた「CeVIO 座談会」という、CeVIO について話し合う場に CeVIO のユーザー代表（？）として招待して頂きました。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0_S4Q2Dx4is8JjfDmqPa0L4dWsDxFTDDYpvbp9v3WHHwJy5r7B9EKl8SZ0JufyM9ri6eekNIDmPGnVcCt6y_JEHWgU4sijFvLNBIp_tHRD0MiaJD0SR-gJHlou3YRNUma8vvRpwbNp60/s1600/cevio.jpg&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;CeVIO 座談会当日の様子。左から ITmedia 松尾さん、ブイシンク加藤さん、エクシング石原さん、自分、オワタPさん&lt;br /&gt;自分以外はみんな人間力が高かったので、喋ってる間でさえ「出るんじゃなかった……」などと丸い背中で思っていた&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
ちなみに改めて oov って誰だよ、というのを簡単に説明すると、最近では主に JOYSOUND からリリースされている &lt;a href=&quot;http://camp.joysound.com/colorvoice/&quot; target=&quot;_blank&quot;&gt;Color Voice Series という CeVIO のソング専用ボイス&lt;/a&gt;を使って遊んでいる人です。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;360&quot; src=&quot;https://www.youtube.com/embed/PUBeoLu-tug?rel=0&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;この曲は Color Voice Series 黄咲愛里のソングボイスを使っているが、実は負荷テストにも使っていた&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;b&gt;■ CeVIO が重い&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
座談会では色々な話をしたんですが、その中で要望のひとつとして「CeVIO が重いのを何とかしてほしい」というのを挙げさせてもらいました。&lt;br /&gt;
もちろん挙げた理由は単純に、自分が CeVIO を使う上でかなり気になっていた部分だったからです。&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;（DTM をそれなりにやってきた身としてはサンプラーよりも物理モデリング音源の方が計算負荷が高いことは経験上理解しているつもりではあるので、ある程度は我慢はできるものの、曲が長くなると操作が辛いと感じるレベルで UI でカクカクして重かった）&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
座談会の場においては簡単に「重い」とだけ表現していたのですが、後日改めてどのようなシーンで重く感じたのか、また状況の再現方法などを連絡させて頂き、この約2ヶ月半の間に多くが改善、軽減したので、主に負荷周りではどんな部分が変わったのかを動画で比較しつつまとめておきます。&lt;br /&gt;
&lt;br /&gt;
なお、今回の話は全てソング機能におけるもので、所要時間の数字や動画はオリジナル曲「&lt;a href=&quot;http://oov.ch/music/%E6%9C%88%E3%81%AE%E4%BA%A4%E7%82%B9%E3%81%A8%E5%BD%BC%E5%A5%B3%E3%81%AE%E6%B5%B7/&quot; target=&quot;_blank&quot;&gt;月の交点と彼女の海&lt;/a&gt;」の&lt;a href=&quot;http://s.oov.ch/music/20150507-lunasea/lunasea-ccs.zip&quot; target=&quot;_blank&quot;&gt;プロジェクトファイル&lt;/a&gt;を Intel Core-i7 4790K /&amp;nbsp;メモリ 32GB の環境で CeVIO v3.1.14.1 と v3.2.15.0 で動かした時のものです。&lt;br /&gt;
トラック数や演奏時間、調整機能を多用しているかどうかなどで負荷の掛かり方は変わりますが、これは比較的重いシーンでの例になります。&lt;br /&gt;
&lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;アクティブトラックの切り替えが重かった&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;以前の CeVIO ではアクティブトラックを切り替える度に 1.5 秒程度の処理待ちが発生していました。&lt;br /&gt;
v3.1.16 以降ではこの待ち時間は発生しなくなりました。&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;270&quot; src=&quot;https://www.youtube.com/embed/KeAYKT098qI?rel=0&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;/dd&gt;&lt;dd&gt;左側では切り替えの度に時間が掛かっていますが、右側ではすぐ切り替わるのが確認できます。&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;再生停止後の最初の操作が重かった&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;以前の CeVIO では、プレビュー再生の停止後に初めて再生カーソルを移動させた時などに数秒の待ち時間が発生していました。&lt;br /&gt;
v3.2 以降ではこの待ち時間は発生しなくなりました。&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;270&quot; src=&quot;https://www.youtube.com/embed/B-ve6eSTi_M?rel=0&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;/dd&gt;&lt;dd&gt;左側ではクリックしてから再生カーソルが移動するまでに 2.23 秒掛かっていますが、右側では即座に移動するのが確認できます。&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;ノート編集後、ピッチやボリュームの調整画面に切り替えようとすると重かった&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;以前の CeVIO ではノート編集後にピッチやボリュームの調整画面に切り替えようとした時、実際に表示が切り替わるまでには 1.5 秒程度の待ち時間が発生していました。&lt;br /&gt;
v3.2 以降では即座に表示が切り替わり、データの準備が間に合っていない場合は完了次第追って反映されるようになったため、体感的な待ち時間はほぼ発生しなくなりました。&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;270&quot; src=&quot;https://www.youtube.com/embed/BNNUFE5TM70?rel=0&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;/dd&gt;&lt;dd&gt;左側ではクリックしてから表示が切り替わるまでに 1.53 秒掛かっていますが、右側では即座に切り替わっているのが確認できます。&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;再生開始までの待ち時間が長かった&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;以前の CeVIO ではプレビュー再生の開始地点が後ろに行けば行くほど実際に再生が始まるまでの待ち時間が長くなっていました。&lt;br /&gt;
v3.2 以降ではこの待ち時間がほぼ一定になり、例えば以前の CeVIO で12秒弱掛かっていたものが v3.2 以降では2秒強にまで短縮されました。&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;270&quot; src=&quot;https://www.youtube.com/embed/b1A3Edm7qVE?rel=0&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;/dd&gt;&lt;dd&gt;左側ではクリックしてから再生が開始されるまでに 11.51 秒掛かっていますが、右側では 2.33 秒で再生開始されているのが確認できます。&lt;/dd&gt;
&lt;/dl&gt;
&lt;br /&gt;
自分が把握している範囲内で大きな改善点は以上の4つです。&lt;br /&gt;
どれもかなり頻繁に行う操作であるためにこの変化による影響はとても大きく、一度 v3.2 での操作に慣れてしまうと古いバージョンに戻るのは結構苦痛になると思います。&lt;br /&gt;
（ベータの時点で既に快適すぎたので実際に自分でもバグでたまにデータが崩壊するのにも関わらず負けずに v3.2 でデータ作りを続けてました）&lt;br /&gt;
&lt;br /&gt;
パフォーマンス面に関しては他にも「トラックの固定」（DAWで言うところのフリーズ機能で、トラックを内部的に Wave 書き出し済みにしておくことによって負荷を減らす）機能が追加されていたり、クォンタイズ単位でのガイド表示（ピアノロール上での右クリックメニューにある「ガイド表示」→「クォンタイズ」）が重かったのが改善されたりなどもしています。&lt;br /&gt;
&lt;br /&gt;
なお v3.2 ではパフォーマンス面以外にも大量の変更が含まれており、操作性の向上や誤操作の抑制など、多方面から使い勝手の向上が図られています。&lt;br /&gt;
変更点の一覧は&lt;a href=&quot;http://guide2.project-cevio.com/&quot; target=&quot;_blank&quot;&gt;ユーザーズガイド内の最新情報ページ&lt;/a&gt;に記載されています。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;■歌ってない場所で呼吸してる？&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
パフォーマンスに関する話ではありませんが、もうひとつ重要な改善点があるのでこれにも触れておきます。&lt;br /&gt;
&lt;br /&gt;
CeVIO はピアノロール上でノートを置いていない場所で自動でブレスが入ったりしますが、大きな余白であっても少しノイズが出ており、綺麗なデータにしたい場合は手動でそこを削ぎ落とす必要がありました。&lt;br /&gt;
v3.2 ではノートのない小節が続いた場合に間の部分を自動的にミュートしてくれるようになったため、間奏部分やコーラスパートなど、長い休みがある場所を手動で消す必要がなくなりました。&lt;br /&gt;
&lt;br /&gt;
以下の画像は1小節目と9小節目に8分音符で「ドレミファソ」を配置したプロジェクトファイルを v3.1.14.1 と v3.2.15.0 で書き出し、その結果を &lt;a href=&quot;http://www.wavosaur.com/&quot; target=&quot;_blank&quot;&gt;Wavosaur&lt;/a&gt;&amp;nbsp;で縦方向に拡大表示して比較したものです。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl77j0YsCcIs4cjS508JJqmfNvXhgU166WV58NovEc5xUTHPcjabB2Sjp8KwSdmso9wT5DiybuXJgT_xiKaRCevofcPz8Qve_UT4YMkL7xQRjkgSPwoqZhYNF_yZO2Q7BUQ-R3AN98fcA/s1600/doremifaso.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;230&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl77j0YsCcIs4cjS508JJqmfNvXhgU166WV58NovEc5xUTHPcjabB2Sjp8KwSdmso9wT5DiybuXJgT_xiKaRCevofcPz8Qve_UT4YMkL7xQRjkgSPwoqZhYNF_yZO2Q7BUQ-R3AN98fcA/s400/doremifaso.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
v3.2 で書きだした側は余白部分に存在していたノイズがなくなっているのがわかります。&lt;br /&gt;
また、これは音量調整画面にも反映されているため、以下のように CeVIO 上でも確認できます。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX47-qPOdVaTiv_ZSZGQ6AEgeMTY3T6zERf7kHRb36fgoe4gFWVKQxThhNhtjJjrOkupHflc87JMYDPyf4VmIgBheKxzFDJ8gsTrlztVG9-7XZCxiM5gtSCGKfIRH229IYWpnA7uiA8l8/s1600/vol.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;121&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX47-qPOdVaTiv_ZSZGQ6AEgeMTY3T6zERf7kHRb36fgoe4gFWVKQxThhNhtjJjrOkupHflc87JMYDPyf4VmIgBheKxzFDJ8gsTrlztVG9-7XZCxiM5gtSCGKfIRH229IYWpnA7uiA8l8/s400/vol.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;ノイズがカットされた結果、音量のラインが下に消えている&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;b&gt;■おわりに&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
生放送で中継もされているようなそこそこパブリックな場所である座談会で関係者を目の前にして「重い」とか言っちゃうのはなかなかに辛く当たっているなぁと自分でも感じていたものの、その重かった印象が覆るようなドラスティックな変更を、たった2ヶ月程度の間に行って頂いていたことをパブリックベータテストの開始によって知ることとなりました。&lt;br /&gt;
発言の場を提供して頂いたこと、そして改善のために尽力して頂いたことについて、エクシング小松様、石原様、ブイシンク加藤様、川出様、その他関係者の皆様に感謝の意を表すとともに、心より厚く御礼申し上げます。&lt;br /&gt;
&lt;br /&gt;
で、別に自分のためだけにやってくれたわけでないにしても、ここまでやってもらっておいてベータテストに参加しないのでは不義理にも程があるし、ブイシンク加藤さんには&lt;strike&gt;「愛をとりもどせ」&lt;/strike&gt;「愛をください &lt;strike&gt;wow wow&lt;/strike&gt;」とも言われてもいたので、今回は逆にやり過ぎなぐらいベータテストに参加しようと思い、他のことが手に付かない程度にはとても積極的にテストやレポートすることで愛を表現させてもらいました。&lt;br /&gt;
こうしてブログにどのような変更があったのかを簡単に紹介しているのもその一環ではありますが、それはそれとして v3.2 がいかに素晴らしいリリースであるかを少しでも伝えられたら幸いです。&lt;br /&gt;
&lt;br /&gt;
今回の変更の影響によるバグや以前からあったバグの報告、改善の要望など、2週間程の短いベータテスト期間ながらほぼ毎日のように、多分10通ぐらい毎回そこそこの長さのレポートを送ったりしてました（送信者控えがなく記録も残していないので正確な数字は不明）。&lt;br /&gt;
正直仕事をしていた頃よりもよっぽど仕事をしてる気分が味わえたような、そんな2週間でした。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;■おまけ&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
8月末までの&lt;a href=&quot;https://musicpost.joysound.com/cp/campaign/html/openingCampaign.html&quot; target=&quot;_blank&quot;&gt;うたスキミュージックポストのオープニングキャンペーン&lt;/a&gt;、募集要項を守って投稿することで抽選ではなく先着100名に CeVIO の &lt;b&gt;Color Voice Series の中から&lt;/b&gt;一つ貰えるんですが、間違っている人を見かけるのでここにもツイートを再掲しておきます。まあ間違っている人はここ見てないとは思いますが。&lt;br /&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;ja&quot;&gt;
&lt;div dir=&quot;ltr&quot; lang=&quot;ja&quot;&gt;
&lt;a href=&quot;https://t.co/1Jr2SFVy6D&quot;&gt;https://t.co/1Jr2SFVy6D&lt;/a&gt;
↑
このキャンペーン、プレゼント対象のキャラクターと投稿可能なキャラクターは違うので注意。間違ってる人がチラホラ居る。 &lt;a href=&quot;http://t.co/4Puu6t4cYK&quot;&gt;pic.twitter.com/4Puu6t4cYK&lt;/a&gt;&lt;/div&gt;
— おおぶ (@oovch) &lt;a href=&quot;https://twitter.com/oovch/status/619448882133479424&quot;&gt;2015, 7月 10&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; charset=&quot;utf-8&quot; src=&quot;//platform.twitter.com/widgets.js&quot;&gt;&lt;/script&gt;
Color Voice Series は純粋に音としての面白さで音声合成界隈を見ている人なら興味深い代物のはずなんだけど、人気（にんき）がないというか、人気（ひとけ）がないみたいな感じで僕ちゃん寂ちい。使って。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;■おまけ2&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7c3XcuXHsNhxyjQlBcAS2I1f1GwCK8AOINk1kFBieOpSCuDT-cxalbdjuSCo0DxToi6Krnb6NYN6TsBfpndr1qfY7n7qpAlafGHlcLTOabbxjOF_CUc-WxtDJ4tibRBI030f67MX9VlU/s1600/Oo0V.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;167&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7c3XcuXHsNhxyjQlBcAS2I1f1GwCK8AOINk1kFBieOpSCuDT-cxalbdjuSCo0DxToi6Krnb6NYN6TsBfpndr1qfY7n7qpAlafGHlcLTOabbxjOF_CUc-WxtDJ4tibRBI030f67MX9VlU/s400/Oo0V.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;oov っぽくて惜しいってずっと思ってた（マジでどうでもいい）&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/1690952136593679816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/1690952136593679816'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2015/07/cevio.html' title='CeVIO が軽くなった話'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0_S4Q2Dx4is8JjfDmqPa0L4dWsDxFTDDYpvbp9v3WHHwJy5r7B9EKl8SZ0JufyM9ri6eekNIDmPGnVcCt6y_JEHWgU4sijFvLNBIp_tHRD0MiaJD0SR-gJHlou3YRNUma8vvRpwbNp60/s72-c/cevio.jpg" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-5636584534911237931</id><published>2015-05-25T23:23:00.000+09:00</published><updated>2015-07-19T05:06:58.771+09:00</updated><title type='text'>CeVIO で歌わせる時に考えていること</title><content type='html'>&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEib8lLJs8PCCC1SlGUta1K4mS1WnAt0gtSWD8yiyuK2OWVs71QM4XCuu1xFRrwlfCgBuIATidz1N9fb51ZefX-VR-RhPJ75hrFzoHbU5EH3rPpFf5mJnmNU5IxyDnRwfXJYY_NvzdqHXqE/s1600/tit.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;204&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEib8lLJs8PCCC1SlGUta1K4mS1WnAt0gtSWD8yiyuK2OWVs71QM4XCuu1xFRrwlfCgBuIATidz1N9fb51ZefX-VR-RhPJ75hrFzoHbU5EH3rPpFf5mJnmNU5IxyDnRwfXJYY_NvzdqHXqE/s320/tit.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;↑この画像に使った CCS ファイルは→&lt;a href=&quot;http://s.oov.ch/music/20150507-lunasea/lunasea-ccs.zip&quot; target=&quot;_blank&quot;&gt;こちらからDLできます&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
自分が CeVIO で歌わせる時に考えている内容を書きます。&lt;br /&gt;
歌わせ方のコツからミックス時の話までひと通り。&lt;br /&gt;
&lt;br /&gt;
まあ一年後には自分でも全然違う手法になっているかも知れないものの、少なくともここ2年程度の間に培ったノウハウ的なものは大体吐き出すつもりなので、使えそうな部分があれば適当につまみ食いでもしてください。&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;なお、画像の下の小さい説明文のところにはしばしば mp3 へのリンクがあり、画像で解説している内容が視聴できるところがいくつかあります。
説明と合わせて聞いてもらえると、より理解が深まるかもしれません。&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#cevio-1&quot;&gt;ミックス時の話&lt;/a&gt;&lt;br /&gt;1. &lt;a href=&quot;#cevio-1-1&quot;&gt;存在感の希薄なリバーブ&lt;/a&gt;&lt;br /&gt;2. &lt;a href=&quot;#cevio-1-2&quot;&gt;どう攻めるか&lt;/a&gt;&lt;br /&gt;3. &lt;a href=&quot;#cevio-1-3&quot;&gt;実際にエフェクトをかけてみる&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#cevio-2&quot;&gt;CeVIO を使う時の基本姿勢&lt;/a&gt;&lt;br /&gt;1. &lt;a href=&quot;#cevio-2-1&quot;&gt;音量、パンは変更しない&lt;/a&gt;&lt;br /&gt;2. &lt;a href=&quot;#cevio-2-2&quot;&gt;書き出す時はトラック単位&lt;/a&gt;&lt;br /&gt;3. &lt;a href=&quot;#cevio-2-3&quot;&gt;全てのビブラートをピッチで手入力する時は標準のビブラートを無効にする&lt;/a&gt;&lt;br /&gt;4. &lt;a href=&quot;#cevio-2-4&quot;&gt;ピッチを描く時は必ずズーム状態で描く&lt;/a&gt;&lt;br /&gt;5. &lt;a href=&quot;#cevio-2-5&quot;&gt;データを入力する時はノート→TMG→PIT/VOLの順&lt;/a&gt;&lt;br /&gt;6. &lt;a href=&quot;#cevio-2-6&quot;&gt;CeVIO で作ったデータを VOCALOID や UTAU に持ち込むなら&lt;/a&gt;&lt;br /&gt;7. &lt;a href=&quot;#cevio-2-7&quot;&gt;まとめ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#cevio-3&quot;&gt;調声の話&lt;/a&gt;&lt;br /&gt;1. &lt;a href=&quot;#cevio-3-1&quot;&gt;VOCALOID を使ったことがある人へ&lt;/a&gt;&lt;br /&gt;2. &lt;a href=&quot;#cevio-3-2&quot;&gt;歌わせる時は「本人に任せる」のが楽&lt;/a&gt;&lt;br /&gt;3. &lt;a href=&quot;#cevio-3-3&quot;&gt;スタッカートとブレスについて&lt;/a&gt;&lt;br /&gt;4. &lt;a href=&quot;#cevio-3-4&quot;&gt;「yes」などの発音をそれっぽくするための母音の脱落記号と「_0」の話&lt;/a&gt;&lt;br /&gt;5. &lt;a href=&quot;#cevio-3-5&quot;&gt;母音が綺麗に繋がらない時&lt;/a&gt;&lt;br /&gt;6. &lt;a href=&quot;#cevio-3-6&quot;&gt;発音が気に入らない時&lt;/a&gt;&lt;br /&gt;7. &lt;a href=&quot;#cevio-3-7&quot;&gt;ピッチを描く&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#cevio-4&quot;&gt;終わりに&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-1&quot;&gt;■ 1. ミックス時の話&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
CeVIO 上でのアレコレの話の前に、ミックスの話です。&lt;br /&gt;
ぶっちゃけ CeVIO の場合細かく弄らなくてもミックスが良ければかなり聴けるデータになると思います。&lt;br /&gt;
なので&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-example.mp3&quot; target=&quot;_blank&quot;&gt;このサンプルデータ&lt;/a&gt;を元にして、処理例などを連々と書いていきます。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-1-1&quot;&gt;1-1. 存在感の希薄なリバーブ&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
まず自分の中の大前提として、CeVIO は音声合成ソフトウェアであり、元になった人が歌いそうな感じの音を想像して出力してくれるソフトです。これは恐らく喉から声を出すところまでの音をシミュレートしてくれます。&lt;br /&gt;
&lt;br /&gt;
しかし現実世界において我々の耳に最終的に入ってくるのは喉から出たダイレクトな音ではなく部屋の反響なども含めた音なので、CeVIO が出力した音を直接聴くと（伝わらないかもしれませんが）物理モデリング音源の出力音をリバーブなしで聴いた時に特有の、嘘っぽい音になります。&lt;br /&gt;
&lt;br /&gt;
なので自分が CeVIO のボーカルトラックをミックスする時は、ボーカルっぽいぼわーっとしたリバーブの前に、部屋っぽい薄いリバーブを先にかけるようにしています。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfmv8M-zfSE-RnuOCV8_9ychz_v36p5u4Gf3q9WRSiH_gQa8mx5SgXD_6ySUYptCC4KbedQG5PXyV79FphftXXf6kyBtTNNomddmIAMaNnTpy83Q7VluzXPZiy9iswQU8_6KdpRGma6gI/s1600/voxengo_oldskoolverb.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;94&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfmv8M-zfSE-RnuOCV8_9ychz_v36p5u4Gf3q9WRSiH_gQa8mx5SgXD_6ySUYptCC4KbedQG5PXyV79FphftXXf6kyBtTNNomddmIAMaNnTpy83Q7VluzXPZiy9iswQU8_6KdpRGma6gI/s400/voxengo_oldskoolverb.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-reverb-demo.mp3&quot; target=&quot;_blank&quot;&gt;エフェクト前後の音を試聴する&lt;/a&gt;（※両方とも同じだけ元より音量を上げています）&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
これはフリープラグイン &lt;a href=&quot;http://www.voxengo.com/product/oldskoolverb/&quot; target=&quot;_blank&quot;&gt;Voxengo OldSkoolVerb&lt;/a&gt;&amp;nbsp;をセンドではなくインサートで入れた時の設定例です。&lt;br /&gt;
個人的には普段は（探さなくても最初からあるから） Cubase 付属のものを使っているんですが、Windows / Mac に対応していて今後も長らく配布してくれそうなベンダー、というところでここでは Voxengo のリバーブを選んでみました。&lt;br /&gt;
&lt;br /&gt;
デフォルトから変更したのは赤枠で囲んだ TIME / WIDTH / REVERB GAIN の3つのみで、それぞれ 最低 / 最高 / -18db に変更しています。&lt;br /&gt;
ヘッドホンなど細かい音が聴こえる環境じゃないとあまり聴こえないような薄いものです。&lt;br /&gt;
また、WIDTH を変えていることからもわかる通りステレオチャンネル上での設定を想定しています。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-1-2&quot;&gt;1-2. どう攻めるか&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
普段イコライザーやコンプレッサーを入れる時はさっきのリバーブの前、リバーブとかディレイを入れる場合はさっきのリバーブの後ろ、という感じで追加していくんですが、「実際のところその辺のエフェクトはどのぐらいどんな感じでかけるの？」というのは、誰もが気になるところだと思います。&lt;br /&gt;
&lt;br /&gt;
例えば&amp;nbsp;&lt;a href=&quot;http://www.amazon.co.jp/dp/B00MZAKQQG/ref=dm_mu_dp_trk5&quot; target=&quot;_blank&quot;&gt;petit milady 恋はみるくてぃ トラック5&lt;/a&gt;&amp;nbsp;を例に挙げますが、これはエフェクト処理済みのボーカルトラックが収録されたもので、もし作りたい曲調に近ければとても参考になると思います。&lt;br /&gt;
ちなみに余談ですが、このトラックを実際に買って聴くともっと高音もハッキリ聴こえるしマイクがヘッドホンの音漏れか何かを拾ったのかセリフ部分でオケが少し残っていたりと、なかなか面白いのでオススメです。&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
試聴で聴ける範囲を細かく見ていくと、基本的にはリバーブ、セリフに入る直前のフレーズはディレイ、セリフ部分には歌部分のリバーブはなし、とか多分そんな感じで場所によりかなり細かく調整されています。&lt;br /&gt;
&lt;br /&gt;
しかしながらそれ以上に特徴的なのはやはりコーラスパートの多さで、いまどきの曲はコーラスパートが多い傾向にあると思うので、音が薄いのを解決するのはエフェクトではなくコーラスパート追加も手段として重要なこともある、ということは肝に銘じておく必要があると思います。&lt;br /&gt;
&lt;br /&gt;
ということで、エフェクトをゴテゴテと盛って誤魔化す前に、まずはその辺を疑ってみてください。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-1-3&quot;&gt;1-3. 実際にエフェクトをかけてみる&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
コーラスパートの追加が重要なのはさておき、エフェクトの例を作ってみました。&lt;br /&gt;
（※これは今回説明のために改めて作ったもので、実際の楽曲に使われているものとは異なります）&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgpe1cbXVEqffFOMDt6jxUeSDONEKtyE8WweP9K4NX93bgmipx8819lvMVyaxkvbJQYBhDu0Q-MAvBcQViLxQUI86Mee5NqpxE4CfVyiOohlfQTnKO99exmWa-s6C_dwQRwCgmbSDERds/s1600/guitarrig5.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgpe1cbXVEqffFOMDt6jxUeSDONEKtyE8WweP9K4NX93bgmipx8819lvMVyaxkvbJQYBhDu0Q-MAvBcQViLxQUI86Mee5NqpxE4CfVyiOohlfQTnKO99exmWa-s6C_dwQRwCgmbSDERds/s640/guitarrig5.png&quot; width=&quot;517&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-mix-demo.mp3&quot; style=&quot;font-size: 12.8000001907349px;&quot; target=&quot;_blank&quot;&gt;エフェクト前後と伴奏付きの音を試聴する&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
今回はやはり無料で使える&amp;nbsp;&lt;a href=&quot;http://www.native-instruments.com/jp/products/komplete/guitar/guitar-rig-5-player/&quot; target=&quot;_blank&quot;&gt;Guitar Rig 5 PLAYER&lt;/a&gt;&amp;nbsp;でエフェクトを設定してみました。実際に使っているのは Guitar Rig 5 PRO ですが、PLAYER でも再現できる範囲で設定しています。&lt;br /&gt;
なお、Guitar Rig は名前にギターと入っているもののギター専用ではなく、実際にはギター以外の用途にも使える便利なエフェクター集で、無料版でもそれなりの種類のエフェクターが収録されているので便利です。&lt;br /&gt;
&lt;br /&gt;
ここでは上で Voxengo OldSkoolVerb を使って説明した分も STUDIO REVERB で代用してみています。&lt;br /&gt;
個々の説明をしていく前に上記の設定がどんな感じの順番で適用されるか確認して下さい。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6ZNb-ZImUnOGAE0lJkatjmouqjNowIo6N9-OegNnY9zWopRB28Rq7bEy1EDK8BX3ofHqqiC_S9W2Od-2rCNzupuLlppYhWx5XEdHPsdu20qYrkK_-7WPUgmcE_8AwvSch5SFN4-XBDRM/s1600/%25E7%2584%25A1%25E9%25A1%258C%25E5%259B%25B3%25E5%25BD%25A2%25E6%258F%258F%25E7%2594%25BB+%25281%2529.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6ZNb-ZImUnOGAE0lJkatjmouqjNowIo6N9-OegNnY9zWopRB28Rq7bEy1EDK8BX3ofHqqiC_S9W2Od-2rCNzupuLlppYhWx5XEdHPsdu20qYrkK_-7WPUgmcE_8AwvSch5SFN4-XBDRM/s400/%25E7%2584%25A1%25E9%25A1%258C%25E5%259B%25B3%25E5%25BD%25A2%25E6%258F%258F%25E7%2594%25BB+%25281%2529.png&quot; width=&quot;301&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
1-3-1. Gate&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color:red&quot;&gt;この部分の説明は CeVIO 3.2.17.3 がリリースされるより前に書いたものです。&lt;br&gt;
CeVIO 3.2.17.3 ではノートのない小節が続く場合自動的にミュートされるようになったため、以前より神経質に削る必要はなくなりました。&lt;/span&gt;&lt;br&gt;
&lt;br&gt;
まず枠外の一番上にある「Gate」です。&lt;br /&gt;
これはツマミで指定した音量以下の音はミュートしてくれるという、ノイズが多いギターにおいて特に役に立つ機能なのですが、CeVIO においても大変役に立ちます。&lt;br /&gt;
CeVIO ではメロディデータが存在していない部分でも常に微量にノイズが出続けているという謎の挙動があるのですが、このノイズは Gate を使うことでほぼ自動で切り落とすことができます。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaqR5YJcDhEj9OsLt23Eb4GIXyTVriIIIxmcrJBHGF49dU7w9GQmtQMf3AMZqFnsaO440SRVJYf7VjzAWMhs3N0-PLZ0-BoIkvEkSuaegpnCIUBBjgUcLZ0SjqnIalLeExrrSLpdrb1Zg/s1600/noise.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;218&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaqR5YJcDhEj9OsLt23Eb4GIXyTVriIIIxmcrJBHGF49dU7w9GQmtQMf3AMZqFnsaO440SRVJYf7VjzAWMhs3N0-PLZ0-BoIkvEkSuaegpnCIUBBjgUcLZ0SjqnIalLeExrrSLpdrb1Zg/s400/noise.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;緑咲香澄に3小節目でC4で「ドー」と歌ってもらった場合の書き出し例&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
画像は今回ミックス例に使ったデータとは違うのですが、緑咲香澄が歌っているデータを &lt;a href=&quot;http://www.wavosaur.com/&quot; target=&quot;_blank&quot;&gt;Wavosaur&lt;/a&gt; という Windows 専用の波形編集ソフトで出力時のノイズ部分を縦方向に拡大表示した時のものです。&lt;br /&gt;
上下ズームの影響で画面からはみ出す勢いの2つの大きな山がありますが、ひとつめはブレス、ふたつめは「ドー」とC4の高さで歌った時のものです。&lt;br /&gt;
&lt;br /&gt;
波形表示エリアの左側にある目盛りに注目すると、真ん中が無音、そこから上下に離れるごとに -78db -72db -69db と書いてあります。&lt;br /&gt;
ノイズがある部分はおおよそ -72db より下の範囲で収まっているので、この場合 Gate の設定を -72db 以上に設定するとノイズを削ぎ落とすことができることになります。&lt;br /&gt;
ただし、みてわかるようにブレスも結構小さい音なので、あまり適当に設定するとノイズと一緒にブレスを巻き添えでそぎ落としてしまうので設定値には注意が必要です。&lt;br /&gt;
&lt;br /&gt;
Guitar Rig 5 では Gate は初期設定で有効で、しかも設定値は -71db なのでこの場合何もしなくても勝手に消えそうです。歌いだしの前など目立つブレスが消えないできちんと残っているかとか、変なところでノイズが漏れてこないかチェックしましょう。&lt;br /&gt;
もし上手くいかない場合は消えてしまうブレスの音量を CeVIO 側で上げておくとか、ノイズを CeVIO 側で下げておくとか、Gate を使わずに手動で削るとか、色々やってみてください。&lt;br /&gt;
&lt;br /&gt;
1-3-2. PRO-FILTER&lt;br /&gt;
&lt;br /&gt;
一番左のツマミを 150Hz、一番右のツマミを HPF にして、緩やかに低音を削っています。&lt;br /&gt;
HPF はハイパスフィルター、ハイをパスするフィルターで、逆に言えばローカットフィルターです。&lt;br /&gt;
今回のサンプルでは一番最初の歌い出しがかなり低い音なので影響が強めに出ます。&lt;br /&gt;
&lt;br /&gt;
1-3-3. TUBE COMPRESSOR&lt;br /&gt;
&lt;br /&gt;
まず子音が潰れないよう ATTACK を 3.00 にして少し遅めにしました。&lt;br /&gt;
あとはこのコンプのデフォルト設定はかなり強く潰しているようなので、それを柔らかくするために RATIO を 9 : 1 に、THRESHOLD を -36db にしました。&lt;br /&gt;
&lt;br /&gt;
1-3-4. PARAMETRIC EQUALIZER&lt;br /&gt;
&lt;br /&gt;
声をもう少しキラキラさせるために4kと8k周辺を持ち上げました。&lt;br /&gt;
Q の設定が標準だと見えていないので EQ の右上にある ▲ をクリックして詳細設定を開きます。&lt;br /&gt;
左からそれぞれ 4019Hz /&amp;nbsp;+3.4db / 0.30 と 8074Hz /&amp;nbsp;+5.4Hz / 0.34 に設定しました。&lt;br /&gt;
&lt;br /&gt;
1-3-5. STUDIO REVERB&lt;br /&gt;
&lt;br /&gt;
ほぼ聴こえないレベルの方です。&lt;br /&gt;
WET / ROOMSIZE / BRIGHT をそれぞれ 20.3% / 2.66 / 最大 に設定しています。&lt;br /&gt;
Voxengo OldSkoolVerb で設定した時よりも更にドライな設定です。&lt;br /&gt;
&lt;br /&gt;
1-3-6. STUDIO REVERB / DELAY MAN&lt;br /&gt;
&lt;br /&gt;
残響成分ですが、Tools にある Split を用い、2つに分離してかけてみました。&lt;br /&gt;
リバーブとディレイがお互いに干渉しないので両方かけても多少スッキリしています。&lt;br /&gt;
リバーブ側は ROOMSIZE と BRIGHT を上げて、ディレイ側は TIME を 233msec にしました。&lt;br /&gt;
ディレイはもう少し控えめでも良かったかもしれません。&lt;br /&gt;
&lt;br /&gt;
1-3-7. まとめ&lt;br /&gt;
&lt;br /&gt;
以上のような感じで一部 CeVIO に特化した話もあったものの、ミックス周りは通常のボーカルのミックスと大きく変わりません。実際のミックスでは更に曲の展開に合わせてトラックの音量をオートメーションで変えて起伏を作ったりしています。&lt;br /&gt;
&lt;br /&gt;
ミックスの細かい部分に関しては曲ごとに変わる部分も大きいのでここに書かれたものを鵜呑みにしても上手くいかないことも多いと思いますが、取りあえず無料の Guitar Rig 5 PLAYER があるだけでもわりといい感じにできる、という感じでどうでしょうか。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-2&quot;&gt;■ 2. CeVIO を使う時の基本姿勢&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
自分が使う時に心掛けている話で、これらは自分の中ではほぼマストです。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-2-1&quot;&gt;2-1. 音量、パンは変更しない&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
せっかく各トラックに付いている音量調節やパン機能ですが、これらはあまり積極的には触りません。&lt;br /&gt;
最終的には DAW 側で設定するし、個人的には編集と書き出しを繰り返す場合に途中で変えて DAW 側でコンプの掛かり方が変わったりと余計なトラブルを生まないために後述するトラック単位の書き出しを使っているため、ここでの設定内容は最終的には DAW 側には一切持ち込まれません。&lt;br /&gt;
&lt;br /&gt;
また、ツールメニューからオプションを開いたところにある「トラック設定」タブのベースボリューム設定も一切変えません。一度これを変えてしまうとマウスでは初期値に戻すのが難しいし、何となく CCS ファイルを開く環境によって音量バランスがぶっ壊れるんじゃないのかという気がするので変えないようにしています。ベースボリューム設定は将来的には廃止されるようです。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3eHqM3nJ6nvSy9rEyZEFr1Wh8ZNE2qy54sqB9rFD3j5ehkEs59qnVseBzQpdgcwqyv3TfNdzQ7OhmmlBdBco3a4tqmUZCRwrJRE3oGPC1DYS6-zKlYJOFqlluNkI1vaPu2AM9qz4QAFo/s1600/bv.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3eHqM3nJ6nvSy9rEyZEFr1Wh8ZNE2qy54sqB9rFD3j5ehkEs59qnVseBzQpdgcwqyv3TfNdzQ7OhmmlBdBco3a4tqmUZCRwrJRE3oGPC1DYS6-zKlYJOFqlluNkI1vaPu2AM9qz4QAFo/s400/bv.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;ゼロに戻すには設定ファイルを直接書き換えるか、一度端にくっつけてから矢印キーを160回分押せばいい&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
数値入力できない上に細かい値を設定させるツマミは基本的に悲しみを生みます。&lt;br /&gt;
なお Guitar Rig 5 も数値入力できませんが、こちらは Shift キーを押していると細かく設定できますし、ダブルクリックすると初期値に戻せるようになっているので悲しみは少なめです。&lt;br /&gt;
なお、CeVIO 3.2.17.3 以降ではボリュームやパンを Ctrl + クリックすることで初期値に戻すことができるようになりました。&lt;br&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-2-2&quot;&gt;2-2. 書き出す時はトラック単位&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
CeVIO 上では各トラックに対してエフェクトをかけたりできず機能としては不十分で、他のソフトでエフェクトをかける場合は複数のトラックがミックスされている場合処理しづらくなるため、ミックスダウン書き出しを使う理由が基本的にありません。&lt;br&gt;
そのため、書き出しは基本的にこのメニューから WAV 書き出しを選んで使用します。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4s7n8auS6VpB-ZisBCCB3IOmUK2_ebZ8Y8jOTaMQPycr2mzzQSYbJZosyyohbTfH5wiuJdwNQqbGHUrlvRQIBkWV-Bwridese63ZQasYefK5PkThfPHy-vo5aUElmAbQchWWFEuq2mNk/s1600/export.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;168&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4s7n8auS6VpB-ZisBCCB3IOmUK2_ebZ8Y8jOTaMQPycr2mzzQSYbJZosyyohbTfH5wiuJdwNQqbGHUrlvRQIBkWV-Bwridese63ZQasYefK5PkThfPHy-vo5aUElmAbQchWWFEuq2mNk/s400/export.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;トラックのタイムライン上で右クリックすると出てくるメニュー&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
トラック単位で書き出しておけば例えばコーラスを修正して書き出し直す時もコーラスだけを書き出せばいいので必要最低限の負荷で書き出せることになり、長い目で見ると作業効率が上がります。&lt;br /&gt;
また、トラック単位での書き出しはベースボリュームの設定やトラック毎のボリューム設定、パン設定が影響しない上モノラルオーディオファイルとして書き出されるので、悲しみも無駄も少なくて済みます。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-2-3&quot;&gt;2-3. 全てのビブラートをピッチで手入力する時は標準のビブラートを無効にする&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNTUHocV0z3WNjwCcyTNL06yhNZBKKXGWfcx5QP5Z4CnZOUsatFXUXceN0FhSVfJia0W-aQ_JaaOimldy8LMckhTEtG3g07cEiwC_DsPL8BIy170zF_Jh7t0VxSb3BXUt_RIWv9VtBpoc/s1600/vib.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;242&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNTUHocV0z3WNjwCcyTNL06yhNZBKKXGWfcx5QP5Z4CnZOUsatFXUXceN0FhSVfJia0W-aQ_JaaOimldy8LMckhTEtG3g07cEiwC_DsPL8BIy170zF_Jh7t0VxSb3BXUt_RIWv9VtBpoc/s400/vib.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;無効にチェックを付けると無効状態が有効になるメニュー&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
メニュー上に無効化できる項目があるので、これにチェックを付けることで無効にする機能を有効にできます。&lt;br /&gt;
&lt;br /&gt;
ビブラートをピッチで描くこと自体はよくあることですが、その場合「ビブラートの振幅」をゼロにしておかないと自分がピッチで描いたものと干渉してしまい、本来イメージしていた高さで出力されないという想像に難くない問題が起きるので、ピッチで描く場合は始めから無効化しておきます。&lt;br /&gt;
&lt;br /&gt;
これに関しては神無月Pさんが Twitter で書かれていたような（&lt;a href=&quot;https://twitter.com/cl_kannaduki/status/592334200356741121&quot; target=&quot;_blank&quot;&gt;その1&lt;/a&gt;、&lt;a href=&quot;https://twitter.com/cl_kannaduki/status/592340355963453440&quot; target=&quot;_blank&quot;&gt;その2&lt;/a&gt;）、ピッチの画面でビブラートの振幅が加算された状態で見えるようになると状況も変わるかも知れません。&lt;br /&gt;
（が、多分個人的には全て手描きしたい時はビブラートに人間味のあるムラを求めてるシーンだと思うので結局あまり使わないような気はする）&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-2-4&quot;&gt;2-4. ピッチを描く時は必ずズーム状態で描く&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
CeVIO のピッチツールは大変センシティブで、あまり考えずにパパっと描くといい感じになりません。&lt;br /&gt;
なぜセンシティブなのかというのは非常に簡単な理由です。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXZy-0eHjpPJe58vDIJeYhVr5UthZBEWzZp36w8LxJPU_WJLRhPd5AQ8An-N14qnuATdBpltGZOQhBKzBeR2buxk_PBXPP9GKB-ggzaK65pvalrIJVnqccgJuwlQ_yP8VlSGCCnv5bS9c/s1600/pitch.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;176&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXZy-0eHjpPJe58vDIJeYhVr5UthZBEWzZp36w8LxJPU_WJLRhPd5AQ8An-N14qnuATdBpltGZOQhBKzBeR2buxk_PBXPP9GKB-ggzaK65pvalrIJVnqccgJuwlQ_yP8VlSGCCnv5bS9c/s400/pitch.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;VOCALOID で PBS=2 の時の CeVIO での範囲&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
そのままの拡大率でピッチを描いてしまうと精細なラインが要求されるシーンを画面上のわずか数ピクセルの中で表現しないといけないことになり、難易度が上がるというか無理に近い状況に陥ることになります。&lt;br /&gt;
&lt;br /&gt;
そのため、作業時にはかなり拡大しておかなければ綺麗なデータが作れません。自分の拡大率の目安は縦方向が1オクターブ程度で、いつも同程度に拡大すればビブラートを描く時に同じ大きさで描けば同じ振幅のビブラートになり作業しやすいです。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5yl2IGgaVZ23KzXPw9b68DN323fC9dgAKDf0siTZXoz6qZ7bifQEAo5PUQ2Z6QvYaBK7gm_anrfeXbxaYHYGpXQf4mvcOAOxQezrCtz5WhwNoRlJq5dZZ6yg_mg8uH4aoxF8M2BlGLks/s1600/pitch2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;218&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5yl2IGgaVZ23KzXPw9b68DN323fC9dgAKDf0siTZXoz6qZ7bifQEAo5PUQ2Z6QvYaBK7gm_anrfeXbxaYHYGpXQf4mvcOAOxQezrCtz5WhwNoRlJq5dZZ6yg_mg8uH4aoxF8M2BlGLks/s400/pitch2.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;自分が作業時に実際に使っている拡大率&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;b id=&quot;cevio-2-5&quot;&gt;2-5. データを入力する時はノート→TMG→PIT/VOLの順&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
基本的にピッチやボリュームはノートと TMG の情報を元にしているので、自分でピッチやボリュームを書き換えた後に改めてノートや TMG を触り直すとピッチやボリュームの部分が乱れるため実際のところこの辺は結構やり直しになります。&lt;br /&gt;
&lt;br /&gt;
これは構造上仕方がないことなので、せめてもの抵抗ということでメロディの入力と TMG の編集で大体納得できるレベルまで編集してから PIT と VOL の調整を始めるようにしています。&lt;br /&gt;
そうすると並行作業するよりも幾分作業効率が良くなります。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-2-6&quot;&gt;2-6. CeVIO で作ったデータを VOCALOID や UTAU に持ち込むなら&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
メロディを DAW で作ってから持ち込んでいる場合は MIDI を各種ソフトに持ち込む感じになるのでそれでもいいですが、CeVIO で作ったデータから歌詞付き MIDI ファイルを作るためのツールを先日作りました。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://oov.github.io/mxl2mid/&quot; target=&quot;_blank&quot;&gt;http://oov.github.io/mxl2mid/&lt;/a&gt;&lt;br /&gt;
（Safari 以外の最新のブラウザでアクセスして下さい）&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLPFE2zDEeHREFSsVF3uojJGW6e6O_1q10RXgy-pKIPyP7V2P1oi3nmrpHZGHtAbDjvrYhAWLRdbaTWIvCf3KFJXFs5tdiMoTA_pwFcm4ErtG5IU-S9OY_jaWmcq2f-u1WwKCBeESA_iM/s1600/exp.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;243&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLPFE2zDEeHREFSsVF3uojJGW6e6O_1q10RXgy-pKIPyP7V2P1oi3nmrpHZGHtAbDjvrYhAWLRdbaTWIvCf3KFJXFs5tdiMoTA_pwFcm4ErtG5IU-S9OY_jaWmcq2f-u1WwKCBeESA_iM/s400/exp.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;「MusicXML の書き出し」で出力したファイルを MIDI に変換できます&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
CeVIO と VOCALOID や UTAU とデュエットさせたい時とか、作業量を減らせると思うのでもし良ければ使ってみてください。&lt;br /&gt;
ちなみに CeVIO の 2015-05-22 のアップデートにより CeVIO 自身でもインポートできるようになりました。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-2-7&quot;&gt;2-7. まとめ&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
結局のところ CeVIO が持つ機能のいくつかを使わないことによって作業効率を上げています。&lt;br /&gt;
ミックス周りの機能が DAW の操作性に追いついていないなど仕方がなさそうな部分もあるものの、音量ツマミが -24db までしか下げられないために外部オーディオトラックの音量調整が上手くできないなど機構上の粗も目立ちます（ベースボリューム併用でもっと下げられるが、なぜそんな二度手間が必要なのか）。&lt;br /&gt;
適材適所という言葉もあるので、使いたい部分を上手く組み合わせて使うのがいいと思います。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-3&quot;&gt;■ 3. 調声の話&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
この辺も人によってやり方や考え方は様々だと思うので一例という感じでお読み下さい。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-3-1&quot;&gt;3-1. VOCALOID を使ったことがある人へ&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
ボーカルパートを作るという目的の上では最終的に目指すピッチの動きなどが似ることは多々ありますが、ソフトの使い方のお作法という意味では VOCALOID での経験が使えない場面も多々あります。&lt;br /&gt;
&lt;br /&gt;
VOCALOID での経験は一旦色々忘れると楽になります。&lt;br /&gt;
新しく覚える必要がある要素は決して多くはありません。&lt;br&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-3-2&quot;&gt;3-2. 歌わせる時は「本人に任せる」のが楽&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
基本的に CeVIO は元になった人の歌い方を再現しようとする性質があり、つまりキャラクターによって声だけではなく音の高さの変化の速さや発音のタイミング、ビブラート具合など歌い方自体が異なります。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBfp-8ecRSBdp0MXu7rVhis9HAyQWPxyIcVuDIXQamIMjp6rz8oCDOSkDfcoX6q0vlnfNBcGtwKo18jdxjyiugONWC6_XbpYomiMwooN7lPZP3pafJzXaGFuVB7RlS5I9U5OV8uPueKFI/s1600/diff.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBfp-8ecRSBdp0MXu7rVhis9HAyQWPxyIcVuDIXQamIMjp6rz8oCDOSkDfcoX6q0vlnfNBcGtwKo18jdxjyiugONWC6_XbpYomiMwooN7lPZP3pafJzXaGFuVB7RlS5I9U5OV8uPueKFI/s1600/diff.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;ピッチ変化の違い。ONE はアップテンポな曲、金咲小春はスローテンポな曲が歌いやすい&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
これらの特性は細部を細かく沢山変えることによってある程度塗り替えることはできるものの、その上で滑らかに歌わせるのはやはりなかなか難しいです。&lt;br /&gt;
そのため、そのキャラクターが得意な感じの歌を本人任せで歌ってもらい、気になるところだけ注文を付ける感じで直していくのがクオリティも作業量もいい感じで進められると思います。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-3-3&quot;&gt;3-3. スタッカートとブレスについて&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
「かもめの水兵さん」の「かもめ」の部分をスタッカートでリズム良く歌わせてみましょう。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjc0ZefR9BF7E9or1Rkvosy8K-Vb1ly768fpyVSH-Wi2vKfagaoeXqJgwBzel2issqKyA1lL5w-1tlBtURGt_PmdtVZk6KcABbitGiao18DGNrSvbyNz3D5Hh9bok1o61E21sSeI_3KY74/s1600/st.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjc0ZefR9BF7E9or1Rkvosy8K-Vb1ly768fpyVSH-Wi2vKfagaoeXqJgwBzel2issqKyA1lL5w-1tlBtURGt_PmdtVZk6KcABbitGiao18DGNrSvbyNz3D5Hh9bok1o61E21sSeI_3KY74/s1600/st.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-st.mp3&quot; target=&quot;_blank&quot;&gt;打ち込み比べてみたものを試聴する&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
CeVIO は音符がない場所にある程度の余裕があればそこでブレスを入れることがあります。&lt;br /&gt;
場合によりこのようなシーンでも細かくブレスが入ることがあり、そういう時は音符自体は繋げたままで歌詞として「っ」を入力することによって「cl」という無音部分を意図的に作ることができます。&lt;br /&gt;
（むしろ、ブレスが入って欲しくない場所は全て「っ」で入力したほうがいいと思う）&lt;br /&gt;
&lt;br /&gt;
また逆に、意図的にブレスを入れることもできます。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsrzXOOEocBgtuuTE_-rNuU2KdN9IPO52DIezGmk8wHOHiVbDN4BAjTXQoog5lJneTEaoaP60-BLh4qyPMMo67TxmXgs7DsRFg3K-7fTMhYPexIVhQ5Ji2Y189Kw7vKu0-NyOv_D3BDS0/s1600/br.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsrzXOOEocBgtuuTE_-rNuU2KdN9IPO52DIezGmk8wHOHiVbDN4BAjTXQoog5lJneTEaoaP60-BLh4qyPMMo67TxmXgs7DsRFg3K-7fTMhYPexIVhQ5Ji2Y189Kw7vKu0-NyOv_D3BDS0/s1600/br.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;右クリックメニューから開けるプロパティにブレスの項目がある&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
このようにすると、音符が繋がっていてもブレス「br」が入ります。&lt;br /&gt;
&lt;br /&gt;
こちらのブレスは短い時間で一気に吸い込む動きが向いていることが多いようなので、個人的にはAメロとBメロの間のように時間に余裕がある場合は休符によるブレス、忙しいフレーズの途中での息継ぎにはプロパティからのブレスのような形で使い分けをしています。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-3-4&quot;&gt;3-4. 「yes」などの発音をそれっぽくするための母音の脱落記号と「_0」の話&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
「yes」を発音させる時は「いぇす」というよりは「いぇｓ」ぐらいで、「す」は子音から母音までキッチリ発音しないほうがそれっぽく聴こえます。&lt;br /&gt;
これを実現させるために CeVIO には「母音の脱落記号」というものがあり、以下のようにすると使えます。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKMpQTE4xTQaqA1CUNIFUMZdy9HB86sjvrn_gKGcMXt_YDpxhWc5WPBoZsOGimsQVXuGBvmvesLBFRXphbHxPHJj1sI-hSoKa83AfaroqIJ4cAXo_gYjpM5YbapOCU67eNm4By6at4Vkw/s1600/boin.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKMpQTE4xTQaqA1CUNIFUMZdy9HB86sjvrn_gKGcMXt_YDpxhWc5WPBoZsOGimsQVXuGBvmvesLBFRXphbHxPHJj1sI-hSoKa83AfaroqIJ4cAXo_gYjpM5YbapOCU67eNm4By6at4Vkw/s1600/boin.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-yes.mp3&quot; target=&quot;_blank&quot;&gt;比べてみたものを試聴&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
単純に全角のアポストロフィー「’」を後ろに付けるだけです。&lt;br /&gt;
&lt;br /&gt;
なおこれは本当に「desu」を「des」にするように本当に母音を脱落させるだけで、VOCALOID で無声音のために「_0」を付与するものとは異なります。&lt;br /&gt;
寒い時に手を温めるために息をかける「はー」とか、熱いものを食べる時の「ふー」とかの喉の振動が伴わないものが無声音です。&lt;br /&gt;
テレビの寝起きドッキリで最初に言うようなヒソヒソ声での「おはようございます」とか、母音を含む言葉も無声音のままで発音できます。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-3-5&quot;&gt;3-5. 母音が綺麗に繋がらない時&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
「青い家」のような母音が連続する歌詞の時、歌い方がぎこちない感じになってしまうことがあります。&lt;br /&gt;
このような時は「スラー」を使うと改善する可能性があります。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIIrRQx5uri_HIQD7FuoE1SXLhsf8agwPXFiKmgpUdb2ci22stJkevp7wd8RouxM9d83_3SfM8KC2exrTZRlvNd4iJfTOro5IS0ooccG3sgdVVTZJSr6fOHCOIOIuuKonMRCpEqrfH6_I/s1600/slur.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;366&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIIrRQx5uri_HIQD7FuoE1SXLhsf8agwPXFiKmgpUdb2ci22stJkevp7wd8RouxM9d83_3SfM8KC2exrTZRlvNd4iJfTOro5IS0ooccG3sgdVVTZJSr6fOHCOIOIuuKonMRCpEqrfH6_I/s400/slur.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-slur.mp3&quot; target=&quot;_blank&quot;&gt;比べてみたものを試聴&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
スラーは右クリックメニューから開ける音符のプロパティで設定することができます。&lt;br /&gt;
この場合だと「あ」でスラーの開始を、「え」でスラーの終了を指定します。&lt;br /&gt;
CeVIO 3.2.17.3 以降ではスラーにしたい範囲のノートを選択して「R」ボタンを押すことでもスラーを設定できます。&lt;br&gt;
&lt;br /&gt;
ただしこれは上手くいった場合で、キャラクターによってはスラーでは改善しないことがあります。&lt;br /&gt;
その場合は&lt;a href=&quot;http://www.nicovideo.jp/watch/sm25421508&quot; target=&quot;_blank&quot;&gt;この動画のように TMG を調整すること&lt;/a&gt;で、滑らかに歌えるようになるかも知れません。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-3-6&quot;&gt;3-6. 発音が気に入らない時&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
歌ってもらう内容によってはなんかいい感じの発音にならない時もあります。&lt;br /&gt;
以下は黄咲愛里に「ミカヅキモ」と歌ってもらった例です。&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTyzZMYSHVJVmIjkpWEib_FOYUVGtcyMwclziQehvK6KQkaLyoAtT6gqz3nZeQhSh0kR5mdXm__LZTHXD_QnRqwcY7TuLZT2K8mjn88jxcFNeFqbbJ97Fm1ONlvnnJH__Z5wQP5vgzpw0/s1600/mz.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTyzZMYSHVJVmIjkpWEib_FOYUVGtcyMwclziQehvK6KQkaLyoAtT6gqz3nZeQhSh0kR5mdXm__LZTHXD_QnRqwcY7TuLZT2K8mjn88jxcFNeFqbbJ97Fm1ONlvnnJH__Z5wQP5vgzpw0/s1600/mz.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;そのままだと「も」が「もら」に聞こえるので直した例。&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-mz.mp3&quot; target=&quot;_blank&quot;&gt;比べてみたものを試聴&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
パッと見ではゴチャゴチャしていて取っ付きにくい TMG 画面ですが、実際に使うのは簡単で、下のアルファベットが書いてある部分の線を引っ張って母音や子音の位置を移動させたり、その上の線を引っ張って発音する上で気に入らない区間を縮めたりいいところを伸ばしたりして使います。&lt;br /&gt;
&lt;br /&gt;
今回の例では「あ」に近い発音の目立つ区間を潰すことで歌い方を抑えこんでいます。&lt;br /&gt;
なお、このように TMG を変更すると場合によってはピッチがずれて音痴になったりもするので、改めて直してあげないといけないこともあります。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-3-7&quot;&gt;3-7. ピッチを描く&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
これは CeVIO に限った話ではないのでここであまり真面目に書く必要もなさそうですが、よくあるパターンを簡単にまとめてみることにします。&lt;br /&gt;
音量の変化も合わせて描くとより効果的なパターンなどもありますが、ここではピッチのみの変化で紹介します。&lt;br /&gt;
&lt;br /&gt;
3-7-1. 上から入る&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBRHNzwyE1ZEtWLLLDBDAGIUbaKbthNoUgNQJIGN9aAF2dt9TdZca8SMhdxPgBR3FUArBJ0yd0CqPwxfp82AbCXDQNYQket6Z3QA2w2whFCrXir7V-JB8ThaCbmUynIMxDM1eTZQg_UUI/s1600/p1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBRHNzwyE1ZEtWLLLDBDAGIUbaKbthNoUgNQJIGN9aAF2dt9TdZca8SMhdxPgBR3FUArBJ0yd0CqPwxfp82AbCXDQNYQket6Z3QA2w2whFCrXir7V-JB8ThaCbmUynIMxDM1eTZQg_UUI/s1600/p1.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;癖のある歌い方をさせたい時に。&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-p1.mp3&quot; target=&quot;_blank&quot;&gt;試聴&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
上からでも下からでもアクセント強めの印象にしやすいです。&lt;br /&gt;
例えば&lt;a href=&quot;http://www.nicovideo.jp/watch/sm25704327&quot; target=&quot;_blank&quot;&gt;オコメライス&lt;/a&gt;で「タカハシもハマってるんだよ」のところは印象的にする目的で上に抜けるのと合わせて過剰に使っています。&lt;br /&gt;
&lt;br /&gt;
3-7-2. 上に抜ける&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuYsZzb9uUUrxjYr_qPR8WpsDWSUDsq0OQPq6fNT1WKB-3HIZZyTF9yEtPlOKSNAeGSqTLMOxewaV7_ZRnKapf6fnNFK3XC0q-ckoiILWVDq4SHf1E86GOSWKBZ5VBCssBq8pwIgNX6vk/s1600/p2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuYsZzb9uUUrxjYr_qPR8WpsDWSUDsq0OQPq6fNT1WKB-3HIZZyTF9yEtPlOKSNAeGSqTLMOxewaV7_ZRnKapf6fnNFK3XC0q-ckoiILWVDq4SHf1E86GOSWKBZ5VBCssBq8pwIgNX6vk/s1600/p2.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;ヒーカップ唱法的な奴。かわいい表現としても。&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-p2.mp3&quot; target=&quot;_blank&quot;&gt;試聴&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
B&#39;z の稲葉さんの歌い方で頻繁に見られる形なので&lt;a href=&quot;http://www.nicovideo.jp/watch/sm25946930&quot; target=&quot;_blank&quot;&gt;愛のままにわがままに僕は君だけを傷つけないをカバーした時&lt;/a&gt;には多用しました。&lt;br /&gt;
下に抜ける形は力を抜いていく感じの表現として使えます。&lt;br /&gt;
&lt;br /&gt;
3-7-3. 前にずらす&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAvJhKJvSCyYdWPvUjMKGAl0afOJiLg86cESmplq4etoNha2w-iPZNFHBAfld9hZ_CAI4xhbUSnKQlWSnnKvgvaLvV6EjR-4kR-zV2kgZjYjiJ513n5kM1NYk-7oa1ecuhD7WiMEwp9Ig/s1600/p3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAvJhKJvSCyYdWPvUjMKGAl0afOJiLg86cESmplq4etoNha2w-iPZNFHBAfld9hZ_CAI4xhbUSnKQlWSnnKvgvaLvV6EjR-4kR-zV2kgZjYjiJ513n5kM1NYk-7oa1ecuhD7WiMEwp9Ig/s1600/p3.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;多分バラードとかでは特に頻繁に出てくるパターン。&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-p3.mp3&quot; target=&quot;_blank&quot;&gt;試聴&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
よく使います。上手く歌ってる風になるか調子に乗ってる風になるか使い方次第で変わりそうです。&lt;br /&gt;
&lt;br /&gt;
3-7-4. 後ろにずらす&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpqToJpSiVK_GeiPcq94HyViJKaF-d9CmGnAJQ2lS6_3Aam0p62bDKEXMI7wPdV-6-Q9tnPfQvfdRbKPgmpTTGLIMQwu38c7vqpZeu9Z9VU1HOPCsxszJZs1tpvXcZuVWiTgRWHDuEpkY/s1600/p4.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpqToJpSiVK_GeiPcq94HyViJKaF-d9CmGnAJQ2lS6_3Aam0p62bDKEXMI7wPdV-6-Q9tnPfQvfdRbKPgmpTTGLIMQwu38c7vqpZeu9Z9VU1HOPCsxszJZs1tpvXcZuVWiTgRWHDuEpkY/s1600/p4.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;これもバラードとかで頻繁に出てくるパターン。&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-p4.mp3&quot; target=&quot;_blank&quot;&gt;試聴&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
これも同じ感じです。&lt;br /&gt;
&lt;br /&gt;
3-7-5. 優しく&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlc4Gi500uSX7DD6lFohbubhgT659kGsBla5065XsYpdFsLjsjgD7vpDKMh8H4l0Tnkr5HGGKAEIzHa3-W28lmZhenruwPgavcqpAts1AfKY3ZYioL_r4DhuoDMoBDUbG9w98YMKTLQmU/s1600/p5.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlc4Gi500uSX7DD6lFohbubhgT659kGsBla5065XsYpdFsLjsjgD7vpDKMh8H4l0Tnkr5HGGKAEIzHa3-W28lmZhenruwPgavcqpAts1AfKY3ZYioL_r4DhuoDMoBDUbG9w98YMKTLQmU/s1600/p5.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;スローテンポな曲とか、柔らかい歌い方をさせたい時。&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-p5.mp3&quot; target=&quot;_blank&quot;&gt;試聴&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
緩やかに目標のピッチに移行するイメージです。&lt;br /&gt;
これは例えば「レ」の頭を下から持ち上げていますが、「ド」の後ろを持ち上げる手もあります（上から下に向かう場合はそのほうが安定しやすいです）。&lt;br /&gt;
ただしこれはミスると音痴に感じやすいので注意も必要です。&lt;br /&gt;
&lt;br /&gt;
3-7-6. 強く&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3i9XsKk7uHH1InrQ6XF7hhRwAE8AvaKhb1rb4HPE51XSPNTYnuHCD2yifM-DDIdtD0yvhHETA3QkyemARnndUnMpqWX6l2kFnPq3r3qEcKDT0RLv2taGQsybMSRXtPCHKo50iv-n6awc/s1600/p6.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3i9XsKk7uHH1InrQ6XF7hhRwAE8AvaKhb1rb4HPE51XSPNTYnuHCD2yifM-DDIdtD0yvhHETA3QkyemARnndUnMpqWX6l2kFnPq3r3qEcKDT0RLv2taGQsybMSRXtPCHKo50iv-n6awc/s1600/p6.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;ちょっと上に行きすぎる感じで力みを表現。&lt;a href=&quot;http://test.oov.ch/junk/20150521_blog-p6.mp3&quot; target=&quot;_blank&quot;&gt;試聴&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
アタック感強めな感じをピッチで演出しています。&lt;a href=&quot;http://www.nicovideo.jp/watch/sm21554922&quot; target=&quot;_blank&quot;&gt;2013年にささら音頭を作った時&lt;/a&gt;、当時は CeVIO にピッチ編集機能が無かったので VocalShifter を使っているものの、同じ手法を用いて表現しています。&lt;br /&gt;
&lt;br /&gt;
3-7-7. まとめ&lt;br /&gt;
&lt;br /&gt;
ピッチ周りは本当に色々な表現があるし、実際に歌っているものを参考にすると得られるものがたくさんあるので色々な曲を聴いてみるのが一番いいと思います。&lt;br /&gt;
得られたバリエーションは音声合成に留まらず自分で歌う時でさえ活用でき、とても有用です。&lt;br /&gt;
&lt;br /&gt;
&lt;b id=&quot;cevio-4&quot;&gt;■ 4. 終わりに&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
CeVIO 界隈はまだまだ発展途上にあり、例えば pixiv に投稿されるイラストなども決して多くはないのですが、2013年にりちかさんが投稿されている&lt;a href=&quot;http://www.pixiv.net/member_illust.php?mode=medium&amp;amp;illust_id=36569510&quot; target=&quot;_blank&quot;&gt;【動画作成にご利用ください】 「CeVIO支援中」タグ&lt;/a&gt;というものもありまして、イラストを投稿される際には差し支えなければこのタグを付けて頂けると、楽曲用の動画製作時に利用させて頂く選択肢が増えるので大変嬉しく思います。&lt;br /&gt;
（ところで &lt;a href=&quot;http://piapro.jp/license/other_character_guideline&quot; target=&quot;_blank&quot;&gt;piapro はここに CeVIO キャラの名前がなく&lt;/a&gt;投稿は厳密には NG のような気がしているので、CeVIO 関連は自分のアカウントからは登録しないようにしています）&lt;br /&gt;
&lt;br /&gt;
また、&lt;a href=&quot;http://camp.joysound.com/colorvoice/&quot; target=&quot;_blank&quot;&gt;Color Voice Series&lt;/a&gt; を使ってニコニコ動画に投稿する際は &lt;a href=&quot;http://commons.nicovideo.jp/material/nc107951&quot; target=&quot;_blank&quot;&gt;nc107951&lt;/a&gt; を親作品に登録してくださいとの事で、登録するだけの簡単なお仕事なのでこちらも是非登録していくと良いと思います。&lt;br /&gt;
&lt;br /&gt;
あと、
&lt;br /&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;ja&quot;&gt;
&lt;div dir=&quot;ltr&quot; lang=&quot;ja&quot;&gt;
CeVIOプロジェクトではさとうささら.comに掲載させていただける作品を常に募集しております。掲載ＯＫな方は、ニコニコ動画やpixiv等で「ささら.com_掲載OK」のタグをつけて投稿して頂けると嬉しいですm(_ _)m&lt;/div&gt;
— CeVIOプロジェクト スタッフ公式 (@CeVIO_st) &lt;a href=&quot;https://twitter.com/CeVIO_st/status/603097730320179200&quot;&gt;2015, 5月 26&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; charset=&quot;utf-8&quot; src=&quot;//platform.twitter.com/widgets.js&quot;&gt;&lt;/script&gt;
とのことですので、こちらも合わせてご利用下さい。&lt;br /&gt;
&lt;a href=&quot;http://satosasara.com/&quot; target=&quot;_blank&quot;&gt;http://satosasara.com/&lt;/a&gt; のコンテンツ欄に載ったりします。&lt;br /&gt;
&lt;br /&gt;
CeVIO における音声やキャラクターの利用可能な範囲については無料版が出た当時色々言われていたものの現在はかなりオープンになっており、&lt;a href=&quot;http://cevio.jp/commercial/&quot; target=&quot;_blank&quot;&gt;非商用でも商用でも使える範囲について細かく記載されている&lt;/a&gt;ので、是非確認してみるのをオススメします（ONE や Color Voice Series など他社キャラクターのライセンスは各社の HP 上にあるのでそちらも確認して下さい）。&lt;br /&gt;
不明点がある場合も質問すればきちんと答えてくれると思いますので Twitter とかで「わからない…」とか呟くぐらいなら実際に聞いてみるといいと思います。&lt;br /&gt;
&lt;br /&gt;
個人的にも CeVIO 界隈にはもっと盛り上がって欲しいと思っていて、そのために自分が持てる情報は積極的に公開していこうという考えのもと、最近は CCS ファイルの公開やこのブログの記事を書いたりなどしております。&lt;br /&gt;
自分だけがこういう情報を持っていても少し優越感に浸れるぐらいしか利点がなくて全然面白くないし、もっと他の人にも CeVIO を使ってもらい面白い何かを作って欲しいな、という心境です。&lt;br /&gt;
&lt;br /&gt;
どんどんお願いします。どんどん。&lt;br /&gt;
&lt;br /&gt;
2015-05-26 - 「ささら.com_掲載OK」に関する記述を追加&lt;br /&gt;
2015-05-28 - タイトルっぽい画像を追加&lt;br&gt;
2015-05-28 - CeVIO 3.2.17.3 のリリースに伴って変わった部分について追記&lt;br&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/5636584534911237931'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/5636584534911237931'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2015/05/cevio.html' title='CeVIO で歌わせる時に考えていること'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEib8lLJs8PCCC1SlGUta1K4mS1WnAt0gtSWD8yiyuK2OWVs71QM4XCuu1xFRrwlfCgBuIATidz1N9fb51ZefX-VR-RhPJ75hrFzoHbU5EH3rPpFf5mJnmNU5IxyDnRwfXJYY_NvzdqHXqE/s72-c/tit.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-754387573075278864</id><published>2015-04-10T04:35:00.000+09:00</published><updated>2015-10-16T23:15:55.741+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="golang"/><category scheme="http://www.blogger.com/atom/ns#" term="Go言語"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><title type='text'>Go で作ったプログラムを GopherJS を使ってブラウザで動かした話</title><content type='html'>先日 &lt;a href=&quot;http://cevio.jp/&quot; target=&quot;_blank&quot;&gt;CeVIO Creative Studio&lt;/a&gt; で出力した&amp;nbsp;&lt;a href=&quot;http://www.musicxml.com/&quot; target=&quot;_blank&quot;&gt;MusicXML&lt;/a&gt; を MIDI ファイルに変換するプログラムを&lt;a href=&quot;https://github.com/oov/mxl2mid&quot; target=&quot;_blank&quot;&gt;作った&lt;/a&gt;。&lt;br /&gt;
&lt;br /&gt;
これは &lt;a href=&quot;http://golang.org/&quot; target=&quot;_blank&quot;&gt;Go 言語&lt;/a&gt;を使って作ったのだが、頻繁に使うわけでもないツールをダウンロードして使うのは面倒だし、バージョンアップなどを考えるとブラウザから使えたほうが手軽かもしれないと思ったので、プログラムを更に&amp;nbsp;&lt;a href=&quot;http://www.gopherjs.org/&quot; target=&quot;_blank&quot;&gt;GopherJS&lt;/a&gt;&amp;nbsp;で&lt;a href=&quot;http://oov.github.io/mxl2mid/&quot; target=&quot;_blank&quot;&gt;ブラウザ単体で動くようにしてみた&lt;/a&gt;。&lt;br /&gt;
サーバサイドでは一切何もやらないため、GitHub Pages に置くこともできる。&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
GopherJS は C 言語に対する &lt;a href=&quot;http://ja.wikipedia.org/wiki/Emscripten&quot; target=&quot;_blank&quot;&gt;Emscripten&lt;/a&gt; のようなもので、Go 言語で書いたプログラムを JavaScript にコンパイルすることができる。&lt;br /&gt;
どの程度そのままで動くのかは&lt;a href=&quot;https://github.com/gopherjs/gopherjs/blob/master/doc/packages.md&quot; target=&quot;_blank&quot;&gt;ここに対応表があり&lt;/a&gt;、例えばローカルファイルを触るような操作は node.js でのみ対応だったり、cgo や syscall のように Go 言語で書かれていない部分が関わるものは動かなかったりもする。&lt;br /&gt;
Goroutine にも対応しているらしいのだが、今回は使用しなかったため特に触れない。&lt;br /&gt;
&lt;br /&gt;
今回のプログラムは元々はコマンドラインで引数としてファイルを渡すとそれを読み込み、ファイルを作成して終了するといった動作をするもの。&lt;br /&gt;
先述の通りローカルファイルへのアクセスはブラウザ上では難しいため、その部分は入出力共に ArrayBuffer で行うことにした。&lt;br /&gt;
&lt;br /&gt;
以下、ここだけ掴んでおけばファイルを受け取ってファイルを吐くツール相当のものが作れるメモ。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;1. Go 言語で書いた処理をJavaScript 側から&amp;nbsp;呼び出すには&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
今回のイメージとしては、jQuery などと同じようにライブラリとして Go 言語で書いた処理を読み込んでおき、必要なところで JavaScript から呼び出すような使い方をしたい。&lt;br /&gt;
&lt;br /&gt;
そのためには&lt;a href=&quot;https://github.com/gopherjs/gopherjs#providing-library-functions-for-use-in-other-javascript-code&quot; target=&quot;_blank&quot;&gt;この辺を参考にするといい&lt;/a&gt;。&lt;br /&gt;
&lt;br /&gt;
js.Global.Set を使って登録すると、JavaScript からその名前でアクセスできるようになる。&lt;br /&gt;
自分のプログラムではオブジェクトである必要すらなかったので、map ではなく直接関数を登録した。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;2. JavaScript から Go 言語にバイナリデータを渡す&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
やり方は色々あるかも知れないが、先述の通り ArrayBuffer で行うことにしたので、JavaScript から以下のような感じでファイルを &lt;code&gt;&amp;lt;input type=&quot;file&quot; id=&quot;inputfile&quot;&amp;gt;&lt;/code&gt; から読み込むような処理を書いた。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-js&quot;&gt; var file = document.getElementById(&#39;inputfile&#39;).files[0];
 var r = new FileReader();
 r.onload=function(e){
  YourGoFunc(r.result);
 };
 r.readAsArrayBuffer(file);
&lt;/pre&gt;
これで Go 言語側に第一引数として ArrayBuffer が渡されてくる。&lt;br /&gt;
渡されてきたデータは&lt;a href=&quot;https://github.com/gopherjs/gopherjs/blob/269de2ef2562eaebbb914ee4145a2e94c0564cce/compiler/natives/net/http/http.go#L41&quot; target=&quot;_blank&quot;&gt;このコード&lt;/a&gt;で&amp;nbsp;xhr.Get(&quot;response&quot;) を []byte に変換しているのと全く同じ方法で、[]byte に変換できる。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;3. Go 言語から JavaScript にバイナリデータを渡す&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Go 言語からは &lt;a href=&quot;https://godoc.org/github.com/gopherjs/gopherjs/js#NewArrayBuffer&quot; target=&quot;_blank&quot;&gt;js.NewArrayBuffer&lt;/a&gt;&amp;nbsp;を使えば []byte から簡単に ArrayBuffer に変換できる。&lt;br /&gt;
今回のツールではその ArrayBuffer を元に Blob を作成し、それを &lt;a href=&quot;https://github.com/eligrey/FileSaver.js&quot; target=&quot;_blank&quot;&gt;FileSaver.js&lt;/a&gt; で保存させるようにした。&lt;br /&gt;
Safari とかいうブラウザではちゃんとダウンロードできない問題があるらしい。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;まとめ&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
ファイル関連の処理を差し替えた以外には何もしなくても動かすことができた。&lt;br /&gt;
Go 言語を使っている場合、データの入出力には io.Reader や io.Writer を使うのが定石のようなものなので、変にファイルに依存せずしっかりやっておけば大変持ち込みやすい。&lt;br /&gt;
&lt;br /&gt;
実際の処理では Shift_JIS の XML を UTF-8 に変換しながら読み込み、MIDI ファイルの出力時には改めて Shift_JIS に変換して保存しているが、XML の読み込みは標準ライブラリがあり、文字コード変換には &lt;a href=&quot;http://godoc.org/golang.org/x/text/encoding&quot; target=&quot;_blank&quot;&gt;golang.org/x/text/encoding&lt;/a&gt;&amp;nbsp;という準標準ライブラリもあり、いずれも大変完成度が高く Pure Go で書かれており外部ファイルにも依存していないため、何の問題もなく持ち込めたのが良かった。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://godoc.org/golang.org/x/net/html/charset#NewReaderLabel&quot; target=&quot;_blank&quot;&gt;charset.NewReaderLabel&lt;/a&gt; を使用すると XML 読み込み時に必要なことを全部やってくれて全く手間がかからないため今回はこれを利用したが、Big5 など今のところは必要ない文字コード変換周りの処理も読み込まれてしまうので、多少オーバーヘッドはありそうだ。&lt;br /&gt;
&lt;br /&gt;
今回のプログラムは Windows 64bit 環境で &lt;kbd&gt;go build&lt;/kbd&gt; すると 3.78MB になるが、&lt;kbd&gt;gopherjs build -m&lt;/kbd&gt; すると js 2.39MB&amp;nbsp;+ js.map 68KB と大きさもなかなか健闘している感じになり、js ファイルを更に gzip 圧縮すると 463KB まで縮んだ。&lt;br /&gt;
ブラウザからアクセスした場合に転送される容量としては小さくはないが、利便性を考えると個人的には許容範囲内に感じた。&lt;br /&gt;
&lt;br /&gt;
使いはじめるための準備も、Go 言語利用者なら誰でも知っている &lt;kbd&gt;go get -u github.com/gopherjs/gopherjs&lt;/kbd&gt; だけで良く、簡単で素晴らしい。&lt;br /&gt;
今後も機会があれば積極的に利用していきたい。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/754387573075278864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/754387573075278864'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2015/04/go-gopherjs.html' title='Go で作ったプログラムを GopherJS を使ってブラウザで動かした話'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-4579812696862213892</id><published>2015-02-12T15:11:00.000+09:00</published><updated>2015-04-10T04:40:07.099+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="golang"/><category scheme="http://www.blogger.com/atom/ns#" term="Go言語"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><category scheme="http://www.blogger.com/atom/ns#" term="音楽"/><title type='text'>Go言語で E-MU Proteus Pack for Kontakt 専用リネーマーを作った</title><content type='html'>わりとただの日記。&lt;br /&gt;
&lt;br /&gt;
以前 &lt;a href=&quot;http://jp.creative.com/products/legacyproduct.asp?category=237&amp;amp;subcategory=239&amp;amp;product=14388&quot; target=&quot;_blank&quot;&gt;E-MU 1616M と Emulator X&lt;/a&gt; のバンドルを買ったことがあって、E-MU 1616M は半年前までは使っていたし Emulator X も古臭い音だけど個性的でわりと好きだった。&lt;br /&gt;
しかし E-MU 1616M 用のドライバ最終リリースが Windows7 用のベータ版で止まっていたり、64bit 対応した Emulator X3 は日本で出ていなかったり、ここ数年は色々な面で危険な匂いが漂っていた。&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
去年の後半、マシンを新調するついでにサウンドカードは&lt;a href=&quot;http://www.dawbench.com/audio-int-lowlatency3.htm&quot; target=&quot;_blank&quot;&gt;このグラフ&lt;/a&gt;を見ながら懐事情と相談して&amp;nbsp;&lt;a href=&quot;http://www.amazon.co.jp/gp/product/B0042DM2FW/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=247&amp;amp;creative=7399&amp;amp;creativeASIN=B0042DM2FW&amp;amp;linkCode=as2&amp;amp;tag=oov-22&quot;&gt;Focusrite Saffire PRO 14&lt;/a&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; src=&quot;http://ir-jp.amazon-adsystem.com/e/ir?t=oov-22&amp;amp;l=as2&amp;amp;o=9&amp;amp;a=B0042DM2FW&quot; height=&quot;1&quot; style=&quot;border: none !important; margin: 0px !important;&quot; width=&quot;1&quot; /&gt;
 を FireWire-Thunderbolt 変換ケーブルを経由して Thunderbolt 接続で使うように変えた。&lt;br /&gt;
そのグラフを見る限り RME は異常だけど基本的には PCIe &amp;gt; FireWire &amp;gt; USB の構図で、FireWire より更に PCIe に近いであろう Thunderbolt 環境があると将来的にも安泰かもしれない、ということもあって Windows で Thunderbolt 接続することを前提に&amp;nbsp;&lt;a href=&quot;http://www.asus.com/jp/Motherboards/ThunderboltEX_II/&quot; target=&quot;_blank&quot;&gt;ThunderboltEX II&lt;/a&gt;&amp;nbsp;が使えるマザーボードでマシンを組んだりした。&lt;br /&gt;
（だから経路としては現状 Saffire PRO 14 -&amp;gt; FireWire6pin↔9pin 変換 -&amp;gt; Apple Thunderbolt - FireWireアダプタ -&amp;gt; ThunderboltEX II -&amp;gt; マザボ、とアホみたいに遠い）&lt;br /&gt;
&lt;br /&gt;
その結果 Saffire PRO 14 は大変好調で文句ない環境になったのだが、当然 Emulator X の代わりは手元にない。&lt;br /&gt;
Emulator X といえば Proteus サウンド、そこで代用品として&amp;nbsp;&lt;a href=&quot;https://www.digitalsoundfactory.com/&quot; target=&quot;_blank&quot;&gt;Digital Sound Factory&lt;/a&gt;&amp;nbsp;の &lt;a href=&quot;https://www.digitalsoundfactory.com/e-mu-proteus-pack&quot; target=&quot;_blank&quot;&gt;E-MU Proteus Pack&lt;/a&gt;&amp;nbsp;の Kontakt 版を買うことにした。Proteus 2000 の他に&amp;nbsp;Virtuoso と Mo&#39; Phatt と Planet Earth と Vintage Pro と Xtreme Lead-1 がついて $99 だった（時期によってはセールで$69とかになるらしい）。&lt;br /&gt;
実際のところはまあぶっちゃけ買わなくても良かった気もするんだけど。&lt;br /&gt;
今となっては小さい波形容量の PCM 音源の音って独特で好きなもので。&lt;br /&gt;
&lt;br /&gt;
で、実際に手に入れてみるとファイル構造がこんな感じになっていて、実機上で表示されていたと思われる &#39;kb1&#39; のようなカテゴリ名がなく、プリセットの順番もグチャグチャになっていた。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgljsh5KOfkNGrZoN0Pt6qsQAG7-YVsLGkWFJE9ZOUPh05Fb6crPkGbQZADgWb0GD2v-Q5HR_9KoVdrujO51jowLz3t52GM3qq-CbVSh0_raR6jirnlfe-_lzysmb2ZPVIdo11_pP1AbP0/s1600/sample.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgljsh5KOfkNGrZoN0Pt6qsQAG7-YVsLGkWFJE9ZOUPh05Fb6crPkGbQZADgWb0GD2v-Q5HR_9KoVdrujO51jowLz3t52GM3qq-CbVSh0_raR6jirnlfe-_lzysmb2ZPVIdo11_pP1AbP0/s1600/sample.png&quot; height=&quot;400&quot; width=&quot;336&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
多分 Emulator X の時もこの辺適当だった気がするしこんなもんかな、とは思ったものの、このままだと実際に使う時音色を探すのに苦労する。&lt;br /&gt;
そのため実機の順番で並ぶようにファイル名を変えたいのだが、全部で3000個近いファイル数があり、手作業で変えていくのはちょっと面倒臭い。&lt;br /&gt;
&lt;br /&gt;
というわけで、これをある程度少ない手数でリネームするために、専用のツールを作ろうと思った。&lt;br /&gt;
ここまでが前置き。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;1. 本来のプリセットの順番やカテゴリ名をデータ化する&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
まずこれをデータにしないと始まらない。&lt;br /&gt;
ハード音源のマニュアルは後ろのほうにプリセット一覧があると思ったので少し探してみたところ、&lt;a href=&quot;https://archive.org/details/synthmanual-emu-proteus-2000-owners-manual&quot; target=&quot;_blank&quot;&gt;Proteus 2000 のマニュアルがアーカイブされていた&lt;/a&gt;。&lt;br /&gt;
&lt;br /&gt;
この一覧は簡素なリストなので、PDF 版の該当部分をコピーするだけでかなり綺麗な形になった。&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;0. kb1:DynamicGrand
1. kb4:Yo My Dynos
2. orc:Orchestral
3. gtr:Grusty
4. bs1:A Ku Stq
5. kb3:Rock&#39;in B
.
.
.
&lt;/pre&gt;
これを、プログラムから扱いやすいように CSV フォーマットに変換する。&lt;br /&gt;
以下の様な正規表現で変換した（本当はサクラエディタ上でやった）。&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;$ cat bank0.txt | sed -e &quot;s/\([0-9]\+\)\. \(...\):\(.\+\)/\1,\2,\3/g&quot; &amp;gt; bank0.csv&lt;/pre&gt;
これだけでわりと上手くいったので、この調子で Proteus 2000 のバンク8個と、Mo&#39; Phatt / Planet Earth / Vintage Pro / Xtreme Lead のバンク4個ずつ、合計 24*128=3072 個分をデータ化した。&lt;br /&gt;
&lt;br /&gt;
ちなみに Xtreme Lead の Bank 2 に「4. bpm:&quot;&lt;a href=&quot;http://ja.wikipedia.org/wiki/%E3%81%9D%E3%81%AE%E6%97%A5%E3%82%92%E6%91%98%E3%82%81&quot; target=&quot;_blank&quot;&gt;Carpe Diem&lt;/a&gt;&quot;」というダブルクォートが入ったものがあったり、Planet Earth の Bank 2 に「60. pr1:BD,Tm,Sn Set」というカンマを含んだものがあったりして若干罠がある。&lt;br /&gt;
&lt;br /&gt;
ともあれ、いくつかの修正を経て&amp;nbsp;Virtuoso 以外のデータ化がサクッと完了した。&lt;br /&gt;
Virtuoso だけはファイル名と実機上のパッチ名が目視でわかるレベルで差が大きく、かつファイル名から音が大体想像つくネーミングになっているので今回の変換処理の対象からは除外することにした。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;2. Kontakt 用 nki&amp;nbsp;&lt;/b&gt;&lt;b&gt;ファイルとパッチの対応付けをどうやるか考える&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
実機上のパッチ名と Kontakt 用ファイル名が 100% 一致しているなら非常に簡単に済むのだが、Proteus 2000 の Bank 0 の一番最初の「DynamicGrand」がそもそも「Dynamic Grand.nki」とスペースが入ったファイル名になっており、完全に一致していることは期待できそうにない。&lt;br /&gt;
&lt;br /&gt;
とはいえ似た名前にはなっているようなので、今回は&lt;a href=&quot;http://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%BC%E3%83%99%E3%83%B3%E3%82%B7%E3%83%A5%E3%82%BF%E3%82%A4%E3%83%B3%E8%B7%9D%E9%9B%A2&quot; target=&quot;_blank&quot;&gt;レーベンシュタイン距離&lt;/a&gt;が近いものを候補として挙げる方向で作業を進めることにした。&lt;br /&gt;
&lt;br /&gt;
また、元々のパッチ名にはファイル名として使用できない事が多いダブルクォートやスラッシュなどを含んでいるものが存在しているので、レーベンシュタイン距離を求める前にパッチ側の記号はアンダースコアに置換しておくことにする。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;3. リネームプログラムを作る&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
ここで説明した内容に合わせてプログラムを作ると、実機上パッチ名と実際のファイル名から拡張子を取り除いたもので完全一致しているファイルの数が出せる。&lt;br /&gt;
完全一致した数は2663件、つまり409件が完全一致しなかった。&lt;br /&gt;
&lt;br /&gt;
エラー率としては13%程度なのでそんなに悪くないが、もう少し改善したい。&lt;br /&gt;
ここからは泥臭い戦いを繰り広げていく。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;4. 検出率の改善&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
「DynamicGrand」の例のように完全に一致していないが正解しているパターン、Proteus 2000 の Bank 3 「84. str:Section」「125. brs:Section」のようにパッチ名が被っていて誤検出してしまうパターンなど、失敗のパターンはいくつかあるようだ。&lt;br /&gt;
&lt;br /&gt;
まずは完全一致していないが正解しているパターンを成功として扱えるようにしていきたい。&lt;br /&gt;
名前が惜しいケースでは以下の様なものがあった。&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;単語の間にスペースが入っているもの&lt;/li&gt;
&lt;li&gt;記号が「_」に変換ではなく取り除かれているもの&lt;/li&gt;
&lt;li&gt;「Drms」が「Drums」になるなど短縮形が戻されているもの&lt;/li&gt;
&lt;li&gt;実機のパッチ名の後ろの「 w」が省略されているもの（パンが左右に振られていたパッチ？）&lt;/li&gt;
&lt;li&gt;実機のパッチ名の後ろの「 mono」が省略されているもの（同時発音数が1だったパッチ？）&lt;/li&gt;
&lt;li&gt;実機のパッチ名の後ろの「 MW」が省略されているもの（モジュレーションホイールで特殊効果があったパッチ？）&lt;/li&gt;
&lt;/ul&gt;
上記のパターンの中で、最後の3つは簡単に機械的な対応できそうだ。&lt;br /&gt;
&lt;div&gt;
実機上のパッチ名が「Pizz&amp;amp;Sct MW」のように「 w」「 mono」「 MW」で終わる場合、それを外した名前で完全一致するものがある場合はそれを採用するように変えてみることにする。&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
この修正によって71件が成功として扱われるようになった。一部「w」や「MW」の前にスペースがないパッチもあり、現状これらは漏れているがひとまずスルーしておく。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
短縮形が戻されているものについても対応したいが、これらはスペースの有無も関係しており、両方を同時に対応しないと作業量の割に成功率があまり上がらなそうだった。&lt;/div&gt;
&lt;div&gt;
そのためこれに対しては、実機上のパッチ名の短縮形の単語を長いものに展開を試みる処理を加え、更に nki ファイルの名前からスペースや記号を取り除いた名前で比較するようにし、これによって完全一致するようになったものを成功として扱うようにする。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
この修正によって更に111件が成功として扱われるようになった。&lt;/div&gt;
&lt;div&gt;
細かい処理を変えることでここの件数もまだ少し増やすことができると思うが、ひとまず保留とする。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;5. 最後の悪あがき&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
一定のルールに従って修正できそうなものはある程度丸め込むことができた。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
まだ残っているものは法則性が薄いか、正しいかどうかがすぐに判断できないものが多い。&lt;/div&gt;
&lt;div&gt;
ここからはリネーム対象にならないまま残った nki ファイルのリストと対象ファイルが見つけられなかったパッチのリストを見比べ、手作業で対応付けを行っていくことにする。&lt;/div&gt;
&lt;div&gt;
そのため、対応するパッチが見つからないまま余ったファイル一覧を見ながら、目視で正しい変換をどうかを「認定」あるいは間違っている場合は手動で正しいものを「指定」することにする。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
しかし作業を始めてみるとこれがなかなかつらい。&lt;/div&gt;
&lt;div&gt;
0-127 までの128種類あるはずなのにファイルが 127 個しかないディレクトリがあったり、他のバンクに探している音色用のファイルがあるように見えるものなど、どう対応していいのか微妙なものが少なくない。&lt;br /&gt;
何より、実機での音がわからないので本当に正しいのか確証が持てない。&lt;br /&gt;
&lt;br /&gt;
結局100個程度手動で対応したが、80個以上の分類不能なままのファイルが残った。&lt;br /&gt;
最後に手作業で割り当てた分が全部正しいと仮定すればここまでで300個近くが成功になっていて、10% 近く改善できたので今回はこれで我慢しようと思う。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAFUqyKYsSFc0VJchv1GAsgS3ROYfX1sB6-Pg17GW7_xm-ww528bJ5a1rC6-HzD9UJPpVhPYMTcJZQhffe9l7EdGuPcz0mbfj-KrRJXl3E9lCIU_oQkF8F4WHcG1NJ54oiTrVvJ9RgvuY/s1600/sample2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAFUqyKYsSFc0VJchv1GAsgS3ROYfX1sB6-Pg17GW7_xm-ww528bJ5a1rC6-HzD9UJPpVhPYMTcJZQhffe9l7EdGuPcz0mbfj-KrRJXl3E9lCIU_oQkF8F4WHcG1NJ54oiTrVvJ9RgvuY/s1600/sample2.png&quot; height=&quot;400&quot; width=&quot;260&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
今回作ったプログラムはこちら。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://github.com/oov/proteus-pack-renamer&quot; target=&quot;_blank&quot;&gt;http://github.com/oov/proteus-pack-renamer&lt;/a&gt;&lt;br /&gt;
多分使う人はほとんどいないと思う。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;6. おまけ：全てのリバーブを外したい&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
なんかしばしばイマイチなリバーブが深めにデフォルトで入ったりしているので、全部切ってしまいたい。&lt;br /&gt;
全ての nki ファイルを書き換える必要があるので、これも nki ファイルの中身を解析して、リバーブをなしに差し替えるツールを……作るのは辛いので、ここは &lt;a href=&quot;https://www.autoitscript.com/site/autoit/&quot; target=&quot;_blank&quot;&gt;AutoIt&lt;/a&gt; でクリアすることにする。&lt;/div&gt;
&lt;pre class=&quot;prettyprint lang-au3&quot;&gt;Local $sPath = &quot;T:\EMU_Proteus_Pack_Kontakt\Proteus 2000\Proteus 2000 Bank 0\&quot;
Local $hWnd = WinWait(&quot;[TITLE:Kontakt 5; REGEXPCLASS:NINormalWindow.*]&quot;)

ReverbBypassDir($hWnd, $sPath &amp;amp; &quot;*.nki&quot;)

Func ReverbBypassDir($hWnd, $pattern)
   Local $h = FileFindFirstFile($pattern)
   If $h = -1 Then
      Return False
   EndIf

   Local $s = &quot;&quot;, $iResult = 0
   While 1
      $s = FileFindNextFile($h)
      If @error Then ExitLoop
   ReverbBypass($hWnd, $sPath &amp;amp; $s)
   WEnd
   FileClose($h)
EndFunc

Func ReverbBypass($hWnd, $sPath)
   WinActivate($hWnd)

   ; Open an instrument
   ControlClick($hWnd, &quot;&quot;, &quot;&quot;, &quot;left&quot;, 1, 315, 23)
   ControlClick($hWnd, &quot;&quot;, &quot;&quot;, &quot;left&quot;, 1, 315, 93)
   Local $hOPWnd = WinWait(&quot;[CLASS:#32770]&quot;)
   ControlSetText($hOPWnd, &quot;&quot;, &quot;[CLASS:Edit; INSTANCE:1]&quot;, $sPath)
   Send(&quot;!O&quot;)
   Sleep(1500)

   ; Select the tab &quot;Proteus 2000&quot;
   ControlClick($hWnd, &quot;&quot;, &quot;&quot;, &quot;left&quot;, 1, 60, 303)
   Sleep(200)

   ; Select the FX Type &amp;lt;&amp;lt;Bypass&amp;gt;&amp;gt;
   ControlClick($hWnd, &quot;&quot;, &quot;&quot;, &quot;left&quot;, 1, 376, 188)
   Sleep(200)
   ControlClick($hWnd, &quot;&quot;, &quot;&quot;, &quot;left&quot;, 1, 376, 209)
   Sleep(200)

   ; Save and close
   ControlClick($hWnd, &quot;&quot;, &quot;&quot;, &quot;left&quot;, 1, 632, 97)
   Sleep(200)
   Send(&quot;!Y&quot;)
EndFunc
&lt;/pre&gt;
処理内容はかなり簡単で、$sPath に指定されたディレクトリ内にある *.nki ファイルを起動済みの Kontakt 5 で開いて（Files メニューをクリック → Load... をクリック → 入力欄にファイルへのフルパスを指定 → Alt+O）、FX Type を選択しなおして閉じる。&lt;br /&gt;
閉じる時に保存するか聞かれるので、そこで「はい」と答えるだけの単純なスクリプト。&lt;br /&gt;
本当は Proteus 2000 タブを選択する処理も必要ないはずだったのだが、Proteus 2000 バンク 0 「L.V.&#39;s Worm.nki」のように初期状態で開いているタブが異なっている音色が一部あったので、タブを選ぶ処理も加えた。&lt;br /&gt;
128個処理するのに大体400秒程度なので、そんなに時間も掛からずほぼ放置で全てのパッチの設定を変えることができた。&lt;br /&gt;
Proteus 2000 以外も画面上の配置は変わらないのでこのスクリプトはそのまま流用できた。&lt;br /&gt;
&lt;br /&gt;
また、この処理中、他にも nki ファイル側で問題があるものが見つかった。&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Proteus 2000 バンク 3「Huge Brass.nki」はサンプルファイルの参照先が間違った場所のままリリースされている&lt;br /&gt;（ただし本来の場所にファイル自体はあるので修正すれば問題なく使える）&lt;/li&gt;
&lt;li&gt;Virtuoso の Orchestral &amp;nbsp;Winds, Brass, &amp;amp; Percussion の Timpani.nki は対応するサンプルファイルの一部が存在しない&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
ティンパニの問題は少し気になるけど、この音源パック自体何年も前からあるはずだし「まあいいか」レベルで誰かが見つけても放置されてるのかなあ…。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjQlFQaIEjogQmOm0F6vFef_6EanhZh-vxXU7d8XgCzGUW1dBWBX3Kyy1M6VVMOpOUFiw9_Pn15m3ZfVwwG_yJcGapMRG0be104dPFm9Ry2ZBqXaM6_yiTWDp80gbPpTIXo9JxReR1NEI/s1600/sample3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjQlFQaIEjogQmOm0F6vFef_6EanhZh-vxXU7d8XgCzGUW1dBWBX3Kyy1M6VVMOpOUFiw9_Pn15m3ZfVwwG_yJcGapMRG0be104dPFm9Ry2ZBqXaM6_yiTWDp80gbPpTIXo9JxReR1NEI/s1600/sample3.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;更におまけ: Go言語に似たパッチ名があった。リネームしたおかげで「bas」でベースだとわかる。&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/4579812696862213892'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/4579812696862213892'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2015/02/go-e-mu-proteus-pack-for-kontakt.html' title='Go言語で E-MU Proteus Pack for Kontakt 専用リネーマーを作った'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgljsh5KOfkNGrZoN0Pt6qsQAG7-YVsLGkWFJE9ZOUPh05Fb6crPkGbQZADgWb0GD2v-Q5HR_9KoVdrujO51jowLz3t52GM3qq-CbVSh0_raR6jirnlfe-_lzysmb2ZPVIdo11_pP1AbP0/s72-c/sample.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-4388792303080542818</id><published>2014-11-24T15:18:00.000+09:00</published><updated>2015-01-04T05:09:31.864+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="golang"/><category scheme="http://www.blogger.com/atom/ns#" term="Go言語"/><title type='text'>転送サービスを使って Gopher 君を買った話</title><content type='html'>&lt;a href=&quot;https://www.spearnet-us.com/&quot; target=&quot;_blank&quot;&gt;個人輸入・転送サービスのスピアネット&lt;/a&gt;というサービスを利用して、&lt;a href=&quot;https://www.googlestore.com/shop.axd/Home&quot; target=&quot;_blank&quot;&gt;Canada/US の Google Merchandise Store&lt;/a&gt;&amp;nbsp;から Gopher 君のピンクとパープルを買った話。&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeb8aM7lJHvr59w9NXc0KqWoaA-syPx1OF9FBmzzDy4PxhCqKDCyHs5YuKDsQzDDeC0q6lydwkw2mJMqvfXBuZgTb0fl2a-jDpCMlnBoUrmSJs657ki6zeDMt2u-_WCQfegSpqt5S2_64/s1600/DSC03117.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt; &lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeb8aM7lJHvr59w9NXc0KqWoaA-syPx1OF9FBmzzDy4PxhCqKDCyHs5YuKDsQzDDeC0q6lydwkw2mJMqvfXBuZgTb0fl2a-jDpCMlnBoUrmSJs657ki6zeDMt2u-_WCQfegSpqt5S2_64/s640/DSC03117.JPG&quot; height=&quot;265&quot; width=&quot;400&quot; /&gt; &lt;/a&gt; &lt;/div&gt;
&lt;a href=&quot;http://golang.org/&quot; target=&quot;_blank&quot;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;a href=&quot;http://golang.org/&quot; target=&quot;_blank&quot;&gt;Go 言語&lt;/a&gt;のマスコットである &lt;a href=&quot;http://blog.golang.org/gopher&quot; target=&quot;_blank&quot;&gt;Gopher 君&lt;/a&gt;は元々はブルーの一種類だったのだが、&lt;a href=&quot;https://blog.golang.org/gophercon&quot; target=&quot;_blank&quot;&gt;GopherCon&lt;/a&gt; の時にピンクとパープルが増えた。&lt;br /&gt;
&lt;a href=&quot;http://bl.oov.ch/2011/12/1.html&quot; target=&quot;_blank&quot;&gt;以前ブルーの Gopher 君を購入した時&lt;/a&gt;は &lt;a href=&quot;http://www.squishable.com/&quot; target=&quot;_blank&quot;&gt;Squishable&lt;/a&gt;&amp;nbsp;で注文し、このショップは日本への配送も可能なため転送サービスを利用しなくても手に入れることができたのだが、ピンクとパープルに関してはイベント類に全く参加せずに家で細々とGo言語と戯れているだけの自分が手に入れる方法がわからず指をくわえていた。&lt;br /&gt;
&lt;br /&gt;
ところが先日 Canada/US 向けの &lt;a href=&quot;https://www.googlestore.com/shop.axd/Home&quot; target=&quot;_blank&quot;&gt;Google Merchandise Store&lt;/a&gt;&amp;nbsp;で販売が開始されているのを発見した。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.googlemerchandisestore.com/Google+Redesign/Fun/Go+Gopher+Blue+Squishable.axd&quot; target=&quot;_blank&quot;&gt;ブルー&lt;/a&gt;&amp;nbsp;/&amp;nbsp;&lt;a href=&quot;https://www.googlemerchandisestore.com/Google+Redesign/Brands/Go/Go+Gopher+Purple+Squishable.axd&quot; target=&quot;_blank&quot;&gt;パープル&lt;/a&gt;&amp;nbsp;/&amp;nbsp;&lt;a href=&quot;https://www.googlemerchandisestore.com/Google+Redesign/Brands/Go/Go+Gopher+Pink+Squishable.axd&quot; target=&quot;_blank&quot;&gt;ピンク&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
日本へ直接配送は出来なかったので、初めて転送サービスを使って購入することにした。&lt;br /&gt;
購入から荷物の到着までは非常にスムースに進めて頂いたのだが、自分へのメモという意味も含めて全体的な流れとトータルで掛かった費用を簡単にまとめることにする。&lt;br /&gt;
&lt;br /&gt;
なお、これは2014年11月時点での情報なので参考にする場合は注意されたし。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;全体の流れ&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
まず、スピアネットでアカウントを新規作成。&lt;br /&gt;
&lt;br /&gt;
↓&lt;br /&gt;
&lt;br /&gt;
スピアネットの荷物配送先に向けて、Google Merchandise Store で商品を注文。&lt;br /&gt;
（ピンクx2&amp;nbsp;$35.40 + パープルx2&amp;nbsp;$35.40&amp;nbsp;+ 送料 $6.50 = $77.3）&lt;br /&gt;
配送先住所には部屋番号のようにスピアネットのアカウント番号を含めることになるので、必ずスピアネットのアカウントを先に作成する必要がある。&lt;br /&gt;
&lt;br /&gt;
↓&lt;br /&gt;
&lt;br /&gt;
翌営業日辺りに Google から送られてくる発送通知メールに書かれている配送のトラッキングナンバーを使って、スピアネットの管理画面から「転送依頼作成」を行う。&lt;br /&gt;
購入先のサイト名「Google Merchandise Store」と送料以外の合計金額、そして商品名１つずつと、1個辺りの単価と個数を入力して依頼する。&lt;br /&gt;
&lt;br /&gt;
よく見ればわかることだったのだが、この時自分は1個辺りの単価を入力するところで間違えて2個合わせた金額を入力してしまっていた。この入力時点では全く間違いに気付いておらず、後々スピアネットの方に荷物に関するメッセージとしてお知らせして頂いた時に初めて知り、お手を煩わせてしまうことになってしまった。すみませんでした。&lt;br /&gt;
&lt;br /&gt;
↓&lt;br /&gt;
&lt;br /&gt;
スピアネットの受取センターへ荷物が届くのを待つ。&lt;br /&gt;
最初は配達予定日が4日後ぐらいだったのだが、FedEx の追跡をこまめに見ていると少しずつ予定日が後ろにずらされ、最終的には1週間後に目的地に辿り着いた。&lt;br /&gt;
この辺は Google Merchandise Store での購入時の配送オプションによって変わるのかも。&lt;br /&gt;
&lt;br /&gt;
↓&lt;br /&gt;
&lt;br /&gt;
その後程なくして、スピアネットから到着のお知らせと上記の入力間違いに対する訂正を行った旨がメールで届いた。
今回の注文では特に一緒に送ってほしい別の荷物などはなかったため、すぐに発送準備に移行された。&lt;br /&gt;
&lt;br /&gt;
↓&lt;br /&gt;
&lt;br /&gt;
6時間後ぐらいにスピアネットから配送準備が整った旨の連絡と、料金の連絡があった。&lt;br /&gt;
（送料 $25.00&amp;nbsp;+ 転送手数料 $9.00 = $34.00）&lt;br /&gt;
&lt;br /&gt;
PayPal 経由ですぐに料金を支払うと、翌日（決済完了後の翌営業日）にヤマト運輸で発送された。&lt;br /&gt;
&lt;br /&gt;
↓&lt;br /&gt;
&lt;br /&gt;
ここからは4日程掛かって手元まで届いた。&lt;br /&gt;
&lt;a href=&quot;https://cmypage.kuronekoyamato.co.jp/portal/entrance&quot; target=&quot;_blank&quot;&gt;クロネコメンバーズ&lt;/a&gt;会員なら荷物が近くまでくると受け取り時間帯指定もできるようだった。&lt;br /&gt;
（※なおクロネコメンバーズ会員カードにはあの猫の絵が入っていてかわいい）&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;まとめ&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
費用&lt;br /&gt;
　(ピンクx2 $35.40 + パープルx2 $35.40 + 送料 $6.50)&amp;nbsp;+ (送料 $25.00 + 転送手数料 $9.00) = $111.3&lt;br /&gt;
　最近は円安なので日本円にすると少し高めになり、支払った合計金額は13301円&lt;br /&gt;
&lt;br /&gt;
所要時間&lt;br /&gt;
　注文から受け取りまで13日&lt;br /&gt;
&lt;br /&gt;
所感&lt;br /&gt;
　「転送サービス面倒そう」と思っていたが、管理画面は日本語だし説明は細かいし良かった。&lt;br /&gt;
　また今回は自分の入力ミス以外のトラブルは特になかったこともあって、進行はとてもスムースだった。&lt;br /&gt;
&lt;br /&gt;
2011年にブルーを購入した時は送料手数料で $75 掛かった代わりに1週間でクリスマス直前という超忙しそうな時期に届いたけど、今回はそれに比べると少し安くなり、代わりに少し届くまで長くなった感じだった。&lt;br /&gt;
（ただしスピアネットが関わっているところは全てを迅速に進めてくれている）&lt;br /&gt;
（あと、ドルベースでは安くなっているけどレートの関係で支払った額は今回のほうが高い）&lt;br /&gt;
&lt;br /&gt;
Gopher 君の完成度については見ての通りで、個体差がゆるふわな奴である。ういやつ。ういやつ。&lt;br /&gt;
注文したあとに知ったのだが、&lt;a href=&quot;https://www.googlemerchandisestore.com/Google+Redesign/Brands/Go/Go+Gopher+Messenger+Bag.axd&quot; target=&quot;_blank&quot;&gt;Gopher 君のメッセンジャーバッグ&lt;/a&gt;も売られていて、それもちょっと欲しかった。&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
あと1週間ほどで runtime のほとんどが Go 言語実装に置き換わる Go 1.4 がリリースされ、Go 1.5 では runtime が完全移行、そしていよいよ &lt;a href=&quot;https://code.google.com/p/go/source/detail?r=36ead9978fcc0be9de634236ad3887b41d9525e1&quot; target=&quot;_blank&quot;&gt;C コンパイラが削除&lt;/a&gt;されるらしい（cgo は無関係）。&lt;br /&gt;
セルフホスティングに向けて速いペースで着実に作業が進んでるし、Android への対応や何度目かの &lt;a href=&quot;https://bitbucket.org/minux/goios/commits/branch/ios3&quot; target=&quot;_blank&quot;&gt;darwin/arm&lt;/a&gt; 対応作業も行われているし、&lt;a href=&quot;http://llvm.org/svn/llvm-project/llvm/trunk/bindings/go/README.txt&quot; target=&quot;_blank&quot;&gt;LLVM の Go バインディング&lt;/a&gt;がマージされたりなど個人的に面白いニュースには事欠かない感じで、活発な活動が続いているのがとても嬉しい。&lt;br /&gt;
これからも楽しみだ。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/4388792303080542818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/4388792303080542818'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2014/11/gopher_24.html' title='転送サービスを使って Gopher 君を買った話'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeb8aM7lJHvr59w9NXc0KqWoaA-syPx1OF9FBmzzDy4PxhCqKDCyHs5YuKDsQzDDeC0q6lydwkw2mJMqvfXBuZgTb0fl2a-jDpCMlnBoUrmSJs657ki6zeDMt2u-_WCQfegSpqt5S2_64/s72-c/DSC03117.JPG" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-2600581200507128858</id><published>2014-08-14T09:02:00.000+09:00</published><updated>2014-08-16T04:24:43.807+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="golang"/><category scheme="http://www.blogger.com/atom/ns#" term="Go言語"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><title type='text'>Go言語で画像縮小できるライブラリのベンチマーク</title><content type='html'>Go言語で画像縮小できるライブラリのベンチマークなどを取ろうと思って少し書いてみた。&lt;br /&gt;
&lt;a href=&quot;https://github.com/oov/resize-test&quot; target=&quot;_blank&quot;&gt;https://github.com/oov/resize-test&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godoc.org/github.com/nfnt/resize&quot; target=&quot;_blank&quot;&gt;github.com/nfnt/resize&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godoc.org/github.com/disintegration/gift&quot; target=&quot;_blank&quot;&gt;github.com/disintegration/gift&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://godoc.org/code.google.com/p/appengine-go/example/moustachio/resize&quot; target=&quot;_blank&quot;&gt;code.google.com/​p/​appengine-go/​example/​moustachio/​resize&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godoc.org/github.com/bamiaux/rez&quot; target=&quot;_blank&quot;&gt;github.com/bamiaux/rez&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godoc.org/code.google.com/p/graphics-go/graphics&quot; target=&quot;_blank&quot;&gt;code.google.com/p/graphics-go/graphics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godoc.org/github.com/disintegration/imaging&quot; target=&quot;_blank&quot;&gt;github.com/disintegration/imaging&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
この辺りのライブラリは画像の縮小ができるので、どれも&amp;nbsp;func(src image.Image, w int, h int) (image.Image, error) で&lt;a href=&quot;https://godoc.org/github.com/oov/resize-test/resizer#pkg-variables&quot; target=&quot;_blank&quot;&gt;呼べるよう整理&lt;/a&gt;して、それを元にした&lt;a href=&quot;https://github.com/oov/resize-test/blob/master/genbench.go&quot; target=&quot;_blank&quot;&gt;ベンチマークのジェネレータ&lt;/a&gt;とか&lt;a href=&quot;https://github.com/oov/resize-test/blob/master/main.go&quot; target=&quot;_blank&quot;&gt;サンプル画像出力プログラム&lt;/a&gt;を作った。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
テスト環境は Intel Atom D510 で、物理コア数2でハイパースレッディング対応。遅い。&lt;/div&gt;
&lt;div&gt;
使用したGo言語環境は go1.3.1 の linux/amd64 版。&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIh9oBrw1g8uWNny0EP_scorCeufBhn8h7j0IqtLD_VCfQpjyO2FqwfyeYya2HmNn-4SKeYVvOp9cUwoaVQWKMeaFy9G2GLqmd2lLgFe9-oTnzLycl_0-A_Q76qWMC5XIIIvY5keuRkuI/s0/vis.png&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIh9oBrw1g8uWNny0EP_scorCeufBhn8h7j0IqtLD_VCfQpjyO2FqwfyeYya2HmNn-4SKeYVvOp9cUwoaVQWKMeaFy9G2GLqmd2lLgFe9-oTnzLycl_0-A_Q76qWMC5XIIIvY5keuRkuI/s1600/vis.png&quot; height=&quot;640&quot; width=&quot;377&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;これはベンチマーク結果を Google ドキュメントでグラフ化したもの。&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIh9oBrw1g8uWNny0EP_scorCeufBhn8h7j0IqtLD_VCfQpjyO2FqwfyeYya2HmNn-4SKeYVvOp9cUwoaVQWKMeaFy9G2GLqmd2lLgFe9-oTnzLycl_0-A_Q76qWMC5XIIIvY5keuRkuI/s0/vis.png&quot; target=&quot;_blank&quot;&gt;画像を別窓で開く&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;
amd64 用に最適化されている rez&amp;nbsp;はかなり速くて、速度と品質のバランスを見て unsafe なコードが使える環境の場合は rez の Bicubic や Lanczos が良さそうに思える。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
unsafe が使えない場合は同じレベルの速度で動くのは品質が NearestNeighbor 相当のものしかなく、それを避けると&amp;nbsp;nfnt/resize&amp;nbsp;の&amp;nbsp;Bilinear、そして nfnt/resize の他のアルゴリズムや&amp;nbsp;gift&amp;nbsp;/&amp;nbsp;imaging&amp;nbsp;の&amp;nbsp;Box という感じで続いていく。&lt;/div&gt;
&lt;div&gt;
nfnt/resize は全体的にパフォーマンスがいいが、出力品質と GOMAXPROCS=1 の時のパフォーマンスなどを見ると gift や imaging の Box が今回のサンプル画像においてはかなり健闘していながら速度もなかなか速いので、個人的に使いたいのはこれかなぁという感じがした。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
テスト環境の物理コア数と一致する&amp;nbsp;GOMAXPROCS=2 の時は大体のライブラリで速度が2倍近くなり、GOMAXPROCS=4 ではそこまで伸びないのもあるがそこそこ速くなり納得できる結果に。&lt;/div&gt;
&lt;div&gt;
一方で moustachio/​resize はおそらく元々 GAE 用のサンプルということもあって並列化されていないため GOMAXPROCS に関わらず速度は変わらず、graphics-go/graphics もまた変わらなかった。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
これは余談だけど moustachio/​resize のルーチンはGo言語にまだこれといった画像縮小ライブラリが無かった時代に重宝されていたように思うが、使いやすい場所にライブラリが配置されていなかったこともあり fork ではなくコピーされたファイルがいくつかのリポジトリにあり紛らわしいのが探すとき地味にだるい。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
そのうち&amp;nbsp;&lt;a href=&quot;https://codereview.appspot.com/101670045/&quot; target=&quot;_blank&quot;&gt;go.image/draw が追加されそう&lt;/a&gt;なので、これが追加されてコミットが落ち着いた頃にでもベンチマークに追加しようと思う。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;個人的なまとめ&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;rez 良い&lt;/li&gt;
&lt;li&gt;gift や imaging の Box がその次ぐらいに好み&lt;/li&gt;
&lt;li&gt;nfnt/resize も良い&lt;/li&gt;
&lt;li&gt;選べるぐらいに選択肢が増えて嬉しい&lt;/li&gt;
&lt;li&gt;ImageMagick とか cgo 経由するようなのとかは面倒なので試してない&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;b&gt;2014-08-16 追記&lt;/b&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxpiNPvID64cVrd7yUFlZ5Wwc20dY2u8l2_hUeIo3yHShrLT0Z-XTbAXZ0ThzTRtcqeMuVZ-cNvh6oRnwS5hSGJcz3kXQR_7hBVUAcBwAHUUgz8vvtiNNLDLJlSQsdLQOFgyG9XqvgB7c/s1600/vis2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxpiNPvID64cVrd7yUFlZ5Wwc20dY2u8l2_hUeIo3yHShrLT0Z-XTbAXZ0ThzTRtcqeMuVZ-cNvh6oRnwS5hSGJcz3kXQR_7hBVUAcBwAHUUgz8vvtiNNLDLJlSQsdLQOFgyG9XqvgB7c/s1600/vis2.png&quot; height=&quot;400&quot; width=&quot;102&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxpiNPvID64cVrd7yUFlZ5Wwc20dY2u8l2_hUeIo3yHShrLT0Z-XTbAXZ0ThzTRtcqeMuVZ-cNvh6oRnwS5hSGJcz3kXQR_7hBVUAcBwAHUUgz8vvtiNNLDLJlSQsdLQOFgyG9XqvgB7c/s0/vis2.png&quot; target=&quot;_blank&quot;&gt;画像を別窓で開く&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;
大変見づらくなってしまっているが、需要がありそうな ImageMagick(convert コマンド) もベンチマークに追加するために色々と変更した。&lt;br /&gt;
&lt;br /&gt;
前回のプログラムでは
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;画像のデコード&lt;/li&gt;
&lt;li&gt;&lt;b&gt;画像の縮小&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;画像のエンコード&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
上記の3つのステップのうち、2の段階のみを純粋に計測していた。&lt;br /&gt;
&lt;br /&gt;
ImageMagick が加わる場合これまでとは違い画像をデコード／エンコードする処理自体も使用されるコードが異なる上これらを切り離して計測するのは難しく需要も薄いと思われるので、公平な結果を簡単に出力できる&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;画像のデコード&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;画像の縮小&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;画像のエンコード&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
この3つのステップ全てをまとめたベンチマークを追加し、ベンチマークのメソッド名を&lt;/div&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;縮小処理のみを計測しているものを BenchmarkScaler*&lt;/li&gt;
&lt;li&gt;一連の流れをまとめて計測したものを BenchmarkFlow*&lt;/li&gt;
&lt;/ul&gt;
という名前で統一した。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
計測用のマシンに前回使用した&amp;nbsp;Intel Atom D510 go1.3.1 linux/amd64 の環境を今回使うとベンチマーク完了まで20分強の時間を要してしまい辛くなってきたので、環境を変えて Intel Core i7 4790K go1.3.1 windows/amd64 で計測した。この CPU は物理コア数4でハイパースレッディング対応なので GOMAXPROCS=8 の結果も追加している。&lt;br /&gt;
また、今回使用した ImageMagick は&amp;nbsp;chocolatey 経由でインストールしたもの。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
早速 BenchmarkFlow* 辺りのスコアを見ていくと、前回速かったニアレストネイバー系や rez のパフォーマンスが BenchmarkScaler* に比べるとかなり落ち込んでいる。これはテストに使っている &lt;a href=&quot;https://github.com/oov/resize-test/blob/master/test.jpg&quot; target=&quot;_blank&quot;&gt;test.jpg&lt;/a&gt;&amp;nbsp;が 4912x3264 で 4.68MB ぐらいある少し大きいファイルであることも原因の一端を担っていると思う（一端どころではない気もする）。&lt;br /&gt;
&lt;br /&gt;
ともかく、この増えた分を差し引けば BenchmarkScaler* の方と関係性はほとんど変わらないので、後は ImageMagick との比較になる。今回&amp;nbsp;&lt;a href=&quot;https://github.com/oov/resize-test/blob/master/resizer/imagemagick.go&quot; target=&quot;_blank&quot;&gt;ImageMagick の縮小方法は -define ありとなしの2種類を定義&lt;/a&gt;していて、-define ありの方は JPEG 画像で縮小後のサイズが予め判明している場合に処理速度が速くなる。&lt;br /&gt;
&lt;br /&gt;
フィルタ指定を特にしていない時 ImageMagick は Lanczos のようなので、それも踏まえて見てみると -define なしの方に関しては rez の Lanczos3 が結構いいところまで迫ってきている。&lt;br /&gt;
一方 -define ありの方はもうなんか他の追随を許さない勢いでとても速い。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;まとめ2&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
今回選んだ画像がかなり大きいせいか、デコードに掛かる所要時間で勝負の大半がついてしまった感じがある。それでも -define なしの時と比べた限りではGo言語で書かれた JPEG デコーダや rez の縮小処理は決して遅くないので、今後同じようなアプローチで高速化されたGo言語版の JPEG デコーダなどが登場すると更に差が縮まりそうで楽しみになった。&lt;/div&gt;
&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/2600581200507128858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/2600581200507128858'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2014/08/go-resize-test.html' title='Go言語で画像縮小できるライブラリのベンチマーク'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIh9oBrw1g8uWNny0EP_scorCeufBhn8h7j0IqtLD_VCfQpjyO2FqwfyeYya2HmNn-4SKeYVvOp9cUwoaVQWKMeaFy9G2GLqmd2lLgFe9-oTnzLycl_0-A_Q76qWMC5XIIIvY5keuRkuI/s72-c/vis.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-2825458184996667800</id><published>2014-07-23T07:04:00.000+09:00</published><updated>2014-07-24T19:51:08.611+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Cocos2d-JS"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><title type='text'>Cocos2d-JS 触り始めた5</title><content type='html'>前回の作業中ホーミングミッソーを試しにたくさん飛ばしてみると、一時的にフレームレートが低下し、それに伴いゲームの進行速度が遅くなることがあった。特にフレームスキップなどの実装をしていないしこれは想定通りの動作ではあるものの、&lt;code&gt;update&lt;/code&gt; メソッドの第一引数に渡されてくる値は前回のフレームからの経過時間（秒）なので、これを適切に扱うようにすればフレーム落ちも加味して計算できる。&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
これによって少しぐらいのフレームレート低下なら何もしないよりはマシに振る舞うことができる可能性がある。ただゲームによってはフレームレート低下でゲームの進行速度が遅くなるのも許容できることもあるだろうし（例えばターン制のゲームなど時間に追われていない場合とか）、実装の面倒臭さも合わせて上手い落としを見つけるのがいい。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index e0d3a27..03ccee6 100644
--- a/src/app.js
+++ b/src/app.js
@@ -119,8 +119,11 @@ var GameOverLayer = cc.Layer.extend({

 var MyShipLayer = cc.Layer.extend({
     sprite:null,
-    vy: 3,
+    vy: 140,
     barrier:false,
+    d:0,
+    origY: 0,
+    G: -280,
     ctor:function () {
         this._super();
         this.sprite = cc.Sprite.create(res.nc_png);
@@ -132,11 +135,12 @@ var MyShipLayer = cc.Layer.extend({
         return true;
     },
     update:function(dt) {
-        this.y += this.vy;
-        this.vy -= 0.1;
+      var d = this.d += dt;
+      this.y = 0.5*this.G*d*d + this.vy*d + this.origY;
     },
     jump:function() {
-      this.vy = 3;
+      this.d = 0;
+      this.origY = this.y;
     }
 });

@@ -157,8 +161,8 @@ var BlockLayer = cc.Layer.extend({
         return true;
     },
     update:function(dt) {
-        this.x += this.vx;
-        this.y += this.vy;
+        this.x += this.vx * dt;
+        this.y += this.vy * dt;
     }
 });

@@ -167,13 +171,17 @@ var HomingMissoLayer = cc.Layer.extend({
     particle:null,
     d:null,
     a:0,
+    acos:0,
+    asin:0,
     vx:0,
     vy:0,
-    speed:6,
+    speed:300,
+    trackInterval: 0.032,
     angle:0.3,
     ctor:function () {
       this._super();
       this.scheduleUpdate();
+      this.schedule(this.track, this.trackInterval);
       this.d = cc.DrawNode.create();
       this.d.drawRect(cc.p(-8, -2), cc.p(8, 2), cc.color(255,255,255,255));
       this.addChild(this.d, 0);
@@ -184,26 +192,29 @@ var HomingMissoLayer = cc.Layer.extend({
       this.addChild(this.particle);
       return true;
     },
-    update:function(dt) {
+    track:function(dt) {
       var bb = this.target.getBoundingBox();
       var dx = this.d.x, dy = this.d.y;
       var vx = this.vx, vy = this.vy;
       var tx = bb.x + bb.width*0.5 - dx, ty = bb.y + bb.height*0.5 - dy;
-      var tl = Math.sqrt(tx*tx+ty*ty), a = this.a;
-      if ((tx*vx+ty*vy)/(tl*Math.sqrt(vx*vx+vy*vy)) &amp;lt; 0) {
-        a += this.angle;
+      if ((tx*vx+ty*vy)/(vx*vx+vy*vy) &amp;lt; 0) {
+        this.a += this.angle;
       } else {
-        a += this.angle * (vx*ty - vy*tx)/tl/this.speed;
+        this.a += this.angle * (vx*ty - vy*tx)/Math.sqrt(tx*tx+ty*ty)/this.speed;
       }
-      this.a = a;
-      var cosa = Math.cos(a), sina = Math.sin(a), r = a*180/Math.PI;
-      this.vx = vx = this.speed * cosa;
-      this.vy = vy = this.speed * sina;
-      this.d.x = dx += vx;
-      this.d.y = dy += vy;
+      this.acos = Math.cos(this.a);
+      this.asin = Math.sin(this.a);
+      this.vx = this.speed * this.acos;
+      this.vy = this.speed * this.asin;
+      var r = this.a*180/Math.PI;
       this.d.rotation = -r;
-      this.particle.setSourcePosition(cc.p(dx + -6 * cosa, dy + -6 * sina));
       this.particle.angle = 180+r;
+    },
+    update:function(dt) {
+      var x = this.vx * dt + this.d.x, y = this.vy * dt + this.d.y;
+      this.d.x = x;
+      this.d.y = y;
+      this.particle.setSourcePosition(cc.p(x + -6 * this.acos, y + -6 * this.asin));
     }
 });

@@ -222,6 +233,7 @@ var HelloWorldScene = cc.Scene.extend({
           x: 40,
           y: size.height / 2
         });
+        this.myShip.jump();

         this.blocks = [];
         for (var i = 0, bl; i &amp;lt; 16; ++i) {
@@ -238,8 +250,8 @@ var HelloWorldScene = cc.Scene.extend({
           bl.attr({
             x: size.width+Math.random()*size.width,
             y: -bl.height+Math.random()*(size.height+bl.height),
-            vx: -0.5-Math.random()*3,
-            vy: -0.25+Math.random()*0.5
+            vx: -60-Math.random()*60,
+            vy: -2.5+Math.random()*5
           });
           this.addChild(bl);
           this.blocks.push(bl);
@@ -298,8 +310,8 @@ var HelloWorldScene = cc.Scene.extend({
           bl.attr({
             x: size.width,
             y: -bl.height+Math.random()*(size.height+bl.height),
-            vx: -0.5-Math.random()*3,
-            vy: -0.25+Math.random()*0.5
+            vx: -60-Math.random()*60,
+            vy: -2.5+Math.random()*5
           });
         }
       }
&lt;/pre&gt;
なお面倒だったので速度などは前と全く同じではなく適当に似た振る舞いになるように合わせているだけ。今までは例えばブロックは毎フレーム &lt;code&gt;this.vx&lt;/code&gt; を足していたが変更後は &lt;code&gt;this.vx*dt&lt;/code&gt; になっていて、&lt;code&gt;dt&lt;/code&gt; は秒単位なので &lt;code&gt;0.0167&lt;/code&gt; などの小さい値が入ってくる。前は60フレーム想定の速度で書いていたので、&lt;code&gt;this.vx/0.0167&lt;/code&gt; ぐらいの値を指定すれば大体同じくらいの速度で動くことにはなるとは思う。&lt;br /&gt;
&lt;br /&gt;
まあでもこの手のものは今どきっぽい実装をするなら物理エンジンに丸投げしてしまえばそれで済むことも多いんだろうし予め最適化されたエンジンが使えるので、下手に自分で作るより頑張らないという選択肢も悪くなさそうだと思った。作るものの複雑さ次第かも。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Cocos Code IDE を使ってみる&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
つい先日 &lt;a href=&quot;http://www.cocos2d-x.org/wiki/Cocos_Code_IDE&quot; target=&quot;_blank&quot;&gt;Cocos Code IDE&lt;/a&gt; というものがリリースされたらしい。&lt;br /&gt;
&lt;br /&gt;
これは Eclipse 系列のよくある開発環境のようなのだが、どうやら Cocos2d-x の Windows 用コンパイル済みバイナリが付属していて手軽に実行して確認できる上、実行中にソースコードを書き換えるとすぐに反映して実行しなおしてくれた。&lt;br /&gt;
&lt;br /&gt;
既存のプロジェクトを Cocos Code IDE で使える状態に移行するには新規プロジェクトを作って必要なファイルを突っ込めばいいようで、Cocos2d-x の新しいバージョンも出たっぽいし試しに使ってみることにした。作業中のリポジトリにはランタイムなども含んでいるのでこれによる変更ファイル数は相当多いものの基本的な振る舞いに変更はないのでここでの diff は省略する。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrCxDD_LQCAKZT0p5YOgZffFNa4N3Ba44IgKP-PIntFcH2Rtt2RjHPcewHe7l6frFyOYn4SpdTh9vXn0xdiN_-s_ROp65KW-mKiiAscMhOG95z54emRjFNZZusK9rpIGftpA5G5eC5IzQ/s1600/ss.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrCxDD_LQCAKZT0p5YOgZffFNa4N3Ba44IgKP-PIntFcH2Rtt2RjHPcewHe7l6frFyOYn4SpdTh9vXn0xdiN_-s_ROp65KW-mKiiAscMhOG95z54emRjFNZZusK9rpIGftpA5G5eC5IzQ/s1600/ss.jpg&quot; height=&quot;218&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
デバッグボタンを押すとほとんどラグなしですぐにウィンドウが出て動き始めるのでテストまでのストレスが少ない。Haxe で NME で開発していた時、Flash が動き出すのも丁度こんな感じだったので作業しやすい（とはいえ元々ブラウザをリロードするだけで試せていたので前もそんなには遅くなかった）。&lt;br /&gt;
&lt;br /&gt;
今後はブラウザ上でのテストも Run In Browser ボタンでいける。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;CocoStudio UI Editor を使ってスコア表示機能を作る&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
今度は &lt;a href=&quot;http://www.cocos2d-x.org/wiki/CocoStudio&quot; target=&quot;_blank&quot;&gt;CocoStudio&lt;/a&gt; に付属している &lt;a href=&quot;http://www.cocos2d-x.org/wiki/UI_Editor&quot; target=&quot;_blank&quot;&gt;UI Editor&lt;/a&gt; を使ってインターフェイスを作り、それを利用してスコア表示をしてみる。&lt;br /&gt;
&lt;br /&gt;
単に画面にスコアを出すという目的だけを達成するなら最初から &lt;code&gt;HelloWorldLayer&lt;/code&gt; に存在しているラベルを応用するなどでできるとは思うものの、デザインに関わる部分は長い目で見ればプログラマーとデザイナーで分業できる形になっていたほうがいい。分業相手は居ないけど。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;ビットマップフォントを作る&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
スコアには点数しか出さないし、多種多様な実行環境に依存せず使えるように数字が格納されたビットマップフォントがあるといい。これには &lt;a href=&quot;http://renderhjs.net/shoebox/&quot; target=&quot;_blank&quot;&gt;ShoeBox&lt;/a&gt; というツールが使えるらしいので、試しに &lt;a href=&quot;http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/index.html&quot; target=&quot;_blank&quot;&gt;M+ フォント&lt;/a&gt;で数字を書いただけの画像を適当に作ってみた。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJODN-LhQ5Z9TdR2QFKeGI1dRkOb55Jca1gJ3cIXxDzwjK4CL0FUpZX4IdUQ3HlNu_yutp-xrLkfZdYcBUH4VKmTOp-dk-EtjDiqTQguJPqTRkJEv_DMPP4nz7_FAFq2ujDlQ-3CG-vk0/s1600/num.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJODN-LhQ5Z9TdR2QFKeGI1dRkOb55Jca1gJ3cIXxDzwjK4CL0FUpZX4IdUQ3HlNu_yutp-xrLkfZdYcBUH4VKmTOp-dk-EtjDiqTQguJPqTRkJEv_DMPP4nz7_FAFq2ujDlQ-3CG-vk0/s1600/num.png&quot; height=&quot;33&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
これを ShoeBox に投げつけていくつか設定をするだけで余白を元に文字の切り分けポイントをは別して簡単にビットマップフォントは生成できたのだが、どうやら Cocos2d-x にはプロポーショナルフォントとして解釈されてしまうようだ（まあ描画したフォント自体もプロポーショナルフォントなので正しくはあるのだが……）。どんどんカウントアップされている点数が左右にガチャガチャ動くのは辛いので、モノスペースフォントになるように回避策を考える必要が出てきた。&lt;br /&gt;
&lt;br /&gt;
自分の手で fnt ファイルを編集するとか生成用ツールを作るとかアプローチは色々ありそうではあるものの、&lt;a href=&quot;http://kvazars.com/littera/&quot; target=&quot;_blank&quot;&gt;Littera&lt;/a&gt; というツールでモノスペースフォントで吐けば綺麗に出ることがわかったので、手間も掛からないし今回はこの方法で行くことにした。&lt;br /&gt;
&lt;br /&gt;
数字を書いて適当にポチポチやって保存すると zip ファイルで書き出される。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;UI Editor でインターフェイスを組み立てる&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
スコア表示周りの素材を作り、その上に作成したビットマップフォントでスコアを表示させるような感じに素材を作った。UI Editor のインターフェイスは若干独特な部分があって操作性が微妙に良くない感じもあるけど、試行錯誤しているうちに目的の形に落ち着いた。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWLyoF7UeVOu1K9Qs8MHitYP4e5tDoSpOunxUqr3FdMonlm9DcALkce40DoBwD5qVm6s1IVirpuDKvOFqJ66LTlQzSaXUAuQY6aUkoJN_XLKFp_LTufulHM_nIA1dGNNi2fap3lSwlqQA/s1600/ss2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWLyoF7UeVOu1K9Qs8MHitYP4e5tDoSpOunxUqr3FdMonlm9DcALkce40DoBwD5qVm6s1IVirpuDKvOFqJ66LTlQzSaXUAuQY6aUkoJN_XLKFp_LTufulHM_nIA1dGNNi2fap3lSwlqQA/s1600/ss2.png&quot; height=&quot;183&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
なお作った素材がダサいことについては重々承知しているのでこれ以上の追撃は必要ない。&lt;br /&gt;
作成したインターフェイスはエクスポートで res ディレクトリ内にサブディレクトリを作って配置した。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;プログラム上でインターフェイスを読み込む&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
早速作った素材を読み込むレイヤーを追加し、そのレイヤーを &lt;code&gt;HelloWorldScene&lt;/code&gt; に追加する。インターフェイス用の JSON ファイルは project.json の &lt;code&gt;modules&lt;/code&gt; に &lt;code&gt;&quot;cocostudio&quot;&lt;/code&gt; を追加すると、&lt;code&gt;ccs.uiReader.widgetFromJsonFile&lt;/code&gt; というメソッドを通じて読み込むことができるようになるらしい。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/project.json b/project.json
index f0c0298..b1cc345 100644
--- a/project.json
+++ b/project.json
@@ -8,7 +8,7 @@
     &quot;renderMode&quot; : 2,
     &quot;engineDir&quot;:&quot;frameworks/cocos2d-html5&quot;,
 
-    &quot;modules&quot; : [&quot;menus&quot;, &quot;cocos2d&quot;],
+    &quot;modules&quot; : [&quot;menus&quot;, &quot;cocos2d&quot;, &quot;cocostudio&quot;],
 
     &quot;jsList&quot; : [
         &quot;src/resource.js&quot;,
diff --git a/res/main/font.fnt b/res/main/font.fnt
new file mode 100644
index 0000000..e0b6c39
--- /dev/null
+++ b/res/main/font.fnt
@@ -0,0 +1,15 @@
+info face=font size=30 bold=1 italic=0 charset= unicode= stretchH=100 smooth=1 aa=1 padding=5,5,5,5 spacing=0,0 outline=0
+common lineHeight=45 base=32 scaleW=128 scaleH=64 pages=1 packed=0
+page id=0 file=&quot;font.png&quot;
+chars count=10
+char id=48 x=5 y=5 width=13 height=23 xoffset=1 yoffset=0 xadvance=15 page=0 chnl=15
+char id=49 x=23 y=5 width=10 height=22 xoffset=2 yoffset=0 xadvance=15 page=0 chnl=15
+char id=50 x=38 y=5 width=12 height=23 xoffset=2 yoffset=0 xadvance=15 page=0 chnl=15
+char id=51 x=55 y=5 width=12 height=23 xoffset=2 yoffset=0 xadvance=15 page=0 chnl=15
+char id=52 x=72 y=5 width=14 height=22 xoffset=1 yoffset=0 xadvance=15 page=0 chnl=15
+char id=53 x=91 y=5 width=12 height=23 xoffset=2 yoffset=0 xadvance=15 page=0 chnl=15
+char id=54 x=72 y=32 width=13 height=23 xoffset=1 yoffset=0 xadvance=15 page=0 chnl=15
+char id=55 x=5 y=33 width=12 height=22 xoffset=2 yoffset=0 xadvance=15 page=0 chnl=15
+char id=56 x=22 y=33 width=14 height=23 xoffset=1 yoffset=0 xadvance=15 page=0 chnl=15
+char id=57 x=41 y=33 width=13 height=23 xoffset=1 yoffset=0 xadvance=15 page=0 chnl=15
+char id=32 x=0 y=0 width=0 height=0 xoffset=1 yoffset=0 xadvance=15 page=0 chnl=15
\ No newline at end of file
diff --git a/res/main/font.png b/res/main/font.png
new file mode 100644
index 0000000..a30c2e2
Binary files /dev/null and b/res/main/font.png differ
diff --git a/res/main/main.json b/res/main/main.json
new file mode 100644
index 0000000..e512a7e
--- /dev/null
+++ b/res/main/main.json
@@ -0,0 +1,183 @@
+{
+  &quot;classname&quot;: null,
+  &quot;name&quot;: null,
+  &quot;animation&quot;: {
+    &quot;classname&quot;: null,
+    &quot;name&quot;: &quot;AnimationManager&quot;,
+    &quot;actionlist&quot;: []
+  },
+  &quot;dataScale&quot;: 1,
+  &quot;designHeight&quot;: 450,
+  &quot;designWidth&quot;: 800,
+  &quot;textures&quot;: [],
+  &quot;texturesPng&quot;: [
+    &quot;score.png&quot;
+  ],
+  &quot;version&quot;: &quot;1.5.0.0&quot;,
+  &quot;widgetTree&quot;: {
+    &quot;classname&quot;: &quot;Panel&quot;,
+    &quot;name&quot;: null,
+    &quot;children&quot;: [
+      {
+        &quot;classname&quot;: &quot;ImageView&quot;,
+        &quot;name&quot;: null,
+        &quot;children&quot;: [],
+        &quot;options&quot;: {
+          &quot;__type&quot;: &quot;ImageViewSurrogate:#EditorCommon.JsonModel.Component.GUI&quot;,
+          &quot;classname&quot;: &quot;ImageView&quot;,
+          &quot;name&quot;: &quot;Image_6&quot;,
+          &quot;ZOrder&quot;: 0,
+          &quot;actiontag&quot;: 45502051,
+          &quot;anchorPointX&quot;: 1,
+          &quot;anchorPointY&quot;: 1,
+          &quot;classType&quot;: &quot;CocoStudio.EngineAdapterWrap.CSImageView&quot;,
+          &quot;colorB&quot;: 255,
+          &quot;colorG&quot;: 255,
+          &quot;colorR&quot;: 255,
+          &quot;customProperty&quot;: &quot;&quot;,
+          &quot;flipX&quot;: false,
+          &quot;flipY&quot;: false,
+          &quot;height&quot;: 34,
+          &quot;ignoreSize&quot;: true,
+          &quot;layoutParameter&quot;: null,
+          &quot;opacity&quot;: 255,
+          &quot;positionPercentX&quot;: 1.0025,
+          &quot;positionPercentY&quot;: 1.00444448,
+          &quot;positionType&quot;: 1,
+          &quot;rotation&quot;: 0,
+          &quot;scaleX&quot;: 1,
+          &quot;scaleY&quot;: 1,
+          &quot;sizePercentX&quot;: 0.31875,
+          &quot;sizePercentY&quot;: 0.0755555555,
+          &quot;sizeType&quot;: 0,
+          &quot;tag&quot;: 10,
+          &quot;touchAble&quot;: false,
+          &quot;useMergedTexture&quot;: false,
+          &quot;visible&quot;: true,
+          &quot;width&quot;: 255,
+          &quot;x&quot;: 802,
+          &quot;y&quot;: 452,
+          &quot;capInsetsHeight&quot;: 1,
+          &quot;capInsetsWidth&quot;: 1,
+          &quot;capInsetsX&quot;: 0,
+          &quot;capInsetsY&quot;: 0,
+          &quot;fileName&quot;: null,
+          &quot;fileNameData&quot;: {
+            &quot;path&quot;: &quot;score.png&quot;,
+            &quot;plistFile&quot;: &quot;&quot;,
+            &quot;resourceType&quot;: 0
+          },
+          &quot;scale9Enable&quot;: false,
+          &quot;scale9Height&quot;: 34,
+          &quot;scale9Width&quot;: 255
+        }
+      },
+      {
+        &quot;classname&quot;: &quot;LabelBMFont&quot;,
+        &quot;name&quot;: null,
+        &quot;children&quot;: [],
+        &quot;options&quot;: {
+          &quot;__type&quot;: &quot;LabelBMFontSurrogate:#EditorCommon.JsonModel.Component.GUI&quot;,
+          &quot;classname&quot;: &quot;LabelBMFont&quot;,
+          &quot;name&quot;: &quot;scoreValue&quot;,
+          &quot;ZOrder&quot;: 0,
+          &quot;actiontag&quot;: 52610097,
+          &quot;anchorPointX&quot;: 1,
+          &quot;anchorPointY&quot;: 1,
+          &quot;classType&quot;: &quot;CocoStudio.EngineAdapterWrap.CSLabelBMFont&quot;,
+          &quot;colorB&quot;: 255,
+          &quot;colorG&quot;: 255,
+          &quot;colorR&quot;: 255,
+          &quot;customProperty&quot;: &quot;&quot;,
+          &quot;flipX&quot;: false,
+          &quot;flipY&quot;: false,
+          &quot;height&quot;: 45,
+          &quot;ignoreSize&quot;: true,
+          &quot;layoutParameter&quot;: null,
+          &quot;opacity&quot;: 255,
+          &quot;positionPercentX&quot;: 0.99625,
+          &quot;positionPercentY&quot;: 0.9911111,
+          &quot;positionType&quot;: 0,
+          &quot;rotation&quot;: 0,
+          &quot;scaleX&quot;: 1,
+          &quot;scaleY&quot;: 1,
+          &quot;sizePercentX&quot;: 0.285,
+          &quot;sizePercentY&quot;: 0.25111112,
+          &quot;sizeType&quot;: 0,
+          &quot;tag&quot;: 12,
+          &quot;touchAble&quot;: false,
+          &quot;useMergedTexture&quot;: false,
+          &quot;visible&quot;: true,
+          &quot;width&quot;: 90,
+          &quot;x&quot;: 797,
+          &quot;y&quot;: 446,
+          &quot;fileNameData&quot;: {
+            &quot;path&quot;: &quot;font.fnt&quot;,
+            &quot;plistFile&quot;: &quot;&quot;,
+            &quot;resourceType&quot;: 0
+          },
+          &quot;text&quot;: &quot;112266&quot;
+        }
+      }
+    ],
+    &quot;options&quot;: {
+      &quot;__type&quot;: &quot;PanelSurrogate:#EditorCommon.JsonModel.Component.GUI&quot;,
+      &quot;classname&quot;: &quot;Panel&quot;,
+      &quot;name&quot;: &quot;Panel_14&quot;,
+      &quot;ZOrder&quot;: 0,
+      &quot;actiontag&quot;: -1,
+      &quot;anchorPointX&quot;: 0,
+      &quot;anchorPointY&quot;: 0,
+      &quot;classType&quot;: &quot;CocoStudio.EngineAdapterWrap.CSPanel&quot;,
+      &quot;colorB&quot;: 255,
+      &quot;colorG&quot;: 255,
+      &quot;colorR&quot;: 255,
+      &quot;customProperty&quot;: &quot;&quot;,
+      &quot;flipX&quot;: false,
+      &quot;flipY&quot;: false,
+      &quot;height&quot;: 450,
+      &quot;ignoreSize&quot;: false,
+      &quot;layoutParameter&quot;: null,
+      &quot;opacity&quot;: 255,
+      &quot;positionPercentX&quot;: 0,
+      &quot;positionPercentY&quot;: 0,
+      &quot;positionType&quot;: 0,
+      &quot;rotation&quot;: 0,
+      &quot;scaleX&quot;: 1,
+      &quot;scaleY&quot;: 1,
+      &quot;sizePercentX&quot;: 1,
+      &quot;sizePercentY&quot;: 1,
+      &quot;sizeType&quot;: 0,
+      &quot;tag&quot;: 3,
+      &quot;touchAble&quot;: false,
+      &quot;useMergedTexture&quot;: false,
+      &quot;visible&quot;: true,
+      &quot;width&quot;: 800,
+      &quot;x&quot;: 0,
+      &quot;y&quot;: 0,
+      &quot;adaptScreen&quot;: false,
+      &quot;backGroundImage&quot;: null,
+      &quot;backGroundImageData&quot;: null,
+      &quot;backGroundScale9Enable&quot;: false,
+      &quot;bgColorB&quot;: 255,
+      &quot;bgColorG&quot;: 200,
+      &quot;bgColorOpacity&quot;: 0,
+      &quot;bgColorR&quot;: 150,
+      &quot;bgEndColorB&quot;: 255,
+      &quot;bgEndColorG&quot;: 200,
+      &quot;bgEndColorR&quot;: 150,
+      &quot;bgStartColorB&quot;: 255,
+      &quot;bgStartColorG&quot;: 255,
+      &quot;bgStartColorR&quot;: 255,
+      &quot;capInsetsHeight&quot;: 1,
+      &quot;capInsetsWidth&quot;: 1,
+      &quot;capInsetsX&quot;: 0,
+      &quot;capInsetsY&quot;: 0,
+      &quot;clipAble&quot;: false,
+      &quot;colorType&quot;: 1,
+      &quot;layoutType&quot;: 0,
+      &quot;vectorX&quot;: 0,
+      &quot;vectorY&quot;: -0.5
+    }
+  }
+}
\ No newline at end of file
diff --git a/res/main/score.png b/res/main/score.png
new file mode 100644
index 0000000..c51951a
Binary files /dev/null and b/res/main/score.png differ
diff --git a/src/app.js b/src/app.js
index f7454fe..468a0df 100644
--- a/src/app.js
+++ b/src/app.js
@@ -62,6 +62,26 @@ var HelloWorldLayer = cc.Layer.extend({
  }
 });
 
+var StatusLayer = cc.Layer.extend({
+ _score:0,
+ ui:null,
+ scoreValue:null,
+ getScore: function () {
+  return this._score;
+ },
+ setScore: function (newScore) {
+  this._score = newScore;
+  this.scoreValue.string = newScore;
+ },
+ ctor:function () {
+  this._super();
+  cc.defineGetterSetter(this, &quot;score&quot;, this.getScore, this.setScore);
+  this.ui = ccs.uiReader.widgetFromJsonFile(res.main_json);
+  this.scoreValue = ccui.helper.seekWidgetByName(this.ui, &quot;scoreValue&quot;);
+  this.addChild(this.ui);
+ }
+});
+
 var GameOverLayer = cc.Layer.extend({
  ctor:function () {
   this._super();
@@ -223,6 +243,8 @@ var HelloWorldScene = cc.Scene.extend({
  myShip:null,
  blocks:null,
  el:null,
+ status:null,
+ live:0,
  onEnter:function () {
   this._super();
   var size = cc.director.getWinSize();
@@ -271,6 +293,10 @@ var HelloWorldScene = cc.Scene.extend({
   ml.attr({target: this.blocks[3]});
   this.addChild(ml);
 
+  this.status = new StatusLayer();
+  this.addChild(this.status);
+  this.status.score = 0;
+
   this.el = cc.EventListener.create({
    event: cc.EventListener.TOUCH_ONE_BY_ONE,
    swallowTouches: true,
@@ -316,6 +342,9 @@ var HelloWorldScene = cc.Scene.extend({
     });
    }
   }
+  
+  this.live += dt;
+  this.status.score = Math.floor(this.live * 1000);
  },
  gameover:function() {
   this.unscheduleUpdate();
diff --git a/src/resource.js b/src/resource.js
index 756e58d..f392785 100644
--- a/src/resource.js
+++ b/src/resource.js
@@ -12,6 +12,10 @@ var res = {
  return_to_title_selected_png : &quot;res/return_to_title_selected.png&quot;,
  FireParticle_plist : &quot;res/FireParticle.plist&quot;,
  stripes_png : &quot;res/stripes.png&quot;,
+ font_png : &quot;res/main/font.png&quot;,
+ main_json : &quot;res/main/main.json&quot;,
+ score_png : &quot;res/main/score.png&quot;,
+ font_fnt : &quot;res/main/font.fnt&quot;,
 };
 
 var g_resources = [
@@ -28,11 +32,14 @@ var g_resources = [
  res.return_to_title_normal_png,
  res.return_to_title_selected_png,
  res.stripes_png,
+ res.score_png,
+ res.font_png,
 
  // plist
  res.FireParticle_plist,
  
  // fnt
+ res.font_fnt,
 
  // tmx
  
@@ -40,4 +47,6 @@ var g_resources = [
  
  // effect
 
+ // json
+ res.main_json,
 ];
&lt;/pre&gt;
今回の &lt;code&gt;StatusLayer&lt;/code&gt; クラスには、プロパティに setter/getter を定義できる &lt;code&gt;cc.defineGetterSetter&lt;/code&gt; を使って &lt;code&gt;score&lt;/code&gt; というプロパティを定義してみた。これによって &lt;code&gt;setScore&lt;/code&gt; のようなメソッドを呼ばない単なる代入に対しても処理を介入させられるようになるので、間違えて &lt;code&gt;setScore&lt;/code&gt; を呼ばずに &lt;code&gt;score&lt;/code&gt; に代入してしまうといったケースを減らせる。&lt;br /&gt;
&lt;br /&gt;
これでひとまず動くようになった。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEavB-pn4vOEv9ljC4thgIbI0CxNIaYpJ3gvdYZfGWRr6HY3lazXLuuCtCtf1cH0czf_mqbZKhAtz0gAH3iILmhnf-1QGA-ydcA2rvIUh3skPPTgt4SGZH84z5mf2yd9W5O2vHEAspsY8/s1600/ss3.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEavB-pn4vOEv9ljC4thgIbI0CxNIaYpJ3gvdYZfGWRr6HY3lazXLuuCtCtf1cH0czf_mqbZKhAtz0gAH3iILmhnf-1QGA-ydcA2rvIUh3skPPTgt4SGZH84z5mf2yd9W5O2vHEAspsY8/s1600/ss3.jpg&quot; height=&quot;223&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
はずだったのだが、コンパイル済みの Windows 版で出ている画像がブラウザ上だと出ない。この手の問題は自分が原因でないケースも考えられるので調べてみたら&lt;a href=&quot;http://www.cocos2d-x.org/issues/5653&quot; target=&quot;_blank&quot;&gt;それっぽい報告がある&lt;/a&gt;模様。&lt;br /&gt;
寝て待つとそのうち果報が来そうな予感がするしこの問題にはこれ以上対処しないことにする。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;効果音をつける&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
オーディオ周りは多数の環境で動かすことを考えるとどのフォーマットで用意すればいいのか悩ましい。一応&lt;a href=&quot;http://www.cocos2d-x.org/wiki/Audio_formats_supported_by_CocosDenshion_on_different_platforms&quot; target=&quot;_blank&quot;&gt;ここに簡単な表&lt;/a&gt;と、&lt;a href=&quot;http://cocos2d-x.org/docs/tutorial/framework/html5/parkour-game-with-javascript-v3.0/chapter10/en&quot; target=&quot;_blank&quot;&gt;ブラウザの場合はこっち&lt;/a&gt;を見るとどの辺が使えるのかがわかる。ただここまで多種多様なものを覚えるのは結構きついので、本当は *.wav とか *.aiff 辺りで用意しておくと書き出し先の動作環境に合わせて適切にエンコードしてくれたりくれると嬉しいと個人的には思う。&lt;br /&gt;
&lt;br /&gt;
今回は各ブラウザへの対応を考えて ogg と mp3、更にその元データになっている wav も用意した。また、&lt;a href=&quot;http://cocos2d-x.org/docs/tutorial/framework/html5/parkour-game-with-javascript-v3.0/chapter10/en&quot; target=&quot;_blank&quot;&gt;チュートリアル&lt;/a&gt;を見る限りでは「And you don&#39;t need to list both in resource.js, please just list &quot;mp3&quot; format which has better compatibility with iOS and Mac when you run your game on devices.」とのことなので resource.js には mp3 だけを書くことにする。&lt;br /&gt;
&lt;br /&gt;
あとはこれを &lt;code&gt;cc.audioEngine.playEffect(res.jump_mp3)&lt;/code&gt; として再生すればいいだけなのだが、どうもブラウザでは5回以降音がでない状態に陥る。挙動的に再生完了を自動的に検知せず再生数上限か何かに引っかかっているんだろうと思い&lt;a href=&quot;http://www.cocos2d-x.org/reference/html5-js/V3.0rc0/symbols/src/_Users_panda_StudyWork_Cocos_cocos2d-js_frameworks_cocos2d-html5_cocos2d_audio_CCAudio.js.html&quot; target=&quot;_blank&quot;&gt;ちょっと見てみる&lt;/a&gt;と &lt;code&gt;_maxAudioInstance: 5&lt;/code&gt; というのがあったので、どうやらこれっぽい。再生前に前回再生時のオーディオIDを停止させるようにすることで問題なく動くようになった。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/res/jump.mp3 b/res/jump.mp3
new file mode 100644
index 0000000..a2d3a5e
Binary files /dev/null and b/res/jump.mp3 differ
diff --git a/res/jump.ogg b/res/jump.ogg
new file mode 100644
index 0000000..d15fc28
Binary files /dev/null and b/res/jump.ogg differ
diff --git a/res/jump.wav b/res/jump.wav
new file mode 100644
index 0000000..fd5c63a
Binary files /dev/null and b/res/jump.wav differ
diff --git a/src/app.js b/src/app.js
index 468a0df..202de44 100644
--- a/src/app.js
+++ b/src/app.js
@@ -244,6 +244,7 @@ var HelloWorldScene = cc.Scene.extend({
  blocks:null,
  el:null,
  status:null,
+ jumpAudioId:null,
  live:0,
  onEnter:function () {
   this._super();
@@ -297,16 +298,21 @@ var HelloWorldScene = cc.Scene.extend({
   this.addChild(this.status);
   this.status.score = 0;
 
+  var that = this;
   this.el = cc.EventListener.create({
    event: cc.EventListener.TOUCH_ONE_BY_ONE,
    swallowTouches: true,
-   onTouchBegan: this.onTap,
+   onTouchBegan: function(touch, event){ return that.onTap(touch, event); },
   });
   cc.eventManager.addListener(this.el, this);
   this.scheduleUpdateWithPriority(10);
  },
  onTap:function(touch, event) {
-  event.getCurrentTarget().myShip.jump();
+  this.myShip.jump();
+  if (this.jumpAudioId !== null) {
+   cc.audioEngine.stopEffect(this.jumpAudioId);
+  }
+  this.jumpAudioId = cc.audioEngine.playEffect(res.jump_mp3);
   return true;
  },
  update:function(dt) {
diff --git a/src/resource.js b/src/resource.js
index f392785..1bcb3f0 100644
--- a/src/resource.js
+++ b/src/resource.js
@@ -16,6 +16,7 @@ var res = {
  main_json : &quot;res/main/main.json&quot;,
  score_png : &quot;res/main/score.png&quot;,
  font_fnt : &quot;res/main/font.fnt&quot;,
+ jump_mp3 : &quot;res/jump.mp3&quot;,
 };
 
 var g_resources = [
@@ -46,6 +47,7 @@ var g_resources = [
  // bgm
  
  // effect
+ res.jump_mp3,
 
  // json
  res.main_json,
&lt;/pre&gt;
同じ流れで BGM も流すようにしてみる。
&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/res/bgm.mp3 b/res/bgm.mp3
new file mode 100644
index 0000000..218012a
Binary files /dev/null and b/res/bgm.mp3 differ
diff --git a/res/bgm.ogg b/res/bgm.ogg
new file mode 100644
index 0000000..6eb0b1b
Binary files /dev/null and b/res/bgm.ogg differ
diff --git a/src/app.js b/src/app.js
index 202de44..8174010 100644
--- a/src/app.js
+++ b/src/app.js
@@ -298,6 +298,7 @@ var HelloWorldScene = cc.Scene.extend({
   this.addChild(this.status);
   this.status.score = 0;
 
+  cc.audioEngine.playMusic(res.bgm_mp3, true);
   var that = this;
   this.el = cc.EventListener.create({
    event: cc.EventListener.TOUCH_ONE_BY_ONE,
@@ -353,6 +354,10 @@ var HelloWorldScene = cc.Scene.extend({
   this.status.score = Math.floor(this.live * 1000);
  },
  gameover:function() {
+  if (this.jumpAudioId !== null) {
+   cc.audioEngine.stopEffect(this.jumpAudioId);
+  }
+  cc.audioEngine.stopMusic(true);
   this.unscheduleUpdate();
   cc.eventManager.removeListener(this.el);
 
diff --git a/src/resource.js b/src/resource.js
index 1bcb3f0..0edd300 100644
--- a/src/resource.js
+++ b/src/resource.js
@@ -17,6 +17,7 @@ var res = {
  score_png : &quot;res/main/score.png&quot;,
  font_fnt : &quot;res/main/font.fnt&quot;,
  jump_mp3 : &quot;res/jump.mp3&quot;,
+ bgm_mp3 : &quot;res/bgm.mp3&quot;,
 };
 
 var g_resources = [
@@ -45,6 +46,7 @@ var g_resources = [
  // tmx
  
  // bgm
+ res.bgm_mp3,
  
  // effect
  res.jump_mp3,
&lt;/pre&gt;
Windows / Android / Web どの環境でもそれなりに鳴るけど、Windows はループ再生してもギャップレスにはならずループ時にノイズが混じり、Android は曲の終端が切れ、Web(Chrome) は一番理想には近いもののギャップレスにはならなかった。&lt;br /&gt;
&lt;br /&gt;
もし実際にゲームを作る時に BGM を入れる場合は1ループは長めに確保し、曲の頭には少しの無音を入れ、かつ曲の終わりはフェードアウトか無音部分を入れて終わらせる感じだろうか。&lt;br /&gt;
&lt;br /&gt;
ゲームと言っても日常の生活の中で開いた時間にちょっとずつプレイするようなスタイルだと重厚な臨場感溢れる BGM とかそもそもあまりお呼びではない気もするし、取りあえずそこそこに音が出せれば十分であまり洗練されていないのかもしれないと思った。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;加速度センサーの値に応じて挙動を変える&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
端末を傾けるとブロックの速度と得点の上がり方が変わるようにしてみる。これによってゲームプレイ方法に幅が広がる。&lt;br /&gt;
&lt;br /&gt;
加速度センサーからの情報を得るには &lt;code&gt;cc.inputManager.setAccelerometerEnabled&lt;/code&gt; で有効化した上で &lt;code&gt;cc.eventManager.addListener&lt;/code&gt; でイベントリスナーを登録しておけばいいようだ。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 8174010..d4ff88c 100644
--- a/src/app.js
+++ b/src/app.js
@@ -169,6 +169,7 @@ var BlockLayer = cc.Layer.extend({
  d:null,
  vx: 0,
  vy: 0,
+ accel:null,
  ctor:function (width, height, fillcolor) {
   this._super();
   this.d = cc.DrawNode.create();
@@ -182,7 +183,7 @@ var BlockLayer = cc.Layer.extend({
   return true;
  },
  update:function(dt) {
-  this.x += this.vx * dt;
+  this.x += this.vx * dt * (1-Math.min(0.5, Math.max(-0.5, this.accel.x)));
   this.y += this.vy * dt;
  }
 });
@@ -243,14 +244,19 @@ var HelloWorldScene = cc.Scene.extend({
  myShip:null,
  blocks:null,
  el:null,
+ ela:null,
  status:null,
  jumpAudioId:null,
+ accel:null,
  live:0,
  onEnter:function () {
   this._super();
   var size = cc.director.getWinSize();
   var layer = new HelloWorldLayer();
   this.addChild(layer);
+  
+  this.accel={x:0, y:0, z:0};
+
   this.myShip = new MyShipLayer();
   this.addChild(this.myShip);
   this.myShip.attr({
@@ -275,7 +281,8 @@ var HelloWorldScene = cc.Scene.extend({
     x: size.width+Math.random()*size.width,
     y: -bl.height+Math.random()*(size.height+bl.height),
     vx: -60-Math.random()*60,
-    vy: -2.5+Math.random()*5
+    vy: -2.5+Math.random()*5,
+    accel: this.accel,
    });
    this.addChild(bl);
    this.blocks.push(bl);
@@ -306,6 +313,13 @@ var HelloWorldScene = cc.Scene.extend({
    onTouchBegan: function(touch, event){ return that.onTap(touch, event); },
   });
   cc.eventManager.addListener(this.el, this);
+
+  cc.inputManager.setAccelerometerEnabled(true);  
+  this.ela = cc.EventListener.create({
+   event: cc.EventListener.ACCELERATION,
+   callback: function(accel, event){ return that.onAccel(accel, event); },
+  });
+  cc.eventManager.addListener(this.ela, this);
   this.scheduleUpdateWithPriority(10);
  },
  onTap:function(touch, event) {
@@ -316,6 +330,12 @@ var HelloWorldScene = cc.Scene.extend({
   this.jumpAudioId = cc.audioEngine.playEffect(res.jump_mp3);
   return true;
  },
+ onAccel:function(accel, event) {
+  this.accel.x = accel.x;
+  this.accel.y = accel.y;
+  this.accel.z = accel.z;
+  return true;
+ },
  update:function(dt) {
   var myShipBB = this.myShip.getBoundingBox();
   var barrierBB = cc.rect(myShipBB.x-40,myShipBB.y-40,myShipBB.width+80,myShipBB.height+80);
@@ -350,7 +370,7 @@ var HelloWorldScene = cc.Scene.extend({
    }
   }
   
-  this.live += dt;
+  this.live += dt * (1-Math.min(0.5, Math.max(-0.5, this.accel.x)));
   this.status.score = Math.floor(this.live * 1000);
  },
  gameover:function() {
@@ -360,6 +380,7 @@ var HelloWorldScene = cc.Scene.extend({
   cc.audioEngine.stopMusic(true);
   this.unscheduleUpdate();
   cc.eventManager.removeListener(this.el);
+  cc.eventManager.removeListener(this.ela);
 
   var layer = new GameOverLayer();
   this.addChild(layer);
&lt;/pre&gt;
簡単に動くようにはなったのだが、端末の持ち方を上下逆さまにした時ゲーム画面はそれに合わせて回転するもののセンサーの反応はそのままなので、結果的に傾きに対するリアクションが正反対になってしまう。orientation 的なものを取得できれば挙動も反転させられそうだけどどこから取得すればいいのかわからない。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;その他&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Cocos Code IDE で新規プロジェクトを作成した時、画面サイズのデフォルト値が 960x640 になっていたので、スクリーンサイズをそれに変更した。これによって画面サイズが広くなったが、自機やブロックのサイズは変わらないままなので画面に余裕ができてかなり間延びした状態になった。&lt;br /&gt;
&lt;br /&gt;
また、インターフェイスでビットマップフォントのラベルはインターフェイス用背景画像とは別の階層に居たが、これはインターフェイス用背景画像の子としてビットマップフォンのトラベルを配置したほうが正しいと思ったのでそうなるように変更した。変更したところ、数字だけは見えていたブラウザ上でのインターフェイス表示は数字も含めて見えなくなった。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://test.oov.ch/junk/20140723-cocos2d-js/&quot; target=&quot;_blank&quot;&gt;今回までの成果物はこちら&lt;/a&gt;。また、今回はインターフェイスの追加が Web から確認できないので &lt;a href=&quot;http://test.oov.ch/junk/20140723-cocos2d-js/NyanCat-debug.apk&quot; target=&quot;_blank&quot;&gt;Android 用の apk ファイルも公開する&lt;/a&gt;。作業に使っている&lt;a href=&quot;https://github.com/oov/NyanCat&quot; target=&quot;_blank&quot;&gt;リポジトリはこちら&lt;/a&gt;。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/2825458184996667800'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/2825458184996667800'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2014/07/cocos2d-js-5.html' title='Cocos2d-JS 触り始めた5'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrCxDD_LQCAKZT0p5YOgZffFNa4N3Ba44IgKP-PIntFcH2Rtt2RjHPcewHe7l6frFyOYn4SpdTh9vXn0xdiN_-s_ROp65KW-mKiiAscMhOG95z54emRjFNZZusK9rpIGftpA5G5eC5IzQ/s72-c/ss.jpg" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-5497519787581772084</id><published>2014-07-19T07:15:00.000+09:00</published><updated>2014-07-24T19:51:08.615+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Cocos2d-JS"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><title type='text'>Cocos2d-JS 触り始めた4</title><content type='html'>だいぶやりたいことが見つからない感じになってきたので、&lt;a href=&quot;http://www.cocos2d-x.org/reference/html5-js/V3.0rc0/symbols/src/_Users_panda_StudyWork_Cocos_cocos2d-js_frameworks_cocos2d-html5_cocos2d_particle_CCParticleSystem.js.html&quot; target=&quot;_blank&quot;&gt;cc.ParticleSystem&lt;/a&gt; を使って適当に何かを散らしてみようかと思った（やったほうがいいことはもちろんまだたくさんあるが面倒くさい）。&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://test.oov.ch/junk/20140719-cocos2d-js/&quot; target=&quot;_blank&quot;&gt;今回作ったものはこちら&lt;/a&gt;。ホーミングミサイル的なものが出るけど、ミサイルとの当たり判定は特に作ってないのでヒットせずにぐるぐる回り続ける。&lt;br /&gt;
&lt;br /&gt;
作業環境である Windows 8.1 で使えるエディタを適当に検索し見つけたこの &lt;a href=&quot;http://games.v-play.net/particleeditor/&quot; target=&quot;_blank&quot;&gt;Particle Editor&lt;/a&gt; を使って plist ファイルを書き出し、それを読み込み、適当に作ったホーミングミッソーの後ろに向けて噴出する感じで動くようにしてみた。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/res/FireParticle.plist b/res/FireParticle.plist
new file mode 100644
index 0000000..bab965a
--- /dev/null
+++ b/res/FireParticle.plist
@@ -0,0 +1,102 @@
+&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
+&amp;lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&amp;gt;
+&amp;lt;plist version=&quot;1.0&quot;&amp;gt;
+&amp;lt;dict&amp;gt;
+&amp;lt;key&amp;gt;angle&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;angleVariance&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;blendFuncDestination&amp;lt;/key&amp;gt;
+&amp;lt;integer&amp;gt;1&amp;lt;/integer&amp;gt;
+&amp;lt;key&amp;gt;blendFuncSource&amp;lt;/key&amp;gt;
+&amp;lt;integer&amp;gt;1&amp;lt;/integer&amp;gt;
+&amp;lt;key&amp;gt;duration&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;-1&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;emitterType&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;finishColorAlpha&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;finishColorBlue&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;finishColorGreen&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;finishColorRed&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;finishColorVarianceAlpha&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;finishColorVarianceBlue&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;finishColorVarianceGreen&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;finishColorVarianceRed&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;finishParticleSize&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;20&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;finishParticleSizeVariance&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;12&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;gravityx&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;gravityy&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;maxParticles&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;20&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;maxRadius&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;maxRadiusVariance&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;minRadius&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;particleLifespan&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0.3&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;particleLifespanVariance&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0.1&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;radialAccelVariance&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;radialAcceleration&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;rotatePerSecond&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;rotatePerSecondVariance&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;sourcePositionVariancex&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;sourcePositionVariancey&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;sourcePositionx&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;sourcePositiony&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;speed&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;80.0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;speedVariance&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;20.0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;startColorAlpha&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;1&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;startColorBlue&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;1&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;startColorGreen&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;1&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;startColorRed&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;1&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;startColorVarianceAlpha&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;1&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;startColorVarianceBlue&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;1&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;startColorVarianceGreen&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;startColorVarianceRed&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;startParticleSize&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;startParticleSizeVariance&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;tangentialAccelVariance&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;tangentialAcceleration&amp;lt;/key&amp;gt;
+&amp;lt;real&amp;gt;0&amp;lt;/real&amp;gt;
+&amp;lt;key&amp;gt;textureFileName&amp;lt;/key&amp;gt;
+&amp;lt;string&amp;gt;stripes.png&amp;lt;/string&amp;gt;
+&amp;lt;key&amp;gt;textureImageData&amp;lt;/key&amp;gt;
+&amp;lt;string&amp;gt;&amp;lt;/string&amp;gt;
+&amp;lt;/dict&amp;gt;
+&amp;lt;/plist&amp;gt;
diff --git a/res/stripes.png b/res/stripes.png
new file mode 100644
index 0000000..f67ac0b
Binary files /dev/null and b/res/stripes.png differ
diff --git a/src/app.js b/src/app.js
index e625506..e0d3a27 100644
--- a/src/app.js
+++ b/src/app.js
@@ -162,6 +162,51 @@ var BlockLayer = cc.Layer.extend({
     }
 });

+var HomingMissoLayer = cc.Layer.extend({
+    target:null,
+    particle:null,
+    d:null,
+    a:0,
+    vx:0,
+    vy:0,
+    speed:6,
+    angle:0.3,
+    ctor:function () {
+      this._super();
+      this.scheduleUpdate();
+      this.d = cc.DrawNode.create();
+      this.d.drawRect(cc.p(-8, -2), cc.p(8, 2), cc.color(255,255,255,255));
+      this.addChild(this.d, 0);
+      var bbox = cc.size(16, 16);
+      this.d.setPosition(bbox.width * 0.5, bbox.height * 0.5);
+      this.setContentSize(bbox);
+      this.particle = cc.ParticleSystem.create(res.FireParticle_plist);
+      this.addChild(this.particle);
+      return true;
+    },
+    update:function(dt) {
+      var bb = this.target.getBoundingBox();
+      var dx = this.d.x, dy = this.d.y;
+      var vx = this.vx, vy = this.vy;
+      var tx = bb.x + bb.width*0.5 - dx, ty = bb.y + bb.height*0.5 - dy;
+      var tl = Math.sqrt(tx*tx+ty*ty), a = this.a;
+      if ((tx*vx+ty*vy)/(tl*Math.sqrt(vx*vx+vy*vy)) &amp;lt; 0) {
+        a += this.angle;
+      } else {
+        a += this.angle * (vx*ty - vy*tx)/tl/this.speed;
+      }
+      this.a = a;
+      var cosa = Math.cos(a), sina = Math.sin(a), r = a*180/Math.PI;
+      this.vx = vx = this.speed * cosa;
+      this.vy = vy = this.speed * sina;
+      this.d.x = dx += vx;
+      this.d.y = dy += vy;
+      this.d.rotation = -r;
+      this.particle.setSourcePosition(cc.p(dx + -6 * cosa, dy + -6 * sina));
+      this.particle.angle = 180+r;
+    }
+});
+
 var HelloWorldScene = cc.Scene.extend({
     myShip:null,
     blocks:null,
@@ -200,6 +245,19 @@ var HelloWorldScene = cc.Scene.extend({
           this.blocks.push(bl);
         }

+        var ml = new HomingMissoLayer();
+        ml.attr({target: this.blocks[0]});
+        this.addChild(ml);
+        var ml = new HomingMissoLayer();
+        ml.attr({target: this.blocks[1]});
+        this.addChild(ml);
+        var ml = new HomingMissoLayer();
+        ml.attr({target: this.blocks[2]});
+        this.addChild(ml);
+        var ml = new HomingMissoLayer();
+        ml.attr({target: this.blocks[3]});
+        this.addChild(ml);
+
         this.el = cc.EventListener.create({
             event: cc.EventListener.TOUCH_ONE_BY_ONE,
             swallowTouches: true,
diff --git a/src/resource.js b/src/resource.js
index 574ab3e..24c26df 100644
--- a/src/resource.js
+++ b/src/resource.js
@@ -9,7 +9,9 @@ var res = {
     restart_normal_png : &quot;res/restart_normal.png&quot;,
     restart_selected_png : &quot;res/restart_selected.png&quot;,
     return_to_title_normal_png : &quot;res/return_to_title_normal.png&quot;,
-    return_to_title_selected_png : &quot;res/return_to_title_selected.png&quot;
+    return_to_title_selected_png : &quot;res/return_to_title_selected.png&quot;,
+    FireParticle_plist : &quot;res/FireParticle.plist&quot;,
+    stripes_png : &quot;res/stripes.png&quot;,
 };

 var g_resources = [
@@ -24,9 +26,11 @@ var g_resources = [
     res.restart_normal_png,
     res.restart_selected_png,
     res.return_to_title_normal_png,
-    res.return_to_title_selected_png
+    res.return_to_title_selected_png,
+    res.stripes_png,

     //plist
+    res.FireParticle_plist,

     //fnt
&lt;/pre&gt;
気をつける必要があるのは &lt;code&gt;cc.ParticleSystem&lt;/code&gt; を &lt;code&gt;addChild&lt;/code&gt; した親のレイヤーなどを移動すると全体が動いてしまい、出力されているパーティクルに本来存在しない動きが加わってしまい予め設定していた振る舞いと若干異なる挙動になってしまう（もちろんわかった上でやる分には問題ない）。&lt;br /&gt;
&lt;br /&gt;
なので、今回のミッソーのように噴出口自体は動くものの噴出したパーティクルは出力時の動きを維持したいようなケースの場合、&lt;code&gt;setSourcePosition&lt;/code&gt; や &lt;code&gt;angle&lt;/code&gt; を使って正しい位置に合わせる必要があるようだった。&lt;br /&gt;
&lt;br /&gt;
その辺りを把握していれば動かす上で障壁になるようなものはあまりない気がするので、非常に効率よく映えるエフェクトを追加できて便利なので積極的に使っていきたい仕組みだった。&lt;br /&gt;
&lt;br /&gt;
plist ファイルに仕組みを追い出しておくことによってプログラマーとデザイナーで作業もある程度分担しやすくなるだろうしとても良い。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/5497519787581772084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/5497519787581772084'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2014/07/cocos2d-js-4.html' title='Cocos2d-JS 触り始めた4'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-5955492553274201665</id><published>2014-07-18T07:25:00.002+09:00</published><updated>2014-07-24T19:51:08.618+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Cocos2d-JS"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><title type='text'>Cocos2d-JS 触り始めた3</title><content type='html'>前回までの作業で操作することでブロックを避けられるようになり、少しゲームっぽくなってきた。&lt;br /&gt;
もう少し弄ってみる。&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;b&gt;画面外に出たらゲームオーバーにする&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
まだ上下にはみ出した時にゲームオーバーにならないので、これがゲームオーバーになるようにする。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 28cd4bc..db28f83 100644
--- a/src/app.js
+++ b/src/app.js
@@ -156,12 +156,14 @@ var HelloWorldScene = cc.Scene.extend({
     update:function(dt) {
       var myShipBB = this.myShip.getBoundingBox();
       var size = cc.director.getWinSize();
+      if (myShipBB.y+myShipBB.height &amp;lt; 0 || myShipBB.y &amp;gt; size.height) {
+        this.gameover();
+        return;
+      }
       for(var i = this.blocks.length - 1, bl; i &amp;gt;= 0; --i) {
         bl = this.blocks[i];
         if (cc.rectIntersectsRect(myShipBB, bl.getBoundingBox())) {
-          this.unscheduleUpdate();
-          cc.eventManager.removeListener(this);
-          cc.director.runScene(cc.TransitionFade.create(0.5, new HelloWorldScene()));
+          this.gameover();
           return;
         }
         if (bl.x+bl.width &amp;lt; 0) {
@@ -172,4 +174,9 @@ var HelloWorldScene = cc.Scene.extend({
         }
       }
     },
+    gameover:function() {
+      this.unscheduleUpdate();
+      cc.eventManager.removeListener(this);
+      cc.director.runScene(cc.TransitionFade.create(0.5, new HelloWorldScene()));
+    }
 });
&lt;/pre&gt;
自機が完全に画面の外に出たら終わり。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;飽きたのでちょっと遊ぶ&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
動作テストのためにブロックを避け続けていたので、たまにはブロック側から自機を避けてもいい気がしてきた。試しに自機に近づいたブロックが勝手に吹っ飛んでいく処理を書いてストレス解消しようと思う。&lt;br /&gt;
&lt;br /&gt;
判定として「自機に半径rピクセル以上近づいたら」とやりたいところではあるものの円と矩形の衝突判定は複雑で重いので、今回は色々な面から妥協して自機を囲う大きな矩形と接触したら弾くことにする。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index db28f83..04473b9 100644
--- a/src/app.js
+++ b/src/app.js
@@ -155,14 +155,25 @@ var HelloWorldScene = cc.Scene.extend({
     },
     update:function(dt) {
       var myShipBB = this.myShip.getBoundingBox();
+      var barrierBB = cc.rect(myShipBB.x-40,myShipBB.y-40,myShipBB.width+80,myShipBB.height+80);
       var size = cc.director.getWinSize();
       if (myShipBB.y+myShipBB.height &amp;lt; 0 || myShipBB.y &amp;gt; size.height) {
         this.gameover();
         return;
       }
-      for(var i = this.blocks.length - 1, bl; i &amp;gt;= 0; --i) {
+      for(var i = this.blocks.length - 1, bl, bb; i &amp;gt;= 0; --i) {
         bl = this.blocks[i];
-        if (cc.rectIntersectsRect(myShipBB, bl.getBoundingBox())) {
+        bb = bl.getBoundingBox();
+        if (cc.rectIntersectsRect(barrierBB, bb)) {
+          var vx = (bb.x+bb.width*0.5) - (barrierBB.x+barrierBB.width*0.5);
+          var vy = (bb.y+bb.height*0.5) - (barrierBB.y+barrierBB.height*0.5);
+          var l = 1/Math.sqrt(vx*vx+vy*vy)*4;
+          bl.attr({
+            vx: vx * l,
+            vy: vy * l,
+          });
+        }
+        if (cc.rectIntersectsRect(myShipBB, bb)) {
           this.gameover();
           return;
         }
&lt;/pre&gt;
自機より40ピクセル大きい矩形で判定して、ヒットした場合は自機と反対側の方向へ吹き飛ばすようにした。&lt;br /&gt;
&lt;br /&gt;
これによってブロックは左端からスクリーンアウトする以外の理由で退場するケースが出てくるようになったので、画面外に出た判定処理をもう少しまともに作り直す。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 04473b9..ecb688c 100644
--- a/src/app.js
+++ b/src/app.js
@@ -177,10 +177,12 @@ var HelloWorldScene = cc.Scene.extend({
           this.gameover();
           return;
         }
-        if (bl.x+bl.width &amp;lt; 0) {
+        if (bl.x+bl.width &amp;lt; 0 || bl.x &amp;gt; size.width || bl.y+bl.height &amp;lt; 0 || bl.y &amp;gt; size.height) {
           bl.attr({
             x: size.width,
-            y: -bl.height+Math.random()*(size.height+bl.height)
+            y: -bl.height+Math.random()*(size.height+bl.height),
+            vx: -0.5-Math.random()*3,
+            vy: -0.25+Math.random()*0.5
           });
         }
       }
&lt;/pre&gt;
ブロックが表示領域外に出たら各パラメータをセットし直す感じになった。&lt;br /&gt;
&lt;br /&gt;
この挙動を修正すると、今度はブロックの初期位置が横に2画面分あると仮定して見えない位置に配置していたのが、ゲーム開始直後に画面外判定を食らっていきなり同じ位置に集結してしまうようになった。&lt;br /&gt;
&lt;br /&gt;
この問題を解決する方法は色々あるものの、致命的な問題ではないのでひとまず保留にする。一番短絡的な解決方法はブロックの画面外判定を右側だけもう1画面先にすることだと思う。&lt;br /&gt;
&lt;br /&gt;
他にも高い位置から落下をすると結構な速度が出るのでそのタイミングでブロックを弾き飛ばしても接触できてしまうとかの問題もある。&lt;br /&gt;
&lt;br /&gt;
これらの問題は後で考えるとして、取りあえず遊びで作ったこのバリアー機能は自機にバリアーフラグが立っている時だけ有効にするようにしよう。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index ecb688c..976cd6f 100644
--- a/src/app.js
+++ b/src/app.js
@@ -64,6 +64,7 @@ var HelloWorldLayer = cc.Layer.extend({
 var MyShipLayer = cc.Layer.extend({
     sprite:null,
     vy: 3,
+    barrier:false,
     ctor:function () {
         this._super();
         this.sprite = cc.Sprite.create(res.nc_png);
@@ -164,7 +165,7 @@ var HelloWorldScene = cc.Scene.extend({
       for(var i = this.blocks.length - 1, bl, bb; i &amp;gt;= 0; --i) {
         bl = this.blocks[i];
         bb = bl.getBoundingBox();
-        if (cc.rectIntersectsRect(barrierBB, bb)) {
+        if (this.myShip.barrier &amp;amp;&amp;amp; cc.rectIntersectsRect(barrierBB, bb)) {
           var vx = (bb.x+bb.width*0.5) - (barrierBB.x+barrierBB.width*0.5);
           var vy = (bb.y+bb.height*0.5) - (barrierBB.y+barrierBB.height*0.5);
           var l = 1/Math.sqrt(vx*vx+vy*vy)*4;
&lt;/pre&gt;
このバリアーフラグを立てる処理自体はまたの機会、ということで。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;タイトル画面をつける&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
重い腰を上げ、ついにタイトル画面をつけることにする。重かった理由は目新しい要素がないことと、リソースを追加するのが面倒だったから。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/main.js b/main.js
index 8569b66..34eecb3 100644
--- a/main.js
+++ b/main.js
@@ -3,7 +3,7 @@ cc.game.onStart = function(){
        cc.view.resizeWithBrowserSize(true);
     //load resources
     cc.LoaderScene.preload(g_resources, function () {
-        cc.director.runScene(new HelloWorldScene());
+        cc.director.runScene(new TitleScene());
     }, this);
 };
 cc.game.run();
diff --git a/res/game_start_normal.png b/res/game_start_normal.png
new file mode 100644
index 0000000..b4212fd
Binary files /dev/null and b/res/game_start_normal.png differ
diff --git a/res/game_start_selected.png b/res/game_start_selected.png
new file mode 100644
index 0000000..4e4c43a
Binary files /dev/null and b/res/game_start_selected.png differ
diff --git a/res/title.jpg b/res/title.jpg
new file mode 100644
index 0000000..d1d1285
Binary files /dev/null and b/res/title.jpg differ
diff --git a/src/app.js b/src/app.js
index 976cd6f..e58c103 100644
--- a/src/app.js
+++ b/src/app.js
@@ -194,3 +194,43 @@ var HelloWorldScene = cc.Scene.extend({
       cc.director.runScene(cc.TransitionFade.create(0.5, new HelloWorldScene()));
     }
 });
+
+var TitleLayer = cc.Layer.extend({
+    ctor:function () {
+        this._super();
+        var size = cc.director.getWinSize();
+        var sprite = cc.Sprite.create(res.title_jpg);
+        sprite.attr({
+            x: size.width * 0.5,
+            y: size.height * 0.5
+        });
+        this.addChild(sprite, 0);
+
+        var gameStartItem = cc.MenuItemImage.create(
+            res.game_start_normal_png,
+            res.game_start_selected_png,
+            function () {
+                cc.director.runScene(cc.TransitionFade.create(0.5, new HelloWorldScene()));
+            }, this);
+        gameStartItem.attr({
+            x: size.width * 0.5,
+            y: 80,
+            anchorX: 0.5,
+            anchorY: 0.5
+        });
+
+        var menu = cc.Menu.create(gameStartItem);
+        menu.x = 0;
+        menu.y = 0;
+        this.addChild(menu, 1);
+        return true;
+    }
+});
+
+var TitleScene = cc.Scene.extend({
+    onEnter:function () {
+        this._super();
+        var layer = new TitleLayer();
+        this.addChild(layer);
+    }
+});
diff --git a/src/resource.js b/src/resource.js
index 00cd775..2f3b84c 100644
--- a/src/resource.js
+++ b/src/resource.js
@@ -2,7 +2,10 @@ var res = {
     HelloWorld_png : &quot;res/HelloWorld.png&quot;,
     CloseNormal_png : &quot;res/CloseNormal.png&quot;,
     CloseSelected_png : &quot;res/CloseSelected.png&quot;,
-    nc_png : &quot;res/nc.png&quot;
+    nc_png : &quot;res/nc.png&quot;,
+    title_jpg : &quot;res/title.jpg&quot;,
+    game_start_normal_png : &quot;res/game_start_normal.png&quot;,
+    game_start_selected_png : &quot;res/game_start_selected.png&quot;
 };

 var g_resources = [
@@ -10,7 +13,10 @@ var g_resources = [
     res.HelloWorld_png,
     res.CloseNormal_png,
     res.CloseSelected_png,
-    res.nc_png
+    res.nc_png,
+    res.title_jpg,
+    res.game_start_normal_png,
+    res.game_start_selected_png

     //plist
&lt;/pre&gt;
リソースとしてタイトル画像、スタートボタンの通常時とタップ中の画像を追加し、それらを使った &lt;code&gt;TitleLayer&lt;/code&gt; を作り、それを配置するだけの単純な &lt;code&gt;TitleScene&lt;/code&gt; を作った。&lt;br /&gt;
&lt;br /&gt;
ほとんど初期状態の &lt;code&gt;HelloWorldScene&lt;/code&gt; と変わらない構成になっている。ボタン画像は&lt;a href=&quot;http://dabuttonfactory.com/&quot; target=&quot;_blank&quot;&gt;このサイト&lt;/a&gt;で適当に作った。&lt;br /&gt;
&lt;br /&gt;
真面目に映える感じのタイトルにするならもう少し動かしたりとかタイトルっぽく振る舞う必要がありそう。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;ゲームオーバーの処理も入れる&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
タイトル画面を作ったついでに、ゲームオーバーの処理も入れてしまうことにする。&lt;br /&gt;
&lt;br /&gt;
この画面はリスタートとタイトル画面に戻ることができるようにする。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/res/restart_normal.png b/res/restart_normal.png
new file mode 100644
index 0000000..a1105f7
Binary files /dev/null and b/res/restart_normal.png differ
diff --git a/res/restart_selected.png b/res/restart_selected.png
new file mode 100644
index 0000000..d953a99
Binary files /dev/null and b/res/restart_selected.png differ
diff --git a/res/return_to_title_normal.png b/res/return_to_title_normal.png
new file mode 100644
index 0000000..de11728
Binary files /dev/null and b/res/return_to_title_normal.png differ
diff --git a/res/return_to_title_selected.png b/res/return_to_title_selected.png
new file mode 100644
index 0000000..af5b3c2
Binary files /dev/null and b/res/return_to_title_selected.png differ
diff --git a/src/app.js b/src/app.js
index e58c103..0c185f2 100644
--- a/src/app.js
+++ b/src/app.js
@@ -61,6 +61,62 @@ var HelloWorldLayer = cc.Layer.extend({
     }
 });

+var GameOverLayer = cc.Layer.extend({
+    ctor:function () {
+        this._super();
+        var size = cc.director.getWinSize();
+        var sprite = cc.Sprite.create(res.title_jpg);
+        sprite.attr({
+            x: size.width * 0.5,
+            y: size.height * 0.5,
+            opacity: 0
+        });
+        this.addChild(sprite, 0);
+
+        var restartItem = cc.MenuItemImage.create(
+            res.restart_normal_png,
+            res.restart_selected_png,
+            function () {
+                cc.director.runScene(cc.TransitionFade.create(0.5, new HelloWorldScene()));
+            }, this);
+        restartItem.attr({
+            x: size.width * 0.5,
+            y: 160,
+            anchorX: 0.5,
+            anchorY: 0.5
+        });
+        var returnToTitleItem = cc.MenuItemImage.create(
+            res.return_to_title_normal_png,
+            res.return_to_title_selected_png,
+            function () {
+                cc.director.runScene(cc.TransitionFade.create(0.5, new TitleScene()));
+            }, this);
+        returnToTitleItem.attr({
+            x: size.width * 0.5,
+            y: 80,
+            anchorX: 0.5,
+            anchorY: 0.5
+        });
+
+        var menu = cc.Menu.create([restartItem, returnToTitleItem]);
+        menu.attr({
+          x: 0,
+          y: 0,
+          opacity: 0,
+          enabled: false,
+        });
+        this.addChild(menu, 1);
+        sprite.runAction(cc.FadeIn.create(0.5));
+        menu.runAction(cc.Sequence.create(
+          cc.DelayTime.create(0.5),
+          cc.FadeIn.create(0.5),
+          cc.CallFunc.create(function(){ this.enabled = true; }, menu)
+        ));
+
+        return true;
+    }
+});
+
 var MyShipLayer = cc.Layer.extend({
     sprite:null,
     vy: 3,
@@ -191,7 +247,9 @@ var HelloWorldScene = cc.Scene.extend({
     gameover:function() {
       this.unscheduleUpdate();
       cc.eventManager.removeListener(this);
-      cc.director.runScene(cc.TransitionFade.create(0.5, new HelloWorldScene()));
+
+      var layer = new GameOverLayer();
+      this.addChild(layer);
     }
 });

diff --git a/src/resource.js b/src/resource.js
index 2f3b84c..574ab3e 100644
--- a/src/resource.js
+++ b/src/resource.js
@@ -5,7 +5,11 @@ var res = {
     nc_png : &quot;res/nc.png&quot;,
     title_jpg : &quot;res/title.jpg&quot;,
     game_start_normal_png : &quot;res/game_start_normal.png&quot;,
-    game_start_selected_png : &quot;res/game_start_selected.png&quot;
+    game_start_selected_png : &quot;res/game_start_selected.png&quot;,
+    restart_normal_png : &quot;res/restart_normal.png&quot;,
+    restart_selected_png : &quot;res/restart_selected.png&quot;,
+    return_to_title_normal_png : &quot;res/return_to_title_normal.png&quot;,
+    return_to_title_selected_png : &quot;res/return_to_title_selected.png&quot;
 };

 var g_resources = [
@@ -16,7 +20,11 @@ var g_resources = [
     res.nc_png,
     res.title_jpg,
     res.game_start_normal_png,
-    res.game_start_selected_png
+    res.game_start_selected_png,
+    res.restart_normal_png,
+    res.restart_selected_png,
+    res.return_to_title_normal_png,
+    res.return_to_title_selected_png

     //plist
&lt;/pre&gt;
タイトル画面とほとんど一緒。本当はゲームオーバーっぽい画面を出すところだけどリソースを使いまわしてタイトルと同じ画像を出している。&lt;br /&gt;
&lt;br /&gt;
ちょっと違うのは、ボタンが2つあるところと、フェードインエフェクトが付いているところ。そしてフェードインの処理は画像のあとボタンがフェードインするようになっていて、完全に表示されるまでの間は &lt;code&gt;enabled&lt;/code&gt; を &lt;code&gt;false&lt;/code&gt; にしておくことで、間違って押すのを防止している。&lt;br /&gt;
&lt;br /&gt;
ゲームオーバー画面はゲームプレイ中にボタンが現れる感じになるので、ゲームオーバーになったことに気付かないまま間違えてボタンを押してしまう可能性がありそうだったのでボタンが有効になるまでの時間を少し遅くした。とはいえ1秒でも足りない気もするので、ちゃんと死亡エフェクトを見せるのが正しい気もする。&lt;br /&gt;
&lt;br /&gt;
あと、この追加によって勘違いによるバグがあったのを見つけたので修正した。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 0c185f2..c2fa56f 100644
--- a/src/app.js
+++ b/src/app.js
@@ -165,6 +165,7 @@ var BlockLayer = cc.Layer.extend({
 var HelloWorldScene = cc.Scene.extend({
     myShip:null,
     blocks:null,
+    el:null,
     onEnter:function () {
         this._super();
         var size = cc.director.getWinSize();
@@ -199,11 +200,12 @@ var HelloWorldScene = cc.Scene.extend({
           this.blocks.push(bl);
         }

-        cc.eventManager.addListener({
+        this.el = cc.EventListener.create({
             event: cc.EventListener.TOUCH_ONE_BY_ONE,
             swallowTouches: true,
             onTouchBegan: this.onTap,
-        }, this)
+        });
+        cc.eventManager.addListener(this.el, this);
         this.scheduleUpdateWithPriority(10);
     },
     onTap:function(touch, event) {
@@ -246,7 +248,7 @@ var HelloWorldScene = cc.Scene.extend({
     },
     gameover:function() {
       this.unscheduleUpdate();
-      cc.eventManager.removeListener(this);
+      cc.eventManager.removeListener(this.el);

       var layer = new GameOverLayer();
       this.addChild(layer);
&lt;/pre&gt;
イベントリスナーの削除は &lt;code&gt;cc.eventManager.removeListener&lt;/code&gt; を呼んだ時点では失敗していて、シーン自体が入れ替わった時に合わせて削除されていたために成功と勘違いしていた。正しく削除するためには &lt;code&gt;cc.EventListener.create&lt;/code&gt; で作成したインスタンスを渡さないといけなかったようだ。&lt;br /&gt;
&lt;br /&gt;
そして Android 版で動かそうとしたところ &quot;Invalid Native Object&quot; というエラーで失敗したため以下の修正を行った。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index c2fa56f..e625506 100644
--- a/src/app.js
+++ b/src/app.js
@@ -98,7 +98,7 @@ var GameOverLayer = cc.Layer.extend({
             anchorY: 0.5
         });

-        var menu = cc.Menu.create([restartItem, returnToTitleItem]);
+        var menu = cc.Menu.create(restartItem, returnToTitleItem);
         menu.attr({
           x: 0,
           y: 0,
&lt;/pre&gt;
配列のネイティブオブジェクトの処理に失敗したのかもしれないと思い、実装を確認してみたところ可変長引数でも受け取れるような構造になっていたのでそのまま渡すことで動くようになった。&lt;br /&gt;
&lt;br /&gt;
また、致命的な問題ではないもののどうやらメニューのフェードインも上手く動いていないようだ。Cocos2dx との挙動の違いがあるんだろうか。&lt;br /&gt;
&lt;br /&gt;
今回はあまり目新しい変化はなくブロック登場時のバグを注入しただけに見える気もするが、&lt;a href=&quot;http://test.oov.ch/junk/20140718-cocos2d-js/&quot; target=&quot;_blank&quot;&gt;今回までの進捗はこちら&lt;/a&gt;。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/5955492553274201665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/5955492553274201665'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2014/07/cocos2d-js-3.html' title='Cocos2d-JS 触り始めた3'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-6224487986054072680</id><published>2014-07-17T02:50:00.000+09:00</published><updated>2014-07-24T19:51:08.622+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Cocos2d-JS"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><title type='text'>Cocos2d-JS 触り始めた2</title><content type='html'>&lt;a href=&quot;http://bl.oov.ch/2014/07/cocos2d-js.html&quot;&gt;前回の記事&lt;/a&gt;ではデモプロジェクトを少し触って大体の雰囲気を掴んだ。&lt;br /&gt;
&lt;br /&gt;
今回はもう少し使い込んでみるために、以前作った&lt;a href=&quot;http://test.oov.ch/junk/space-keyC.html&quot; target=&quot;_blank&quot;&gt;簡単なゲーム&lt;/a&gt;を Cocos2d-JS 上で動くよう移植してみることにする。&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
このゲームはスペースキーを押すとジャンプするだけのいわゆるところのワンキーゲームで、邪魔なブロックの登場パターンはランダムながら毎回同じシード値で生成しているので、練習するとその分だけスコアが伸びる感じのゲームになっている。&lt;br /&gt;
&lt;br /&gt;
死んだらページをリロードするしかなくなったり、BGM を YouTube の動画で再生しているけどループしなかったりとか、読み込み完了前にゲームを開始すると動画再生が上手く始まらなかったりとか、つくりは色々投げやりであるものの、まあ枝葉の問題なので放置している。&lt;a href=&quot;http://bl.oov.ch/2011/09/blog-post.html&quot; target=&quot;_blank&quot;&gt;この記事&lt;/a&gt;で当時作っていた頃の変更履歴的なものを追える。&lt;br /&gt;
&lt;br /&gt;
移植と言っても完全な再現とかではなく、似たような動きをする程度のものにしようと思う。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://test.oov.ch/junk/20140717-cocos2d-js/&quot; target=&quot;_blank&quot;&gt;現在までの成果物はこんな感じ&lt;/a&gt;。&lt;br /&gt;
クリックやタップするとジャンプでき、ブロックを避けることができる。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;プロジェクトを準備する&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
前回までの変更は一旦全て忘れて、改めて &lt;code&gt;cocos new -l js NyanCat&lt;/code&gt; として新規にプロジェクトを作成した。&lt;br /&gt;
今回のゲームは横画面の方が向いているので、前回行ったような画面の向きの変更などは行わない。&lt;br /&gt;
&lt;br /&gt;
また、本格的にものを作るに当たり、今回からデータは git で管理することにする。&lt;br /&gt;
そのため、まず新規にプロジェクトを作成した直後の状態でコミットし、その後 Android 版のビルドを行い、生成されたファイルのうちコミットに含める必要がなさそうなファイルを &lt;code&gt;.gitignore&lt;/code&gt; で除外した上で作業を始める。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;ゲームのメイン画面を作る&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
前回適当にいじくり回したことで、シーン切り替えの方法は概ね分かった。&lt;br /&gt;
タイトル画面を作ることに対しては特に難しい課題もないため、いきなりゲームのメイン画面を作っていくことにする。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;自機を作る&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
猫のキャラを表示させるため NyanCat/res/nc.png にファイルを配置し、NyanCat/src/resource.js を以下のように改変した。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/res/nc.png b/res/nc.png
new file mode 100644
index 0000000..10e8307
Binary files /dev/null and b/res/nc.png differ
diff --git a/src/resource.js b/src/resource.js
index 508554a..00cd775 100644
--- a/src/resource.js
+++ b/src/resource.js
@@ -1,14 +1,16 @@
 var res = {
     HelloWorld_png : &quot;res/HelloWorld.png&quot;,
     CloseNormal_png : &quot;res/CloseNormal.png&quot;,
-    CloseSelected_png : &quot;res/CloseSelected.png&quot;
+    CloseSelected_png : &quot;res/CloseSelected.png&quot;,
+    nc_png : &quot;res/nc.png&quot;
 };

 var g_resources = [
     //image
     res.HelloWorld_png,
     res.CloseNormal_png,
-    res.CloseSelected_png
+    res.CloseSelected_png,
+    res.nc_png

     //plist
&lt;/pre&gt;
そして追加した画像を &lt;code&gt;HelloWorldScene&lt;/code&gt; に自機用のレイヤーとして追加したい。&lt;br /&gt;
なので、NyanCat/src/app.js に &lt;code&gt;MyShipLayer&lt;/code&gt; を追加し、&lt;code&gt;HelloWorldScene&lt;/code&gt; に &lt;code&gt;MyShipLayer&lt;/code&gt; を追加するためのコードを追加。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 1f2857e..3a4c7e4 100644
--- a/src/app.js
+++ b/src/app.js
@@ -61,11 +61,22 @@ var HelloWorldLayer = cc.Layer.extend({
     }
 });

+var MyShipLayer = cc.Layer.extend({
+    sprite:null,
+    ctor:function () {
+        this._super();
+        this.sprite = cc.Sprite.create(res.nc_png);
+        this.addChild(this.sprite, 0);
+        return true;
+    }
+});
+
 var HelloWorldScene = cc.Scene.extend({
     onEnter:function () {
         this._super();
         var layer = new HelloWorldLayer();
         this.addChild(layer);
+        var myShip = new MyShipLayer();
+        this.addChild(myShip);
     }
 });
&lt;/pre&gt;
この時点で画面の左下に自機が少し見えるようになった。&lt;br /&gt;
&lt;br /&gt;
まだ静止状態なので動かしたいが、この自機はユーザーの入力に反応して動かす必要があるので、サンプルで動いていたアニメーションのように未来の動きを予め登録しておくようなことはできない。&lt;br /&gt;
&lt;br /&gt;
そのため&lt;a href=&quot;http://www.cocos2d-x.org/docs/tutorial/framework/html5/parkour-game-with-javascript-v3.0/chapter6/en&quot; target=&quot;_blank&quot;&gt;この辺のチュートリアル&lt;/a&gt;でも使われている &lt;code&gt;update&lt;/code&gt; メソッドを追加して、毎フレームの処理として動きを記述する。更にコンストラクタで &lt;code&gt;this.scheduleUpdate&lt;/code&gt; を呼んで、毎フレーム &lt;code&gt;update&lt;/code&gt; が呼ばれるようにする。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 3a4c7e4..23a3322 100644
--- a/src/app.js
+++ b/src/app.js
@@ -67,7 +67,12 @@ var MyShipLayer = cc.Layer.extend({
         this._super();
         this.sprite = cc.Sprite.create(res.nc_png);
         this.addChild(this.sprite, 0);
+        this.scheduleUpdate();
         return true;
+    },
+    update:function(dt) {
+        this.x += 1;
+        this.y += 1;
     }
 });
&lt;/pre&gt;
移動処理は取りあえず適当に直線移動。実行すると、左下から右上に向かって毎フレーム 1px ずつ動くようになる。&lt;br /&gt;
&lt;br /&gt;
毎フレームの処理によってアニメーションが一応できたし、後はユーザーの入力によって反応して動くようにすれば、基本的な自機の振る舞いは完成する。&lt;br /&gt;
&lt;br /&gt;
これも&lt;a href=&quot;http://www.cocos2d-x.org/docs/tutorial/framework/html5/parkour-game-with-javascript-v3.0/chapter9/en&quot; target=&quot;_blank&quot;&gt;チュートリアル&lt;/a&gt;を参考にして Scene のタップに反応できるようにする。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 23a3322..2e324b7 100644
--- a/src/app.js
+++ b/src/app.js
@@ -83,5 +83,15 @@ var HelloWorldScene = cc.Scene.extend({
         this.addChild(layer);
         var myShip = new MyShipLayer();
         this.addChild(myShip);
-    }
+        cc.eventManager.addListener({
+            event: cc.EventListener.TOUCH_ONE_BY_ONE,
+            swallowTouches: true,
+            onTouchBegan: this.onTap,
+        }, this)
+    },
+    onTap:function(touch, event) {
+        var pos = touch.getLocation();
+        cc.log(pos);
+        return true;
+    },
 });
&lt;/pre&gt;
ひとまず、タップされたら座標をログに吐くようにしてみた。&lt;br /&gt;
実行してみると、タップ位置が左下を &lt;code&gt;0, 0&lt;/code&gt; とした値で取得できているのが確認できる。&lt;br /&gt;
&lt;br /&gt;
動きの流れのイメージとしてはシーンをタップしたらシーンが &lt;code&gt;MyShipLayer&lt;/code&gt; にジャンプしろと伝達する感じなので、そうなるようにコードを修正していく。&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;MyShipLayer&lt;/code&gt; に &lt;code&gt;jump&lt;/code&gt; メソッドを新しく追加し、タップされた時にそのメソッドを呼び出すようにする。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 2e324b7..dddb3d2 100644
--- a/src/app.js
+++ b/src/app.js
@@ -73,16 +73,20 @@ var MyShipLayer = cc.Layer.extend({
     update:function(dt) {
         this.x += 1;
         this.y += 1;
+    },
+    jump:function() {
+      this.y += 10;
     }
 });

 var HelloWorldScene = cc.Scene.extend({
+    myShip:null,
     onEnter:function () {
         this._super();
         var layer = new HelloWorldLayer();
         this.addChild(layer);
-        var myShip = new MyShipLayer();
-        this.addChild(myShip);
+        this.myShip = new MyShipLayer();
+        this.addChild(this.myShip);
         cc.eventManager.addListener({
             event: cc.EventListener.TOUCH_ONE_BY_ONE,
             swallowTouches: true,
@@ -90,8 +94,7 @@ var HelloWorldScene = cc.Scene.extend({
         }, this)
     },
     onTap:function(touch, event) {
-        var pos = touch.getLocation();
-        cc.log(pos);
+        event.getCurrentTarget().myShip.jump();
         return true;
     },
 });
&lt;/pre&gt;
これで実行すると、右上に動き続けている自機がちょっと大きめに上に動くようになった。&lt;br /&gt;
&lt;br /&gt;
期待していた流れが完成したので、あとは自機の動きの振る舞いを変えていく。&lt;br /&gt;
&lt;br /&gt;
まずは自機の初期位置を左中央に変更する。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index dddb3d2..69e9ee8 100644
--- a/src/app.js
+++ b/src/app.js
@@ -87,6 +87,10 @@ var HelloWorldScene = cc.Scene.extend({
         this.addChild(layer);
         this.myShip = new MyShipLayer();
         this.addChild(this.myShip);
+        this.myShip.attr({
+          x: 40,
+          y: cc.director.getWinSize().height / 2
+        });
         cc.eventManager.addListener({
             event: cc.EventListener.TOUCH_ONE_BY_ONE,
             swallowTouches: true,
&lt;/pre&gt;
次に動きを自由落下っぽく変更する。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 69e9ee8..0d34a6d 100644
--- a/src/app.js
+++ b/src/app.js
@@ -63,6 +63,7 @@ var HelloWorldLayer = cc.Layer.extend({

 var MyShipLayer = cc.Layer.extend({
     sprite:null,
+    vy: 0,
     ctor:function () {
         this._super();
         this.sprite = cc.Sprite.create(res.nc_png);
@@ -71,8 +72,8 @@ var MyShipLayer = cc.Layer.extend({
         return true;
     },
     update:function(dt) {
-        this.x += 1;
-        this.y += 1;
+        this.y += this.vy;
+        this.vy -= 0.09;
     },
     jump:function() {
       this.y += 10;
&lt;/pre&gt;
&lt;code&gt;y&lt;/code&gt; を加算すると上方向に移動することになるので、値が下がっていけば落下の動きが表現できる。&lt;br /&gt;
&lt;br /&gt;
このコードでは &lt;code&gt;vy&lt;/code&gt; が &lt;code&gt;0.09&lt;/code&gt; ずつ値が小さくなっていくので、結果的に落ちれば落ちるほど落下速度が上がっていく。&lt;code&gt;0.09&lt;/code&gt; という値は適当。&lt;br /&gt;
&lt;br /&gt;
あとはジャンプと、そのついでにゲーム開始直後にいきなり落ち始める代わりに最初だけはちょっとジャンプっぽく見せるように変更する。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 0d34a6d..a7817fd 100644
--- a/src/app.js
+++ b/src/app.js
@@ -63,7 +63,7 @@ var HelloWorldLayer = cc.Layer.extend({

 var MyShipLayer = cc.Layer.extend({
     sprite:null,
-    vy: 0,
+    vy: 3,
     ctor:function () {
         this._super();
         this.sprite = cc.Sprite.create(res.nc_png);
@@ -76,7 +76,7 @@ var MyShipLayer = cc.Layer.extend({
         this.vy -= 0.09;
     },
     jump:function() {
-      this.y += 10;
+      this.vy = 3;
     }
 });
&lt;/pre&gt;
どちらも単純に &lt;code&gt;vy&lt;/code&gt; を &lt;code&gt;3&lt;/code&gt; にするだけ。&lt;code&gt;vy&lt;/code&gt; は毎フレーム &lt;code&gt;-0.09&lt;/code&gt; されていくので、少し経てばまたすぐ落下傾向の動きになる。&lt;br /&gt;
&lt;br /&gt;
まだ衝突周りの判定はできていないが、自機としては概ねそれっぽい動きが完成した。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;邪魔なブロックを作る&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
邪魔なブロックは、可変サイズの長方形プリミティブオブジェクト的な何かである。&lt;br /&gt;
ゲームの実行中に大きさを動的に決め、そのサイズでオブジェクトを作る必要がある。&lt;br /&gt;
&lt;br /&gt;
このようなオブジェクトは &lt;a href=&quot;http://www.cocos2d-x.org/reference/html5-js/V3.0rc0/symbols/src/_Users_panda_StudyWork_Cocos_cocos2d-js_frameworks_cocos2d-html5_cocos2d_shape-nodes_CCDrawNode.js.html&quot; target=&quot;_blank&quot;&gt;cc.DrawNode&lt;/a&gt; を使って実現できるようだ。&lt;br /&gt;
&lt;br /&gt;
まずは &lt;code&gt;BlockLayer&lt;/code&gt; を作って &lt;code&gt;HelloWorldScene&lt;/code&gt; に追加してみることにする。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index a7817fd..8059657 100644
--- a/src/app.js
+++ b/src/app.js
@@ -80,6 +80,16 @@ var MyShipLayer = cc.Layer.extend({
     }
 });

+var BlockLayer = cc.Layer.extend({
+    d:null,
+    ctor:function () {
+        this._super();
+        this.d = cc.DrawNode.create();
+        this.addChild(this.d, 0);
+        return true;
+    }
+});
+
 var HelloWorldScene = cc.Scene.extend({
     myShip:null,
     onEnter:function () {
@@ -92,6 +102,8 @@ var HelloWorldScene = cc.Scene.extend({
           x: 40,
           y: cc.director.getWinSize().height / 2
         });
+        var block = new BlockLayer();
+        this.addChild(block);
         cc.eventManager.addListener({
             event: cc.EventListener.TOUCH_ONE_BY_ONE,
             swallowTouches: true,
&lt;/pre&gt;
最終的な登場個数は1個ではないが、取りあえず実験として1つだけ追加した。&lt;br /&gt;
&lt;br /&gt;
次に長方形を描くのだが、大きさや色は &lt;code&gt;BlockLayer&lt;/code&gt; の外から渡せるようにしたい。また、一度作られたもののサイズや色が変わる予定はないので、今回はコンストラクタで渡せるような形にする。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 8059657..5c6d1a0 100644
--- a/src/app.js
+++ b/src/app.js
@@ -82,9 +82,11 @@ var MyShipLayer = cc.Layer.extend({

 var BlockLayer = cc.Layer.extend({
     d:null,
-    ctor:function () {
+    ctor:function (width, height, fillcolor) {
         this._super();
         this.d = cc.DrawNode.create();
+        var hw = width * 0.5, hh = height * 0.5;
+        this.d.drawRect(cc.p(-hw, -hh), cc.p(hw, hh), fillcolor);
         this.addChild(this.d, 0);
         return true;
     }
@@ -102,7 +104,7 @@ var HelloWorldScene = cc.Scene.extend({
           x: 40,
           y: cc.director.getWinSize().height / 2
         });
-        var block = new BlockLayer();
+        var block = new BlockLayer(100, 100, cc.color(128,128,128,255));
         this.addChild(block);
         cc.eventManager.addListener({
             event: cc.EventListener.TOUCH_ONE_BY_ONE,
&lt;/pre&gt;
長方形の中央が &lt;code&gt;0, 0&lt;/code&gt; になるように描画するようにした。&lt;br /&gt;
&lt;br /&gt;
この長方形が描画されたノードを持つ &lt;code&gt;BlockLayer&lt;/code&gt; の位置も今のところ &lt;code&gt;0, 0&lt;/code&gt; なので、この状態で実行すると画面の左下に &lt;code&gt;50x50&lt;/code&gt; 程度の大きさの正方形が現れる。&lt;br /&gt;
&lt;br /&gt;
これを適当な大きさと色で複数生成して動くようにすればいいのだが、今の表示位置は丁度自機と衝突する場所でもあるので、動かす前に当たり判定に関する処理を作り、自機との衝突を検出できるようにしてみる。&lt;br /&gt;
&lt;br /&gt;
衝突の判定は取りあえずインスタンス化した &lt;code&gt;MyShipLayer&lt;/code&gt; と &lt;code&gt;BlockLayer&lt;/code&gt; が視野にある &lt;code&gt;HelloWorldScene&lt;/code&gt; で行ってみることにする。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 5c6d1a0..36f3a69 100644
--- a/src/app.js
+++ b/src/app.js
@@ -111,9 +111,12 @@ var HelloWorldScene = cc.Scene.extend({
             swallowTouches: true,
             onTouchBegan: this.onTap,
         }, this)
+        this.scheduleUpdateWithPriority(10);
     },
     onTap:function(touch, event) {
         event.getCurrentTarget().myShip.jump();
         return true;
     },
+    update:function(dt) {
+    },
 });
&lt;/pre&gt;
まずはまた &lt;code&gt;update&lt;/code&gt; メソッドを足し、毎フレーム呼ばれるようにする。&lt;br /&gt;
&lt;br /&gt;
ただし今回は自機の時とは違い &lt;code&gt;scheduleUpdate&lt;/code&gt; ではなく &lt;code&gt;cheduleUpdateWithPriority&lt;/code&gt; を使う。&lt;code&gt;scheduleUpdate&lt;/code&gt; は &lt;code&gt;cheduleUpdateWithPriority&lt;/code&gt; を &lt;code&gt;0&lt;/code&gt; で呼び出しているだけなのであまり大きな違いはないが、こうして優先順位を指定すると &lt;code&gt;update&lt;/code&gt; が呼び出される順番をコントロールできる。&lt;br /&gt;
&lt;br /&gt;
この点は地味ながらも非常に重要で、移動と衝突判定の順番がおかしいと表示される位置と実際に衝突する位置が噛み合わなくなってしまう。これは低速移動するオブジェクト同士ならあまり目立たないかもしれないが、高速なオブジェクトほど誤差が大きくなる。&lt;br /&gt;
&lt;br /&gt;
実際に衝突判定をするにあたり注意すべき点は他にもあって、ゲームにおける当たり判定は必ずしもスプライトの表示領域とイコールである必要はなくて、少しめり込むぐらいが適しているシーンが多い。&lt;br /&gt;
&lt;br /&gt;
なので、ここでは衝突判定用の領域は別に設け、そこを基準に判定していくことにする。&lt;br /&gt;
&lt;br /&gt;
まずはレイヤーに &lt;code&gt;setContentSize&lt;/code&gt; で当たり判定になるボックスのサイズを指定する。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 36f3a69..3a4628d 100644
--- a/src/app.js
+++ b/src/app.js
@@ -68,6 +68,9 @@ var MyShipLayer = cc.Layer.extend({
         this._super();
         this.sprite = cc.Sprite.create(res.nc_png);
         this.addChild(this.sprite, 0);
+        var bbox = cc.size(18, 12);
+        this.sprite.setPosition(bbox.width * 0.5, bbox.height * 0.5);
+        this.setContentSize(bbox);
         this.scheduleUpdate();
         return true;
     },
@@ -88,6 +91,9 @@ var BlockLayer = cc.Layer.extend({
         var hw = width * 0.5, hh = height * 0.5;
         this.d.drawRect(cc.p(-hw, -hh), cc.p(hw, hh), fillcolor);
         this.addChild(this.d, 0);
+        var bbox = cc.size(width, height);
+        this.d.setPosition(bbox.width * 0.5, bbox.height * 0.5);
+        this.setContentSize(bbox);
         return true;
     }
 });
&lt;/pre&gt;
&lt;code&gt;MyShipLayer&lt;/code&gt; では自機画像より少し小さいサイズ、&lt;code&gt;BlockLayer&lt;/code&gt; ではそのままのサイズとした。&lt;br /&gt;
&lt;br /&gt;
レイヤーは &lt;code&gt;setContentSize&lt;/code&gt; をしなかった場合デフォルトではスクリーンと同じサイズになっているのだが、具体的に指定するとそのサイズとして振る舞うようになるようだ。&lt;br /&gt;
&lt;br /&gt;
これで当たり判定に使えるボックスが出来上がったので、実際に &lt;code&gt;cc.rectIntersectsRect&lt;/code&gt; で判定を行ってみる。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 3a4628d..e8f4e8d 100644
--- a/src/app.js
+++ b/src/app.js
@@ -100,6 +100,7 @@ var BlockLayer = cc.Layer.extend({

 var HelloWorldScene = cc.Scene.extend({
     myShip:null,
+    block:null,
     onEnter:function () {
         this._super();
         var layer = new HelloWorldLayer();
@@ -110,8 +111,8 @@ var HelloWorldScene = cc.Scene.extend({
           x: 40,
           y: cc.director.getWinSize().height / 2
         });
-        var block = new BlockLayer(100, 100, cc.color(128,128,128,255));
-        this.addChild(block);
+        this.block = new BlockLayer(100, 100, cc.color(128,128,128,255));
+        this.addChild(this.block);
         cc.eventManager.addListener({
             event: cc.EventListener.TOUCH_ONE_BY_ONE,
             swallowTouches: true,
@@ -124,5 +125,8 @@ var HelloWorldScene = cc.Scene.extend({
         return true;
     },
     update:function(dt) {
+      if (cc.rectIntersectsRect(this.myShip.getBoundingBox(), this.block.getBoundingBox())) {
+        cc.log(&#39;hit&#39;);
+      }
     },
 });
&lt;/pre&gt;
当たっている間だけ、毎フレーム &lt;code&gt;hit&lt;/code&gt; というログが出続けるようになった。&lt;br /&gt;
&lt;br /&gt;
当たった時にどう振る舞うのか、という実際のところについてはひとまず後回しにして、先にブロックがたくさん出てくる処理を書いていく。&lt;br /&gt;
&lt;br /&gt;
この動きに関してはユーザーの入力に関わらず同じ振る舞いをさせられるので、その側面から見た場合 &lt;code&gt;update&lt;/code&gt; で自分で位置を管理する必要はないが、フレーム落ちした時などに自機は落ちた分だけ遅くなるのにブロックは同じ速度で進行してくると不公平な感じがするので、その辺りの理由からこのブロックも &lt;code&gt;update&lt;/code&gt; で位置を更新していく形にしてみる。&lt;br /&gt;
&lt;br /&gt;
まずはブロックを100個に増やしてみる。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index e8f4e8d..5c133bd 100644
--- a/src/app.js
+++ b/src/app.js
@@ -100,7 +100,7 @@ var BlockLayer = cc.Layer.extend({

 var HelloWorldScene = cc.Scene.extend({
     myShip:null,
-    block:null,
+    blocks:[],
     onEnter:function () {
         this._super();
         var layer = new HelloWorldLayer();
@@ -111,8 +111,22 @@ var HelloWorldScene = cc.Scene.extend({
           x: 40,
           y: cc.director.getWinSize().height / 2
         });
-        this.block = new BlockLayer(100, 100, cc.color(128,128,128,255));
-        this.addChild(this.block);
+
+        for (var i = 0, bl; i &amp;lt; 100; ++i) {
+          bl = new BlockLayer(
+            40+Math.random()*60,
+            40+Math.random()*60,
+            cc.color(
+              128+Math.random()*64,
+              128+Math.random()*64,
+              128+Math.random()*64,
+              255
+            )
+          );
+          this.addChild(bl);
+          this.blocks.push(bl);
+        }
+
         cc.eventManager.addListener({
             event: cc.EventListener.TOUCH_ONE_BY_ONE,
             swallowTouches: true,
@@ -125,8 +139,11 @@ var HelloWorldScene = cc.Scene.extend({
         return true;
     },
     update:function(dt) {
-      if (cc.rectIntersectsRect(this.myShip.getBoundingBox(), this.block.getBoundingBox())) {
+      var myShipBB = this.myShip.getBoundingBox();
+      for(var i = this.blocks.length - 1; i &amp;gt;= 0; --i) {
+        if (cc.rectIntersectsRect(myShipBB, this.blocks[i].getBoundingBox())) {
           cc.log(&#39;hit&#39;);
         }
+      }
     },
 });
&lt;/pre&gt;
ランダムな大きさ、ランダムな色で100個のブロックを生成するようにした。まだ初期位置の指定や移動の処理が入っていないので、この段階では左下に大量のブロックが貯まるだけ。&lt;br /&gt;
&lt;br /&gt;
ここから更に、ブロックに移動機能をつけてみる。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 5c133bd..21c2666 100644
--- a/src/app.js
+++ b/src/app.js
@@ -85,6 +85,8 @@ var MyShipLayer = cc.Layer.extend({

 var BlockLayer = cc.Layer.extend({
     d:null,
+    vx: 0,
+    vy: 0,
     ctor:function (width, height, fillcolor) {
         this._super();
         this.d = cc.DrawNode.create();
@@ -94,7 +96,12 @@ var BlockLayer = cc.Layer.extend({
         var bbox = cc.size(width, height);
         this.d.setPosition(bbox.width * 0.5, bbox.height * 0.5);
         this.setContentSize(bbox);
+        this.scheduleUpdate();
         return true;
+    },
+    update:function(dt) {
+        this.x += this.vx;
+        this.y += this.vy;
     }
 });
&lt;/pre&gt;
これは既に説明する必要がないほど既に通ったパターンなので省く。&lt;br /&gt;
&lt;br /&gt;
移動機能が付いたが &lt;code&gt;vx&lt;/code&gt; と &lt;code&gt;vy&lt;/code&gt; は &lt;code&gt;0&lt;/code&gt; のままなので、初期化時に指定するようにする。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 21c2666..eb3d3ea 100644
--- a/src/app.js
+++ b/src/app.js
@@ -110,13 +110,14 @@ var HelloWorldScene = cc.Scene.extend({
     blocks:[],
     onEnter:function () {
         this._super();
+        var size = cc.director.getWinSize();
         var layer = new HelloWorldLayer();
         this.addChild(layer);
         this.myShip = new MyShipLayer();
         this.addChild(this.myShip);
         this.myShip.attr({
           x: 40,
-          y: cc.director.getWinSize().height / 2
+          y: size.height / 2
         });

         for (var i = 0, bl; i &amp;lt; 100; ++i) {
@@ -130,6 +131,12 @@ var HelloWorldScene = cc.Scene.extend({
               255
             )
           );
+          bl.attr({
+            x: size.width+Math.random()*size.width,
+            y: -bl.height+Math.random()*(size.height+bl.height),
+            vx: -1-Math.random()*3,
+            vy: -0.5+Math.random()
+          });
           this.addChild(bl);
           this.blocks.push(bl);
         }
&lt;/pre&gt;
&lt;code&gt;x&lt;/code&gt; 座標は同じ大きさの画面が横に2画面続いてるイメージで、見えてないエリアにだけランダムで配置している。&lt;code&gt;y&lt;/code&gt; 座標は画面の範囲内のどこか。&lt;code&gt;vx&lt;/code&gt; と &lt;code&gt;vy&lt;/code&gt; は適当な値を指定した。&lt;br /&gt;
&lt;br /&gt;
これで実行すると、わりと濃い密度のブロックの大群が押し寄せてきて、大体避けられそうにない感じになる。&lt;br /&gt;
&lt;br /&gt;
ただ、このままでは通り過ぎた後はもう二度と再登場することはないのであまりゲームにならない。&lt;br /&gt;
&lt;br /&gt;
なので、画面の左端を抜けたブロックは右端のランダムな上下位置へワープさせて再登場させることにする。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index eb3d3ea..2119bc1 100644
--- a/src/app.js
+++ b/src/app.js
@@ -154,10 +154,18 @@ var HelloWorldScene = cc.Scene.extend({
     },
     update:function(dt) {
       var myShipBB = this.myShip.getBoundingBox();
-      for(var i = this.blocks.length - 1; i &amp;gt;= 0; --i) {
-        if (cc.rectIntersectsRect(myShipBB, this.blocks[i].getBoundingBox())) {
+      var size = cc.director.getWinSize();
+      for(var i = this.blocks.length - 1, bl; i &amp;gt;= 0; --i) {
+        bl = this.blocks[i];
+        if (cc.rectIntersectsRect(myShipBB, bl.getBoundingBox())) {
           cc.log(&#39;hit&#39;);
         }
+        if (bl.x+bl.width &amp;lt; 0) {
+          bl.attr({
+            x: size.width,
+            y: -bl.height+Math.random()*(size.height+bl.height)
+          });
+        }
       }
     },
 });
&lt;/pre&gt;
どこで実装するのか若干迷ったが、ブロックレイヤーが表示領域のサイズを見て移動する処理を行うのは何となく気持ち悪い気がしたのでひとまずシーン側の update で行うようにした。&lt;br /&gt;
&lt;br /&gt;
これで地獄絵図みたいな状態が延々と続くようになった。少しブロック数を減らしたりして、ゲームバランスをマシにしてみる。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index 2119bc1..a00e2c4 100644
--- a/src/app.js
+++ b/src/app.js
@@ -76,7 +76,7 @@ var MyShipLayer = cc.Layer.extend({
     },
     update:function(dt) {
         this.y += this.vy;
-        this.vy -= 0.09;
+        this.vy -= 0.1;
     },
     jump:function() {
       this.vy = 3;
@@ -120,9 +120,9 @@ var HelloWorldScene = cc.Scene.extend({
           y: size.height / 2
         });

-        for (var i = 0, bl; i &amp;lt; 100; ++i) {
+        for (var i = 0, bl; i &amp;lt; 16; ++i) {
           bl = new BlockLayer(
-            40+Math.random()*60,
+            30+Math.random()*60,
             40+Math.random()*60,
             cc.color(
               128+Math.random()*64,
@@ -134,8 +134,8 @@ var HelloWorldScene = cc.Scene.extend({
           bl.attr({
             x: size.width+Math.random()*size.width,
             y: -bl.height+Math.random()*(size.height+bl.height),
-            vx: -1-Math.random()*3,
-            vy: -0.5+Math.random()
+            vx: -0.5-Math.random()*3,
+            vy: -0.25+Math.random()*0.5
           });
           this.addChild(bl);
           this.blocks.push(bl);
&lt;/pre&gt;
少しマシになってきた。&lt;br /&gt;
&lt;br /&gt;
ダメージ感がないので、ブロックに接触した時にログにメッセージを出力しているのをやめて、同じシーンをもう一度読み込むようにしてゲームオーバー画面の代わりとしてみる。&lt;br /&gt;
&lt;br /&gt;
その作業をしている時にひとつバグを見つけたので修正した。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index a00e2c4..bbed316 100644
--- a/src/app.js
+++ b/src/app.js
@@ -107,7 +107,7 @@ var BlockLayer = cc.Layer.extend({

 var HelloWorldScene = cc.Scene.extend({
     myShip:null,
-    blocks:[],
+    blocks:null,
     onEnter:function () {
         this._super();
         var size = cc.director.getWinSize();
@@ -120,6 +120,7 @@ var HelloWorldScene = cc.Scene.extend({
           y: size.height / 2
         });

+        this.blocks = [];
         for (var i = 0, bl; i &amp;lt; 16; ++i) {
           bl = new BlockLayer(
             30+Math.random()*60,
&lt;/pre&gt;
&lt;code&gt;onEnter&lt;/code&gt; のタイミングで &lt;code&gt;blocks&lt;/code&gt; を初期化せず、インスタンス化前に作った配列のインスタンスに &lt;code&gt;push&lt;/code&gt; してしまっていたので、全ての HelloWorldScene でこの配列が共有されてしまうバグがあった。&lt;br /&gt;
&lt;br /&gt;
シーンを切り替えても挙動がおかしいのが原因でこのバグに気付いた。&lt;br /&gt;
&lt;br /&gt;
で、実際のシーン切り替えのコード。&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-diff&quot;&gt;diff --git a/src/app.js b/src/app.js
index bbed316..28cd4bc 100644
--- a/src/app.js
+++ b/src/app.js
@@ -159,7 +159,10 @@ var HelloWorldScene = cc.Scene.extend({
       for(var i = this.blocks.length - 1, bl; i &amp;gt;= 0; --i) {
         bl = this.blocks[i];
         if (cc.rectIntersectsRect(myShipBB, bl.getBoundingBox())) {
-          cc.log(&#39;hit&#39;);
+          this.unscheduleUpdate();
+          cc.eventManager.removeListener(this);
+          cc.director.runScene(cc.TransitionFade.create(0.5, new HelloWorldScene()));
+          return;
         }
         if (bl.x+bl.width &amp;lt; 0) {
           bl.attr({
&lt;/pre&gt;
まず &lt;code&gt;unscheduleUpdate&lt;/code&gt; でこれ以上 &lt;code&gt;update&lt;/code&gt; が呼ばれないようにする。これをやらないと自機がブロックに衝突後そのままめり込んでいき何度も死亡判定されてしまう。&lt;br /&gt;
&lt;br /&gt;
次に &lt;code&gt;cc.eventManager.removeListener&lt;/code&gt; でイベントリスナーを削除する。これをやらないと使わなくなっているのにずっと Listen したままになってしまう。&lt;br /&gt;
&lt;br /&gt;
シーン切り替えのコードは前回試したコードと全く同じで、これ以上ループを処理必要がないのが明白なので最後はそのまま &lt;code&gt;return&lt;/code&gt; で抜けている。&lt;br /&gt;
&lt;br /&gt;
この辺はもっと本格的にゲームオーバー画面とか、死亡時のエフェクトとか、何らかのリアクションを作り込む時に見なおしたほうが良さそうだ。&lt;br /&gt;
&lt;br /&gt;
記事の最初の方でもリンクを置いているが、&lt;a href=&quot;http://test.oov.ch/junk/20140717-cocos2d-js/&quot; target=&quot;_blank&quot;&gt;ここまでの成果物はこちら&lt;/a&gt;。&lt;br /&gt;
クリックやタップするとジャンプでき、ブロックを避けることができる。ブロックに衝突した場合は強制的にリセットされて最初からやり直しになる。&lt;br /&gt;
&lt;br /&gt;
まだ最初から入っている HelloWorld の動きを一切消さずに書いてるので背景がカオスに華やか。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/6224487986054072680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/6224487986054072680'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2014/07/cocos2d-js-2.html' title='Cocos2d-JS 触り始めた2'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-5278644128837140091</id><published>2014-07-15T04:47:00.000+09:00</published><updated>2014-07-24T19:50:39.471+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Cocos2d-JS"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><title type='text'>Cocos2d-JS 触り始めた</title><content type='html'>以前 &lt;a href=&quot;http://googleplus-1st-aniv-iwatte-mirror.oov.ch/plus-hairpin/&quot; target=&quot;_blank&quot;&gt;NME でゲームを作ってみたりしたこと&lt;/a&gt;などもあったが、巷で話題なのは Unity とか Cocos2dx 辺りで、この辺は有名だしもっと色々便利なんだろうなあと思っていた。最近会社を辞めて少し時間ができたので、そんなに情報が多くなくて面白そうな &lt;a href=&quot;http://www.cocos2d-x.org/products#cocos2dx-js&quot; target=&quot;_blank&quot;&gt;Cocos2d-JS&lt;/a&gt;&amp;nbsp;を触ってみようかと思ったので、合わせて日誌的にブログを書いてみることにする。&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
何も知らない状態から探り探りやっているので、書いている内容が正確ではない可能性があります。&lt;br /&gt;
俺っていつもそう。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;インストール&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
ひとまず「ブラウザで動くものが出来上がればいいかな」と思っていたものの、Android デバイスを持っているのでせっかくだし目標としては Web 版と Android 版を書き出す方向で進めてみたい。&lt;br /&gt;
&lt;br /&gt;
作業環境は Windows8.1 64bit 版。まず Cocos2d-JS v3.0rc0 と、Android SDK、Android NDK、Apache Ant、Python 辺りを導入。NME の時はこの辺はインストーラがほとんど自動で全てをやってくれたので、それに比べるとわりと面倒くさい。&lt;br /&gt;
&lt;br /&gt;
それぞれをインストールしたら&lt;a href=&quot;http://staffblog.yumemi.jp/cocos2d-js%E3%82%92%E5%8B%95%E3%81%8B%E3%81%99%E3%81%BE%E3%81%A7%E3%81%AE%E6%89%8B%E9%A0%86/&quot; target=&quot;_blank&quot;&gt;こちらのページを参考&lt;/a&gt;に setup.py を実行し、環境変数の設定について聞かれるので適宜設定し、手順の通り MyGame を作って &lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-bash&quot;&gt;cocos run -p web&lt;/pre&gt;
してみると確かにブラウザが開き簡単なサンプルが動いているのを確認できた。&lt;br /&gt;
ブラウザを閉じ今度は Android 端末を USB デバッグを有効にした状態で接続し、&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-bash&quot;&gt;cocos run -p android&lt;/pre&gt;
すると C++ ソースファイルのコンパイルが始まり、そのまま3年の月日が流れた。それは嘘だが、しばらく待つと端末にアプリが転送され難なく起動した。これ以降はコンパイル済みのライブラリをリンクするだけで済むようになるので、実行までの所要時間もだいぶ減るようだ。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;生成されたコードを眺めてみる&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
生成されたファイル群を見てみると、JavaScript ソースファイルは MyGame/main.js&amp;nbsp;MyGame/src/resource.js MyGame/src/app.js の3つのファイルがあるようだ。

&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;MyGame/main.js&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-js&quot;&gt;cc.game.onStart = function(){
    cc.view.setDesignResolutionSize(800, 450, cc.ResolutionPolicy.SHOW_ALL);
 cc.view.resizeWithBrowserSize(true);
    //load resources
    cc.LoaderScene.preload(g_resources, function () {
        cc.director.runScene(new HelloWorldScene());
    }, this);
};
cc.game.run();
&lt;/pre&gt;
このファイルはゲームメイン処理実行において一番最初に呼ばれるであろう &lt;code&gt;cc.game&lt;/code&gt; オブジェクトが持つ &lt;code&gt;onStart&lt;/code&gt; イベントのイベントハンドラを設定し、ゲームのメイン処理が開始されるよう &lt;code&gt;cc.game.run&lt;/code&gt; を呼んでいるように見える。&lt;br /&gt;
設定したイベントハンドラ内では、画面の大きさを設定し、ブラウザの大きさに合わせて表示領域を拡大縮小するように設定し、更に &lt;code&gt;g_resources&lt;/code&gt; を読み込み、恐らくその読み込み完了時に呼ばれるコールバック関数で &lt;code&gt;HelloWorldScene&lt;/code&gt; シーンを作り、そのシーンの実行を開始していると思われる。&lt;br /&gt;
&lt;br /&gt;
それを検証するため試しに &lt;code&gt;cc.view.setDesignResolutionSize&lt;/code&gt; に渡している数字を逆にすると縦長の画面になったり、&lt;code&gt;cc.view.resizeWithBrowserSize&lt;/code&gt; を &lt;code&gt;false&lt;/code&gt; で呼ぶとブラウザの表示領域が狭くても縮小表示されないことを確認できた。&lt;br /&gt;
ただし縦長の画面にするだけでは Android のアプリケーションとしては横向き（landscape）から縦向き（portrait）に切り替わるわけではないようだ。また、画面周りの設定は厳密にはここだけに存在するわけではないようなのでもう少し掘り下げて探してみて、一応 MyGame/index.html の &lt;code&gt;canvas&lt;/code&gt; タグの幅と高さと、MyGame/frameworks/runtime-src/proj.android/AndroidManifest.xml 内の &lt;code&gt;android:screenOrientation=&quot;landscape&quot;&lt;/code&gt; を &lt;code&gt;android:screenOrientation=&quot;portrait&quot;&lt;/code&gt; に変更してみたところ、Android アプリ上で縦画面として動くようになった。&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;canvas&lt;/code&gt; タグの大きさはブラウザで &lt;code&gt;cc.game.onStart&lt;/code&gt; が呼ばれる前のタイミングでどの程度の大きさで出すのかという話だと思う（ここを変更しなくても JavaScript ファイルの変更だけで縦長にはなっていたから）のであまり重要ではないと思う。&lt;br /&gt;
ただこの HTML ファイル内にも &lt;code&gt;&amp;lt;meta name=&quot;screen-orientation&quot; content=&quot;portrait&quot;/&amp;gt;&lt;/code&gt; の記述があったので、この辺も必要に応じて変えたほうがいいのかも知れない（最初から portrait だったけど…）。

&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;MyGame/src/resource.js&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-js&quot;&gt;var res = {
    HelloWorld_png : &quot;res/HelloWorld.png&quot;,
    CloseNormal_png : &quot;res/CloseNormal.png&quot;,
    CloseSelected_png : &quot;res/CloseSelected.png&quot;
};

var g_resources = [
    //image
    res.HelloWorld_png,
    res.CloseNormal_png,
    res.CloseSelected_png

    //plist

    //fnt

    //tmx

    //bgm

    //effect
];
&lt;/pre&gt;
これは MyGame/main.js で &lt;code&gt;cc.LoaderScene.preload&lt;/code&gt; に渡していた &lt;code&gt;g_resources&lt;/code&gt; を定義しているファイルのようだ。&lt;br /&gt;
リソースの追加時はここに追記したりすれば良さそうだが、表示頻度の少ないシーン用のリソース（エンディング周りとか？）などはここに定義せずシーンを読み込む際に個別に読み込んだりした方が起動時のローディング時間を減らせるのかも知れない。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;MyGame/src/app.js&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-js&quot;&gt;var HelloWorldLayer = cc.Layer.extend({
    sprite:null,
    ctor:function () {
        //////////////////////////////
        // 1. super init first
        this._super();

        /////////////////////////////
        // 2. add a menu item with &quot;X&quot; image, which is clicked to quit the program
        //    you may modify it.
        // ask director the window size
        var size = cc.director.getWinSize();

        // add a &quot;close&quot; icon to exit the progress. it&#39;s an autorelease object
        var closeItem = cc.MenuItemImage.create(
            res.CloseNormal_png,
            res.CloseSelected_png,
            function () {
                cc.log(&quot;Menu is clicked!&quot;);
            }, this);
        closeItem.attr({
            x: size.width - 20,
            y: 20,
            anchorX: 0.5,
            anchorY: 0.5
        });

        var menu = cc.Menu.create(closeItem);
        menu.x = 0;
        menu.y = 0;
        this.addChild(menu, 1);

        /////////////////////////////
        // 3. add your codes below...
        // add a label shows &quot;Hello World&quot;
        // create and initialize a label
        var helloLabel = cc.LabelTTF.create(&quot;Hello World&quot;, &quot;Arial&quot;, 38);
        // position the label on the center of the screen
        helloLabel.x = size.width / 2;
        helloLabel.y = 0;
        // add the label as a child to this layer
        this.addChild(helloLabel, 5);

        // add &quot;HelloWorld&quot; splash screen&quot;
        this.sprite = cc.Sprite.create(res.HelloWorld_png);
        this.sprite.attr({
            x: size.width / 2,
            y: size.height / 2,
            scale: 0.5,
            rotation: 180
        });
        this.addChild(this.sprite, 0);

        var rotateToA = cc.RotateTo.create(2, 0);
        var scaleToA = cc.ScaleTo.create(2, 1, 1);

        this.sprite.runAction(cc.Sequence.create(rotateToA, scaleToA));
        helloLabel.runAction(cc.Spawn.create(cc.MoveBy.create(2.5, cc.p(0, size.height - 40)),cc.TintTo.create(2.5,255,125,0)));
        return true;
    }
});

var HelloWorldScene = cc.Scene.extend({
    onEnter:function () {
        this._super();
        var layer = new HelloWorldLayer();
        this.addChild(layer);
    }
});
&lt;/pre&gt;
これはゲームのメイン処理を行っているらしいファイルで、少し長い。
全体としては &lt;code&gt;&lt;a href=&quot;http://www.cocos2d-x.org/reference/html5-js/V3.0rc0/symbols/src/_Users_panda_StudyWork_Cocos_cocos2d-js_frameworks_cocos2d-html5_cocos2d_core_layers_CCLayer.js.html&quot;&gt;cc.Layer&lt;/a&gt;&lt;/code&gt; を継承した &lt;code&gt;HelloWorldLayer&lt;/code&gt; と &lt;code&gt;&lt;a href=&quot;http://www.cocos2d-x.org/reference/html5-js/V3.0rc0/symbols/src/_Users_panda_StudyWork_Cocos_cocos2d-js_frameworks_cocos2d-html5_cocos2d_core_scenes_CCScene.js.html&quot;&gt;cc.Scene&lt;/a&gt;&lt;/code&gt; を継承した &lt;code&gt;HelloWorldScene&lt;/code&gt; を定義している。少し見てみた限りでは &lt;code&gt;cc.Layer&lt;/code&gt; も &lt;code&gt;cc.Scene&lt;/code&gt; も &lt;code&gt;&lt;a href=&quot;http://www.cocos2d-x.org/reference/html5-js/V3.0rc0/symbols/src/_Users_panda_StudyWork_Cocos_cocos2d-js_frameworks_cocos2d-html5_cocos2d_core_base-nodes_CCNode.js.html&quot;&gt;cc.Node&lt;/a&gt;&lt;/code&gt; を継承しているようだ。&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;&lt;/code&gt;
&lt;code&gt;HelloWorldScene&lt;/code&gt; では &lt;code&gt;onEnter&lt;/code&gt; イベントの発生時に &lt;code&gt;HelloWorldLayer&lt;/code&gt; を作成し、自身の子として追加している。MyGame/main.js では &lt;code&gt;HelloWorldScene&lt;/code&gt; を作成しているコードがあったので、実行順序としては&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;cc.game&lt;/code&gt; の &lt;code&gt;onStart&lt;/code&gt; イベントが発生&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HelloWorldScene&lt;/code&gt;&amp;nbsp;のインスタンスを作成&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cc.director.runScene&lt;/code&gt;&amp;nbsp;に作成したインスタンスを渡してシーン実行開始&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HelloWorldScene&lt;/code&gt; の &lt;code&gt;onEnter&lt;/code&gt; イベントが発生&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HelloWorldLayer&lt;/code&gt;&amp;nbsp;のインスタンスを作成&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HelloWorldLayer&lt;/code&gt; のコンストラクタで定義したアニメーションが順次実行されていく&lt;/li&gt;
&lt;/ol&gt;
という感じになっていると思われる。&lt;br /&gt;
この辺、行番号付いてた頃の BASIC 辺りとか、HSP とか、DX ライブラリとかのようにメインループを自分で回すのではなく、知らんところで勝手にメインループ的なものは動いているので何かが起きた時に対応するコードだけを書く感じで進めていくようなイメージが基本になるので、こういうイベントドリブン型に慣れる必要があるように思う（まあブラウザで JavaScript を使ったことがあるならば自ずと触ることになったとは思うけど）。&lt;br /&gt;
なので &lt;code&gt;HelloWorldLayer&lt;/code&gt; のコンストラクタで定義したアニメーションは自分で実行するんじゃなく、あくまでアニメーションの実行計画を渡しておくと、それを元にうまい具合に実行してくれる、というイメージ。&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;HelloWorldLayer&lt;/code&gt; では右下に表示されている電源ボタン、中央で回転する画像、下から上に移動していくテキストをそれぞれ作成し、自身の子として追加している。また、アニメーションの実行計画を作成し、それぞれのオブジェクトの runAction で実行開始しているようだ。&lt;br /&gt;
スプライトに対しては &lt;code&gt;cc.Sequence.create&lt;/code&gt; で回転と拡大のアニメーションを連続実行するようにして登録し、テキストに対しては &lt;code&gt;cc.Spawn.create&lt;/code&gt; でテキスト移動と色変化のアニメーションを並列実行しているようだ。&lt;br /&gt;
&lt;br /&gt;
電源ボタンである &lt;code&gt;closeItem&lt;/code&gt; を作っているところのコードでは関数を渡しているが、内容から察するにボタンがタップされた時にログメッセージを出力する処理を行っている。&lt;br /&gt;
確かにブラウザのデバッグコンソールを表示したり、Android ではコマンドプロンプトで &lt;code&gt;adb logcat&lt;/code&gt; をした状態で端末でタップするとログメッセージが表示されるのを確認できた。&lt;br /&gt;
ちょっとした変数の内容確認などはここでも行えるようだ。&lt;br /&gt;
&lt;br /&gt;
自動で生成されたプロジェクトの全体像が少し見えたような気がする。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;ちょっと書き換えてみる&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
ひとまず cc.Scene や cc.Layer などのクラスがあることがわかった。シーンというのは恐らくタイトル画面とかプレイヤーセレクト画面などの大きな単位で、レイヤーはその画面内に登場するパーツを表現している気がする。&lt;br /&gt;
&lt;br /&gt;
例えばアクションゲームを作ったとして、起動した瞬間にいきなり動き出すのではなくタイトル画面でゲームスタートボタン的なものを押してからゲームを始めるような流れを作るなら、タイトル画面のシーンとゲーム本編のシーンの2つを作り、それらを切り替えることで実現できそうに思える。&lt;br /&gt;
&lt;br /&gt;
いきなりシーンを2つ作るのはちょっと面倒臭いので、既にある &lt;code&gt;HelloWorldScene&lt;/code&gt; から &lt;code&gt;HelloWorldScene&lt;/code&gt; に切り替えるのをやってみることにする。また、 closeItem というボタンがあるので、役割は違うものの、これをそのまま流用してログメッセージを吐く代わりにシーン切り替えを行わせてみることにする。&lt;br /&gt;
&lt;br /&gt;
シーンを実行させる処理は MyGame/main.js で &lt;code&gt;cc.director.runScene(new HelloWorldScene());&lt;/code&gt; というコードを見かけているので、これをこのまま持ち込めば同じシーンに画面切り替えすると思われる。
&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-js&quot;&gt;        var closeItem = cc.MenuItemImage.create(
            res.CloseNormal_png,
            res.CloseSelected_png,
            function () {
                cc.log(&quot;Menu is clicked!&quot;);
                cc.director.runScene(new HelloWorldScene());
            }, this);
&lt;/pre&gt;
要するに、こんな感じでそのまま持ち込んでみる。&lt;br /&gt;
&lt;br /&gt;
これを試しに実行してみると、確かにボタンを押されたタイミングでまた回転アニメーションなどが最初から始まり、シーンが1から実行されなおしている感じになった。&lt;br /&gt;
&lt;br /&gt;
ただし、このままだと本当に単純に切り替わるだけで物足りない。完成度の高いゲームはほとんど全ての動作で細かい動きなどを入れていたりするし、画面切り替えにはやっぱりエフェクトが欲しい。&lt;br /&gt;
&lt;br /&gt;
適当に見て回っていると&lt;a href=&quot;https://github.com/yuye-liu/CocostudioIronCity/blob/master/cocos2d-html5_i/IronCity/src/scene/MainMenuScene.js#L53&quot;&gt;この辺&lt;/a&gt;で &lt;code&gt;cc.TransitionFade&lt;/code&gt; というそれっぽいものを見つけた。このコードは Cocos2d-JS の v2.x 系のものなので少し実装が違うようなのだが、&lt;a href=&quot;http://www.cocos2d-x.org/reference/html5-js/V3.0rc0/symbols/src/_Users_panda_StudyWork_Cocos_cocos2d-js_frameworks_cocos2d-html5_cocos2d_transitions_CCTransition.js.html&quot;&gt;v3.0 系でも存在している&lt;/a&gt;ようなので、これを使ってみることにする。&lt;br /&gt;
&lt;code&gt;cc.TransitionFade&lt;/code&gt; は &lt;code&gt;cc.TransitionScene&lt;/code&gt; を継承していて、それは &lt;code&gt;cc.Scene&lt;/code&gt; を継承しているので、画面切り替え用のシーンをシーンとシーンの間に挟み込むことで切り替えエフェクトを実現するんだろうか。取りあえずシーンだということはまた&lt;code&gt;cc.director.runScene&lt;/code&gt; に渡せばいいということだと思った。&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;cc.TransitionScene&lt;/code&gt; のコンストラクタは第1引数に切り替え所要時間を秒で、第2引数に切り替え先シーンを指定すればいいようなので、少しコードを変えて以下のようにした。
&lt;br /&gt;
&lt;pre class=&quot;prettyprint lang-js&quot;&gt;        var closeItem = cc.MenuItemImage.create(
            res.CloseNormal_png,
            res.CloseSelected_png,
            function () {
                cc.log(&quot;Menu is clicked!&quot;);
                cc.director.runScene(cc.TransitionFade.create(0.5, new HelloWorldScene()));
            }, this);
&lt;/pre&gt;
これで、0.5秒でフェードアウト、フェードインしてシーンが切り替わるようになった。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;今後&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.cocos2d-x.org/docs/tutorial/framework/html5/parkour-game-with-javascript-v3.0/chapter1/en&quot;&gt;この辺&lt;/a&gt;にわりと丁寧なチュートリアルっぽいのがあるようなので、そっちも目を通してみたい。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/5278644128837140091'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/5278644128837140091'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2014/07/cocos2d-js.html' title='Cocos2d-JS 触り始めた'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-4633015228974058554</id><published>2014-05-01T18:35:00.000+09:00</published><updated>2018-05-22T04:06:33.270+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="golang"/><category scheme="http://www.blogger.com/atom/ns#" term="Go言語"/><category scheme="http://www.blogger.com/atom/ns#" term="プログラム"/><title type='text'>Amazon Route 53 用 Dynamic DNS 更新ツールを Go で作った話</title><content type='html'>なんか記事にするほどのことでもないんだけど。じゃあ記事のするほどのことって何だよ、俺にあるのかよ、って思ったので、深く考えずに書く。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://oov.ch/&quot; target=&quot;_blank&quot;&gt;oov.ch&lt;/a&gt;&amp;nbsp;で使うネームサーバーとして今までは &lt;a href=&quot;http://www.value-domain.com/&quot; target=&quot;_blank&quot;&gt;VALUE-DOMAIN&lt;/a&gt; のものを利用してて、特に不満もなかったものの &lt;a href=&quot;https://aws.amazon.com/jp/route53/?nc1=h_l2_cn&quot; target=&quot;_blank&quot;&gt;Route 53&lt;/a&gt; の使い方を把握しておいた方が今後のためにもなると思ったので、試しに移行してみることにした。&lt;br /&gt;
&lt;br /&gt;
レコード情報はそんなに量もないし手作業でチマチマ入力して、dig コマンドで適宜応答も確認しながらやったのでこれといったトラブルもなく比較的スムースに移行は完了した。っぽい。&lt;br /&gt;
&lt;br /&gt;
ただ、遊び用の自宅サーバーには Dynamic DNS 的な奴が必要で、これは編集画面でチマチマ触ってるだけじゃ無理そうなのでちょっと調べた。すると&amp;nbsp;&lt;a href=&quot;http://aws.amazon.com/code/Amazon-Route-53/9706686376855511&quot; target=&quot;_blank&quot;&gt;Amazon Route 53 Authentication Tool for Curl&lt;/a&gt;&amp;nbsp;と &lt;a href=&quot;https://www.improvisedscience.org/notes/Route_53_Dynamic_DNS&quot; target=&quot;_blank&quot;&gt; AWS Route 53 Dynamic DNS Updater Script&lt;/a&gt;&amp;nbsp;を使うと比較的簡単にできるという情報がゴロゴロ転がってきた。&lt;br /&gt;
&lt;br /&gt;
それらをそのまま使えば多分解決するものの、そんなに難しいことをするわけじゃないだろうし自作してみてもいいかな、と思ってGo言語で作ってみることにした。できた。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/oov/r53ddns&quot; target=&quot;_blank&quot;&gt;https://github.com/oov/r53ddns&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
AWS へのアクセスには&amp;nbsp;&lt;a href=&quot;https://godoc.org/github.com/crowdmob/goamz/aws&quot; target=&quot;_blank&quot;&gt;github.com/crowdmob/goamz/aws&lt;/a&gt;&amp;nbsp;を使った。&lt;br /&gt;
&lt;a href=&quot;https://godoc.org/github.com/crowdmob/goamz/route53&quot; target=&quot;_blank&quot;&gt;Route 53 周りも一応実装はあった&lt;/a&gt;ものの、ChangeResourceRecordSet でバッチ処理が出来なかったりとか若干イケてない気配がしたので、この辺に関しては自前で用意した。&lt;br /&gt;
&lt;br /&gt;
自分が触るサーバは間違いなく Go が使える状態になっているので go get すればすぐ使えて便利だし、あと、あれだ、あの、なんでもない。&lt;br /&gt;
&lt;br /&gt;
--ip オプションのデフォルト値は&amp;nbsp;&lt;a href=&quot;http://checkip.dyndns.org/&quot; target=&quot;_blank&quot;&gt;http://checkip.dyndns.org/&lt;/a&gt;&amp;nbsp;になってるけど、はるか昔に自分で自分のために用意した GAE 上で動く同じようなものが&lt;a href=&quot;https://my-global-ip.appspot.com/&quot; target=&quot;_blank&quot;&gt;ある&lt;/a&gt;ので、自分の環境ではこっちを使うようにした。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/4633015228974058554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/4633015228974058554'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2014/05/amazon-route-53-dynamic-dns-go.html' title='Amazon Route 53 用 Dynamic DNS 更新ツールを Go で作った話'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-7864262528611545747.post-7339302367385450647</id><published>2014-04-22T16:45:00.000+09:00</published><updated>2014-04-22T16:45:02.492+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="CSS"/><category scheme="http://www.blogger.com/atom/ns#" term="HTML"/><title type='text'>アルファチャンネル付き PNG の減色</title><content type='html'>&lt;a href=&quot;http://oov.ch/music/%E9%BB%84%E6%98%8F%E3%83%AD%E3%83%9E%E3%83%B3%E3%83%81%E3%82%AB/&quot; target=&quot;_blank&quot;&gt;最近公開した楽曲用ページ&lt;/a&gt;を作る際に、簡単なパララックス風の効果を作るためにアルファチャンネル情報を持った画像ファイルを複数扱う必要があるケースに出くわした。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://bl.oov.ch/2012/10/nme-webp.html&quot; target=&quot;_blank&quot;&gt;以前ゲームを作った時&lt;/a&gt;は画像のデコード処理自体を外部デコーダに頼らなくてもできる状況だったので画像ファイルを WebP 形式にして、PNG 形式で 3.60MB ぐらいあった画像が 207KB まで容量削減できた（※この時使った WebP 形式はロスレスではない）のだが、ブラウザで WebP 形式を使うのは時期尚早……というかまともに使える日は来るんだろうか。&lt;br /&gt;
JavaScript によるデコーダがあることも知ってはいるものの&lt;a href=&quot;https://web.archive.org/web/20121218151650/http://webpjs.appspot.com/&quot; target=&quot;_blank&quot;&gt;わりとどうでもいい理由でボイコットされても困る&lt;/a&gt;し、それは冗談としても、とにかく WebP 形式は今回の選択肢にはなかった。&lt;br /&gt;
&lt;br /&gt;
そうなると PNG の最適化の路線が残るわけで、まずは有名どころの &lt;a href=&quot;http://pnggauntlet.com/&quot; target=&quot;_blank&quot;&gt;PNGGauntlet&lt;/a&gt;&amp;nbsp;を試してみた。圧縮アルゴリズムの最適化だけではそう大きくは縮まないだろうとは思ってはいたものの、ほぼ容量が変わらなかったので、別のアプローチの併用も必要そうだと思った。&lt;br /&gt;
&lt;br /&gt;
とはいえここまで来ると減色ぐらいしか選択肢がない気がしてきた。&lt;br /&gt;
昔、今よりもインターネットが好きだった頃は OPTPiX webDesigner v2.x を買って使ったりもしていたこともあったものの、今となっては開発終了してしまっていて v2.x 系では少なくともアルファチャンネル付き PNG は扱えない。WebFree 版は制限厳しそうで面倒そうだし、Padie 系列のソフト類も現状は消息がよくわからんことになってるし、&lt;a href=&quot;http://www14.plala.or.jp/lptrans/colga/colgatop.html&quot; target=&quot;_blank&quot;&gt;COLGA&lt;/a&gt; も試してみたものの容易く視認できるぐらいに劣化してしまった。&lt;br /&gt;
&lt;br /&gt;
などと困っていたところで&amp;nbsp;&lt;a href=&quot;http://kaede-software.com/tools/pngcolorreducerpro.html&quot; target=&quot;_blank&quot;&gt;PNG Color Reducer Pro&lt;/a&gt; を見つけた。&lt;br /&gt;
試しに減色してみたら見た目がほとんど変化しないまま減色後の画像が生成された。&lt;br /&gt;
減色後のファイルサイズが表示されなかったのでどのぐらい縮んだのかわからず若干不安ではあったものの、楓 software の人は以前からアルファチャンネル対応動画形式の実装日記なんかも書いてて半透明を含む圧縮という分野での遭遇は初めてじゃないし、少なくとも出力結果から視認できるレベルの劣化は見当たらなかったのですぐ購入して実際にファイルを書き出してみた。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9UKop5T6Hnrhh9IkphUraw8W1q9H9jbUDMI7Yrg3yZ3xdhJIv4P-xIec6G2MXQSFIH7ZTiHm8ya1bro4SG4EbN92Wvi3BNp54FVRSOtZEIo7yb2l1UDtsXW31ZS-tWzDNWvCNjLnWh1M/s1600/before.png&quot; /&gt;
&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh19CF65Yh_Dxya3DzzeIcnTGXgzhkz897JedreTLw3rrCjLssMOC5VoV2mWzMLYgHJrGL7X0c0wHFMfezfuyLH_JQJFnBKTHly_sg-PWhDFYQH5Ap78QYjTQGC3QmLMq-X6d9uFC7_m_g/s1600/after.png&quot; /&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
左が元のファイル、右が減色後のファイル&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
全体で 10MB 近かったファイルが、3.12MB まで縮めることができた。この縮んだあとのファイルを改めて&amp;nbsp;PNGGauntlet にかけると最終的には 2.88MB 程度まで縮んだ。全てのファイルを WebP 形式に変換した場合は非可逆圧縮によってエッジが丸くなりつつも 1.78MB ぐらいにはなったので、PNG で 2.88MB は結構頑張ってる感じがする（根拠なし）。&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
思わぬところで良いツールに出会えてよかった。&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
欲を言えば自分の用途ではブラウザがある場所で使うことが多いので、NativeClient で動く Chrome アプリ版とかがあるとどこでも簡単に使えて嬉しいなあと思った。&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/7339302367385450647'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864262528611545747/posts/default/7339302367385450647'/><link rel='alternate' type='text/html' href='http://bl.oov.ch/2014/04/png.html' title='アルファチャンネル付き PNG の減色'/><author><name>oov</name><uri>http://www.blogger.com/profile/17770305811675025491</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9UKop5T6Hnrhh9IkphUraw8W1q9H9jbUDMI7Yrg3yZ3xdhJIv4P-xIec6G2MXQSFIH7ZTiHm8ya1bro4SG4EbN92Wvi3BNp54FVRSOtZEIo7yb2l1UDtsXW31ZS-tWzDNWvCNjLnWh1M/s72-c/before.png" height="72" width="72"/></entry></feed>