2018年7月18日水曜日

ジュラシックワールド FALLEN KINGDOM

7/13 から上映開始だったので観てきました。
概ね分かりやすいエンターテインメント作品で、シリーズに特別思い入れのない人でも軽薄に楽しめる作品だったと思います。

今作ではあの初代ジュラシックパークとジュラシックワールドのあったイスラヌブラが火山活動によって致命的なダメージを受け、そこから恐竜を助け出したはずが……というストーリです。
前作のラストでモササウルスに食べられてしまったインドミナス・レックスの骨を回収する怪しい団体の活動で映画はスタート。
前作、「雨のシーンがない」という(数少ない)不満に応えるかのように、これでもかと雨が降っています。

前作、マスラニ社の財務を担当していたクレアは今や恐竜を守る団体の理事でしたが活動は暗礁に乗り上げていました。
イアン・マルカムの証言で、国はイスラヌブラの放棄を決定したからです。
これに対してロックウッド財団の代理人ミルズから資金提供の申し出があり、クレアはオーウェンを連れてイスラヌブラに乗り込みます。前作のラストで自然に帰っていったラプトルのブルーを保護するためです。

前半のシーケンスは、まぁまぁよくできてました。
イスラヌブラがジュラシックパークの陰の主人公と言い切る僕のような人間からすれば、イスラヌブラの破壊は痛ましく、恐竜たちの第二の絶滅はとても悲しいものでした。

なので、観に行く価値はあります。
迫りくる炎と煙の中、立ち尽くすブラキオサウルスの姿は自分で観ないと意味がない。
これは観るべき映画です。
しかしながら、素晴らしい二時間を過ごせる映画かと期待すると、そこまでのものではありません。
個人的な感覚では、まあまあ面白いけど、大嫌いな映画です。

後半のクソっぷりでもって前半の素晴らしさが色あせるわけではないので、平均的な点数を付けることは好きではありませんが、「詰め込み過ぎた」「おかげで編集もクソ」というところは明らかに製作の落ち度ですんで、少し厳しめにしてもいいでしょう。

前作ジュラシックワールドを 100 とすると本作は 55 かいいとこ 60 です。今後字幕版を観たらちょっと上がるかもしれませんが、おそらくそれ以上にはなり得ません。
素晴らしい前半にも、落ち度は多く、話が頭に入ってこないくらいには目立ちました。
しかしながら、初代ジュラシックパークを 100 とした場合のロストワールドは 25 くらいなので、それから比べれば大躍進であります。

いいですか。ロストワールドよりはマシです。

本作は、ロストワールドの精神的続編、オマージュでありながら、ロストワールドを超えた。
それは大いに評価すべきポイントだと思っています。

素晴らしい点は

*ブルーがかわいい(設定がブレてる気がするけど)
*ラプトルたちが尊い(捏造気味であるけれど)
*ブラキオサウルスが泣ける
*ティギー(パキケファロサウルス)がいい味出している
*荒ぶるイスラヌブラが壮大
*モササウルスはやっぱり最高
*ある意味原作のラスト(の状況)に一番近い

とまぁ、結構あるんです。

ロストワールドの精神的続編と言い切る理由はいくつかあります。
イスラヌブラの外に恐竜が出ること、イアンマルカムが登場すること、(マルカムではないが)娘との関係性が描かれること、暗いシーンがとても多いこと、恐竜ハンターが活躍すること、コンプトゥソグナトゥス、パキケファロサウルスが登場することなどです。

で、ここからネタバレです。
まだみてない人は見ちゃだめですよ。

といっても本作、ストーリーライン上別に隠さなきゃいけないことはそんなないと思いますけどね。
くだらない話なんで。








ここからネタバレです。
本作は、あのイスラヌブラが噴火で崩壊するということで、まぁ、観る前からあまり高い期待はなかったのですけど、それにしても酷いなーと思うのでいくつか書かないと気持ちの整理がつかない。

まずオープニングが酷い。
荒れ狂う海なのにやったら綺麗な海底を、今時珍しいくらいわかりやすい CG の潜水艇が進んできます。
このシーン、こんな尺使う必要あったんですか。
それで地上。荒れ狂う海の迫力は良し。雨も良し。
地上のチームを狙う T-REX の影。まぁ、お約束ですね。
でもこのティラノサウルスは、音もなく一瞬で背後まで距離を詰めてきます。
お化けじゃないんだから。せっかくの巨大な恐竜なのに実在感皆無。
ヘリコプターで逃げるところも、 3D 映画ってことを全く無視した細かいカット割りで何だろうなって思いますよね。
それで仲間を食われたチームメンはまるでアホみたいなオーバーアクト。
初代ジュラシックパークのオープニングの現地作業員を見習ってください。
ガム食いながら、ラプトルの檻を見ているだけなのにあの緊張感。
湾から逃げるモササウルスだけが素晴らしかった。

前半のイスラヌブラのシーンは大体素晴らしいんですが、あちこちおかしなところがあります。
ザックたちの乗っていたジャイロスフィア以外は全部基地に戻っていたはずなのに、なんであの平原に一台だけあったのですか。
ザックたちのはインドミナスに壊されていたはずです。あんなところに、気密性を維持したジャイロがあるのはおかしいですよね。
ブルーを確保した現場にあったひっくり返った初代のランドクルーザーもちょっと微妙ですよね。
まぁ、細かいところに目を瞑れば、良かったです。
特に第二の絶滅に瀕した恐竜たち。炎に巻かれ、船を見送るブラキオサウルス。
あんなに大事にしていたラプトルを撃たれる絶望感。
輸血シーンも、ジュラシックパークらしさがありました。

しかし後半、舞台がロックウッド邸になると話は大きく変わります。

前作は T-REX をベースにしたインドミナスが登場しましたが、今作はラプトルをベースにしたインドラプトルが登場します。
あれ? でもインドミナスは T-REX がベースじゃなかったし、むしろラプトルに近かったはずでは?
今作のインドラプトルは、ブルー達が忘れてしまった狡猾さを持った殺戮マシーンです。
が、このインドラプトルがまた実に、良くない。

インドミナスは走って良し、撃たれてよし、食って良し、食われて良しの最高のパフォーマーでした。
前にも言いましたが、ただ居るだけで絵になる。
コミュ障で食事のマナーもなってないとか酷い言われようでしたが、アイツはヒース・レジャーのジョーカーに匹敵する最高のヴィランでした。

インドラプトルはだめです。
まずラプトルの癖に図体がでかい。シルエットは不気味でとてもいいが、活躍する場が狭い。
笑うシーンがありますが、殺戮マシーンの癖に、何のために表情筋発達させたんですか?
SF としても成立してませんよこんなの。
まぁ、映画館で観客にはウケていたので、エンタメ作品としたら良いのでしょうが、僕は許せません。
動きや狙いも、何がしたいのかさっぱりわからず、ただ製作サイドの都合であっち行ったりこっち行ったりしていて、ただ不気味な舞台装置としてのみ存在していましたね。
あんなに暴れたのに結局戦果は、ケンと地下オークションの関係者四名くらい。 2800 万ドルが聞いて呆れる。
インドミナスは、ただ食い散らかした痕跡を見せるだけで、サトラー教授のコスプレをしたクレアの SAN 値を 20 くらい削る迫力があったというのに。

本作はシリーズでは珍しく人体損壊描写がありましたが、どういうわけか例によって流血はなかったですね。
インドラプトルの鉤爪が刺さってクレアが微妙に怪我するシーンがありましたが、何ですかね、あれ。何か重要なシーンだったんですか、あれ。笑いそうになりました。

ロックウッド邸でのインドラプトルとの追いかけっこは、本来もっとスリリングになって然るべきシーンですが、まったく退屈でした。
前半にロックウッド邸のシーンが少なすぎます。特に外観で、尖塔がどこにあって中庭がすごく高いとか、博物ホールの天井がどうなっていて、メイジーの部屋がどこにあってエレベータとの位置関係がどうとか、それを知っていたら、このシーンはもっとスリリングになったろうに勿体ない。
例えば監督がフィンチャーだったら、絶対そういうとこ効果的に見せるじゃないですか。
本作はまるっきり場当たり的で散発的。
恐竜たちも、どっから出てきたとか、どこに入ってた?とか、そんな疑問ばっかり気になって内容に集中できない。
とにかく雑。

インドラプトルの最後も、意外性の全くない雑な死に方で逆に驚いた。
ここはティラノサウルスの代わりにブルーが活躍しますんで、良しとしたい。
シリーズにおける、ラプトルの攻撃にはある法則性があって、基本的に味方よりも人数の多い敵は襲わないということです(ロストワールドあたりで若干怪しいとこもあった気がしますが)。
だからブルーが果敢に戦ったことは意外ではないものの、飛び掛かる姿には胸に来るものがありましたね。
ロストワールドで回し蹴りを食らってノビていたあの間抜けな生物ではない!

そして物語の終盤、謎だったメイジーが活躍します。
唐突に漏洩した青酸ガス(説明なし。キルスイッチとして用意されたもの?)によって地下の恐竜たちが全滅するかどうかという手に汗握るシーンで、メイジーがある決断を下します。
理由は、彼女がクローンだったからです。

前作ではインドミナスの遺伝形成がサイエンスミステリであり、歯の数が合わないという一点突破によってティラノサウルスの加勢をとりつける決断につながるという太いストーリーラインがありました。
今作では、ロックウッドの孫、メイジーの出生の秘密がミステリでありましたが、 まぁ、ぶっちゃけ彼女は事故死した母親をオリジナルとした、クローン人間なのです。

で?

っていうところなのですが、それがここで彼女の判断に影響するわけです。
彼女は恐竜も同じクローンであると言います。

……で?
観客は誰も、遺伝子技術によって復活した恐竜をクローンだと思ってないでしょうよ。
クローンだとかどうでもよくて、ただの生き物と認識してるからラプトルの兄弟がかわいく、第二の絶滅があんなにも悲しかったのです。
我々は遺伝子操作されたインドミナスさえ受け入れました。クローンだとかどうでもよくないですか?
ねぇ脚本の人?
今そこを強調して何になります? メイジーにしても自分がクローンだって、ついさっき世間話のついでにポロっと漏らしたのを聞いただけなんですよ?
ここが、本作の最大にして最悪の致命的なミスです。

なんでこう雑なんでしょうか。思えば本作、パンフレットもなんか変です。
「ヘンリー・ウーを演じた B.D. ウォンのインタビューによれば、彼の中ではインドラプトルは未完成のままなんだ」ってスタッフの証言がありますが、ヘンリー・ウーは劇中で何度も「インドラプトルは未完成だ」って主張しています。
心中どう思ってるかなんて関係ないです。はっきりセリフになってるじゃないですか。
ちゃんと脚本読んで、内容理解して作ってます?

そして、今まで「マッドサイエンティストではない」、科学者としてシリーズに登場してきたヘンリー・ウーも、人間に殺されてしまいます。
最後にウーは唐突になんかのスイッチが入って、マッドサイエンティストの一面を覗かせます。
今まで実利を求めるリアリストとしてふるまってきたのに、最後にブレブレになって、特に意味なく人の手にかかって死ぬ。何でしょうね。せめて恐竜に食われろ。
脚本のやっつけさ加減がありありと伝わるワンシーンでした。
まぁ、もっとも本当に死んだかどうかなんてわかりゃしないですけどね。

そういえばインドミナスの骨を使った兵器転用計画はどうなった?というと、これはティラノサウルスに踏みつぶされて文字通り蹴散らされてしまいます。本当にオープニングのシーン、必要だった???

こうして悪党はみんな死んで、恐竜たちは野に放たれていきます。
サーファーの間から、波間に映るモササウルスの影は、今作屈指の素晴らしいシーン。
荒野を見下ろすブルーも大変感動的。
ロストワールドでも恐竜は本土に来ますが、最後はハモンドの厚意によって返されます。何と厚かましくて押しつけがましい最後でしょうか。
それに比べりゃずっとずっとマシな最後です。

原作ジュラシックパークの最後には、島から脱出した二頭のラプトルについて触れられます。
パークの恐竜たちは、必須アミノ酸のリジンを生成できないキルスイッチを組み込まれたせいで、人間が島を放棄したことで第二の絶滅を迎えるのですね。
ところが、二頭のラプトルは外部からリジンを自力で補給し、強く生きる道を見つけてゆくというストーリーでした。
第二の絶滅、それでも恐竜たちは生きる道を見つける。
ハモンドもヘンリーも死亡、マルカム死亡(実は生きていた)、傲慢な人間たちは全員死亡。(といっても原作のヘンリーは主任研究者じゃないし、犠牲者サイドに立って結構健闘したような気がするけど、映画では完全に傲慢サイドに振られていた)
これがジュラシックパーク原作の描いた姿です。
本作は、とんでもなく雑なエンターテインメントに振られた駄作ですが、少なくともこの重要なモチーフは完全に描き切った。
ハモンドの気まぐれでイスラソルナに移されたり、島に強制送還されたりしていない。
この点は、本作の良い点に数えてもよいと思うのです。


2017年12月7日木曜日

Raspberry Pi で自動演奏システムを作ってみた話

この記事は 2017 年雑スラ Advent Calendar 7 日目の記事です。

去年の話ですが、ギターのエフェクタとして BOSS SY-300 を買いました。
かなりでかい
これは本来ギターシンセというやつで、その中でもピックアップ(弦の振動を拾うコイル)を特殊なものにしなくても使えるもので、且つ(たぶん処理によるけど)デジタル信号処理に頼らないので FFT など周波数変換に起因するディレイも(ほとんど)ないというものです。
入力波形に対してアナログシンセっぽいパラメータを設定しいろんな音を鳴らせるらしいです。

詳しいことはこちらをどうぞ

そしてこれは素朴なマルチエフェクタとしても利用できます。
マルチとして見ると、エフェクタとパッチの内部の結線(微妙に嘘・大げさ・まぎらわしい書き方でした。詳細は追記)をスイッチひとつで劇的に換えられるベリー賢いマルチエフェクタであります。

複数のエフェクタをまとめて制御するのはもちろん、ループの有無、出力のチャンネル (Main/Sub) まで選択できるわけです。
ディストーションのループを切って後段の空間系を入れて……、と 2 アクションで済むとしても既存システムでやろうとしたら足元は意外と忙しいことになり、歌まで歌っているともう「本番で気が回らなかったら諦めよう」と諦観さえ抱くことになりますよ。
これを買ってループセレクタは完全に捨てました。
下の図は同じバンクの設定の入力と出力で、 OSC1 とスルーを Comp+Dist から Main に出していますが、これを EXP ペダル一つで OSC3 -> OD -> CHO -> REV -> Sub 出力へ切り替えることができます。

入力系:OSCがオシレータ(OSC1 のみが有効な状態)
出力系: OSC1とTHRU以外SUB出力

 ……ちょっとこいつで一席遊んでみようという気になったので、所属軽音部のライブに混ぜてもらって、珍しくソロで出演することにしたのですがやっぱりソロでは音が薄い。
残念ながら当方、テクニックで 20 分間を持たせるタイプのギターは弾けない。ずっとバッキングだけしていたい。そんな気持ち。
そんなこんなで、リズム隊をラズパイ君にサポートしてもらうことにしました。

ラズパイ is



言わずと知れた Raspberry Pi は Linux がうごく ARM ボードです。
Linux で動く ALSA を筆頭に、サウンドアプリケーションは大体動くはずです。
音自体は USB オーディオで出すつもりでいたので、ぶっちゃけ USB のドライバがあるコンピュータならなんでもよかったのですが、今回ラズパイにこだわった理由は簡単に MIDI で制御できることです。
SY-300 は(というか大体の MIDI がついているマルチエフェクタならば)操作を信号として MIDI 出力に出すことができます。 MIDI は、決して高速大容量の規格ではないけれど、距離やノイズによるトラブルが少なく、この分野では大人気の制御信号であります。

電源入れたら余分な操作を一切せず起動し、あとは足元で操作が完結するプレイアビリティ。今回はそこに全振りすることにしました。

要するにこっちのタイミングでラグ無しにスタートできて、スイッチ入れてからマイクの前まで戻る必要がないとかそういうことです。
 他にも、プレイ中に「あー、スリープしちゃった。ログインしなきゃ」みたいな事故はもちろん、システムがよぶんな音を出さないことや、目立つ画面がないことも重要なポイントであります。
人によってはノート PC をステージに持ち込んで、音作りやプレイに活用しているのですが、当方一人でステージ中央にいる都合上、ノート PC に向かって操作するのもパフォーマンス的に居心地の悪いものがあります。画面のバックライトで顔が照らされるのも勘弁して欲しい感じであります。

その点では今回組み上げたシステムは最高のチョイスとなりました。

MIDI 制御

SY-300 ではプログラムバンクの切り替えの信号を並列して MIDI に出力できます。
これをラズパイ側は USB の MIDI インターフェイス、 Roland UM-ONE で受けます(UM-ONE でもいいですが、不要でした。追記参照)。
曲ごとにバンクを作っておけば、対応するトラック(wav ファイル)をラズパイ側でシークできます。

ラズパイ上では、 MIDI 信号を解釈して対応 PCM を読み込んで ALSA (Linux のサウンド API)に出力するプログラムを常時動かしています。
これはイチから作りました。
midiplay という名称ですが MIDI は play しません。 MIDI で PCM を play します。
アンプで増幅した音をそれなりのスピーカーで鳴らすので、まかりまちがっても変な PCM が出ることだけは絶対に避けなければいけません。ですので音周りではあまり冒険せず、結果を完全にコントロールできる技術でのみ構成される必要があります。

サーバーとして必要な fork などは行っていませんのでデフォルトでログインするユーザーの起動スクリプトに書いておくという無精をしております。
MIDI 信号のパースも素朴な FSM を作ってでっち上げております。
その気になれば SY-300 からのテンポの入力、ラズパイからの音色制御も可能ですが、今回はそこまでしませんでした。


サウンド部

出力側 API は ALSA なので対応するドライバをロードした USB オーディオを利用します。
ここでは Cakewalk(ROLAND) UA-25EX を使いました。 MIDI と Balanced 出力が L/R あり、 Linux で動作するもので選びました。
CS3 の箱でいい子にしているラズパイと渦中のCakewalk
ASIO の対応がいまいち不明だったので標準的な PCM 48KHz 16bit を利用します。
ひとつ注意が必要なのは、ラズパイの標準 AC アダプタです。これではバスパワー駆動の USB オーディオを維持できなかったので、強い AC アダプタに替えてあります。
標準のはせいぜいマウスとキーボードでおしまいの電力量ですんで、ラズパイ使いの人は大容量のものを持っておいて損はないと思います。
ラズパイはとても小さく, AC アダプタと一緒にしても BOSS ペダルの箱にすっぽり収まってしまう良い子です。

ラグ無しで、とは申しましたが残念ながらラグはございます。
スイッチを踏むとロードを開始し、ある程度サンプルを読んだところで READY になります。足を離すと再生を開始するようにしたことで予想しにくい IO のディレイをなくしましたが、 PCM のバッファが潤沢にありましてこの分はどうしてもディレイになります。
これを十ミリオーダーまで削ろうとしたら初期化でエラーになってうまく行かなかったので、 150ms のラグを受け入れるという形で乗り切りました。
150ms くらい……と思ったのですが、実際試してみると微妙に間が悪い。 若干不安があったのでカウントを入れるなどしたのですが、あまりかっこよくありませんね。

まぁそんなわけで、コレを使うと以下の動画のように超カッコいい感じになります。

できあがり


<ここに超かっこいい動画を貼る>

すいません。動画なんか間に合いませんでした。ごめんなさい。
ここで尊敬してやまない偉大なベーシスト, J.K.Freaks 様の華麗なプレイ動画を貼ろうと思ったのですが、アドベントカレンダーの性質上、職場などでご覧の方も多いかと思って思いとどまりました。
想像力で補ってください。

本番ではやっぱとにかくドラムが聞こえなくってダメでした。
PCM と L/R にドラムとベースをそれぞれ寄せていたのでバランスで調整可能なはずでしたが、 PA もおらんイベントなのでぶっつけ本番で調整したらうまくいきませんでした。
スタジオでは適当にやっても聞こえてたんだけどなー。

PA 卓をいじっていたらバランス調整に手間取りすぎてセッティングも完全じゃなかったので、せっかくクリーンを JC に、歪み系をマーシャルに出し分けるように設定していたのも本番では使いませんでした。

宿題を残す結果になったので、そのうちリベンジの機会を待つことにします。

Coming Up Next...

明日 8 日目の担当は enpel さんです。
ビシっと決めてくれるはずと思ってます。


2015年8月9日日曜日

ジュラシックワールド観て来た(最高)

ジュラシックパークのまともな続編が出ないとお嘆きの諸兄。
あれもこれものリメイクラッシュの中、なぜジュラシックパークがリメイクされないのかとうち震えている諸兄。
3D 映画を結構制作しているのになぜジュラシックパークを 3D 化しないのかと憤懣やるかたない諸兄。

新作、ジュラシックワールドは諸兄らが待ちに待ったそれ、正にそれだということをまずお知らせしたい。
このような最高の続編を観られる幸福に乾杯。

いえ、慧眼なる諸兄らにあらせましては、予告編を見終えた瞬間に、椅子をガタと言わせて立ち上がったことでしょう。
これまでの続編がまぁ、ファンとしてはクソだとゴミだとは言い難いのですけれど、控えめにいって大変ガッカリな出来だったことは認めます。
次もきっとそうであろう、と思いながら予告編が始まるのを目にし、咄嗟に 「またやるのか。懲りないな」「もしかするとジョークムービーなんじゃないか」と思ってしまったほどであります。
ですがどうでしょう。
舞台はあのコスタリカ、イスラ・ヌブラ島。
二十年後、オープンした人気リゾード「ジュラシックワールド」。

来た。
来るべき時が。

スピルバーグは「シリーズとして継続可能な脚本が書けた者に続編を任せる」と言いました。
けれど、皆解っていたはずです。我々が本当に求めていたのはそうじゃない。
サイト B や、キャラクターは名優揃いで惜しいけど、昔の恋人とかそんな掘り下げは全然求めてない。

考えてもみてくださいよ。例えば一作目が素晴らしいメジャーリーグベースボールの話だったのに、出てきた続編がなぜかスポーツのための先進トレーニング研究所の話だったとか、監督の娘が体操の選手になって鉄棒で 160km/h のストレートを蹴り上げたとか、カーブが来たら必ずホームランを打つ強力な打者がマイナーリーグから来たけど誰もカーブを投げないとか、そんな話だったらどうします?

えっ、ナニコレ、外伝ですか?テレビシリーズですか?

ってそうなりますよね。
まぁ、日本の原作付き実写版とか、そういうポカを毎回のようにやってますけど、ハリウッドでもやるんだな〜よっぽど強いビジョンがないとこうなっちゃうんだな〜という学びはありますね。

皆解っていたはずです。
我々が本当に求めていたものは、

イスラ・ヌブラ、
そして恐竜のテーマパークなのです。

いわば念願にして唯一の正当続編、それがジュラシックワールドです。
夢のあるパークのプロップ、わくわくする設備の数々、明るいところで恐竜と戯れ、ジャングルを彷徨い、海岸線の絶壁、丘陵地帯のブロントサウルス……。
恐竜は登場人物の一部であり、 パークと島はもはや主役といっていいものです。
それらが 3D と最新の技術で、凄まじい実在感を持って目の前に展開します。

まず本作を未見の方には、観ようかどうか迷っている暇があったら観に行くべきだということと、シリーズ前作までを知らない方にはこんなものを読むよりさっさと劇場に行ってスクリーンの全てに集中したほうが有意義だということ、そして 3D で観るかどうしようか迷っている人には 3D で観るべきだということを強く推奨します。
字幕かどうかについては、そんなにシビアな演技はないから、タレントの吹き替えでない限りどちらでもよいと思います。より絵に集中できる吹き替えも検討できます。
前作までを観てからにすべきかどうかについては、初代のオマージュがとても多いので観ておいたほうがより楽しめると言えます。が、単なるオマージュではなく、現実の時間の流れに意味があるので、初代をリアルタイムで観ていることが重要です。
だから、リアルタイムで観ていないのなら、わざわざ復習してから劇場に行く必要はないでしょう。本作だけでも完成されたエンターテインメントなのですから。
というか、まず本作を観よう。それから初代を観て、また本作を観たらいいのです。
重要なのは二十年の時間の流れ。それがないなら多少順番が前後したところでどうってことありません。

しかし憂慮されるのは、いかんせん微妙な続編をこれまで続けてしまったことでトレイラーや宣伝の、たとえば「新種」であるとか、キャンセルされた「ジュラシックパーク4」のプロットであるとか、そういう部分が鼻について期待が薄れてしまい、まぁ時間があったら観てみよう、くらいになってしまうことです。ファンであるが故に、というわけですね。

そんなファン諸氏に対して、安心したまえ、これは俺達の求めていたジュラシックパークだよ、ということがこの記事の目的であります。
以下、ネタバレがないように気をつけつつ、シリーズ四作目「ジュラシックワールド」の見所の一部を紹介します。
一方で、初代、二作目、三作目のネタバレには全然気をつけてないのでよろしく。
気をつけるとはいってもトレイラーや CM で解るくらいのことは書いちゃってますし、できれば何にも前提知識なく観てもらいたいなと思っております。




コスタリカ沖の霧の島イスラヌブラに建設中だったジュラシックパークの惨劇の後、インジェン社の経営は悪化しマスラニ傘下に組み入れられており、ハモンドはパークの技術とウー博士、そして経営権をマスラニに譲っていました。
マスラニはパークの経営権だけでなく(映画版の)ハモンドの思想も受け継いでいるようで、ただの成金経営者ではなくて中々好人物のようです。
ただしこのへんは完全に映画オリジナルの流れで、二作目(ロストワールド)、三作目(ジュラシックパーク4)を観てないとわからないところです。原作のハモンドは基本金の亡者でしたしそもそもロストワールドの軍人同様、川で小さい恐竜(たしかコンプソグナトゥス)に食われて死んでいます。
一方ウー博士は生き残り、出世しておりましたが、顔は初代からあまり変わっていませんね。

二十年後、マスラニはイスラヌブラ島に「ジュラシックワールド」として遺伝子から復刻した恐竜のテーマパークを開園し、一日二万人にも上る集客を誇るまでに成功させていました。
ランクルではなくモノレールとハイテクのジャイロやカヤックを使った遊覧ルートとモダンになり、ビジターセンターも T-Rex の骨格模型ではなくディロフォサウルスなど色々な恐竜の立体映像に。なんと Mr.DNA の展示も健在!
そして前回の失敗から、恐竜は逃げるもの、トラブルは起きるもの、進化には勝てないという哲学をスタッフの末端にまで叩き込んでおり、 パークは格段に安全で快適になっておりました。
しかし映像を観ると……二万人くらいでちょっと混雑しすぎじゃね?と思いますね。島全体を使ったパークですが立ち入り可能なエリアは南部の僅か。 大部分は恐竜の飼育と研究のためのエリアのようです。
イスラヌブラ島はダイヤモンドのような形状をしており、西部と北部に肉食竜の飼育施設、中部に翼竜ドーム、南部の一部がアトラクションになっているようです。観客は南端の入り江へフェリーでやってきます。

更に、その後の研究でジュラシックワールドでは新たな知見が得られておりました。
一つは遺伝子の掛け合わせで新種を作り出して集客力を高められるということと、ラプトルほどの知能があれば飼い馴らすこともできるということ、そして隔離して飼育した生物にはある傾向がある、ということです。
設備も、恐竜も、(かなり無茶しているようだけど)資金、スタッフも全てがパワーアップ!それがジュラシックワールド!
そのスタッフの二人が主人公です。
一人は海軍からラプトルの管理に雇われたオーウェン(Mr. グレイディ)。初代でいう飲んだくれマルドゥーンと同じ仕事をしていますが、彼は恐竜をよく知って愛しており、その接し方はかのグラント博士よりも手慣れております。
専門はラプトルで、グラント博士のような子供っぽさはないけれど、草食竜との絡みはグラント博士よりも一段深い恐竜への愛情を感じさせます。
子供とも機械とも上手く付き合ってゆける中々の世渡り上手。
なぜ海軍をやめたのか語られませんが、会話の端々からするとどうやら海軍内での競争についていけなくなったようです。

そしてもう一人の主人公は広告マーケティングの責任者クレア。
彼女は恐竜のことを殆ど知らず、パークの研究からも蚊帳の外に置かれ、ただただ集客のために忙殺されている現場の人。
字幕では出てないけどかなり早い段階で「あなたのラプトル」という言い方をしていることからも、その辺りの事情についてだけはオーウェンから聞いていたようです。
クレアは姉(しかも離婚調停中)の子供、つまり甥っ子の兄弟、ザックとグレイをゲストとして預かることになりますが、忙しさのため秘書のザハに預けきりにしてしまいます。
グレイは恐竜好きで詳しいが子供、ザックは高校生にもなって反抗期真っ盛り。
この辺はハモンドと二人の孫、レックスとティムをそのまんまなぞる形になっています。
これに限らず、本作の登場人物は概ね初代の誰かの立場を踏襲する形で配置されています。なのでそれぞれの役割が解りやすいです。
もっともザックは IRIX よりは Android 派、ましてハッカーでもなんでもない普通の高校生ですが……。

叔母であり経営陣であるクレアは、甥っ子にも嫌われ、姉にも嫌味を言われ、目先の利益ばかり追求し行く先々で総スカンを食らうのですが彼女は決して鼻持ちならない人物ではないです。
ドジッ子全開ですが途中からサトラー博士の服を真似をしたりかなりお茶目です。

クレアの目線から描かれるパークの内情は決して単純ではないようですね。
エキセントリックな CEO, 遺伝子操作に反感を持つスタッフ、でもそれをしなければただの動物園になってしまうというジレンマ。
他にも……まぁ、色々な問題があるようなのですが、こうした複雑になりがちなところが面倒にならない程度にストレートに解りやすく話に組み込まれているので、鑑賞する上で邪魔になることはありません。

そして一番の問題は、遺伝子組み替えで作られ、三週間後にお披露目を控えた新種「インドミナス・レックス」の存在。
新種とか(話の出来的に)もう悪い予感しかしませんよね。僕は「また余計なもの入れやがって」と心配していました。
ところがどっこい、この新種は、凄く使える奴でした。
何をしても映える。凄い絵になる。
CGI で描かれたでっかいトカゲが、こんなに恐ろしい演技を見せてくれるなんて想像できませんでした。迫力は、勿論映画館の音響っていうのも大きいのでしょうが、その怖さは、例えるならダークナイトのジョーカーにも近いものを感じます。
噛み方一つとっても、いい意味でいちいち恐竜らしくない。
知能も破格だが、それが荒唐無稽にならない程、登場の仕方が様になっている。
T-Rex の前足の弱さのような脆さもなく、動くものしか見えない視力といった弱点も持たない。

恐竜の種類といえばシリーズ中、ちょっとずつ増えてきて、その度ちょっと上滑りしている感じがあったけれど、今回は描かれ方もとてもよいです。
初代ではたとえば、トリケラよりもトリケラと戯れるサム・ニールを主体にしていた印象がありますが、 本作ではずっとはっきりと恐竜を捉えています。
ガリミムスの群れやブラキオサウルス、例えばプテラノドンにいじめられるトリケラトプスの子供とかすごく可愛い。
そしておなじみのラプトル同士の掛け合いなど。ラプトルの鉤爪があまりフィーチャーされなかったのは何か理由があるのでしょうか。新しい学説で実は爪は大したことなかったかも知れないとか、そういう。本作では新しい学説に基づいて変更が加えられているようで、登場しなくなった恐竜などはその兼ね合いが大きそうです。
元々初代からラプトルはかなり愛嬌のある描かれ方をしていたけれど、今作では表情まで伝わってきそうな気迫がありますね。
彼女らの狩りの仕方について、初代からずっと御都合主義過ぎるんじゃないかという批判もありますが、集団の狩りをする計算高い生き物ということは作中でも繰り返し語られるところでして、不合理はありません。
三作目に華々しく登場し、あまり活躍しなかったプテラノドンなどの翼竜もちょっとやり過ぎなんじゃねってくらい大活躍します。
ただうざいだけでなく、遠くからでも絶望感があってスピーディー。彼女らの性質がとても生かされていました。
特筆すべきは新登場のモササウルスです。
途轍もなくでかい、クジラのような水棲の恐竜。
これはトレイラーで観て「あーまた使えなさそうなの出るなぁ」くらいにしか思わなかったんですけど、これが実にとんでもない。
よく考えたなぁ、こんなの。今作のスタッフは、恐竜が好きで好きでたまらないんだな。

初代以外はずっとホラーのような暗い場面でのショッキングな襲撃ばかりでしたが、今作は明るい、成功した恐竜パークとしてもずっとしっかり描かれています。
恐竜ばかりではありません。ヘリのシーンが何度も登場しますので、あの入り江や、ランドマークになった滝や、懐かしの雄大な尾根からの丘陵地帯も 3D で堪能することができます。風景どころか空気感だけでも懐かしさがあります。真っ暗な中で施設や車の照明が灯っているカットだけでもちゃんと「ジュラシックパークっぽい!」となるんだから不思議なものです。
初代だと、変電所の入り口でラプトルとサトラーが追い掛けっこするシーンなんか霧が濃過ぎてちょっと安っぽいというか、セット狭いんじゃね?という感じがあったのですけど、今作はそういうこともなく空間と空気が描ききられています。
加えて、人間に対する暴力も容赦がなくなっています。
いえそもそも狩りで手加減などしてくれないので、その意味じゃ元々容赦ないんですが、描き方の話です。
初代などは人間の捕食シーンもどちらかというとコミカルにぼかしていたし、アーノルドのように腕だけになって登場したり、そもそも死んだのかどうかさえ解らない人物さえいました。
そういうところはかなり減って、グロくはないものの比較的苛烈になりました。

今作は、初代の朝のブラキオサウルスやハモンドのノミのサーカスの話のような、しんみりするシーンは殆どないけれど、これでもかとオマージュが詰め込まれた上、素晴らしい恐竜パークとその熾烈なパニックが実在感をもって詰め込まれた最高傑作です。
恐竜はただの猛獣ではなく、個性的で物語の担い手として完璧に組み込まれています。
昼も夜も広々と描かれた島は空気感まで伝わってくるようです。
全部見所ですが、こうしたところに注目するとより楽しく観られるのではないでしょうか。
あ、そうそう、これは完全に余談ですが、もう一つ書いておくと松本そっくりの浜田でちょっと笑いました。

2015年7月31日金曜日

ARM SoC ボード Helio で SDRAM アクセスが遅い問題の解決

もう二ヶ月くらい経ってしまいましたが、 Helio の SDRAM アクセスがやったら遅い問題は解決しておりました。
まとめる時間がなく今もないので軽く書き置きしておきます。

まずは Avalon 経由でアクセスしている FIFO 付き SDRAM インターフェイスですが、これに入れているクロック 50MHz がまずかったようです。
これはそのまま SDRAM のクロックとなるようですが、それ以外にも重要だったようで、これを 100MHz にしないとパフォーマンスが出ませんでした。
「いや、スループットが二倍になっても全然足りねーじゃん」と思ったのですが、これを 100MHz にしたところ 2 倍どころか 8 倍くらいになり、かなり納得のいくパフォーマンスになりました。
(ちょっと具体的なデータが今すぐ見られませんが)

以下の helio_ghrd_top モジュールに与えられている二種類のクロックのうち

input  wire        fpga_clk_50,
input  wire        fpga_clk_100

fpga_clk_100 のほうを、 export してあるインターフェイス(前回作った memwrite_master) を経由してコントローラに供給しましょう。

 memwrite_master m0(.clk(fpga_clk_100),


ただしこれでも理論値には微妙に届きません。
今は一回のストアに 4 ステートほどを経由する必要があり( Avalon MM の先のオービターで更にどれくらいのオーバーヘッドがあるかわかりませんが、少なくとも……)、このままでは理論値には届きません。
可能な限りコントローラの介入を避けられるよう、これ以上はバーストモードを使いましょう。

バーストモードでは最大 255 * 512bit のストアが可能です。
Avalon のインターフェイスには burstcount が出ています。ぱっと見、これを使えば簡単そうなのですが反面データシートの記述とは一致しないため不安があります。

実際やってみるとこれはまたハマりました。
詳しいことはまた後ほどまとめたいと思いますが、やっぱり Avalon のインターフェイスがデータシートと一致していないのが敗因で、バースト転送の開始を示す信号は、 burstlength の変化をトリガーにして暗黙に生成されているようでした。
つまり毎回同じ burstcount だからといって定数を出していると、他の信号を変化させてもたった一度しかストアが行われません。
また転送終了のシグナルも出ていないので、これの捕捉にも癖があり苦戦したように思います。

結果、 1 クロック余計なステートが増えましたが無事バーストアクセスできるようになり、(バースト長にもよりますが)理論値と比較してそれほど遜色のないパフォーマンスがでるようになりましたとさ。

2015年7月19日日曜日

ヘッドフォンを買う

えーと……なんでだっけ。なんで買おうと思ったんだっけ。
そうだ、思い出した。蛍光灯が壊れたからだ。
リビングが真っ暗になっちゃいましてね。
なくてもいいかなと思ったのですが暗いと以外と何もやる気出ない。条件反射のようにもう寝るしかねーかな見たいな気分になってしまって。
これはいかんなと思って照明を買いに行ったわけですが、11-2 畳くらいで使えるシーリングライトとなると、これが結構バカにならない値段がしてた記憶があるわけです。7万か8万か……安くてもそれくらい。
それまで使ってたシーリングライトも、あんまり高いので 10 畳くらいのやつを 5 万くらいで買って使っていたので微妙に暗かった。

あーもう急な出費がーと覚悟をキメてヨドバシ行ったらですね、ないんですよ。蛍光灯が。
全部 LED だけになってまして。
LED かー、あんまりいい印象ないなーとしぶしぶながら見てみると、これが安い。 3 万もしない。
12 畳用でさえ 2 万しないくらいからある。

パナの二色を調光できるタイプのやつを二万三千円くらいで買いました。

予想よりだいぶ安上がりについたな〜と帰ろうとしたとき、ふと足が止まったわけですよ。そこがヘッドフォン売り場でした。
怖いですね。
金色の四角の真ん中に HI-RES とか書いてあるんですよ。黒い文字で。
怖いめう。

買ってました。
そういやプレイヤーがハイレゾ対応になっていたのに対応のヘッドフォンもイヤフォンも持ってないから放置してたのですよ。

二万六千円くらい。
シーリングライトより高くついたじゃん。

MDR-1A というやつを買いました。
……これは! MDR-EX90 を越える自然さじゃないか!大人し過ぎてもっとドンシャリじゃないと物足りない感じもあります。イコライザーでカバーしましょう。
MDR-10R 系も試聴したのですけど、ドンシャリの派手さは好きだけど籠もりがひどくって 1A の魅力にはかないませんでした。安いけど、籠り方だけ聞くと 9000 円くらいのやつとあんまりかわらない感じするし、むしろ割高かも。

せっかくなのでハイレゾ用の音源も買いました。
mora であっという間にダウンロードできたので、「でかいと思ってたけど大したことねーな」と四曲くらい買ったところデータ転送しすぎ警告が来ました。
転送量見てみたら mora だけで 800MB 以上使ってました。一曲あたり 200MB 以上。
うわーでけぇ! 怖い!!

同じ曲のハイレゾ版と AAC 版を聞き比べてみるとまるで別の音源なのですけど、冷静に考えるとこれはそもそも別の音源であって、比較のしようがない。

もちろん AAC だって、いいんですよ。
ハイレゾ版はなんていうか、アレだ、スタジオで聞いてる音だ。ヘッドフォンから鳴ってる音じゃない。

ああ、これはいい買い物した。
ハイレゾ音源でなくとも、 MDR-EX90 くらいの仕事は余裕でこなしてくれます。
なんだかんだ文句いいつつ XBA-40 を気に入って使っていたわけですが、これが XBA-40と同じくらいの値段ですよ。
家用にもうひとつ欲しい。

2015年5月6日水曜日

Altera SoC Helio で SDRAM の HPS/FPGA 共有(作業編)

前回の記事で詳細には踏み込んでいないものの、ほぼ必要な情報が揃っているはずです。
実際の作業を始めましょう。
でもまず、作業を始めるのにあたって必要なものが欠けています。
Helio として最低限動作可能な FPGA 側のリファレンス実装です。

作業編

リファレンスデザインの入手

Helio は GSRD (Golden System Reference Design) というものに則って設計/実装/提供されています。
生憎こちら、GSRD という枠組みについては詳しくなく、どこからどこまでが GSRD なのかはよくわからないのですが説明によると, Altera か Xilinx かにはよらず

FPGA(GHRD) + ARM SoC + Linux 環境  = GSRD

ということのようです。
Altera だろうとそうでなかろうと GSRD に従った設計がなされているものについてはある程度似たような環境が用意されているということですね。
ここで重要なのは、主に FPGA 側の設計のリファレンスデザインである GHRD (Golden Hardware Reference Design) のほうですね。

Getting Start Guide に従ってサンプルを動かした人ならば既に手元にあるはずです。
しかしながらドキュメントに従って cv_soc_devkit_ghrd.tar.gz をゲットしてしまった人は残念。理由はわかりませんがこれは Helio では動かず、 Helio には以下から専用のものを入手しましょう。
helio_ghrd_5csxc4_v14.*.zip が正解です(Quartus II のバージョンに合わせて選んでください)。

http://www.rocketboards.org/foswiki/Documentation/HelioResourcesForRev14

に一通りあります (ボードの Revision が 14 用。 Quartus のバージョンと紛らわしいですが、今はたまたまどっちも 14 です)。
間違えると回路が正しくても Linux がブートしなくなります。
なんというハメでしょうね。僕はこれにハマって二三日悩みました。(.sof イメージは焼いても電源を切ると揮発するので、復旧自体は簡単です)

GHRD の中身は全て Quartus II のプロジェクト及び Qsys で扱う IP とサンプルの helio_ghrd_top.v です。
自分でビルドしない限り、一切のビルド物は含まれていません。

helio_ghrd_top.v を開くと、殆どの信号は soc_system という下位回路に結線されているのがわかります。
その soc_system が GHRD の本体であり、 HPS のインターフェイスを実装した(あるいはこれから実装する)回路であります。

ところが soc_system の実装自体は .v としては付属しません。
soc_system は全て IP として提供されており、 soc_system.qsys から Qsys で生成して入手します。
このへんの手順は Getting Start Guide でも一通り説明されていますので、僕のような noob でも脳細胞が Hi-Z になったり X になったりすることなく進めました。

これからの作業は殆ど Qsys と普通のエディタで行います。 Quartus II はビルド(Analysis & Synthesis, Place & Route, Fitting) と死んで再起動くらいしかしません。

なぜここで GHRD を必要とするかはここまででお解りと思います。
このリファレンスデザインを Qsys でカスタマイズしたり、外部に設計した回路に繋げることでシステムを実装していくわけです。
どれを IP にしてくっつけるか、どれを外部に引き出すかは任意です。

SDRAM Controller のインターフェイスを取り出す

soc_system の hps_0 というインスタンスに結線されている f2h_sdram_data というのが SDRAM Controller のインターフェイスです。
これは信号の種類を見れば解る通り Avalon MM Slave のインターフェイスの形をしております。
hps_0 というインスタンス(下位回路への結線)は、ソフト的にいうと複数のインターフェイスを多重継承したようなイメージですね。
飽くまでこれはイメージで、実際には各信号ごとに wire が結線されております。

Qsys を使ってリファレンスデザインをカスタマイズしていくと書きましたが、 IP を変更すると毎回 "Generate HDL" を実行せねばならず、そこそこ時間がかかってしまいます。
ここはこのインターフェイスを外部に引き出して、 IP の外にある helio_ghrd_top.v で回路を実装したほうがよいでしょう。

helio_ghrd_top.v からは hps_0 の上位回路である soc_system しか見えません。
まずは hps_0 の f2h_sdram_data を soc_system に引き出さねばなりません。
普通の FPGA であればピンに出ているのですが、 SoC では外部といっても内部です。 ピンが出ているとは限りません。
このへんは Qsys を使い慣れていなければわからないので、身近なハード屋さんに助けてもらいました。
引き出したいインターフェイスを右クリックして "Export as" を選べばオッケーのようです。
Export できた!

hps_0 の上のほう、 f2h_sdram0_data の左側がタグのようになっています。これが外部に引き出せた状況です。
各カラムは左から Name, Description, Export name, Associate Clock となっております。
Export の column にある名前で上位回路に自動的に結線されます。
典型的には、
(接続先インスタンス名)_(信号名) 

という名前になるようですね。

バス幅の決定と HDL の再生成


それから hps_0 を右クリックして "Edit" を選ぶと、プロパティシートから細かい設定を変更できます。
デフォルトは f2h_sdram_data のバス幅として 256bit が設定されています。
メモリのデータバスは 16+16bit で 32bit なので、まずはデータバスに合わせておこうと32 に変更します。
そして Generate HDL を再度行います。
この場合はこうなりました。
        input  wire [29:0]  hps_0_f2h_sdram0_data_address,
        input  wire [7:0]   hps_0_f2h_sdram0_data_burstcount,
        output wire         hps_0_f2h_sdram0_data_waitrequest,
        output wire [31:0] hps_0_f2h_sdram0_data_readdata,
        output wire         hps_0_f2h_sdram0_data_readdatavalid,
        input  wire         hps_0_f2h_sdram0_data_read,
        input  wire [31:0] hps_0_f2h_sdram0_data_writedata,
        input  wire [3:0]  hps_0_f2h_sdram0_data_byteenable,
        input  wire         hps_0_f2h_sdram0_data_write,
あとはこれに対して Master となる回路を書けばメモリのアクセスが可能になります。

ところで、元々 f2h_sdram0_data はどこに繋がっているのでしょうか。
Export すればこのインターフェイスを外から使えるようになりますが、それだけでは元々ここに繋がっていた回路は使えなくなってしまいます。
f2sdram_only_master に繋がっているのは Qsys 上でも確認できますが、この副作用を看過できるかどうかは f2sdram_only_master の先を注意深く調べる必要があります。
調べてみたところ、これは幾つかの FIFO を経由して JTAG に繋がっていました。
JTAG からのリクエストを非同期で処理するための回路のようです。
SDRAM Controller はこれを調停可能なはずですが、今回は華麗にカットしました。

Master の回路を書く

Avalon MM の仕様書より、タイミングチャートを調べて実装します。
メモリアクセスの方法は、基本的な同期 RW 以外にもパイプラインリード、バースト転送などがありますが、今回はまず基本的なライトだけを実装します。

Figure 3-3: Read and Write Transfers with Waitrequest の右半分がそれです。

言葉で説明すると、

a. (Master が)パラメータを設定して Write を立てると
b. ビジー状態を示す WaitRequest が(Slave によって) 1 にドライブされ、
c. 書き込みが完了すると (Slave によって) WaitRequest が 0 にドライブされるので、
d. (Master は)パラメータは次のクロックの立ち上がりまでは保持し続けること

ということです。
今度は W と R がゲシュタルト崩壊しそうなので WaitRequest は Busy と読み替えたほうがいいかも知れません。
Write を立てるときには WaitRequest は(あと当然 Write も) 0 でなければいけません。
書き込みのパラメータはアドレス、書き込みマスク(byteenable)、書き込むデータの三つです。
これらパラメータが設定されるのは Write と同時かそれ以前で、クロック同期である必要はありません。(書き込みデータだけは WaitRequest が立つ瞬間まで遅延できるようです)
今回はバーストライトではないので burstcount は 1 のままにします。

byteenable はかなり使い手のあるパラメータで、実際に書き込むバイトを自由に設定できます。バイト同士が連続である必要も、popcount が 2^n でなければならないといった制約もありません。

1 ワードのデータを書き込むだけならただの組み合わせ回路でもできるのですが、やはり沢山書いてみたいので少し凝りましょう。
WaitRequest と書き込み信号をイベントとしたクロック同期する FSM を中心に構成しましょう。
そうしておくことで、連続したリクエストに対しても d の制約を満たすことができます。

メモリレイアウトを考える

回路の構成には関係ありませんが FPGA と HPS で物理アドレスを配分しなければいけません。
しなくてもいいのですが、現時点でバッティングしてよいことは何もありませんのでとりあえず 512MB ずつ半分にして HPS には 0x000000000-0x1fffffff, FPGA には 0x20000000-0x3fffffff としておきましょう。
これは完全に自由にできるわけではありません。FPGA 側がハイアドレスを使うことは決まっていますし、アドレス空間の最上位 1GB の空間は予約されています。
詳細は Device Handbook Volume 3: 9-11 Boot ROM Mapping をご覧ください。
そんなに情熱を注ぐところでもないです。

HPS 側で物理メモリを制限するためには、 Linux の boot に UART で割り込んで抑制し、 代わりに上がってくる SOPC shell 上で editenv コマンドを使って mmcboot 環境変数に書き込めばよいです。
言葉で書くとなんか大変そうに見えますが EFI shell とか EPROM shell みたいなもんです。
こちらの blog の JTAG を使って DDR3 をシェアする記事が大変大変参考になります。

出来上がった回路はこうなりました。
なんか色々怪しいですが。

https://github.com/roentgen/sdram_helio/blob/master/helio_ghrd_top.v

CPU 向けのインターフェイスを用意する

起動したらずっとメモリに書き込み続ける回路ならば不要なのですが、それはちょっと熱や消費電力(それから精神衛生面)に優しくないので、 ARM 上で動くプログラムからメモリの書き込みと停止をコントロールできるようにしましょう。

CPU とのインターフェイスは、今や SDRAM さえ使用可能ですが、最もお気楽な方法としては MemoryMapped-IO があります(以下 MMIO)。
今回はハードウェアらしく MMIO を使います。

MMIO は、CPU からコントロールレジスタが見えないようなデバイスを扱うのにとても歴史がある方法で、個人的にも古い友人のような感慨があります。
この方法では物理アドレスの一部を予約し、そこに対する CPU からのアクセスを(メモリではなく)外部バスにリダイレクトするのです。
リダイレクト先は(典型的には) FPGA のレジスタになります。

……と、理論上は簡単なのですが、 Altera SoC におけるここの実装は相当に複雑です。
Device Handbook Volume3: 8-3 HPS-FPGA Bridges Block Diagram and System Integration の Figure 8-1 をご覧くださいませ。
HPS->FPGA と FPGA->HPS 二つのブリッジがあるのはわかるのですが、その間に Lightweight HPS-to-FPGA Bridge なるものが介在しています。
これらは AHB/AXI なのですが、にも関わらず soc_system の IP 上見えているのは AvalonMM Slave です。
32bit アクセスだけは lightweight を通って、64bit はネイティブにそれぞれのブリッジ上を通る。 128bit はブリッジ上の FIFO で分割される……という風に読めますがそれにしては AHB の Master/Slave が逆のようにも思いますし、よくわからないです

よくわからないものの、使い方は非常に簡単です。
一つだけ注意することがありましたが、それだけです。
GHRD にはとてもよいサンプルとして sysid_qsys と led_pio という二つの IP が組み込まれています。
これな
MMIO の物理アドレス 0x10000 からとなっていますが、実際は FGPA ペリフェラルの物理アドレス空間は 0xff200000 からなのでここからのオフセットです。
物理アドレス 0xff210000 がこのレジスタです。
コードも簡単で、ここへのアクセスに対し定数を readdata に突っ込むだけのコードになっています。
インターフェイスは Avalon-MM Slave になります。

ただし、前述した HPS-to-FPGA Peripheral のよくわからないことの一つでよくわからないままなのですが、どうやら注意しなければいけないことがあります。
IP を設計するときは Avalon-MM Slave と思っておいていいのですが Qsys で繋ぐときに h2f_lw_axi_master とも接続しないとこの単純な回路さえ値が見えません。

(実際 sysid_qsys は接続されています)
sysid_qsys を見てみると fpga_only_master の Avalon MM Master に繋ぐだけでなく、 hps0 の h2f_lw_axi_master とも接続されています。
インターフェイスには出て来ない AXI を接続しなければいけないとは、記述的にはまったく想像がつかないです。
ですがたしかに HPS-to-FPGA Peripheral のブロック図からすると、ここのブリッジは AXI でのみ繋がっているはずで Avalon ではないはずです。
つまり、バスが AXI であっても Avalon に見せる魔術があるのだろうとは予想されますが、今回枝葉なのであまり詳しくみてません。「へー、なるほどねー」くらいのことです。

lw (Lightweight)なのは MMIO の幅が 32bit だからなのでしょうか。64bit か 128bit のときは別のマスタと接続する必要があるのかも知れませんが、オービターがうまいことやってくれるような気もします。

一方、書き込むほうは led_pio を参考にしたほうがよいでしょう。
有意な writedata があるときだけライトストローブが有効になるようにします。

   assign wr_strobe = chipselect && write;

この wr_strobe を見て内部のレジスタを変更するようにすればよいです。
state_splat は 1bit reg, wd は 8bit reg です。 その他、サイクルカウンタに使ったレジスタなどが見えますが、重要なのは state_splat だけです。

   always @(posedge clk or posedge rst) begin
       if (rst == 1) begin
           state_splat = 0;
           wd = 0;
      end
      else if (clk) begin
          if (wr_strobe) begin
              state_splat <= writedata ? 1 : 0;
              wd <= writedata[7:0];
          end
      end
   end
  
   assign readdata[31] = reset_n;
   assign readdata[30] = mem_rst;
   assign readdata[29] = state_splat;
   assign readdata[28:8] = regcc[31:11];
   assign readdata[7:0] = wd;

これを module に仕立て上げ IP として Qsys に追加しましょう。
コードはこちら。
https://github.com/roentgen/sdram_helio/blob/master/mem_sdram_interface.v

具体的な手順は……スクリーンショットを保存しておらずですね……再びこちらの記事がとてもとても参考になります。ありがとうございます。

Qsys は任意の .v を検証し、インターフェイスを明示的に選ばせることで IP として使用可能にします。
(.v はそのまま soc_system/synthesis/submodules/ 以下にコピーされる)
IP として登録するのに clock, reset が超重要です。
これは module の信号リストの順番が結構センシティブですので、信号リスト先頭から clock, reset の順番にしておいたほうが無難のようです。
reset は正論理か負論理かも設定できます(IP をインスタンス化するときにケアされます)。
 
正しく設定しておくと色々ご利益がありそうですが、よくわからんので今回は雑に設定しました。
名前はどうしてこんな長い名前にしてしまったのかと後悔しつつも mem_sdram_interface とし MMIO のベースアドレスは 0x10010 としました。

ちなみに qsys の検証は、簡単なシンタックスチェックが入るだけで合成はしません。
ですのでエラーがあると HDL を生成してからあとで Quartus II で発覚することになります。事前にチェックしときましょうね。面倒だからといって submodules 以下のファイルを直接いじったりしてはいけませんよ(実証済み)。

出来上がりの確認

MMIO で非ゼロを受けて memwrite_master を駆動するシステムができました。
MMIO でゼロが書き込まれるまで、 memwrite_master はアドレスからアドレスまで定数を書き込み続けます。

Qsys ではこうなります。

全体像
一番下の mem_sdram_interface が MMIO の Slave です。
いくつかよぶんな信号線が生えていますが、これは後述するサイクルカウンタの値を設定するためのものです。
memwrite_master は IP にしてないのでここには出ません。

CPU 側のプログラム

とりあえず動かしてみるために、ちょっとしたプログラムを書いて回路をキックします。
MMIO の物理アドレスを指定して mmap し、仮想アドレスに対して 32bit データを書き込むだけです。
1 を書き込むと memwrite_master が動きだし 0 を書くと止まります。
memwrite_master は動き続ける限りあるアドレスの範囲に書き込み続けます。

https://github.com/roentgen/sdram_helio/blob/master/addrmap.cpp

このプログラムは単純に、物理アドレス指定してコントロールレジスタのマップされてる領域と、実際に書き込みが行われる DDR3 上の領域を mmap し、ダンプします。
/dev/mem を open しますので、その権限が必要ですが、全てユーザーランドからコントロールできておりドライバは不要です。
(実際もっとページ属性を細かくコントロールしてやろうとか、カーネルモードでしか触れないレジスタを触ろうとか、 マルチプロセスに対応してやろうとかなるとドライバやカーネルモジュールを作る必要がでてきますが……)

open() システムコールのフラグに O_SYNC を指定しています。
最近のカーネルではこれを立てて /dev/mem を open すると uncached になるらしいです。
(kernel が STRICT_DEVMEM 付きでビルドされてないといけないようですが、たぶんそうなってるでしょう)

サイクルカウンタを実装してパフォーマンスを測定する

これは後付けですが、パフォーマンスを測定するためのサイクルカウンタを実装して、メモリの fill が終わったら fin を上げて自律的にリセットしてサイクルカウンタを MMIO のレジスタに書き込み、 CPU からサイクル数を計測できるようにもしましょう。


この場合のリセットの方法はいろいろあると思いますが、ハードリセットほどは単純でなく自律的にやるのは結構苦労しました。
簡単そうに見えたのですがね、リセット解除しないと次の実行を始められないので。
リセット解除は FSM を別に作るかそもそもワンショットパルスにするべきかはたまた CPU からやったほうがいいのかも知れませんが、この辺はノウハウを溜めないといけないな〜。

ともあれ、満身創痍ですがサイクルの測定はできました。
実行してみると物理アドレス 0x20000000 から 0x2000 * 32bytes の領域に書き込めています。
fin が立ったあと mem_sdram_interface の WE  に間に合わないのかサイクルカウンタが終了時にリセットされてないように見えますが、開始時にリセットされているのでよしとしましょう。

結果

master 側の fpga_clock50 は 50MHz です。
なんと 0x10000 回の 32bit ライトアクセスで 500ms もかかった計算になります。
遅い!!
500KB/s しか出てないじゃないか!
一回のライトリクエストごとに 390 サイクル (7us@50MHz) かかったことになります。
大体 40-50 サイクルじゃないかな〜と思っていたのに……これはちょっと遅過ぎます。

メモリは 200MHz 駆動だけどもコントローラは 50MHz だし、 SDRAM Controller がボトルネックになっているようです。
確かめてみるためにデータレートを上げてみます。

Qsys で HPS のプロパティシートを開き f2sdram0_data のビット幅を 256 に変更します。(32bit にしていた)
デフォルト 256 ですがメモリのデータバスが 32bit だし 256 は厳しかろうと思っていたのですが、メモリバンドネックではなさそうなので 256 で試します。
memwrite_master の wire 幅にも影響があるのでそっちも修正して試してみると……。

結果はトータル 0x061a << 3 カウント。 1 トランザクションあたりのサイクル数は 390 で同じでした。
ただしトランザクション回数が 1/8 になったぶんバスバンドはリニアに向上し 4MB/s 程度出ます。かかった時間も 60msec 程度と大人しめ。
Avalon MM の Master/Slave 間にブリッジが存在しないのであれば、 Multiport SDRAM  Controller のクロックが低過ぎるか、スマートすぎて DDR3 の帯域を生かせないということです。

ただしまだ結論は出せません。
だってメモリコントローラがバスネックになるのじゃ ARM から見ても DDR3 の帯域がでないはずで、じゃあ DDR3 を積む必要なんかないじゃないかということになります。
SDRAM Controller Subsystem の説明ではコントローラへのアクセスは AXI だったはず。

ならば現時点の仮説は

1. Avalon が悪い。 AXI を使え
2. メモリコントローラの実力だ。 バーストモードか DMA を使え
3. GHRD が悪い。 クロックを上げろ

の組み合わせということになります。

なぜ遅いかはまだ解らないにしても、ひとまず実験の結果は出ました。
まとめましょう。
  • FPGA と HPS で SDRAM を共有することは(解ってしまえば)簡単だ
  • Avalon MM 同期 Write を使ったメモリの書き込み@50MHz ではメモリの帯域が出せない(256bit でも 2MB/s が上限)
  • ライトトランザクションにはデータ幅によらず 390 サイクル, 7us@50MHz かかる
以上となりました。

2015年5月5日火曜日

Altera SoC 開発ボード Helio

一時期、なんか上司氏と顔を合わせるたびに「VHDL 書ける?」と聞かれていたのですよ。月刊インターフェイスの読者であった僕は勿論それがどういうものかは大体知っているけど上司に向かって「書けます」というのは純粋な嘘になるので

「知りません、書けません」

と逃げ回っていたのです。
ところが最近身近で FPGA ブームが起きていまして、 DE1 を買って来たのです。
DE1 は低価格で Sound DAC や DSUB を備えた楽しいボードなのですが、気軽に使える IO はプッシュボタンと 7SEG と LED くらいなので LED 光らせる(いわゆる L チカ)などラボの講義を終えたくらいで止まっておりました。
(だっていきなり音とか出したら耳ぶっ壊れそうだし、 DSUB のモニターなんかもうないよ……)
ところが最近よいものが DE1 の価格帯で売っていると聞きました。



Altera SoC Cyclone V の評価ボード Helio です。
CycloneV はデュアルコア ARM SoC に FPGA fabric をオンダイに搭載したものです。
FPGA として見ても、 DE1 の Cyclone II から見ればとても豪華なものです。
Helio ボード上には UART, JTAG, USB Blaster, Ethernet, DDR3, USB(OTG), MMC が実装されており、すぐ MMC で Linux をブートして LAN 上に見ることができます。

Ethernet, DDR3 は説明不要でしょう. UART はシリアルポート, JTAG はデバッグインターフェイス, USB Blaster は FPGA の再構成デバイス, USB(OTG) は Host にもなれる USB コントローラー、 MMC はマルチメディアカードです。
どれも実績と人気のあるパーツでまとまっております。
HDMI や DVI がないくらいです。

Helio のメインコンポネント

さて、ところが FPGA fabric を搭載した ARM なんて使ったことがありません。
例えば ARM はメモリを混成しておらず、ボード上の DDR3 を使いますが、 FPGA から DDR3 を使えるのでしょうか。
FPGA からはどのバスで DDR3 を叩くのがベストな方法でしょうか。

ここでは FPGA から DDR3 に書き込んで ARM から読むのを目標に、 Cyclone V のアーキテクチャを見ていこうと思います。

準備編

Linux の起動

とりあえず Helio ボード上で Linux くらい起動していなければ話になりません。
Helio の開発元である rocketboards のサイトに丁寧な Getting Start Guide がありますのでそれを見て行ってください。
Helio には SD カードが付属していますが漢のブランクであります。
ラズベリなんかだとちょっとお金を払うだけで u-boots で起動する Linux のイメージを焼いたものが付属しますが、イメージは rocketboards に各種ありますので好きなものをダウンロードして dd で焼きます。
一番シンプルなものでも httpd,sshd くらいは動いてくれます。これで充分でしょう。
起動すると DHCP で勝手に IP アドレスを取得します。

UART は Linux の起動に割り込むのに必要ですので常時繋いでおきます。
FPGA の再構成に使うケーブルも繋いでおいたほうがいいので、二本は必要ですね。
なくても Linux のブートくらいはできますが(ありがちな話ですが) UART は IP アドレスを確認するのにも必要です。

Helio では、何かがバグっているものか起動するたびに MAC アドレスが変わってしまいます。
このためルーターで IP を指定することもできません。会社で使うにはトラブルの元でしょう。
しかもブロードキャストに応答しないので、真っ先に static にしておきましょう。

開発環境

ソフトの人向けに、まず開発環境とワークフローについて書いておきます。
Parallels9 for Mac + Ubuntu13.04 の環境に Quartus II 14.1 web edition  を入れて使っています。
Quartus II は Altera によるいわゆる IDE で論理合成、配置配線といったソフトでいうところのビルドを行います。
Cyclone II などの古い FPGA は Quartus II 14.0 以降サポートを切られていますが、 Cyclone V では Quartus II 14.1 を使いましょう。
v14 以降は 64bit サポートもしています。(シミュレータである ModelSim は 64bit 対応ですがインストーラが 32bit というクソです)
詳しくは後述しますが Quartus II よりはその付属の Qsys というツールをよく使います。

Qsys はかつて SoC Builder と呼ばれていたソフトで、ハードウェアの情報から IP (ソフトでいうライブラリみたいなもの)をカスタムした一つの大きな回路とそのインターフェイスを生成するものです。
cmake みたいなもの……といえばいいでしょうか。
IP というと「高そう」とか思ってしまいますが、後述する GHRD というリファレンスデザインが既に IP として提供されています。

シミュレーションには敢えて ModelSim を使わず GHDL や icarus verilog を使っています。
GHDL は幸せでしたがこれは VHDL 用ですんで icarus verilog にしました。

ARM コア用にはツールチェインが提供されていますが、普通に arm-eabi 用の gcc を使うことにします。
(VM で clang ビルドしたら三日四日かかってもリンクが終わらなかったのでいったん諦めました)

ちなみに Quartus II は安定しているときは安定していますが、死ぬときは 10 分で死にます。
調べてみると Talkback を OFF にしていても定期的に CDN に https で何かのリクエストを送っており、これがタイムアウトすると割り込みハンドラの中で死んでいます。
場合によってこのリクエストは一度も発行されない状態で起動することもあるので、その場合は安定します。二度と終了しないでください。
あとアメリカが寝ている、日本時間の昼間には安定しています。
会社から帰って来て夜中に起動すると大体時間を無駄にすることになるので、「なるべく昼間にやる」「安定したら二度と終了しない」ルールで運用しています。

さてフローは、 Quartus II で .sof などのネットリストのファイルを生成し、 Programer という何とも漠然とした名前のツールでそれを FPGA に焼き、ボードの動きを確認することが目的になります。ボードには WARM/COLD のリセットボタンが付いていますが、これはどうやら HPS 側であって FPGA 側は焼いた時にリセットが走っているっぽいので、やらなくてもいいです。
(HPS 側をリセットしても FPGA 側のリセット信号は動きません。よくできている)
.sof は揮発性なので電源スイッチでハードオフすると飛んじゃいます。開発時には便利です。

Programmer は onboard USB Blaster を使って FPGA を再構成します。
Parallels9 for Mac ではゲスト OS の Ubuntu 側で USB Blaster の認識が安定しませんでした。(20 回に一回くらい?)
しかし VM の設定で USB3.0 サポートを切るとすんなり認識するようになりました。
Altera のドキュメント通り udev だけ設定しておけばドライバは不要です。
UART も伝統の FTDI 互換ですので(Linux では)ドライバ不要です。 Mac OSX でも marverics なら不要のはず。うちのは違うので知りませんが Arduino はドライバ不要でいけたのにこれはちょっとダメそうでした。

Quartus II でコンパイル(正確には論理合成)するソースは好きなエディタで書いてかまいませんが、 Qsys が生成する HDL は再度上書きされる可能性があります(標準では soc_system ディレクトリ以下に生成される)。
このへんは Qsys を使うときにまた詳しく書きます。
回路の HDL を書いたら(シミュレーション用のテストベンチも書いて)シミュレータで波形を見ましょう。
僕は icarus verilog と GHDL, gtkwave を使っています。
これらのシミュレータは論理合成をしてくれません。従って、シミュレータでは動いても、論理合成不可能な回路を書いている可能性があります。
その場合 Quartus II でエラーが発見されるので TAT が長くつらいです。

icarus verilog 0.9 では -S を付けてコンパイルすると論理合成を試します。この機能は開発中のようですが、シミュレーションできるが論理合成できない記述を発見するには大変役立ちました。

HPS

HPS とは Hard Processor System (再プログラム不可能なプロセッサとそのシステム)のことで、おそらくはこれは Altera の言葉と思います(Soft Processor ありきの言葉なのでね)。
ここでは、要するに ARM 側のシステムのことです。 ARM の MPU コア以外にも L3 インターコネクトや AXI が含まれます。
Altera が決めてないことは全部 HPS だと思っていいのではないでしょうか。 ARM を主体に見ると軒先を貸して母屋に変な名前を付けられたように見えるかもですが、 ARM はそもそも IP で売ってるものですし、土地は元々 FPGA のものだっりします(今回 HPS の家屋を造り直すことはできませんが)。
これはドキュメントじゅうに現れる言葉ですので、何が何でも最初に覚えておかねばなりません。

本記事で主に扱うのは FPGA 側と ARM 側のやりとりの方法でありますから、これは即ち HPS Subsystem の使い方であるわけです。

HPS SDRAM Controller Subsystem

Cyclone V のマニュアル(の一部. Book3)より https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/hb/cyclone-v/cv_5v4.pdf "HPS SDRAM Controller Subsystem" の章、 Figure.11 がまさしく求める情報です。

Altera SoC Cyclone V の全容

右側の赤丸を付けたコンポネント、これがメモリコントローラです。
L2 キャッシュを経由せずに L3 インターコネクトから接続されていますので、ほぼ全コンポネントからこのメモリコントローラにアクセスすることができます。
FPGA fabric から直接このコントローラを叩くには FPGA-to-HPS SDRAM というのがヒントになるようです。

当然ながら——このレベルの混同はないと思いますが念のため断っておくと、ここでいうメモリコントローラは飽くまでメモリモジュールを制御するコントローラで、 MMU は含みません。
ARM の、ちょっと特殊な MMU はコア側に実装されております。つまり、物理アドレスが必要です。

FPGA-to-HPS SDRAM のインターフェイスについて更に調べていきます。
Figure.11-1 SDRAM Controller Subsystem High-Level Block Diagram によるとこの部分についての詳細が記述されています。

32-256bit の AXI か Avalon-MM を使って SDRAM Controller を制御できそうです。(ちなみに、 External Memory へ繋がる DDR PHY から HPS I/O Pins というバスは FPGA を経由しておりまして、メモリを好きなようにできそうです。ただそれをやってしまうと ARM 側が困ってしまうので、やりません)

AXI は ARM の定義する AMBA 第三世代か第四世代のバスアーキテクチャだったはず(うろ覚え)ですが、 Avalon-MM というのはあまり耳馴染みがありません。
Avalon, Altera, ARM, AXI, AMBA, AHB …… A から始まる用語がゲシュタルト崩壊しそうです。電話帳かよ!!
これは Altera が定義するインターフェイスのようで、使い方は簡単です。

仕様はこちらにございました。
https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/manual/mnl_avalon_spec.pdf

シンプルな Master/Slave のアーキテクチャで、実際のバスアーキテクチャは任意に選ぶことができる……というようなものだった気がします。
SDRAM Controller に関しては直接繋がってるようなのであまり気にしませんでした。
データのリードライトを要求する側が Master で、物理的なレジスタやメモリにアクセスしてデータを提供するのが Slave です。

従って、この Avalon MM の Master を実装するのが今回の目的です。
実際の作業を始めましょう。

……ところがここまでで非常に長くなってしまったので、作業編は次の記事に回します。
つづく。

2015年1月19日月曜日

すごい erlang のすごさがチョットワカルくらいまで学ぶ。できればゆかいに。

昨日、というか今朝方書き上がったエントリで大枠程度は書けたと思うのですが、バイナリのマッチまでメモって力つきており、あまり実用的な機能に触れられていませんでした。
もう少し学んだところをメモっておかねば消化不良です。

ビット操作


ビット列の生成とマッチングまでは前回メモってあるけども、それだけではパーサーは(大体)書けません。

バイナリの生成いろいろ

バイナリの生成について細かい規則は、公式のここにかなりしっかり書かれているので迷うところは実際少なかったです。
唯一迷ったのは "The Erlang Type Language"  という章に書かれた、バイナリの一般式は <<_:M, _:_*N>> であり他のは省略系だとあったところです。
これが頭の片隅に残っていたので試したところ、 <<1:8, 0:32*8>> などと食わすと無情なシンタックスエラーであります。
他に合わせて << SegList >> とか書けばいいのに……と思ったのですが、これは Erlang の組み込み型を定義する上での記法の記法であり、 Erlang の文法ではありません。

バイナリかビット列か

組み込み型 bitstring は 1 ビットの列です。
 <<1:3>> は 2 進でいう 001です、
組み込み型 binary は 8bits 境界に並んだビット列です。生成時、 <<1>> とビット幅を指定しなければこれは /binary になります。ビット列でいうと <<1:8>> です。
両者は、組み込み型としては別ですが、前項で確認したように組み込み型はあくまで erlang にとっては(ユーザープログラムよりも)事前定義された型に過ぎず、ユーザー定義型と違いはありません。

ならばビット列を使って、わざと 8bits 境界にマッチするようなバイナリを作ったら、それは bitstring と binary のどちらにマッチするのでしょうか。

bytes(<<B/binary>>)->
    io:fwrite("binary B:~w\n", [B]);
bytes(<<B/bitstring>>)->
    io:fwrite("bitstring B:~w\n", [B]).

321> test:bytes(<<0>>).
binary B:<<0>>

322> test:bytes(<<0:7, 0:2>>).
bitstring B:<<0,0:1>>

324> test:bytes(<<0:9>>).
bitstring B:<<0,0:1>>

326> test:bytes(<<0:8>>).
binary B:<<0>>

327> test:bytes(<<1:1, 1:7>>).
binary B:<<129>>

328> test:bytes(<<1:1, 1:7, 1:1>>).
bitstring B:<<129,1:1>>

329> test:bytes(<<0, 0>>).
binary B:<<0,0>>

330> test:bytes(<<0, 1:1, 1:7>>).
binary B:<<0,129>>

332> test:bytes(<<0:4, 0:5, 1:6, 1:1>>).
binary B:<<0,3>>

全体の長さが  8bits アラインであれば、各セグメントのアラインがどうであっても、 binary にマッチするようです。

ビットの展開

バイナリ内からマッチした部分で引数に拘束される値はどうなるでしょうか。
上位 7bit を取り出してみます。

rst(<<R:7, _:1>>)->
    io:fwrite("R:~w\n", [R]).

341> test:rst(<<128>>).
R:64
ok
342> test:rst(<<255>>).
R:127
ok

なるほど。ビット列からそのまま射影されるのでなく、論理右シフトされるのですね。
算術シフトではありませんから符号には注意が要りそうです。
ん……本当? 型修飾には signed/unsigned を書けたはず。そう思って試してみると……

rst(<<R:7/signed, _:1>>)->
    io:fwrite("R:~w\n", [R]).

344> test:rst(<<255>>).
R:-1
ok
345> test:rst(<<128>>).
R:-64
ok

おー、算術シフトになった!
ちなみにデフォルトは unsigned だと仕様にも書かれておりました。
型修飾リストにはエンディアンも書けるようです(デフォルト:big)。
すげえ!!

バイナリのトラバース

公式のドキュメントを参考に、ビット列全体の処理の取り扱いについて調べました。
4.7 Appending to a Binary です。このセクションではごく短い例を挙げています。

繰り返し処理

このコンテキストで書くのも急というか、本来昨日のエントリに書いておいて然るべき内容でありますので、ここでは簡単にしか触れませんが、実のところ erlang にはループがありません。
再帰を使いましょう。昔の Lisp なんかの関数型や、テンプレートメタプログラミングと一緒です。
与えられたリストの中から最下位ビットだけを取り出して、第二引数の binary に追加したものを構築する append_bin() を考えます。

[] はリストです。 car/cdr といったリスト操作を明示的に呼び出す代わりにパターンマッチ [CAR|CDR] を使用します。これは variadic template と同じですね。
この場合は、再帰の終端は append_bin([], Acc) -> Acc. であり、 CDR が空になるとこちらにマッチするので Acc がそのまま返ります。
末尾再帰ですので、この場合は C++ 同様に暗黙にループによる最適化が期待できそうです。
lsb_of(<<_:7, LSB:1>>) -> LSB.

append_bin([CAR|CDR], Acc) ->
    LSB = lsb_of(<<CAR>>),
    append_bin(CDR, <<Acc/binary, LSB>>);
append_bin([], Acc) ->
    Acc.
Acc というのは再帰ステップを通じて与えられる任意の変数ですが、 erlang においてはアキュムレータという名前で親しまれているようです。
379> test:append_bin([1, 2, 3, 4, 5], <<>>).
<<1,0,1,0,1>>
奇数,偶数,奇数,... なのでこれで大丈夫のようです。
結果はバイナリ binary であります。 bitstring に詰め込む場合はもっと簡単で、
append_bin([CAR|CDR], Acc) ->
    append_bin(CDR, <<Acc/bitstring, CAR:1>>);
append_bin([], Acc) ->
    Acc.
だけでよく、 LSB を取り出す操作を関数にする必要さえありません。
ドキュメントによると、最近のバージョン(R12B)から再帰ステップ間の Acc はコピーされなくなっていて効率的になったとあります。意図的に参照を用いる必要はないようです。素晴らしいですね。

非常に美しく書けましたが、不満はあります。
ビット列のトラバースがしたかったのに、いつの間にかリストのトラバースの話になっていた!
再帰の話が混じったので一度リストにしましたが、バイナリを受けるバージョンはもっと簡単になります。
append_bin(<<CAR:8, CDR/binary>>, Acc) ->
    append_bin(CDR, <<Acc/bitstring, CAR:1>>);
append_bin(<<>>, Acc) ->
    Acc.
406> test:append_bin(<<1, 2, 3, 4, 5>>, <<>>).
<<21:5>>
うーん、良い……。

チャンクの扱い

erlang で扱うバイナリはかなり良いことが解ってきましたが、さすがにこれで何百 MB もあるバイナリを扱ってよいかどうか気になります。
上の例では末尾再帰に出来ましたが、そうでない場合はスタックが延々と伸びて行ってしまいます。
できれば適当なチャンクに区切って処理したい。
そういうときは標準ライブラリ (stdlib ではなく kernel) を使いましょう。
file:open() して開いた FD に対してシーク位置を指定した file:pread() すればいいっぽいですが、パフォーマンスについては Performance のセクションにかなり長めの警告が書かれています。
これは言い換えれば、 read()/write() には恐らくライブラリレベルのバッファが存在しないようで、アプリケーションが自分でバッファを作る必要がある。
ストリームリードならさほど気にしなくていい場合が多いでしょうが、この辺はケースバイケースです。
他にも open 時に指定出来る mode によって、遅延書き込みや先読みを有効にさせてシステムコールの削減、スループット向上を期待できそうです。
一通り似たライブラリがありながらも C/C++ の標準ライブラリとはかなり思想が違うなという印象です。

データ構造

erlang のデータはリスト、タプル、マップと人気のあるものは一通り揃えている感じがあって素敵ですが、馴染みのある構造体も使いたいです。
バイナリをパースしたらそれを馴染みのある構造体に入れて取り回したいというのは自然な欲求なのではないでしょうか。
タプルでもいいけどオブジェクトに名前が欲しい……。タプルでもリストでも、 {X=0, Y=1, Z=2} という風に各要素に名前を付けられるけど、同じ構造のタプルを複数扱うとき大変。
レコードを使えばよいようです。
-record(Name, {Field1 [= Value1],
               ...
               FieldN [= ValueN]}).
レコードの定義はやや仰々しく、 -record ディレクティブを使ってコンパイラに示す必要があります。

インスタンス化

#Name{Field1=Expr1,...,FieldK=ExprK}
T = #rec{x, y, z}.
などとして使います。名前付きタプルという感じでしょうか。

アクセス

Expr#Name.Field
Expr はインスタンスで Name はレコード型名、 Field はメンバ名です。
やや気持ちが悪い。オブジェクトの名前を使って各要素にアクセスしたいだけなのにちょっと大袈裟ですね。 erlan shell からアクセスしずらいのも難です。
もっといい方法があるのかも知れませんが……。

まとめ

というわけで、ここ二日で学んだところは概ね以上です。
僕の必要な知識は大体手に入れたのでよしとしましょう。
 

2015年1月18日日曜日

すごい erlang のすごさがわからないくらいまで学ぶ。できればゆかいに。

昨日急に Erlang のコードをいじらなければならなくなりまして。
別にがっつり開発するとかメンテするとか大袈裟な話じゃなく、ちょっと弄ってちょっとの間動かせればいいのです。
目的もバイト列のパーサーだから Erlang らしいスレッドモデルは使わない。
それくらいを目標に、他言語話者が Erlang を学んだところをメモっておきます。

ランタイムや開発環境は brew install erlang で一発です。
パッケージ内には emacs の erlang-mode も入ってますので、

((setq load-path (cons "/usr/local/opt/erlang/lib/erlang/lib/tools-2.6.15/emacs/" load-path))
(setq erlang-root-dir "/usr/local/opt/erlang/")
(setq exec-path (cons "/usr/local/opt/erlang/bin/" exec-path))
(require 'erlang-start))

と書いたら ok.
C-c C-k でコンパイル、 erlang シェルの起動まで一発であります。
らくちんらくちん。

文法

Erlang は結構古い言語で 86 年登場だそうなので C++ と同世代くらいでしょうか。
しかしながら文法はかなりモダンな機能が多く、かと思えば全然モダンじゃないところもあってアクセルを踏んだりエンストするような感じもあり、乗り物酔いしそうです。

他の言語であれば、一つの文は 式; でブロックは { 文 ... } で……みたいな大枠から説明できるのですが、 Erlang の文は文脈依存の構文が多く、なんとも一言では説明できません。
もっとも原始的な文を
Expr.
と言うことはできるですが、多くの手続き型言語がこれの列挙をする文法なのに対し、 Erlang はこれだけ解ってもどうにもならない文法だと思います。
……といってもなかなか伝わらないと思うので、僕の見た厄介な例に目線を合わせる意味で、こんな例を書いておきます。

-module(test).
-export([func/2]).

func({A, B, C, _} = Tuple, A) when A > 1,
            B > 1;
            C > 1 ->
    io:fwrite("func({A,B,C,_}, A)\n"),
    case A of 2 when B > 2 ->
        io:fwrite("Tuple = ~w\n", [Tuple])
    end;
func({A, B, C, _}, _) ->
    io:fwrite("func({A,B,C, _}, _)\n").

手続き型か、いや関数型プログラミング言語か……というよりどことなく BNF 記法による構文解析を思い出します。
なんとなく見てわかるところと、解らないところがあると思います。

解るところは:
  • test モジュールと関数 func が定義されている
  • 関数は複数定義され、 template の特殊化のように動くようだ
  • 特殊化は when 条件式によっても細かく制御できるようだ
  • {} は何らかの無名構造体を構成するようだ
  • io:fwrite() は printf() のようなものだ
C++11 の variadic template はお気に入りの機能で、記述が冗長なことを除けばこれを(大枠では)すんなり理解できると思います。
パッと見、疑問に思うところは
  • 文末の記号があったりなかったり、複数の種類があるのは何なの?
  • . (dot) が文末なのに、本体のコードではたった一回しか登場しないじゃん
  • -> と ; の厳密な定義は何?
  • ; の定義によっては二つ目の func は一つ目の func の中に定義されているようにも見えそうだけど、どうなるの?
  • 一つ目の func({A, B, C, _} = Tuple, A) は特殊バージョンにとしては二つ目の func とあまり違わないように見えるが、パターンマッチが動くのだろうか?
  • when の条件式が複数あるけどこれはどう評価されるの?
  • case も when も条件分岐だと思うけど、二つあるのは何なの?
  • if じゃダメなの?
  • = は何なの?
ということでしょうか。
これらの疑問には、個別の解説を読むより公式のドキュメントが一番わかりやすく答えてくれました。

関数の宣言は関数節の集合

公式ドキュメントによる仕様をみてみましょう。
  • 関数宣言はセミコロンに区切られた関数節からなり、 .(dot) で終端する
  • 関数節は節頭と節本体を -> で繋いだもの
  • 関数節本体は一つ以上のカンマで区切られた式からなる
と書かれています。
つまり、下記は全体で一つの関数 Name の宣言であると言えます。
一つ一つが関数節であり、下線部は各関数節の頭です。関数節はそれぞれセミコロンで区切られ、.(dot) で終端します。
下の構文は公式ドキュメントのものですが、関数節が一つの場合はセミコロンで終端しそうに見えますが、 関数宣言は . で終端すると定義されているのでまぁ、文章で書かれた定義のほうに従うようです。

Name(Pattern11,...,Pattern1N) [when GuardSeq1] ->
    Body1;
...;
Name(PatternK1,...,PatternKN) [when GuardSeqK] ->
    BodyK.
 
各節は構文解析器のノードと一緒です。全ての引数についてパターンがマッチし when 節が還元すれば、その関数が還元します。呼ばれるわけです。
variadic なテンプレート引数展開や特殊化はまず汎用版を宣言してから特殊バージョンを宣言しますが、この場合は逆であり、節ごとに緩くしていきます。
when シークエンスからなる(ガード節というらしい)は省略可能です。
関数節頭の () 内は、仮引数リストというよりパターンであることは予想通りですが、仮引数リストのようにも機能します。
引数の数をアリティと呼び、アリティが違えば別の関数とされるのは他の言語と一緒です。
上の、 func 関数の場合は引数は 2 個で、フルネームは test:func/2 であります。
パターンとは項ですが、未拘束変数が許されています。
詳しくは後述しますが、ここでは C++ の人に解りやすいパターンの例をあげておきます。
func/2 は未拘束変数 A を持ち、第二引数の値によって特殊化……のように動きます。
 

func(A, 0) -> io:fwrite("zero\n");
func(A, 1) -> io:fwrite("one\n");
func(A, 2) -> io:fwrite("two\n");
func(A, _) -> io:fwrite("other\n").

when 節

続いて when 節を見てみましょう。
ここでいう when 節とは when GuardSeq で表現される節です。
ここはドキュメントで明示されるまでは信じられないような仕様でありまして、
  • カンマで区切られたガード式の集合がガードである
  • セミコロンで区切られたガードの集合がガードシーケンスである
とされています。
いやまぁ、 C/C++ だってカンマ演算子を使ってギョッとするようなコードを書くことはできますが、仕様でこう書かれてしまうと「アアア……」となってしまいます。
違いは、ガードは全部評価され全部が true にならなければなりません。ガードシーケンスは順番に評価されて true になればシーケンス全体が還元します。  
ガード式とは、 Erlang の式ですが、全部使えるわけではなく、副作用のあるようなことはできません。
引数リストはパターンでなければならないので、 式は書けません。
仕様では"a subset of the set of valid Erlang expressions" と表現されております。
副作用がないのだから、ガードシーケンスやガードの残りの式は評価されない(will not)ようです。
大体 C++ の条件式の評価と同じですが、この辺は評価が決まっている C++ とは違いますね。

ちなみに、ある解説によるとセミコロンは orelse 演算子の意味とありましたので、ガード節においてはセミコロンは演算子であると考えたほうが意味的には合っていると思います。しかし文法上はセミコロンで区切られていると明示されているので、演算子であるという理解だと無理があるように思います。
僕はそっちの解説を先に読んでしまったので激しく混乱しましたが……。

以上のことを頭に入れて when 節をもう一度よーく見てみましょう。
さっきも書きましたが、引数リストはパターンしか書けません。ガードには式が書けます。

func({A, B, C, _} = Tuple, A) when A > 1,
            B > 1;
            C > 1 ->

これは要するに (A > 1 && B > 1) || C > 1 と解釈できます。
これが true ならこの関数が呼ばれます。

パターンとマッチとパターンマッチと式

さて、 Erlang に特徴的なことのうち、難しい文法の話も大詰めでります。
ここまでナアナアにしていた関数の引数リストを理解しなければなりません。
ちなみに、 {} はタプルの宣言で、変数は大文字で始まらなければなりません。

({A, B, C, _} = Tuple, A)

引数リストはパターンの列挙だということを理解しても尚、上の例には首を捻るところがあります。
  • タプル中の A と第二引数 A の関係は?
  • = は式なのではないのか?
仕様では、パターンは項と同じように表現される構造体であるが、未拘束の名前を使ってもよいとあります。
すなわち、 A とか B といった名前が宣言なしでそこに登場することは問題ありません。
引数は {A, B, C, _} などに拘束されるわけです。(_ は無名オブジェクト)
{A, B, C, _} = Tuple のほうも同様で = はマッチ演算子とされますが、パターン中に現れることを許されております。

マッチ演算子を使って評価を行うことをパターンマッチングと呼びますが、パターンマッチングに成功すると右辺の項を左辺のパターンに拘束します。例えばこうです。

X = 1.
1 = X.

X を 1 に拘束します。一度拘束してしまえば、 1 = X も同じです。右辺が評価可能な項であり、 1 もパターンであるからです。
X=1 は何度も評価できますが、一度拘束してしまえば X=2 は拘束できなくなります。
(erlang shell においては f(X). とすれば X は未拘束になり、再度拘束できるようになります)
これは X が const になってしまうのではなく、エラーメッセージによると「X と 2 はマッチしません」という、要するに X=n. というのは正確には、マッチを使って拘束していた、ということになります。
この仕組みで、引数リストはマッチをしつつ未拘束変数に実引数を拘束している、と思われます
通常、パターンマッチでは右辺は項でなければならず、未拘束変数は項とはならないっぽいです(本当?)が、パターンであれば未拘束変数が許されます(これは本当)。
関数頭の宣言は (Patterns...) とありますので、パターンであることが明白なので、マッチの右辺も常にパターンであると見做され、右辺に未拘束変数が来ることを許されているのかなぁと思いますが……書いてて全然信じられませんね。自分でも何言ってんだろうこの人と思います。
実際、引数リストに = (マッチ演算子)を使ってマッチしつつ拘束を行う場合、 {} = Tuple と Tuple = {} は等価であります。それどころか Tuple = ({} = Tuple1) とかでもいけます。
なぜ解り難いほうを使って例にしたのかというと、公式ドキュメントの例ではこの順番だったからです。僕の見ていたソースでも全部 {} = Tuple のほうでした。

パターンマッチングでは基本は Pattern = Term です。
引数リストにおいてはなぜか Pattern = Pattern です。
右辺はパターンでない限り、未拘束変数はエラーになってしまいます。その代わり、右辺の評価を使って関数呼び出しなどができます。

Ret = func().

という、ごくありふれたあれですね。


さて、パターンマッチングを使って引数を拘束することは解りました。
この関数が呼ばれたということは、全てのパターンマッチングが成功したということですので、全変数が拘束済みであります。

ここまでで大体明確になったと思いますが変数 A はたった一度拘束されるので、関数呼び出しの時に何度評価されても、矛盾がおきればマッチングは失敗し、その関数は呼ばれなくなります。
erlang のタプルは無名で、オブジェクトの名前をつけないでアクセスするので、タプル中の A と第二引数の A は同じ名前でアクセスされます。
従って、タプルの A と第二引数の A は同じでなければマッチングは失敗します。

erlang shell で実際に確かめてみます。
emacs で C-c C-k でコンパイルすると、 erlang shell にモジュールがロードされるっぽいので実行は簡単です。

187> test:func({2, 3, 3, 0}, 2).
func({A,B,C,_}, A)
Tuple = {2,3,3,0}
ok

188> test:func({2, 3, 3, 0}, 5).
func({A,B,C, _}, _)
ok
ちなみにタプルが {A, B, C} がタプルの名前を使わず A, B, C としてアクセスされるなら Tuple という名前は何なのかと思いましたが、タプル全体にアクセスするのにこの名前を使用します。サンプルでは io:fwrite() の引数に使っています。
これは全体にアクセスしたいとき、同じオブジェクトを再構築するのを避けるためのシンタックスシュガーであると仕様にはあります。
更にちなみに、名前付きの、所謂構造体を使う場合は record を使います。
R = #Type { MemberList } で構築されます。アクセスするには R#Type.member という、二日したら多分もう覚えていないであろう変態的文法のようです。型推論あるんだから頼むよ!

case of

大方疑問は解消しましたが、文末の問題についてはまだ一つ気持ち悪いところがあります。
case が -> を還元したあとの終端規則です。予約語 end がいきなり登場してますが、いいんでしょうか。

case Expr of
    Pattern1 [when GuardSeq1] ->
        Body1;
    ...;
    PatternN [when GuardSeqN] ->
        BodyN
end
 
仕様によると、いいんです
ああ、何なのこの煩わしい曖昧な書き方は。Pattern が一個しかないときはセミコロン要るの?要らないの?
関数宣言のときもそうだったでしょう。あっちは自然言語で定義が書かれてたけど。
この書き方を素直に解釈するとダメそうに見えるんですけど、試した限り Pattern が一個しかないときはセミコロンあるとダメっぽいです。
 
パターンにはパターンか定数式しか書けないので、 when ガード節を取ることができます。
待てよ?パターンということは未拘束変数も書けるのだろうか?と試したところ、仕様通り書けました。
しかし未拘束変数とのマッチは失敗するはずだと思いきや、なぜかマッチは成功してしまいました。
 
foo(A) ->
    case A of UnboundX ->
     io:fwrite("matched to unbound\n");
    0 ->
     io:fwrite("matched to zero")
    end.

212> test:foo(0).
matched to unbound
ok

ok じゃねえよ!!
C++ の default ラベルみたいなことができるように無名オブジェクトを使って _ -> ignored が動くようにしてあるのかなぁ。
それともバグかなぁ。バグだとしてももうこれを直したら死人が出ると思うし、直せないよなぁ。

if 文もありますが if 文との最大の違いは、 case はいずれかのパターンにマッチしなければランタイムエラーとなります。

サンプルで動きを確かめる


上にあげたサンプルは、特に意味はありませんが文法のいやらしい所を満載しており、 erlang shell 上で引数を変えて呼び出すといやらしい動きをします。 

C>1 が false でも A>1, B>1 ならば特殊バージョンが呼ばれる

190> test:func({2, 3, 0, 0}, 2).
func({A,B,C,_}, A)
Tuple = {2,3,0,0}

C>1 も A>1 も false なら特殊バージョンは呼ばれない

231> test:func({0, 2, 0, 0}, 0).
func({A,B,C, _}, _)
ok

そもそもタプルの構造が違えば _ であってもマッチしない

235> test:func({2, 3, 2}, 2).
** exception error: no function clause matching test:func({2,3,2},2)

特殊バージョンが呼ばれる。しかし case Expr of Pattern にマッチしないのでエラー

193> test:func({3, 3, 3, 0}, 3).
func({A,B,C,_}, A)
** exception error: no case clause matching 3

特殊バージョンが呼ばれる。しかし case のガードにマッチしないのでエラー

227> test:func({2, 2, 3, 0}, 2).
func({A,B,C,_}, A)
** exception error: no case clause matching 2


バイナリの扱い

例外や悪夢のような -spec の文法、多彩なライブラリや組み込みデータ型についてはスルーします。
まぁ、大体の文法は解りました。
一番興味があるのは erlang の強力なマッチングはバイナリでどうなるかということです。
もう一晩これを書いてるので、簡単なメモを書いて寝たいです。

バイナリは Bin = <<SegList>> で表現されます。
Bin = <<1:1, 0:7>> ならば 0x80 になります。
Bin = <<1:8>> は 0x01 です。 0xff ではありません。
では Bin = <<1:4, 0:4>> ならば 0x10 ですね。
カンマで区切られたゼロ個以上の各要素を、セグメントと呼びます。
セグメントの完全な表記は Value:Size/Type となっています。

ちなみにサイズの合計は 8 bits の境界に揃っている必要はありませんが、他の型と変換する場合など、当然ながら必要となるシーンはあります。

バイナリのマッチング


MSB というか、 8bit 以上の任意の長さのビット列の一番左のビットにマッチし、続く 7bit を取得し、残りは気にしないという関数 bin を考えます。

bin(<<1:1, R:7, _/bitstring>>)->
    io:fwrite("MSB 1 (rest:~w)\n", [R]);
bin(<<0:1, R:7, _/bitstring>>) ->
    io:fwrite("MSB 0 (rest:~w)\n", [R]);
bin(<<_/bitstring>>) ->
    io:fwrite("unmatched <<_>>\n").

/bitstring という型の指定は重要です。
この例では、8bit 以上の任意の長さのビット列を扱いたいので、ビット列のセグメントがどのように作られたかに依存したくはありません。マッチの場合は、これがないとセグメントの数や長さに依存するようになってしまい、 <<1:1, 0:7, 0:*>> などとして生成されたバイナリにしかマッチしなくなってしまいます。
/bitstring は一つのセグメントについてあればよいようです。

最後の bin(<<_/bitstring>>) は、ビット列の長さが 7bits 以下の場合にここに還元します。

では実際の動きを見てみましょう。

MSB=1 のビット列の場合

273> test:bin(<<128:8>>).
MSB 1 (rest:0)
ok

MSB=0 のビット列の場合

274> test:bin(<<127:8>>).
MSB 0 (rest:127)
ok

セグメントを明示して作ったビット列

268> test:bin(<<1:1, 5:7>>).
MSB 1 (rest:5)
ok

/binary を指定しているので、生成時のセグメントには依存しません。

267> test:bin(<<129:8, 0:128>>).
MSB 1 (rest:1)
ok
271> test:bin(<<129>>).
MSB 1 (rest:1)
ok

まとめ

強力なライブラリと list, map などのデータ構造……。興味は尽きないのですが、取り急ぎ、自分が必要とするところだけを駆け足で見てみました。
なかでもバイナリマッチングは強力で、パフォーマンスまではわかりませんが、かなり面白いです。
なんでこんなに俺得仕様なのかといえば、 Erlang は元々携帯電話の基地局などで使われていたと聞きます。当然何層ものパケットを解析してディスパッチ先を決め、割り込みにも高速に応答しなければなりません。そのせいでしょうか。
現在はスケールしやすさが買われているようです。
実際に動かしてみると、仮想マシン上のシステムにしてはなかなか IO の応答性がよくてときめきました。
携帯電話の基地局といえば、かつて WindRiver の VxWorks で動いているシステムを見たことがありますが、ああした環境の小さな OS で動けば、かなり使い手のある言語/環境だと思います。


2014年2月9日日曜日

テラリア VITA 版

東京では20年ぶりの大雪です。
ネタかと思ったらマジ積もりしてます。
寒いです。
先週インフルエンザにもなりました。
みなさんいかがお過ごしでしょう。

テラリア VITA 版がでました!
テラリア公式フォーラムじゃ発表されるや否や「モバイル版を作ってるんだって!? ……なんだ、vita かよ。ケッ」みたいな扱いを受けていた VITA 版ですが、個人的には首を長〜くして待ってました。
ゴロゴロしながら掘りまくれると色々捗りすぎて健康で文化的な生活が危うい感じであります。

基本的には PS3 版といっしょ……どころかセーブデータすら共有できるらしいです。
せっかくなので最初からやり直してますが。

新しいバージョンでは前回までは作れなかった新しいアイテムが作れるようになった他、敵キャラも数段パワーアップしています。
操作体系も vita 合わせで大きく変わりました。ざっくり整理します。

  • アイテムの選択は L2/R2 の変わりにタッチパネルをつかう
  • アイテムボックスをタッチパネルで直接選択もできるけど、画面の左右をタップして切り換えもできる
  • マニュアル/オートの切り換えはタッチパネルの左下(マニュアルとかオートとか書いてある四角そのものが切り換えボタンになっています)
  • ピンチイン/アウトでズームイン/アウト
  • L/R はそれぞれつかまりフックと攻撃
といったところでしょうか。
マニュアル/オートの切り換えが少々煩わしい以外は特に問題なくプレイできています。

これは絶賛オススメできましょう。

[2014/02/11 追記]

PS3 版も Vita 版と相当のテラリア ver1.2 へのパッチが出ているようです。
テラリア 1.2 とそれ以前でどこが変わったのか解りにくいのですが、気がついたところを書いておきます。

グラスブレード

ジャングルの地下中部くらいにある「ジャングルの胞子」と地下浅いところを飛び回ってる強敵「ホーネット」などが落とすトゲ、それぞれ 13-15 個くらいで作成できるグラスブレードは、リーチもあり近接攻撃力 30 と必携の武器であります。
ホーネットの撃ってくる針は剣を振って落とすことができます。

強敵、ホーネット

焚き火

松明 3 つで焚き火ができます。焚き火の周囲では体力の回復が早くなります。
周囲の墓石を見た通り、あまり役に立たない

キャンプファイヤーどころか斎場です。本当にありがとうございました。

つかまりフックからムチへ

つかまりフックとツタなどを組み合わせてムチを作れます。
つかまりフックは一方にしか射出できませんが、ムチは好きな方向へ何本も射出し、好きな位置にキャラクターを固定できます。これはなかなか画期的な便利さで、汚染地域の縦坑を昇降したり、深い水の底から復帰するのに威力を発揮します。

料理/お酒

家に料理壷や酒樽を作って設置することで料理や酒造ができるようになった……らしいです。
今のところスープくらいしか作れておりませんが、全ステータスが向上するのでなかなか使えます。



それから以前紹介した Rocksmith 2014 ですが、ニコ生の生放送タイムシフトはわりと早く消えてしまうのを知りませんで、せっかくリンクした動画が消えておりました。
そのうちちゃんと撮影して再編集して公開しようと思っていたのですが伸び伸びになっておりましたところ、折よくインフルで寝込んだのでこの機会にとやっておきました。
病み上がりにつきややフラフラしておりますが、健康でもさほど変わらなかった気がしますのでご愛嬌ということで。

以下でご覧になれるはずです。
前回ご紹介しところ、わりと「そこまで挑戦的なゲームとは知らなかった」と反響がありましたので、この動画をみて「ああ、これはやらなきゃ」と思っていただけると幸いです。



2014年1月26日日曜日

Might and Magic X Legacy goes gold

しばらく放置しておりましたが、 M&M X Legacy 以下 MMXL がついに出荷バージョンとなりました。
おめでとうございます!!

2014 年春と言われていたので、事前の発表よりもだいぶ早かったですね。

Early Access を購入していた人は追加料金なく製品バージョンにアップグレード可能ですが、アップデート前に古いバージョンをアンインストールしましょう。 uplay の画面からはアンインストールできないので、 Windows のコンパネから明示的にアンインストールする必要があります。
そうでないと無限アップデート地獄 (毎回 700MB 以上ダウンロード)に落ちます。

セーブデータが引き継げるかどうか解りません。
手順が悪かったのかも知れませんが、僕は引き継げませんでした。
アップデート前に savedata (デフォルトでは Program Files の uplay のディレクトリの下)にある奴を消しておけよ、とメールに書いてありました。 uplay ならばクラウドにコピーがあるはずですんで、メールにある通りアップデート前に消しておけばちゃんと同期された可能性もあります。
互換性が崩れたとき、クラウドにはこうしたリスクはつきものですよね。

さて肝心のプレイですが、まずパフォーマンスが飛躍的に向上しております。
まったく最適化されてない感があった Early Access とは雲泥の差です。
i5 のクソノートでもデフォルト設定で結構うごきますが、シャドウディティールやビューディスタンスなどを削ればさらに快適になります。
i5 内蔵の Intel HD Graphics は、頂点処理能力はまぁまぁ高いようで、オブジェクトディティールなどは Medium 〜 High レベルで問題ないようです。
まぁ、ものすごく快適とは参りませんが。かなりマシになったということです。

更に、おそらくパッチ 1.2 くらいからの対応ですが、日本語にも対応しております。HUD, Tips のみならずキャラクターのセリフも日本語になりました。
翻訳しながら進めるのも楽しかったのですが、ラクになるのは間違いありません。僕はこの洋ゲーらしいカタい直訳が好きですが、翻訳が気に入らないなら英語モードで遊べばいいだけのことです。

戦闘でコマンドを選んだ後の謎の硬直もなくなり、スムースであります。
宝箱開けた後に固まったり、全てのコマンドが入力できなくなるバグも直ったようです。

というわけで、飛躍的に快適になりました。
期待通りであります。

2013年12月30日月曜日

C++11 と obj-c

別に obj-c を使ってるわけでもなんでもないのですけど、前回の記事で cocoa のコードをつらつら書いてるときに”拡張子を .mm にして clang に食わすとその中身は C++ と obj-c が混在しまくってても OK"というのを知りました。
オブジェクトの互換性はなく、 obj-c のクラスを C++ で継承したりその逆はできないみたいですが、それ以外のことは一通りできており、 C++ で設計、記述したクラスに実に自然に mac の GUI を被せることができて結構感動したものです。
もうこれでくだらない glue を沢山書かなくていいんだ!

それはそれで大変素晴らしいことなのですが、それでは C++11 ではどうなのかというところが気になりました。
気になる事は試してみましょう。

まず、 clang で C++11 を有効にするには -std=c++11 をつけてコンパイルします。
更に、 C++11 の新しい標準ライブラリを使うには -stdlib=libc++ をつけます。
libstdc++ とかではありません。

cmake でいうとこうです。
   set(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++")
ただ一つ問題があります。
cmake の project に .m が混在しているとこれはエラーになってしまいます。
.mm だけならば問題ありません。 project を別けるかしましょう。
無論、自分で makefile を書くならば suffix ルールを別けるだけで済みます。
まぁ、全部 .mm にしちゃいましょう。

これで準備は完了です。
界面に関わらないことであれば、概ね C++11 の機能が使えるようです。

クロージャ

C++ のコンテキストでクロージャを呼び出すことは難なくできました。
これを obj-c のメソッドに渡すことを考えてみましょう。
Test::foobar というクロージャを受け取って呼び出す obj-c のクラスを考えます。

Test* test = [Test alloc];
const char* msg = "Hello";
[test foobar:[msg](){printf ("%s\n", msg);}];
太字がクロージャであります。
Test の実装はこんな感じ。
@interface Test : NSObject
@end

@implementation Test
- (void) foobar:(std::function< void(void) >)closure
{
  printf("call begin\n");
  closure();
  printf("call end\n");
}
@end
結果は以下の通り。

call begin
Hello
call end
問題なく受け渡すことができました。

__attribute__

クロージャが使えるならかなり色々なことができそうです。
界面に関わるうち、構文上どうにもならなそうなもの以外(例えば obj-c メソッドのシグネチャでrvalue 制約や型推論を使う等)は一通り使えそうです。

構文上厳しそうなものといえば [[]] による __attribute__ です。
GNU 拡張の __attribute__ ディレクティブを、 clang は理解しますが、折角仕様に盛り込まれたのだからそっちを使ったほうがいいです(もっとも、 no_inline だけは効いてくれたことがないですが)。
でもこいつは [] で括る obj-c の文法と相性が悪そうです。
    float vec4 [[align(4)]] [1];
    float vec8 [[align(8)]] [1];
    float vec16 [[align(16)]] [1];
    printf("vec4:%p vec8:%p vec16:%p\n", vec4, vec8, vec16);
一応コンパイルは通りました。ですが実行結果は以下の通り。
vec4:0x7fff5fbfee78 vec8:0x7fff5fbfee74 vec16:0x7fff5fbfee70
手元の clang では残念ながら [[align(x)]] 自体が動いていないようです。
gcc-4.9 では大丈夫だったように思うのですが。

まぁ、使えるんじゃないですかね?(鼻をほじりながら)

2013年12月27日金曜日

cmake で Mac のアプリケーションをビルド

働きたくないでござる!
絶対に IDE で働きたくないでござる!!

……というわけで、 mac 用のアプリも CUI ベースで開発してビルドも make で行いたいというような話です。
マルチプラットフォームでビルドしたいので cmake を使います。

mac のバイナリ(?)である bundle は、要するに何の変哲もないディレクトリで中身にお作法があります。
ここにはアプリケーションの実行形式とアプリケーションの MANIFEST である InfoList.plist 、そしてメインウインドウを記述する .nib ファイルが最低限必要です。
それ以外にも、必要に応じて so や、リソースファイルなどが必要になるでしょう。

こうしたものは Xcode を使えば特に気にすることもなく出来てしまうのですが、そもそも Xcode 使いたくないじゃん?というようなところに動機があります。

Linux や cygwin にも対応できるよう、 gnu make をベースにビルドすることを考えます。
makefile を最初から書く覚悟のある人は、バンドルの規則に従ってターゲット書けばいいだけの話ですのでそうしてもらうとして、ここでは cmake を使って makefile や好みにあわせて .xproj なども作れるようにしておきましょう。

CMakeLists.txt を書く

framework の追加

まずは framework を追加しましょう。ぶっちゃけ GUI でチマチマ追加するより簡単ですよ。

cmake_minimum_required(VERSION 2.8)
project(cocoa.app)

if (APPLE)
   add_executable(cocoa MACOSX_BUNDLE main.mm app.mm)
   include_directories ( /System/Library )
   find_library(COCOA_LIBRARY Cocoa)
   find_library(APPKIT_LIBRARY AppKit)
   find_library(APP_SERVICES_LIBRARY ApplicationServices )
   mark_as_advanced (COCOA_LIBRARY
                     APPKIT_LIBRARY
                     APP_SERVICES_LIBRARY)
   set(EXTRA_LIBS ${COCOA_LIBRARY} ${APPKIT_LIBRARY} ${APP_SERVICES_LIBRARY})
   target_link_libraries(cocoa ${EXTRA_LIBS})
endif (APPLE)

小文字は cmake の組み込み関数やディレクティブです。
この場合は project として cocoa.app というバンドル名を指定します。この CMakeLists.txt のルートですので、この場合はバンドル名とするのが自然と思います。さて、これは実行形式の名前とは無関係です。

実行形式は通常 project 名とは無関係で、任意の名前です。(一つのバンドルに複数の実行形式があってかまわないので)
実行形式は、ターゲットとして、  add_executable() で宣言されます。
これは極めて重要な指定で、これを行った以降でないと設定できない属性が沢山あります。実際は依存関係を記述する都合上、後方に書かれることが多いですが、ここでは解りやすさのためには真っ先に書いています。
ここでは特に、 MACOSX_BUNDLE としていることに注意してください。

mac 用の環境を切り離すのには if (APPLE) を使います。
find_library() で framework を探し、 mark_as_advanced() に設定します。
そのリストを target_link_libraries() で(上で宣言した)実行形式にリンクしましょう。
find_library() で指定するのは /System/Library/Frameworks/ 以下にある *.framework のワイルドカードにマッチする名前です。
さてここまでは簡単です。次にバンドルに突っ込むリソース類のレシピを記述します。

InfoList.plist の追加

バンドルに絶対欠かせないものの一つに InfoList.plist があります。こいつはいわゆる MANIFEST です。
バンドルは shell でさっくり実行形式をロードするのとは異なり、 Mac のシステムサービスを経由して実行されるので、これの記述は重要です。

絶対に欠かせないものは
  • 実行形式の場所
  • nib の名前
  • NSPrincipal クラスの名前
です。これがないとバンドルはシステムのロンチサービスに蹴られてエラーになります。
他にも固有識別子やバンドル自体の名前など、重要そうなものがあります。

これらは基本的には cmake 固有の変数に値を設定することで勝手に InfoList.plist が作られるのですが、なぜか nib と NSPrincipal クラスの名前だけ、 cmake 固有の変数がありません。
従ってそのままだとバンドルができても、ロンチサービスに蹴られてしまいます。ありがちですね。
すぐ思いつく対策は、あらかじめ作り込んだ InfoList.plist を用意しておいてビルドのポストコマンドでコピーすることですが、それだとカスタマイズ性がよくありません。

そこでカスタム用の変数を宣言しましょう。カスタム変数は要するに cmake の置換文字列機能です。この場合は、 MACOSX_BUNDLE_INFO_PLIST プロパティを使って、ベースとなる InfoList.plist のテンプレートを指定し、 その中身を任意の変数で置換すればよいわけです。
set_target_properties(cocoa PROPERTIES MACOSX_BUNDLE_INFO_PLIST Info-CMake.plist)
まず一点。プロパティは変数とは違います。変数がプロセッシングの過程で置換されるだけのものだとしたら、プロパティは cmake の振る舞いを変えることができます。
cmake --help-property-list で一覧を見て、 cmake --help-property でその意味を調べられます。 MACOSX で grep すると以下のようになりました。

MACOSX_BUNDLE
MACOSX_BUNDLE_INFO_PLIST
MACOSX_FRAMEWORK_INFO_PLIST
MACOSX_PACKAGE_LOCATION

困ったときに役立つと思いますので、頭に入れておきましょう。

もう一つ気をつけなければならないのは、 自分のテンプレートを指定したらもう cmake 固有のテンプレートはもう使われないというところです。
なのでどこから適当に拾ってきた InfoList.plist からテンプレートを作ると、ここで使わなかった置換変数は使われなくなります。これではカスタマイズ性が下がります。

ここでは cmake が持っているテンプレートを参考にしたほうがよいでしょう。
cmake のテンプレートは、

/Applications/CMake\ 2.8-12.app/Contents//share/cmake-2.8/Templates/AppleInfo.plist

などにあると思います。
これを使うと以下のような plist が出来上がります。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>CFBundleDevelopmentRegion</key>
        <string>English</string>
        <key>CFBundleExecutable</key>
        <string>${MACOSX_BUNDLE_EXECUTABLE}</string>
        <key>CFBundleGetInfoString</key>
        <string>${MACOSX_BUNDLE_INFO_STRING}</string>
        <key>CFBundleIconFile</key>
        <string>${MACOSX_BUNDLE_ICON}</string>
        <key>CFBundleIdentifier</key>
        <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        <key>CFBundleLongVersionString</key>
        <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
        <key>CFBundleName</key>
        <string>${MACOSX_BUNDLE_NAME}</string>
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
        <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
        <key>CFBundleSignature</key>
        <string>????</string>
        <key>CFBundleVersion</key>
        <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
        <key>CSResourcesFileMapped</key>
        <true/>
        <key>LSRequiresCarbon</key>
        <true/>
        <key>NSHumanReadableCopyright</key>
        <string>${MACOSX_BUNDLE_COPYRIGHT}</string>
        <key>NSMainNibFile</key>
        <string>${MACOSX_BUNDLE_NSMAIN_NIB_FILE}</string>
        <key>NSPrincipalClass</key>
        <string>${MACOSX_BUNDLE_NSPRINCIPAL_CLASS}</string>
</dict>
</plist>
せっかく DTD がありますので、必須の要素がどれかなどは DTD か Apple のドキュメントを参照してください。
しかしながら、一点問題があります。
CFBundleExecutable の値を、デフォルトの cmake のテンプレートはターゲット名から自動で埋めてくれるのですが、カスタムテンプレートを使うとどうもその機能が動かなくなってしまいます。従って、 MACOS_BUNDLE_EXECUTABLE は明示的に指定するようにしましょう。(なくっても、バンドル名が 実行形式名.app なら勝手にそれを実行してくれるようですが)  
   set(MACOSX_BUNDLE_EXECUTABLE "cocoa")
   set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}")
   set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.example")
   set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} Version ${VERSION}")
   set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME})
   set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION})
   set(MACOSX_BUNDLE_BUNDLE_VERSION ${VERSION})
   set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2013.")
   set(MACOSX_BUNDLE_NSMAIN_NIB_FILE "MainMenu")
   set(MACOSX_BUNDLE_NSPRINCIPAL_CLASS "NSApplication")
   set_target_properties(cocoa PROPERTIES MACOSX_BUNDLE_INFO_PLIST Info-CMake.plist)
まとめるとこんな感じでしょうか。
InfoList.plist については以上です。

.nib などのリソース類

最後にして大物です。
リソースは bundle.app/Contents/Resources 以下にある雑多なものですが、 .nib のようにウインドウシステムレベルで重要なものも含まれます。
雑多なリソースは依存を書いておけば勝手にコピーされますが、 nib に関してはそうはいきません。 .xib から .nib へのコンパイルなどはカスタムコマンドを定義してビルドのポストフェイズに走らせる必要があります。

これについては以下のようにするのがよいようです。

   set(XIB MainMenu)
   set_source_files_properties(
       ${MACOSX_RESOURCE_FILES}
       # 任意のリソース群 (.nib は除く)
       PROPERTIES
       MACOSX_PACKAGE_LOCATION Resources)

   find_program(IBTOOL ibtool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin")

   add_custom_command (TARGET cocoa PRE_BUILD
       COMMAND mkdir -p ${PROJECT_NAME}/Contents/Resources)
   add_custom_command (TARGET cocoa POST_BUILD
           COMMAND ${IBTOOL} --errors --warnings --notices --output-format human-readable-text
           --compile ${PROJECT_NAME}/Contents/Resources/${XIB}.nib
           ${XIB}.xib
           COMMENT "Compiling ${XIB}.xib -> ${MODULE}/Contents/Resources/${XIB}.nib")
新たな変数が登場していますが、全部デフォルトでよいです。
基本は set_source_files_properties() でリソースファイルの MACOSX_PACKAGE_LOCATION を指定してやることと、 ibtool を探してビルドのポストフェイズに明示的にコンパイルするようにしてやることです。
.xib はこの場合一つですが、複数ある場合は foreach でリスト要素ごとに add_custom_command() でポストフェイズのコマンドを追加してやる必要があります。

パスを自分で指定しなおしてやったりと少々冗長なところもありますが汎用の関数だし、これでいいのです。

重要じゃありませんが、 find_program() も find_library() と同様よくつかう関数です。

さて、問題はどうやって .xib を用意するか、です。
XML エディタで作ってもいいですが、なんせ結構煩雑ですし、ドキュメントも DTD すらもありません。
ノードを削っていって最小限の .xib を作るというのも面白いところですが、結構でかいファイルですんでここは Xcode 様にご登場願いましょう。
一個作って使い回しましょう。直したいところだけ XML エディタで直しゃーいいじゃないですか。
ibtool は結構便利なプロセッサですが、真面目に UI を作ろうと思ったらどうしたって Interface Builder を使うことになりますから。

ではそういったところでお疲れさん。

2013年12月25日水曜日

XBA ハイブリッド

メリークリスマス!
プレゼントにいいイヤフォンを探して試聴してきたのでメモ。

XBA のハイブリッド型を見て来ました。

XBA-H1:

ダイナミックとフルレンジ BA のハイブリッド。
音が籠るけれど、全体的に自然で、低音が効きすぎるとも金物がうるさいとも思わず、値段とのバランスもとてもいいと思いました。
17000 円くらい。


XBA-H2:

音の籠もりがブーストされた感。
正直言って 25000 円もするイヤフォンとは思えないです。9000 円くらいでもっといいのあると思います。
中音重視で選んだので、まぁ中音は癖もなくよく聞こえるかなと思ったんですけど、聞く曲によっては 1500 円のイヤフォンと違わないんじゃないかこれ。
XBA-H3:

ダイナミックと BA 2 ドライバらしいです。……それにしてはデカイ。
音の籠もりはやや軽減され、バランスはこの中では一番いいです。
でも中音がやや痩せたような部分も気になりました。総合して 32000 円も取るような品質か?
自分で使うならギリギリこれだけど、別に面白くもないし、 XBA-40 のほうが全然好き。
ドライバが重過ぎるのも、人に使わせるものとしては使用シーンを限定しすぎていてちょっと難しい。
でもケーブルにウレタンが入っていて耳にかけられるようになっているのはちょっといい。

お勧めは XBA-1H です。
以前の XBA シリーズがどれも尖ってる印象ですが、かなり丸くなった感じですね。値段とのバランスも優れていると思います。
ドライバも小さくてとても軽い。
特にロックばっかり聞いているとかじゃないなら、オールマイティに使える良いイヤフィンだと思います。

2013年11月11日月曜日

Rocksmith2014

Rocksmith 2014。
Rocksmith の待望の続編でございます。
本ゲームは市販のエレキギターを専用のシールドで PS3 などに接続してゲームに使います。

そんなのゲームになるのかよ……。

前作をプレイするまではそう思ってました。
倍音の多い弦楽器に置いて、弦の音をちゃんと分離して判定するのは難しい。勿論ポジションが合ってなくても音的に合ってればよかろうなのですが、それだって簡単じゃなかったのです。
ましてギターのテクニックにはミュートといった、波形的にはノイズに近くて判定しずらいものもありますから。

まぁそれでもチャレンジ精神は買わないとということで北米版を輸入してやってみたら、これがゲームになる。しかも面白い。
アンプシミュレータも搭載し、エフェクターなどもデザインできます。これがまた高性能です。

リアルギターを使うデメリットとしては、ギターが必要でチューニングしないといけないというのはありますが、これは仕方が無いです。
かなりバグってる感じがありますが、一応チューナーも入ってます。

続編が出たということで、これは買うしかありません。

どういうゲームかは、こればっかりは説明しずらいしスクリーンショット撮ってもどうしようもないので動画を撮ってみました。前作の曲を久々に2曲プレイしてみましたが……かなり失敗してますが……いや、というかダラダラ中継って言う割には二曲だけなんですよね。誰かが間違って紛れ込んでくる前にご飯食べに行きたかったんですよ。ごめんなさい。
PS Eye のマイクからの入力を拾えなくって、 Mac の内蔵マイクになってしまったようで音量がかなり小さいのでご注意ください。



メニューの曲選択に中割りがないのは動画が悪いからではなく、こういうものです。
HDMI で直接テレビから出しており、ディレイがあるせいか気持ち早めに弾かないと合わない感じありますが、前作もそうでした。
(Rocksmith 推奨は HDMI と avmulti ケーブルを併用し、音声を別にアナログで出力する低遅延環境です)

こんなに難しいのかよとビビる人もいるでしょうが、これはプレイヤーの練度に合わせて勝手に難易度が変わるシステムのためで、ゲーム開始時はもっと全然らくちんなアレンジで始まります。
何度もやれば勝手に難易度が上がっていきますが、予期せぬ上がり方をされると「あっ、あっ」ってなります。逆に言うと自分で設定できないということでもあります。
国産の音ゲーとはだいぶ発想が異なりますね。

でもってそのアレンジこそが重要で、簡単過ぎると弾いてるって感じがしない。難し過ぎると心が折れるし指が痛くなる。
今作 Rocksmith 2014 のアレンジはイマイチのよーな気がしてですね……。好みの問題でしょうけど曲も前作のほうが良かったと思う。
前作のセーブデータがあれば 1200 円でディスクインポートパックを買って前作の曲を今作でも遊べます。前作の DLC は無料で引き継げます。
前作を北米版でしか遊んでいない僕でも一応できました。(最初上手くいかなくて、前作のセーブデータのタイムスタンプを一度更新させてからストアでディスクインポートパックをダウンロードしなおして遊べるようにしました)

ちなみに、前作はサクセスモードといいますか、セットリストを構成してライブを行い、成功すると生活が良くなってゆくモードがありました。
煩わしいと思うかもしれませんが——これってあまり好きじゃない曲やアレンジが好きになれない曲でもこなしているうちに好きになれるというモードでもありました。
ライブは三曲連続(スコアが一定以上ならアンコールもあり)で失敗できないということで、簡単なアレンジでも緊張感がありました。

そんなサクセスモードは今作ではなくなってしまいました。これってかなりの改悪のような気がするんですけども……。
今作では新たにセッションモードができました。
これは他のバンドメンバーのリズムや指定のスケールに合わせて適当に弾くだけで勝手に盛り上がったり盛り下がってくれるというモードです。
ちょっとやってみたけどいまいち……よく解らない。もうちょっとやってみないと解らないですかねえ。

そして一番肝心な今回の収録曲ですが——うーん、アレンジも含めて今のところいまいち好きになれない。
いやー多分好きになる動機がないということなのかな。サクセスモードがなくなっちゃったことで。
原曲に近づくのはいいことだと思うのですが、 Hotei (布袋寅泰)の Thrill (エガちゃんのテーマ呼ばわりされている曲)とか延々と F5 のバッキングを座って弾くのはつらいです。しんどいです。
今回の日本語版では、 B'z の Ultra soul が収録されていることは話題ですが、 Hotei 曲は全部 DLC であります。音ゲーというやつは曲のライセンス料がすんごいしんどくて、あれだけ爆売れした Rockband シリーズでさえライセンス料が嵩んだことで台所は火の車、遂にはシリーズを継続できなくなりました。(北米の事情で、日本はどうか知らないけど)
ですので音ゲーに関しては、 DLC にしないといけないという事情も鑑みて温かく接してあげましょう。

各曲のチューニングも複雑になりました。曲毎に微妙なチューニングを要求されることはありますが、今作では頻度が上がり、それも少し高めとか少し低めとか全フラットとかでチューニングする必要があります。
チューニングというのは、弦の伸縮で音が狂うのを補正する作業であります。しかしながらテクニックとして変則的なチューニングをしておく場合があります。解放弦の音を6 弦(ギターを構えたときに一番上の弦)から順に EGBDAE と鳴る場合を "E スタンダード" と呼ぶとしましょう。これが普通です。
例えば 6 弦(一番太い弦)を D にして DGBDAE とか。これを "D ドロップ" と呼び、特定のキーの曲を弾くのを楽にしてくれます。

チューナーがまともならそれも気にならないのですが、今回チューナーの出来がどうも謎で、どう考えても合ってるのに合ってないと判定されて再度チューニングさせられ、その結果がどうもおかしいと思いつつもプレイが始まってミスを連発するというような事故です。
それと、あまりヘタった弦(古くなって音の伸びが悪くなってるような場合)のことを考慮しておらず、ロングトーンを正しく維持できないとチューニングが合っているとは判定されません。チューニングが合ってないとプレイを開始できないため、延々とチューニングを続けるということもありました。(張り替えろって話ですが)

そういうわけでチューナーはなるべく早く直してくれ、弦は張り替えてくれということで。

個人的には前作のほうが好きなので、まず前作をプレイしてから今作を買うかどうか決めてもいいんじゃないかと思います。
付属のリアルトーンケーブルは使い回しできますよ。